Skip to content

Implementation guide

Start with the development overview and testing guide. When supplied locally, also read AGENTS.md, the authoritative docs/prompts/MASTER_PROMPT.md and plans/IMPLEMENTATION_STATUS.md. Agent instructions and implementation records are ignored local working files. Tracked KNOWLEDGE.md maps describe the project for humans and agents, starting at the repository root and continuing through each folder.

Install with uv sync --locked --extra dev. Start uv run gravetrail-ui from the repo root (direct uv run streamlit run app.py remains available). Use uv run pytest -q and uv run ruff check . before each coherent feature commit. Installation, packaging and operations commands live in SETUP_AND_DEPLOYMENT.md. No manual database edits are needed for normal use. Runtime source and keys must never be committed. Use gravetrail --home PATH ... for independent CLI/MCP access only after stopping another owner of that home. Source launchers default to .dataforge/; installed UI/CLI/MCP launchers share the platform user-data default. Configure the home explicitly when switching owners.

Implement feature changes at their owning layer: parsers emit ParseResult, services persist and resolve, graph transforms view topology, UI presents it. Add a failing fixture reproducing the desired behavior, implement the smallest complete change, run the suite and update docs/status. Increment PARSER_VERSION whenever extraction semantics change to invalidate cached results.

Add new migrations in Store.__init__, preserving existing rows and original blobs. Use a transaction and a new migration version. Stop the app and back up the entire data home before deployment updates. Restore that backup with the prior code to roll back an incompatible migration.

Migration 3 adds history_archives without rewriting existing facts. History archiving journals the compressed recovery body in the same transaction as metadata removal, so recovery remains available if the filesystem publication is lost. Do not drop the journal when archived records are absent. Before downgrading, restore archived history with the current version, or restore the complete pre-upgrade data-home backup with its matching code. Recovery files alone are not a complete workspace backup. Automated tests simulate filename loss and restart, not physical power loss.

Do not put imported code, API keys, rendered trees or complete inventories in session state. Use file IDs and lazy source retrieval. New graph code must preserve evidence and cap work on cycles/dense graphs. New provider code must honor privacy before any network request.

Configuration transfer boundary

configuration.py owns WorkspaceIdentity, PortableSettings, WorkspaceConfiguration and the export/import functions. The v2 allowlist includes parsing, import limits, parse workers, graph limit and traversal depth. New portable fields require bounds/types and round-trip tests. Legacy v1 imports ignore unrecognized/private settings; v2 rejects unknown settings. Validate the complete document before creating the new workspace, then create identity/settings in one transaction. Never serialize arbitrary settings, provider endpoints, credentials or privacy gates. Restart Streamlit after changing cached Store class definitions during development. Also restart after editing registered custom-component JavaScript: hot reload can retain an older component asset even after Python re-registers the same name. Verify the running graph in a real browser; AppTest cannot prove component rendering.

VisualizationSettings validates display preferences; PortableSettings includes that same allowlist. Explorer passes one settings object to the live graph and offline HTML/JSON exports. The component mount root can be a ShadowRoot/DocumentFragment with no .style; style the contained graph element. Full SVG label fitting uses actual rendered text measurement. Before reusing asset/source selectbox keys, clear selections absent from the current choices. Key-based widget identity can otherwise preserve an ID from another workspace or filter.

assistance_queue.py owns current diagnostic eligibility, bounded context and request_review. Its request path passes expected_hash through assist_task into assist, which snapshots and checks the source before network use. Queue request button identity includes the preview hash so an old click cannot authorize a newly reanalyzed file. ui/assistance_result.py reads existing AI records by workspace/ID and owns shared explicit merge presentation. Keep future queues offline on render; do not use model confidence to clear deterministic diagnostics.

ui/themes.py owns eight named native presets (four palettes × dark/light), current detection and atomic theme-only configuration updates. Explicit presets use root theme.base and colors; dual appearance sections are consolidated so a dark choice applies even after browser Light selection. Non-color layout/sidebar overrides are retained. A bounded native fragment waits for st.get_option to reflect the configuration watcher before rerunning; no private theme API or injected CSS is used. Theme is app-wide deployment configuration, not workspace data. The local config must be writable. Installed/frozen builds copy the default into the writable data home; DATAFORGE_THEME_CONFIG selects that copy and leaves packaged assets unchanged. TOML Kit preserves custom TOML syntax/comments during updates (including multiline arrays): official API.

Maintenance and runtime configuration

connections.py owns RuntimeConnections, prefixed default provider key names and the context-local injection scope. A manager belongs to one browser session, never a global Streamlit cache. Pass secrets directly to adapters; do not mutate os.environ for session credentials. The explicit profile variable name can resolve a legacy startup value read-only. Only non-secret validated profile preferences persist; reject secret-bearing endpoints and redact provider responses/errors before durable storage. Keep imported original evidence exact.

cleanup.py owns preview/apply and category dependencies; database_maintenance.py owns integrity, snapshots and safe new-home restore. UI and AgentService delegate to these services. New cleanup categories require fixed selectors, dependent-record handling, active-operation guards, preview revalidation, scope permission and confirmation tests. Never add caller SQL or arbitrary cleanup paths. Whole-home actions must reject workspace-limited authorization. MCP cleanup tools remain read-only. Logical row estimates must not be described as disk savings.

Use isolated temporary homes for deletion/restore tests, including shared blobs across two workspaces, retained history, expired preview, active jobs and redirected paths. General cleanup does not acquire a journaled undo guarantee simply because the older history/staging helper does. Opt-in expired staging cleanup runs when its UI page is visited, not in a background scheduler. Migration 5 adds column transformation records; never delete schema as ordinary runtime reset. Register new column facts in cleanup dependencies, backup compatibility and integrity checks.

Packaging and release checks

launcher.py resolves checkout/packaged UI assets; build.py exposes gravetrail-build. Include new UI/static/prompt resources in the wheel and frozen build, and verify imports plus real startup outside the checkout. Source data belongs under the runtime home, not _app. Frozen --cli forwards to existing CLI/MCP code. Keep default data homes and installed theme copies compatible across upgrades.

Run uv run gravetrail-build --target python, --target docker or --target executable. Use --target all when all prerequisites exist. Run uv run gravetrail-build --verify, then smoke-test each artifact; checksum verification alone does not prove startup. Builds run on their target OS for native bundles and receive no provider credentials from the parent environment. Preserve the lock and version metadata, review output exclusions, record native platform/remote CI limits, and update folder knowledge and implementation status.