Forkable Docs (f05): Fork/Unfork/Update Workflow + DocRef/DocMap#170
Forkable Docs (f05): Fork/Unfork/Update Workflow + DocRef/DocMap#170jlevy wants to merge 41 commits into
Conversation
Add a docs-and-tests-first Phase 0 to the forkable-docs spec, per review: - Documentation Contract Changes: per-doc before/after map for every tbd doc (tbd-docs.md, tbd-design.md, README, development.md, docs-overview.md, skill-baseline, welcome-user) plus the new reference docs, playbook shortcut, and generated layout/index files. - Golden-Test Maps: a console-output style contract (icons, color roles, bracket state markers, stderr provenance, JSON/docmap shape) plus expected output for every new and changed command against one canonical fixture. - Resolve the tbd docs surface gap: explicitly re-home all four existing behaviors (bare manual, --section, --list, --all) and catalog the existing goldens that break (notably cli-help-all.tryscript.md), each as a bead blocked on its behavior's phase. - Add resolved decisions 16-17 and open question 3 (per-kind JSON shape); gate the new tbd status Docs line on forks existing so zero-fork output stays byte-identical. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Apply review decisions: - Rename the update strategy flag --rebase -> --keep-ours (--rebase collided with git-rebase's meaning); keep the historical references in Alternatives #7 and decision 5 accurate. - Forked-doc serve note is on by default (not gated behind --verbose); the extra context helps agents track customized docs. - docmap becomes the single data model for ALL doc output, no backward-compat carve-out: per-kind --list --json switches from a flat array to docmap; one shared renderer drives both text and JSON so they cannot drift. Resolves the former OQ3. - Make output consistency structural: a single rendering layer owns list/table/overview/marker formatting; the style contract is authoritative; pre-existing status/doctor drift tracked separately. - docref + docmap ship as standalone, dependency-free, fully-tested modules designed for later extraction into their own package. Adds resolved decisions 18-23, removes OQ3, updates the golden-test catalog (per-kind --list --json change) and Phase 1/2 module items. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Apply review decisions and rename the spec off "eject": - Drop the --relevant flag, repo auto-detection, and the hard-coded pack->doc map. Themes come from each doc's frontmatter `category` (retiring the brittle inferGuidelineCategory name-inference, which mis-files convex-rules and has no convex/electron theme). The agent forks the general theme plus the repo's languages/frameworks from a clear list; fork accepts names, --category, or --all. - Onboarding now presents two explicit axes: scope (all standard guidelines, recommended, or a theme subset) and visibility (hidden cache "magic" vs forked into docs/tbd/). Both make the same guidelines active; forking only adds visibility and customizability. - Handle out-of-band deletion of a forked file: serving falls back to upstream, status reports `missing` with restore/finalize options, and doctor --fix finalizes the unfork. Added a golden map and E2E coverage. - Rename the spec file eject-forkable-docs.md -> forkable-docs.md and remove lingering current-usage "eject" (the routing synonym); keep only the rename-rationale references in Alternatives #7 and decisions 1/2/14. - Resolved decisions 24-26; closed all open questions; corrected the Phase 1/2/4/5 beads (themes, shared renderer, deletion handling, two-axis onboarding, skill-baseline routing). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Rename the guideline-grouping concept from "theme(s)" to "category"/ "categories" throughout the spec. "Theme" was ambiguous (suggests UX appearance/styling), and "category" matches the existing `--category` flag and frontmatter field — one consistent term. Tidy the few spots that would otherwise read redundantly (e.g. "category (frontmatter category)"). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Add the two dependency-free, extraction-ready modules the forkable-docs plan (plan-2026-06-11-forkable-docs.md) builds on: - docref: the single URI-like address grammar for documents (internal:, local paths, URLs, and github:/gitlab:/git: with ref + path), plus normalization of github/gitlab blob and raw URLs to the canonical scheme. Parser, formatter, normalizer, and helpers with a spec-mirror test suite (23 tests). - docmap (docmap/0.1): a minimal document-inventory format (identity = type + name, location, presentation metadata) with create/parse/validate and query helpers. Unknown fields are preserved so producers can attach extension fields. 13 tests. Neither module imports tbd-internal code, per the standalone-module decision in the spec. Implements part of tbd-ljqx. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Add src/file/fork-manifest.ts — the committed fork state under .tbd/doc-forks/ (forks.yml manifest + base/<kind>/<name>.md snapshots) for forkable docs. - Pure functions: LF-normalized sha256 hashing, conflict-marker detection (requires all three standard markers so prose mentioning one isn't flagged), and computeForkStatus, a total function mapping base/file/cache hashes plus the conflicted flag to one of upstream/forked/customized/stale/conflicted/ local/missing/orphaned, with customized and stale as combinable modifiers. - Manifest helpers (find/upsert/remove) and a zod schema. - Filesystem I/O: read/write the manifest (yaml) and base snapshots. Full unit coverage including the table-driven state matrix (22 tests). Implements tbd-hgf3. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Add src/file/doc-fork.ts: the fork/unfork/status operations on top of the fork-manifest module, using the default docs/tbd/ fork dir laid out as <fork_dir>/<kind-dir>/<name>.md. - forkDoc: write the forked file + base snapshot + manifest entry; refuse to overwrite a target that exists and is not an unmodified fork (unless --force); re-forking an unmodified fork refreshes it and advances the base. - unforkDoc: remove file + base + entry; refuse to discard local customizations unless forced; clean up a missing-file entry without complaint. - forkStatusFor: compute live state by reading the forked file and base and comparing to current upstream content (incl. out-of-band deletion -> missing and source-gone -> orphaned). 9 tests. CLI wiring follows. Part of tbd-q04x. Note: this slice uses the default docs/tbd/ dir; configurable fork_dir and the f05 format bump are deferred to a finalization step (see tbd-z1b5). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Wire the forkable-docs CLI as subcommands of `tbd docs`. The existing manual viewer stays the default action, so `tbd docs`, `--list`, `--all`, and `<topic>` are unchanged (verified against the existing goldens). - tbd docs fork [names...] [--kind] [--all] [--force] [--dry-run]: resolve docs from the pristine cache and copy them into docs/tbd/<kind>/, recording base snapshots + manifest; overwrite refusal; cross-kind ambiguity needs --kind. - tbd docs unfork [names...] [--all] [--force]: remove forks; refuse to discard customizations without --force; clean up missing-file entries. - tbd docs status [--json]: per-doc state table, docmap --json, summary line. - Serving precedence: prepend the fork dir to guidelines/shortcut/template lookup paths so forked docs shadow the cache (missing dirs are skipped, so repos with no forks behave identically). Adds a 12-step fork-lifecycle e2e tryscript (fork -> status -> unfork -> out-of-band deletion -> upstream fallback -> missing). A parent/child --all collision is handled by reading merged options. Implements tbd-q04x, tbd-d31n, tbd-h7ft; partial tbd-i49m (status). Bare `tbd docs` stays the manual viewer for now; the overview reorg + f05 are deferred (tbd-z1b5). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Add the "keep forks current" loop the spec calls the most common lifecycle event. - src/file/fork-update.ts: a git merge-file wrapper (captures the conflict-count exit code, standard markers, no repo state touched) and updateOne — the per-state decision logic across default / --merge / --keep-ours (refresh unmodified, clean three-way merge, conflict skip+list, --merge markers + base advance + conflicted flag, --keep-ours keep+advance, missing-base repair, and orphaned/missing/not-stale skips). 14 unit tests cover the table. - tbd docs update [names...] [--merge|--keep-ours] [--dry-run]: iterate forks, read fork/base/upstream content, apply, advance bases, set/clear the conflicted flag, and print the applied + needs-decision summary. Adds cli-docs-update.tryscript.md (refresh, conflict skip, --merge markers, status conflicted, mutual exclusion). Implements tbd-jme1, tbd-f8bu. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
- diffContents: a git diff --no-index wrapper with clean labels (runs from a temp dir with --no-prefix so headers read "--- upstream / +++ ours", not temp paths). - tbd docs diff <name>: your file vs current upstream (the net fork); --base = your file vs its base (what you changed); --upstream = base vs current upstream (incoming changes). Errors clearly when a side is unavailable. Used by the upstream-contribution workflow. Adds diffContents unit tests. Implements tbd-pghj. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
tbd docs list [--kind]: lists docs across guideline/shortcut/template grouped by kind, with dim [forked] / [forked, customized] / [local] markers (matching the existing [shadowed] convention) and the standard two-line name + size / title: description format. --json emits a docmap. Forks are reflected because listing uses the serving lookup paths (fork dir prepended). Partial tbd-wzqp (list done; kind-agnostic show and the per-kind --list -> docmap renderer migration remain). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Land the forkable-docs format gate (plan-2026-06-11-forkable-docs.md): - f05 in FORMAT_HISTORY + migrate_f04_to_f05 (metadata-only stamp; fork artifacts appear lazily when `tbd docs fork` is first used). The history entry documents the revert recipe: restore .tbd/config.yml and delete $GIT_COMMON_DIR/tbd/layout.yml (it regenerates from the config). - New layout co-migration: an older-but-compatible layout.yml next to a newer-format config is the normal mid-migration state, not an error. isLayoutUpgradeable() + ensureCommonDirLayout re-stamp it in place (preserving created_at) under the shared lock, and the data-context probe routes that state through the locked ensure path instead of failing validation. This is what f03->f04 never needed (no layout predated f04) and what makes the f04->f05 upgrade a clean single command. - Old-client contract: 0.2.3-era clients refuse f05 repos with the standard upgrade message (verified against the published 0.2.3 binary). Tests: new f04->f05 migrate->revert->repeat e2e (two rounds + steady-state silence), read-path layout upgrade preserving created_at, metadata-only stamp unit test, old-client gate (isFormatCompatibleWithSupported), and all format-asserting goldens updated to f05. Full suite: 1262 vitest + 863 tryscript tests pass. Implements tbd-z1b5, tbd-ns1b. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
The publish step of the f05 bump: .tbd/config.yml stamped f05 (via the migration itself) and the generated agent surfaces regenerated with the format=f05 marker via `tbd setup --auto`. Verified the full loop on this repo before committing: migrate -> new build works (fork/unfork/status/list/sync) -> published tbd 0.2.3 refuses with the upgrade message -> revert (git checkout .tbd/config.yml + rm layout.yml) -> 0.2.3 works again -> re-migrate -> steady state silent. Note: f04-era clients (including npx get-tbd@0.2.3, which the session hook uses) will refuse this branch until upgraded - that is the gate working. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Address the "what if an upgrade goes wrong" review concern with a complete, tested abort story: - tbd-docs.md Troubleshooting gains "Aborting a Format Upgrade": a state inventory table (tracked vs machine-local vs never-touched), the abort recipe (git-restore tracked files + delete the machine-local layout.yml, which regenerates from the config), and safety notes. The nuclear option (deleting all of $GIT_COMMON_DIR/tbd/) is documented with its real caveat, verified live: recoverable for synced data, but --no-sync changes since the last sync live as uncommitted files in the worktree and would be lost - so the recipe deletes only layout.yml. - development.md cross-references the inventory, recipe, and tests. - Two new e2e tests pin the crash windows and the recipe: an interrupted upgrade (layout f05 + config f04, the data-command window) completes on the next command; the documented abort (restore config + rm layout.yml) restores the exact pre-upgrade state and re-upgrading from it works. The setup-path window (config f05 + layout f04) was already pinned by the read-path upgrade test, now annotated as such. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Both paths are now stated concisely where users and agents actually look:
- README Quick Start gains the third tell-your-agent prompt: upgrading an
existing installation ("upgrade tbd, run tbd setup --auto, and commit the
changes"), alongside the existing fresh-machine and fresh-cloud prompts.
- README gains an "Upgrading" section: the same two commands as install,
the automatic format migration + commit-the-diff guidance, what teammates
on older versions see, and the pointer to the abort recipe in tbd docs.
- skill-baseline.md (the injected agent skill) labels npm install as
install-or-upgrade, marks `tbd setup --auto` as the upgrade step that
applies format migrations, and tells agents what to do when they hit the
"requires a newer version of tbd" refusal. Installed SKILL.md surfaces
regenerated.
The upgrade flow was verified end-to-end on an f04 repo: setup --auto
migrates both stamps, prints the commit guidance, and refreshes surfaces.
https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
…tice) Systematic handling for everything a user or agent can do to docs/tbd/ after forking, per review. Two invariants make it predictable: names are identity (<kind>/<name>.md, flat — nested folders are not scanned), and tracking is derived from content hashes, so no git operation can desynchronize tbd from the folder. - Generated docs/tbd/README.md index (the spec'd orientation layer): what the folder is, the layout rules, one line per doc with its description; local files marked; regenerated on every fork/unfork/update; removed (and empty dirs pruned) when the last fork is removed, so unfork --all leaves the repo pristine. - tbd docs status now shows hand-authored `local` files (covers adds, the new half of a rename, and a deleted manifest wholesale) and prints restore/ finalize hints for `missing` docs - a rename reads as an explicit missing+local pair with resolutions. - tbd sync prints a one-line drift notice (stale / conflicted / missing counts) right after the doc-cache refresh - awareness for agents running routine syncs, with mutation still reserved to tbd docs update. - tbd-docs.md documents the full drift matrix (edit / delete / rename / add / subfolder-move / manifest-deletion / git operations) and the derived-tracking guarantee. Tested: 3 new unit tests (local listing ignores nested dirs; drift summary counts stale/missing/local; README generation + pruning) and 11 new tryscript golden steps (README content, rename pair legibility, [local] list marker, sync notice, fork-dir pruning). All verified live first. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
…sign.md First-principles review of the doc-synchronization design surfaced one real hole and produced the canonical design-doc specification: - Version-skew guard: within the f05 era, a teammate on an OLDER tbd sees a fork whose base was advanced by a newer tbd as "stale", and update would silently downgrade it to their older bundled content (and ping-pong the base across the team). The manifest's tbd_version field now does the job it was designed for: update refreshes it on every base advance and refuses (under every strategy) to touch a doc whose fork point is newer than the running tbd, with an upgrade message. Loose semver compare handles dev versions and never guards on unparseable versions. 6 new unit tests. - tbd-design.md gains the canonical model: §2.9 "Managed Docs: Copies, Forks, and Synchronization" (the four copies + manifest, the seven invariants incl. derived tracking and the format gate, the who-writes-what flow table, drift and degraded modes) and §4.13 "Docs Commands" (the command group, the three-sync taxonomy, update semantics). Directory-structure listings and development.md path conventions updated; ToC entries added. Also verified during review (no code change needed): an empty cache on a fresh clone self-heals via doc auto-sync before states are computed, so teammates see correct fork states immediately (tested live). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
Full review: correctness findings (3 release blockers), phase-completion audit, holistic docs review, fork/export and docmap-vs-search-path design analysis, and DocRef/DocMap abstraction review. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Address the Tier 1 + Tier 2 findings from the forkable-docs adversarial review, plus doc-accuracy corrections, each with unit/golden coverage. Code: - doctor (F1): an older-but-compatible common-dir layout is now a pending migration (warning, exit 0), not a mismatch error (was exit 1, which broke CI on un-migrated repos). `doctor --fix` routes through prepareDataSyncContext so it migrates BOTH config and layout (never a layout-only half-migration); a corrupt layout.yml is rewritten from config. - manifest (S1): serialize forks.yml read-modify-write under a shared lock so concurrent fork/unfork/update cannot drop entries to last-writer-wins. - manifest (S2/S8): validate doc names (isSafeDocName) and parse per-entry, dropping unsafe/malformed entries with a warning instead of aborting the whole read — a crafted name can no longer escape the fork dir, and one bad entry no longer takes down status/update for the rest. - merge (S5): LF-normalize all three inputs before git merge-file so a CRLF fork against an LF base/upstream does not report a spurious whole-file conflict. - conflicts (S7): detect *unresolved* conflicts by tbd's own marker labels, so a doc that legitimately contains conflict-marker examples is not stuck conflicted. - README injection (S6): sanitize fork names/paths written into the generated fork-dir README. - update (S3): surface skipped docs (conflicted/orphaned/missing/no-base/ newer-base) instead of silently swallowing them. - version-skew guard: skip a doc whose base was advanced by a newer tbd, under every strategy, until the client upgrades. - status hint (D3): point an empty repo at `tbd docs fork <name>` / `--all`. Docs: - tbd-docs.md abort recipe: warn that a concurrent tbd write re-stamps the layout and can undo an abort (revert config in step 1 before deleting the stamp in step 2); clarify that reverting config alone drops the format gate even when forks were already committed. - development.md: layout.yml mirrors the config's tbd_format (was a stale "f04"). https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
… (S6) The code fixes for these two findings shipped without dedicated unit tests: - S4: forkDoc refuses to refresh an unmodified fork when the fork point was set by a newer tbd (would silently downgrade), with --force as the escape hatch; and proceeds when the running tbd is the same or newer. - S6: a hand-authored local doc's frontmatter blurb and (unvalidated) filename are sanitized/encoded before going into the generated fork-dir README, so they cannot inject markdown, links, raw HTML, or a broken link target. https://claude.ai/code/session_01X8S12JzmmxEfLpYzgH8Y7E
… exits Two release blockers from the senior review (PR comment): - Forked shortcuts were never served: setup persists docs_cache.lookup_path into every repo config, and the shortcut command let it replace the fork-dir-prepended defaults, so a forked shortcut showed [forked] in 'tbd docs list' while 'tbd shortcut <name>' served the upstream copy. The fork dir is now prepended structurally (tbd-design.md 2.9 invariant 1); golden-tested by a new fork-a-shortcut serve block. Closes tbd-62qe. - git merge-file exits with the conflict count truncated to 127; error exits (255, e.g. binary input) were misread as conflict counts with empty stdout, which would overwrite a customized fork with empty content. Exits above 127 now reject with stderr context. Closes tbd-xbpe. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Per the DocRef review (epic tbd-3neh), tightening v0.1 before anything depends on it: - Drop the git: scheme: it had no hostname (unresolvable) and mis-parsed host-bearing forms. Additional protocols may be added in future versions. Closes tbd-s6tb. - Strict local paths: must be anchored with ./, ../, /, or a Windows drive letter; bare relative strings and ~ are rejected with actionable errors (consumers may coerce at their own boundary). Closes tbd-z9hs. - Windows drive-letter paths (C:/, C:\) parse as local instead of hitting the unknown-scheme rejection. Closes tbd-devl. - URL fragments are preserved through parsing and blob-URL normalization (new optional fragment field on git refs) instead of being silently dropped. Closes tbd-0n4l. - docRefsEqual documented as purely syntactic. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Per the DocMap review (epic tbd-ss3p): - Every entry must carry a location (path and/or source) — an inventory whose entries cannot be located is not an inventory. Closes tbd-qylm. - Drop word_count from the core schema; size metrics are producer extension fields (tbd renders bytes + approx tokens and never emitted it). Closes tbd-7mxx. - Readers accept docmap/0.* only and reject other majors with a clear unsupported-version error. Closes tbd-hayb. - tbd docs list --json now emits the provenance source docref for upstream entries (and a path for local files), so every entry is locatable and the output is consumable as a real inventory. Closes tbd-xnbl. - Path-relativity convention (relative to the docmap's own location) documented in the module; the docmap-format.md reference doc tracks the rest (tbd-arsr). Also in docs-fork.ts (epic tbd-5wv9 polish): - Validate --kind across fork/unfork/list/diff (closes tbd-00wl) - Error on unknown names in docs update instead of silently reporting up-to-date (closes tbd-fywy) - Clear the stored conflicted flag once markers are resolved, so the committed manifest matches computed state (closes tbd-y85r) - Actionable overwrite refusal naming diff/--force options (closes tbd-y1kp) - Reference-kind guard comment; CLI cannot create reference entries until Phase 5 (closes tbd-roz6) https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
- pathExists used readFile (full content read, then callers re-read) and swallowed non-ENOENT errors; now stat-based, propagating real failures. Closes tbd-znnn. - UpdateAction 'noop' was never returned; removed (with its one reference). Part of tbd-d9l0; the tryscript sed portability half lands with the docs commit. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
- Spec Alternatives: add the copy-all-and-gitignore fork-dir variant as considered and rejected (gitignored mirrors are invisible on GitHub/PRs; edits to them diverge silently; fork --all is the tracked all-visible posture). Closes the rationale-capture half of tbd-a3pj. - tbd-design.md 2.9: state the resolution principle — resolve by convention; track only what cannot be derived; publish the inventory as a generated view — with the gitignore-mirror rejection noted. Closes tbd-a3pj. - Spec update decision table: add the version-skew guard row and design point (update + re-fork refresh refuse when the fork point was set by a newer tbd), and note the tbd sync drift notice. Closes tbd-69g0. - Spec docmap section: location required per entry, word_count moved to extension fields, path-relativity convention; golden list --json block updated to match (upstream entries carry source). - Spec docref mentions: github:/gitlab: only, future-protocols note. - tbd-docs.md drift table cites tbd-design 2.9 as the canonical model (closes tbd-koe4); README Upgrading gains the forked-docs update line (closes tbd-jznb). - cli-docs-update.tryscript.md: portable perl -pi instead of GNU-only sed -i (closes tbd-d9l0 with the noop removal). https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…eal repo
Critical infrastructure fix (tbd-a1lc). Git exports GIT_DIR (plus GIT_PREFIX
etc.) into hook environments; pushing from a linked worktree therefore runs
the lefthook pre-push test suite with GIT_DIR pointing at the real repo's
gitdir. An absolute GIT_DIR overrides cwd-based repo discovery in every git
and tbd subprocess the tests spawn, so test fixtures executed their git
init/commit/branch and data-corruption scenarios against the REAL repository:
in the observed incident this rewrote local main to fixture commits, created
fixture branches, flipped core.bare, stamped the shared layout.yml, and
overwrote the live data-sync ids.yml with the corrupted-data test fixture
(merge-refs.test.ts and corrupted-data.test.ts identified as the writers).
Two independent layers, either of which closes the hole:
- tests/scrub-git-env.ts (wired via vitest setupFiles) deletes the git
location vars in every worker before any test spawns a subprocess, so the
pervasive { ...process.env } spawn pattern across ~60 test sites is safe
regardless of how the runner was invoked.
- scripts/scrub-git-env.mjs wraps every lefthook pre-push command, so no
current or future hook command (including non-vitest ones) inherits the
poisoned env.
Verified red/green against a sacrificial victim repo using the real suite:
pre-fix config + hook-style env mutates the victim's refs (reproducing the
incident exactly: ours/theirs fixture branches appear); fixed config leaves
the victim byte-identical. Wrapper verified to scrub, propagate exit codes,
and run the real hook commands.
https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Implements the spec's four-row disposition (closes tbd-g0hu), plus the
review's remaining UX beads, and reconciles the spec's golden maps with
shipped output (closes tbd-8gt5):
- Bare 'tbd docs' is the managed-docs status overview, presenting the three
postures — hidden / curated / everything (closes tbd-3h1s) — and degrades
to a manual pointer before init. The old [topic], --section, --list, and
--all viewer flags are retired.
- 'tbd docs show <name>' is the kind-agnostic reader: serves any doc through
fork-dir-precedence paths with a '(serving forked copy: ...)' stderr
provenance note (spec Decision 18 for show), and serves the bundled manual
as the reserved tbd-docs name with --section/--sections navigation.
'tbd docs manual [topic]' is its alias.
- 'tbd docs sync' is the canonical cache refresh (spec Phase 1 item 4);
'tbd sync --docs' stays as a deprecated alias rendering through one shared
module (docs-sync-output.ts), so the surfaces cannot drift. The fork drift
notice goes through the output layer, and 'docs update --json' carries
structured {name, message} entries (closes tbd-cab2).
- Unfork refusal now lists options (diff / --force), matching fork.
- Goldens rewritten: cli-help-all docs blocks, cli-doc-output sections
block, golden-output bare-overview snapshot (count masked), cli-setup
top-level help; manual's Documentation Commands section updated.
- Spec golden maps reconciled to shipped output with a shipped-vs-phase-
contract status convention; f05 documented as the stamp-only migration it
is, with fork_dir configurability and .tbd/README.md noted as in-era
additions (closes tbd-wngu).
https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…surface Closes the remaining tractable review beads: - One POSIX fork-dir constant (closes tbd-iqm1, with the tryscript-on-Windows half explicitly blocked upstream): FORK_DIR and its kind subdirs are POSIX literals (committed manifest paths and output must be platform-identical; fs access joins them), the duplicate DEFAULT_FORK_DIR is removed so tests exercise the production value, and a new cross-platform vitest e2e (fork-cross-platform-e2e.test.ts) pins POSIX manifest paths, fork-dir shadowing, show provenance, and unfork restoration on every CI OS — tryscript goldens cannot run on Windows because tryscript executes blocks via the platform shell (cmd), which is an upstream limitation. - Manifest source fields are validated as docrefs at read (the docref-everywhere rule now enforced somewhere real), and the docref and docmap formats ship as reference docs (references/docref-format.md, references/docmap-format.md) covering strictness, normalization, equality, purl prior art, location requirements, path relativity, version policy, and the view-not-input principle (closes tbd-vu3d, tbd-arsr; serving them via a reference kind remains Phase 5). - Agent surface: skill routing rows for list/fork/update/missing-file plus the docs command table entries (closes tbd-msh3), regenerated into all skill copies via setup --auto. - Manual: "Managing Docs: Two Modes" chapter (hidden cache vs forked, from first principles) with the four-row sync taxonomy table (closes tbd-j7im, tbd-low8); docs-overview.md rewritten for the tbd docs group (closes tbd-slcn). https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…tatus Docs line Phase 2/5 items from the forkable-docs spec: - reference kind is live (closes tbd-f233): references/ is scanned into the cache (copy-docs now bundles it), docref-format/docmap-format are served, and the self-docs tbd-docs + tbd-design are registered as reference docs under their existing names — served from the cache so forks shadow them, with a bundled fallback so they stay readable before init or first sync. fork/unfork/list/show/update all work on references; the doc-references extractor now validates `tbd docs show <name>` mentions and drops the stale `tbd reference` skip. - One data model, one renderer (tbd-wzqp): servedEntryFor() in lib/doc-serve.ts is the single point of docmap-entry construction; docs list, the bare overview (--json now emits the docmap itself), kind- agnostic show (--json emits entry + content), and the per-kind readers all build entries there. Per-kind --list --json switches from the flat array to the same docmap filtered to that kind (spec Decision 21); per-kind reads emit entry + score + content; per-kind text serving gains the '(serving forked copy: …)' stderr provenance note (Decision 18). cli-doc-output goldens rewritten per the spec's existing-goldens table. - tbd status gains a Docs drift line only when forks exist (tbd-i49m), computed in the async gather phase into StatusData so rendering stays pure; zero-fork output is byte-identical (orientation golden unchanged). https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Each guideline now carries exactly one category (general, typescript, python, convex, electron) in its YAML frontmatter, per the forkable-docs spec (Phase 4 item 15, tbd-jme1): categories live on the docs themselves, not in a central map. References already declared category: general and are unchanged. Adds tests/doc-categories.test.ts, which walks the source-level packages/tbd/docs/guidelines/*.md, parses frontmatter with gray-matter, and asserts every guideline declares exactly one category from the allowed set. The CLI-side category constant lands separately. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…ANGELOG Phase 5 docs items (17/19/20) plus contract-table rows for the forkable-docs feature (beads tbd-e12s, tbd-lxab, tbd-5bvs): - New suggest-upstream-improvements shortcut: the playbook for reviewing fork customizations (tbd docs status --json, tbd docs diff <name> --base), deciding what generalizes, filing upstream, and re-syncing via tbd docs update once merged. - welcome-user: the two-axis onboarding offer (scope + visibility) and a make-guidelines-visible routing row (tbd docs fork <name> / --all). - README: Forkable paragraph in Shortcuts/Guidelines/Templates, tbd docs as the managed-docs overview with the manual at tbd docs show tbd-docs, --add lines annotated as aliases for the upcoming tbd docs add <docref>. - development.md: .tbd/doc-forks/ tracking-state note (fork dir lives outside .tbd/, default docs/tbd/) and a Testing Forkable Docs pointer. - CHANGELOG: Unreleased (0.3.0) f05 entry per release-notes-guidelines. - tbd-docs.md Configuration Reference: docs_cache example with docref values, planned fork_dir/local_dirs keys, docref-format/docmap-format cross-links. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
… (tbd-5xt0) Append a fork-consistency check group to doctor's HEALTH CHECKS, per the forkable-docs spec (Phase 2 item 12, folding tbd-nt2c): - forks.yml totally unparseable: report ⚠ naming the file instead of crashing - missing forked files (deleted out-of-band): ⚠; --fix finalizes the unfork (removes manifest entry + base, regenerates the fork-dir README) and the doc is served from upstream again - orphaned entries (upstream/cache doc gone): ⚠; --fix removes entry + base, keeping the file as a local doc - base snapshot missing or hash-mismatched: ⚠ with re-fork/unfork remediation (no auto-fix - choosing would guess at user intent) - unresolved tbd conflict markers in forked files (flag-independent) - reserved tbd-* names claimed by non-manifest files in the fork dir - fork dir gitignored (git check-ignore, only when forks exist) Zero forks and no fork dir: the group contributes nothing, so doctor output is byte-identical for repos that never touched forking (golden-output and cli-setup goldens verified unchanged). All forks healthy: one line, '✓ Forked docs - N forked, base snapshots intact' plus the Fork dir ✓. KIND_CACHE_PATHS is replicated locally in doctor.ts rather than exported from doc-fork.ts (kept out of that module's API on purpose). E2e coverage in tests/doctor-fork-checks.test.ts against the built CLI: healthy headline, missing+--fix finalize, orphaned+--fix, conflict markers, reserved names, base tamper, gitignored fork dir, corrupt manifest, and zero-fork silence. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…bd-f8bu) Phase 4 item 16 of the forkable-docs spec, setup side: - tbd setup --auto now ends its summary with a Docs section before the completion banner. Zero forks: the three-posture menu (shared wording with the bare 'tbd docs' overview via new cli/lib/docs-menu.ts) under a 'Docs: N docs available in the cache' lead line. Forks present: one line with the fork count and, when any fork is stale, the pending upstream-update count and a 'tbd docs update' nudge. Reporting only - setup never writes the fork dir. Suppressed under --quiet/--json. - Remove the --interactive flag (never had prompts; agents are the operators). Commander option, help text, and doc comments dropped; 'tbd setup --interactive' now fails with unknown option. - Goldens: extend golden-output post-setup block and setup-flows with the zero-fork menu and fork/stale one-liners; drop --interactive from the pinned 'tbd setup --help' block in cli-setup-commands.tryscript.md. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Consolidates four parallel workstreams plus the mainline data-plane work; every numbered item in the spec's Phase 2-5 plan is now implemented, and the spec's golden maps carry captured output with no remaining phase-contract annotations for shipped behavior. Data plane (tbd-ohkj): - `tbd docs add <docref>` unifies the per-kind --add flags (kept as aliases): docref normalization replaces the ad-hoc blob-URL conversion, the canonical docref is what config records, git docrefs require an explicit @ref, and local docrefs work offline. `reference` joins the addable kinds. - `docs_cache.local_dirs` serves additional in-repo doc directories between the fork dir and the cache: first-class for list/show/per-kind reading with state `local` and a "(serving local doc: …)" note; not forkable (no upstream). Fixed the cache refresh dropping sibling docs_cache keys on config rewrite, and an effectiveServePaths double-sanitize that silently emptied the local-dir list. - Sync groups source entries per git repo+ref with per-group failure isolation (one unreachable source skips its own entries instead of timing out per file), one best-effort `git ls-remote` revision capture per group for provenance, and never prunes cached copies on fetch failure. Doctor (tbd-5xt0, folding tbd-nt2c): a "Forked docs" health-check group — corrupt forks.yml, missing forked files (--fix finalizes the unfork), orphaned entries (--fix cleans), base snapshot missing/mismatch, unresolved conflict markers, reserved tbd-* names, gitignored fork dir — silent for non-users, with 9 e2e tests. Categories (tbd-jme1): every bundled guideline declares exactly one category in frontmatter (enforced by a new unit test); the name-based inferGuidelineCategory inference is retired (the old 'testing' value errors clearly); `tbd docs fork --category=<name>` (repeatable) selects by the declared field, with the dry-run summary naming the categories. Setup (tbd-f8bu): the unused --interactive flag is removed everywhere (including the stale help epilog and its six pinned tryscripts); setup --auto prints the Docs summary — the shared three-posture menu (docs-menu.ts, also adopted by the bare overview, now including the --category line since the flag exists) for zero forks, and the pending-update one-liner when forks exist. Setup never writes the fork dir. Experience docs (tbd-lxab, tbd-e12s, tbd-5bvs): the suggest-upstream-improvements playbook shortcut; two-axis (scope × visibility) welcome-user onboarding; README forkable-docs paragraph and managed-docs pointers; development.md path-conventions and testing pointers; CHANGELOG f05 entry per release-notes-guidelines; tbd-docs.md Configuration Reference documents docrefs, shipped local_dirs, and the still-reserved fork_dir. Goldens: per-kind frontmatter block, category filter semantics, setup menus, overview snapshot, and the spec's golden maps recaptured from the built CLI (fork --category dry-run, docs add incl. the offline local form, status Docs line, setup summaries, doctor excerpts). isLayoutUpgradeable left as designed after re-verification against the H3 test (tbd-m72a: F3 verified safe earlier, S9 is correct behavior, F7 intentionally permissive for derived metadata). Full suite: 1,311 unit tests + 881 tryscript blocks green. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Applied automatically by the f05-capable CLI from PR #169 when bead operations ran here: the shared common-dir layout was already co-migrated to f05 (by the same CLI running in linked worktrees), and an f05 layout next to an f04 checkout config is the designed pending-migration state that resolves by stamping the config on the next write. The diff is the stamp itself, the new reference-kind and playbook entries merged into the docs_cache.files map, and the known cosmetic key reordering on migration write (noted on tbd-wngu). https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
…into claude/jolly-hawking-ttkxgw
Closes tbd-tgwi, the product-level half of the tbd-a1lc incident. Git exports GIT_DIR (and related location vars) into every hook environment, and an inherited absolute GIT_DIR overrides cwd-based discovery in every git child process — `-C <dir>` included. tbd's `.tbd` root discovery is a cwd-based filesystem walk, but its git context (notably resolveGitCommonDir, which locates the shared data-sync layout) came from git discovery, so a user running tbd inside any git hook got a split-brain: fs root from cwd, git common dir from the hook's repository — reading and writing the WRONG repo's tbd data. This is exactly the path that rewrote the real ids.yml during the incident. Policy now enforced at the spawn boundary: tbd always operates on the repository containing cwd. New src/lib/git-env.ts exports the git location variable list and gitSafeEnv(); every git subprocess tbd spawns uses it — the central git()/gitNoPrompt() helpers, resolveGitCommonDir (both the primary and fallback invocations — the actual misresolution site), fork-update's merge-file/diff, doc-sync's ls-remote, and uninstall's six execSync sites. When an ambient GIT_DIR/GIT_WORK_TREE was present, tbd prints a one-line stderr notice once per process so intentional GIT_DIR users learn it does not redirect tbd. tests/scrub-git-env.ts now imports the variable list from the product module (single source). Verified red/green with a new e2e (git-env-isolation-e2e.test.ts): with the scrub disabled, running the built CLI under a hostile GIT_DIR fails the isolation assertions; with it enabled, `tbd create` lands in the cwd repo, the GIT_DIR repo stays byte-identical (refs and shared tbd layout), the warning fires exactly once, and status/docs resolve the cwd repository. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| Secrets | Jun 13, 2026 1:41a.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
Unifies the two branch histories for PR #169 so the entire forkable-docs feature lives on a single stable branch. The session branch contributed no unique content (its config stamp was already byte-identical here); this merge exists so its history is an ancestor and the duplicate PR #170 can close cleanly. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
|
Combined into #169, which is the single stable PR branch for the entire forkable-docs feature. This branch ( https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh Generated by Claude Code |
…tation Delivers the complete forkable-docs workflow (format f05): the full design spec plus its implementation through all phases (0-5). - The tbd docs surface: overview, list, show/manual, sync, add, fork (by name / --all / --category), unfork, update (three-way merge), diff, status; per-kind --add kept as aliases. - Fork kernel: committed manifest + base snapshots, hash-derived states, POSIX paths, version-skew guards, out-of-band deletion handling. - docs_cache.local_dirs; one-docmap rendering across every doc surface; reference kind serving docref/docmap formats and the forkable self-docs. - tbd doctor fork checks with --fix; tbd status Docs line; setup Docs summary; --interactive removed. - Hardening: tbd ignores ambient GIT_DIR and operates on the cwd repo (safe inside git hooks); pre-push suite carries two scrub layers; cross-platform fork e2e on Windows CI. - Release readiness: v0.3.0 QA playbook for manual end-to-end testing. Spec epic tbd-67ek closed with all 12 phase beads; tbd-tgwi (ambient GIT_DIR) fixed here. CI green on ubuntu/macos/windows + coverage/lint + benchmark; 1,313 unit tests + 881 tryscript blocks. PR #170 combined in. https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh
Summary
Implements the forkable-docs kernel (f05 format migration) — a minimal, shadcn-style workflow that makes tbd-served docs (guidelines, shortcuts, templates, references) visible, customizable, and git-tracked in the repo. Users can fork bundled docs into
docs/tbd/, customize them, keep them in sync with upstream via three-way merge, and contribute improvements back.This is the Phase 1 kernel of the larger docs-config redesign (PR #117), implementable now as a metadata-only format bump with no breaking changes to existing workflows.
Key Changes
Core Forkable-Docs Infrastructure
fork-manifest.ts): Committed.tbd/doc-forks/directory stores fork provenance, content hashes, and merge bases — enabling cheap, exact, offline state computation and three-way mergingdoc-fork.ts):forkDoc(),unforkDoc(),forkStatusFor()handle copying docs into visible fork dir, removing forks with customization protection, and reporting fork state (clean/customized/stale/conflicted)fork-update.ts): Three-way merge viagit merge-filewith per-state decision logic (auto-merge clean changes, conflict markers for customized docs,--rebaseto keep local version)CLI Command Group:
tbd docstbd docs fork [name]— copy any bundled doc (or--all, or--relevantlanguage packs) intodocs/tbd/; forked copies shadow bundled versions in all lookupstbd docs unfork [name]— remove forked copy, refuse if customized unless--forcetbd docs update [name]— after tbd upgrade, pull upstream changes via three-way merge; auto-apply clean merges, list conflicts for manual resolutiontbd docs status— overview of fork state (clean/customized/stale/conflicted counts)tbd docs list/show— browse and read any doc by name (kind-agnostic)tbd docs sync— refresh the gitignored cache (canonical form of deprecatedtbd sync --docs)New Abstractions: DocRef & DocMap
docref.ts): Single-string, URI-like address grammar for any document (internal:guidelines/python-rules.md,./docs/local/,/abs/path/file.md). Standalone, dependency-free, designed to move to its own package. Used everywhere a doc source is named (config, fork manifest,tbd docs addargs)docmap.ts): Minimal, machine-readable document inventory (sitemap for docs). One entry per doc with identity (type + name), location (path/source docref), and metadata (title, description). Structured output fortbd docs list --jsonFormat Migration
.tbd/doc-forks/directory structure; existing f04 repos upgrade silently on firsttbd docs forkortbd doctorruntbd doctorgains "Forked docs" check group: validates manifest ↔ base snapshots ↔ fork dir consistency, detects conflict markers, enforces reservedtbd-names, reports corrupt manifests. Zero-fork repos see no new output (doctor output doesn't grow for non-fork users)Serving & Lookup
doc-serve.ts): Shared construction of docmap entries fortbd docs list/show, bare overview, and per-kind readers — one model, one renderer, no driftDocumentation & Testing
https://claude.ai/code/session_01QPsCSYGtwR8JtX2R1aaxyh