DR-142: The deployed unit is the runtime bundle, and it moves to pipeline/ with its tests
DR-142 — The deployed unit is the runtime bundle, and it moves to pipeline/ with its tests
Section titled “DR-142 — The deployed unit is the runtime bundle, and it moves to pipeline/ with its tests”Context
Section titled “Context”DR-117 ratified the cocoindex relocation in principle and deferred it —
“when cocoindex moves out of scripts/ (agreed in principle, deferred),
anything under scripts/cocoindex_pipeline/ moves as one unit, while anything
under scripts/tests/ would be orphaned by that move” — and explicitly left the
destination, and where the Python tests live, unsettled. services/ has been
working shorthand since S525 but was never ratified: no decision covers repo
structure at all, and the only two structural occurrences of the string are
DR-121’s rejected alternative and an S525 drawer, both rejecting waiting
for it.
DR-117’s “moves as one unit” premise is false at the directory boundary
(measured, id-443 {443.1}). Six cross-boundary edges: outbound,
form_extractors/docx.py:84,89,90 imports three loose scripts/ root modules;
inbound, bid_worker.py:53-57, fill_template.py:448, _okf_prototype_draft.py.
The deployment agrees with the code. onprem-deploy.yml:274 is
cp -R scripts ${CTX}/scripts — the whole directory — and the single
kh-cocoindex-pipeline image runs three processes off the scripts. prefix:
cocoindex_pipeline.server, bid_worker (a real service in both platform
composes), and cocoindex_pipeline.mock_llm. reference/deployment-architecture.md
§2.1 states it: “scripts/** is packaged whole by onprem-deploy.yml, so no
second image build/CI pipeline is needed.”
So moving cocoindex_pipeline/ alone would split the deployed image’s source
across two roots and force cp -R scripts && cp -R services — strictly worse
than today.
Decision
Section titled “Decision”The unit that moves is the runtime bundle, not the directory. cocoindex
leaves scripts/ for a singular, root-level pipeline/ holding
cocoindex_pipeline/, its six runtime sibling modules, and its Python tests.
pipeline/, notingest/(owner, S549): “ingest doesn’t cover the full pipeline scope.”- The package name is unchanged, so the rewrite is a single prefix
substitution and the fixtures tree,
DR-121’sdata/tree and the 47 guard-enforcedcorpus-manifest.jsonpaths all move by prefix only. - The Python tests move with it.
docs/reference/testing/test-philosophy.md§3’s mirror rule is scoped to__tests__/and does not reachscripts/tests/; the doctrine that does isDR-117’s own — “filed by the domain that owns them, never by the kind of artefact they are” — whose Consequences route this question here.DR-121already extended that rule once, from fixture trees to runtime data; tests are its third application, not a new ruling. deploy/stays a root-level sibling. Only 12 of its 30 tracked files name cocoindex; the rest are generic Coolify authoring skills andgoose-pilot/, which is explicitly carved out of the pipeline concern (mempalace.yaml:94; the goose compose records at:5that it “does NOT share the cocoindex image/compose”).- The move is codemod-driven (owner, S549), following the
scripts/codemods/precedent, and paired with the measured hazard list — a naive prefix rewrite is not sufficient (see Consequences). id-416/DR-119runs first (owner, S549). It rewrites the sameonprem-deploy.ymllines and the same module-path strings in the same four composes; concurrent execution risks an image that builds green and fails at container start on the live path every deployment pulls.
This amends DR-117 rather than superseding it: DR-117’s filing rule stands
and is what decides the tests; only its unit-boundary assumption is corrected.
Alternatives Considered
Section titled “Alternatives Considered”services/cocoindex_pipeline/ — the standing shorthand. Declined on two
independent grounds: no requirement and no current source could be named for a
services/ tier (the carried UNDECIDABLE went unanswered against the in-force
register, all eight DR files naming the path, platform-context.md,
deployment-architecture.md, cocoindex-pipeline.md and four MemPalace passes),
and — decisively — it is insufficient, because it splits the deployed image’s
source as described above.
Nesting deploy/ under the new root. Declined: it demotes deploy/ from the
root level the owner named as the deciding criterion, it reintroduces the
mixed-language junk-drawer property the move exists to remove (the new root is a
Python package root and the image build context; deploy/ holds .yaml, .sh,
Dockerfile and .ts), and the S521 ruling “deploy/ belongs with the pipeline”
settles a concern grouping that mempalace.yaml’s ingest-pipeline room
already discharges under DR-116.
Leaving the tests behind. Declined: only 3 of 93 are genuinely non-pipeline,
and splitting would mean two testpaths and two pytest invocations for three
files. It would also leave mempalace.yaml:236-240’s standing claim — “it
resolves for free when cocoindex leaves scripts/” — permanently false in a
live config.
Continuing to defer. Declined: DR-117 and DR-121 have both been filing
files against the deferral since August, and DR-121 warned that a wrong
placement guarantees “a third move of the same files.”
Consequences
Section titled “Consequences”DR-116binds the name. Room keys are directory names matched at any depth, andmempalace.yaml:141-143keys roomingest-pipelineon the literal tokencocoindex_pipeline. Because the package name is unchanged, room routing survives the move untouched. Renaming the package later would silently gut the room —mempalace.yaml:127-131records a prior draft that lost 95 files exactly this way.- A naive prefix codemod is insufficient. Four measured shapes defeat
grep-for-the-old-path and an attribute-node AST pass alike: 15 segment-wise
Path()construction sites across 7 files; imports inside triple-quoted_PROBE_PRELUDEstrings executed as subprocesses; 6importlib.import_moduleliterals plus one package-relative f-string form; and 2sys.moduleskeyings. The id-412 lesson, quoted in the code it produced (test_form_extractors.py:48-56), is the standing instruction: grep for the segments, not the path. ci.yml’spython:paths-filter (:65-68) is the one silent-and-green failure mode. If it stops matching, the pytest step never fires and every PR passes with zero Python tests run.- A resolve-first is owed before the move (id-443
{443.0}): the same modules are imported under two spellings simultaneously in live code, resolving only viapythonpath. This is the ID-44.5 / ID-177 dual-sys.modules-identity shape, harmless today because those modules are stateless — and the relocation is what detonates it, because the move changespythonpath. - Docs are repointed by rule, not wholesale. ~42 files make current-state
claims and are repointed; ~281 are point-in-time spec, task, report and retro
prose that keep the old path as a historical record. A DR body is never
rewritten — a decision that becomes wrong is amended by a new decision, which
is what this record does to
DR-117.