Skip to content

Replace corpus revision pins with immutable snapshot tokens and held-out MCP evaluations - #248

Merged
morluto merged 3 commits into
mainfrom
refactor/snapshot-token-heldout-evals
Jul 31, 2026
Merged

Replace corpus revision pins with immutable snapshot tokens and held-out MCP evaluations#248
morluto merged 3 commits into
mainfrom
refactor/snapshot-token-heldout-evals

Conversation

@morluto

@morluto morluto commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Summary

This follow-up closes the two remaining gaps from the architecture remediation:

  • Public and application read contracts now use immutable string snapshot_token values instead of corpus_revision pins and outputs. Durable tokens resolve through the persisted snapshot artifact; stale or missing tokens return typed snapshot_expired or snapshot_unavailable errors, and unpinned reads expose explicitly non-durable identities.
  • The MCP test surface now includes held-out, multi-step workflow scenarios for missing coverage, stale-token recovery, exact resource handoffs, and audit-only fix-pattern preview. Each run records task success, tool calls, resource reads, errors, retries, latency, response bytes, and a documented serialization-based context-token estimate.

The SQLite observation watermark remains private implementation state used to detect concurrent mutation; it is no longer part of the public MCP/application identity contract.

Review order

  1. 38c38ac — public snapshot-token contract and fail-closed read cutover.
  2. 44c1668 — held-out workflow fixture, oracle assertions, metrics, and evaluation documentation.

Validation

  • make verify — passed (uncached tests, golangci-lint, module tidiness, generated outputs, and AGENTS validation).
  • go test ./... — passed.
  • go test ./internal/mcpserver -run AgentEval -count=1 — passed, with per-scenario metrics logged.
  • git diff --check — passed.

Compatibility and limitations

This is an intentional MCP contract cutover; corpus_revision is not retained in public schemas or JSON. The held-out suite is deterministic and model-free, so its token estimate is a serialized-response proxy and its latency is local harness timing, not a model benchmark or service SLO. Model-backed comparisons remain an optional follow-up requiring reproducible model and sampling inputs.

@morluto
morluto marked this pull request as ready for review July 31, 2026 16:34
@cursor

cursor Bot commented Jul 31, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@morluto
morluto merged commit e25372e into main Jul 31, 2026
14 checks passed
@morluto
morluto deleted the refactor/snapshot-token-heldout-evals branch July 31, 2026 16:35

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

if result.SnapshotToken != "" {
artifact.SnapshotToken = result.SnapshotToken

P1 Badge Preserve each code-index artifact's own snapshot token

When an index_repositories job completes more than one repository, the producer sets the top-level token to the first successful item's token, and this block then overwrites every artifact's item-specific token with it. Consequently, later artifacts claim the first repository's scoped immutable snapshot, so resolving their token returns provenance and payload for the wrong repository; retain item.SnapshotToken rather than applying the batch token to every artifact.

AGENTS.md reference: AGENTS.md:L5-L6


batch, err := r.GetRepositories(ctx, mcpcontract.GetRepositoriesInput{Repositories: refs, SnapshotToken: in.SnapshotToken})

P2 Badge Keep repository search hydration on the captured watermark

If the corpus changes after searchCorpus returns but before this unpinned GetRepositories call begins, the second call reads the newer projection because in.SnapshotToken is empty. The response then combines result identities and totals from the earlier search with repository metadata from the later state while reporting the earlier res.SnapshotToken and provenance; preserve or revalidate the first read's watermark across this composed operation instead of starting an independent unpinned read.

AGENTS.md reference: AGENTS.md:L5-L6


if err != nil {
return 0, mcpcontract.Unavailable("snapshot_unavailable", err.Error())

P2 Badge Preserve expired-token errors during snapshot resolution

When ResolveReadSnapshot returns corpus.ErrSnapshotExpired—for example, after a stored token's contract version is no longer supported—this unconditional conversion reports snapshot_unavailable, the same reason used for missing or corrupt artifacts. That prevents callers from following the documented stale-token recovery path; inspect the cause and return snapshot_expired for ErrSnapshotExpired while reserving snapshot_unavailable for unavailable artifacts.

AGENTS.md reference: AGENTS.md:L5-L6


if snapshot.ObservationWatermark != revision {
return 0, mcpcontract.Unavailable("snapshot_expired", fmt.Sprintf("snapshot observation watermark %d is no longer current; current watermark is %d", snapshot.ObservationWatermark, revision))
}
return revision, nil

P1 Badge Validate a token's scope before accepting the pin

A persisted token is scoped to the artifact and repository or thread encoded in snapshot.Scope, but this accepts any resolvable token solely because its global watermark is current. For example, a coverage token for acme/rocket can be supplied to a search of an unrelated repository at the same watermark; the read succeeds against that unrelated mutable projection and labels the result with the original scoped token. Validate the artifact kind and scope against the requested read before accepting the pin.

AGENTS.md reference: AGENTS.md:L5-L6


CommitSHA: record.CommitSHA, SnapshotToken: record.SnapshotToken,

P2 Badge Persist a resolvable payload for code-index tokens

The code-index artifact now exposes record.SnapshotToken as its sole snapshot identity, but code-index storage only inserts that token into corpus_snapshot_tokens and never inserts the corresponding digest and payload into corpus_read_artifacts. ResolveReadSnapshot therefore always rejects an advertised code-index token as snapshot_unavailable because its left-joined payload is missing, so callers cannot use this token for the new snapshot-bound read contract; store a matching immutable payload or resolve this artifact kind from its durable code-index record.

AGENTS.md reference: AGENTS.md:L5-L6


if snapshot.ObservationWatermark != revision {
return 0, mcpcontract.Unavailable("snapshot_expired", fmt.Sprintf("snapshot observation watermark %d is no longer current; current watermark is %d", snapshot.ObservationWatermark, revision))

P1 Badge Keep job-produced snapshot tokens valid after completion

Every durable snapshot created by ensure_coverage or fix-pattern mining is followed by job progress and terminal-status writes, and the existing SQLite triggers advance corpus_state.revision for both jobs and job_events. This equality check therefore classifies the newly returned token as snapshot_expired before the caller can perform the documented snapshot-bound reread, even when no source observation changed; freshness must use an observation-only watermark that excludes executor bookkeeping, or materialize an identity that remains valid after the terminal transition.

AGENTS.md reference: AGENTS.md:L5-L6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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