Skip to content

DR-146: git_sync is the sole writer of the bundle tree; the cocoindex engine is not a bundle writer

DR-146 — git_sync is the sole writer of the bundle tree

Section titled “DR-146 — git_sync is the sole writer of the bundle tree”

localfs.declare_file is a declaration, not a write: the cocoindex engine performs the physical write only after the flow body returns. Every bundle artefact — concepts, per-directory indexes, ontology.json, context.jsonld, log.md — was declared this way, which put the engine’s write after git_sync.sync_bundle.

That ordering produced a defect class, not a defect. Anything git_sync writes is overwritten by the engine afterwards. Two live instances:

  • The log.md findings block. sync_bundle merges its reconcile findings into log.md and writes them; the engine then rewrites log.md from the pre-merge declared content, so the DR-016 human-edit warning never reaches disk (id-445 AC-3).
  • The override fold. git_sync.reapply_overrides’ own docstring says the fold happens “BEFORE it is handed to declare_file / sync_bundle, but flow_def.py:648 folds after write_bundle has declared every concept. The engine writes the un-overridden bytes over the applied override, leaving the working tree and the git index disagreeing.

The obvious narrow fix — let sync_bundle merge, then re-declare log.md — is impossible. Measured against the installed pin (cocoindex 1.0.18), a second declare_file on one path in one flow body raises ValueError: Invalid Request: Target state already declared with key. Amendments cannot be threaded back into a declaration after the fact.

Nothing in OKF asks for the declarative write model. The upstream reference agent (GoogleCloudPlatform/knowledge-catalog, okf/src/reference_agent) writes every file with path.write_text(), ships no log.md writer at all, and never unlinks. The indirection is ours, and so is the defect class.

No bundle path is registered as a cocoindex target state. git_sync writes the bundle tree; write_bundle becomes pure content computation.

write_bundle already returns the complete {rel_path: content} map as RunSummary.declared (added by id-445 for an unrelated reason) — that is the enabling step. sync_bundle reads the tree, decides, and writes; nothing writes after it. Executed in id-448.

The engine keeps everything it is actually good at: source ingestion, memoisation, and incremental re-drafting via enrich_concept / run_web_pass. write_bundle is plain orchestration, not @coco.fn, so none of that is touched.

Keep declarations, compute the findings before log.md is declared. Requires splitting sync_bundle into a decide phase and an apply phase and threading the result back through write_bundle. It fixes the log.md instance and leaves the override instance, and it adds machinery where the alternative deletes it.

Have the producer write files directly, mirroring the reference agent. Rejected: it breaks human-edit detection. _decide_and_apply must read the tree before this run’s content lands on it; a direct write from write_bundle would clobber the human’s edit before sync_bundle ever saw it, failing DR-016’s central promise without a symptom. The engine’s bad ordering is currently the only thing making that branch reachable, which is why the writer moves to git_sync rather than to write_bundle.

The engine store must be wiped when this lands. A read of the live platform-staging store (/cocoindex-state/lmdb/mdb) found declarations for all 24 bundle paths, put there by the producer’s own earlier declare_file calls. Once they stop being declared they drop out of the keyset, and the engine’s orphan-delete branch is free to delete the whole bundle. The wipe costs a memo rebuild only; Supabase remains canonical, and bundles are synthetic pre-launch.

This corrects an S552 finding recorded as measured. id-445 concluded “for a DR-016 client-owned git-clone bundle the engine can never be the removal actor”, reasoning that cloned files are never declared and so cannot drop out. That holds only for the first run on a freshly-provisioned container; it stopped being true the moment a run completed. The conclusion was reached by contrapositive from a throwaway local probe, with the real store explicitly recorded as unread.

A non-git write path is still required. When repo_path is None (tests, and any non-git shape) flow_def returns before sync_bundle, so nothing would write. That path must exist and must not be the one the deployed shape takes — bundle_dir == repo_path in deployment, both being OKF_BUNDLE_DIR.

The removal actor becomes documentable. BI-11’s reporting requirement and the physical-removal actor can now be stated separately in TECH, with a single answer rather than two competing channels.