Skip to content

ID-68 Subtask {68.11} — Relocate dev-internal docs/ + .planning/ IP to a private docs repo before the public flip

ID-68 Subtask {68.11} — Relocate dev-internal docs/ + .planning/ IP to a private docs repo before the public flip

Section titled “ID-68 Subtask {68.11} — Relocate dev-internal docs/ + .planning/ IP to a private docs repo before the public flip”

Type: TM-shape subtask spec (planning artefact only — NO move executed here). Parent: Task ID-68 (repo visibility / IP separation). Proposed id: {68.11} (sits between {68.8} confirm-clean and {68.9} public flip). Authored: S293 (01/06/2026), read-only investigation. Worktree branched from canonical-pipeline-setup. Status: DRAFT — pending Liam ratification of the topology + split recommendation.


The knowledge-hub repo is being flipped PRIVATE→PUBLIC under ID-68 ({68.9} flip, {68.10} git filter-repo history purge). Two large dev-team-internal trees are git-TRACKED, not gitignored and would become world-readable on the flip:

  • git check-ignore docs → (nothing); git check-ignore .planning → (nothing). Only /docs/database/ is ignored (root .gitignore:112).
  • docs/ ≈ 28 MB across 15 subdirs (largest: specs/ 6.2M, reference/ 6.0M, workflow-evaluation/ 3.9M, themes/ 3.5M, continuation-prompts/ 2.1M).
  • .planning/ = 36 MB, 1,269 files (RESEARCH §5.1), two parts: .planning/codebase/ (7 live, cited architecture docs) + .planning/.archive/ (~1,260 historical files concentrating process IP + client-name leaks).

Corroboration this is internal-only: .github/workflows/onprem-deploy.yml:89 explicitly prunes docs/ + .planning/ from the cocoindex build context, commented “client IP”. The ID-68 RESEARCH already lists .planning/** as an §3.12 hard constraint that must be “excluded or relocated before any public flip” (§4.227), and docs/ client docs likewise (§4.50–51).

Decision (Liam): move dev-internal docs/ + .planning/ OUT into a SEPARATE PRIVATE repo (proposed knowledge-hub-docs-site) BEFORE {68.9}. This subtask specs that move cleanly, accounting for the in-repo SDLC workflow that mutates ledgers/specs constantly and the ID-9 Astro docs-site that builds FROM docs/.

This supersedes the PLAN’s AC9 / OQ-68-1-R2 deferral (“.planning/ untouched”). PLAN §“Architecture decisions” line 24 said .planning/ was deferred pending a live docs-site; Liam has now elected to relocate rather than defer. Record the AC9 override in the parent TECH acceptance contract when this lands.


Investigation findings (load-bearing — these constrain the design)

Section titled “Investigation findings (load-bearing — these constrain the design)”

F1 — ID-9 docs-site builds FROM docs/ via a relative source root (docs-site/ is in-repo)

Section titled “F1 — ID-9 docs-site builds FROM docs/ via a relative source root (docs-site/ is in-repo)”
  • docs-site/ is an Astro+Starlight project sibling to the app, deployed as a separate Vercel project knowledge-hub-docs (ID-9 TECH §2.2).
  • Content is synced, not authored in place: docs-site/sync-manifest.json has "source_root": "../docs" and maps five spaces (product-functionality, ontology, reference, runbooks, specsdecisions ratified-only) plus additional_source_dirs.reference = [design, generated]. The sync runs at build time: docs-site/package.json build = bun run sync && check-broken-links && check-token-parity && astro check && astro build; sync = bun scripts/sync-content.ts (docs-site/scripts/sync-content.ts, orchestrator; pure helpers in sync-content-lib.ts).
  • The manifest source_root: "../docs" is the single hard coupling. If docs/ moves and docs-site/ stays, the relative ../docs dangles → build fails at walk(spaceRoot) (sync-content.ts:106, “Fail loudly on missing source”).
  • The manifest already deny-lists the four ledger JSON + their MD mirrors + the reference/{tasks,roadmap,backlog}/ per-record dirs (sync-manifest.json deny_list). So the docs-site never publishes the ledgers today — confirming ledgers are NOT doc-site content; they are workflow state that happens to live under docs/reference/.
  • ID-9 TECH §2.1 rationale: “Mirrors Warp’s separation (their docs are a sibling repo; KH co-locates because the corpus is repo-internal).” That co-location premise inverts the moment the repo goes public — the corpus must now leave the public repo. The docs-site (which Liam also deems dev-internal) is the natural thing to co-locate WITH the relocated docs/ in the new private repo.

F2 — Reference-count of dangling docs/ + .planning/ paths (tracked files, outside those trees)

Section titled “F2 — Reference-count of dangling docs/ + .planning/ paths (tracked files, outside those trees)”

git grep -lF (with :(exclude) pathspecs for docs/**, .planning/**, node_modules, dist, lockfiles):

  • 715 tracked files reference docs/ (702) or .planning/ (34) — 21 overlap.
  • By extension: 406 .ts, 98 .md, 61 .py, 54 .sql, 49 .tsx, 13 .yml, 8 .yaml, 5 .sh, 5 .js, 4 .json, 2 .mjs, plus .gitignore (2), .prettierignore, .gcloudignore, .toml, .css, .svg, .example, .worktreeinclude.
  • Caveat — most are NON-load-bearing. The bulk of the 406 .ts + 54 .sql hits are doc-path mentions in code comments / spec-slice citations (Spec: docs/...), not runtime path resolution. The operationally load-bearing subset — things that read/write a real docs/ path or gate CI on one — is small and enumerated in F3–F5. The rewrite job is “repoint the load-bearing few + accept the comment-mentions dangle or batch-rewrite them cosmetically,” NOT “rewrite 715 files.”

F3 — Code/tests that RESOLVE real docs/reference/*.json ledger paths (the high-friction class)

Section titled “F3 — Code/tests that RESOLVE real docs/reference/*.json ledger paths (the high-friction class)”

These break (or write to the wrong place) if docs/reference/ moves:

  • Roundtrip guard tests (run on every vitest): __tests__/docs/backlog-schema-roundtrip.test.ts (BACKLOG_PATH = join(PROJECT_ROOT, 'docs/reference/product-backlog.json')), retro-ledger-roundtrip.test.ts, roadmap-roundtrip.test.ts, umbrellas-task-list-roundtrip.test.ts (readFileSync('docs/reference/task-list.json')).
  • Ledger CLI + scripts hard-coding the paths: scripts/ledger-cli.ts, scripts/ledger-normalise-oqls2.ts:36-38, scripts/ledger-sweep-s269.ts:38, scripts/backfill-capability-theme.ts:39-40, scripts/roadmap-from-json.ts, scripts/detect-roadmap-shipped-framings.ts:54, scripts/regen-mirrors.sh:77-79 (task-view --check), lib/validation/ledger-budgets.ts:89 (DISCIPLINE_DOC = 'docs/reference/task-list-discipline.md').
  • SDLC skills that READ/WRITE these constantly: write-product-spec, write-tech-spec (resolve N from docs/reference/task-list.json; write docs/specs/ID-N-.../), spec-driven-implementation, workflow-orchestration, start-session, implement-subtask (append <info added> to task-list.json details), update-roadmap-backlog, the evaluate-* skills (read product-retros.json).

Implication: the ledgers + specs are written by the orchestration loop on essentially every session. A submodule-with-separate-commits or a moved-out path would impose a second commit/push on every ledger mutation — unacceptable friction. The ledgers and active specs must stay in the main repo.

F4 — Docs-automation that targets the docs CORPUS (mostly docs-site, not docs/)

Section titled “F4 — Docs-automation that targets the docs CORPUS (mostly docs-site, not docs/)”
  • docubot (.github/workflows/docubot.yml, .github/actions/docubot/{action.yml,prompt.txt}, scripts/docubot/run-agent.ts): writes DIRECTLY to docs-site/src/content/docs/<space>/<file>.md (ID-9 TECH §2.4 Inv-30 direct-write path). It does not write docs/. It consults docs/reference/documentation-inventory.md (prompt.txt) to avoid recreating existing docs → that one read repoints with the inventory.
  • 5 ported docs skills + keep-docs-in-sync (scripts/skills/run-skill.ts): check-for-broken-links (--root docs-site/src/content/docs), missing-docs/audit_docs.py (--root <repo-root> --docs-root <dir>, targets the content tree + surface map), docs-seo-audit/audit_seo.py, review-docs-pr, sync-source-docs. sync-source-docs is the one that bridges code→docs-site: it writes docs-site/src/content/docs/reference/{schema-quick-reference,mcp-inventory, api-routes}.md from supabase/types, lib/mcp/, app/api/**. Its spec citations point at docs/specs/id-9-.../. These skills operate on docs-site/, so if docs-site moves WITH docs/, they move too and keep working; if docs-site stays, they keep working but their spec-citation comments dangle (cosmetic).

F5 — Config / CI / tooling that name docs/ or .planning/ (the exact load-bearing list)

Section titled “F5 — Config / CI / tooling that name docs/ or .planning/ (the exact load-bearing list)”
FileReferenceAction on move
.gcloudignore:18-19docs/, .planning/ excludesHarmless if dirs gone; leave or trim.
.github/workflows/onprem-deploy.yml:89prunes docs/,.planning/ “client IP”Harmless if gone; update comment.
.prettierignore:39,42,57.planning/, docs/, docs/reference/classification-prompt.mdKeep for whatever stays (ledgers/specs).
knip.config.ts:56-57docs/**, .planning/** ignore globsKeep for staying subset; trim moved globs.
tsconfig.json:46excludes docs-site (NOT docs/)Only changes if docs-site moves.
__tests__/validation/doc-freshness.test.tsreads docs/reference/data-entry-points.md, classification-architecture.md, docs/runbooks/taxonomy-change-runbook.md via PROJECT_ROOTThese runbook/reference MDs must STAY or the test repoints.
CLAUDE.md (root) + .ast-dataflow/CLAUDE.mddozens of docs/... + .planning/codebase/ pointers (ledgers, runbooks, design, codebase map)Repoint the moved pointers; keep the staying ones.
GitNexus + ccc indexesboth index .planning/ (RESEARCH §5.2; ~2,574 + index)Removing .planning/.archive/ shrinks recall — accept, or keep .planning/codebase/.

.worktreeinclude does NOT reference docs/.planning (only .env.local) — no change.


R-Topology: Plain separate private repo + SPLIT (NOT submodule, NOT subtree)

Section titled “R-Topology: Plain separate private repo + SPLIT (NOT submodule, NOT subtree)”

Create private repo knowledge-hub-docs-site and move into it the truly-internal, rarely-mutated-by-the-loop trees. Keep in the main repo the workflow-mutated ledgers and active specs (content-sanitised in place, not relocated). The new repo houses BOTH the relocated source content AND the docs-site/ Astro project, so the build’s ../docs coupling (F1) stays intact inside one repo.

Why not submodule / subtree:

  • Submodule: every ledger/spec write (F3 — multiple per session) would require a second commit in the submodule + a pointer-bump commit in the parent. The SDLC loop mutates task-list.json on essentially every subtask. Submodule friction on the ledger write path is the dominant cost and rules it out.
  • Subtree: avoids the pointer-bump but git subtree push/pull is error-prone for a high-churn directory and still splits history awkwardly; no real gain over a plain repo once the SPLIT removes the high-churn paths from the moved set.
  • Plain separate repo: lowest friction. The main repo retains only the things the loop writes; the moved trees are low-churn (archives, themes, evaluation runs, historical specs).

MOVES to knowledge-hub-docs-site (private):

  • docs-site/ (the Astro project itself — Liam deems it dev-internal; moving it keeps the ../docs source coupling in one repo).
  • docs/ dev-internal corpus: themes/, workflow-evaluation/, continuation-prompts/, audits/, handover-guides/, research/, operations/, product-functionality/, ontology/, design/, generated/, testing/ (note: testing/test-data/templates/ holds the CSP file {68.5} already removes — coordinate so it isn’t double-handled).
  • .planning/.archive/ (the ~1,260-file historical IP store — RESEARCH §5 R4 “archive externally”). This is the single biggest IP + client-name concentration.

STAYS in knowledge-hub (content-sanitised in place, NOT relocated):

  • docs/reference/*.json ledgers (task-list, product-backlog, product-roadmap, product-retros) + their MD mirrors + reference/{tasks,roadmap,backlog}/ — written by the loop (F3). Sanitise client identifiers in place (already largely done at HEAD per PLAN §17).
  • docs/specs/ active spec dirs (ID-N-.../) — written by spec-driven skills (F3). These are architecture decisions, low client-IP risk; sanitise rather than move.
  • docs/runbooks/ + the specific docs/reference/*.md that doc-freshness.test.ts and CLAUDE.md assert on (data-entry-points, classification-architecture, documentation-inventory, task-list-discipline, ai-visibility-policy, the {68.10} filter-repo runbook PLAN §85 leaves here). These are operational and loop-adjacent; keep + sanitise.
  • .planning/codebase/ (7 live architecture docs cited by CLAUDE.md + write-tech-spec — RESEARCH §5.1; removing them degrades agent grounding immediately).

This split removes ~95% of the IP surface and ALL client-name-concentrated history (.planning/.archive/) from the public repo while leaving the high-churn ledger/spec write path untouched.

Docs-site source after the split: in the new repo, sync-manifest.json source_root stays "../docs" ONLY IF the staying reference//runbooks//specs content is also mirrored/available to it. Two sub-options:

  • (a) Recommended: the docs-site syncs the STAYING reference/runbooks/specs from the PUBLIC repo via a read-only checkout (CI step git clone --depth 1 knowledge-hub into a sibling dir, point source_root at it) PLUS the moved product-functionality/ontology/ design/generated from its own repo. Manifest becomes multi-root.
  • (b) Simpler interim: copy the five published spaces into the new repo and let the public repo keep its own canonical copies; accept duplication of the published-only reference/runbook MDs (the ledgers were never published anyway). Lower engineering, mild drift risk — acceptable since docubot/sync-source-docs already manage drift. Recommend (b) for the first move, (a) as a follow-up if drift becomes painful.

R-Automation: reference-rewrite plan (concrete commands)

Section titled “R-Automation: reference-rewrite plan (concrete commands)”

The move splits cleanly by file type. ast-dataflow does NOT cover Python or SQL (per .ast-dataflow/CLAUDE.md), so combine tools:

  1. TS string-literal path refs (load-bearing resolution sites) — ast-dataflow CLI:
    bun scripts/ast-dataflow-cli.ts string-literal-uses "docs/reference/task-list.json"
    bun scripts/ast-dataflow-cli.ts string-literal-uses "docs/reference/product-backlog.json"
    bun scripts/ast-dataflow-cli.ts string-literal-uses "docs/" # broad sweep, triage
    Cross-check against F3’s enumerated list. For any that resolve a MOVED path, repoint.
  2. TS symbol/import graph (does any module import a moved TS helper under docs-site?) — GitNexus: mcp__gitnexus__impact({target:"syncContent", direction:"upstream"}) and mcp__gitnexus__query({query:"docs-site sync content build"}) to confirm docs-site has no inbound app/lib import edges (it is a separate package — expect none).
  3. .md / .py / .yml / .yaml / .json / .sh refs (NOT covered by ast-dataflow) — plain grep sweep, triage load-bearing vs cosmetic:
    git grep -nF 'docs/' -- '*.py' '*.yml' '*.yaml' '*.sh' '*.json' ':(exclude)docs/**'
    git grep -nF '.planning/' -- . ':(exclude).planning/**' ':(exclude)docs/**'
    Python load-bearing sites to check: audit_docs.py, check_links.py, audit_seo.py (these take --root/--docs-root args — verify defaults, repoint if they default to a moved path).
  4. Config files — hand-edit the F5 table list (.gcloudignore, onprem-deploy.yml comment, .prettierignore, knip.config.ts, tsconfig.json only if docs-site moves, CLAUDE.md ×2). These are few and exact.
  5. Verification: after rewrite, bun run test (catches the four roundtrip guards + doc-freshness.test.ts), bun lint, python3 -m pytest scripts/tests/, mcp__gitnexus__detect_changes() to confirm only expected symbols moved, and a docs-site build dry-run (cd docs-site && bun run build in the NEW repo).

This subtask MUST land before {68.9} flip and before {68.10} history purge, and after {68.8} (so the scrub/relocation of scripts/ literals is already done and the build context is confirmed clean). Insert as {68.11} with the dependency graph edge:

{68.8} confirm ghcr clean
└── {68.11} relocate dev-internal docs/+.planning/.archive/ to private repo (THIS)
└── {68.9} repo public flip + security suite (now also gated on {68.11})
└── {68.10} git filter-repo history purge (LAST)

History-purge interplay (critical): moving the trees at HEAD does NOT remove them from git history — every old commit still contains the client IP. Therefore {68.10}’s git filter-repo path set MUST be extended to also strip the relocated paths (docs/themes/, docs/workflow-evaluation/, .planning/.archive/, docs-site/, and the other MOVED subdirs from F’s MOVES list) from ALL history of the public repo. Add an explicit acceptance line to {68.10}: “fresh mirror clone shows zero blobs for the {68.11}-relocated path set across all history.” Without this, the move leaves the IP in history and the public flip still leaks it. The relocated content’s history can be preserved in the new private repo via git filter-repo --path <subdir> --path-rename extraction from a pre-purge mirror clone (do the extraction BEFORE {68.10} rewrites SHAs).

RiskLikelihoodImpactMitigation
docs-site build breaks (../docs source root dangles)High if naiveHighMove docs-site WITH the corpus into one repo (keeps relative coupling); OR repoint source_root + multi-root manifest (R-Split option a). Dry-run bun run build in new repo before flip.
Ledger-write friction (loop blocked / split-brain ledgers)High if ledgers movedCriticalDON’T move ledgers/specs — keep + sanitise in main repo (R-Split STAYS). Submodule explicitly rejected for this reason.
Dangling refs in skills → silent SDLC failuresMediumMediumEnumerate load-bearing sites (F3–F5); repoint via ast-dataflow + grep sweep; bun run test (4 roundtrip guards + doc-freshness) must pass before flip.
History purge omits relocated paths → IP still leaks post-flipMediumCriticalExtend {68.10} filter-repo path set to the {68.11} MOVES list; add the “zero blobs across all history” acceptance line; extract preserved history to new repo from a pre-purge mirror.
.planning/.archive/ removal degrades GitNexus/ccc recallMediumLow–MedKeep .planning/codebase/ (live map); accept archive-recall loss (RESEARCH §5.2 trade-off already framed); archive lives in new private repo, re-indexable there if needed.
AC9 override not recordedLowMedThis subtask supersedes PLAN AC9 / OQ-68-1-R2 deferral; record the override in parent TECH acceptance contract when {68.11} lands.

TM-shape record (for orchestrator to append to docs/reference/task-list.json)

Section titled “TM-shape record (for orchestrator to append to docs/reference/task-list.json)”
  • id: {68.11} (sequence: between {68.8} and {68.9})
  • description (≤250 chars): Relocate dev-internal docs/ corpus + .planning/.archive/ (and the docs-site/ Astro project) into a private knowledge-hub-docs-site repo before the public flip; KEEP + sanitise the workflow-mutated ledgers/specs/runbooks and .planning/codebase/ in the main repo. Repoint all load-bearing refs.
  • dependencies: {68.8} (sibling — confirm build context clean). Blocks {68.9}.
  • details: see this file (load-bearing F1–F5 findings, R-Split MOVES/STAYS lists, R-Automation commands, R-Sequencing edge, history-purge extension to {68.10}).
  • testStrategy: After rewrite, bun run test (4 ledger roundtrip guards + doc-freshness.test.ts must pass), bun lint, python3 -m pytest scripts/tests/, mcp__gitnexus__detect_changes() clean, and a docs-site bun run build dry-run in the new repo all green; git grep -F 'docs/<moved-subdir>' and '.planning/.archive' return no load-bearing resolution sites.

  1. OQ-68.11-1 — docs-site source after move: R-Split option (a) multi-root manifest syncing staying-reference from the public repo, or (b) interim duplication? (Spec recommends (b) first, (a) as follow-up.)
  2. OQ-68.11-2 — published reference/runbook MDs: these STAY (loop/test-adjacent) but are also published by the docs-site. Confirm duplication-with-drift-tooling is acceptable vs. a single-source multi-root sync.
  3. OQ-68.11-3 — .planning/codebase/: confirmed STAY (live, cited). Any of the 7 docs carry client IP needing sanitisation before the public flip?