280 lines
9.2 KiB
Markdown
280 lines
9.2 KiB
Markdown
|
|
# Project descriptor
|
||
|
|
|
||
|
|
A generic DocForge project is selected by one fixed file:
|
||
|
|
`.docforge/project.toml` beneath an explicit project root. The descriptor is schema version 1.
|
||
|
|
DocForge validates both the JSON-schema shape in `schemas/project.schema.json` and runtime
|
||
|
|
invariants that schema alone cannot prove.
|
||
|
|
|
||
|
|
Reference integrations use a different fixed descriptor,
|
||
|
|
`.docforge/reference-adapter.toml`; see [Reference adapters](REFERENCE_ADAPTERS.md).
|
||
|
|
Project-owned adapters construct the same runtime `ProjectDescriptor` contract through the public
|
||
|
|
adapter SDK.
|
||
|
|
|
||
|
|
## Complete generic example
|
||
|
|
|
||
|
|
```toml
|
||
|
|
schema_version = 1
|
||
|
|
project_id = "my-project"
|
||
|
|
title = "My Project"
|
||
|
|
adapter = "generic"
|
||
|
|
|
||
|
|
[sources]
|
||
|
|
content_roots = ["docs/docforge/content"]
|
||
|
|
authority_files = []
|
||
|
|
|
||
|
|
[derived]
|
||
|
|
cache_root = ".docforge/cache"
|
||
|
|
index = ".docforge/cache/index.sqlite3"
|
||
|
|
|
||
|
|
[changesets]
|
||
|
|
root = ".docforge/changesets"
|
||
|
|
|
||
|
|
[[changesets.writers]]
|
||
|
|
id = "project-editor"
|
||
|
|
families = ["architecture", "operations", "system"]
|
||
|
|
operations = ["create", "update", "move", "delete"]
|
||
|
|
|
||
|
|
[render]
|
||
|
|
template_root = ".docforge/templates"
|
||
|
|
preview_root = ".docforge/previews"
|
||
|
|
|
||
|
|
[[render.views]]
|
||
|
|
id = "manual"
|
||
|
|
renderer = "generic_html"
|
||
|
|
template = "manual.html"
|
||
|
|
output = ".docforge/rendered/manual.html"
|
||
|
|
title = "My Project Manual"
|
||
|
|
families = ["architecture", "operations", "system"]
|
||
|
|
|
||
|
|
[graph_render]
|
||
|
|
output_root = ".docforge/portable-graph"
|
||
|
|
|
||
|
|
[[graph_render.views]]
|
||
|
|
id = "architecture"
|
||
|
|
renderer = "portable_graph_html"
|
||
|
|
output = "architecture.html"
|
||
|
|
title = "Architecture"
|
||
|
|
root = "architecture.overview"
|
||
|
|
initial_mode = "web"
|
||
|
|
depth = 3
|
||
|
|
max_nodes = 250
|
||
|
|
max_edges = 1000
|
||
|
|
max_work = 100000
|
||
|
|
families = ["architecture", "system"]
|
||
|
|
relations = ["depends_on", "owns", "calls", "reads", "writes", "tested_by", "relates_to"]
|
||
|
|
authorities = []
|
||
|
|
statuses = ["current", "active", "verified"]
|
||
|
|
tags = []
|
||
|
|
include_logic = false
|
||
|
|
|
||
|
|
[graph]
|
||
|
|
allowed_relations = [
|
||
|
|
"calls",
|
||
|
|
"depends_on",
|
||
|
|
"owns",
|
||
|
|
"reads",
|
||
|
|
"relates_to",
|
||
|
|
"tested_by",
|
||
|
|
"writes",
|
||
|
|
]
|
||
|
|
|
||
|
|
[limits]
|
||
|
|
max_source_bytes = 500000
|
||
|
|
max_nodes = 10000
|
||
|
|
max_query_chars = 500
|
||
|
|
max_results = 100
|
||
|
|
max_traversal_depth = 6
|
||
|
|
max_context_tokens = 12000
|
||
|
|
max_tool_output_chars = 200000
|
||
|
|
max_changesets = 100
|
||
|
|
max_changeset_operations = 100
|
||
|
|
max_changeset_bytes = 1000000
|
||
|
|
max_render_views = 20
|
||
|
|
max_template_bytes = 1000000
|
||
|
|
max_render_bytes = 1000000
|
||
|
|
|
||
|
|
[[profiles]]
|
||
|
|
id = "development"
|
||
|
|
families = ["architecture", "operations", "system"]
|
||
|
|
statuses = ["current", "active", "verified"]
|
||
|
|
required_nodes = ["architecture.overview"]
|
||
|
|
token_budget = 8000
|
||
|
|
dependency_depth = 3
|
||
|
|
```
|
||
|
|
|
||
|
|
Rendering sections are optional. Profiles and limits may also be omitted; runtime defaults then
|
||
|
|
apply. The required top-level fields are `schema_version`, `project_id`, `title`, `adapter`,
|
||
|
|
`sources`, `derived`, `changesets`, and `graph`.
|
||
|
|
|
||
|
|
## Identity fields
|
||
|
|
|
||
|
|
`schema_version` must be `1`.
|
||
|
|
|
||
|
|
`project_id` is the stable machine identity. It is lowercase and may contain digits, dots,
|
||
|
|
underscores, and hyphens after its first character. Do not derive it from a mutable display title.
|
||
|
|
|
||
|
|
`title` is the human-readable project name.
|
||
|
|
|
||
|
|
`adapter` is `generic` for this file format. Project-owned adapters publish a validated
|
||
|
|
`adapter_id@adapter_version` identity through their runtime descriptor; changing adapter identity
|
||
|
|
invalidates incompatible derived state.
|
||
|
|
|
||
|
|
The descriptor's byte content contributes to a descriptor hash. Client fragments, launchers, index
|
||
|
|
evidence, and policy results use that hash to detect drift.
|
||
|
|
|
||
|
|
## Canonical sources
|
||
|
|
|
||
|
|
`sources.content_roots` lists the project-relative directories containing generic Markdown and TOML
|
||
|
|
nodes. Each path must resolve beneath the project root. Canonical content roots may not overlap the
|
||
|
|
derived cache.
|
||
|
|
|
||
|
|
`sources.authority_files` lists additional project-relative regular files whose content belongs to
|
||
|
|
the canonical project identity. They are not automatically parsed as nodes.
|
||
|
|
|
||
|
|
A Markdown node contains one TOML metadata block followed by Markdown content:
|
||
|
|
|
||
|
|
```markdown
|
||
|
|
+++
|
||
|
|
schema_version = 1
|
||
|
|
id = "architecture.overview"
|
||
|
|
title = "Architecture overview"
|
||
|
|
family = "architecture"
|
||
|
|
authority = "authoritative"
|
||
|
|
status = "current"
|
||
|
|
tags = ["architecture"]
|
||
|
|
summary = "Defines the top-level architecture and ownership."
|
||
|
|
+++
|
||
|
|
|
||
|
|
# Architecture overview
|
||
|
|
|
||
|
|
Describe systems, ownership, runtime flow, failure behavior, and proof.
|
||
|
|
```
|
||
|
|
|
||
|
|
Every node ID is project-wide and stable. Every relationship target must resolve. A TOML source may
|
||
|
|
contain multiple `[[nodes]]` records; proposal-enabled multi-node files need stable
|
||
|
|
`source_anchor` values where creation or movement requires an exact record boundary.
|
||
|
|
|
||
|
|
## Derived state
|
||
|
|
|
||
|
|
`derived.cache_root` owns disposable indexes, attestations, extraction caches, render receipts,
|
||
|
|
projection artifacts, and viewer registry state.
|
||
|
|
|
||
|
|
`derived.index` must be inside `derived.cache_root`. Canonical content and cache paths must not
|
||
|
|
overlap.
|
||
|
|
|
||
|
|
Derived state is not a backup. If it is deleted or rejected as corrupt, DocForge rebuilds it from
|
||
|
|
validated canonical sources.
|
||
|
|
|
||
|
|
## Changesets and writers
|
||
|
|
|
||
|
|
`changesets.root` is the confined proposal store. It must not overlap canonical content or the
|
||
|
|
derived cache.
|
||
|
|
|
||
|
|
Each `changesets.writers` entry declares:
|
||
|
|
|
||
|
|
- a stable writer `id`;
|
||
|
|
- the node `families` that writer may change;
|
||
|
|
- allowed `operations`: `create`, `update`, `move`, and/or `delete`.
|
||
|
|
|
||
|
|
The descriptor grant is necessary but not sufficient. A process must also select that writer at
|
||
|
|
startup, and canonical application requires a separately bound matching applier. Capability mode
|
||
|
|
does not broaden the descriptor grant. See [Policy precedence](POLICY_PRECEDENCE.md).
|
||
|
|
|
||
|
|
## Allowed relationships
|
||
|
|
|
||
|
|
`graph.allowed_relations` is the exact project vocabulary accepted on edges. It must be nonempty.
|
||
|
|
Relationship names are stable IDs. DocForge rejects relationships that are not declared and gives
|
||
|
|
`depends_on` additional cycle validation.
|
||
|
|
|
||
|
|
The core does not reinterpret a custom relationship just because its spelling resembles a known
|
||
|
|
term. Task-context retrieval classifies only the documented versioned aliases and preserves
|
||
|
|
unknown allowed relationships as `unclassified`.
|
||
|
|
|
||
|
|
## Context profiles
|
||
|
|
|
||
|
|
Each `profiles` entry defines one bounded context compilation:
|
||
|
|
|
||
|
|
- `id` selects the profile;
|
||
|
|
- `families` and `statuses` filter eligible nodes;
|
||
|
|
- `required_nodes` names stable nodes that must be present;
|
||
|
|
- `token_budget` limits compiled content;
|
||
|
|
- `dependency_depth` bounds relationship expansion.
|
||
|
|
|
||
|
|
Profiles choose derived retrieval scope. They do not change node authority or writer permissions.
|
||
|
|
|
||
|
|
## Limits
|
||
|
|
|
||
|
|
Positive limits bound input, graph, retrieval, proposal, and rendering work. Current defaults are:
|
||
|
|
|
||
|
|
- `max_source_bytes = 1000000`
|
||
|
|
- `max_nodes = 10000`
|
||
|
|
- `max_query_chars = 500`
|
||
|
|
- `max_results = 100`
|
||
|
|
- `max_traversal_depth = 8`
|
||
|
|
- `max_context_tokens = 32000`
|
||
|
|
- `max_tool_output_chars = 200000`
|
||
|
|
- `max_changesets = 1000`
|
||
|
|
- `max_changeset_operations = 100`
|
||
|
|
- `max_changeset_bytes = 1000000`
|
||
|
|
- `max_render_views = 100`
|
||
|
|
- `max_template_bytes = 1000000`
|
||
|
|
- `max_render_bytes = 1000000`
|
||
|
|
|
||
|
|
Smaller project limits are useful policy. They cannot widen fixed internal worker, package,
|
||
|
|
response, or cache ceilings. In particular, detached renderer transfer has its own fixed boundary
|
||
|
|
even if a compatibility descriptor retains a larger `max_render_bytes`.
|
||
|
|
|
||
|
|
## Manual rendering
|
||
|
|
|
||
|
|
The optional `render` section declares:
|
||
|
|
|
||
|
|
- one confined `template_root`;
|
||
|
|
- one isolated `preview_root`;
|
||
|
|
- one or more stable views.
|
||
|
|
|
||
|
|
Each view uses the built-in `generic_html` renderer, a template beneath `template_root`, one
|
||
|
|
declared output path, a title, and a family filter. Paths may not overlap canonical content,
|
||
|
|
authority files, changesets, cache, templates, or previews in unsafe ways.
|
||
|
|
|
||
|
|
Templates are inert UTF-8 files with a fixed token vocabulary. They cannot select executable
|
||
|
|
renderers or commands. See [Rendering and visualization](RENDERING_AND_VISUALIZATION.md).
|
||
|
|
|
||
|
|
## Portable graph rendering
|
||
|
|
|
||
|
|
The optional `graph_render` section declares an output root and one or more
|
||
|
|
`portable_graph_html` views. Each view selects exactly one:
|
||
|
|
|
||
|
|
- `root`, an exact stable node ID; or
|
||
|
|
- `query`, a bounded metadata-only lexical seed.
|
||
|
|
|
||
|
|
It may then restrict families, relations, authorities, statuses, and tags, plus depth, node, edge,
|
||
|
|
and work limits. `initial_mode` is `nodes`, `flow`, or `web`. Portable graph contract version 1
|
||
|
|
requires `include_logic = false`.
|
||
|
|
|
||
|
|
The declared `output` is relative to `graph_render.output_root`.
|
||
|
|
|
||
|
|
## Paths and confinement
|
||
|
|
|
||
|
|
Descriptor paths are project-relative. Absolute paths and parent traversal are rejected. Runtime
|
||
|
|
validation also rejects symlink escapes, unexpected file types, unsafe overlap, changing path
|
||
|
|
identity during sensitive reads or publication, and derived outputs outside their declared roots.
|
||
|
|
|
||
|
|
The explicit CLI `--project-root` is the only project selector. The MCP process binds it at startup
|
||
|
|
and exposes no project-switching tool.
|
||
|
|
|
||
|
|
## Validate changes safely
|
||
|
|
|
||
|
|
After editing the descriptor:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docforge --project-root /absolute/path/MyProject validate
|
||
|
|
docforge --project-root /absolute/path/MyProject reindex
|
||
|
|
docforge --project-root /absolute/path/MyProject check
|
||
|
|
```
|
||
|
|
|
||
|
|
Descriptor or adapter implementation drift makes a project-owned running process fail closed; start
|
||
|
|
a fresh process after changing those boundaries.
|
||
|
|
|
||
|
|
For first-time creation, prefer the create-only [New-project
|
||
|
|
quickstart](NEW_PROJECT_QUICKSTART.md). For full invariants, read the [Core contract](CONTRACT.md).
|