Skip to content

PLAN — ID-427 producer-inversion subtasks

Proposed subtasks for TECH.md. Decision cites are §N in that document; measurement cites are M*/C* in RESEARCH.md.

Test strategy throughout is behaviour-first (docs/reference/testing/test-philosophy.md §1): assert on what the emitted bundle and the run log say, never on which private helper was called. A test whose title reads “routes through _list_residual_concepts has failed criterion 1 before it runs; the title to write is “a published document with no answers appears in the bundle”.

InterfaceOwnerGates
v0.2 frontmatter (generated, sources[], footnotes, no top-level resource:)id-426{427.10} — the residual template emits into that shape. Sequencing only; land id-426 first or coordinate on frontmatter.py.
status: draft for no-content conceptsid-428Nothing. {427.10} emits confidence: no-content; id-428 maps it later.
Index emission + theme retirement, shared file bundle_writer.pyid-429{427.8}, {427.9}, {427.11} touch the same module. Not logically blocking; merge-order coordination required.
Parser acceptance of the new log.md lineid-439{427.9} verifies parse-log.ts rather than assuming it (TECH §7).
scope_tag publication gateid-422Nothing. {427.10}‘s hole-1 branch is defensive by ruling and stays until id-422 lands (TQ-4).

Wave rule (DR-060, TECH §5): exactly one version= bump on enrich_concept, applied in {427.7} and not re-bumped by any later subtask in this wave.


{427.4} — Neutral Source protocol module · free now · closes id-362 F1 leg 1

Section titled “{427.4} — Neutral Source protocol module · free now · closes id-362 F1 leg 1”

Move ConceptKey, ConceptRaw and the Source protocol out of l_records.py into scripts/cocoindex_pipeline/sources/base.py. enrich.py:246 imports from there; repo_docs.py:234-242’s duplicate protocol declaration deletes (M14). No behaviour change, no field change, no gate change — a pure re-home so the two Sources stop being parallel implementations of one idea.

Test strategy. Existing suites pass byte-unchanged (that is the assertion for a re-home). One new test: both LRecordsSource and RepoDocsSource satisfy isinstance(x, Source) — the runtime-checkable protocol is the public contract id-362 F1 asks for, so asserting it is behaviour, not structure.


{427.5} — type becomes a shape-validated label · depends {427.4}

Section titled “{427.5} — type becomes a shape-validated label · depends {427.4}”

Delete ALLOWED_CONCEPT_TYPES, CONCEPT_TYPES + its __post_init__ membership branch, _CLASS_CONCEPT_TYPES, _permit_overlay_concept_types and its contextvar, SYSTEM_BASELINE_CONCEPT_TYPES, EffectiveOntology.base_for_class and the concept_types field. check_type_membershipcheck_type_shape (TECH §2.8). RepoConceptKey’s membership check becomes the same shape check. write_bundle’s internal_dev fail-loud at gate entry deletes with the register it guarded.

Closes AC 2.

Test strategy (test_producer_validator.py, test_l_records_source.py, test_producer_bundle_writer.py):

  • a concept typed procurement_policy — a value no registry has ever held — is written to the bundle and appears in the run summary;
  • Q A Pair!, x, a_very_long_five_word_type_label and "" are each rejected with a shape error naming what is wrong, and the error text names no permitted set;
  • q_a_pair is still refused unconditionally (BI-3), including via ConceptKey;
  • a system_baseline run writes a concept typed document — the uniformity ruling (PI-7) stated as behaviour;
  • an internal_dev run reaches the write loop instead of raising at gate entry.

{427.6} — Facet vocabulary retires; the three bends resolve as types · depends {427.5}

Section titled “{427.6} — Facet vocabulary retires; the three bends resolve as types · depends {427.5}”

Delete RECOGNISED_FACET_TAGS, FACET_TAG_ALIASES, canonical_facet_tag, normalise_facet_tags. web_pass.py emits type="reference" and drops _REFERENCE_CONCEPT_TAG. Delete TS CONCEPT_TYPE_VALUES; extend KNOWN_TYPES with document, questionnaire_response, answer_set, reference.

Closes AC 3. Note for the implementer: policy/capability have no emitter to re-point (M2) — this subtask is a registry deletion for them, and going looking for an emission change will waste a session.

Test strategy:

  • a Pass-2 reference concept is emitted with type: reference and no reference tag;
  • a concept carrying metric and dataset tags is written and validated — free-form facet tags survive without a registry;
  • a concept carrying an arbitrary tag (encryption) is still accepted (unchanged behaviour, asserted because its guard is being deleted);
  • TS: an unmapped type falls back to the default badge token (already true — assert it holds after CONCEPT_TYPE_VALUES is gone).

{427.7} — Grain registry + dispatch unification · depends {427.5} · the structural subtask

Section titled “{427.7} — Grain registry + dispatch unification · depends {427.5} · the structural subtask”

Introduce GrainSpec / GrainEnumeration / Coverage; add ConceptKey.grain; convert the six preferred grains + the feeder grain to registry entries declaring directory and type_label; replace all three concept_type dispatchers (TECH §1). _source_documents_for_key takes patterns. Feeder config gains directory (TECH §2.7). Apply the wave’s single version= bump.

Also lands the reserved-name slug guard (TECH §2.6, id-429 IA-3 — net-new, created by this task and owed to id-429): mint_concept_slug suffixes index/log with -concept; _validate_concept_feeder_schema rejects them as directory names.

Test strategy — the assertion that proves the structure, not the refactor:

  • a fake grain registered in a test appears in the emitted bundle with no edit to any dispatcher. This is the property whose absence caused the inversion; it is the one test that must exist.
  • each existing grain still returns the same rows for the same key (a read-parity test per grain, asserting on returned row ids — the public return value, not the SQL);
  • a scope_tag literally named index produces topics/index-concept.md, and the directory’s own index.md is untouched;
  • a feeder config declaring "directory": "log" is rejected at read time;
  • relabelling a grain’s type_label changes the emitted type and does not move the file (PI-5, asserted directly).

{427.8} — Won-bid identity collapse · depends {427.7} · coordinate id-429

Section titled “{427.8} — Won-bid identity collapse · depends {427.7} · coordinate id-429”

The won-bid grain declares directory="case-studies/won-bid". Delete _won_bid_case_study_redirect, bundle_write_path, bundle_write_path_for_key; flow_def’s BI-28 map and the embed lookup key on rel_path.

Test strategy:

  • a won-bid concept is written at case-studies/won-bid/<slug>.mdunchanged physical path, asserted against the shipped showcase layout;
  • a named-client and a won-bid case study with the same buyer slug both write, neither overwriting the other (the collision the redirect existed to prevent);
  • a BI-9 cross-link to a won-bid concept resolves to the path the reader can open;
  • BI-28 provenance still attributes the won-bid proposal (guards the rel_path re-key).

{427.9} — Corpus census · depends {427.7} · coordinate id-429, verify id-439

Section titled “{427.9} — Corpus census · depends {427.7} · coordinate id-429, verify id-439”

Every grain returns Coverage; Source.census() on both implementations; RunSummary.census; the unconditional Considered line and the conditional Unrouted line in _render_run_bullets; is_no_op accounts for unrouted (TECH §2.11).

Verify, do not assume: read lib/okf/parse-log.ts and confirm the new bullet does not break log parsing; if it does, that is an id-439 hand-off, not a silent revert (TECH §7).

Closes AC 4.

Test strategy:

  • a run that changes nothing still writes a Considered line naming both unit counts — “a no-op run still says what it considered”;
  • with a grain stubbed to cover nothing, the run reports a non-zero Unrouted count and is not a no-op, even though every file on disk is byte-identical;
  • a fully covered corpus reports considered == routed for both unit kinds;
  • no uuid appears anywhere in log.md (BI-10 asserted directly on the artefact).

{427.10} — The residual grain · depends {427.9} · sequence after id-426 · the headline AC

Section titled “{427.10} — The residual grain · depends {427.9} · sequence after id-426 · the headline AC”

The three-step attribution cascade, the three directories, the deterministic render_undistilled_draft template with its contains_record_pointer title guard, and confidence: no-content (TECH §2.1–§2.3).

Closes AC 1.

Test strategy — this is the acceptance test the task is judged on, and it is written as a corpus fixture, not a unit test:

A corpus deliberately containing both measured holes — (a) a published source_document that produced no q_a_pair, and (b) a published q_a_pair with an empty scope_tag array — is run end-to-end, and both records are present in the emitted bundle, each citing its own record, with the run census reporting zero unrouted.

Plus:

  • a published pair whose only lineage is source_form_instance_id lands in questionnaire-responses/; one with neither lineage lands in the single unattributed-answers/ concept; that concept is absent when the corpus has no such pair (no empty placeholder);
  • an undistilled document’s concept body contains the escalation sentence and no model-generated prose — assert the agent loop was not entered by asserting the emitted body equals the template render for that record (behaviour: the page says only what the records support);
  • a residual concept for a document whose filename embeds a uuid still writes, with the neutral fallback title;
  • two documents with identical filenames produce two distinct concepts (the unconditional -<uuid[:8]> suffix), and neither trips the write-path collision guard;
  • a residual document that does have published pairs drafts through Pass-1 normally — the drafts_via switch stated as behaviour.

{427.11} — Ontology artefacts overlay-only · depends {427.5} · executes DR-027 (S546)

Section titled “{427.11} — Ontology artefacts overlay-only · depends {427.5} · executes DR-027 (S546)”

write_ontology_artefact emits {"overlay": …}; _base_ontology_snapshot deletes; EffectiveOntology keeps two dimensions; iri_projection drops the concept_types row and its docstring counts (TECH §2.10). _OVERLAY_DIMENSIONS keeps all three keys.

Test strategy:

  • the emitted ontology.json has no base key and still carries overlay: null for a bundle with no overlay;
  • a client bundle with an ontology-overlay.json declaring concept_types still validates, still echoes those terms into overlay, and no longer gates any write on them;
  • a non-client_business bundle with a stray overlay still hard-rejects (DR-054/DR-079 gate unchanged — asserted because its neighbour is being deleted);
  • context.jsonld carries entity and relationship terms only; SUPERSEDED by TQ-1’s S546 ruling, executed by {427.14} — the base entity/relationship projection retires too, so context.jsonld carries CLIENT-OVERLAY terms only and a bundle with no overlay emits {"@context": {}}. This bullet described {427.11}‘s scope correctly at authoring time and was already contradicted by the ruling when {427.11} landed; {427.11} executed the ontology.json half only and carried the rest to {427.14}. Left struck rather than deleted so {427.13}‘s disposition-table walk sees the supersession rather than a silently edited requirement;
  • bundle-graph.ts’s overlay read still produces its styling signal against the new payload. Widened by {427.14}: that read is now the ONLY on-disk source for BOTH union-graph signals — the bundle-class shape channel and the re-sourced border channel (overlay.concept_types -> typeDeclaration).

{427.12} — id-358 rename · depends {427.7}

Section titled “{427.12} — id-358 rename · depends {427.7}”

ConceptKey.workspace_idform_instance_id; ProposedChange.source_workspace_idsource_form_instance_id; sync_bundle(source_workspace_ids=)source_form_instance_ids=; flow_def’s map follows. The BI-28 slot stays.

Closes id-358. Sequenced after {427.7} so the two ConceptKey field changes ride one fingerprint invalidation, not two.

Test strategy (test_producer_bi28_bidoutcome_proposal.py): a won-bid draft proposal is still stamped with its form instance’s id and is still never unattributed. The rename is proved by the provenance surviving, not by grepping for the old name.


{427.13} — Audit delivery + artefact sweep · depends all · closes AC 5, AC 6

Section titled “{427.13} — Audit delivery + artefact sweep · depends all · closes AC 5, AC 6”

Walk TECH §3’s disposition table and mark each row landed; withdraw superset 1 from the showcase CONFORMANCE.md (hand-authored, producer-preserved — a manual edit); correct producer docstring §-cites that name the withdrawn taxonomy; sweep specs/id-132-* for DR-141 propagation (carried open from the substrate); record the DR-141 coverage-not- partition rider recommendation (TECH §2.1) and the four TQ-* UNDECIDABLEs into the task file for the owner.

Test strategy: none executable. The gate is the table itself — every row changed, kept, deleted or UNDECIDABLE with its question, which is what AC 5 asks for. A row left blank is the failure condition.


{427.4} ──▶ {427.5} ──┬──▶ {427.6}
├──▶ {427.7} ──┬──▶ {427.8}
│ ├──▶ {427.9} ──▶ {427.10} [after id-426]
│ └──▶ {427.12}
└──▶ {427.11}
all ──▶ {427.13}

Independently landable and free now: {427.4}. Free after {427.5}: {427.6}, {427.11}. The two that carry the task’s meaning: {427.7} (a grain is one registry entry) and {427.10} (both holes land in the bundle). If the wave has to stop early, stop after {427.10} — {427.11}/{427.12}/{427.13} are honest tidy-up; {427.10} is the acceptance.