Skip to content

fix: refuse a legacy export cursor ahead of the store - #25

Merged
Freshair129 merged 1 commit into
mainfrom
fix/legacy-export-cursor
Sep 27, 2026
Merged

Freshair129 merged 1 commit into
mainfrom
fix/legacy-export-cursor

Conversation

@Freshair129

Copy link
Copy Markdown
Owner

Summary

This PR closes owner decision GKS-PIP-008 from the P1 report.

Before: when gks_stage_evidence_export got a since_cursor beyond anything the store had issued, it returned an empty page. gks_pipeline_evidence already refused an afterCursor that was ahead of the store, so the two exports behaved differently.

Change (packages/gks-persistence/src/index.mjs, exportStageEvidence)

since_cursor Response
≤ the store-wide graph_state.evidence_cursor unchanged: the scope's page, which may be empty
greater than it gks_invalid_request: since_cursor is ahead of the stage evidence cursor.

The bound is the store-wide cursor, not the last row of the scope being read, for two reasons:

  • every cursor that a scope's own pages ever returned stays valid, and a scope with no newer rows still reads an empty page;
  • the refusal reveals nothing about another scope's rows.

The per-scope cursor rule in ADR-GKS-LEDGER-REPORTING is unchanged.

Callers

  • zuri-ai: knowledge-evidence-importer.js only ever moves its cursor to a next_cursor or a row cursor that a page returned, so it cannot send a cursor that is ahead in normal operation.
  • MSP: forwards the request unchanged.

The one situation that behaves differently is a GKS store restored from an older backup. A puller whose stored cursor is now ahead of the store gets an error instead of a silent empty page.

Golden corpus

C0.4-TOOL-009 (the legacy export case) gains an aheadCursor step. It sends since_cursor: 3 when the store's cursor is 2, and expects gks_invalid_request with that exact message. That case is the only transcript that changes.

Docs

  • GKS-PORT-CONTRACT 0.10.3b: a new behavioural-requirement paragraph, and an updated row for the tool.
  • ADR-GKS-LEDGER-REPORTING 0.6.1b: a revision row.
  • P1 report: the GKS-PIP-008 row is marked done.

Test plan

  • tests/contract/persistence-port-conformance.test.mjs: this test pinned the old empty page (cursor 7 on an empty store). It now expects the refusal, and adds three checks:
    • cursor 0 on an empty store still returns an empty page;
    • a different scope can read up to the store cursor;
    • store cursor + 1 is refused.
  • tests/contract/stage-evidence-export.test.mjs: after paging through all 5 rows, since_cursor: 6 is refused with the exact message.
  • npm test: vitest 298 passed and 2 skipped (the MSP suites need MSP_REPO_ROOT); security 12/12.
  • npm run check:c0 (PASS 24 / NOT_RUN 1), check:baseline and check:corpus pass.
  • CI on this PR

🤖 Generated with Claude Code

GKS-PIP-008 was an open owner decision in the P1 report. A
gks_stage_evidence_export call whose since_cursor was beyond anything the
store had issued returned an empty page. Now it is refused with
gks_invalid_request ("since_cursor is ahead of the stage evidence
cursor."), the same way gks_pipeline_evidence already refuses an
afterCursor that is ahead.

The bound is the store-wide graph_state.evidence_cursor, not the scope's
own last row:

- every cursor a scope's pages ever returned stays valid, and a scope with
  no newer rows still reads an empty page;
- the refusal reveals nothing about another scope's rows.

The per-scope cursor rule in ADR-GKS-LEDGER-REPORTING is unchanged.
zuri-ai's importer only ever advances to a returned cursor, so it is
unaffected in normal operation. The one case that now surfaces is a store
restored from an older backup: the caller gets an error instead of a
silent empty page.

The conformance test that pinned the old empty page now expects the
refusal and covers the boundary cases. The C0.4 corpus gains an
aheadCursor step in TOOL-009, the only transcript that changes.

Docs updated: GKS-PORT-CONTRACT 0.10.3b and ADR-GKS-LEDGER-REPORTING
0.6.1b. The P1 report row is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Freshair129
Freshair129 merged commit bd1ab3b 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