Skip to content

tools+hooks: preflight_drift (board-vs-cargo reconciliation) wired into SessionStart#406

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/check-att-agents-ousks
May 20, 2026
Merged

tools+hooks: preflight_drift (board-vs-cargo reconciliation) wired into SessionStart#406
AdaWorldAPI merged 2 commits into
mainfrom
claude/check-att-agents-ousks

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

Summary

Surfaces board-claim vs. cargo-reality drift at session start instead of catching it after-the-fact during PR review. Three commits:

  1. 73910fe.claude/tools/preflight_drift.rs (274 LOC, zero-deps Rust). Self-contained port of WoA/.claude/v0.2/tools/preflight_drift.py to the Rust ecosystem. Parses CLAUDE.md for the canonical "N crates, M in workspace, K excluded" claim and LATEST_STATE.md for PR-table rows; compares against actual Cargo.toml members / exclude counts via a tolerant inline TOML scanner. Exits 1 when drift exceeds the configured threshold.

  2. 0c170e2.claude/hooks/session-start.sh extended with a three-way path:

    • compiled binary at .claude/tools/preflight_drift → run it; on exit 1 append a ⚠ Board drift detected block (full table) to hookSpecificOutput.additionalContext.
    • binary missing but source present → emit a one-line rustc hint so the operator can opt-in.
    • neither → silent, hook behaves exactly as before.
      .gitignore excludes the compiled binary (platform-specific); source stays committed.

Live demonstration

Running today against this branch:

preflight_drift: DRIFT — refuse to spawn (threshold=2)
  metric                    claimed       real      drift
  workspace_members               7         16          9  <— OVER
  excluded                       15         16          1
  workspace_total                22         32         10  <— OVER

CLAUDE.md line 6 still reads "22 crates, 7 in workspace, 15 excluded" — predates the post-#372 cognitive-shader-driver / sigma-tier-router / lance-graph-supervisor / lance-graph-ontology / lance-graph-archetype / lance-graph-rbac / lance-graph-callcenter additions. The doc update is intentionally not in this PR (separate APPEND-ONLY governance commit per Mandatory Board-Hygiene Rule); this PR ships the detection, not the fix.

Why it exists

This is the automation of the Mandatory Board-Hygiene Rule from CLAUDE.md:

A PR that adds a type, plan, deliverable, or epiphany without updating the relevant board file in the SAME commit is incomplete.

Until now the rule was enforced by human noticing during review. With this hook, the gap shows up in the model's first turn of every session — same surface as the existing bootload context — so the next session can resolve the drift before adding more claims on top of stale ones.

Conformance with .claude/ATT/autoattended-orchestrator-spec.md

Maps to PP-16 preflight-drift-auditor in the spec's sprint-spawn gate model. Currently wired as a soft signal (informational pressure, not a session-start gate); future hardening could promote drift > threshold to a refuse-to-spawn in the wave orchestrator without changing this tool's API.

Test plan

  • rustc .claude/tools/preflight_drift.rs -o /tmp/preflight_drift && /tmp/preflight_drift exits 1 with the table above
  • bash .claude/hooks/session-start.sh with binary present surfaces the drift block in additionalContext
  • bash .claude/hooks/session-start.sh with binary absent surfaces the rustc build-hint block
  • bash .claude/hooks/session-start.sh always emits valid JSON (python3 -c 'json.load(sys.stdin)' succeeds)
  • Reviewer: confirm zero-deps invariant (grep -E 'extern crate|^use [a-z]+::' .claude/tools/preflight_drift.rs should yield only std::*)

Generated by Claude Code

claude added 2 commits May 20, 2026 10:59
…iation

Single-file zero-deps Rust port of WoA/.claude/v0.2/tools/preflight_drift.py.
Compares numeric claims in CLAUDE.md and .claude/board/LATEST_STATE.md
against actual Cargo.toml workspace members/exclude counts and PR-table
row counts. Exits 1 when drift exceeds the configured threshold so a
sprint spawn can refuse-to-start instead of building on stale claims.

Currently surfaces 9-crate drift in the workspace_members metric (claim 7,
real 16) and 10-crate drift in workspace_total (claim 22, real 32) — the
existing CLAUDE.md "22 crates, 7 in workspace, 15 excluded" line predates
the post-#372 expansions and needs an update independent of this PR.

Build: rustc .claude/tools/preflight_drift.rs -o preflight_drift
Run:   ./preflight_drift [--threshold N] [--metric KEY]
Three-way path:
  1. compiled binary at .claude/tools/preflight_drift → run it; on exit 1
     (drift > threshold) append a "⚠ Board drift detected" block with the
     full table to the additionalContext, surfacing the gap as a
     system-reminder in the model's first turn.
  2. binary missing but source present → emit a one-line rustc hint so
     the operator can opt-in.
  3. neither → silent, hook behaves exactly as before.

Never blocks: the JSON payload always emits; drift is informational
pressure, not a session-start gate. The hook surfaces what the
Mandatory Board-Hygiene Rule already says, instead of relying on a
human to notice the drift after-the-fact.

`.gitignore` excludes the compiled binary (platform-specific); source
stays committed.
@AdaWorldAPI AdaWorldAPI merged commit 02b066e into main May 20, 2026

@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

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c170e2dad

ℹ️ 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".

Comment on lines +131 to +135
Ok(vec![
("workspace_members".into(), members),
("excluded".into(), excluded),
("workspace_total".into(), members + excluded),
])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Implement advertised prs_in_table metric filtering

The CLI help advertises --metric prs_in_table, but this metric is never produced in real_metrics, and the drift loop iterates only real keys, so --metric prs_in_table yields an empty table and exits 0 even when drift exists. This is a silent false-negative path for users trying to validate that specific metric and should be fixed by either generating a real prs_in_table value or removing the advertised option.

Useful? React with 👍 / 👎.

Comment on lines +19 to +20
if ! DRIFT_OUT="$(cd "$REPO_ROOT" && "$DRIFT_TOOL" 2>&1)"; then
DRIFT_BLOCK=$'\n\n## ⚠ Board drift detected (preflight_drift exit 1)\n\n```\n'"$DRIFT_OUT"$'\n```\n\nThe board claims do not match Cargo.toml workspace state. Before any new sprint spawn, update `CLAUDE.md` / `.claude/board/LATEST_STATE.md` to match real counts, OR document why the drift is intentional. The Mandatory Board-Hygiene Rule in CLAUDE.md applies.'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Distinguish parser failures from actual drift in hook output

This branch treats any non-zero preflight_drift exit as board drift and hardcodes exit 1 in the message, but the tool defines exit code 2 for parse/read errors. When the binary fails for non-drift reasons (for example, transient read/parse failure), the session-start context incorrectly tells operators to edit board files instead of reporting a tool failure, which can send remediation in the wrong direction.

Useful? React with 👍 / 👎.

AdaWorldAPI added a commit that referenced this pull request May 28, 2026
tools+hooks: preflight_drift (board-vs-cargo reconciliation) wired into SessionStart
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.

2 participants