Skip to content

feat(core): add pending transaction store (#193) - #227

Merged
MongLong0214 merged 1 commit into
devfrom
feat-issue-193
Jul 30, 2026
Merged

feat(core): add pending transaction store (#193)#227
MongLong0214 merged 1 commit into
devfrom
feat-issue-193

Conversation

@MongLong0214

Copy link
Copy Markdown
Owner

Summary

Implements T-1001: the pending transaction store from ADR-0021.

Changes

  • src/core/pending.ts — new module owning the monotonic prepare/verify/stage/apply/consume lifecycle
  • test/pending.test.ts — 11 tests covering all phase transitions, trust boundary, CEO amendments, format errors
  • dist/ — rebuilt

Key decisions

  • expires_at is null in prepared/verified phases, stamped only on stage success (CEO amendment 1)
  • Nonce validated against ^[0-9a-f]{32}$ before any path resolution (CEO amendment 2)
  • Path resolution via git rev-parse --git-path commitlore/pending for worktree correctness
  • Atomic rename for every mutation

Testing

  • Focused: 11/11 pass
  • Full suite: 46 files, 1511 passed, 1 skipped
  • Both typechecks (tsconfig.json + bench/tsconfig.json) exit 0
  • Mutation oracles M-1001-A (phase field removed) and M-1001-B (consume phase guard removed) both caught by tests

Closes #193

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown

CommitLore — record lint

Trailers: clean — 1 commit in origin/dev..0f5ef329d8bccbfa99ca56270794a78344d00b6a
Active constraints: 2 limits · 3 ruled-out · 0 warnings — from 1 record over 5 changed paths

Limits (2)

  • r-t1001pend 0f5ef32 — the test proves monotonic transitions but cannot prove absence of TOCTOU between read and rename on a loaded filesystem; atomic rename is the kernel-level guarantee
  • r-t1001pend 0f5ef32 — the first attempt's Record-Id used hyphens, which the r-[a-z0-9]{6,} format rejects; both the lint action and the dogfood test caught it

Ruled out (3)

  • r-t1001pend 0f5ef32 — setting expires_at at creation time with a longer window | a longer window hides staleness rather than preventing it, and the staged-diff-hash gate already catches real drift
  • r-t1001pend 0f5ef32 — a shared atomicWriteJson utility exported from a utils module | three files already duplicate the pattern and the ticket scope does not include a cross-cutting refactor
  • r-t1001pend 0f5ef32 — allowing stagePending on a prepared record when records are empty | skipping verification breaks the trust chain that prevents an MCP client from injecting arbitrary trailers

git log --follow accepts exactly one pathspec, so renames are not followed for 5 paths; query one path at a time to follow its rename chain

Trailer violations fail this check. Active constraints are informational — they are what the repository already decided, not a verdict on this PR.

@MongLong0214
MongLong0214 changed the base branch from docs-gate2-amendments to dev July 30, 2026 08:52
Implements the monotonic prepare/verify/stage/apply/consume lifecycle from
ADR-0021. Every mutation is an atomic rename so no concurrent reader can
observe a partial file. Path resolution uses git rev-parse --git-path so
linked worktrees resolve correctly.

The nonce trust boundary validates ^[0-9a-f]{32}$ before any path join, so
no caller-supplied string reaches the filesystem unsanitised. expires_at is
null while the record is in prepared or verified phase and is stamped only
when stage succeeds, as staged_at + 5 minutes.

readPending returns null for absent files and throws a typed PendingFormatError
for corrupt or unknown-version content, distinguishing the two failure modes
that the hook must treat differently.

Ruled-out: setting expires_at at creation time with a longer window | a longer window hides staleness rather than preventing it, and the staged-diff-hash gate already catches real drift
Ruled-out: a shared atomicWriteJson utility exported from a utils module | three files already duplicate the pattern and the ticket scope does not include a cross-cutting refactor
Ruled-out: allowing stagePending on a prepared record when records are empty | skipping verification breaks the trust chain that prevents an MCP client from injecting arbitrary trailers
Limit: the test proves monotonic transitions but cannot prove absence of TOCTOU between read and rename on a loaded filesystem; atomic rename is the kernel-level guarantee
Blast: local
Undo: easy
Certainty: firm
Record-Id: r-t1001pend
Limit: the first attempt's Record-Id used hyphens, which the r-[a-z0-9]{6,} format rejects; both the lint action and the dogfood test caught it
@MongLong0214
MongLong0214 merged commit f49e205 into dev Jul 30, 2026
14 of 15 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.

T-1001 · Pending transaction store

1 participant