# 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).