Skip to content

fix(sync): R5 — code↔DB sync reliability & correctness (closes 22 audit findings) - #177

Merged
sshlg merged 24 commits into
mainfrom
fix/sync-remediation-2026-06-25
Jun 26, 2026
Merged

fix(sync): R5 — code↔DB sync reliability & correctness (closes 22 audit findings)#177
sshlg merged 24 commits into
mainfrom
fix/sync-remediation-2026-06-25

Conversation

@sshlg

@sshlg sshlg commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

R5 — code↔DB sync reliability & correctness

Closes all 22 findings of the 2026-06-25 five-specialist code↔DB synchronization audit
(9 High · 9 Medium · 4 Low). Executed as 18 TDD tasks across 5 waves via subagent-driven
development; every task got a fresh implementer + a two-stage (spec + quality) review, and the
whole branch passed a final opus review (Ready to merge, 0 Critical / 0 Important).

  • Spec: docs/superpowers/specs/2026-06-25-sync-remediation-design.md
  • Plan + per-finding validation log: docs/superpowers/plans/2026-06-25-sync-remediation.md

Gates (verified locally)

  • Combined unit + integration: 4560 passed / 0 failed
  • Coverage 75% (≥ 72% CI floor)
  • ruff format + ruff check + mypy clean; alembic upgrade head / downgrade base clean
  • No frontend changes

What changed

Reliability (High)

  • H1 daily-sync parent run emits a continuous heartbeat (targeted UPDATE, no version
    lost-update) → the stale-run reaper no longer kills a healthy multi-minute sync.
  • H8 RunCoordinator.start translates the single-active IntegrityError into a clean
    RunAlreadyActiveError/409 with session rollback; the partial-unique active index is mirrored
    onto the model for create_all test parity.
  • H9 daily cron sub-steps adopt-or-skip instead of launching an untracked concurrent
    pipeline on an active-run conflict.
  • H7 is_indexed counts only completed/completed_partial (a failed-only index is no longer
    reported as indexed).

Data correctness (High)

  • H2 batch table analyses reconciled by the LLM-echoed table_name, not tool-call position
    (ends silent cross-table misattribution).
  • H3 a malformed confidence_score degrades only its own table instead of aborting the batch.
  • H4 a degraded LLM run no longer overwrites previously-good sync rows; low-confidence rows no
    longer enforce/surface required-filter guidance.

Cost & privacy (High)

  • H5 sync LLM calls are metered + budget-gated against the project owner (manual → 429 on
    exhaustion; cron degrades gracefully; ownerless projects run unenforced).
  • H6 DB sample data + distinct values are scrubbed (column denylist + value redaction) before
    LLM egress at both the sync and db-index analyzers, with a per-connection
    send_sample_data_to_llm opt-out (default on).

Medium — M1 reconciler covers all connections · M2 schema-qualified table identity (no
cross-schema collapse) · M3 parent-run progress steps (no 0%→100%) · M4 cron honors per-project
schedule hour · M5 overview regen + worker log key · M6 enrichment payload validation + deep-merge

  • producer reroute · M7 graph op_kind heuristics labelled non-authoritative · M8 freshness
    defaults + sync_failed flag · M9 get_index_age NULL guard.

Low — L1 reaper logs sweep on unknown rowcount · L2 prompt header no longer fabricates an
"analyzed" date · L3 child-run orphaning (covered by H1+H9) · L4 truncation markers + tightened
relevance matching.

Migrations (linear chain, single head e909ec65d857)

  • 2317bf9d9126 schema-qualified db_index uniqueness (M2)
  • f37386df158c indexing-run active-index parity (no-op; index owned by upstream a1f2b3c4d5e6)
  • e909ec65d857 connections.send_sample_data_to_llm (H6)

Notes

  • Deferred Minors (cosmetic / test-hygiene — see the plan's Validation Log): none block merge.
  • Runbook note (no regression): a restart spanning a project's scheduled hour misses that
    day's run — matches the prior daily-cron model (no startup catch-up).

🤖 Generated with Claude Code

NGHTBOY and others added 24 commits June 26, 2026 15:01
…tracts)

Covers all 22 sync-audit findings (9 High, 9 Medium, 4 Low). Locks contracts
for: parent-run heartbeat (H1), batch analysis name-reconciliation (H2/H3),
all-fallback overwrite guard + confidence gate (H4), owner-attributed budget
gate (H5), PII scrubber + opt-in flag (H6), is_indexed status whitelist (H7),
IntegrityError->409 + model index parity (H8), adopt-not-run (H9), schema-
qualified table identity (M2), and the Medium/Low set. Approach A (surgical +
targeted structural). Branch + 5-wave plan defined.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Turns the R5 design spec into 18 bite-sized TDD tasks across 5 waves with a
dependency graph and disjoint file ownership for parallel subagents. Each task:
exact file:line anchors, complete code, failing-test-first, exact commands,
conventional commit, DoD. Covers all 22 audit findings; T18 includes the
per-finding business-logic validation cycle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s C1-C7

Adversarial re-validation (3 independent passes) confirmed the business-logic
understanding for all 22 findings is correct (zero misreads). Applied binding
corrections: C1 owner-less project degrades (not blocks) budget; C2 omit_samples
threaded param not instance attr; C3 sync_now 429 step; C4 M3 parent workflow_id
+ targeted heartbeat UPDATE (no version lost-update); C5 validate ORM partial-
index form + Alembic kwargs; C6 index required-filters under bare suffix for
schema-qualified names; C7 wording. Plan Validation Log governs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implement sync_budget.py with C1 correction: missing owner degrades gracefully
(unenforced) rather than blocking syncs for legacy/deleted-owner projects.

- resolve_owner_user_id: query project owner from DB
- build_sink: instantiate DbUsageSink with owner attribution
- preflight_owner_budget: pre-flight check with C1 graceful degradation

TDD: 3 tests, all passing. C1 applied: test_preflight_owner_missing asserts
ok=True when owner is None (unenforced, not blocked).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… + fallback marker (H2,H3,H4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…budget wiring, op_kind heuristic label (M2,H4,H6,H5,H2,M7,L4)

- M2: _match_tables now uses (schema, name) tuple key; schema-ambiguous
  tables get qualified display name and a NOTE in code_context
- H4: all-fallback guard aborts Step 5 store when < sync_min_success_ratio
  of analyses are non-fallback, leaving previous sync rows intact
- H6: _build_db_context gains scrub/omit_samples params; run() reads
  Connection.send_sample_data_to_llm and settings.sync_pii_scrubbing_enabled
  to set local vars threaded through _match_tables → _build_db_context
- H5: budget preflight (preflight_owner_budget) + per-run DbUsageSink wired
  into run() before heartbeat; Step 6 summary skipped when sink.budget_exceeded()
- H2: store loop guards mt is None with continue + warning instead of
  if-mt-else ternaries; removes phantom upserts for misnamed analyses
- M7: _AMBIGUOUS_VERBS tuple extracted; process_/handle_/sync_/set_/add_/
  register_ removed from _WRITE_VERBS and classified as "unknown"
- L4: distinct values block emits (+N more) when vals > 15; sample_data
  block appends …[truncated] when truncated at 800 chars
- _make_matched() static helper DRYs up _MatchedTable construction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…or qualified tables (H4,C6)

- _load_required_filters_by_table: skip CodeDbSync entries with confidence_score < settings.sync_min_confidence_to_enforce_filters (H4); also index schema-qualified table names (e.g. analytics.orders) under their bare suffix (e.g. orders) so check_required_filters matches bare query references (C6)
- _load_sync_filters_and_mappings: apply the same H4 confidence gate so low-confidence entries are also omitted from prompt guidance text
- 6 unit tests: 3 H4 cases (low-conf excluded, sufficient-conf included, boundary=threshold passes) and 3 C6 cases (qualified indexed under both keys, qualified+low-conf excluded from both, unqualified not duplicated)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ema-qualified db_index, scrub db-index LLM egress (H7,M9,M2,H6)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…model index parity (H8)

- run_coordinator.start(): catch IntegrityError on commit, rollback session,
  re-query for the race-winner, raise RunAlreadyActiveError — prevents raw 500
  on TOCTOU concurrent starts (R5)
- indexing_run.py __table_args__: add uq_indexing_runs_active_one partial-unique
  Index (coalesce form, verified compiles + enforces in SQLite) matching prod
  migration a1f2b3c4d5e6 — unit tests now enforce single-active via real DB
  constraint, not just app-level pre-check
- migration f37386df158c: idempotent create_index (if_not_exists) / drop_index
  (if_exists) parity for envs missing the original hotfix migration
- test: H8 TOCTOU test confirms IntegrityError→RunAlreadyActiveError translation
  and session usability after rollback; TDD RED→GREEN cycle documented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t skip, overview regen (H1,H9,M3,H5,M5)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The run_code_db_sync worker task was reading result.get("synced_tables")
but CodeDbSyncPipeline.run returns the key "synced", causing matched=None
in logs. Changed to result.get("synced") to fix the log output.

Added test_worker_sync.py with a test that patches the pipeline to return
the correct dict and asserts the log contains matched=2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…onConfig propagation + trigger_sync budget 429 (H6,H5)

- Connection model: add send_sample_data_to_llm bool (default True, server_default=1)
- Migration e909ec65d857: add column with batch-mode downgrade for SQLite
- ConnectionConfig dataclass: add send_sample_data_to_llm field (default True)
- ConnectionService.to_config: propagate flag from ORM row into ConnectionConfig
- ConnectionCreate/Update/Response schemas: expose flag (not a secret)
- _UPDATABLE_FIELDS: include send_sample_data_to_llm so PATCH persists it
- trigger_sync: preflight_owner_budget gate before start-lock (429 when over budget)
- Tests: 4 unit (model default/persist/to_config propagation) + 2 integration (429 gate)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…onnections (M4,M1)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…gression test (C3,M4)

C3: Add preflight_owner_budget 429 gate to sync_now route (after role check, before coord.start).
Reuses same pattern as trigger_sync (T14). Blocks over-budget owners before any state changes.

M4: Add regression tests asserting get_sync_schedule next_run hour matches effective per-project hour.
Tests confirm hourly cron contract is held (no drift between displayed hour and compute_next_scheduled_run).

All 4 tests pass (budget gate 429, passthrough 202/409, hour consistency with override, global fallback).
Ruff/mypy pass. No code changes needed to schedule logic—already correct.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…light, defensive owner lookup, migration no-op, update stale tests to new contracts (T18)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ssues.md (T18)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sshlg
sshlg merged commit c5db40c into main Jun 26, 2026
2 checks passed
@sshlg
sshlg deleted the fix/sync-remediation-2026-06-25 branch June 26, 2026 18:54
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