1
0
Fork 0
Code Issues Pull requests Projects Releases 2 Packages Wiki Activity Actions Pages

Close Milestone 4 with adapter adoption evidence

This commit is contained in:
Andraxion 2026-07-29 15:34:25 -04:00
parent 95271dcf2e
commit 6d06195950
27 changed files with 2870 additions and 325 deletions

View file

@ -8,6 +8,30 @@ Use this guide after the repository assessment in
[Project Onboarding](PROJECT_ONBOARDING.md). The onboarding checklist decides whether an adapter
is needed. This guide defines how to build and prove one.
## Public authoring surface
Adapter authors should import typed contracts, validation helpers, graph models, and the
conformance helper from `docforge.adapter_sdk`:
```python
from docforge.adapter_sdk import (
AdapterAssembly,
AdapterImplementation,
AdapterManifest,
AdapterProject,
AdapterProjectSettings,
AdapterProjection,
AdapterSource,
AdapterSourceProjection,
LogicProjection,
verify_adapter_conformance,
)
```
Language-specific modules under `docforge.adapters` are repository reference implementations, not
the general authoring namespace. Their exact, deliberately narrow behavior is documented in
[Reference Adapters](REFERENCE_ADAPTERS.md).
## Required outcome
A production adapter must provide one reproducible public graph from authoritative project
@ -51,11 +75,39 @@ class MyAdapter:
manifest: AdapterManifest,
contributions: tuple[AdapterSourceProjection, ...],
) -> AdapterAssembly: ...
def load_complete_assembly(self) -> AdapterAssembly: ...
def load_projection(self) -> AdapterProjection: ...
```
`assemble_projection()` is optional only when extraction units already have disjoint ownership.
`load_projection()` is always required. It is the clean rebuild and equivalence oracle.
`load_projection()` is always required. It is the clean primary-graph compatibility oracle.
`load_complete_assembly()` supplies the cache-independent complete graph-plus-Logic oracle. It is
required when incremental contributions publish Logic; without it, DocForge cannot prove Logic
parity.
## What the conformance helper proves
Run the public helper against a fresh confined cache root:
```python
report = verify_adapter_conformance(
MyAdapter(project_root),
cache_root=project_root / ".docforge" / "cache" / "adapter-conformance",
)
```
`verify_adapter_conformance()` proves:
- two repeated complete assemblies are exactly deterministic, including Logic;
- when `load_complete_assembly()` exists, its primary graph exactly matches
`load_projection()`; and
- when the incremental methods exist, the assembled incremental graph and Logic exactly match the
independent complete oracle.
The report records stable identity, counts, and an assembly hash. This helper does not by itself
prove process confinement, implementation restart behavior, corrupt-cache recovery, warm zero
parsing, retrieval, or every case in the proof matrix below. Keep those as separate focused and
integration tests.
## Step 1: define authority before parsing
@ -324,6 +376,25 @@ The manifest remains a current source snapshot, not a Git-index snapshot. A Git-
omit a deleted source whether its deletion is unstaged or staged. Staging is never a required
DocForge synchronization step.
### Current aggregate bounds
DocForge limits one extraction-cache generation to 10,000 source contributions and 64,000,000
encoded bytes. Malformed, incompatible, missing, oversized, or unsafe cache data is treated as a
cache miss.
The assembled graph is bounded by the effective project `Limits.max_nodes`. When adapter settings
do not override limits, DocForge selects at least 10,000 nodes and raises that bound to the
manifest's `estimated_nodes` when larger. Aggregate assembly ceilings are:
- nodes: `max_nodes`;
- relationships: `max_nodes * 32`;
- Logic nodes across all functions: `max_nodes * 32`; and
- Logic edges across all functions: `max_nodes * 64`.
Adapters should set realistic limits and fail before retaining unbounded frontend evidence. The
cache and assembly bounds do not replace each adapter's own bounded source, command, parser, or
per-function limits.
## Step 9: keep the complete path independent
The full rebuild must not read the incremental extraction cache. Otherwise equivalence compares
@ -470,7 +541,8 @@ For every such change:
- [ ] Two independent complete builds match exactly.
- [ ] Incremental extraction uses authoritative dependencies.
- [ ] The complete oracle is independent of the cache.
- [ ] Complete and incremental public projections match exactly.
- [ ] Complete and incremental public graph and Logic projections match exactly.
- [ ] `verify_adapter_conformance()` passes, and the separate proof-matrix cases also pass.
- [ ] Corrupt, missing, and interrupted cache cases fail safely.
- [ ] Session composition and family isolation are proven.
- [ ] Viewer, query, context, and Logic retrieval are proven.

105
docs/AGENT_INTEGRATION.md Normal file
View file

@ -0,0 +1,105 @@
# Agent integration
DocForge generates deterministic, project-bound MCP client fragments for Codex, Claude, and
OpenClaw. Generic projects and custom adapters use different configuration APIs, but both produce
fixed standard-input/output bindings with empty generated environments and explicit capability
policy.
## Fixed reference binding
Configure one of the in-repository adapters with
`.docforge/reference-adapter.toml` as described in
[Reference Adapters](REFERENCE_ADAPTERS.md), then start:
```bash
python -I -m docforge.reference_mcp \
--project-root /absolute/path/to/project \
--capability-mode read
```
`docforge.reference_mcp` is a fixed trusted module in the installed DocForge distribution. It
selects a provider only from the validated reference configuration and reports binding metadata
containing:
- `server_module = "docforge.reference_mcp"`;
- `adapter_mode = "reference"`;
- the selected `reference_language`; and
- the exact `reference_config_hash`.
The reference binding registers exactly the 21 read tools listed in
[MCP Boundary](MCP_CONTRACT.md#read-tools). It has no proposal or canonical-application surface.
## Generate a reference client fragment
The public launcher and generator APIs are:
```python
from pathlib import Path
from docforge.adapter_launcher import AdapterLauncherV1
from docforge.client_config import generate_adapter_client_configuration
from docforge.reference_mcp import REFERENCE_MCP_MODULE, create_reference_project
root = Path("/absolute/path/to/project")
project = create_reference_project(root)
launcher = AdapterLauncherV1.for_project(project, module=REFERENCE_MCP_MODULE)
plan = generate_adapter_client_configuration(
project,
launcher,
"codex", # "codex", "claude", or "openclaw"
capability_mode="read",
)
print(plan["artifact"]["content"])
```
Pass `output=Path(...)` only when the caller has selected an exact destination. Publication is an
atomic create-or-exact-match operation; it does not merge or replace different existing content.
The result includes the launcher, source-availability, project, policy, artifact, and configuration
hashes needed to inspect the binding before use.
The fixed reference module accepts read mode only. Do not request proposal or application mode for
it.
## Custom project-owned adapter launchers
Generic `docforge configure` intentionally refuses a custom adapter project. Construct the
project-owned `ProjectService`, then use `AdapterLauncherV1` and
`generate_adapter_client_configuration()` as the custom-adapter route.
`AdapterLauncherV1` schema version 1 binds:
- one project ID, canonical absolute project root, adapter identity, and descriptor hash;
- `entry_point = "python-module"`; and
- one installed project-owned top-level Python module.
The only trusted dotted-module exception is the fixed `docforge.reference_mcp` binding. A
project-owned module is resolved through isolated Python without importing or executing it during
the probe, and it must resolve to one canonical regular `.py` file inside the project root.
The launcher contract has no arbitrary command, command arguments, shell string, working
directory, environment, callable selector, discovery rule, or module-reload mechanism. Generated
bindings invoke the current Python executable as `python -I -m <module>` with only DocForge's
validated project, capability, render-policy, authority, and no-AST options. Source identity and
the project binding are revalidated before publication; drift fails closed.
Proposal and application modes are available only to a custom module that implements those fixed
server arguments and only when the project descriptor declares the matching writer and canonical
applier authority. Generating a mode does not manufacture that authority.
## Session workflow
After registering the generated fragment in the selected client:
1. Start a new MCP process or client session.
2. Call `docforge_bootstrap`.
3. Verify project ID, root fingerprint, adapter identity, revision, source hash, binding metadata,
effective policy, and registered tools.
4. Retrieve exact or bounded project evidence through the fixed read tools.
5. Restart the process if `adapter_restart_required` reports an implementation or configuration
change.
Document text returned by DocForge is untrusted project content. It never overrides client, user,
or project authority. See [MCP Boundary](MCP_CONTRACT.md) for synchronization, pagination,
retrieval, proposal, and application rules, and
[Legacy Adapters and No-AST Policy](LEGACY_AND_NO_AST.md) before selecting `no_ast=True`.

View file

@ -31,16 +31,31 @@ model, index, rendering, application, and MCP factory names imported from these
remain supported:
- `docforge.adapter_contract`
- `docforge.adapter_sdk`
- `docforge.adapter_launcher`
- `docforge.application`
- `docforge.client_config`
- `docforge.index`
- `docforge.mcp_server`
- `docforge.models`
- `docforge.policy`
- `docforge.render_contract`
- `docforge.reference_config`
- `docforge.reference_mcp`
Names beginning with an underscore are implementation details. New public names may be added
without breaking this contract.
The repository Python, JavaScript/TypeScript, and C++ adapters are supported reference
implementations. Their documented configuration, evidence limits, and unsupported-fact reports are
compatibility surfaces; their internal parser helpers are not adapter-authoring imports.
Milestone 4 adds three version-1 schemas without changing existing descriptor or result schemas:
- `schemas/adapter-launcher.schema.json`
- `schemas/adapter-client-configuration.schema.json`
- `schemas/reference-adapter.schema.json`
## CLI and MCP surfaces
Existing `docforge` command names and arguments remain supported. Existing `docforge-mcp` tool
@ -233,6 +248,11 @@ Milestone 3 adds `ManualRenderPlanV1`, `GraphViewPlanV1`, projection package and
and projection policy version 2. These are additive submodule and schema contracts. They do not
change the legacy task-context, adapter, changeset, or effective-policy contracts described above.
Milestone 4 adds `docforge.adapter_sdk`, the fixed reference configuration and read-only MCP
binding, immutable adapter launchers, and generated adapter client fragments. A legacy adapter with
only `load_projection()` remains first-class and need not adopt incremental extraction, Logic, a
reference configuration, or launcher metadata.
## Safety boundary
DocForge remains bound to one explicit project root. It rejects absolute paths, root escapes, and
@ -245,8 +265,9 @@ publication, or project switching.
Milestone 0 records rather than redesigns these areas:
- Generic warm reads still repeat whole-project discovery, parsing, and validation.
- Tree-sitter and the JavaScript and C++ grammars remain mandatory installation dependencies even
when their runtime modules are unused.
- The base wheel intentionally omits Tree-sitter. JavaScript, TypeScript, and C++ syntax evidence
requires the matching `docforge[javascript]`, `docforge[typescript]`, or `docforge[cpp]` extra.
Python reference evidence uses the standard library and remains available in the base wheel.
- Several version strings and defaults remain duplicated.
- One individually oversized context entry is represented as explicit bounded omission evidence;
callers use targeted retrieval for that node.

View file

@ -29,6 +29,9 @@ commit when Git is available; it cannot change repository state.
- Projection package: `schemas/projection-package.schema.json`, version 1.
- Projection receipt: `schemas/projection-receipt.schema.json`, version 1.
- Independent projection policy: `schemas/projection-policy.schema.json`, version 2.
- Adapter launcher: `schemas/adapter-launcher.schema.json`, version 1.
- Adapter client configuration: `schemas/adapter-client-configuration.schema.json`, version 1.
- Reference adapter configuration: `schemas/reference-adapter.schema.json`, version 1.
- Index schema: version 3, disposable and reproducible.
- Index attestation: schema version 1, disposable and reproducible.
- Core, CLI, and MCP server: version 1.3.0.dev0.
@ -103,6 +106,42 @@ complete projection, opens SQLite, starts MCP, executes the configured command,
builds, renders, starts a viewer, or writes configuration. Unprovable client behavior is a warning,
not an invented success.
## Public adapter SDK and reference binding
`docforge.adapter_sdk` is the stable adapter-authoring import boundary. It exposes the typed
projection, manifest, source contribution, complete assembly, project wrapper, graph model, and
conformance contracts needed by an adapter without requiring authors to import core implementation
modules.
Complete evidence includes the primary graph and function Logic. An incremental adapter that
publishes Logic must implement `load_complete_assembly()` as an independent complete oracle.
`verify_adapter_conformance()` proves repeated complete determinism, equality between the complete
assembly and `load_projection()`, and exact complete/incremental graph-plus-Logic parity. Separate
tests remain responsible for confinement, restart behavior, no-AST behavior, cache recovery, and
retrieval.
Adapter assemblies are bounded before publication. Primary nodes use the descriptor `max_nodes`
limit. Primary edges, Logic nodes, and Logic edges use fixed deterministic multipliers over that
limit. Version-1 extraction caches are regular-file-only, bounded to 10,000 sources and
64,000,000 bytes, and are treated as misses when corrupt, oversized, foreign, or incompatible.
`.docforge/reference-adapter.toml` is a closed version-1 selection among `python`, `javascript`,
`typescript`, and `cpp`. It declares one project identity and explicit non-overlapping source
roots. C++ additionally requires a confined `compile_commands.json`. It cannot declare a command,
module, environment, writer, applier, or remote endpoint.
`python -m docforge.reference_mcp --project-root ROOT` constructs only the selected fixed
repository reference adapter and exposes the read surface. It never registers proposal or
application tools.
`AdapterLauncherV1` is an immutable project-bound Python-module declaration. It accepts no
arbitrary command, arguments, working directory, environment, discovery, callable selector, or
module reload. Custom launchers resolve one installed top-level module through isolated Python and
require its origin inside the project root. The fixed `docforge.reference_mcp` module is the only
trusted dotted exception. `generate_adapter_client_configuration()` binds generated Codex,
Claude, and OpenClaw fragments to that launcher, current source availability, effective policy,
descriptor, interpreter, and exact artifact bytes.
## Isolated proposal model
Create, update, move, and delete are ordered node operations inside an isolated changeset. Every

View file

@ -0,0 +1,165 @@
# Core concepts and authority
DocForge is a project-bound knowledge compiler. It turns explicit canonical project facts into
validated graphs and bounded derived views without transferring authority to the index, an agent,
or a renderer.
## One project, one explicit root
Every operation is bound to one canonical real project directory. Paths in descriptors and adapter
configuration are project-relative and confined beneath that root. A CLI or MCP process does not
discover or switch projects after startup.
The project root determines:
- which descriptor and canonical sources may be read;
- where derived cache and changeset roots may exist;
- which project identity, revision, source hash, and generation appear in results;
- which writer, applier, rendering, and viewer policies can be selected.
Generated client fragments preserve that binding. They are machine-local configuration
projections, not portable project authority.
## Canonical facts and derived evidence
Canonical inputs own the facts:
- generic Markdown or TOML node sources;
- authority files named by a generic descriptor;
- an adapter's declared canonical sources and implementation boundary;
- `.docforge/project.toml` for a generic project;
- `.docforge/reference-adapter.toml` for a fixed reference integration.
Everything DocForge builds from those inputs is derived:
- SQLite indexes, attestations, generation receipts, and generation diffs;
- incremental extraction caches;
- task-context capsules and query responses;
- changeset previews;
- render plans, immutable packages, fragments, artifacts, and receipts;
- portable graph publications and live-viewer processes;
- generated Codex, Claude, and OpenClaw fragments.
Derived state may be discarded and rebuilt. A derived artifact can prove what it was bound to, but
it cannot override current canonical content.
## Nodes, relationships, and Logic
The primary graph contains nodes and directed relationships.
A node has a stable project-wide ID, title, family, authority, status, tags, summary, content,
source identity, and content hash. A generic Markdown file contains one node beneath a TOML
metadata block; a generic TOML source may contain multiple nodes. Adapter nodes use the same public
graph contract.
A relationship is an exact `(source, relation, target)` triple. The project descriptor defines the
allowed relation names. DocForge gives `depends_on` special acyclic validation, but it does not
invent domain meaning for a project's other relation names. Retrieval recognizes only a versioned
alias set for task planning and reports unknown allowed relations as `unclassified`.
Logic is deliberately separate. It is a lazy function-scoped control-flow projection owned by one
primary node. Decisions, actions, loops, convergence points, returns, and exceptions connect
through explicit branch edges. Logic does not add statement-level nodes to ordinary Nodes, Flow,
Web, search, or generation-diff results. Python, JavaScript, TypeScript, and C++ integrations may
publish Logic when their adapter contract supports it.
## Authority, status, family, and tags
These fields answer different questions:
- `authority` describes the role of the content. The generic vocabulary is `authoritative`,
`approved_plan`, `derived`, `proposal`, and `historical`.
- `status` is project-defined lifecycle state such as `current`, `active`, or `verified`.
- `family` is a project-defined content grouping used for filtering, profiles, rendering, and
writer permissions.
- `tags` are exact project labels for retrieval and presentation.
An `authoritative` node can still become stale; authority is not a freshness claim. A `derived`
node is still canonical if it is stored in a declared canonical source; the label describes its
role, not whether DocForge may silently regenerate it. Status and authority never grant an MCP
writer permission.
## Validation, synchronization, and generations
Validation loads the complete canonical graph and rejects unsafe paths, invalid source formats,
duplicate IDs, unresolved relationships, prohibited cycles, violated project limits, and
adapter-specific contract failures.
The disposable index is published atomically only after the complete graph and SQLite integrity
checks pass. Its attestation binds the whole index file. A successful replacement is the derived
publication commit point; later receipt-writing trouble is reported as degraded evidence rather
than as a false claim that the replacement failed.
MCP operations automatically synchronize derived state under a project lock before normal work.
A graph generation identifies one validated indexed snapshot. Generation-pinned retrieval and
rendering do not reopen mutable sources behind an older snapshot.
The latest generation diff is one bounded primary-graph transition, not a history database. It
contains no Logic details or source text.
## Complete and incremental adapters
`load_projection()` is the compatibility baseline and clean graph oracle. An incremental adapter
adds:
- `load_manifest()` for cheap project identity, source inventory, fingerprints, and dependencies;
- `extract_source()` for one cacheable source contribution;
- optionally `assemble_projection()` to normalize overlapping contributions.
When incremental contributions contain Logic, `load_complete_assembly()` supplies a
cache-independent complete graph-plus-Logic oracle. Warm cache behavior is an optimization, never a
different authority path. Corrupt or incompatible extraction caches are treated as misses, and a
clean complete build remains the equivalence and recovery boundary.
The public types, validators, and conformance helper are exported from `docforge.adapter_sdk`. See
the [Adapter authoring guide](ADAPTER_AUTHORING_GUIDE.md) and [Incremental
indexing](INCREMENTAL_INDEXING.md).
## Proposals are not canonical changes
A changeset is an isolated, ordered proposal over an exact canonical base. Each operation names
preconditions, and the final changeset has a content-derived hash. Validation projects the complete
resulting graph before application.
Canonical application requires:
1. a writer declared in the descriptor;
2. a process started with the matching proposal and application authority;
3. one explicit final changeset hash;
4. unchanged source, relationship, permission, and graph preconditions.
Application does not perform Git mutation, build, deployment, or publication. Until exact-hash
application succeeds, canonical project files remain unchanged.
## Three independent output projections
Manual rendering, portable graph publication, and the live viewer are separate:
- a manual is a declared derived HTML view over selected nodes;
- a portable graph is a content-addressed static Nodes, Flow, or Web artifact;
- the live viewer is a managed loopback process pinned to one validated index generation and can
request lazy Logic.
Their policies compose independently. Disabling one does not transfer its authority to another.
None is canonical documentation or a retrieval authority. See [Rendering and
visualization](RENDERING_AND_VISUALIZATION.md).
## Binding policy is not project truth
Capability mode, no-AST preservation, diagnostics, and projection modes describe one running
process or generated client binding. They do not rewrite the descriptor or canonical graph.
`--no-ast` forbids AST-family adapter evolution and Logic publication/retrieval for that binding.
It does not inspect parser implementation, sandbox the filesystem, or convert an existing
AST/Tree-sitter adapter into a no-AST adapter. Read [Policy precedence](POLICY_PRECEDENCE.md) and
[Legacy and no-AST operation](LEGACY_AND_NO_AST.md).
## Trust the narrowest evidence
DocForge reports stable IDs, hashes, generations, omissions, truncation, and provenance limits so a
consumer can distinguish proof from inference. A source inventory is not a semantic graph; a
syntax-level relationship is not compiler resolution; a configuration doctor is not a connection
test; a viewer snapshot is not continuous monitoring; and a benchmark is not a release.
Continue with the [Project descriptor](PROJECT_DESCRIPTOR.md), [New-project
quickstart](NEW_PROJECT_QUICKSTART.md), or [Core contract](CONTRACT.md).

View file

@ -4,6 +4,10 @@ DocForge Release 1 adapters return one complete immutable projection. That contr
supported. The incremental compiler adds an opt-in source-scoped contract that avoids reparsing
unchanged files while preserving the same validated, atomically published graph.
Import these contracts from the public `docforge.adapter_sdk` facade. See
[Legacy Adapters and No-AST Policy](LEGACY_AND_NO_AST.md) for the preserved one-method contract and
[Reference Adapters](REFERENCE_ADAPTERS.md) for the four maintained implementations.
## Release 1 compatibility
The incremental interface is additive:
@ -34,18 +38,26 @@ Incremental indexing is an extraction optimization. It does not weaken publicati
An interrupted extraction never replaces the last validated SQLite index. A malformed,
incompatible, or missing cache is a cache miss, not a partial graph.
One cache generation is bounded to 10,000 source contributions and 64,000,000 encoded bytes.
Aggregate graph and Logic assembly limits are described in the
[Language Adapter Authoring Guide](ADAPTER_AUTHORING_GUIDE.md#current-aggregate-bounds).
## Adapter contract
An incremental loader implements all three methods:
An incremental loader implements the first, second, and fourth methods. It implements
`load_complete_assembly()` as well when it publishes Logic:
```python
class MyAdapter:
def load_manifest(self) -> AdapterManifest: ...
def extract_source(self, source: AdapterSource) -> AdapterSourceProjection: ...
def load_complete_assembly(self) -> AdapterAssembly: ...
def load_projection(self) -> AdapterProjection: ...
```
`load_projection()` remains the deterministic full-rebuild fallback and equivalence oracle.
`load_projection()` remains the deterministic full-rebuild primary-graph oracle. An incremental
adapter that publishes Logic must additionally implement `load_complete_assembly()` as the
cache-independent complete graph-plus-Logic oracle.
Each `AdapterSource` declares:
@ -137,7 +149,8 @@ fresh process must import and validate the current adapter.
```
Adapters can call `AdapterProject.verify_incremental_equivalence()` in release and contract tests.
The check compares project identity, revision, source hash, nodes, and relationships against
The check compares project identity, revision, source hash, nodes, relationships, and Logic against
the independent complete assembly. It also requires the complete assembly's primary graph to match
`load_projection()`.
## Manual changes and relationships
@ -170,12 +183,29 @@ logic empty until they implement a language analyzer.
This boundary prevents thousands of boolean expressions and basic blocks from polluting Nodes,
Flow, Web, ordinary search, or architectural traversal. The Logic tab and `docforge_get_logic`
request one function-scoped projection on demand. The built-in analyzers cover Python,
JavaScript, and C++. Python uses the standard-library AST. JavaScript and C++ share pinned
Tree-sitter infrastructure with thin language-aware control-flow profiles. Parsers run only while
extracting a changed source contribution; ordinary graph reads do not load or execute them. A
grammar alone supplies syntax, not control-flow meaning, so each new language still needs a small
semantic profile for its branch, loop, case, exception, and termination constructs. All analyzers
report possible static paths; they do not claim runtime branch outcomes.
JavaScript, TypeScript, and C++. Python uses the standard-library AST. JavaScript, TypeScript, and
C++ use distinct optional Tree-sitter grammars with thin language-aware control-flow profiles.
Ordinary graph reads use stored projections and do not load or execute these parsers. A grammar
alone supplies syntax, not control-flow meaning, so each new language still needs a profile for
its branch, loop, case, exception, and termination constructs. All analyzers report possible
static paths; they do not claim runtime branch outcomes.
## Manifest and warm-parser scope
Parser work is language-specific and must be measured at the correct boundary:
- Python manifest construction fingerprints source and tokenizes local imports without calling
`ast.parse`.
- JavaScript and TypeScript manifest construction lexes static relative module specifiers without
invoking their distinct Tree-sitter extraction parsers. Focused tests prove this behavior on an
unchanged warm build.
- The C++ reference manifest parses inventoried sources with `tree-sitter-cpp` to discover quoted
include dependencies. A warm C++ extraction-cache hit is not a zero-parser claim.
The maintained Python benchmark instruments the unchanged warm path and requires zero
`ast.parse` calls and zero `extract_source` calls. That exact zero-parser benchmark claim is Python
only. JavaScript and TypeScript retain focused parser-free-manifest tests; C++ deliberately does
not.
## Full rebuilds

80
docs/LEGACY_AND_NO_AST.md Normal file
View file

@ -0,0 +1,80 @@
# Legacy adapters and no-AST policy
DocForge preserves the original one-method adapter contract while offering incremental extraction,
complete graph-plus-Logic assemblies, and an independently selectable no-AST MCP policy. These are
separate compatibility boundaries.
## One-method adapters remain valid
An existing adapter that implements only:
```python
def load_projection(self) -> AdapterProjection: ...
```
continues to work. It receives the same projection validation, indexing, querying, visualization,
and MCP behavior as before. It does not receive extraction-cache speedups and publishes no Logic
through the complete assembly contract.
Incremental adoption is additive. Implement `load_manifest()` and `extract_source()` while keeping
`load_projection()` as the independent complete graph oracle. If incremental contributions publish
Logic, also implement `load_complete_assembly()` so complete and incremental graph-plus-Logic
output can be compared exactly. The public authoring surface is `docforge.adapter_sdk`; see the
[Language Adapter Authoring Guide](ADAPTER_AUTHORING_GUIDE.md).
## What no-AST means
`--no-ast` is an immutable policy on one MCP process binding. It preserves the configured adapter
strategy but forbids AST, Tree-sitter, compiler-AST, and function-Logic evolution for that binding.
It also rejects nonempty Logic before index publication and rejects a pre-existing index that
contains Logic before reads or live visualization.
Start the generic server with:
```bash
docforge-mcp --project-root /absolute/path/to/project --no-ast
```
Project-owned integrations select the same policy with
`create_project_server(..., no_ast=True)` or `create_read_only_server(..., no_ast=True)`.
`docforge_bootstrap` and `docforge_get_contract` report the effective policy, and
`docforge_get_logic` returns `adapter_policy_forbids_logic`.
Changing this policy requires a new process. It does not rewrite or reload an adapter in place.
## What no-AST does not mean
No-AST is not:
- an inspection mechanism that proves which parser an arbitrary adapter uses;
- a filesystem, Python-import, or process sandbox;
- a promise that the adapter performs no non-AST fingerprinting, dependency discovery, caching, or
complete-projection loading; or
- permission to change project files, adapter code, or the configured capability surface.
Repository permissions and project instructions remain responsible for processes that have direct
filesystem access. The MCP boundary still excludes shell execution, arbitrary file operations,
Git mutation, builds, deployment, publication, project switching, and cross-project retrieval.
## Reference adapters are not no-AST adapters
The built-in Python reference extracts syntax and Logic with the standard-library AST. The
JavaScript, TypeScript, and C++ references extract syntax and Logic with their optional
Tree-sitter grammars. Their normal projections contain Logic, so they must not be presented or
configured as no-AST adapters.
The narrower performance statements in [Reference Adapters](REFERENCE_ADAPTERS.md) concern
specific manifest and warm-cache paths. A parser-free manifest does not turn an adapter that
publishes AST-derived Logic into a no-AST adapter.
## Migration choices
Keep a one-method adapter when its complete rebuild is acceptable and no function Logic is needed.
Adopt incremental extraction when measured source parsing dominates synchronization. Adopt the
complete assembly contract when Logic is published or overlapping raw evidence needs deterministic
ownership.
Choose no-AST only when the project binding must forbid AST-derived publication and future adapter
upgrades of that kind. It is a binding policy, not a substitute for adapter confinement,
conformance tests, restart detection, cache recovery, or the full proof matrix in
[Incremental Adapter Indexing](INCREMENTAL_INDEXING.md).

View file

@ -28,6 +28,30 @@ Bootstrap and contract results include `effective_policy` schema version 1 plus
registered surface and startup-bound proposal/application access. The project descriptor remains
schema version 1 and does not silently acquire machine-specific process policy.
## Fixed reference binding
`docforge.reference_mcp` is the fixed runnable binding for the in-repository Python, JavaScript,
TypeScript, and C++ reference adapters:
```bash
python -I -m docforge.reference_mcp \
--project-root /absolute/project \
--capability-mode read
```
It loads only `.docforge/reference-adapter.toml`, selects only the fixed provider for the declared
language, and constructs the same project-bound read surface through `create_read_only_server()`.
Bootstrap binding metadata includes `server_module = "docforge.reference_mcp"`,
`adapter_mode = "reference"`, the selected `reference_language`, and the exact
`reference_config_hash`.
The reference command accepts read capability mode only. It registers all 21 read tools below and
no isolated proposal or canonical-application tools. The configuration cannot select a provider,
module, command, arguments, working directory, environment, or arbitrary discovery behavior. See
[Reference Adapters](REFERENCE_ADAPTERS.md) for language scope and
[Agent Integration](AGENT_INTEGRATION.md) for the immutable launcher and generated client
fragments.
## Read tools
- `docforge_bootstrap`
@ -45,6 +69,8 @@ schema version 1 and does not silently acquire machine-specific process policy.
- `docforge_get_task_context`
- `docforge_validate_project`
- `docforge_render_status`
- `docforge_graph_plan`
- `docforge_graph_render_status`
- `docforge_visualize`
- `docforge_stop_visualization`
- `docforge_visualization_status`
@ -349,3 +375,6 @@ permissions and project instructions remain responsible for that broader boundar
The legacy `adapter_policy` object remains byte-compatible. It is now a projection of the
versioned `effective_policy`; `--no-ast` restrictively overrides adapter evolution, AST analysis,
and Logic indexing without widening any other capability.
See [Legacy Adapters and No-AST Policy](LEGACY_AND_NO_AST.md) for the compatibility boundary and
why the AST and Tree-sitter reference adapters are not no-AST adapters.

79
docs/MIGRATING_FROM_V1.md Normal file
View file

@ -0,0 +1,79 @@
# Migrating from DocForge v1
DocForge2 preserves the `docforge` distribution, Python package, CLI command, MCP tool prefix,
generic project descriptor version, and legacy adapter entry point. Migration is an additive
validation exercise, not a canonical-content rewrite.
Read [compatibility](COMPATIBILITY.md), [legacy and no-AST operation](LEGACY_AND_NO_AST.md), and
[recovery](RECOVERY_AND_PERFORMANCE.md) before changing a production binding.
## What remains compatible
- Generic Markdown and TOML projects keep `.docforge/project.toml` schema version 1.
- A custom adapter implementing only `load_projection()` remains valid.
- Existing canonical nodes, stable IDs, source paths, relationship vocabulary, changesets, and
reviewed hashes are not silently rewritten.
- Existing CLI and MCP names remain available. New fields, commands, tools, plans, policies, and
reference integrations are additive.
- `docforge-mcp --no-ast` remains the shorthand for the preserved no-AST binding.
Disposable index and cache schemas may change. Rebuild them rather than copying them as authority.
## Recommended migration
1. Record the v1 package version, adapter identity, project descriptor, canonical source hash,
active changesets, generated client configuration, and current rendered outputs.
2. Back up canonical sources and active proposal files. Derived `.docforge/cache`, preview,
portable-graph, and viewer state need not be authoritative backups.
3. Install the DocForge2 candidate in a separate environment. Do not repoint the production MCP
binding yet.
4. Run the existing adapter through complete loading and `ProjectIndex.check()`. For incremental
adapters, verify complete/incremental equivalence. Logic-producing incremental adapters must
implement the independent complete assembly oracle.
5. Compare project ID, adapter, revision, source hash, primary node and edge hashes, Logic policy,
and retrieval results with the v1 evidence.
6. Recompose effective capability and projection policy. Do not assume that a new default grants
proposal, application, rendering, or viewer access.
7. Rebuild disposable indexes, extraction caches, fragments, receipts, previews, and portable
artifacts from the validated candidate.
8. Generate a new client fragment. Generic projects use `docforge configure`; custom adapters use
`AdapterLauncherV1` with `generate_adapter_client_configuration()`.
9. Run doctor, real MCP bootstrap, representative retrieval, rendering status, recovery, and
exact-hash proposal/application checks in a disposable or shadow environment.
10. Repoint one production binding only after the candidate and rollback procedure pass.
## Adopting reference adapters
The repository reference adapters are narrow syntax evidence, not automatic semantic replacements
for mature v1 integrations. Python publishes modules, classes, functions, and local imports.
JavaScript and TypeScript publish syntax plus static project-relative imports and re-exports.
C++ uses a confined `compile_commands.json` as inert translation-unit inventory and publishes
directly resolvable project-local quoted includes.
Do not replace a compiler-, language-server-, or project-owned semantic adapter if the task depends
on resolved calls, types, inheritance, macros, compiler include semantics, runtime facts, or
ownership. Use [reference adapters](REFERENCE_ADAPTERS.md) as a measured starting point.
## Changesets
Existing changesets remain bound to their original project, root, base revision, source hash,
writer, operations, and exact hash. Do not edit a changeset to make it look current. Retrieve and
validate it through the candidate. Rebase only when every precondition remains true; otherwise
create and review a new proposal.
Before enabling canonical application, prove the exact serializer round trip and recovery behavior
for the selected adapter. The fixed reference MCP server is intentionally read-only.
## Rollback
Keep the v1 environment, configuration fragment, and service definition until the DocForge2
binding has passed live validation. To roll back:
1. Stop the DocForge2 MCP/viewer processes.
2. Restore the previous client or service binding.
3. Restore canonical sources only if a verified application receipt says they changed and the
adapter-specific rollback requires it.
4. Discard DocForge2-derived indexes, receipts, fragments, previews, and portable artifacts.
5. Restart v1 and verify its recorded project/source identity.
Never move a published tag or reuse a version identity for a corrected release.

View file

@ -0,0 +1,95 @@
# Milestone 4 baseline
## Scope and method
This baseline records the adapter SDK, Python reference adapter, incremental equivalence, and
recovery behavior completed in Milestone 4. It was captured on 2026-07-29 from clean executable
candidate `95271dcf2e48045b9d3aed9b9ea09c7fc155692c`.
The maintained command was:
```bash
.venv/bin/python tools/milestone4_benchmark.py \
--mode full \
--output benchmarks/milestone4-2026-07-29.json
```
The synthetic project contains 334 Python files. Each file contributes one module, one function,
and one argument node, for 1,002 primary nodes. Imports form a deterministic chain. Durations use
`time.perf_counter_ns()` and nearest-rank p95. Each ordinary result is serialized as compact sorted
JSON for its response-size gate. Per-operation memory uses `tracemalloc`; cumulative process
high-water uses `RUSAGE_SELF`.
Environment:
- Linux 7.1.3-200.nobara.fc44.x86_64 with glibc 2.43.
- CPython 3.14.6.
- x86_64.
- Three warm samples; cold, equivalence, and recovery operations run once.
- Per-operation traced-memory ceiling: 268,435,456 bytes.
- Process high-water ceiling: 536,870,912 bytes.
- Response ceiling: 524,288 bytes.
The complete machine-readable result is
[`benchmarks/milestone4-2026-07-29.json`](../benchmarks/milestone4-2026-07-29.json).
Its SHA-256 is `b6a871dde730a119fc0a138c47bd25f2c533ed3171a9a3d098075aef08173600`.
The stable evidence payload SHA-256 is
`4a8db461a311b5df4abd5aa00063e9a347d8b9dba19b30e35684f561d5271549`.
## Results
| Operation | Median | p95 | Limit | Traced peak | Response |
|---|---:|---:|---:|---:|---:|
| Cold incremental build | 1,599.760 ms | 1,599.760 ms | 20,000 ms | 68,573,540 B | 14,580 B |
| Warm incremental build | 1,244.427 ms | 1,265.387 ms | 20,000 ms | 69,997,166 B | 14,578 B |
| Complete/incremental equivalence | 1,897.919 ms | 1,897.919 ms | 30,000 ms | 64,305,919 B | 230 B |
| Corrupt extraction-cache recovery | 1,695.406 ms | 1,695.406 ms | 30,000 ms | 69,776,923 B | 14,580 B |
| Corrupt index recovery | 1,707.879 ms | 1,707.879 ms | 30,000 ms | 68,561,924 B | 14,779 B |
Process high-water was 78,798,848 bytes. The regression limits intentionally leave multiple times
the measured headroom; they are tripwires, not performance promises.
## Deterministic graph and Logic evidence
The candidate produced:
- 1,002 primary nodes with hash
`f7705dedf8dd388857a20d11f459dc74de797bcd12d3ed36e7f3aa75d67c328f`.
- 1,001 primary edges with hash
`00cc90998b6783afc8c9d1fd900409e5e3ba352868c4ba30e9bb2cbd59c52d35`.
- 334 Logic projections containing 2,338 Logic nodes and 2,338 Logic edges, with hash
`c86a3ae74777c2cec3a82c83e6e5bcca0196772ccea63cb13340fa9141593b0c`.
- Complete assembly hash
`2888182ab765fbffe3ba873c1613345640e8e6d89be74ddfca7452c0a5056345`.
- Source hash
`30ef23aa41061de2d4a7c995fe109d7a41518d9ee5493d805dd256581b47dae2`.
The independently loaded complete assembly and the incremental assembly matched exactly across
project identity, revision, source, nodes, edges, and Logic.
## Work and recovery gates
The warm build recorded all 334 sources as cache hits, zero reparsed sources, zero `ast.parse`
calls, and zero `extract_source` calls. Corrupting the extraction cache forced all sources through
extraction and reproduced the same graph and Logic hashes. Corrupting SQLite rebuilt the index
entirely from cache hits with zero parsing or extraction and reproduced those hashes.
This zero-parser evidence applies to the Python benchmark. Focused JavaScript and TypeScript tests
prove parser-free manifests. The C++ reference manifest uses Tree-sitter for bounded quoted-include
discovery and does not make a zero-warm-parser claim.
## Fresh-wheel and reference evidence
The offline adoption proof built and installed the base wheel without Tree-sitter packages,
built and checked a real Python reference project, started the isolated reference MCP server, and
performed bootstrap, search, and exact retrieval over its 21 read tools. Selecting C++ without its
extra failed with `optional_dependency_missing` and `docforge[cpp]` remediation.
Focused fixtures produced:
- Python: 14 nodes, 13 edges, 5 Logic projections.
- JavaScript: 14 nodes, 14 edges, 5 Logic projections.
- TypeScript: 13 nodes, 14 edges, 4 Logic projections.
- C++: 17 nodes, 16 edges, 5 Logic projections.
These fixture counts verify implementations; they are not language-wide completeness claims.

View file

@ -0,0 +1,83 @@
# Milestone 4 closeout
## Outcome
Milestone 4 is complete. New projects can adopt a public adapter SDK or one of four narrow
repository reference integrations, attach a fixed read-only MCP server, and follow maintained
product documentation without reading core implementation.
Implemented contracts:
- Stable `docforge.adapter_sdk` authoring imports.
- Independent complete primary-graph-plus-Logic oracle and exact incremental equivalence.
- Bounded adapter assemblies and version-1 extraction caches.
- Base Python, optional JavaScript, optional TypeScript, and optional C++ reference integrations.
- Closed `.docforge/reference-adapter.toml` and fixed `docforge.reference_mcp` read-only binding.
- Immutable, project-bound, launchable `AdapterLauncherV1` declarations and generated Codex,
Claude, and OpenClaw fragments for custom adapters.
- Live implementation-derived CLI and MCP reference tables with race-safe publication and drift
checking.
- Strict documentation graph, link, anchor, H1, reachability, required-page, generated-notice, and
documented-reference-config checks.
- Offline fresh-wheel adoption and maintained 1,002-node scale/recovery gates.
## Candidate evidence
The frozen executable candidate is
`95271dcf2e48045b9d3aed9b9ea09c7fc155692c`.
Its complete executable gate passed:
- Ruff formatting and Python lint.
- HTML, rendered-manual HTML, portable-graph HTML, CSS, and JavaScript checks.
- Pyright with zero diagnostics.
- Warning-strict compilation.
- 142 contract tests and 268 subtests.
- 347 complete tests and 402 subtests.
- Three Playwright and axe accessibility flows for the manual, portable graph, and live viewer.
- Lock and npm dependency-tree checks.
- Wheel and source-distribution builds.
- Offline fresh-wheel adoption.
- Milestone 0, 1, 2, 3, and 4 smoke benchmarks.
The clean full benchmark passed exact graph-plus-Logic equivalence, warm zero Python parser and
extraction work, corrupt-cache recovery, corrupt-index recovery, response, memory, and latency
gates. Exact results are in [the Milestone 4 baseline](MILESTONE_4_BASELINE.md) and
[`benchmarks/milestone4-2026-07-29.json`](../benchmarks/milestone4-2026-07-29.json).
Gitleaks 8.30.1 scanned the Milestone 4 commit range and candidate tree with no findings. The SSH
remote syntax prevents Gitleaks from constructing finding hyperlinks; it does not affect scanning.
## Reference scope
- Python uses the standard-library AST and publishes syntax plus local imports.
- JavaScript and TypeScript use distinct optional Tree-sitter grammars and publish syntax plus
project-local static relative imports and re-exports.
- C++ uses a confined `compile_commands.json` as inert translation-unit inventory and publishes
syntax plus directly resolvable project-local quoted includes.
- The references do not claim resolved calls, inheritance, types, symbol references, compiler
include semantics, macro semantics, runtime behavior, or semantic ownership.
The C++ reference never executes a compiler or compilation-database command. It is not a Clang
semantic adapter.
## Preserved boundaries
- The `docforge` distribution, Python package, CLI, MCP executable, and tool names remain.
- Generic projects and one-method `load_projection()` adapters remain supported.
- Descriptor schema version 1, index schema version 3, effective policy version 1, and no-AST
behavior remain.
- Heavy language frontends are optional. Base generic and Python operation installs no
Tree-sitter distribution.
- Reference MCP is read-only. Proposal and application remain explicit project-owned gates.
- No language adapter was separately published.
- No WorldForge, ScrapeStation, legacy-repository, production-binding, storage, or self-hosting
change was made.
- No tag or Forgejo release was created for Milestone 4.
## Later work
Milestone 5 owns stabilization and the first DocForge2 release. Release identity, compatibility
matrix, migration and recovery proofs, comparative real-task evidence, versioning, tagging, and
publication must be validated there. Remote adapters, render farms, third-party renderers,
cross-project graphs, storage replacement, and self-hosting remain deferred without measured need.

View file

@ -1,6 +1,276 @@
# DocForge setup moved to the user manual
# New-project quickstart
The complete installation, project setup, visualization, CLI, MCP, application, adapter, and
troubleshooting reference now lives in the [DocForge user manual](USER_MANUAL.md).
This guide takes a new installation from an empty project binding to a validated generic manual or
one of DocForge's fixed reference source graphs. For the complete operating reference, see the
[user manual](USER_MANUAL.md).
This file remains only so existing bookmarks and links continue to resolve.
## 1. Install DocForge
DocForge requires Python 3.12 or newer. A base installation includes the generic project service,
the public adapter SDK, the Python reference integration, CLI, MCP server, renderers, and viewer
assets. It does not install Tree-sitter.
From a source checkout:
```bash
git clone <repository-url> /absolute/path/DocForge
cd /absolute/path/DocForge
uv sync --group dev
DOCFORGE=/absolute/path/DocForge/.venv/bin/docforge
DOCFORGE_MCP=/absolute/path/DocForge/.venv/bin/docforge-mcp
DOCFORGE_PYTHON=/absolute/path/DocForge/.venv/bin/python
```
For an isolated consumer environment, install the checkout or a built wheel with `uv pip install`.
Add only the language extras that project needs:
```bash
uv venv /absolute/path/docforge-env --python 3.12
uv pip install --python /absolute/path/docforge-env/bin/python /absolute/path/DocForge
uv pip install --python /absolute/path/docforge-env/bin/python \
"/absolute/path/DocForge[javascript]"
uv pip install --python /absolute/path/docforge-env/bin/python \
"/absolute/path/DocForge[typescript]"
uv pip install --python /absolute/path/docforge-env/bin/python \
"/absolute/path/DocForge[cpp]"
```
The `languages` extra installs all three optional grammar families. JavaScript and TypeScript are
separate extras because they use distinct grammars. The C++ extra supplies a syntax grammar, not a
compiler or Clang semantic frontend.
Confirm the installation:
```bash
"$DOCFORGE" --help
"$DOCFORGE_MCP" --help
```
## 2. Choose a project route
Use a generic project when Markdown or TOML documentation is canonical. Use a fixed reference
adapter when you want a bounded source inventory and syntax-level graph for Python, JavaScript,
TypeScript, or C++. Use a project-owned adapter when the production contract must supply richer
semantics.
- Generic manual: continue with [Create a generic project](#create-a-generic-project).
- Fixed source example: continue with [Use a reference adapter](#use-a-reference-adapter).
- Production language frontend: follow the [Adapter authoring guide](ADAPTER_AUTHORING_GUIDE.md).
## Create a generic project
Set the absolute project root and assess it without writing:
```bash
PROJECT=/absolute/path/MyProject
"$DOCFORGE" --project-root "$PROJECT" onboard
```
The assessment reports detected languages, build evidence, documentation candidates, current
configuration, and available capabilities. Detection never invents a source graph.
Create a generic starter explicitly:
```bash
"$DOCFORGE" --project-root "$PROJECT" onboard \
--scaffold \
--project-id my-project \
--title "My Project"
```
Scaffolding is create-only and refuses existing target files. It creates:
- `.docforge/project.toml`, the generic project descriptor;
- `docs/docforge/content/architecture-overview.md`, one canonical node;
- `.docforge/templates/manual.html`, one built-in-renderer template;
- derived index, receipt, and rendered output below `.docforge`.
If a language was detected, source graph status remains `adapter_required`. The generic starter
does not claim source semantics.
Validate and inspect it:
```bash
"$DOCFORGE" --project-root "$PROJECT" validate
"$DOCFORGE" --project-root "$PROJECT" reindex
"$DOCFORGE" --project-root "$PROJECT" search architecture
"$DOCFORGE" --project-root "$PROJECT" show architecture.overview
"$DOCFORGE" --project-root "$PROJECT" render-status
```
The descriptor is explained field by field in [Project descriptor](PROJECT_DESCRIPTOR.md). Add
canonical nodes only after choosing their authority, stable IDs, families, statuses, and allowed
relationships; [Core concepts and authority](CORE_CONCEPTS_AND_AUTHORITY.md) defines those terms.
### Start a generic MCP binding
Start read-only first:
```bash
"$DOCFORGE_MCP" \
--project-root "$PROJECT" \
--capability-mode read
```
Call `docforge_bootstrap` before other tools. It reports the exact project identity, generation,
effective policy, projection policy, available capabilities, and recommended first read.
To enable proposals, the descriptor must declare the writer and the process must select it:
```bash
"$DOCFORGE_MCP" \
--project-root "$PROJECT" \
--capability-mode proposal \
--proposal-writer project-editor
```
Canonical application is a separate startup gate. Do not add it to a read-only client:
```bash
"$DOCFORGE_MCP" \
--project-root "$PROJECT" \
--capability-mode application \
--proposal-writer project-editor \
--canonical-applier project-editor
```
Application accepts one exact reviewed changeset hash. It does not commit, push, build, deploy, or
publish the project.
### Generate a generic client fragment
Preview is side-effect free:
```bash
"$DOCFORGE" configure codex --project "$PROJECT"
"$DOCFORGE" configure claude --project "$PROJECT"
"$DOCFORGE" configure openclaw --project "$PROJECT"
```
Add `--output /absolute/path/new-fragment` to create one new private standalone file. Generation
does not merge with or replace a different existing file. Diagnose an installed binding with:
```bash
"$DOCFORGE" doctor --client codex --project "$PROJECT"
```
Doctor is a bounded configuration inspector, not a connection test. See [Agent
integration](AGENT_INTEGRATION.md) for client-specific layouts and limitations.
## Use a reference adapter
Reference adapters read one fixed descriptor:
`.docforge/reference-adapter.toml`. The path is not selectable.
Create a Python project configuration:
```toml reference-adapter
schema_version = 1
project_id = "my-python-project"
title = "My Python Project"
language = "python"
source_roots = ["src"]
```
JavaScript uses `language = "javascript"` and the `javascript` extra. TypeScript uses
`language = "typescript"` and the `typescript` extra.
C++ additionally requires a confined compilation database:
```toml reference-adapter
schema_version = 1
project_id = "my-cpp-project"
title = "My C++ Project"
language = "cpp"
source_roots = ["src", "include"]
compilation_database = "compile_commands.json"
```
The C++ integration reads `compile_commands.json` only as bounded translation-unit inventory and
fingerprint evidence. It never executes a recorded command or compiler.
Run the fixed read-only server through the same installed Python interpreter:
```bash
"$DOCFORGE_PYTHON" -I -m docforge.reference_mcp \
--project-root "$PROJECT" \
--capability-mode read
```
The binding chooses one in-package provider from the descriptor language. It accepts no provider
module, command, argument list, working directory, environment, discovery rule, proposal writer,
or canonical applier. Its MCP surface is the read subset documented in the [generated command
reference](COMMAND_REFERENCE.md).
### Generate a reference-adapter client fragment
Generic `docforge configure` intentionally refuses custom adapters. Construct the adapter project
and immutable launcher, then call the custom-adapter generator:
```python
from pathlib import Path
from docforge.adapter_launcher import AdapterLauncherV1
from docforge.client_config import generate_adapter_client_configuration
from docforge.reference_mcp import REFERENCE_MCP_MODULE, create_reference_project
root = Path("/absolute/path/MyProject").resolve(strict=True)
project = create_reference_project(root)
launcher = AdapterLauncherV1.for_project(project, module=REFERENCE_MCP_MODULE)
preview = generate_adapter_client_configuration(
project,
launcher,
"codex",
capability_mode="read",
)
print(preview["artifact"]["content"])
```
Use `client="claude"` or `client="openclaw"` for those formats. Pass an absolute `output` path only
when creating a new standalone private fragment. The generated launch is bound to the selected
project, descriptor hash, adapter identity, installed module, isolated interpreter, policy, and
source availability evidence.
Read [Reference adapters](REFERENCE_ADAPTERS.md) before relying on the graph. The Python example
publishes local imports, the JavaScript and TypeScript examples publish project-local static
relative imports and re-exports, and the C++ example publishes directly resolvable project-local
quoted includes. None is a complete semantic compiler frontend.
## 3. Add visualization only when needed
Install the per-user viewer manager once:
```bash
/absolute/path/DocForge/.venv/bin/docforge-viewer-manager install-user-service
```
Then start a project-bound snapshot:
```bash
"$DOCFORGE" --project-root "$PROJECT" visualize
"$DOCFORGE" --project-root "$PROJECT" visualization-status
```
The listener is loopback-only and tokenized. The viewer is a derived snapshot, not canonical
authority and not a continuously monitored filesystem view. Read [Rendering and
visualization](RENDERING_AND_VISUALIZATION.md) for manual, portable, and live-viewer differences.
## 4. Verify the maintained checkout
Contributors can run:
```bash
make command-reference-check
make docs-check
make adoption-m4
make benchmark-m4-smoke
```
Run `make gate` before a release candidate. `benchmark-m4-smoke` is routine coverage;
`benchmark-m4` is the maintained full adapter workload.
Continue with [Project onboarding](PROJECT_ONBOARDING.md) for production integration,
[Policy precedence](POLICY_PRECEDENCE.md) before widening a binding, and [Security](SECURITY.md)
before exposing any MCP process.

202
docs/POLICY_PRECEDENCE.md Normal file
View file

@ -0,0 +1,202 @@
# Policy precedence
DocForge composes an immutable effective policy for each project-bound process. It resolves
restrictions in a fixed order:
```text
core safety
> explicit binding
> no-AST shorthand
> resource availability
```
A lower layer can make a requested operation unavailable; it cannot override a higher-layer
prohibition. Bootstrap and generated client evidence report the composed result and hashes so a
client does not need to infer policy from command-line arguments.
## 1. Core safety
Core safety is unconditional. No capability mode exposes:
- arbitrary file access or project switching;
- arbitrary renderer, module, command, shell, argument, working-directory, or environment
selection;
- Git mutation;
- project builds or compiler execution;
- deployment or publication.
Canonical application is limited to DocForge's validated serializer boundary. Adapter launch is
limited to the immutable launcher contract. Rendering is limited to declared views and fixed
built-in workers. An indexed instruction cannot alter any of these rules.
## 2. Explicit binding
A process is bound at startup to one project root, descriptor, adapter, capability mode, writer and
applier identities when present, no-AST selection, diagnostics selection, and projection modes.
The binding does not change during the process lifetime.
Capability modes are:
- `read`: register only the read surface;
- `proposal`: add proposal tools when a valid selected writer is available;
- `application`: require a startup-bound canonical applier and expose exact-hash application;
- `operator`: reserved; it currently adds no tools.
Mode describes the maximum registered surface. Actual authority can be narrower. A descriptor must
declare the selected writer, including allowed families and operation types. Application requires
the matching configured writer, changeset creator, and canonical-applier identity. A mode name
cannot create a missing descriptor grant.
Generic generated client fragments default to read mode. Other construction paths preserve their
documented compatible factory defaults. Treat `docforge_bootstrap.session_contract` and its actual
capabilities as authoritative for a running server.
## 3. No-AST shorthand
`--no-ast` is a restrictive compatibility shorthand. It composes:
- adapter evolution `preserve`;
- AST analysis `forbidden`;
- Logic indexing `off`;
- `docforge_get_logic` blocked;
- prohibitions on AST, Tree-sitter, compiler-AST, and function-Logic upgrades.
Non-AST source fingerprinting and incremental caching remain allowed. Existing one-method adapters
continue to use `load_projection()`.
The shorthand does not inspect how an existing adapter was implemented, sandbox its filesystem
reads, or transform an AST-based adapter into a no-AST adapter. Do not run the Python,
JavaScript/TypeScript, or C++ syntax reference integrations and then describe the binding as a
proved no-AST integration. See [Legacy and no-AST operation](LEGACY_AND_NO_AST.md).
## 4. Resource availability
Even an allowed policy cannot create a missing resource:
- application mode requires a startup-bound canonical applier;
- manual `explicit` or `auto` requires declared manual render configuration;
- manual `auto` also requires canonical application in the current operation or server;
- portable graph `explicit` requires declared portable graph configuration;
- live viewer `on-demand` requires the viewer runtime;
- an optional reference language requires its installed extra;
- a project-owned launcher module must be installed, isolated, project-owned, and unchanged.
Unavailable requested modes fail before hidden work. Missing optional language grammars return an
actionable install target such as `docforge[typescript]` or `docforge[cpp]`; DocForge does not
silently downgrade to a different frontend.
## Effective process policy
The version-1 effective policy reports:
- capability mode and whether it came from a factory default or explicit selection;
- adapter evolution, AST analysis, and Logic indexing;
- automatic synchronization and validated integrity;
- the compatible manual-render projection;
- profiling state;
- blocked tools and prohibitions;
- the exact precedence list.
This version-1 projection preserves compatibility. It is not the complete version-2 rendering
policy; portable graph and live-viewer choices are reported separately.
## Independent projection policy
Manual rendering, portable graph publication, and live visualization use a separate immutable
version-2 policy:
```text
manual: auto | explicit | disabled
portable_graph: explicit | disabled
live_viewer: on-demand | disabled
```
When a selector is omitted, composition uses availability-aware defaults:
- manual is `auto` only when manual configuration and canonical application are both available;
otherwise it is `explicit` when configured, or `disabled`;
- portable graph is `explicit` when configured, otherwise `disabled`;
- live viewer is `on-demand` when the runtime is available, otherwise `disabled`.
An explicit non-disabled selection for a missing resource returns
`projection_policy_unavailable`. An active operation prohibited by the selected policy returns
`projection_policy_forbids_operation` before planning, rendering, or viewer startup.
Status remains intentionally narrower than active work. Manual and portable receipt-only status
are available when their active operations are disabled. Viewer status and explicit stop remain
available when viewer start is disabled.
Ordinary standalone CLI rendering uses manual `explicit`. Manual `auto` belongs to a canonical
application operation that owns automatic regeneration.
## Diagnostics do not grant authority
`--diagnostics` enables bounded request-local stage timings and compiler-work counters. It does not
enable tools, broaden paths, retain project content, or displace a primary MCP result that already
needs the response budget.
## Descriptor policy and process policy
The project descriptor is canonical project configuration. The process policy is a runtime
restriction. They compose by intersection:
```text
operation is available
only if core permits it
and the startup binding registers it
and no-AST permits it
and required resources exist
and the descriptor grants the requested project authority
and current graph/hash preconditions validate
```
Changing a descriptor does not retarget a running project-owned process. Descriptor, adapter
implementation, or launcher drift requires a fresh process.
## Common decisions
For an agent that only reads documentation:
```bash
docforge-mcp \
--project-root /absolute/path/MyProject \
--capability-mode read
```
For an agent that may prepare reviewable proposals:
```bash
docforge-mcp \
--project-root /absolute/path/MyProject \
--capability-mode proposal \
--proposal-writer project-editor
```
For a tightly controlled application process:
```bash
docforge-mcp \
--project-root /absolute/path/MyProject \
--capability-mode application \
--proposal-writer project-editor \
--canonical-applier project-editor
```
For a read binding with every active output projection disabled:
```bash
docforge-mcp \
--project-root /absolute/path/MyProject \
--capability-mode read \
--manual-render-policy disabled \
--portable-graph-policy disabled \
--live-viewer-policy disabled
```
Prefer the narrowest binding that completes the workflow. Call `docforge_bootstrap` first and use
the returned effective policy, projection policy, actual capabilities, and prohibitions rather
than assumptions based on client configuration.
See the [MCP contract](MCP_CONTRACT.md), [Security](SECURITY.md), [Project
descriptor](PROJECT_DESCRIPTOR.md), and [Rendering and
visualization](RENDERING_AND_VISUALIZATION.md).

279
docs/PROJECT_DESCRIPTOR.md Normal file
View file

@ -0,0 +1,279 @@
# 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).

View file

@ -5,6 +5,11 @@ DocForge onboarding has two separate outcomes:
1. A generic manual can be configured, indexed, rendered, visualized, and exposed through the MCP.
2. A source graph additionally requires one validated language frontend per source language.
DocForge ships narrow fixed references for Python, JavaScript, TypeScript, and C++. They are useful
for syntax-scoped projects and adoption proof, but production semantic requirements may still
require a project-owned compiler or language-service adapter. Review
[Reference Adapters](REFERENCE_ADAPTERS.md) before selecting a frontend.
The onboarding command never claims that source semantics exist merely because it found source
files. It reports each detected language as `adapter_required` until a project integration supplies
and proves that frontend.
@ -64,6 +69,32 @@ immediately usable through the generic CLI, viewer, and MCP.
The starter overview records detected languages and states that the source graph is unavailable
until a language frontend passes the adapter proof. That limitation is deliberate.
## Configure a fixed reference adapter
The generic onboarding scaffold and fixed reference configuration are separate project routes.
For a syntax-scoped reference project, create `.docforge/reference-adapter.toml`:
```toml
schema_version = 1
project_id = "my-python-project"
title = "My Python project"
language = "python"
source_roots = ["src"]
```
Then start the fixed read-only binding:
```bash
python -I -m docforge.reference_mcp \
--project-root /absolute/path/MyProject \
--capability-mode read
```
The configuration selects only a fixed in-repository provider and cannot name a command or custom
module. JavaScript, TypeScript, and C++ require their respective optional extras; C++ also requires
`compilation_database = "compile_commands.json"`. The complete configuration and supported-fact
contract are in [Reference Adapters](REFERENCE_ADAPTERS.md).
## Complete onboarding checklist
### 1. Repository assessment
@ -118,6 +149,13 @@ For every source language:
- [ ] Define dependency discovery.
- [ ] State unsupported semantic facts explicitly.
Decide whether the project needs production semantic evidence or the narrower syntax-only
reference scope. The Python reference publishes only project-local imports. JavaScript and
TypeScript publish only project-local static relative imports and re-exports. The C++ reference
publishes only directly resolvable project-local quoted includes and does not run a compiler. None
of those references resolves calls, inheritance, types, symbols, runtime behavior, or semantic
ownership.
All frontends emit the same DocForge contracts:
- `AdapterManifest` inventories fingerprinted extraction units and dependencies.
@ -151,6 +189,11 @@ this checklist summarizes.
- [ ] Assign shared symbols to one deterministic source contribution.
- [ ] Record compiler-derived project include dependencies.
These are production semantic-adapter expectations. The built-in C++ reference uses
`compile_commands.json` only as bounded translation-unit inventory and fingerprint evidence. It
parses commands and arguments as inert data, executes no command or compiler, and does not claim
compiler include semantics, symbol ownership, or a Clang-derived graph.
#### Rust
- [ ] Read the Cargo workspace and package graph.
@ -240,6 +283,10 @@ from filenames.
Done when a new session can identify and retrieve the correct project without being told its file
layout.
The fixed reference server is read-only. Generic and custom-adapter client generation, including
the immutable custom launcher boundary, is documented in
[Agent Integration](AGENT_INTEGRATION.md).
### 10. Operating guide and maintenance
- [ ] Record the authority and progressive-reading order.
@ -286,14 +333,18 @@ to one configured project root.
DocForge does not let an MCP call install dependencies, run project builds, modify Git, deploy, or
publish. A project integration may use its own normal development workflow for those actions.
## Frontend packaging direction
## Reference and production frontend boundaries
Reusable language frontends should be separate packages or project-owned adapters over the public
DocForge contracts. They must not put language-specific rules into the graph, index, viewer, or MCP
core.
The public authoring namespace is `docforge.adapter_sdk`. Project-owned and separately distributed
production frontends should build on that contract without putting language-specific rules into
the graph, index, viewer, or MCP core.
Worldforge is the first complete C++ reference integration. A reusable C++ package should be
extracted only after that integration proves stable ownership, compiler dependency invalidation,
and complete/incremental equivalence. Rust and Java frontends should then implement the same
contract using their authoritative build and language tooling rather than copying C++ extraction
rules.
The in-repository Python, JavaScript, TypeScript, and C++ adapters are reference implementations.
Their syntax-scoped behavior is useful without becoming a claim that every project in those
languages has complete semantic coverage. A compiler-backed production C++ adapter may resolve
build flags, calls, types, inheritance, include semantics, and ownership when it can prove those
facts. The reference C++ adapter does none of that and is not a Clang semantic adapter.
New Rust, Java, or other frontends should use their authoritative build and language tooling and
must pass the graph-plus-Logic complete/incremental contract in the
[Language Adapter Authoring Guide](ADAPTER_AUTHORING_GUIDE.md).

View file

@ -0,0 +1,86 @@
# Recovery and performance
DocForge keeps canonical project evidence separate from disposable indexes, caches, receipts, and
rendered projections. Recovery rebuilds derived state from current authority; it does not rewrite
canonical content to make a cache look valid.
For the underlying boundaries, see [core authority](CORE_CONCEPTS_AND_AUTHORITY.md) and the
[security model](SECURITY.md).
## Recovery order
Use the narrowest verified recovery:
1. Run `docforge check` or the corresponding MCP status operation.
2. Run `docforge sync` to repair a missing or stale disposable index when safe.
3. Run `docforge reindex` for an explicit complete rebuild.
4. Restart a long-running MCP or viewer binding after descriptor or adapter implementation drift.
5. Regenerate a derived client fragment, command reference, preview, manual, or portable graph
from current project evidence.
Never delete or rewrite canonical sources, active changesets, or reviewed hashes as cache cleanup.
## Extraction-cache recovery
Incremental adapter contributions are stored in a version-1 disposable extraction cache. Reads are
no-follow, regular-file-only, identity-checked, bounded to 64,000,000 bytes and 10,000 source
records, and fail to a cache miss on malformed or incompatible data. Publication is atomic.
After a miss, current manifest sources are extracted again and the complete assembly is validated.
The active SQLite generation remains authoritative for reads until a new verified index is
published. An extraction cache may therefore be safely ahead of the last index; the two files do
not pretend to be one transaction.
## Index and receipt recovery
A validated SQLite index is a generation-pinned derived snapshot. Missing, corrupt, unattested, or
stale indexes rebuild from the complete project or adapter oracle. Generation receipts and render
receipts are post-publication evidence. Failure to write a receipt after a committed artifact is
reported as degraded committed success, not as permission to repeat a mutation.
The live viewer pins one validated index identity. Index replacement makes the running snapshot
stale and causes a later visualize request to start a fresh worker.
## Proposal and application recovery
Hash or base conflicts are not cache failures. Retrieve the current changeset and diff, then
review the new exact hash. Rebase is allowed only when every touched node, relationship, source,
permission, and graph invariant still matches. A content conflict requires a new proposal.
If a canonical serializer fails its round-trip check, use its reported rollback state. Do not
reapply a changeset whose application may already have committed. See
[migrating from v1](MIGRATING_FROM_V1.md) for rollback planning.
## Milestone 4 scale evidence
The maintained Python reference benchmark creates 334 Python source files and produces:
- 1,002 primary nodes and 1,001 primary edges.
- 334 Logic projections with 2,338 Logic nodes and 2,338 Logic edges.
- Exact complete/incremental primary graph and Logic equality.
- Exact output after corrupt extraction-cache recovery and corrupt-index recovery.
- Zero `ast.parse` calls and zero `extract_source` calls during a warm build.
On the frozen Milestone 4 candidate, operation p95 values were 1.266 to 1.898 seconds. Per-operation
traced peaks were about 61 to 67 MiB, and process high-water was 78,798,848 bytes. These are
regression measurements from one machine, not universal latency promises. The machine-readable
record is `../benchmarks/milestone4-2026-07-29.json`; detailed method and hashes are in
[the Milestone 4 baseline](MILESTONE_4_BASELINE.md).
The zero-parser claim is deliberately Python-only. Focused JavaScript and TypeScript tests prove
their manifests avoid Tree-sitter. The C++ reference manifest currently uses Tree-sitter while
discovering bounded quoted includes, so a warm C++ cache hit is not evidence of zero parser work.
## Maintained gates
Run:
```bash
make gate
make adoption-m4
make benchmark-m4-full
make docs-check
```
The main gate includes smoke benchmarks. Full milestone evidence is recorded separately from a
clean candidate so smoke or dirty-tree results cannot become release claims.

149
docs/REFERENCE_ADAPTERS.md Normal file
View file

@ -0,0 +1,149 @@
# Reference adapters
DocForge includes fixed, syntax-scoped reference adapters for Python, JavaScript, TypeScript, and
C++. They demonstrate the public adapter contract, deterministic complete and incremental
publication, function Logic, cache recovery, and a runnable read-only MCP binding. They are not
compiler or language-service replacements.
Use a reference adapter when its deliberately narrow graph is sufficient or when proving a fresh
DocForge integration. Use the [Language Adapter Authoring Guide](ADAPTER_AUTHORING_GUIDE.md) for a
production adapter that needs resolved symbols, calls, inheritance, types, build semantics, or
semantic ownership.
## Installation
The Python reference adapter uses the standard library and is available in the base wheel. The
other frontends are separate optional extras:
```bash
python -m pip install docforge
python -m pip install "docforge[javascript]"
python -m pip install "docforge[typescript]"
python -m pip install "docforge[cpp]"
```
Install `docforge[languages]` only when one environment intentionally needs all three Tree-sitter
frontends. JavaScript and TypeScript use distinct grammar packages and distinct extras.
## Fixed project configuration
The runnable reference binding reads exactly
`.docforge/reference-adapter.toml` below the selected project root. The configuration is data only:
it cannot name a provider, Python module, command, arguments, working directory, environment, or
discovery rule.
Reference adapter configuration `.docforge/reference-adapter.toml`:
```toml
schema_version = 1
project_id = "example-python"
title = "Example Python project"
language = "python"
source_roots = ["src"]
```
`language` is one of `python`, `javascript`, `typescript`, or `cpp`. Source roots must be sorted,
unique, non-overlapping project-relative directories outside `.docforge`. The configuration,
roots, and inventoried source files must be regular confined paths without symlink traversal. The
configuration is limited to 65,536 bytes, at most 64 source roots, and 65,536 examined inventory
entries.
C++ additionally requires one explicit project-confined compilation database:
```toml
schema_version = 1
project_id = "example-cpp"
title = "Example C++ project"
language = "cpp"
source_roots = ["include", "src"]
compilation_database = "compile_commands.json"
```
`compilation_database` is forbidden for the other three languages.
## Published scope
All four adapters publish deterministic file, module or translation-unit, class or struct, and
function nodes where their syntax supports those categories. They publish lexical `contains`
relationships, a narrow set of project-local `depends_on` relationships, and function-scoped
Logic. Calls that appear inside Logic are syntax steps, not resolved symbol relationships.
### Python
Python uses `ast` from the standard library for source extraction and Logic. Manifest construction
does not build a Python AST; it tokenizes imports only far enough to publish dependencies that
resolve to another module in the declared source inventory.
It does not import or execute project code. It does not resolve dynamic imports, calls,
inheritance, imported symbols, types, overloads, re-exports, decorators, metaclasses, descriptors,
or runtime-generated behavior.
### JavaScript and TypeScript
JavaScript uses the optional `tree-sitter-javascript` grammar. TypeScript uses the distinct
optional `tree-sitter-typescript` grammar. Their focused incremental tests prove that manifest
construction and an unchanged warm build do not invoke the Tree-sitter extraction parser.
Only static relative imports and re-exports that resolve to another inventoried source file become
dependencies. Dynamic `import()`, `require()`, bare package specifiers, aliases, `tsconfig` paths,
loader hooks, types, interfaces, overloads, calls, inheritance, symbols, and runtime behavior are
not resolved. The adapters never import, compile, transpile, or execute project code.
### C++
C++ treats `compile_commands.json` as the authoritative bounded translation-unit inventory and as
fingerprint evidence. Commands, arguments, directories, and output fields are parsed as inert
data. The adapter executes no compiler, build tool, command, project binary, or project code.
The reference C++ manifest uses the optional `tree-sitter-cpp` grammar to parse inventoried sources
and discover quoted includes. It publishes a dependency only when that quoted include resolves
directly to a real project-local header in the declared roots. Angle-bracket includes, compiler
include paths, frameworks, generated headers, compiler-provided headers, conditional compilation,
and macro expansion are omitted.
This is not a Clang semantic adapter. It does not claim resolved calls, types, templates, aliases,
concepts, references, inheritance, overload ownership, out-of-line semantic ownership, macro
semantics, or compiler include semantics. A warm C++ extraction-cache hit is therefore not evidence
that manifest construction performed zero parser work.
## Complete and incremental proof
Each reference adapter implements:
- `load_projection()` for the complete primary graph compatibility oracle;
- `load_complete_assembly()` for the complete graph-plus-Logic oracle;
- `load_manifest()` and `extract_source()` for incremental extraction; and
- deterministic assembly of the complete current contribution set.
The maintained fixtures prove complete determinism, exact complete/incremental graph and Logic
parity, reverse-dependency invalidation, additions and deletions, corrupt extraction-cache
recovery, confinement, and exact unsupported-fact inventories. See
[Incremental Adapter Indexing](INCREMENTAL_INDEXING.md) for the cache contract and
[Legacy Adapters and No-AST Policy](LEGACY_AND_NO_AST.md) for compatibility and policy limits.
Current fixture evidence is:
| Language | Primary nodes | Relationships | Logic projections |
|---|---:|---:|---:|
| Python | 14 | 13 | 5 |
| JavaScript | 14 | 14 | 5 |
| TypeScript | 13 | 14 | 4 |
| C++ | 17 | 16 | 5 |
These are regression-fixture shapes, not promises for arbitrary repositories.
## Read-only reference server
Start the fixed server with:
```bash
python -I -m docforge.reference_mcp \
--project-root /absolute/path/to/project \
--capability-mode read
```
The server selects one of the four in-repository providers solely from the validated fixed
configuration. Its cache stays below `.docforge/cache/reference-adapter/<language>`. It registers
the 21-tool read surface and no proposal or application tools. See
[Agent Integration](AGENT_INTEGRATION.md) for generated client fragments and
[MCP Boundary](MCP_CONTRACT.md) for the exact tool contract.

View file

@ -0,0 +1,233 @@
# Rendering and visualization
DocForge has three independent ways to present one validated graph generation:
```text
validated generation
├── manual plan → immutable package → detached HTML renderer → declared manual output
├── graph plan → immutable package → detached graph renderer → portable static artifact
└── pinned index → managed loopback viewer → interactive Nodes, Flow, Web, and lazy Logic
```
They share validated facts but not authority, publication, or lifecycle. A manual render does not
publish a portable graph. A portable graph does not start the live viewer. None is canonical
project content.
## Manual rendering
A generic descriptor may declare a manual template root, isolated preview root, and stable views:
```toml
[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"]
```
The renderer name is fixed to `generic_html`. Templates are confined UTF-8 assets with a fixed
token vocabulary. They cannot name commands, Python modules, executable renderers, or arbitrary
publication paths. Raw HTML in canonical Markdown is disabled by the pinned CommonMark path.
Plan and render identity covers the canonical source hash, optional changeset hash, selected nodes
and relationships, view configuration, template hash, renderer contract, and parser version.
Use:
```bash
docforge --project-root "$PROJECT" render-status
docforge --project-root "$PROJECT" render-status manual --deep
docforge --project-root "$PROJECT" render manual
docforge --project-root "$PROJECT" preview CHANGESET_ID manual
```
Normal status verifies bounded source, configuration, template, output, renderer, and receipt
identities without reconstructing output. `--deep` explicitly runs the side-effect-free full-render
oracle. A changeset preview writes only to the isolated preview root.
Manual `auto` means that a successful canonical application owns regeneration of every declared
manual view. It does not mean background rendering, and ordinary standalone CLI render cannot
select `auto`; use `explicit`.
## Portable graph rendering
Portable graph configuration is separate:
```toml
[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
```
A view selects exactly one exact `root` node or bounded metadata-only lexical `query`. Exact
filters and node, edge, depth, and work limits close the selection. The initial mode is `nodes`,
`flow`, or `web`. Portable graph contract version 1 excludes function-scoped Logic.
Use:
```bash
docforge --project-root "$PROJECT" graph-plan architecture
docforge --project-root "$PROJECT" graph-render architecture
docforge --project-root "$PROJECT" graph-render-status architecture
```
`graph-plan` validates and returns the generation-pinned plan without publication. `graph-render`
is an explicit local CLI publication action. It commits a content-addressed artifact, renderer
receipt, and bounded generation/view manifest in that order; the manifest is the publication
commit. `graph-render-status` checks only bounded committed evidence and never plans or renders.
MCP may plan and inspect portable graph evidence, but it does not publish the portable artifact.
Use the explicit project-local CLI command for publication.
Portable output is a complete static artifact. JavaScript is progressive enhancement, not a
requirement for the graph facts to be present.
## Live viewer
The live viewer is a managed, read-only browser over one generation-pinned validated SQLite file.
Install the per-user manager once:
```bash
docforge-viewer-manager install-user-service
```
Then operate a project viewer:
```bash
docforge --project-root "$PROJECT" visualize
docforge --project-root "$PROJECT" visualize --node architecture.overview
docforge --project-root "$PROJECT" visualize --query persistence
docforge --project-root "$PROJECT" visualization-status
docforge --project-root "$PROJECT" visualization-stop
```
`visualize` accepts only an optional stable node ID or lexical query, bounded traversal depth, and
the local `--no-open` presentation choice. It does not accept a project root override, database
path, SQL, template, command, renderer, bind address, or module.
The HTTP listener binds to `127.0.0.1` on an operating-system-selected port. A random token is part
of every accepted path. The server supports only `GET` and `HEAD`, sets no-store and restrictive
browser security headers, and has no write, project-selection, arbitrary-query, or static
filesystem endpoint.
The viewer is a snapshot. Index replacement or alteration makes that snapshot fail closed; start a
new visualization to use a new validated generation. Source inspection reads only project-confined
source evidence bound to the pinned generation.
## Nodes, Flow, Web, and Logic
The interactive viewer offers four complementary projections:
- **Nodes** shows a bounded relation-neutral incoming and outgoing neighborhood.
- **Flow** presents semantic contributors toward the focus. Prerequisite-style stored
relationships may be reversed for presentation without changing stored direction.
- **Web** expands the convergence picture with contributors, callers, containers, members, and
contextual relationships.
- **Logic** loads a function-scoped control-flow projection only when requested.
Logic supports the explicit control paths published by Python, JavaScript, TypeScript, and C++
integrations. It stays outside the primary graph, portable graph version 1, search, and
generation-diff receipts.
Hiding a node is presentation-only. In Flow and Web, ancestors without another path to the focus
are removed. In Logic, an omitted-path bridge preserves downstream readability. Restore reverses
the presentation change; neither action mutates the index.
## Immutable plan and worker boundary
Manual and graph plans are versioned, canonical JSON with deterministic ordering and fixed
structural and byte limits. They contain selected graph facts and bounded content, but no live
project object, SQLite handle, absolute project or index path, arbitrary query, command,
executable path, or caller-selected renderer module.
A projection package binds one plan to inert assets, fixed component versions, a closed built-in
renderer identity, and an exact artifact inventory. The detached worker:
- runs one fixed private module through isolated Python;
- uses a trusted working directory and sanitized environment;
- accepts one canonical newline-terminated JSON request;
- returns one bounded canonical JSON response;
- has a fixed renderer allowlist and timeout;
- cannot select graph facts, read project state, choose output paths, or mutate canonical files.
The package contract is bounded, and actual artifact transfer has a fixed 20,000,000-byte ceiling.
A larger descriptor `max_render_bytes` compatibility value does not widen that worker boundary.
## Manual fragment reuse
Manual fragments are disposable semantic cache records, not publication authority. A cold record
is accepted only after byte-exact comparison against a full detached render. On a warm hit, the
worker independently recomputes the expected fragment before reuse.
Corrupt, forged, stale, incompatible, individually oversized, or aggregate-oversized records fall
back to the complete full-render oracle. The current cache is bounded to 10,000 records and
64,000,000 bytes.
Fragment reuse is a correctness and recovery boundary. Do not promise a speedup without current
measurements.
## Projection policy
The version-2 projection policy is independent of capability mode:
```text
manual: auto | explicit | disabled
portable_graph: explicit | disabled
live_viewer: on-demand | disabled
```
Set global CLI flags before the subcommand:
```bash
docforge --project-root "$PROJECT" --manual-render-policy disabled render manual
docforge --project-root "$PROJECT" --portable-graph-policy disabled graph-plan architecture
docforge --project-root "$PROJECT" --live-viewer-policy disabled visualize
```
An active operation blocked by policy fails before hidden work. Receipt-only manual and portable
status remain available. Viewer status and explicit stop remain available when viewer startup is
disabled.
A non-disabled selection also requires its resource: declared manual configuration, declared
portable graph configuration, or viewer runtime. Manual `auto` additionally requires canonical
application. Read [Policy precedence](POLICY_PRECEDENCE.md) for exact composition.
## Failure and recovery
Render input drift detected before replacement fails without publishing a current receipt for stale
output. Portable graph publication uses content-addressed evidence and a final manifest commit.
Status never repairs implicitly.
Recovery is explicit:
- rerun a manual render from validated canonical state;
- use deep manual status for the full, side-effect-free equivalence oracle;
- rerun portable graph publication, or repair only from validated content-addressed evidence;
- stop and restart a stale live viewer against a current validated index.
Do not recover a derived output by editing its receipt or treating it as canonical. See [Recovery
and performance](RECOVERY_AND_PERFORMANCE.md), [Security](SECURITY.md), and the [Viewer
manager](VIEWER_MANAGER.md).

101
docs/SECURITY.md Normal file
View file

@ -0,0 +1,101 @@
# Security model
DocForge is a project-bound knowledge compiler. Its security boundary is an explicit project root,
closed configuration, bounded data, and exact identities. It is not a general process sandbox.
Start with [core authority](CORE_CONCEPTS_AND_AUTHORITY.md), then use
[policy precedence](POLICY_PRECEDENCE.md) to decide which capabilities a server should expose.
## Project and path confinement
Descriptors, reference-adapter configurations, canonical sources, authority files, templates,
changesets, caches, indexes, previews, and declared outputs are resolved against one project root.
DocForge rejects absolute paths where only project-relative paths are allowed, parent traversal,
symbolic-link escapes, unsafe file types, and protected-root overlap. Important reads use
no-follow file descriptors and compare file identity before and after reading.
Confinement protects DocForge operations. It does not stop another process with repository access
from changing files. Long-running bindings revalidate descriptor and adapter implementation
identity and require a restart after drift.
## Untrusted project content
Documentation, source text, templates, adapter metadata, compiler-database entries, and changeset
content are data. They cannot redefine policy or instruct DocForge to execute a command. Generic
manual rendering disables raw HTML, accepts a fixed template vocabulary, and rejects script-like
content. Portable graph and manual workers accept validated inert packages and fixed built-in
renderer identities.
The C++ reference adapter reads `compile_commands.json` only as bounded translation-unit inventory
and fingerprint evidence. It never executes the recorded command, compiler, response file, or
project program.
## Adapter launcher boundary
`AdapterLauncherV1` contains one Python module name and project identity. It contains no command,
shell string, arbitrary argument list, working directory, environment, discovery rule, or callable
selector.
Custom modules must be installed top-level modules. An isolated `python -I` probe resolves the
module without importing it and requires its regular-file origin to remain inside the bound
project. The sole trusted dotted exception is the packaged `docforge.reference_mcp` module. Client
fragments use the exact validated interpreter and canonical fixed arguments with an empty
environment.
This proves that the declared module is resolvable and project-bound. It does not make arbitrary
module code safe. Project owners remain responsible for the implementation they install.
## Mutation boundary
Normal MCP and the fixed reference server are read-only. Proposal tools exist only when a
startup-bound writer is authorized by the descriptor. Canonical application exists only when a
matching applier is explicitly configured.
Every proposal append, rebase, abandonment, and application is hash-bound. Application requires
the exact changeset hash that was reviewed. Source identity, content hashes, permissions,
conflicts, graph validity, and serializer round trips are checked before success. DocForge never
turns prose approval into a fuzzy merge.
## Derived state and publication
SQLite indexes, source-generation receipts, extraction caches, render fragments, previews, and
portable artifacts are disposable. Corrupt, stale, foreign, oversized, or mismatched derived
state is rejected or rebuilt from current project evidence.
Generated command-reference publication serializes cooperating writers and uses no-clobber or
compare-and-swap publication. A raced target is restored or retained for recovery instead of being
silently discarded. Projection publication records when an artifact was committed but later
receipt verification degraded, so a completed mutation is never reported as an ordinary failure.
## Limits and denial-of-service resistance
Inputs, results, traversal, context, changesets, renders, worker protocols, manifests, adapter
assemblies, and extraction caches have explicit count and byte limits. Incremental extraction
caches are capped at 10,000 sources and 64,000,000 bytes. Adapter primary nodes use the project
`max_nodes` limit; edges and Logic have deterministic multipliers over that limit.
Limits reduce accidental and adversarial amplification. An in-process adapter can still allocate
memory before returning data, so only trusted project-owned adapter code should run in the server
process.
## Secrets and network behavior
DocForge does not copy the parent environment into generated client fragments or detached
projection workers. Doctor checks never return environment values. The live viewer binds to
loopback, uses an unguessable URL token, supports read-only methods, and serves no arbitrary
filesystem tree.
Project secrets must not be placed in canonical documentation, adapter configuration, compiler
databases, templates, or changesets. Repository release gates include secret scanning, but that
scan is not a substitute for credential hygiene.
## No-AST boundary
`--no-ast` is a binding policy that preserves the selected adapter and prohibits Logic publication
and retrieval. It is not a parser detector, filesystem sandbox, or promise that unrelated
processes cannot parse source. See [legacy and no-AST operation](LEGACY_AND_NO_AST.md).
## Reporting and recovery
Do not bypass a confinement, identity, policy, hash, or limit error. Preserve the failing evidence,
stop the affected binding, and follow [recovery and performance](RECOVERY_AND_PERFORMANCE.md).

View file

@ -35,6 +35,8 @@ incremental methods while retaining the full loader as a fallback.
relationship keys, source inspection, branch-aware node hiding, panel resizing, zooming, and
managed idle shutdown.
- A generic Markdown/TOML adapter plus contracts for deterministic project-owned adapters.
- A public adapter SDK, optional Python/JavaScript/TypeScript/C++ reference integrations, and one
fixed read-only reference MCP binding.
DocForge does not run shell commands from documentation, mutate Git, build an application, deploy,
publish, choose a project globally, or cross project boundaries.
@ -100,6 +102,49 @@ uv run pytest -q
Use the executables under `/absolute/path/DocForge/.venv/bin/` when DocForge is not installed into
the active shell environment.
For an installed distribution, choose only the language extras the project needs:
```bash
python -m pip install docforge
python -m pip install 'docforge[javascript]'
python -m pip install 'docforge[typescript]'
python -m pip install 'docforge[cpp]'
```
The base wheel contains the Python reference adapter and no Tree-sitter distribution. JavaScript,
TypeScript, and C++ require their matching optional extras. `docforge[languages]` installs all
three optional frontend groups.
### Configure a reference source project
Reference adapters are a narrow alternative to the generic documentation descriptor. Create
`.docforge/reference-adapter.toml`:
```toml reference-adapter
schema_version = 1
project_id = "my-python-project"
title = "My Python Project"
language = "python"
source_roots = ["src"]
```
Start the fixed read-only server:
```bash
python -I -m docforge.reference_mcp \
--project-root /absolute/path/MyProject \
--capability-mode read
```
For C++, set `language = "cpp"` and add a project-relative
`compilation_database = "compile_commands.json"`. The database is inert bounded inventory; the
reference adapter does not execute its commands or compiler.
The reference integrations publish syntax and local static relationships only. They do not claim
resolved calls, types, inheritance, macro behavior, compiler include semantics, runtime behavior,
or semantic ownership. See [reference adapters](REFERENCE_ADAPTERS.md) for exact evidence and
limitations.
### Assess and onboard an unconfigured project
Run a read-only assessment before writing configuration:
@ -478,6 +523,10 @@ Every command emits deterministic JSON:
docforge --project-root /absolute/path/MyProject <command>
```
The sections below group common workflows. The implementation-derived list of all 28 current
commands, exact invocations, 36 generic MCP tools, arguments, and input-schema hashes is the
[generated command reference](COMMAND_REFERENCE.md).
### Project and index commands
```text
@ -590,6 +639,31 @@ declares the named writer, and application requires the same writer/applier iden
`--no-ast` to preserve the no-AST binding. Generic CLI generation refuses project-owned adapters
because it cannot safely reconstruct their composition.
A custom adapter owner supplies the already constructed project and immutable launcher through the
Python API:
```python
from docforge.adapter_launcher import AdapterLauncherV1
from docforge.client_config import generate_adapter_client_configuration
launcher = AdapterLauncherV1.for_project(
project,
module="my_project_docforge",
)
fragment = generate_adapter_client_configuration(
project,
launcher,
"codex",
capability_mode="read",
)
```
The top-level module must be installed for the exact isolated Python environment and resolve to a
regular file inside the project root. The fixed `docforge.reference_mcp` module is the only trusted
dotted exception. Generation probes resolution without importing the custom module, binds current
source availability and policy, and emits no arbitrary command, arguments, working directory, or
environment.
Select projection behavior independently:
```bash
@ -684,6 +758,10 @@ docforge-mcp \
Omit `--proposal-writer` when the MCP client should not create or append proposals.
For `.docforge/reference-adapter.toml`, use the fixed `docforge.reference_mcp` command shown in
[setup](#configure-a-reference-source-project). It exposes exactly the 21 read tools and never
registers proposal or application tools.
Select the session's declared surface explicitly when useful:
```bash
@ -986,6 +1064,28 @@ changes outside this process boundary.
## Troubleshooting
### `optional_dependency_missing`
Install the exact extra named in the error into the same Python environment that starts DocForge:
```bash
python -m pip install 'docforge[javascript]'
python -m pip install 'docforge[typescript]'
python -m pip install 'docforge[cpp]'
```
Do not install every frontend merely to suppress the check. A missing optional parser is a closed,
actionable capability error and does not affect base generic or Python reference operation.
### `adapter_launcher_unavailable` or `invalid_adapter_launcher`
Use one installed top-level Python module whose resolved regular-file origin is inside the project
root, or use the fixed `docforge.reference_mcp` binding. Arbitrary dotted modules, packages,
stdlib modules, missing modules, commands, argument strings, working directories, and environment
injection are rejected. Test the exact generated fragment rather than editing its command by hand.
See [agent integration](AGENT_INTEGRATION.md) and the [security model](SECURITY.md).
### `adapter_restart_required`
The project-local adapter code, its declared descriptor, or another implementation file changed
@ -1128,8 +1228,11 @@ make gate
Use `make benchmark` for the historical Milestone 0 baseline, `make benchmark-m1` for the
counter-gated warm-operation benchmark, `make benchmark-m2` for agent workflow gates, and
`make benchmark-m3-full` for the ten-sample 1,000-node projection, worker, fragment, status,
equivalence, response-size, and memory gates. `make accessibility` runs the generated manual,
portable graph, and live viewer axe and keyboard flows.
equivalence, response-size, and memory gates. `make benchmark-m4-full` runs the 1,002-node adapter
and recovery benchmark. `make adoption-m4` performs the offline fresh-wheel proof.
`make command-reference-check` rejects command-reference drift, and `make docs-check` validates
the maintained documentation graph. `make accessibility` runs the generated manual, portable
graph, and live viewer axe and keyboard flows.
Project-specific vocabulary, extraction rules, and serialization belong in the project adapter.
Generic core behavior must remain deterministic, project-bound, and recoverable.