feat(ghost-drift): add --gate mode to compare for deterministic ledger reconciliation#78
Open
gnahCnayR wants to merge 2 commits into
Open
feat(ghost-drift): add --gate mode to compare for deterministic ledger reconciliation#78gnahCnayR wants to merge 2 commits into
gnahCnayR wants to merge 2 commits into
Conversation
…r reconciliation Compose the existing checkBounds() helper into a structured gate verdict on top of the existing compare command. New flags: --gate, --sync, and --max-divergence-days. Versioned JSON output as ghost.compare.gate/v1 suitable for CI and programmatic consumers; exit codes 0/1/2 reflect aligned-or-covered, uncovered drift, and hard error respectively. No new top-level orchestration verb; the existing review.md skill recipe gets a short pointer to the gate flag for agents that want a deterministic signal. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019e1874-9118-7527-b5e0-5bc0f8dcf913
Address sq agents review findings on the original gate scaffold: - Replace `unknown` types in RunGateCliOptions with concrete string | undefined / number | undefined; validate at the runGateCli boundary. - Use synchronous-flush stdout-write before process.exit so JSON output isn't truncated on piped or non-TTY runs. - Consolidate the multiple process.exit branches in runGateCli to a single exit at the end with intermediate paths returning the exit code via a typed result. - Remove (or wire up — verify intent) the unused effectiveTolerance in buildGateReport. No public-API or behavioral changes; the ghost.compare.gate/v1 schema and exit-code semantics are unchanged. Amp-Thread-ID: https://ampcode.com/threads/T-019e1874-9118-7527-b5e0-5bc0f8dcf913 Co-authored-by: Amp <amp@ampcode.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
--gatemode to the existingghost-drift comparecommand for deterministic ledger reconciliation suitable for CI, programmatic consumers, and Block'ssq agents designadapter.This is not a new orchestration verb — it composes the existing
compareFingerprints,readSyncManifest, andcheckBoundsprimitives into a stable contract surface. Theverify.mdskill recipe's "Ghost has noghost verifyCLI command" stance is intentional and unchanged; the gate is a deterministic signal, not a workflow.What it does
When
--gateis passed:.ghost-sync.json(path overridable via--sync, default./.ghost-sync.json).checkBounds()helper.aligned/covered/reconverging/uncovered) in CLI or JSON form.0aligned-or-covered,1uncovered drift,2hard error.JSON schema (
ghost.compare.gate/v1){ "schema": "ghost.compare.gate/v1", "trackedFingerprintId": "market-theme", "localFingerprintId": "example-app", "overall": { "distance": 0.52, "verdict": "uncovered" }, "dimensions": { "spacing": { "distance": 0.73, "ackDistance": 0.73, "stance": "accepted", "verdict": "covered" }, "palette": { "distance": 0.95, "ackDistance": 0.875, "stance": "accepted", "verdict": "uncovered", "reason": "current 0.95 exceeds acked 0.875 + tolerance 0.05" }, "decisions": { "distance": 0, "ackDistance": 0, "stance": "diverging", "verdict": "covered" }, "newDimension": { "distance": 0.4, "verdict": "uncovered", "reason": "no ack recorded" } } }Why this shape
The
--gatework originated from a need at Block to integrate Ghost into our internal CI/review pipeline (sq agents design check --gate) and into a marketplace agent skill (ghost-drift-check). Both consumers were re-implementing the same reconciliation logic — read the manifest, walk dimensions, compare-with-tolerance, classify each dimension. Surfacing it as a flag oncompare(rather than scattered downstream code) gives every consumer:Files
--gate,--sync,--max-divergence-daysflags oncompare; branches torunGateCliwhen--gateis set.buildGateReport,gateExitCode,formatGateReportCLI,formatGateReportJSON,runGateCli. Pure functions plus a CLI runner; all re-exported fromcore/index.tsfor programmatic consumers.minorbump forghost-drift.pnpm dump:cli-help(cli-manifest drift gate is part ofpnpm check).Architectural fit
Ghost's tools are calculators; agent workflows are skills. This change keeps that separation intact:
--gateis a flag on the existing calculator (compare), not a new orchestration verb.review.mdgets one new subsection pointing at the flag for agents that want a deterministic signal.verify.mdis untouched (its "no CLI verify command" framing is unchanged).compareFingerprints,readSyncManifest, andcheckBounds. No new ledger logic.ghost-drift loop(or similar),--gatebecomes its gate stage; nothing thrown away.Testing
pnpm installpnpm buildpnpm test— 456 passed (40 files), including 14 new gate tests covering the spec cases.pnpm check— biome check, typecheck, file-size limits (cli.ts 411 / gate.ts 328, both under the 500-line limit), docs frontmatter, cli-manifest drift.pnpm fmt— clean.Both
cli.tsand the newgate.tsstay under the repo's 500-line file-size gate. TherunGateClirunner sits ingate.ts(rather than inline incli.ts) specifically to satisfy that gate while keeping the CLI runner re-exportable for programmatic consumers.Notes for reviewers
compareinvocations behave identically;--gateis opt-in.alignedverdict requiresack.stance === "aligned"— adivergingack with current distance 0 reads ascovered, notaligned. This matches the spec test expectation for the diverging-covered case.palettedistance 0.92 vs ack 0.875 with tolerance 0.05 (which doesn't actually exceed: 0.875 + 0.05 = 0.925 > 0.92). The test fixture uses 0.95 so the case actually exercises the uncovered path. The reason-string format matches the spec.