Skip to content

Verifier report — cocoindex-ledger-api TECH.md (S242 WP1.2)

Verifier report — cocoindex-ledger-api TECH.md (S242 WP1.2)

Section titled “Verifier report — cocoindex-ledger-api TECH.md (S242 WP1.2)”

Date: 18/05/2026 (S242) Drafter commit: d9790760 (agent worktree agent-a916593d21171c156, branch worktree-agent-a916593d21171c156) Spec under review: /Users/liamj/Documents/development/knowledge-hub/.claude/worktrees/agent-a916593d21171c156/docs/specs/id-36-cocoindex-ledger-api/TECH.md (381 lines) Verifier head: 70336a3c (post-reset to origin/content-items-investigation) Verdict: FAIL — one blocking finding plus three non-blocking findings; rework required before merge to main.


The spec covers the brief well at a structural level: it includes the boundary statement, four functions with signatures, the TS landing-path table, three terminal states with the partial-completed motivator, the LMDB exposure boundary, file:line landing points, a test plan, failure-mode + error-envelope sections, and three gap flags in the construction-guide §5.3 format. UK English clean. No inline TBD / TODO. Feature-spec pattern (Summary+Behavior via /write-tech-spec) — not the construction-guide P-N pattern — correctly applied per S241 critical-rule 5.

However, the spec carries one factual error about a load-bearing precedent (Result<T, E> shape from @/lib/supabase/safe) that would propagate into implementation as a bug, plus two file:line citation slips. These warrant FAIL rather than PASS-WITH-NOTES because the Result<T, E> error is the central pattern the brief specifically called out (“Result envelope per tryQuery() precedent”), and the spec’s example code in §2.3 shows the wrong shape — an implementer reading this spec at T1.3 build-time would copy the wrong discriminant.


B1 — Result<T, E> shape is factually wrong (§2.2.1 + §2.3 + §2.2.3 + §2.2.4). The spec describes the Result<T, E> envelope as { data: T, error: null } | { data: null, error: E } with callers branching on error (spec line 141 + example code at lines 192-200). The actual Result<T, E> from @/lib/supabase/safe.ts:108-110 is:

export type Result<T, E = SupabaseError> =
| { ok: true; data: T }
| { ok: false; error: E };

The discriminant is ok (boolean), not error. The actual tryQuery() precedent (verified at lib/supabase/safe.ts:160-181) returns { ok: false, error: ... } on failure and { ok: true, data: ... } on success. The spec’s own §2.2.3 description (“Returns data: null when status is completed / failed / in_progress”) is also incompatible with the actual shape — under the real Result<T, E>, returning a null data success is { ok: true, data: null }, not data: null at the top level. The isOk() type guard at lib/supabase/safe.ts:190-194 reinforces the ok discriminant.

This finding is blocking because:

  1. The spec explicitly cites tryQuery() as the precedent (§2.3 line 203). The implementer at T1.3 build-time will pattern-match on the spec’s example, not the actual safe.ts source, and will produce an inconsistent Result<T, E> shape that breaks the codebase convention.
  2. CLAUDE.md “Silent failures in Supabase calls” gotcha calls out { ok, data } / { ok, error } as the discipline. The spec inverts the discipline by branching on error.
  3. The spec’s data: null overload for getPartialResolveDetail (§2.2.3) and correlateToPipelineRun (§2.2.4) reads as “the error field is absent” — under the real Result<T, E>, that semantic is encoded as { ok: true, data: null }, which is a different and stricter type contract.

Fix: rewrite §2.2.1, §2.2.3, §2.2.4, §2.3 example to use { ok: true, data: T } | { ok: false, error: E } shape; update the example handler at lines 192-200 to branch on if (!result.ok); reconcile the data: null overloads explicitly as { ok: true, data: null }.

N1 — lib/pipeline/start-run.ts:1-60 citation is wrong (spec §1.5 line 58). The spec cites lib/pipeline/start-run.ts:1-60 as the landing site for startPipelineRun(). Verified: start-run.ts is 180 lines; the function startPipelineRun actually starts at line 100 and ends at line 179. Lines 1-60 are the file-header comment block plus the start of the StartPipelineRunParams interface — the function is not in that range.

Fix: change citation to lib/pipeline/start-run.ts:100-179 (function body), or lib/pipeline/start-run.ts:78-179 (JSDoc + function body).

N2 — §1.4 “schema-volatility insulation” framing is extrapolated, not literally cited (spec §1.4 ref to phase-b-prerequisite-2-cocoindex-deep-dive.md §3.4). The spec attributes “schema-volatility insulation” to §3.4 of the cocoindex deep-dive. Verified content of §3.4 (deep-dive lines 147-151): the section names the gap (“cocoindex’s per-flow-run ledger query semantics + retention aren’t documented at the TS-facing API level”) and recommends keeping pipeline_runs as the KH-facing surface; it does not explicitly use the phrase or framing “schema-volatility insulation”. The drafter is plausibly extending the framing — but the cite reads as if the source explicitly states it.

Fix: either soften the cite (“paraphrased from §3.4”) or expand the inline quote so the extrapolation is visible.

N3 — 00-synthesis-v2.md cite path is implicit, not explicit (multiple references). The spec cites 00-synthesis-v2.md §5.2 row 5 and 00-synthesis-v2.md §5.1 without specifying the subdirectory docs/plans/phase-0-investigation/10-feedback-investigation-findings/. The file lives only in that subdirectory; the top-level phase-0-investigation/ has no 00-synthesis-v2.md. A future agent grepping for the path will resolve to the wrong location (or to none).

Fix: prefix every 00-synthesis-v2.md citation with the path 10-feedback-investigation-findings/00-synthesis-v2.md (matches the actual file location).

S1 — §3.4 msw (Mock Service Worker) introduces a new test dependency without a CLAUDE.md or package.json check. The spec proposes msw as the sidecar mock harness without verifying that msw is already a project dependency. If msw is not currently in package.json, the spec implicitly creates a new dependency-add task that is not in scope and is not reflected in the test plan. Alternative: stub the ledger-client module directly via vi.mock() (already named at §3.1) without msw.

Fix: verify msw presence in package.json before T1.3 build, or remove msw from the test design and rely on the vi.mock('@/lib/pipeline/ledger-client', ...) pattern already described at §3.1.

S2 — §2.7 “implementation will overload as needed” is a buried TBD. The spec says: “the public function signatures default to ‘scope-from-auth’ mode … for service-role callers, an overload accepts an explicit workspace_id parameter. … Document in JSDoc. Add a Vitest test that calling the API without authorised context AND without explicit workspace_id returns rls_denied.” The “overload as needed” framing is acceptable but soft — it would benefit from being either (a) explicit in the §2.2 signatures (e.g. getFlowRunMetrics(flow_run_id: string, options?: { workspace_id?: string }): Promise<Result<...>>) or (b) explicitly flagged as a follow-up subtask at T1.3 build-time. As written, the overload requirement might be missed in implementation.

Fix: add the optional options.workspace_id to the signatures in §2.2, or explicitly call it out as the second-half of §2.7.

S3 — §4.5 Bun fetch + HTTP 204 mitigation is well-intentioned but the contract claim deserves verification. The spec asserts: “The cocoindex sidecar HTTP transport should not return 204 — it returns 200 with a JSON body”. This is an assumption about the sidecar contract that is itself flagged as STILL-OPEN in [GAP-LEDGER-003]. If the sidecar HTTP contract turns out to be 204-on-empty-stages (as some REST sidecar shapes do), the §4.5 mitigation reverses into a real implementation issue. The fix is fine but should explicitly link back to [GAP-LEDGER-003] resolution.

Fix: add an inline note in §4.5: “Contract verification depends on [GAP-LEDGER-003] spike.”


#AxisVerdictNotes
1Brief compliancePASSAll seven acceptance criteria from the drafter brief are addressed: boundary statement (§1.2), TS API surface with signatures (§2.2), partial-resolution framing per 02-data-flow.md §7.2 (§1.3), LMDB exposure boundary (§1.4), file:line landing points (§1.5 + §2.1), test plan (§3), failure modes + error envelope (§2.3 + §4).
2No-fabrication discipline (citation spot-check)PASS-WITH-NOTESSpot-checked ≥5: record-run.ts:128-212 ✓ (function exact); record-run.ts:34-37 ✓ (JSDoc for completed_with_errors); record-run.ts:38-41 ✓ (PipelineRunStatus type); database.types.ts:1768-1842 ✓ (pipeline_runs schema, closing brace at 1842); 02-data-flow.md §7.2 ✓ (text supports N6 RESOLVED rollup framing); cocoindex-deep-dive.md §3.4 ✓ ledger gap; §3.5 ✓ LMDB-per-container. Failures: start-run.ts:1-60 ✗ (function actually at 100-179, see N1); cocoindex-deep-dive.md §3.4 as “schema-volatility insulation” ✗ (paraphrase, not literal — see N2); 00-synthesis-v2.md path implicit ✗ (see N3).
3Boundary correctness (pipeline_runs rollup vs LMDB)PASS§1.2 boundary table + §1.4 LMDB-exposure prohibition + §2.5 explicit non-modification of recordPipelineRun() / startPipelineRun() collectively make the boundary tight. Per-instance LMDB multi-container framing addressed in §4.2 with DEFERRED-v1.1 disposition.
4Result envelope pattern (tryQuery() consistency)FAILSee B1. The discriminant is error in the spec but ok in the actual codebase.
5Gap-flag format (§5.3)PASSThree gap flags all include Category + Sources checked + Prerequisite work. None is a disguised TBD.
6Tag vocabulary ([RATIFIED-...], [DEFERRED-v1.1])PASS[DRAFT — Liam review pending] status correct; DEFERRED-v1.1 used for MCP wrappers (§2.9) and gRPC transport (§5.1.2) and multi-instance coordinator (§5.1.3). No inline TBD / XXX / TODO.
7Feature-spec pattern (Summary+Behavior via /write-tech-spec)PASSSpec uses Context + Proposed changes + Testing + Risks + Follow-ups — the /write-tech-spec skill structure. Not construction-guide P-N pattern, per S241 critical-rule 5.
8CLAUDE.md gotcha applicationPASS-WITH-NOTEStryQuery() cited (§1.5 line 62 + §2.3) ✓; “No barrel re-exports” cited (§2.1 line 79) ✓; getAuthorisedClient() discriminated union cited (§2.7) ✓; Bun fetch + HTTP 204 cited (§4.5) ✓. But: the spec’s described Result<T, E> shape contradicts the gotcha (see B1).
9Partial-resolution invariants (three terminal states)PASScompleted / partial_completed / failed distinguished correctly in §1.3 + §2.2.1. The “binary conversion produced empty content_text → LLM extraction skipped” motivator is consistent with 02-data-flow.md §7.2 + the cocoindex 6-stage topology at §3.1.
10Regression-fixture lock-in (partial_completed contract)PASS§3.3 specifies __tests__/lib/pipeline/fixtures/partial-resolve-malformed-pdf.json with explicit invariant assertions (six stages, short_circuit_reason === 'no_upstream_output', exact skipped_stages array). Lock-in surface is concrete.
11UK EnglishPASSSearched for behavior, color, organization, serialization, optimization, capitalize — zero matches.
12Path-correction disciplinePASSSpec lives only in worktree (/Users/liamj/Documents/development/knowledge-hub/.claude/worktrees/agent-a916593d21171c156/docs/specs/id-36-cocoindex-ledger-api/TECH.md). Main repo docs/specs/ has no cocoindex-ledger-api/ directory. No leak detected.

FAIL — rework required before merge. One blocking finding (B1 — Result<T, E> discriminant inversion); three non-blocking findings (N1 file:line off, N2 framing extrapolation, N3 implicit path); three suggestions.

Blocking count: 1 Non-blocking count: 3 Suggestion count: 3

Path to PASS-WITH-NOTES: rewrite §2.2.1 / §2.2.3 / §2.2.4 / §2.3 to use { ok: true, data: T } | { ok: false, error: E } shape; fix the example handler to branch on if (!result.ok); fix N1 + N2 + N3. Suggestions (S1, S2, S3) are nice-to-have but do not block PASS.

Path to FAIL → PASS straight-through: apply the B1 + N1 + N2 + N3 fixes in a follow-up commit on the same worktree branch; verifier re-runs (this same report applies to the corrected spec by axis count).


  • Spec was read in full (381 lines) from the drafter worktree path.
  • Citations spot-checked: 8 distinct file:line claims verified, 1 confirmed wrong (N1), 1 paraphrase noted (N2), 1 path-prefix issue noted (N3).
  • The Result<T, E> shape claim was verified by reading lib/supabase/safe.ts:104-194 end-to-end.
  • The drafter brief acceptance criteria were tracked one-for-one in §3 row 1.
  • Phase 0 investigation docs referenced for 02-data-flow.md §7.2 N6 RESOLVED + phase-b-prerequisite-2-cocoindex-deep-dive.md §3.3 / §3.4 / §3.5; all support the spec’s framing.
  • Verifier worktree HEAD verified at 70336a3c (S242 continuation prompt). Worktree status clean prior to verifier-report write.
  • One CLAUDE.md gotcha applied during verification: “Bash CWD drifts into worktree dirs after Read” — handled by reading worktree files via absolute paths and running git operations from main-repo working tree only.