SuperAPI AI Directory

agent-history-hygiene

Agents & models

View repository

Updated 10-15 20:33

About this skill

agent-history-hygiene

Keep agent transcripts and plan files in the commit that records their feature, without leaking secrets or mutating a file while its recorder is active.

Surfaces, separated by purpose:

Surface Question it answers
run-specstory-session.sh "How do I prove the recorder exited before finalization?"
queue-agent-commit.sh "How do I queue this exact authorized commit without touching live history?"
finalize-agent-commit.sh "How is the exact post-session snapshot sanitized and committed?"
find-session.sh "Which transcript / plan file is my current session?"
stage-agent-artifacts.sh "Which exact agent files belong in the prepared index?"
agent-commit-metadata.sh "Which provenance trailers belong in the commit?"
bootstrap-project.sh "How do I install validation-only hooks?"
scan-staged.sh "Is the effective staged index clean?"
probe-specstory-redaction.py "What does SpecStory redact natively?"

Core invariants

  1. Commit the transcript and plan with the feature they produced. Never ignore .specstory/history/ or silently drop dirty agent artifacts from the review trail.

  2. SpecStory state is not review history. Ignore only .specstory/.project.json and .specstory/statistics.json; both are machine-local/regenerable.

  3. Quiescence comes before Git history movement. An active recorder blocks commit, pull --rebase, rebase/update-base, integration, and worktree/session retirement. Required order:

    recorder quiescence
      -> exact artifact + message finalization
      -> ordinary hook-enabled commit proven
      -> history/ref movement or retirement
    
  4. Provenance comes from the staged snapshot. Use exact session, transcript, and plan selectors; never point a trailer at an unstaged artifact or guess an unknown model.

  5. Rotate at the provider before any Git rewrite. Redaction prevents new reachability; it does not revoke a credential or erase every local copy.

  6. Never use plain git push --force as leak remediation. It can destroy teammate work and does not remove bytes from existing clones/caches.

  7. Shipped files must be scanner-safe. npx skills add installs this whole directory under a consumer's scan scope. Never embed a literal private-key header; build it at test runtime or use synthetic placeholders. Enforced by tests/test_shipped_file_hygiene.py.

When to use

Use this skill when:

  • the user asks to commit/save a SpecStory session, transcript, or plan;
  • dirty .specstory/history/*.md, .claude/plans/*.md, .cursor/plans/*.md, or another configured artifact accompanies a feature;
  • .specstory/.project.json or statistics.json keeps churning;
  • the user asks to install pre-commit/gitleaks for agent artifacts;
  • a scanner finds a secret in an agent artifact; or
  • a credential was committed or pushed—read references/remediation.md before any rewrite.

Do not use it when the user explicitly wants agent history excluded, or when no agent artifacts exist. If a leak is already on shared main/release, do not suggest rewriting that branch; use the remediation runbook.

Default lifecycle: queue now, finalize after exit

Read references/post-session-finalization.md before running or recovering this lifecycle. It defines every state, exit code, recovery action, and no-retry boundary.

1. Launch the synchronous wrapper

From the intended worktree, before starting Claude:

bash skills/local/agent-history-hygiene/scripts/run-specstory-session.sh \
  --allow-commit claude

--allow-commit gives the outer parent a one-run finalizer capability. It does not let the child infer user consent. The agent still waits for explicit commit authorization before queueing.

Do not replace the wrapper with a Stop hook, detached watcher/daemon, or live checkpoint. A Stop hook runs before process/recorder quiescence is proven; a daemon separates recorder lifetime from the session; a live checkpoint commits while the file can still change.

2. Work normally; stage only feature paths

Take the canonical lowercase UUID from Claude /status. Resolve the exact SpecStory path and plan policy; if one UUID has multiple rendered aliases, pass both UUID and path to disambiguate.

SESSION_ID=01234567-89ab-4cde-8fab-0123456789ab
TRANSCRIPT='.specstory/history/2026-09-01_02-14-50Z.md'
PLAN='.claude/plans/exact-change.md'

git add -- path/to/feature-a path/to/feature-b

Do not stage the live transcript or plan. The finalizer owns exact artifact staging and sanitation after SpecStory exits.

3. After explicit user commit authorization, queue once

Create a UTF-8 base subject/body without lifecycle-managed trailers, then queue with exact selectors:

MESSAGE_FILE="${TMPDIR:-/tmp}/agent-commit-message.txt"
printf '%s\n' \
  'feat(scope): summarize the change' \
  '' \
  'Explain why the change is needed.' > "$MESSAGE_FILE"

bash skills/local/agent-history-hygiene/scripts/queue-agent-commit.sh \
  --commit \
  --session-id "$SESSION_ID" \
  --specstory-path "$TRANSCRIPT" \
  --plan "$PLAN" \
  --message-file "$MESSAGE_FILE"
# Use --no-plan instead of --plan only when no plan exists.

The queue copies only the base message into private per-worktree state. It does not read, stage, scan, redact, or commit the live transcript.

On status=queued, report "finalization queued" and exit the agent session. Perform no further repository/index operation—not even git status, a diff, a message edit, sync, or a finalizer call.

4. Let the outer runner finish

Only after child exit 0, the runner:

  1. records lifecycle proof;
  2. runs one exact quiet specstory sync claude -s UUID --silent;
  3. re-proves the exact session/path;
  4. calls the finalizer with its parent-held token;
  5. stages and sanitizes exact artifacts in a locked alternate index;
  6. derives canonical trailers, writes private handoff drafts, and calls one ordinary git commit -F with normal hooks enabled; and
  7. proves the resulting parent, tree, branch, and Agent-History-Request trailer.

Normal pre-commit execution is unsafe while a writer is active: when unstaged changes exist, pre-commit can stash them, check out/modify staged content, then restore the stash. That checkout/restore sequence can race a recorder and clobber, conflict with, or reintroduce bytes. Therefore the pre-commit checker is validation-only; the quiescent finalizer is the sole sanctioned mutator.

Finalization outcomes

  • Success (committed / exit 0): an ordinary hook-enabled commit is proven. Only now may rebase/update-base, integration, or retirement proceed.
  • Sanitation (rotation_required / exit 10): exact index and live artifacts are sanitized, drafts are ready, no commit was attempted. Rotate first, then resume with fresh --allow-commit --rotation-confirmed; recovery does not restage.
  • Hook/commit failure (commit_failed / exit 11): HEAD is unchanged and the exact prepared tree plus drafts remain. Fix the hook without changing that snapshot, then make one explicit recovery call with fresh --allow-commit. There is no automatic retry.
  • Uncertain outcome (exit 8): reconcile only. Never issue another commit automatically; prove the exact HEAD or escalate for inspection.
  • Manual handoff: LazyGit reads per-worktree LAZYGIT_PENDING_COMMIT; standard Git uses per-worktree COMMIT_EDITMSG. A draft makes recovery possible but is not proof that a commit happened.

An actual rebase is identified by Git's state directories (rebase-merge or rebase-apply), not by a stale REBASE_HEAD file alone. Other active operation markers and index.lock also block queue/finalization.

Bootstrap a repository

Read references/pre-commit-redaction-stack.md before bootstrap or migration.

Current hook release: ahh-v2.0.1 is the immutable published pin used by the checked-in template and hook migration target. Fresh bootstrap also pins gitleaks v8.30.1 and installs the scoped Sourcegraph/Git-OID false-positive fix. Updating only the ahh tag does not rewrite an existing root .gitleaks.toml; follow the rollout steps in the reference for existing repos.

bash skills/local/agent-history-hygiene/scripts/bootstrap-project.sh \
  --install-hook
pre-commit run --all-files

Bootstrap installs:

  • a validation-only staged-index checker plus gitleaks;
  • precise nested SpecStory state ignores, keeping history/ visible;
  • generic hygiene hooks whose mutators exclude every agent/archive/install root: .agents, .claude, .codex, .cursor, .opencode, .specify, .specstory; and
  • optionally a validation-only prepare-commit-msg exact-selector gate.

Security checkers retain coverage of those roots. Excluding only .specstory from whitespace/EOF/formatters is insufficient: .agents/skills/ contains installed vendored code, and .claude/skills/ may symlink to it.

For old ahh-v1.1.0 or exact local-redactor layouts, the post-release migration is transactional and fail-closed:

bash skills/local/agent-history-hygiene/scripts/bootstrap-project.sh --migrate

It preserves compatible sibling hooks/options, validates a candidate before publishing, and refuses ambiguous/customized legacy commands.

Post-leak remediation

When a scan finds a real credential, or the user says it was committed/pushed:

  1. stop any reflexive force-push;
  2. read references/remediation.md end-to-end;
  3. rotate at the provider regardless of blast radius; and
  4. only then choose the least disruptive Git action from the runbook.

Gotchas

  • .gitignore does not untrack existing state. Use bootstrap-project.sh --untrack-specstory-state; never broaden the ignore to .specstory/.
  • SpecStory >= 2.4.0 redacts on write but has context/rule gaps. Keep native redaction enabled and retain the finalizer check. Read references/specstory-native-redaction.md before changing placeholders or coverage.
  • SpecStory 2.10 is checkout-path scoped, not branch scoped. Create the worktree first, then start one wrapper/session per change stream. EnterWorktree does not rebind an existing recorder.
  • Exact discovery rejects incomplete JSONL, aliases, symlinks, unsafe bytes, and cross-worktree identity. Read references/transcript-session-discovery.md when resolution is empty or ambiguous.
  • plansDirectory may be ignored at project scope in some Claude Code versions. Verify where the plan landed; user-level "plansDirectory": "./.claude/plans" is the recommended default.
  • pre-commit install is per clone. CI is a second chance, not the last gate.
  • --install-hook requires core.hooksPath genuinely unset. Empty, relative, global, or custom values are refused; integrate the validation-only gate in the configured hook directory yourself.
  • gitleaks protect is deprecated. Use gitleaks git --staged or gitleaks dir.
  • Rotate oversized sessions instead of raising the 2 MiB transcript allowance.

Available scripts

  • run-specstory-session.sh [--allow-commit] claude [-- OPTIONS...] — owns the foreground process group, preserves child/signal status, performs one post-exit exact sync, and optionally invokes one finalizer.
  • queue-agent-commit.sh --commit --session-id UUID --specstory-path PATH (--plan PATH|--no-plan) --message-file PATH — writes one strict, metadata-only, per-worktree request; identical repeats are idempotent.
  • finalize-agent-commit.sh --request ABSOLUTE_PATH (--allow-commit|--runner-token TOKEN) [--rotation-confirmed] — validates lifecycle/snapshot proof, prepares exact sanitized artifacts and drafts, executes one ordinary commit, and reconciles uncertain state without retry.
  • find-session.sh (--session-id UUID|--specstory-path PATH|--newest) [--format specstory|claude|both] [--json] — exact checkout/session proof; --newest is heuristic compatibility only.
  • stage-agent-artifacts.sh ... — exact or broad atomic staging. --sanitize-index sanitizes selected blobs in the locked alternate index; --materialize-sanitized safely writes those exact sanitized bytes back after generation checks; --check-staged never mutates.
  • agent-commit-metadata.sh [--harness NAME --model NAME] [--format trailers|json] — derives canonical provenance from staged blobs.
  • scan-staged.sh [--redact|--no-redact] [--config PATH] [--verbose] — validates the effective index; emits only bounded secret-free JSON findings and suppresses raw scanner output.
  • probe-specstory-redaction.py [--json] [--keep] [--dry-run] — measures native coverage; exit 30 when SpecStory is absent.
  • bootstrap-project.sh [--migrate] [--install-hook] [--untrack-specstory-state] [--force] [--dry-run] — installs/migrates the validation stack; remember the unpublished-v2.0.0 blocker above.

Bundled assets

  • assets/artifact-dirs.txt — canonical artifact directory set.
  • assets/pre-commit-config.yaml.template — validation-only checker, gitleaks, and generic hygiene with full archive/install-root mutator exclusions.
  • assets/gitleaks.toml.template — custom rules and scoped example allowlists.
  • assets/redact_secrets.py — staged-index checker/sanitizer. --check-index accepts canonical or temporary commit indexes; --fix-index requires an explicit user-owned noncanonical GIT_INDEX_FILE and exact --files.

Reference files

Tests

Run from the repository root:

make test-skill

The suite covers exact discovery/staging, alternate-index sanitation and materialization, validation-only migration/hooks, scanner-safe output, runner/finalizer authorization and recovery, stale REBASE_HEAD, private state, and shipped-file safety. Read tests/README.md for the full inventory and direct commands.

Related skills