Skip to content

fix: close P1 C0 gaps and queue concurrent writers - #19

Merged
Freshair129 merged 2 commits into
mainfrom
fix/p1-c0-closure
Sep 27, 2026
Merged

Freshair129 merged 2 commits into
mainfrom
fix/p1-c0-closure

Conversation

@Freshair129

Copy link
Copy Markdown
Owner

Summary

This PR is P1 from the SRS blueprint gap analysis. It turns C0 obligations that were implemented but never demonstrated into executable evidence. Along the way it fixes a real C0 defect in concurrent writes.

Full report: docs/reports/2026-09-27-p1-c0-closure.md. It supersedes the G0 baseline lock pinned at be97c93.

Defect fixed: concurrent writers got SQLITE_BUSY (GKS-IDN-005, GKS-STO-001, GKS-API-005)

A new multi-process test showed two problems when several processes write to the same store:

  1. Writes failed instead of waiting. One process could fail at once with database is locked, even with busy_timeout set.
    • Cause: every write transaction was DEFERRED. It reads first and writes later, so another process's commit in between makes SQLite return SQLITE_BUSY straight away. busy_timeout does not apply in that case.
    • Fix: every write transaction now begins IMMEDIATE (writeTransaction() in packages/gks-persistence).
  2. A driver error code leaked to callers. The raw SQLITE_BUSY code reached the caller.
    • Fix: createJsonRpcToolErrorResponse now emits only gks_* codes; anything else becomes gks_backend_unavailable and keeps its message.

The migration runner also re-checks each migration under the write lock, so processes opening a fresh store together apply each migration exactly once.

Cost: idempotent replays now also take the write lock, so writers are serialized. This is acceptable for the single-writer profile and is noted in the report.

Lost-response replay (C0.4-LOST-RESPONSE-REPLAY: NOT_RUN → PASS)

tests/integration/lost-response-replay.test.mjs exercises three operations against a real stdio server:

  • a legacy promote;
  • a GenesisRAG17 submit;
  • a Tier-4 graph receipt.

For each one, it kills the server with SIGKILL after the write is durable and before any response is read, then replays the request against a fresh process. Each replay returns the committed result with idempotent: true, and the store holds exactly one row for it. Sending a changed payload under the same key returns gks_conflict.

The C0.4 manifest is now PASS 23 / NOT_RUN 1. The case still NOT_RUN is the Tier-4 physical readback, which is outside the GKS boundary. productionReady and deploymentAuthorized stay false.

Baseline lock you can run (GKS-MIG-001, GKS-API-001)

npm run check:baseline (scripts/check-baseline-lock.mjs + tests/fixtures/baseline-lock.json) fails when any of these drift:

  • the 17-tool registry hash;
  • the lockfile or workflow hash;
  • migrations, if one is edited, removed, or added without a re-lock.

Line endings are folded to LF before hashing, so Windows and Linux runs agree. --write records <HEAD>+working-tree whenever a hashed input is uncommitted, so the lock never claims a commit it was not taken from. A new CI slice, c0-gate, runs check:c0 and check:baseline on Node 22 and 24.

C0 acceptance evidence

New tests cover:

  • SCP-002: each of the six scope fields is denied before persistence.
  • ING-001, ING-002, ING-004, ING-005.
  • SYS-003: a full run with fetch and socket connect trapped.
  • IDN-004.
  • IDN-005: a deterministic lock-queue test, a 6×4 process race, and a 5-process fresh-store migration.
  • PIP-001, PIP-003, PIP-008.
  • API-002: interleaved request ids.
  • API-005.
  • SEC-002: canary secrets absent from the SQLite main, WAL and SHM files and from stdout/stderr, across restart.

The GenesisRAG17 fixtures are extracted to tests/fixtures/genesisrag17.mjs so the tests can share them.

Not in this PR (the report lists these as needing an owner decision)

Each of these would change accepted C0 behaviour:

  • validating jsonrpc: "2.0";
  • enforcing stage catalog order;
  • raw-byte hashing, or rejecting lone surrogates;
  • endpoint checks on legacy relations;
  • an error for an out-of-range legacy export cursor;
  • verifying human-repair proofs;
  • a GKS-side benchmark manifest;
  • a replayable golden corpus. Registry v2 would need request fixtures plus a runner, and every hash would be re-baselined.

Test plan

  • npm test: vitest 262 passed, 2 skipped (the MSP integration suites need MSP_REPO_ROOT); security 12/12. tests/unit 9/9.
  • npm run check:c0: PASS=23 NOT_RUN=1. npm run check:baseline holds.
  • Mutation checks:
    • deterministic lock test: fails 3 of 3 runs with DEFERRED transactions;
    • fresh-store migration test: fails 3 of 3 runs without the re-check;
    • the 6-process race: catches DEFERRED about 3 runs in 5, so it backs up the deterministic test rather than standing alone.
  • RKOI architecture review. First pass: REVISION_NEEDED (the lock's provenance claim, a lock test that could pass without testing anything, no test for the migration re-check, the undocumented replay cost). All four are addressed; re-review APPROVED.
  • CI on this PR (the new c0-gate slice and process tests running on Linux)

🤖 Generated with Claude Code

Freshair129 and others added 2 commits September 27, 2026 10:27
Every write transaction now begins IMMEDIATE, so concurrent writer processes
wait under busy_timeout. Before, a DEFERRED read-then-write transaction failed
at once with SQLITE_BUSY (GKS-IDN-005, GKS-STO-001). The migration runner
re-checks each migration under the write lock. Tool errors now carry only
gks_* codes on the wire; any other code is sent as gks_backend_unavailable
(GKS-API-005).

The lost-response replay harness SIGKILLs a real server after the durable
commit and replays the request against a fresh process. This covers legacy
promote, pipeline submit and graph receipt, so C0.4-LOST-RESPONSE-REPLAY
moves to PASS (manifest: PASS 23, NOT_RUN 1).

The baseline lock can now be run as a check (GKS-MIG-001, GKS-API-001).
scripts/check-baseline-lock.mjs locks the hashes of the lockfile, the
workflow, each migration and the tool registry. A new CI c0-gate slice runs
check:c0 and check:baseline.

Adds C0 acceptance tests for SCP-002, ING-001/002/004/005, SYS-003,
IDN-004/005, PIP-001/003/008, API-002/005 and SEC-002, and extracts the
GenesisRAG17 fixtures into tests/fixtures/genesisrag17.mjs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The six-process identity race timed out at vitest's 5 s default on the
Node 22 CI runner. Spawning and migrating several server processes is
slower on a shared runner than locally. The race and the fresh-store
migration test now allow 30 s; their assertions are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Freshair129
Freshair129 merged commit f50116a into main Sep 27, 2026
14 checks passed
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