Goal
R12 USER-CONFIRMED storage layout: ONE flat content/intel/ folder, sibling of the content/section-<sid>/ section dirs (this plan’s content/ holds content/section-s-<hex>/ dirs; content/overview/ is the write.py template-intended mission_ref home, not materialized in this plan on disk) — plan.intel.md (plan-level rollup) + <slug>--s-<hex>.intel.md per section, name-matched 1:1 to the section body. Retire the gitignored .intel/intel_package/ ephemeral cache AND the nested content/section-<sid>/intel/ per-section dirs.
Section ownership boundary (WBS — resolves the plan.intel.md rollup double-ownership)
storage-layout (s-983a8a1b) is the lexorank PREDECESSOR of schema-integration (s-e2117453); a successor cannot satisfy a predecessor’s hint_needs under INV-19. The plan-root rollup work is split so neither section double-migrates nor leaves a gap:
- storage-layout (THIS section) owns the on-disk FILE layer: it owns the storage layout, so it owns (a) the
write.pydest-template flattening, (b) thecontent/intel/plan.intel.mdrollup-FILE write + its exact-filename INV-18 exemption registration, and (c) the on-disk FILE relocation (moving eachcontent/section-<sid>/intel/package.mdinto the flatcontent/intel/layout, plus the sibling.intel.jsonenvelope). - schema-integration (successor
s-e2117453) owns the plan.json SCHEMA layer ONLY: the structuredSection.intel_package{path, generated_at_sha}shape, the new plan-rootintel_packageref struct, theplan-routing.schema.jsonpath pattern, the v2PlanSectionSchemareconcile, and the forward-onlyplan.jsonref backfill (w-d7213728). It builds ON this flat layout; it never writes or relocates a.intel.md/.intel.jsonFILE. - The boundary: storage-layout writes/moves the FILES (incl.
content/intel/plan.intel.mditself); schema-integration backfills theplan.jsonREF struct{path, generated_at_sha}that POINTS at those files. The rollup FILE write is here; only the rollup REF struct is the successor’s.
Relocation-seam framing:
- The seam is
scripts/plan_corpus/write.py— the hardcoded engine-writer dest template_SIDECAR_PATH_TEMPLATE['intel_package'](write.py:3593) currently maps tocontent/section-{sid}/intel/{name}.md. - The flat layout lands by changing THAT template, not by rewriting the runtime files that delegate to it.
- This section folds in the MINIMAL schema-side changes its own writes need to land + validate (the write.py dest template + the EXACT
content/intel/plan.intel.mdplan-level exemption registration). It also owns the rollup FILE write (content/intel/plan.intel.md) since it owns the storage layout. - The broader v7 schema integration — the
Section.intel_package{path, generated_at_sha}struct, the plan-rootintel_packageREF field, theplan-routing.schema.jsonpath pattern, the v2 reconcile, and theplan.jsonREF backfill — is a forward-handoff to successorschema-integration--s-e2117453.md. Only the REF struct is the successor’s; the FILES are this section’s per the §Section ownership boundary.
Implementation Sketch
- Change the engine-writer dest template
scripts/plan_corpus/write.py:3593_SIDECAR_PATH_TEMPLATE['intel_package']fromcontent/section-{sid}/intel/{name}.mdto the flatcontent/intel/{name}.mdlayout. This template is the SINGLE relocation seam —update_sidecar(write.py:3601) formats it per write; every sidecar consumer routes through it. - Register the EXACT rollup path
content/intel/plan.intel.md(NOT the bare directory prefixcontent/intel/) for the INV-18 checkbox-strip exemption inscripts/plan_corpus/route_v7.py:409.PLAN_LEVEL_SIDECAR_PREFIXESmatches bystartswith, so registering thecontent/intel/prefix would ALSO exempt every per-section<slug>--<id>.intel.md— the over-exemption this section forbids. Register the exact rollup filename: if the registry only accepts directory prefixes, add an exact-match branch (or a dedicatedPLAN_LEVEL_SIDECAR_FILESexact set) so onlycontent/intel/plan.intel.mdis plan-level and the per-section files stay per-section reference content. - Do NOT rewrite
storage.py—store_v7_sidecar(storage.py:27) delegates towrite.update_sidecar; it carries no destination knowledge and changes nothing once the write.py template is flat. Leave its delegation intact. persist.py(98) — keep as-is for what it owns:persist_packagevalidates the stagedpackage.json(viaconsume.load_package), derives the siblingpackage.md, route-detects, and on the v7 path callsstore_v7_sidecar;resolve_persistence_section_idvalidates the section. It already routes the package.md through the engine writer, so it inherits the flat dest automatically. No path-template logic lives here.stage.py(91) —stage_for_plan_dir(stage.py:44) writes the ephemeral<plan_dir>/.intel/intel_package/cache (package.json+package.md), keyed offtarget_for_plan_name(...). The DURABLE persistence path is what moves to the flatcontent/intel/layout; the ephemeral staging SCRATCH dir is RETAINED for its run-time one-shot-staleness cache role (stage_for_target’spriorenvelope cache key), NOT retired wholesale. Two distinct concerns:- Durable layout (this section’s deliverable):
content/intel/<slug>--<id>.intel.{md,json}— the committed, name-matched sidecars. - Ephemeral run-time cache (retained, NOT durable): the
.intel/intel_package/scratch dir feedingprior-based staleness checks. - The ephemeral scratch PATH MUST disambiguate across target kinds —
stage_for_plan_dironly resolves plan-section targets viatarget_for_plan_name, butstage_for_targetserves all four kinds (plan-section / bug / diff / symbol-set pertarget_kinds.py). A bare<plan_dir>/.intel/intel_package/path collides a diff-target or symbol-set-target cache against the plan-section cache. The retained scratch path keys on the target-kind + target-ref (e.g..intel/intel_package/<kind>/<ref-stem>/) so distinct target kinds never share one cache slot. The durablecontent/intel/flat layout is a SEPARATE namespace from this ephemeral cache and never collides with it.
- Durable layout (this section’s deliverable):
- Migrate the existing populated packages into
content/intel/(see Work Items for the count disambiguation). - The package md is REFERENCE CONTENT ONLY (no checkbox, no status) per
plan-read-discipline.md §3.5.
JSON-envelope replacement before retiring .intel
- Retiring the
.intel/intel_package/ephemeral cache removes thepackage.jsonenvelope thatconsume.py:load_package(consume.py:20) parses for the/intel-packageconsume path. - The replacement is DURABLE but MUST be uniquely named per package — a flat
content/intel/directory holds one rollup + every section’s package, so a barepackage.jsonper section would collide to a single file. Name each durable envelope to MIRROR its.intel.mdsidecar: per-section<slug>--<id>.intel.jsonbeside<slug>--<id>.intel.md, and the rollupplan.intel.jsonbesideplan.intel.md. The.mdsidecars are already uniquely named, so the.jsonenvelopes adopt the same unique stem. consume.py:load_packagecurrently key-resolves by appending a barepackage.jsonto a directory (thepath.is_dir()→path / "package.json"branch at consume.py:30-31). In the shared flatcontent/intel/dir that shortcut collides every package to one file — it MUST instead key on the unique<slug>--<id>STEM: given acontent/intel/<slug>--<id>.intel.mdref (or its bare stem), resolve the sibling<slug>--<id>.intel.json; given the rollupcontent/intel/plan.intel.mdref, resolvecontent/intel/plan.intel.json. Stem-keyed resolution replaces directory-append for the flat layout.- The ref→JSON-envelope path mapping is centralized in ONE helper in the already-present
scripts/intel_package_runtime/envelope.py(the moduleconsume.pyalready importsvalidate_envelopefrom) as the SSOT across all 5 consumers (/continue-roadmaprecon,/review-planaudit,/tpr-reviewgrounding,/add-bugrelatedness,/fix-bugRCA). Add anenvelope.pyfunction (e.g.envelope_path_for_ref(ref) -> Path) that maps a.intel.mdref / stem / dir to its sibling.intel.json;load_packageand every other consumer call it rather than re-deriving the.md→.jsonmapping inline. Re-deriving the mapping anywhere else isLEAK:scattered-knowledge. persist_packagealready derives the siblingpackage.mdfrom the stagedpackage.json(persist.py:89); persist BOTH the uniquely-named.intel.md(engine sidecar) and the uniquely-named.intel.json(envelope) intocontent/intel/so every per-package envelope resolves with no collision. The s-15e599fa pins assert each flat package JSON resolves to its own envelope via theenvelope.pymapping.
Spec References
scripts/plan_corpus/write.py:3593(_SIDECAR_PATH_TEMPLATE['intel_package']dest template — the relocation seam) + write.py:3601 (update_sidecarformats it)scripts/plan_corpus/route_v7.py:409(PLAN_LEVEL_SIDECAR_PREFIXES— register the EXACT filenamecontent/intel/plan.intel.mdvia a new exact-match set/branch, NOT the barecontent/intel/prefix which wouldstartswith-over-exempt per-section files)scripts/intel_package_runtime/persist.py(persist_packagederives package.md, callsstore_v7_sidecar) +stage.py:44(stage_for_plan_dir— repurpose.intel/intel_package/away from durable output to a target-kind-disambiguated ephemeral cache) +consume.py:load_package(consume.py:30-31 directory-append → stem-keyed flat-ref resolution) +scripts/intel_package_runtime/envelope.py(centralized ref→.intel.jsonpath-mapping SSOT for all 5 consumers;consume.pyalready importsvalidate_envelopefrom it)scripts/intel_package_runtime/storage.py:27(delegates towrite.update_sidecar— NOT a rewrite target)plans/intel-package-dossier-rebuild/content/schema-integration--s-e2117453.md(successor — owns the BROADER v7 schema integration as a forward-handoff; this section declares NOdepends_on:)intel-package-rebuild-notes.mdR11, R12; the ASCII layout confirmed in session.claude/rules/plan-read-discipline.md §3.5(sidecars are reference content only)
Sequencing — forward-handoff, not backward dependency
- This section declares no advisory dependency (
hint_needs: [], the active v7 field —depends_onis retired per v7-plan-system.md §7) and is independently completable: the write.py dest-template change + thecontent/intel/plan-level-prefix registration are the minimal schema-side changes its OWN writes need to land + validate. - It does NOT declare
hint_needs:— an advisory edge to a lexorank SUCCESSOR is a backward/diamond edge that violates INV-19 (strict-linear-single-branch DAG). - The lexorank order
storage-layout→schema-integrationsequences the BROADER schema work AFTER this layout lands: the structuredSection.intel_package{path, generated_at_sha}shape, the plan-rootintel_packageREF field (the{path, generated_at_sha}struct that POINTS at thecontent/intel/plan.intel.mdFILE this section writes — the FILE is here, the REF is the successor’s), the path pattern inplan-routing.schema.json, the v2PlanSectionSchemareconcile, the forwardplan.jsonREF backfill. schema-integration--s-e2117453.mdbuilds ON this flat layout; this section hands it forward.
Work Items
- Flat-layout writing
content/intel/plan.intel.md(plan-level rollup) +content/intel/<slug>--<id>.intel.md(per-section), via the two seam changes (storage-layout owns the rollup FILE write per the §Section ownership boundary; the rollup REF struct is schema-integration’s):- Change
scripts/plan_corpus/write.py:3593_SIDECAR_PATH_TEMPLATE['intel_package']to the flatcontent/intel/{name}.mddest. - Register the EXACT rollup filename
content/intel/plan.intel.mdfor the INV-18 checkbox-strip exemption — NOT the bare directory prefixcontent/intel/.scripts/plan_corpus/route_v7.py:409PLAN_LEVEL_SIDECAR_PREFIXESis a tuple matched bystartswith; registering thecontent/intel/prefix wouldstartswith-match (and thus over-exempt) every per-sectioncontent/intel/<slug>--<id>.intel.mdtoo. Add a dedicated exact-match set/branch (e.g.PLAN_LEVEL_SIDECAR_FILES = ("content/intel/plan.intel.md",)consulted by an==-equality check alongside the prefixstartswithcheck) so ONLYcontent/intel/plan.intel.mdis treated as plan-level; per-section.intel.mdfiles stay per-section reference content. - Persist a durable
.intel.jsonenvelope beside each.intel.mdincontent/intel/soconsume.py:load_packageresolves the flat ref via the centralizedenvelope.pystem→envelope mapping; leavestorage.pydelegation +persist.pyderivation untouched (they inherit the flat dest).
- Change
- Retire
.intel/intel_package/durable-write + nestedcontent/section-<sid>/intel/write paths:- Retire the
stage.py:44<plan_dir>/.intel/intel_package/location AS A DURABLE-PERSISTENCE target; the ephemeral run-time staging cache is RETAINED with a target-kind-disambiguated path per the §stage.py clarification above (it is not deleted, only repurposed away from durable output). - The nested
content/section-<sid>/intel/path disappears as a consequence of the write.py:3593 template flattening — no separate code site retires it; verify the old prefix is absent after execution.
- Retire the
- Relocate the existing on-disk packages into
content/intel/(storage-layout owns the FILE relocation; schema-integration’sw-d7213728owns the disjointplan.jsonREF backfill — no overlap):- Migration scope — explicit (resolves the understated “9 plans” figure): the migration target is the ref-populated set — the plans whose
plan.jsoncarries a populatedintel_packageref (measured on disk: 15 plan.json files carry the ref; the prior “9” figure is stale and is superseded by the measured count). The on-disk file footprint of that set iscontent/section-<sid>/intel/package.mdnested dirs (measured: 15 plans corpus-wide) plus.intel/intel_package/ephemeral caches (measured: 10 plans). The 6 in-plan on-diskpackage.mdfiles under THIS plan are a subset of that nested-dir footprint. - In-plan: relocate the 6 on-disk
content/section-<sid>/intel/package.mdfiles under this plan into the flatcontent/intel/layout (each becomescontent/intel/<slug>--<id>.intel.md+ sibling.intel.json). - Cross-plan corpus: relocate every ref-populated plan’s nested
content/section-<sid>/intel/package.md(the durable per-section packages) into that plan’s flatcontent/intel/layout. Ephemeral.intel/intel_package/caches are NOT relocated — they are run-time scratch (regenerated on next stage) and are out of the durable-relocation scope; the Verification “old paths absent” criterion below scopes “absent” precisely to the durable nested dirs of the ref-populated set, NOT to ephemeral caches that legitimately regenerate.
- Migration scope — explicit (resolves the understated “9 plans” figure): the migration target is the ref-populated set — the plans whose
Verification
Pinned by the success criteria the engine evaluates (extending sc-0006 per learning-ledger.md-discipline criteria; the assertion pins live in plan.json and reference the s-15e599fa pin section). Execution is not complete until ALL hold:
content/intel/plan.intel.mdrollup FILE is generated (the plan-level rollup exists at the flat location and the EXACT filenamecontent/intel/plan.intel.md— not the barecontent/intel/prefix — is registered for the INV-18 exemption inroute_v7.py:409). Per-sectioncontent/intel/<slug>--<id>.intel.mdfiles are confirmed NOT plan-level-exempt (a per-section file with a checkbox still fails INV-18 validation).- New per-section path is populated: each section’s
content/intel/<slug>--<id>.intel.mdexists, name-matched 1:1 to the section body. - Old durable paths are ABSENT after execution, scoped to the ref-populated set: no
content/section-<sid>/intel/nested dir remains in any plan whoseplan.jsoncarries a populatedintel_packageref (the migration target). The “absent” qualifier is the durable nested dirs of the ref-populated set; ephemeral.intel/intel_package/run-time caches are out of scope (they regenerate on next stage and are not a durable old-path leak). - Each migrated package’s content matches its original: every relocated
.intel.mdis byte-identical (modulo the path move) to the sourcepackage.mdit was migrated from — no content lost or altered in the relocation. - Persist + consume round-trip (not just file placement): for a generated plan-section package, the sibling
.intel.jsonenvelope persists beside the.intel.mdANDconsume.py:load_package, resolving via the centralizedenvelope.pystem→envelope mapping, returns a valid (non-degraded) envelope from the flatcontent/intel/ref. The pytest pin inscripts/intel_package_runtime/tests/exercises the full round-trip (persist.intel.md+.intel.json→ resolve the flat ref →load_packagereturns the same non-degraded envelope), not merely that the.mdfile landed.
Reference the s-15e599fa pins, which assert the rollup-generation, old-paths-absent, migrated-content-match, and persist+consume-round-trip conditions.