Skip to content

ID-114 {114.4} PLAN - relocate product functionality out of the private docs-site

ID-114 {114.4} PLAN - relocate product functionality out of the private docs-site (de-ID + re-home)

Section titled “ID-114 {114.4} PLAN - relocate product functionality out of the private docs-site (de-ID + re-home)”

Date: 16/06/2026 Author: Task Planner (FRESH dispatch - Q-PLANNER-2; read {114.1} RESEARCH + {114.2} PRODUCT + {114.3} TECH in full, NOT a prior-leg author). Tier: PLAN (decomposition leg of a PRODUCT+TECH+PLAN chain). Decomposes the ratified 13-PI-N spec pair into implementation Subtasks {114.5}-{114.13}. Predecessors (read in full this dispatch): {114.1} RESEARCH, {114.2} PRODUCT, {114.3} TECH (specs/id-114-docs-site-relocation/), all ratified. Baseline: HEAD 7fe481f74 on canonical-pipeline-setup.

Governing principle: “the private docs-site must host docs ONLY - no product functionality.”


Ratified revisions folded into this PLAN (these CORRECT the TECH PI prose)

Section titled “Ratified revisions folded into this PLAN (these CORRECT the TECH PI prose)”

The TECH was ratified WITH the following five orchestrator-supplied corrections. They override the TECH’s Proposed changes prose where they conflict; every Subtask below encodes them:

  1. PI-3 forward-ports NOTHING (delete-not-port). The TECH’s “diff each DS harness eval file against its MAIN twin and forward-port any genuine divergence” instruction is WRONG for eval-procurement-drafting.ts. Grounded this dispatch: the DS copy (harness/scripts/eval-procurement-drafting.ts, 486 L) queries the dropped bid_responses.cited_items column (L154-155) with a silent-swallow catch (L176) and reads db.cited_items ?? [] (L243). MAIN’s scripts/eval-procurement-drafting.ts (478 L) already carries the bl-215 fix: it queries form_responses selecting source_content_ids (L156-157) and fails loud (throw new Error(...), L161). The DS copy is a stale regression of an already-fixed bug. Forward-porting it would RE-INTRODUCE the bug. MAIN is canonical for EVERY runner; the action is DELETE the DS eval slice, port nothing. (The only divergence the TECH expected - the resolveEvalFixture import line - is moot, because every DS eval runner is deleted, not migrated.)

  2. PI-9 reconcile - “regenerate-from-DB” = a MAIN-homed materialised DB-derived taxonomy artefact, NOT a zero-intermediary runtime fetch. The codegen materialises the DB taxonomy into a MAIN-resident artefact wrapped in TAXONOMY_START/TAXONOMY_END markers so parseCanonicalTaxonomy()’s regex contract (matches #### N. DOMAIN + - `slug`: desc) is preserved with a one-line path change. The marker-wrapper is mandatory.

  3. OQ-E - the eval-fixture rewire (PI-5 step-2) is OWNED BY {71.14}, EXCLUDED from ID-114. The parallel id71-id104 (S364) track owns the resolveEvalFixture flip (moving summarisation + procurement-drafting from PRIVATE_FIXTURES to PUBLIC_FIXTURES) and the eval-fixture-sync.test.ts un-gating. ID-114 keeps ONLY PI-5 step-1 (add de-ID’d copies to MAIN, bridge route untouched) and step-3 (delete the DS originals, AFTER {71.14}‘s rewire lands). This PLAN does NOT include the lib/eval/fixtures.ts edit or the eval-fixture-sync.test.ts edit.

  4. OQ-C - use the EXISTING KH_CLIENT_NAME_DENYLIST secret. No net-new secret. The local consumers re-home to a MAIN-checkout-relative config path (or the secret-materialised file in CI); the existing secret remains the canonical store.

  5. Sequencing precondition (already satisfied). ID-114 impl runs AFTER the id71-id104 classify.ts rewrite is on the shared base. It already is at 7fe481f74, so PI-11 baseline-parity measures cleanly. No Subtask waits on it.


Context (code-intelligence orientation, cited verbatim this dispatch)

Section titled “Context (code-intelligence orientation, cited verbatim this dispatch)”

GitNexus is TS-only; flow.py/SQL surfaces were grep-swept (came back empty - confirming scripts/kb_pipeline/ is gone and no SQL references the bridge). Per the brief I did NOT run npx gitnexus analyze.

  • gitnexus_query({query: 'taxonomy generation eval runner fixture', repo: 'knowledge-hub'}) -> "processes": [] (no graphed runtime flow - matches PRODUCT’s build-time-only finding). Definitions returned the canonical eval surface: scripts/eval-runner.ts:runEvalTouchpoint (228-331) - ID-104’s central runner, already on the shared base; scripts/eval-classification.ts:classifyFixtureItem (411-564) + GoldItem (100-116); scripts/mcp-eval/seed-fixtures.ts (a DISTINCT module from lib/eval/fixtures.ts, confirming OQ-B’s no-collision finding). No taxonomy-codegen process is graphed (build-time codegen, not a runtime flow).
  • gitnexus_context({name: 'resolveEvalFixture', file_path: 'lib/eval/fixtures.ts', repo: 'knowledge-hub'}) -> incoming.calls: exactly FOUR - scripts/eval-{classification,entity-classification,procurement-drafting,summarisation}.ts:main; outgoing.calls: ONE - lib/private-docs.ts:resolvePrivateDocsDir. This is the precise blast radius of the PI-5 fixture path: four runner mains + the one bridge call. It confirms (a) the rewire surface is exactly the PUBLIC_FIXTURES/PRIVATE_FIXTURES maps in lib/eval/fixtures.ts (owned by {71.14} per OQ-E, NOT touched here), and (b) ID-114’s PI-5 step-1 only ADDS files under __tests__/fixtures/eval-gold/ - it does not touch resolveEvalFixture at all, so no caller is perturbed.

Grounded MAIN-side contracts this PLAN relies on (verified this dispatch):

  • lib/eval/fixtures.ts:70-85 - resolveEvalFixture; PUBLIC_FIXTURES = {classification, entity} (in-repo __tests__/fixtures/eval-gold/), PRIVATE_FIXTURES = {summarisation, procurement-drafting} (bridge route). ID-114 step-1 adds the two private gold standards to __tests__/fixtures/eval-gold/ WITHOUT moving them in the map (the map flip = {71.14}).
  • scripts/generate-classification-prompt-taxonomy.ts - PROMPT_PATH (L26-30) is the bridge to retire; fetchTaxonomy (L93, DB select), generateTaxonomySection (L143, emits #### N. DOMAIN + - `slug`: desc + ---), inject (L190, writes between markers). Keep fetch+render; change only the write target + drop resolvePrivateDocsDir.
  • scripts/lib/taxonomy-parser.ts:7-61 - parseCanonicalTaxonomy(filePath): regex over TAXONOMY_START..END, matches #### N. DOMAIN (L28) and - `slug`: desc (L46). This is the contract the materialised artefact MUST satisfy (revision 2).
  • scripts/bundle-plugin.ts:validate() - CANONICAL_PATH (the bridge) -> parseCanonicalTaxonomy(CANONICAL_PATH). Re-point CANONICAL_PATH at the MAIN artefact; drop the bridge.
  • Denylist consumers: scripts/sweep-identity-relocation.ts:180-219 (candidate-chain: --denylist flag -> KH_PRIVATE_DOCS_DIR/ops/identity-denylist.json -> sibling-checkout default), scripts/generate-purge-path-inventory.ts:349-380 (same chain), .claude/hooks/ip-leak-filename-guard.sh:23 (${KH_PRIVATE_DOCS_DIR}/.config/ip-denylist.txt, silent no-op if absent, L24).
  • scripts/check-token-parity.ts lives ONLY in DS (docs-site/scripts/, 8545 B; NOT in MAIN). Escape: resolve(here, '../../app/globals.css') (L172); DS mirror resolve(here, '../src/styles/warm-meridian.css') (L173).
  • DS harness eval slice present + confirmed stale: harness/scripts/eval-*.ts (5 runners), harness/__tests__/eval/*-eval.test.ts (5 suites), harness/__tests__/fixtures/* (gold
    • eval-baselines/), harness/lib/{anthropic.ts,ai/pricing.ts,eval/{types,reporter,baseline,metrics}.ts} (the fork - lacks ai/classify.ts, hence the latent --live break in the 2 classify-importing runners). DS root eval-fixtures/{procurement-drafting,summarisation}-eval-gold-standard.json present. MAIN already holds the canonical twins (__tests__/fixtures/eval-gold/, __tests__/fixtures/eval-baselines/, scripts/eval-*.ts, __tests__/eval/*).

Decomposition - 9 Subtasks {114.5}-{114.13}

Section titled “Decomposition - 9 Subtasks {114.5}-{114.13}”

Sibling-only deps. Cross-Task coordination (the {71.14} fixture flip) is a Task-level handshake, not a Subtask dep - no cross-Task Subtask dependency is created (the would-be dep is the reason PI-5 step-2 is excluded entirely). 9 Subtasks is well under the 25 soft ceiling. The two repos (MAIN-write vs DS-write) are kept in separate Subtasks so each lands as a coherent commit in its own repo.

SubtaskTitleRepoPIDeps
{114.5}de-ID worked-examples in classification-prompt.mdDSPI-8-
{114.6}codegen regenerate-from-DB to MAIN artefact + retire bridgeMAINPI-9/10-
{114.7}delete DS harness eval slice (delete-not-port)DSPI-3/4-
{114.8}add de-ID’d procurement/summarisation fixtures to MAINMAINPI-5 step-1-
{114.9}re-home denylist local consumers off the bridgeMAINPI-6-
{114.10}re-home check-token-parity coupling (drop parent-dir escape)DSPI-7-
{114.11}wire DS root __tests__/ into DS CIDSPI-12{114.7}
{114.12}delete DS root eval-fixtures originals (after {71.14} rewire)DSPI-5 step-3{114.8}
{114.13}PI-1/2/11/13 verification sweep (no-regression gate)MAIN+DSPI-1/2/11/13{114.5},{114.6},{114.7},{114.8},{114.9},{114.10}
  • {114.5} (de-ID) is independent of {114.6} (codegen). They touch different regions of different files (DS ops/classification-prompt.md worked-examples L194-809 vs MAIN codegen scripts). Both feed the PI-10 “MAIN carries only the generic skeleton” end-state.
  • {114.6} bundles PI-9 + PI-10 because PI-10 is fully ensured by PI-8 ({114.5}) + the materialised-generic-artefact of PI-9 - no separate code. {114.6} is the single MAIN codegen edit: rewire generate-classification-prompt-taxonomy.ts write target, add the marker-wrapped MAIN artefact, re-point bundle-plugin.ts:validate(), drop the bridge at both sites.
  • {114.7} (harness delete) and {114.8} (MAIN fixture add) are split by repo. {114.7} is a pure DS deletion (retires the fork + the latent --live break in one move - PI-4 is resolved BY the deletion). {114.8} is a pure MAIN add (de-ID’d copies into __tests__/fixtures/eval-gold/, map untouched).
  • {114.11} depends on {114.7} because wiring the DS root __tests__/ into CI must happen against the post-deletion DS tree (the harness/ CI job’s eval suites are gone; the root-test job is the new wiring). Sibling dep, both DS.
  • {114.12} depends on {114.8} (the MAIN copies must exist before the DS originals are deleted) AND is gated at the Task level on the {71.14} rewire landing (a status_note handshake, NOT a Subtask dep - that cross-Task dep is exactly what OQ-E removes by giving {71.14} the flip). The bridge keeps resolving the unmoved DS copy until {71.14} flips the map, so there is never a broken-path window.
  • {114.13} is the final verification gate - it depends on every code-touching Subtask and asserts the cross-cutting invariants (PI-1 de-ID sweep zero-hits, PI-2 not flip-blocking, PI-11 classifier runtime byte-unchanged, PI-13 no new dependency). It writes no product code; it runs sweeps + git diff assertions + bun run test.

Wave order (sibling deps only; cross-Task handshake noted):

  1. Wave 1 (parallel, no deps): {114.5} de-ID, {114.6} codegen, {114.7} harness delete, {114.8} MAIN fixture add, {114.9} denylist re-home, {114.10} token-parity re-home.
  2. Wave 2: {114.11} (after {114.7}), {114.12} (after {114.8}; Task-level gated on {71.14}).
  3. Wave 3: {114.13} verification gate (after all code-touching Subtasks).

Task-level handshake (NOT a Subtask dep): {114.12} (DS delete of the eval-fixtures originals) must merge AFTER {71.14}‘s resolveEvalFixture flip lands on the shared trunk. Record this as a status_note marker on ID-114 when {114.8} completes; do not encode it as a Subtask dependency (it is cross-Task; per OQ-E the flip itself is {71.14}‘s, so the only ID-114 obligation is “delete the DS originals after the flip is live”).


Risks and mitigations (decomposition-specific)

Section titled “Risks and mitigations (decomposition-specific)”
  • {114.7} could remove unique DS history. Mitigated by revision 1: MAIN is canonical for every runner; the one suspected divergence (eval-procurement-drafting.ts) is a stale regression, not unique value. The Executor verifies via grep that no DS eval runner holds a fix absent from MAIN before deleting (it does not - bl-215 is already in MAIN). No forward-port.
  • {114.12} broken-path window if it runs before {71.14}‘s flip. Mitigated by the Task-level status_note gate + the bridge resolving the unmoved DS copy until the flip. Never delete the DS originals in the same commit as anything else.
  • {114.6} bundle-plugin validation breaks if the MAIN artefact shape drifts from parseCanonicalTaxonomy’s regex. Mitigated by revision 2: the artefact keeps the TAXONOMY_START..END + #### N. DOMAIN + - `slug`: desc shape; the KH_PRIVATE_DOCS_DIR-unset validation test ({114.6} testStrategy) catches drift.
  • {114.9} denylist regression if the term set changes post-move. Mitigated by diffing the resolved term set pre/post; the KH_CLIENT_NAME_DENYLIST secret stays canonical (OQ-C).

Carried open questions (home-selection - do NOT block decomposition)

Section titled “Carried open questions (home-selection - do NOT block decomposition)”

These were left OPEN in TECH for Liam and are NOT resolved by this PLAN; each Subtask encodes the recommended shape but flags the choice in details:

  • OQ-C (PI-6 local-consumer config path): MAIN-checkout config path vs secret-materialised file in CI; and whether to consolidate .config/ip-denylist.txt + ops/identity-denylist.json into one source. {114.9} encodes the MAIN-checkout-relative recommendation (no new secret per the ratified revision).
  • OQ-D (PI-7 re-home shape): MAIN-homed guard (reads MAIN CSS, fetches DS mirror via GitHub-App bridge) vs shared composite action. {114.10} encodes the drop-the-parent-dir-escape requirement either way; the exact host is flagged.

  • {114.1} RESEARCH + {114.2} PRODUCT + {114.3} TECH (specs/id-114-docs-site-relocation/), read in full.
  • bun scripts/ledger-cli.ts show task 114 (slice read); ID-71 title (slice read, {71.14} ownership confirmation).
  • gitnexus (repo knowledge-hub): query('taxonomy generation eval runner fixture') -> no runtime flow, eval-surface definitions; context('resolveEvalFixture', lib/eval/fixtures.ts) -> 4 runner-main callers + resolvePrivateDocsDir.
  • MAIN (verified this dispatch): lib/eval/fixtures.ts (full), scripts/generate-classification-prompt-taxonomy.ts (full), scripts/lib/taxonomy-parser.ts (full), scripts/bundle-plugin.ts:150-230, scripts/eval-procurement-drafting.ts (bl-215 fix region), scripts/sweep-identity-relocation.ts:180-219, scripts/generate-purge-path-inventory.ts:349-380, .claude/hooks/ip-leak-filename-guard.sh:18-30, __tests__/validation/eval-fixture-sync.test.ts (skip-gate region), __tests__/fixtures/eval-gold/ + eval-baselines/ (existing twins).
  • DS (verified this dispatch): harness/{scripts,lib,__tests__} (stale eval slice + fork), harness/scripts/eval-procurement-drafting.ts (the bid_responses.cited_items stale regression), eval-fixtures/*.json, scripts/check-token-parity.ts (the ../../app/globals.css escape).
  • grep sweep (Python/SQL, TS-only-tool gap): scripts/cocoindex_pipeline/, scripts/kb_pipeline/ (empty), supabase/migrations/ - no taxonomy-marker / bridge references.