Skip to content

feat(blackbox): add per-call subscription attribution store - #799

Merged
Kyzcreig merged 6 commits into
mainfrom
daedalus-opus/t_75b1f823-api-calls
Sep 21, 2026
Merged

Kyzcreig merged 6 commits into
mainfrom
daedalus-opus/t_75b1f823-api-calls

Conversation

@Kyzcreig

Copy link
Copy Markdown
Collaborator

Scope

C1 store half only: guarded-additive turn_api_calls table and turns rollup columns, CanonicalUsage insertion, parent-retention cascade. No accumulator or transport changes; no live DB writes.

Verification

scripts/run_tests.sh tests/plugins/blackbox -q
15 files, 151 tests passed, 0 failed (100% complete) in 111.3s.
Initial per-call tests: 8 failed because insert_api_call was absent; then 8 passed.
AC7: 500 synthetic legacy rows copied using VACUUM INTO; actual frozen tokens.ace/journal/reprice SQL and unchanged cost/context readers produce byte-identical consumer output after migration and populated attribution. Repeated migration preserves SQL dump. Malformed INSERT control detects swallowed telemetry failure.

Review notes

Kyzcreig/hermes-agent resolves to ANG-Ventures/hermes-agent (verified via gh). No upstream port: Blackbox is fork-specific. AC7 covers consumer SQL/outputs, not a full external dashboard bake or separately executed old checkout.
Two SQL fixture EOF lines have whitespace flagged by git diff --check. Cleanup was refused by the committed-test guard, which misclassified this task worktree as live; left unchanged rather than bypass the guard.
Attribution validation rejects values outside wire/pinned/inferred/external; duplicate call keys raise instead of replacing records.

@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

🔴 HOLD — do not merge (Apollo, subs-ace lane). FleetReview on d7fdcac5 escalated with 3 unresolved P1 (judge arm failed closed on a 503, so the findings were never posted here — mirrored from the ACE-AI record):

  1. Nullable composite key — turn_api_calls PRIMARY KEY(turn_id, seq) with neither column NOT NULL; SQLite accepts NULL PK parts, so duplicate/NULL rows insert. → NOT NULL on both + reject in insert_api_call.
  2. Orphan retention — sweep() only cascades turn_api_calls through the selected turns; a call row whose parent insert_turn never landed is never swept. → also delete parentless rows older than the cutoff.
  3. INSERT OR REPLACE INTO turns erases served_subs_json/attribution on re-finalize (columns not in the list). → UPSERT that updates only TurnRecord-owned columns.

Work returns to kanban card t_75b1f823 (subs-ace). Re-push triggers a fresh review; lands via fleet-merge.sh when green, no bypass.

… upsert

Three P1 findings from FleetReview on d7fdcac:
- turn_api_calls PRIMARY KEY(turn_id, seq) accepted NULLs (SQLite allows NULL
  in non-INTEGER PK columns, and NULLs never collide) -> NOT NULL on both
  columns plus a boundary reject in insert_api_call.
- sweep() cascaded call rows only through the selected turns, so a call whose
  parent insert_turn never landed outlived retention forever -> bounded
  parentless delete on the call's own ts, same transaction.
- insert_turn used INSERT OR REPLACE, which deletes the row first and NULLed
  served_subs_json/attribution on any re-finalize -> INSERT ... ON CONFLICT
  DO UPDATE over the TurnRecord-owned columns only.
… not OR REPLACE

The probe matched the literal 'INSERT OR REPLACE INTO turns', so the upsert
rewrite made its mutation inert (caught by the probe's own self-check).
Match the turns INSERT regardless of conflict strategy.
@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

FleetReview

Confidence: 2/5

Findings

  • P1 plugins/blackbox/store.py:863 — Active-call deletion
  • P2 tests/plugins/blackbox/fixtures/pre_api_calls.sql:16 — Invalid Fixture
  • P0 tests/plugins/blackbox/test_api_calls_compat.py:118 — Placeholder-probe test still matches the removed INSERT OR REPLACE INTO turns string — test now fails deterministically
  • P2 tests/plugins/blackbox/test_api_calls.py:41 — Missing Assertion
  • P0 plugins/blackbox/store.py:340 — Generated UPSERT makes the compatibility test fail unconditionally

FleetReview provenance · models: B=gpt-5.6-sol, C=claude-code-opus-5, F=gpt-5.6-sol, G=grok-4.6 · cost: $8.57 · duration: 23m 42s · rounds: 1 · files examined: 5

@Kyzcreig

Copy link
Copy Markdown
Collaborator Author

FleetReview

Confidence: 3/5

Findings

  • P1 plugins/blackbox/store.py:861 — Premature Cleanup
  • P2 tests/plugins/blackbox/test_api_calls_compat.py:85 — Weak Oracle

FleetReview provenance · models: B=gpt-5.6-sol, C=claude-code-opus-5, F=gpt-5.6-sol, G=grok-4.6 · cost: $7.11 · duration: 32m 51s · rounds: 1 · files examined: 5

Parentless turn_api_calls rows are the normal state of an in-flight turn
(calls are appended as they happen; the parent turns row only lands at
finalize). Sweeping them on the retention cutoff alone deletes a live
turn's call ledger whenever retention is short or the turn outruns it.

Orphan deletion now requires the row to be past BOTH retention and
_ORPHAN_GRACE_S (24h): orphan_cutoff = now - max(grace, retention).

Verified: scripts/run_tests.sh tests/plugins/blackbox -q -> 15 files,
156 tests passed, 0 failed, EXIT=0. Mutation gate: replacing
orphan_cutoff with the retention cutoff fails exactly
test_orphan_sweep_respects_grace_period_independent_of_retention
(1 failed, 12 passed).
@Kyzcreig
Kyzcreig added this pull request to the merge queue Sep 21, 2026
Merged via the queue into main with commit 35d6bb4 Sep 21, 2026
40 checks passed
@Kyzcreig
Kyzcreig deleted the daedalus-opus/t_75b1f823-api-calls branch September 21, 2026 13:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant