Docs drift playbook¶
This playbook enumerates every documentation file in the bernstein repository, points each one at a concrete source of truth in code (or marks it static), and records the drift signal that should trigger a refresh.
Why this exists¶
Code and docs drift apart over time. This playbook gives future agents mechanically discoverable check-points so the gap can be detected and closed without re-reading every file. The companion CI workflow at .github/workflows/docs-drift.yml consumes this file via scripts/check_docs_drift.py and reports drift on every push to main and on every pull request.
How agents pick up drift¶
Convention: when you change code under src/bernstein/<package>/, also update the doc rows in the table below that name that package as their source of truth. The CI workflow will block merges to main on uncorrected drift; on pull requests it posts a non-blocking comment so the change can land in the same PR.
Drift remediation paths used by the rows below:
| Remediation token | What it runs |
|---|---|
agents-md-sync | uv run bernstein agents-md sync then uv run bernstein agents-md verify |
gen-agents-md | uv run python scripts/gen_agents_md.py --update (legacy detailed module map; do not run if agents-md-sync is the active source of truth) |
manual-prose | Re-read the source-of-truth module and adjust the prose by hand; no script generates this content |
manual-cmd | The doc lists a CLI command surface; re-run bernstein <cmd> --help and reconcile |
gen-benchmarks | uv run python scripts/generate_benchmark_docs.py |
static | No code source; check only repo URL / contact / license updates |
Doc inventory¶
Root (./)¶
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
README.md | src/bernstein/cli/main.py, src/bernstein/cli/commands/, src/bernstein/adapters/registry.py, pyproject.toml ([project.scripts], [project.optional-dependencies]) | New top-level command, adapter added or removed, install method changed, optional extra added | manual-prose |
AGENTS.md | src/bernstein/ package layout (auto-derived); curated content under .sdd/agents-md/ | New top-level package or module under src/bernstein/; any change to the canonical IR | agents-md-sync |
CLAUDE.md | Mirror of canonical IR via bernstein agents-md sync | Drift versus AGENTS.md canonical | agents-md-sync |
CONVENTIONS.md | Mirror of canonical IR for Aider via bernstein agents-md sync | Drift versus AGENTS.md canonical | agents-md-sync |
CODE_OF_CONDUCT.md | None (Contributor Covenant 2.1 verbatim) | Repo URL change, contact email change | static |
CONTRIBUTING.md | src/bernstein/adapters/registry.py, src/bernstein/adapters/base.py, templates/roles/, scripts/run_tests.py, .importlinter | New adapter contract method, new role added under templates/roles/, change to lint / type-check pipeline | manual-prose |
SECURITY.md | pyproject.toml version, security policy contacts | Disclosure policy changes, scope changes, new in-scope target | manual-prose |
CHANGELOG.md | Release-please managed (release-please-config.json, release-please-manifest.json) plus hand-curated ## Unreleased section | New release tag, manual entry needed for behaviour-visible code change | manual-prose |
CONTRIBUTORS.md | None (hand-curated list of named contributors) | New contributor merged a PR | static |
docs/ top-level¶
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
docs/index.md | All docs subdirs (each linked entry must resolve) | Linked page renamed / deleted, getting-started / installation / operations / reference / architecture path moved | manual-prose |
docs/adapter-deferred.md | src/bernstein/adapters/registry.py (negative-space: agents NOT integrated) | A previously-deferred agent now has a stable CLI binary | manual-prose |
docs/agents-md.md | src/bernstein/cli/commands/agents_md_cmd.py, src/bernstein/core/knowledge/agents_md_bridge.py, src/bernstein/core/knowledge/agents_md_generator.py | New target format added to the canonical IR, sync command options change | manual-prose |
docs/CHANGELOG.md | Mirror of root CHANGELOG.md for mkdocs | Root changelog edited | manual-prose |
docs/CODE_REVIEW.md | src/bernstein/core/quality/, src/bernstein/core/review/, src/bernstein/core/review_responder/ | Review pipeline stage added, reviewer-role policy change | manual-prose |
docs/ENTERPRISE.md | src/bernstein/core/compliance/, src/bernstein/core/security/, audit / lineage / air-gap surface | New regulator mapping, new compliance pack target, audit export schema change | manual-prose |
docs/lineage.md | src/bernstein/core/lineage/, src/bernstein/core/persistence/lineage.py, src/bernstein/cli/commands/lineage_cmd.py | Lineage record schema change, signature algorithm change, new verify CLI subcommand | manual-prose |
docs/llm-citation-surface.md | None (positioning note about how the project surfaces in LLM citations) | External citation pattern audited | static |
docs/routine-scenarios.md | src/bernstein/core/planning/routine_bridge.py, src/bernstein/cli/commands/routine_cmd.py, src/bernstein/mcp/routine_tools.py | Routine <-> Scenario bridge surface change | manual-prose |
docs/telemetry.md | src/bernstein/core/telemetry/, src/bernstein/cli/commands/telemetry_cmd.py | New telemetry event, opt-out matrix change, retention policy change | manual-prose |
docs/use-cases.md | src/bernstein/cli/main.py (command list) plus operator workflow CLIs | New operator workflow command (autofix, review-responder, dep-impact, etc.) | manual-prose |
docs/whats-new.md | Release tags, hand-curated highlights | New release published | manual-prose |
docs/concepts/¶
Each file in this folder pairs a Bernstein architectural concept with a concrete module path. The drift signal in every row is "the named source module has been moved, renamed, deleted, or its public surface changed."
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
abstracted-code-review.md | src/bernstein/core/quality/review_pipeline/abstract_diff.py, src/bernstein/core/review_responder/pr_gen.py | Diff abstraction class renamed, PR-generation signature change | manual-prose |
action-cache.md | src/bernstein/core/persistence/action_cache.py, src/bernstein/core/persistence/fingerprint.py, src/bernstein/cli/commands/cache_cmd.py | Cache record schema change, fingerprint inputs change | manual-prose |
agent-mode-profiles.md | src/bernstein/core/routing/mode_profile.py, src/bernstein/core/agents/spawner_prompt.py | New mode profile, profile selection input change | manual-prose |
artifact-lineage.md | src/bernstein/core/persistence/lineage.py, src/bernstein/cli/commands/lineage_cmd.py | Lineage record schema change | manual-prose |
ast-aware-chunking.md | src/bernstein/core/quality/review_pipeline/ast_chunker.py, src/bernstein/core/knowledge/ast_symbol_graph.py | Chunker public surface change | manual-prose |
best-of-n.md | src/bernstein/core/orchestration/best_of_n.py, src/bernstein/core/orchestration/tick_pipeline.py | New routing input, tick-pipeline hook change | manual-prose |
feature-contract.md | src/bernstein/core/planning/feature_contract.py, src/bernstein/core/security/audit.py, src/bernstein/core/quality/janitor.py | Feature-contract schema change, audit hook signature change | manual-prose |
fingerprint-memoization.md | src/bernstein/core/persistence/fingerprint.py | Memoization key inputs change | manual-prose |
jsonl-memory-log.md | src/bernstein/core/memory/jsonl_log.py, src/bernstein/core/memory/sqlite_store.py, src/bernstein/cli/commands/memory_cmd.py | Event schema change, store backend swap | manual-prose |
orchestrator-hardening.md | src/bernstein/core/orchestration/orchestrator.py, src/bernstein/core/orchestration/adaptive_parallelism.py, src/bernstein/core/orchestration/tick_budget.py, src/bernstein/core/cost/cost_tracker.py | New hardening primitive, budget knob added | manual-prose |
phase-pipeline.md | src/bernstein/core/orchestration/phase_pipeline.py, src/bernstein/core/planning/plan_loader.py, src/bernstein/core/routing/router.py | New phase, phase contract change | manual-prose |
sandbox-selector.md | src/bernstein/core/sandbox/selector.py, src/bernstein/core/sandbox/registry.py | New sandbox backend registered, selector heuristic change | manual-prose |
scaffold.md | src/bernstein/cli/commands/scaffold_cmd.py, src/bernstein/cli/scaffold/templates.py | New scaffold template, CLI flag change | manual-prose |
schema-validation-retry.md | src/bernstein/core/tasks/schema_retry.py | Retry policy change, schema validator change | manual-prose |
spec-as-test.md | src/bernstein/core/planning/spec_assertions.py, src/bernstein/core/orchestration/drain.py, src/bernstein/cli/run_cmd.py | Spec-assertion schema change, drain-hook signature change | manual-prose |
swarm-migration.md | src/bernstein/core/tasks/swarm_migration.py, src/bernstein/cli/commands/migrate_cmd.py | Migration entry point change, CLI flag change | manual-prose |
task-budgets.md | src/bernstein/core/cost/budget_countdown.py | Budget-format function renamed | manual-prose |
team-hub.md | src/bernstein/core/plugins_core/team_hub_loader.py, src/bernstein/core/plugins_core/team_hub_manifest.py | Manifest schema change | manual-prose |
wiki-build.md | src/bernstein/cli/commands/wiki_cmd.py, src/bernstein/core/knowledge/ast_symbol_graph.py, src/bernstein/core/knowledge/wiki_renderer.py | Wiki-build CLI flag change, renderer output schema change | manual-prose |
docs/gui/¶
Source of truth for this group is src/bernstein/gui/ (FastAPI + SPA), web/ (Vite + React + Tailwind), and the GUI mount logic.
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
index.md | src/bernstein/gui/__init__.py, src/bernstein/gui/cli.py, web/ | New tab added to the SPA, gui-meta route change | manual-prose |
install.md | src/bernstein/gui/cli.py, pyproject.toml [project.optional-dependencies] (gui extra) | New runtime dep in gui extra, install-time check change | manual-prose |
configuration.md | src/bernstein/core/security/auth_middleware.py, src/bernstein/cli/run_bootstrap.py, src/bernstein/adapters/mock.py, src/bernstein/core/fleet/ | Auth source change, idle-mode change, fleet wiring change | manual-prose |
screens.md | web/src/ SPA component tree | New per-task drawer tab, new top-level tab | manual-prose |
playground.md | src/bernstein/adapters/mock.py, src/bernstein/cli/run_bootstrap.py (--idle) | Mock-idle option change | manual-prose |
troubleshooting.md | src/bernstein/gui/cli.py (_check_gui_extras), static-assets gating | Error-message change in the extras check | manual-prose |
mobile.md | web/ responsive breakpoints | Layout breakpoint change | manual-prose |
docs/sdd/¶
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
ticket_schema.md | .sdd/backlog/ ticket files; ticket consumers under src/bernstein/core/planning/ | New required ticket field, label taxonomy change | manual-prose |
Benchmarks¶
| Doc | Source of truth | Drift signal | Remediation |
|---|---|---|---|
docs/benchmarks/BENCHMARKS.md | src/bernstein/benchmark/, scripts/generate_benchmark_docs.py, simulation harness inputs | New benchmark added, methodology change | gen-benchmarks (regenerate) or manual-prose |
Data-freshness drift (time-stamped metrics)¶
Some docs include time-stamped factual metrics (stars, downloads, dates) that grow stale even when the code does not change. These lines look like as of YYYY-MM-DD: ... or (YYYY-MM-DD) in a table heading. The drift gate here is purely temporal: the underlying numbers were correct on the recorded date and are expected to drift between scheduled refreshes.
To enumerate the known time-stamped lines, run:
The current inventory is:
| File | Stale-prone substring shape | Data source | Refresh command |
|---|---|---|---|
README.md (intro line) | as of YYYY-MM-DD: N stars, N forks, ~N pypi downloads/day (~Nk/month) | GitHub API, PyPI | gh api repos/sipyourdrink-ltd/bernstein --jq '{stargazers_count, forks_count}' and curl -sS https://pypistats.org/api/packages/bernstein/recent \| jq .data |
README.md (regulatory anchors) | ### regulatory anchors (as of YYYY-MM-DD) | Regulator publications | Manual review of cited regulations |
Refresh commands¶
For the README intro stats line:
gh api repos/sipyourdrink-ltd/bernstein --jq '{stargazers_count, forks_count}'
curl -sS https://pypistats.org/api/packages/bernstein/recent | jq .data
Staleness policy¶
- An
as of YYYY-MM-DDmarker older than 30 days is considered stale and emits a soft warning fromscripts/check_data_freshness.py. - A marker older than 60 days is a hard fail only on the weekly scheduled run of the
docs-data-freshnessjob. Instead of failing pushes tomain, the scheduled run opens or updates a single marker-tagged tracking issue so a stale marker never reddens unrelated main pushes. - Pushes to
mainstill run the checker in soft (non-strict) mode for the inline warning, but never fail on staleness. - The
docs-data-freshnesscheck is advisory and is not part of the canary list of required checks.
Cross-repo¶
The public website mirrors some docs pages.
Adding a new doc¶
When you add a new file under docs/ (or a new root-level doc):
- Add a row to the appropriate table above.
- Set the source-of-truth column to a concrete module path under
src/, or tostaticif there is no code source. - Update
scripts/check_docs_drift.pyonly if the new doc needs a custom check beyond "the named source-of-truth file still exists." The default path-existence check covers the common case. - If the new doc is operator-facing, link it from
docs/index.md.
Adding a new code module¶
When you add a new top-level package under src/bernstein/ or a new module that becomes a source of truth for a doc:
- Run
uv run bernstein agents-md syncso the canonical AGENTS.md / CLAUDE.md / CONVENTIONS.md /.goosehints/.cursor/rules/*.mdcpick up the new module entry. - Run
uv run bernstein agents-md verifyto confirm all five outputs agree. - If the new module has a concept-doc-worthy public surface, add a file under
docs/concepts/and a corresponding row in this playbook.