ID-132 {132.2} TECH — OKF L-concepts layer + 2-pass producer (producer architecture)
TECH — OKF L-concepts layer + 2-pass producer (the producer architecture)
Section titled “TECH — OKF L-concepts layer + 2-pass producer (the producer architecture)”Status:
DRAFT — 28/06/2026.The HOW for Task A of the OKF re-architecture — the two-pass producer that emits the client-owned OKF concept bundle. Authored by a FRESH Planner (Q-PLANNER-2 — a different instance from the{132.1}PRODUCT author), with the ratified{132.1}PRODUCT.md read in full as the contract. Every change maps 1:1 to a Behaviour invariant (BI-N).Spec chain:
okf-record-model-v3.md§1/§7 + ID-131{131.1}RESEARCH (served as RESEARCH) →{132.1}PRODUCT →{132.2}TECH (this doc) →{132.3}PLAN.How to read this: The producer at a glance is the plain-English shape; The Source adapter is the one genuinely bespoke piece; The two-pass loop + The agent-loop port are the lift-and-shift with the real cost named; BI-26 reconciliation resolves the path-vs-uuid duality; Proposed changes per invariant is the BI→change map (the Checker’s per-invariant compliance grid); Empirical verification carries the mandatory import-and-call results for every net-new external API. Behaviour lives in PRODUCT.md — this doc is HOW.
De-identification: the real first ingest corpus is “the first client”; the name and any client-identifying path/filename are omitted throughout.
Context
Section titled “Context”ID-132 builds a net-new cocoindex producer flow that distils Canonical L-records (ID-131) into a
client-owned markdown concept bundle. There is no DB schema migration in ID-132 — the producer is
additive Python/TS code plus a client-owned git repository; every table it touches
(record_embeddings, record_lifecycle, citations.cited_concept_path, the typed records) is
ID-131-owned and consumed read-only or appended-to via ID-131’s contract. The single subtle reconciliation
(BI-26, the concept embedding key) resolves to a frozen constant, not a schema change (see
§BI-26 reconciliation). PRODUCT.md owns the behaviour; this section grounds the plan at current head.
The genuinely bespoke vs the lift-and-shift split (v3 §7.2, re-confirmed at head). Of the four
producer parts, three are lift-and-shift (the Source protocol shape, the prompts/, the
bundle-writer + index.md/log.md generators, the read-only viewer) and two are real cost: (a) the
Source adapter over L-records — the one bespoke piece, because the reference implementation’s
sources/base.py is theirs and we have no scripts/cocoindex_pipeline/sources/ package (verified
absent, below); (b) the ADK+Gemini → Anthropic agent-loop port (the real cost behind “lift-and-shift”,
v3 risk 3). The .md writer is the cocoindex self-updating-wiki localfs.declare_file FILE target, which
gives BI-18 delta-only regeneration for free.
Code-intelligence orientation (verbatim — Checker: confirm this ran)
Section titled “Code-intelligence orientation (verbatim — Checker: confirm this ran)”Tools (28/06/2026): gitnexus query (repo canonical), ripgrep over lib//scripts//app/ + the
Python pipeline, the installed-package import-and-call probes (§Empirical verification), and the ratified
ID-131 {131.3} TECH for the contract this Task consumes. Tool catalogue: .gitnexus/CLAUDE.md,
.ast-dataflow/CLAUDE.md (cited, not reproduced).
- No producer / Source-adapter / bundle-writer symbol exists — net-new confirmed.
gitnexus query({query:"OKF concept bundle markdown producer two-pass enrichment source adapter", repo:"canonical"})returned no producer process: the single top-ranked process was the unrelatedFunction:app/reference/[id]/page.tsx:ReferenceDetailPage(lines 54–139, priority 0.107); remaining hits were scattered definitions (scripts/cocoindex_pipeline/flow.py:_ingest_content_branch2049–2469,_ingest_qa_sidecar_branch2472–2588,lib/ai/classify.ts:isGenericConcept299–301,components/ui/concept-help.tsx:ConceptHelp). Noenrich_concept,run_web_pass,write_concept_doc,list_concepts,read_concept_raw, bundle-writer, or Source-protocol symbol.grep -rln "reference_agent|enrich_concept|run_web_pass|write_concept_doc|read_concept_raw" --include=*.py --include=*.ts→ zero hits: the reference_agent is the external Google ADK implementation we port FROM; it is not vendored in this repo (so it is out of the empirical- verification scope — we re-implement its protocol natively; §Empirical verification note). - No
resource:scheme, nosources/package.grep -rln "canonical://" --include=*.ts --include=*.py→ zero hits (thecanonical://URI scheme is net-new).scripts/cocoindex_pipeline/ sources/does not exist (confirms v3 §7.2: our sources arelocalfs.walk_dir+ a hand-rolledurl_source.py, both present — the reference_agent’ssources/base.pypattern is theirs). No self-updating-wiki /index.md/log.md/ bundle-writer artefact exists inscripts/cocoindex_pipeline/. - The bundle vector index target (
record_embeddings) is ID-131-owned, not in the code graph yet.gitnexus query({query:"record embeddings vector index hybrid search owner_kind concept"})surfaced onlylib/mcp/tools/search.ts:registerSearchTools(76–819) /runItemSearch(89–233),scripts/eval-search.ts:generateEmbedding, and cocoindex test fakes (test_cocoindex_flow_write_path.py:_FakeTarget.declare_vector_index) — norecord_embeddingssymbol, because it is the net-new ID-131 table (ID-131{131.3}TECH M1b:288:owner_kind text CHECK (… 'concept'),owner_id uuid,model text,embedding vector(1024),UNIQUE (owner_kind, owner_id, model), per-owner_kindpartial HNSW indexes incl.WHERE owner_kind='concept'). citations.cited_concept_path(the concept→concept citation contract) is ID-131-owned, absent at head.grep -rln "cited_concept_path"→ zero hits (ID-131 TECH M4b:293 addscitations.cited_concept_path text; this Task’s concept-path citations target it once ID-131 lands).- The SEED-CONTRACT family is live and frozen by ID-131.
_KH_PIPELINE_DOC_NS(flow.py:1640=fbfaf1ff-1ee4-583c-9757-1674465b2ec1) seedssd:{rel_path},ri:{source_url},qa:{rel_path}:{idx}; the namespace + seed strings are asserted across the pipeline test corpus (test_cocoindex_flow_fork_routing.py:594/596,test_platform_manifest_gate.py:576/577, et al.) and frozen by ID-131’s M0 /{131.5}G-SEED Vitest freeze test (ID-131 TECH:285/530–536). BI-26’s concept-namespace constant joins this family (§BI-26 reconciliation). - Reuse anchors verified at head (the agent loop is a PORT, not a from-scratch build):
scripts/cocoindex_pipeline/extraction.pyalready runs the Anthropic SDK directly —ANTHROPIC_MODEL = "claude-opus-4-6"(:71),anthropic.AsyncAnthropic()client construction (:1042/:1070/:1097/:1129),_anthropic_retrytenacity wrapper (:916),_guard_not_truncatedstop-reason guard (:862–872), a streaming wrapper (:1001–1027). These are plainmessages.create/messages.streamextraction calls —grep -rnE "tools=|tool_use|tool_result"overscripts/cocoindex_pipeline/returned ZERO, so the tool-use agent loop is net-new external API usage (hence the mandatory empirical probe in §Empirical verification). The producer reusesANTHROPIC_MODEL+_anthropic_retry+_guard_not_truncatedrather than re-inventing them. localfs.declare_fileis the.mdwriter.flow.pyalready importslocalfsvia the{67.4}insulation façade (scripts/cocoindex_pipeline/_coco_api.py:"localfs" → cocoindex.connectors.localfs);flow.pyuseslocalfs.walk_dir(the source).declare_fileis the unused-by-us FILE target the producer adds (empirically PRESENT in the pinned version, below).
Figma: none — a backend producer + a client-owned file artefact + a lift-and-shift read-only viewer. No new Canonical-hosted UI is designed here.
The producer at a glance (plain English)
Section titled “The producer at a glance (plain English)”The producer is one cocoindex flow (the same engine the ingest pipeline uses), wired end-to-end:
L-records (ID-131, Postgres) the client-owned OKF bundle (git repo) ┌────────────────────────────┐ ┌──────────────────────────────────────┐ │ source_documents │ Source adapter │ security/encryption.md (concept) │ │ q_a_pairs │ (the BESPOKE piece) │ products/lms.md (concept) │ │ reference_items │ ──list_concepts()──▶ │ …~30–50 concept .md files… │ │ entity_mentions/relationships│ read_concept_raw() │ references/<slug>.md (Pass-2) │ │ record_lifecycle (facet) │ sample_rows() │ index.md (progressive disclosure) │ │ record_embeddings (store) │ │ log.md (append-only change log) │ └────────────────────────────┘ └──────────────────────────────────────┘ │ Pass-1: draft each concept from L-records ONLY (no web) ▲ │ Pass-2: enrich each concept from the GATED authoritative corpus │ ▼ (host-allowlist + depth-limit), create references/<slug> │ 2-pass agent loop (Anthropic tool-use) ──validator gate (BI-13)──▶ localfs.declare_file (.md) │ + record_embeddings(owner_kind='concept') └────────────────────────────────────────────────────────────────────────────────┘ then (OUTSIDE cocoindex): git knowledge-sync writer → STAGE bundle tree (stage_only; publish commits)The flow’s three cocoindex moving parts:
- Source — the L-records Source adapter (
scripts/cocoindex_pipeline/sources/l_records.py, net-new), a hand-rolled cocoindex source in the exact shape of the existingurl_source.py(runtime_checkableprotocol, no eagercocoindeximport,mount_each-consumable). Its enumeration key is the concept rel_path (BI-2) — the cocoindex memo key, so a concept whose backing records are unchanged memo-hits and is not re-drafted (BI-18). - Transform — the two-pass enrich functions (
@coco.fn(memo=True)), each calling the Anthropic tool-use agent loop with the Source-adapter tools (Pass-1) and the gated-web tools (Pass-2). Memoised on the frozen-dataclass concept arg (theurl_source.pyEXECUTOR-VERIFY-1 precedent:memo_fingerprint._canonicalize_dataclasskeys on field VALUES) → delta-only. - Targets —
localfs.declare_file(bundle_dir / concept_path, markdown, create_parent_dirs=True)for each.md(BI-11), andmount_table_target(record_embeddings)writing oneowner_kind='concept'row per concept (BI-25).index.md+log.mdare regenerated/appended each run (BI-11/BI-18).
Outside cocoindex (BI-19): a separate git knowledge-sync writer stages the bundle working tree
to the client-owned private repo — runs land in a STAGING state (sync_bundle(stage_only=True), no
flow-level commit; the one gated commit is the human-triggered publish, {132.27}) — and is where the
human-edit-vs-regeneration reconciliation lives (§Git knowledge-sync + human-edit reconciliation).
Where the producer runs. Co-located with the ingest pipeline (scripts/cocoindex_pipeline/) as a
second flow entry point (sibling to flow.py), invoked as a discrete producer command retained for
manual operator invocation (producer/trigger.py’s run_producer_now). Cadence (S436 D4 / DR-018,
{132.16}): a successful ingest walk that touched (created/updated) one or more source_documents
rows chains ONE producer run automatically — an in-server post-walk hook inside flow.py’s app_main,
gated on the walk’s own op_id-scoped source_documents delta (no new/updated source_documents this
walk ⇒ no-op; declare_file/@coco.fn(memo=True) memoisation then scopes any triggered run to only the
affected concepts, BI-18). This is ADDITIVE to, not a replacement for, the manual producer command. The
read-only viewer (lift-and-shift) is a separate TS surface (§Viewer).
The Source adapter over L-records (the one bespoke piece)
Section titled “The Source adapter over L-records (the one bespoke piece)”The adapter implements the reference_agent Source protocol — the base.py abstract methods
list_concepts() / read_concept(ref) plus the concrete helpers sample_rows(concept, n) / find(query)
— over the typed L-records tables. It is the only part that cannot be lifted, because it encodes which
records back which concept type (a PRODUCT decision, BI-4/BI-5) rather than a generic file/row walk.
(read_concept_raw is the agent-tool wrapper in the reference_agent’s tools/source_tools.py, NOT
the ABC method — it wraps read_concept for the agent loop; the producer preserves that distinction:
the adapter exposes read_concept, the agent loop calls a read_concept_raw tool over it.)
Read-path posture (accounting for ID-115 api.*)
Section titled “Read-path posture (accounting for ID-115 api.*)”The adapter is Python and reads public.* typed tables via a direct Postgres connection (the same
connection posture the cocoindex postgres connector + mount_table_target already use in flow.py —
_coco_api.py re-exports ColumnDef/TableSchema/mount_table_target from
cocoindex.connectors.postgres). The ID-115 api.* schema-isolation (security_invoker views, PGRST106
boundary) governs the supabase-js / PostgREST APP surface, NOT a direct DB reader — so the producer
needs no new api.* view for its own reads. The single place ID-115 bites is the read-only viewer
(TS, supabase-js): it reads api.record_embeddings / api.q_a_pairs / api.source_documents, which
ID-131’s G-API group already adds to SURFACE_TABLES (ID-131 TECH:288, §api-schema migration class).
Accounted for: no api-layer work in ID-132; the viewer inherits ID-131’s api surface.
Per-concept-type table/join grid (BI-3/BI-4/BI-5)
Section titled “Per-concept-type table/join grid (BI-3/BI-4/BI-5)”The ratified type set is {topic, product, company, certification, case_study} (metric/playbook
distinct, carried in tags: not as separate types — BI-4). list_concepts() enumerates the concept
set per type from L-records; read_concept(ref) runs the joins below. A q_a_pair is never enumerated
as a concept (BI-3) — it enters only via the BI-8 table/query resource form.
| Concept type | list_concepts() enumeration grain (BI-5) | read_concept(ref) reads (the joins) | Anchors emitted (BI-9) |
|---|---|---|---|
topic | one per distinct citable answer-cluster (q_a_pairs grouped by scope_tag / primary_domain+primary_subtopic) — the spine; themes render as index.md nav, not files | q_a_pairs (the cluster) + their source_document_id parents + cited reference_items + record_lifecycle (freshness/governance) + the entity_mentions/entity_relationships neighbourhood | source_documents, reference_items (per-row); q_a_pairs via canonical://q_a_pairs?scope_tag=… (BI-8) |
product | one per per-product library / product entity (the three per-product libraries) | source_documents (the product’s docs) + product-scoped q_a_pairs + reference_items | source_documents, reference_items |
company | one (company overview + size/structure/people) | source_documents (company-overview, team-structure) + reference_items + the company entity_mentions graph | source_documents, reference_items |
certification | one per certification (ISO, Cyber Essentials, CREST, CSA CCM, NCSC Cloud Principles) | source_documents (compliance/governance) + external-evidence reference_items + the certification entity_mentions | source_documents, reference_items |
case_study | one per named client/case study OR won-bid outcome (a procurement workspace with a won form_templates.outcome) | source_documents (named-clients) + supporting q_a_pairs + reference_items; for the won-bid grain: workspace buyer identity (domain_metadata) + won-bid-provenance q_a_pairs (origin_kind='derived_from_form_response', source_workspace_id) + form_templates.outcome_notes | source_documents, reference_items |
Won-bid re-entry (DR-029 / {132.17}). The four retired
content_itemsKB-integration types re-enter here as concepts, not rows.case_studygains the won-bid enumeration source above.policy/methodology/capabilityare facettags:ontopicconcepts (methodology ≡ the existingplaybookfacet) — not new enumerated types, so no new join is added: they refine the existingtopicenumeration, which already reads all promotedq_a_pairsbyscope_tag. All won-bid-seeded concept drafts are proposal-shaped (PRODUCT BI-28) — staged for human accept/edit/reject via the BI-27 substrate, never auto-published. The won-bidq_a_pairwrite path landedb89ae76a({131.28}); the adapter reads it read-only. Physical write path ({132.29}): the won-bidcase_studygrain’s bundle file (and its path-derived embeddingowner_id) is redirected tocase-studies/won-bid/<slug>.mdso it can never collide with a same-slug named-clientcase_study— see §BI-26 reconciliation.
sample_rows(concept, n) returns a bounded sample of the concept’s backing rows for the Pass-1 prompt
context window (bounded so a large answer-cluster does not blow max_tokens).
Source protocol shape (lift the shape, write the body):
# scripts/cocoindex_pipeline/sources/l_records.py (NET-NEW; shape mirrors url_source.py)@dataclass(frozen=True) # frozen → deterministic cocoindex memo key (BI-18)class ConceptKey: rel_path: str # concept identity = cocoindex memo key + DR-016 override key (BI-2) concept_type: str # one of the BI-4 ratified set # plus the deterministic locator fields the joins need (scope_tag / domain / entity id)
class LRecordsSource: # runtime_checkable protocol; never imports cocoindex (collection safety) async def list_concepts(self) -> list[ConceptKey]: ... # abstract (base.py) async def read_concept(self, key: ConceptKey) -> ConceptRaw: ... # abstract (base.py): the joins above async def sample_rows(self, key: ConceptKey, n: int) -> list[Mapping[str, Any]]: ... # concrete (base.py) async def find(self, query: str) -> list[ConceptKey]: ... # concrete (base.py)The two-pass loop (lift-and-shift logic, gated)
Section titled “The two-pass loop (lift-and-shift logic, gated)”Pass-1 — draft from L-records ONLY (BI-15). For each ConceptKey, enrich_concept() runs the agent
loop with the Source-adapter tools only (read_concept_raw, sample_rows) — no web access. It
emits the concept body (a distilled synthesis, never a copy — BI-1/BI-17), the initial # Citations
(record anchors via the BI-6 resource builder), and the BI-12 frontmatter. The draft is gated by the
validator (BI-13) before declare_file.
Pass-2 — enrich from the GATED corpus ONLY (BI-16). run_web_pass() enriches each concept from the
client’s own authoritative sources (the 10-site-structure-and-key-urls set), with the host-allowlist +
depth-limit + path-filter knobs wired to those sources only — never the open web. These knobs are
net-new (the reference_agent’s web_ingestion config; our url_source.py enumerates
feed_articles WHERE passed=true and carries no allowlist/depth knobs — verified at head, so Pass-2
does not reuse it). Fetch substrate: httpx (already a flow dependency, flow.py:62) +
charset_normalizer (already imported). Pass-2 adds enrichment prose, creates references/<slug>.md
reference concepts, and appends # Citations (the new reference_items it cites). Pass-2 is also gated
by the validator before each write.
Pass-2 reader — lift
MarkdownFileset, not a web-fetcher (E8). Our L-raw gated corpus is a local markdown filesystem (the10-site-structure-and-key-urlssources resolve to local files), so consider lifting theMarkdownFilesetpattern fromGoogleCloudPlatform/knowledge-catalogtoolbox/enrichment/src/tools/md/fileset.ts— a ~140-LOC framework-agnostic, path-traversal-safe list/read/search over a markdown directory — for the Pass-2 GATED-CORPUS LOCAL reader, rather than bending thehttpxweb-fetch substrate above to a local path. Lift the class/algorithm only, not the ADK+Gemini harness (we standardise on Anthropic tool-use). Caveat:MarkdownFilesethandles.mdonly — docx/pdf in L-raw are already extracted intosource_documents.extracted_textand read via the Source adapter, so it covers only the raw-markdown slice of the gated corpus.
prompts/ home. The two instruction prompts (reference_instruction.md Pass-1,
web_ingestion_instruction.md Pass-2) are lifted into the existing prompt home pattern —
scripts/cocoindex_pipeline/prompts.py already hosts static instruction templates sent as cached
system blocks (prompts.py:3–15, system=[{"type":"text","text":PROMPT,"cache_control":…}]). The
producer adds two constants there (or a sibling producer/prompts.py) following the same cached-system
convention.
index.md / log.md (BI-11). [SUPERSEDED S547 — id-429
{429.3}/{429.5}, canonical regenerate_indexes() renders the ~17 themes as index.md nav
sections over the concept set (progressive disclosure, BI-5)9037022e3. The theme machinery is deleted: build_index_themes, the
unthemed_heading fallback and the theme_config parameter are gone, and passing theme_config= now
raises TypeError. The index axis is the directory itself — regenerate_indexes() emits one
index.md per directory, root to leaf, with membership taken from the writer’s own written set, and
okf_version frontmatter on the root index only (a required renderer parameter, so a nested index
cannot acquire it). BI-5’s progressive-disclosure requirement survives intact; only its mechanism
changed. See specs/id-429-bundle-index/DESIGN.md D1–D9 and DR-141. The owner ruled the underlying
client theme-index requirement not live in S546, which closed id-323.]** The log.md writer appends
one block per producer run (concepts added/changed/removed/moved + any orphaned-anchor warnings,
BI-22) and stays root-only (D8). Both are localfs.declare_file writes.
The agent-loop port — ADK+Gemini → Anthropic (the real cost; EMPIRICALLY VERIFIED)
Section titled “The agent-loop port — ADK+Gemini → Anthropic (the real cost; EMPIRICALLY VERIFIED)”The reference_agent’s loop is Google ADK + Gemini. We port it to the Anthropic tool-use surface we
already run (provider consistency with extraction.py). This is the explicit Task-A line item v3 risk 3
names as the true cost behind “lift-and-shift”. Because this is net-new external API usage (the
existing extraction calls are plain messages.create, no tools — verified above), the pinned tool-use
surface was empirically import-and-call verified (§Empirical verification): all primitives PRESENT in
anthropic==0.79.0.
The loop (reuses the existing retry/guard/model anchors):
# the Source-adapter + gated-web tools as Anthropic ToolParam listtools: list[ToolParam] = [READ_CONCEPT_RAW_TOOL, SAMPLE_ROWS_TOOL, ...] # Pass-2 adds WEB_FETCH_TOOLmessages: list[MessageParam] = [{"role": "user", "content": initial_prompt}]while True: resp = await _anthropic_retry(lambda: client.messages.create( # extraction.py:916 reuse model=ANTHROPIC_MODEL, # extraction.py:71 reuse max_tokens=MAX, system=[CACHED_PROMPT_BLOCK], messages=messages, tools=tools, tool_choice={"type": "auto"})) _guard_not_truncated(resp, "enrich_concept", MAX) # extraction.py:862 reuse if resp.stop_reason != "tool_use": break # final concept body messages.append({"role": "assistant", "content": resp.content}) tool_results = [run_tool(b) for b in resp.content if b.type == "tool_use"] # ToolUseBlock messages.append({"role": "user", "content": tool_results}) # ToolResultBlockParam[]Gemini fallback caveat (FLAGGED). The PRODUCT/v3 “Gemini lift-and-shift fallback if the port slips”
is not import-ready: the empirical probe found google.genai, google.generativeai, google.adk,
vertexai all ABSENT from the pinned environment. The fallback therefore carries a dependency-add
cost (a new pinned SDK + its own empirical verification), so it is not the cheap safety net the prose
implies. Recommendation: treat the Anthropic port as the primary AND the only import-ready path; if it
slips, the fallback is a scoped dependency-add Subtask, not a drop-in. (Surfaced to the Orchestrator.)
BI-26 reconciliation — the concept embedding key (RATIFIED uuid5; NO ID-131 schema touch)
Section titled “BI-26 reconciliation — the concept embedding key (RATIFIED uuid5; NO ID-131 schema touch)”The duality: record_embeddings.owner_id is typed uuid (ID-131 TECH M1b:288) but a concept’s
identity is a path (BI-2). Decision (ratified approach, implemented): the producer derives a
deterministic embedding key
owner_id = uuid5(_KH_CONCEPT_NS, bundle_write_path)# bundle_write_path == concept_rel_path for every grain EXCEPT the {132.29} won-bid case_study redirect (carve-out below)so the uuid column stores a stable-across-regenerations key, while the citation still uses the path
(BI-9, targeting ID-131’s citations.cited_concept_path text). The concept embedding row is
(owner_kind='concept', owner_id=uuid5(…), model=ANTHROPIC embedding model, embedding=vector(1024)),
written via mount_table_target(record_embeddings); ID-131’s per-owner_kind partial HNSW index
WHERE owner_kind='concept' (M1b) already covers it.
⚠️ {132.29} AMENDMENT — physical-path carve-out (10/07/2026; S456-ratified follow-on; DR-016 / DR-029; triggered by the 2026-07-10 checker escalation). The
uuid5argument is the concept’s PHYSICAL bundle write path, which equals its identityrel_pathfor every grain except the won-bidcase_studygrain. {132.29} (canonicalb83c6c73+b20108f5) redirects that one grain’s physical write target fromcase-studies/<slug>.mdinto a distinctcase-studies/won-bid/<slug>.mdsibling. Reason: a buyer that is BOTH a named-client entity and a won-bidissuing_organisationslugs identically, so without the redirect the two grains would share one physical.mdand — because the embedding key is path-derived — oneowner_id, silently collapsing two distinct concepts into a singlerecord_embeddingsrow (a BI-25/BI-26 violation; BI-25 requires exactly one row per added/changed concept). The redirect gives the won-bid grain a distinct file and a distinctowner_id = uuid5(_KH_CONCEPT_NS, "case-studies/won-bid/<slug>.md"). Single source of truth:bundle_writer.bundle_write_path(draft)/bundle_write_path_for_key(key), both backed by_won_bid_case_study_redirect(conditionconcept_type == "case_study" AND workspace_id is not None);flow_def.py’s G-EMBED lookup and its BI-28 provenance map both key on that physical path, while read-onlyembed.pystays a pureconcept_owner_id(rel_path) = uuid5(_KH_CONCEPT_NS, rel_path)hasher of whatever path it is handed. What does NOT move: the concept’s identityConceptKey.rel_pathremains the cocoindex memo key (BI-2/BI-18) and the DR-016 human-override key — redirecting a concept’s on-disk location cannot invalidate an override.write_bundleadditionally hard-guards the general case: two drafts resolving to one physical path raiseValueErrorbefore either is written. Why reconcile now rather than “fix”: no production consumer reads concept embeddingowner_ids today (no FK forowner_kind='concept', no search RPC joins concepts, id-135 unbuilt) — this amendment documents the carve-out so future implementers (id-135, {132.11}-adjacent consumers) build against reality.
Where the constant lives (RECOMMENDED + FLAGGED): _KH_CONCEPT_NS is a new frozen uuid5
namespace constant added to the SEED-CONTRACT family — defined in flow.py alongside
_KH_PIPELINE_DOC_NS (flow.py:1640) and asserted by ID-131’s {131.5} G-SEED / M0 freeze test (the
same Vitest test that pins _KH_PIPELINE_DOC_NS + the three seed strings, ID-131 TECH:530–536). Rationale:
the concept embedding key is a durable contract pinned at first publish (BI-20) — if _KH_CONCEPT_NS
is ever “tidied” after a bundle ships, every concept embedding key silently re-mints and the bundle vector
index orphans (the exact BI-21/BI-22 hazard the seed-contract test exists to prevent). It therefore belongs
in the frozen family, gated by the publish precondition (BI-21).
Is any ACTUAL ID-131 SCHEMA touch needed? NO — explicitly cleared. record_embeddings.owner_id is
already uuid and accepts any uuid5 value; the UNIQUE (owner_kind, owner_id, model) and the
owner_kind='concept' CHECK + partial HNSW index all already accommodate concept rows (ID-131 TECH M1b).
The only ID-131-side change is a one-line CONTRACT ADDITION to {131.5} G-SEED — add _KH_CONCEPT_NS
(and, optionally, freeze a concept:{rel_path} documentation marker) to the freeze test + the constant in
flow.py. This is not a migration and not a schema change.
ESCALATION FLAG to the Orchestrator (non-blocking, recommended): fold
_KH_CONCEPT_NSinto ID-131{131.5}G-SEED as a contract addition (constant inflow.py+ one assertion in the seed-contract freeze test). It is a Task-level coordination item, not a cross-Task Subtask dependency (sibling-only constraint preserved). Fallback if the Orchestrator prefers ID-131 frozen-as-ratified: define_KH_CONCEPT_NSin Task A and add a Task-A freeze assertion — weaker, because it is not gated before ID-131’s first-publish gate, but it keeps the dependency fully Task-internal. Recommend the ID-131 addition.
Concept-frontmatter validator (BI-13) — consume ID-133, in-Task fallback
Section titled “Concept-frontmatter validator (BI-13) — consume ID-133, in-Task fallback”BI-13 gates every concept write on a concept-frontmatter validator: required-key check + the BI-4 type
set + the BI-6 resource: scheme + the closed 12-entity / 10-relation ontology used as a semantic
linter. The ontology pass (ID-133 / v3 Task D) owns the OKF concept-frontmatter ontology + the
validator (v3 §6 item 6).
- Preferred: consume ID-133’s validator. Dependency expressed as a Task-level edge (ID-132 SOFT depends on ID-133 for the validator) — not a Subtask-level dep (sibling-only constraint).
- Fallback (removes any hard blocker): if ID-133 has not landed, define a minimal in-Task
validator — required-key check (
type/title/description/timestamp+resource:+tags:, BI-12) + BI-4 type-set membership + BI-6 scheme-string check + the closed entity/relation set as a lint. Evaluate lifting Google’sokf-skillsvalidator rather than building from zero (v3 §6 item 6). The in-Task fallback is what keeps the ID-133 dependency SOFT — ID-132 can ship its own gate and later swap to ID-133’s without a contract change (both gate the samedeclare_filecall site).
Git knowledge-sync + human-edit reconciliation (BI-14/BI-18/BI-19/BI-22)
Section titled “Git knowledge-sync + human-edit reconciliation (BI-14/BI-18/BI-19/BI-22)”⚠️ S436 AMENDMENT (2026-07-02 — DR-016, PRODUCT §S436 Amendments BI-27). The 3-way reconcile below is upgraded from flag divergence, leave file in place to flag → capture-as-override → re-apply: an approved human edit becomes a producer override (keyed by
concept_path+ section/frontmatter-field) applied on top of every fresh Pass-1/Pass-2 draft — never a direct file mutation. The producer must also EMIT its per-run proposed-change set machine-readably (it already computes it forlog.md) so the follow-on accept/edit/reject review UI (DR-013 shape) can bind to a staged diff. Runs land in a STAGING state; the single gated commit happens after the review/publish gate.{132.27} landed the staging model (10/07/2026, canonical
edcac44f).run_producer_flowcallsgit_sync.sync_bundle(..., stage_only=True)and makes no commit (ProducerRunReport.committedis always False; the flow-levelstatus_sourceseam is removed); the BI-21 hard gate and the one gated commit live solely inpublish.py’s human-triggeredpublish. Every “one commit per run” phrasing elsewhere in this document is superseded by this model and reconciled to it below.
The writer (BI-14/BI-19 — staging model, {132.27}; sole-writer model, DR-146/id-448). A git writer
outside cocoindex stages the bundle working tree to the client-owned private repository — the
producer flow calls git_sync.sync_bundle(..., stage_only=True) (apply + git add) and makes NO
commit (ProducerRunReport.committed is always False; the flow-level status_source seam is removed).
The one gated commit — and the BI-21 hard gate — live solely in the human-triggered publish
step (§first-publish gate), the origin of point-in-time rollback via the git history.
cocoindex’s [DR-146, S556: no bundle path is
declared as a cocoindex target state — localfs.declare_file lands files in the working treewrite_bundle is pure content computation returning
RunSummary.declared, and git_sync performs every physical write.] The git stage never runs as a
cocoindex target — keeps the “no out-of-band side effects in the flow” property.
⚠️ DR-146 AMENDMENT (2026-08-12, id-448 — the two removal questions, stated separately).
- BI-11’s REPORTING requirement: every run’s
log.mdblock reports the run’s concept-level diff, includingRemoved (N)for concepts confirmed absent from the source catalogue andRemoved stale indexes (N)for per-directoryindex.mdfiles whose directory lost its last concept (RunSummary.removed/RunSummary.removed_indexes). Reporting iswrite_bundle’s job and is unconditional — a removal is never silent.- The PHYSICAL-REMOVAL actor: for a DR-016 client-owned bundle the producer’s git layer —
git_sync.sync_bundle’s removal branch (orwrite_treein the non-git shape) — is the ONE actor that unlinks a bundle file. The cocoindex engine is not a bundle writer and not a removal actor: nothing is declared to it, so its orphan-delete has no bundle keyset to act on. (The S552 “the engine can never be the removal actor” finding was corrected in S553 — the live store held declarations for all 24 bundle paths — and the question is now moot by construction; the engine store is wiped as part of landing id-448, owner ruling D1.)- Removal remains caller-supplied, never inferred from a content diff:
summary.removed+summary.removed_indexes→sync_bundle(removed_paths=...), and the 3-way reconcile still protects a human-edited file from removal (conflict, not delete).
Coordination note (Platform OKF repo — DR-027 / {132.15} owner ruling, 2026-07-08). The producer’s git knowledge-sync writer is what physically creates and populates the OKF bundle repo. Per DR-027 (S441 board R6, “Ontology CVs co-locate with the bundle”) every bundle repo carries the materialised effective ontology (pinned base snapshot + client overlay); the base-CV source-of-truth moved docs-site → the canonical/platform repo under ID-133 (docs-site copies demoted to a development mirror). The Platform private OKF repo is NOT deferred — it is required infrastructure and the promotion source, the same pattern as the Platform staging/prod DBs + IONOS VPS/Coolify + the ingestion pipeline: client deploys promote FROM it, the ontology becomes part of a new client deploy (runbook updated to this effect), and E2E does not work without it. Repo provisioning is therefore a live, pre-approved item, not a deferred cross-Task question — mint both the Platform private OKF repo (promotion source, feeds {132.15}) and the client-owned bundle repo ({132.12}/{132.13}) now, with denylist-compliant naming.
The clobber/orphan hazard (BI-18/BI-22 — designed for). cocoindex’s incremental FILE-target lineage
is self-updating: when a source concept key disappears, the engine removes the .md it produced (the
property that makes BI-18 delta-only free) — but on a client-owned dir that may carry human edits, that
same lineage is a clobber/orphan hazard (a human-edited or human-added file in the managed subtree could be
fingerprint-overwritten or orphan-deleted). Design:
- Managed-keyset boundary. The producer owns a manifest of producer-generated paths (the
list_concepts()keyset +index.md/log.md+references/<slug>); cocoindex manages only that keyset. Human-authored files live outside it (or in a reservednotes/subtree the producer never declares). - 3-way reconcile before commit (BI-22 analog). The git writer compares last-producer-output
(the previous commit’s managed tree) vs current-repo state vs new-producer-output. A managed
file whose current-repo state diverges from last-producer-output = a human edit: the writer
does not silently overwrite or orphan-delete it — it flags the divergence in
log.md+ a producer warning and leaves the human file in place for resolution (exactly the BI-22 posture for a pinned anchor that no longer resolves: flag, never silently re-point/drop). - EXECUTOR-VERIFY: the exact cocoindex orphan-delete trigger (does
declare_filealone orphan, or only aDirTarget/declare_dir_targetkeyset?) must be confirmed empirically at implementation against the pinnedcocoindex==1.0.7(thelocalfssurface exposesDirTarget,declare_dir_target,mount_dir_target,declare_file— verified present, §Empirical verification), mirroring theurl_source.pyEXECUTOR-VERIFY-1 precedent.
The bundle vector index (BI-25) + first-publish gate (BI-20/BI-21/BI-23)
Section titled “The bundle vector index (BI-25) + first-publish gate (BI-20/BI-21/BI-23)”Vector index (BI-25). Each concept is embedded into record_embeddings with owner_kind='concept',
the pinned model, and owner_id = uuid5(_KH_CONCEPT_NS, bundle_write_path) (BI-26; bundle_write_path == concept_rel_path for every grain except the {132.29} won-bid case_study redirect — see §BI-26
reconciliation), written delta-only on each create/change (BI-18) via mount_table_target(record_embeddings). One store serves both L-records
hybrid_search (ID-131’s polymorphic UNION) and L-concept bundle traversal — the concept arm is queried
the same way the record arms are.
First-publish gate (BI-20/BI-21). First publication is the irreversible act. The producer flow
stages only and never gates or commits ({132.27}); both the gate and the single commit are publish-only.
The publish step is a HARD precondition gate that refuses to publish unless ID-131’s seed-contract
test is CI-green (the
{131.5} freeze test asserting _KH_PIPELINE_DOC_NS + the three seed strings and — per BI-26 —
_KH_CONCEPT_NS). Implementation: the producer publish command checks the freeze-test status (CI gate
reference, not a re-run) and aborts on red. Sequencing (BI-23): producer scaffolding + the agent-loop
port may proceed in parallel with ID-131, but concept vector indexing and first publish wait on ID-131
(record uuids for resource: + record_embeddings for the index). This is a Task-level dependency:
ID-132 depends_on ID-131 (already the ratified Task edge) — no cross-Task Subtask dep arises.
Proposed changes per invariant (BI → change map)
Section titled “Proposed changes per invariant (BI → change map)”The Checker uses this as the per-invariant compliance grid. Footprint: net-new
scripts/cocoindex_pipeline/sources/l_records.py (Source adapter) + producer/ flow module(s)
(enrich/agent-loop/bundle-writer/index-log/git-sync) + two prompts.py constants + a lift-and-shift TS
viewer; plus one ID-131 contract addition (_KH_CONCEPT_NS in flow.py + {131.5} freeze test).
No DB migration in ID-132.
| BI | Change | Key site(s) |
|---|---|---|
| BI-1 | Enrich prompts emit distilled synthesis + pointers only; no record-body copy. | producer/prompts.py (Pass-1/2 instructions); BI-17 test |
| BI-2 | Concept identity = ConceptKey.rel_path = cocoindex memo key + DR-016 override key; no identity uuid minted. PHYSICAL bundle write path == identity rel_path except the {132.29} won-bid case_study redirect. | sources/l_records.py:ConceptKey.rel_path; bundle_writer.bundle_write_path |
| BI-3 | list_concepts() never enumerates a q_a_pair as a concept; Q&A enters only via BI-8. | sources/l_records.py:list_concepts |
| BI-4 | Type set {topic,product,company,certification,case_study}; metric/playbook via tags:. | type constant + validator (BI-13); frontmatter emitter |
| BI-5 | Entry-level topic grain; themes → index.md nav; ~30–50 files. | list_concepts grain; regenerate_indexes() |
| BI-6 | resource: builder emits canonical://<table>/<uuid>; only source_documents/reference_items per-row. | producer/resource_uri.py (net-new) |
| BI-7 | Resource builder reads the seed-contract uuid5 ids; never the q_a_pairs gen_random_uuid master. | resource_uri.py; Source adapter joins |
| BI-8 | Q&A corpus referenced via canonical://q_a_pairs?scope_tag=… table/query form. | resource_uri.py |
| BI-9 | # Citations cite {source_document, reference_item, concept}; concept→concept by path. | bundle-writer; concept-path → citations.cited_concept_path (ID-131) |
| BI-10 | Only resource: + # Citations carry a Canonical uuid; validator asserts no uuid elsewhere. | validator (BI-13) |
| BI-11 | Bundle = N .md + one index.md + one log.md. | declare_file; regenerate_indexes(); log.md appender |
| BI-12 | Required frontmatter (type/title/description/timestamp/resource:/tags:); UK English/DD-MM-YYYY. | frontmatter emitter; validator gate |
| BI-13 | Validator gates every write; consume ID-133 OR in-Task minimal validator. | validator module; §Concept-frontmatter validator |
| BI-14 | Output is a client-owned private git repo (not the app repo, not the DB). | git knowledge-sync writer |
| BI-15 | Pass-1 drafts from the Source adapter only — no web. | producer/enrich.py:enrich_concept |
| BI-16 | Pass-2 enriches from the gated corpus only (host-allowlist + depth + path-filter). | producer/web_pass.py:run_web_pass (net-new knobs) |
| BI-17 | Both passes emit pointers, never copies; uncited assertion = producer defect. | prompts + BI-17 traceability test |
| BI-18 | Delta-only via cocoindex memo + declare_file lineage; no-op re-run = no-op diff. | @coco.fn(memo=True) enrich fns |
| BI-19 | git knowledge-sync: the flow STAGES per run (sync_bundle(stage_only=True), no commit — {132.27}); the one gated commit is the human publish; rollback via history. | git-sync writer; publish.py |
| BI-20 | First publish pins uuids — the point-of-no-return; owned here. | producer publish command |
| BI-21 | HARD gate: refuse publish unless ID-131 {131.5} seed-contract test green (+_KH_CONCEPT_NS). | publish precondition check |
| BI-22 | Pinned anchor never silently re-pointed; orphaned uuid → flag in log.md + warning. | resource builder diff; git-sync 3-way reconcile |
| BI-23 | Concept vector indexing + first publish wait on ID-131 (Task-level dep). | Task edge ID-132 depends_on ID-131 |
| BI-24 | Bundle sized to fit one context window — load wholesale; posture, not code. | (assertion; bundle-size check in tests) |
| BI-25 | Concept embedded into record_embeddings(owner_kind='concept'), delta-only. | mount_table_target(record_embeddings) |
| BI-26 | owner_id = uuid5(_KH_CONCEPT_NS, bundle_write_path) (== concept_rel_path except the {132.29} won-bid case_study redirect); constant joins SEED-CONTRACT; NO schema touch. | flow.py _KH_CONCEPT_NS; {131.5} freeze test; producer/embed.py; bundle_writer.bundle_write_path/_won_bid_case_study_redirect |
Empirical verification (OQ-3 / Q-EX2 — mandatory; net-new external APIs)
Section titled “Empirical verification (OQ-3 / Q-EX2 — mandatory; net-new external APIs)”Run 28/06/2026 against the installed, pinned environment (/opt/homebrew/bin/python3:
cocoindex==1.0.7, anthropic==0.79.0 per requirements.txt; TS @anthropic-ai/sdk@0.96.0 per
package.json). Import-and-call probes (signatures introspected; agent-loop request structures
constructed).
| # | Symbol / surface | Pinned version | Result | Evidence |
|---|---|---|---|---|
| 1 | cocoindex.connectors.localfs.declare_file(path, content, *, create_parent_dirs=False) -> None | cocoindex==1.0.7 | PRESENT — signature matches v3 §7.2 (declare_file(output_dir/f'{name}.md', markdown, create_parent_dirs=True)) exactly | dir(localfs) → ['DirTarget','DirWalker','File','FilePath','declare_dir_target','declare_file','dir_target','mount_dir_target','walk_dir',…]; inspect.signature as cited |
| 2 | anthropic tool-use: messages.create(tools=, tool_choice=) (sync and async) | anthropic==0.79.0 | PRESENT | "tools" in signature(Messages.create).parameters → True; same for AsyncMessages.create |
| 3 | anthropic.types: ToolParam, ToolUseBlock, ToolResultBlockParam, MessageParam, ToolChoiceToolParam, ToolChoiceAutoParam, TextBlock; Message.stop_reason | anthropic==0.79.0 | PRESENT | all hasattr(types, …) True; "stop_reason" in Message.model_fields True |
| 4 | Agent-loop request structures (ToolParam + tool_use assistant turn + tool_result user turn) constructible | anthropic==0.79.0 | PRESENT (constructs OK) | built ToolParam/MessageParam with tool_use/tool_result content blocks without error |
| 5 | Gemini fallback SDK (google.genai / google.generativeai / google.adk / vertexai) | n/a | ABSENT (all four) | every __import__ raised ModuleNotFoundError — fallback requires a dependency-add (FLAGGED, §agent-loop port) |
No ABSENT/SIGNATURE_DRIFT/BEHAVIOUR_DRIFT on any symbol the producer DEPENDS on (items 1–4
PRESENT, signatures match). Item 5 (Gemini fallback) is ABSENT but the producer does not depend on it
— it is the optional fallback, surfaced with its dependency-add cost. Spec returns clean for ratification.
Empirical-verification note (scope). The reference_agent is the external Google ADK implementation
we port FROM — it is not vendored (grep zero hits) and is therefore out of the import-and-call
scope (the discipline covers external-library symbols we import — cocoindex, anthropic — not a
reference design we re-author natively). Internal Canonical symbols
(ANTHROPIC_MODEL/_anthropic_retry/_guard_not_truncated/localfs/mount_table_target) are covered
by gitnexus/ast-dataflow, not this block. Drift caveat (noted, non-blocking): the _coco_api.py
façade docstring still says “Pinned version: cocoindex==1.0.3” while requirements.txt pins
cocoindex[postgres]==1.0.7; both walk_dir and declare_file are present in the installed 1.0.7, so no
drift affects this spec — but the façade docstring/version-pin test should be reconciled (a small ID-131
or housekeeping item; surfaced to the Orchestrator).
Testing and validation
Section titled “Testing and validation”bun run test (Vitest; never bun test) for any TS (viewer); python3 -m pytest scripts/tests/ for the
producer flow; behaviour-first per test-philosophy.md. Map to BIs:
- BI-2/BI-18 → producer memo test: a no-op re-run over unchanged L-records produces a no-op diff (no
declare_filerewrite, no new commit); changing one backing record re-drafts only that concept (theurl_source.pyfrozen-dataclass memo precedent). - BI-3/BI-4/BI-5 → Source-adapter test:
list_concepts()never yields aq_a_pair; type set is the ratified five; topic grain is entry-level (themes appear only inindex.md); a fixture corpus yields ~30–50 files. - BI-6/BI-7/BI-8/BI-9/BI-10 → resource/citation contract test: per-row anchors only for
source_documents/reference_items;q_a_pairsonly viacanonical://q_a_pairs?scope_tag=…; concept→concept by path; the mastergen_random_uuidis never emitted; a uuid never appears outsideresource:/# Citations. - BI-13 → validator gate test: a concept missing a required key / wrong type / bad scheme is not
written (the
declare_filecall site is gated). - BI-15/BI-16/BI-17 → pass-isolation test: Pass-1 makes no web call (mock asserts zero egress); Pass-2 egress is confined to the host-allowlist; every asserted datum is traceable to a cited record or cited gated reference.
- BI-19/BI-22 → git-sync test: a producer run STAGES only (
sync_bundle(stage_only=True), no commit;ProducerRunReport.committedFalse), the one gated commit is asserted only in thepublishpath ({132.27}); a human-edited managed file is flagged inlog.md+ warning, not overwritten/orphaned (the 3-way reconcile). - BI-21 → publish-gate test:
producer publishaborts when the ID-131 seed-contract test is red. - BI-25/BI-26 → embedding-key test:
owner_id == uuid5(_KH_CONCEPT_NS, bundle_write_path); stable across two runs; onerecord_embeddings(owner_kind='concept')row per concept; renaming the constant changes the key (proves it is the frozen contract). {132.29} collision test: a same-slug named-client- won-bid
case_studypair yields two distinct rows (each keyed on its physicalbundle_write_path, the won-bid one undercase-studies/won-bid/<slug>.md) — never one silently-collapsed row.
- won-bid
- Agent-loop port → a stubbed tool-use loop test: a
stop_reason=='tool_use'response triggers a tool execution +tool_resultturn; a final response terminates the loop;_guard_not_truncated/_anthropic_retryare exercised.
Risks and mitigations
Section titled “Risks and mitigations”- Agent-loop port is the real cost (MEDIUM-HIGH, v3 risk 3). Mitigation: empirically-verified
primitives (above); reuse
_anthropic_retry/_guard_not_truncated/ANTHROPIC_MODEL; Gemini fallback is not drop-in (ABSENT SDK) — scope it as a dependency-add Subtask only if the port slips. - cocoindex orphan-delete clobbers human edits (MEDIUM, BI-22). Mitigation: managed-keyset boundary
- 3-way reconcile + EXECUTOR-VERIFY of the exact orphan trigger on
cocoindex==1.0.7.
- 3-way reconcile + EXECUTOR-VERIFY of the exact orphan trigger on
- Seed-contract drift orphans concept anchors AND the embedding key (MEDIUM, irreversible-flavoured,
BI-21/BI-26). Mitigation:
_KH_CONCEPT_NSjoins ID-131’s frozen seed-contract test; publish gate refuses on red. - BI-26 reconciliation mis-sited (LOW — cleared). Confirmed NO ID-131 schema touch (owner_id already
uuid); only a one-line{131.5}contract addition — FLAGGED for Orchestrator coordination. - Validator dependency on ID-133 (LOW). Mitigation: SOFT Task-level dep + in-Task minimal validator fallback (no cross-Task Subtask dep; sibling-only constraint preserved).
- EMB-STORE lands after the producer (MEDIUM, sequencing, BI-23). Mitigation: ID-132
depends_onID-131 (ratified Task edge); scaffolding + port proceed in parallel, vector indexing + publish wait. - Pass-2 over-reach beyond the gated corpus (MEDIUM, BI-16). Mitigation: net-new host-allowlist + depth + path-filter wired to the client’s authoritative sources only; egress test asserts confinement.
Parallelization — file-ownership groupings for {132.3} PLAN
Section titled “Parallelization — file-ownership groupings for {132.3} PLAN”Worktree isolation per group; cherry-pick parallel branches; agents git fetch origin {branch} && git reset --hard origin/{branch} first. Sequencing: G-SOURCE + G-LOOP (parallel, can start before
ID-131 lands) → G-PASS1 → G-PASS2 → G-BUNDLE (index/log/validator) → G-EMBED + G-GITSYNC →
G-PUBLISH-GATE (waits on ID-131) → G-VIEWER (independent, last). The _KH_CONCEPT_NS contract addition is
a coordination item handed to ID-131 {131.5}, not an ID-132 Subtask.
- G-SOURCE (bespoke, early):
sources/l_records.py— the Source adapter + the per-type joins +ConceptKey. No ID-131 runtime dep to BUILD (joins target ID-131 tables, exercised against fixtures). - G-LOOP (parallel, early): the Anthropic tool-use agent loop + tool definitions; reuse
_anthropic_retry/_guard_not_truncated/ANTHROPIC_MODEL. - G-PASS1:
enrich_concept(L-records-only draft) + theresource:/# Citationsbuilder (resource_uri.py) + the BI-12 frontmatter emitter. - G-PASS2:
run_web_pass+ the net-new host-allowlist/depth/path-filter knobs +references/<slug>creation. - G-BUNDLE:
regenerate_indexes()(index.md), thelog.mdappender, and the validator gate (consume ID-133 OR the in-Task minimal validator) on everydeclare_file. - G-EMBED: the concept embedding write to
record_embeddings(owner_kind='concept')+owner_id=uuid5(_KH_CONCEPT_NS, bundle_write_path)(BI-25/BI-26; physical-path-keyed — the {132.29} won-bidcase_studycarve-out). Waits on ID-131record_embeddings. - G-GITSYNC: the git knowledge-sync writer (one commit/run) + the 3-way human-edit reconcile.
- G-PUBLISH-GATE: the first-publish HARD gate (BI-20/BI-21) checking the ID-131 seed-contract test. Waits on ID-131.
- G-VIEWER (independent, last): lift-and-shift the read-only viewer (TS, reads
api.*per ID-115).
Sibling-only Subtask deps hold within each group; the cross-group ordering above is the Task-level sequence
{132.3}encodes as sibling deps inside ID-132. The two cross-Task edges (ID-131 for records/EMB-STORE/seed-contract; ID-133 SOFT for the validator) live at Task level — no cross-Task Subtask dependency arises.
Quality bars (inherited by every Executor)
Section titled “Quality bars (inherited by every Executor)”UK English (DD/MM/YYYY, “colour”, “organisation”) in the spec and in the produced bundle; semantic
design tokens only for the viewer UI; auth.success + authFailureResponse(auth);
getAuthorisedClient() / sb() / tryQuery() Supabase safety for any TS DB access; no barrel re-exports
(direct file imports); TanStack Query only for any viewer data fetch (keys in lib/query/); bun run test (never bun test); behaviour-first tests (test-philosophy.md). Python pipeline: keep the
_coco_api.py insulation façade discipline (off-surface cocoindex symbols resolve through it — a version
bump is a one-file fix); collection-safety (no eager cocoindex import on a module-import path).
Tool-discipline: gitnexus impact({direction:'upstream'}) before modifying any existing symbol (warn
HIGH/CRITICAL), gitnexus detect_changes() before committing, gitnexus rename for renames; grep for the
Python pipeline + any SQL. Pre-ratification empirical import-and-call verification is recorded above for
every net-new external API.
Non-goals (ID-132)
Section titled “Non-goals (ID-132)”The L-records refactor + record_embeddings/record_lifecycle/citations schema (ID-131 — consumed,
never altered; the only ID-131-side touch is the _KH_CONCEPT_NS contract addition to {131.5}); the
ontology pass + the allowed_types/allowed_relations register + the canonical concept-frontmatter
validator (ID-133 — consumed, with an in-Task fallback); the {127.4} promotion-confidence corpus
(Task C). ID-132 changes no L-records table, RPC, or pipeline extraction write-site, and adds no DB
migration.