Imported from Nagavenkatasai7/claude-payments (
.claude/skills/tracker-sync/SKILL.md). Install upstream withnpx skills add Nagavenkatasai7/claude-payments --skill tracker-sync. Copyright stays with the author.
/tracker-sync — keep the Program Ledger true
Ledger: https://claude.ai/artifact/7wD2psZ6fndztDjwZC3oNZ (private to the owner). The database is the source of truth for status; the repo holds the tooling. Scratch dir below = this session's scratchpad (/tmp/... in the cloud routine).
Engine: scripts/tracker/sync.mjs (pure logic in sync-core.mjs, tested in tests/tracker-sync-core.test.ts). It supersedes snapshot.mjs, which stays in the repo but is no longer part of this procedure.
Truth rules (non-negotiable)
- A fix is
doneonly with all three: its PR(s) merged, the post-deploy smoke green on a SHA that contains them, and verification evidence for the finding (a live probe, a test that reproduces the finding now passing, or a Chrome check). Write that evidence intoevidencein one or two sentences. - Merged but not yet verified →
merged. PR open →in_review. Branch with commits →in_progress. Plan approved and task written →planned. Otherwiseopen. - Never mark
donefrom a PR title or description alone. Never lower adonewithout writing anincidentevent that says why. - No secrets, tokens or unmasked phone numbers/names in any document.
The engine enforces the automatable part: it writes in_review (open PR with Program-Fix: <n>) and merged (merged PR), never done, and never a status lower than the fix already has (open < planned < in_progress < in_review < merged < done). done is yours to write, by hand, with evidence (section 6).
The ledger is the program's system of record (owner direction 2026-09-21). Every action, decision and approval is recorded, in the same turn as the action:
- Agent launches and finishes (one start row and one finish row per agent the main thread launches) and
gh pr merge|closecommands are journaled by hooks automatically (.claude/hooks/ledger-journal.mjs). The Agent prompt is never logged. - Owner decisions and approvals given in chat: append them at once, one line each:
Kinds: decision | approval | agent | plan | review | pr | merge | deploy | migration | owner-step | verify | milestone | incident | security. Actors: owner | claude | agent | github | ci. Results: ok | blocked | failed | running | info. Refs:node scripts/tracker/journal.mjs add --kind approval --actor owner --title "Owner approves the Wave 2 plan" --detail "In chat, plan p1-w2 v3" --refs '{"plan":"p1-w2"}'{fix:[n], pr:[n], plan:"p1-w2", sha:"abc1234"}. Titles plain and specific; no secrets, phone numbers or names (text is scrubbed anyway). - Every plan lives in
plans(schema:scripts/tracker/PLAN-SCHEMA.md) and is updated whenever the plan changes (section 7).
Always run the engine, even after a hand-written fix or event update. Only the engine refreshes meta/state (main SHA, CI, smoke, what production serves); skipping it leaves the status strip and the "Needs attention" list stale (2026-09-16: a 'smoke pending' incident stayed up for 3 hours after it went green).
Procedure (identical in a session and in the cloud routine)
ArtifactData, url = the ledger, throughout.
- Dump the database. Start from an empty
<scratch>/ledger-db(rm -rfit first).listwithout_dir: <scratch>/ledger-dbforprs(limit 1000),prstate(1000),fixstate(1000),events(1000) andfixes(100). Page withquery.cursorwhile a result hasnext_cursor. A missing collection is fine (the first write creates it). - meta/state and its version.
getmeta/state with the sameout_dir(the engine keeps every key it does not own from that file) and note theversionin the result text → V. The saved file has no version; it is only in the result. If meta/state does not exist, V = 0. - Run the engine.
- Session:
node scripts/tracker/sync.mjs --db <scratch>/ledger-db --state-version V --by session --out <scratch>/ledger-sync --journal ~/.smartremit-ledger/journal.ndjson(--journal-fromdefaults to theflushedmarker). - Cloud routine: the same with
--by cloudand no--journal. It reads GitHub with curl (locally withgh auth token, passed on stdin; in the cloud with no header, so the egress proxy authenticates it) and prints one line:{mainSha, ci, smoke, prodServes, newDocs, batches, warnings, newOffset}. It writesbatch-N.jsonfiles and the doc files beside them. Running it twice against the same dump yields only the meta/state write.
- Session:
- Write. For each
batch-N.jsonin order: ArtifactDatabatchwith its entries aswrites. Every entry is aset: new docs carry no version; meta/state is alone in the last batch and carriesif_version: V.version_mismatchon meta/state (someone wrote it in between): repeat step 2, re-run step 3 with the new V into a fresh--out, and send only the last batch (the earlier ones are already applied).version_mismatchon any other entry (another writer created that doc between your list and your write; batches are all-or-nothing): repeat from step 1.- Never drop
if_versionto force a write.
- Close out (session only). After every batch succeeded:
node scripts/tracker/journal.mjs mark-flushed <newOffset> --main-sha <mainSha>with both values from the summary. This moves the journal marker and writes~/.smartremit-ledger/last-sync.json, which the Stop hook compares withorigin/main. Report the summary line. Do not claim the page updated without the write results.
6. Hand-written status (done, and anything the engine cannot see)
- done:
seta NEW docfixstate/fix-NN-done-<sha7 of the verified deploy>={fix, status: "done", at, prs, mergeSha, source: "verification", evidence}. Until the page overlaysfixstate, alsoupdatefixes/fix-NN(status,evidence,updatedAt) pinned with itsif_version. in_progress/planned(no PR yet):setfixstate/fix-NN-<status>-<short ref>withsource: "session".- Phase state (plan approved, first fix merged, all fixes done):
updatephases/phase-N(status,note), pinned. - Backlog items (
backlog/<key>): setstatus: "done"when closed, with a one-linedetail, pinned. - Timeline rows go through the journal (
journal.mjs add), not direct event writes.
7. Plans (plans/<id>: p0, p1-w1…p1-wN, p2, p3, p4)
- When a plan is written or revised, update its doc (
planVersion+1,updatedAt, items) and journal aplanrow. - On approval: set
approval: {state: "approved", at, by: "owner", ref}andstatus: "approved", and journal anapprovalrow. - When a fix in a plan opens a PR, merges or is verified, update that item's
statustogether with the fix. - Working copies:
~/dev/program-ledger/plans/*.json. Every write is pinned withif_version.
8. Library refresh (only with --corpus, or when a doc it indexes changed: the audit, security results, spec, phase plans, verification records, COMPONENTS/architecture docs, blueprint data)
python3 scripts/tracker/build-corpus.py "$PWD" <scratch>/ledger-corpus [docs/superpowers/plans/<phase plan>.md ...]
Send every batch-N.json it prints with ArtifactData batch. If corpusParts shrank, delete the stale corpus/part-NNN docs. Run it from the checkout that holds the git-ignored CLAUDE-SECURITY-*/ results.
What the engine writes (append-only, deterministic ids)
| Doc | When | Fields |
|---|---|---|
prs/pr-<n> |
first time a program PR (#237+, not dependabot, not loop/) is seen among the 60 most recently updated |
number, title, url, createdAt, fix |
prstate/pr-<n>-<open|merged|closed> |
each state a PR reaches (the page takes the latest) | number, state, at, mergeSha, fix |
fixstate/fix-NN-<in_review|merged>-<pr<n>|sha7> |
open / merged PR with Program-Fix (or the legacy map) |
fix, status, at, prs, mergeSha, source: "github" |
events/gh-pr-open-<n>, gh-merge-<n>, gh-pr-closed-<n> |
PR opened / merged / closed unmerged | at, kind, actor: github, title, detail, refs, result, source |
events/gh-ci-<runId> |
failed push CI run on main | kind incident |
events/gh-smoke-<runId> |
completed push Smoke run on main | success → verify; failure → incident |
events/j-<sha1(line)[0:16]> |
each journal line | as journaled, source: "journal" |
meta/state (overwrite, if_version) |
every run | mainSha, ciMain, smokeMain, smokeNote, prodServes, prodServesNote, prodDeploy, openPrs, syncedAt, syncedBy, program, currentPhase + every other existing key |
prodServes = mainSha when the latest push-triggered Smoke run for it succeeded (the smoke waits until production's /api/version reports the commit, so success proves production serves it); otherwise the newest sha with a successful push Smoke, with a note. Only push runs count: a workflow_dispatch run's head_sha is the dispatching branch's head, not the commit under test. Hand-written or snapshot.mjs events for the same PR merge / open / smoke are recognized by title, so the first run does not duplicate them.
Hooks (.claude/settings.json; fields per https://code.claude.com/docs/en/hooks.md)
PostToolUsematcherAgentand matcherBash, andSubagentStop→ledger-journal.mjs. Main thread only: input withagent_id(a subagent's own tool call) is skipped. A main-thread Agent launch is recorded bytool_response.agentIdin~/.smartremit-ledger/agents.jsonand journaled as "Agent started: ".SubagentStopjournals "Agent finished: " (first 280 scrubbed chars oflast_assistant_message) only for the first stop of a recorded agent; later stops only update its stored last message, and unknownagent_ids (nested helpers, Claude Code's internal agents, which stop with an emptyagent_type) are skipped. A foreground Agent (status: "completed") gets both rows from its PostToolUse, since its SubagentStop fires first.agents.jsonis updated under a lock (a stale lock is renamed aside; a holder removes the lock only if its inode and owner token still match), after the journal row is appended, and pruned (finished > 24 h, unfinished > 7 days). Text is scrubbed (phones incl. bare 10+ digit numbers, emails, API keys incl.sk-ant-, AWS key ids, JWTs, bearer tokens) in both the journal andagents.json. Always exits 0.Stop→ledger-sync-due.mjs(beside the tsc/eslint/vitest gate). It blocks once (never whilestop_hook_active) when (c) an unflushed journal line has kindincident,mergeormigration, or is a successful sessiongh pr mergerow (a Bash PostToolUse carries no exit code, but PostToolUse fires only on success; a non-zero exit fires PostToolUseFailure); (b) the journal is longer than theflushedmarker and the last sync (atinlast-sync.json) is more than 60 minutes old or unknown; or (a)git ls-remote origin main(3 s timeout; on error it does not block; run only when (b) and (c) do not block) differs frommainShainlast-sync.json. Routine rows (approval,decision,verify,owner-step, agent starts and finishes,gh pr close) therefore wait for the next 60-minute window (narrowed from 10 min/7 kinds on 2026-09-22 — owner decision, the hook was blocking too often and draining usage). Cloud safety: it never blocks whenCLAUDE_CODE_REMOTEis set (Claude Code sets it to"true"in remote/web sessions, where the cloud routine runs), and~/.smartremit-ledgeris created lazily.LEDGER_SYNC_HOOK=offdisables it by hand.