9.2 KiB
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.
Project-owned adapters construct the same runtime ProjectDescriptor contract through the public
adapter SDK.
Complete generic example
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:
+++
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
familiesthat writer may change; - allowed
operations:create,update,move, and/ordelete.
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.
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:
idselects the profile;familiesandstatusesfilter eligible nodes;required_nodesnames stable nodes that must be present;token_budgetlimits compiled content;dependency_depthbounds 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 = 1000000max_nodes = 10000max_query_chars = 500max_results = 100max_traversal_depth = 8max_context_tokens = 32000max_tool_output_chars = 200000max_changesets = 1000max_changeset_operations = 100max_changeset_bytes = 1000000max_render_views = 100max_template_bytes = 1000000max_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.
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; orquery, 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:
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. For full invariants, read the Core contract.