Summary:
- lands the audited current WIP lanes and excludes stale/process-only
material
- hardens prune path confinement, probe-v3 evidence binding,
codebase-recon identity, handoff/release/reverse-engineer behavior, and
Codex prompt handling
- truth-labels static skill scoring and regenerates all owning
projections
Validation:
- fresh independent PASS on commit
427098ed10
- full Go suite and full Go race suite
- quick local release CI
- focused Bats, scenario/linkage, native-skill, reverse-engineer,
Cathedral, Ruff, Python ratchet, projection, and diff checks
Residual boundaries:
- final-basename ABA remains unclaimed
- live skill-probe coverage remains honestly 0/12
- release-only cross-build, SBOM, vulnerability, and release-evidence
checks are left to delivery CI
7.1 KiB
title, description, permalink, last_reviewed
| title | description | permalink | last_reviewed |
|---|---|---|---|
| .agents/ workspace hygiene | Retention conventions for the .agents/ runtime workspace and how ao doctor keeps it clean. | /agents-dir-hygiene | 2026-08-16 |
.agents/ workspace hygiene
.agents/ is the per-repository runtime workspace: gitignored local state
written by the ao CLI and by skills while they work. It is not committed
(except a small pinned config under .agents/ao/), it is not a deliverable,
and it is never a source of authority — work belongs to the tracker, source
and delivery history to Git, sessions to CASS, curated memory to CM, and
rules to docs/ADRs.
The preferred target layout from
ADR-0016: ao/ (requested proof), scratch/
(disposable work, scratch/WRITER/DATE-SLUG/), and projections/
(named-consumer, manifest-stamped derived views). This target is not enforced
as an exact closed set. A root outside it must have a declared compatibility or
exact-path consumer contract; otherwise it is migration debt. Current declared
exceptions include read-only .agents/handoff/, live
.agents/mto-handoff/, and earlier skill-output roots retained by an explicit
consumer contract in their owning skill.
Two broad shapes of migration debt exist:
- Knowledge-shaped directories — postmortems, pre-mortem checks, handoffs, retros, proofs: human-readable records a later session may re-read while they await promotion or expiry.
- Machine-artifact directories — queue dirs, run scratch, staged outputs: consumed once by tooling and then debris.
Left alone, the second class accumulates. The workspace subsystem of
ao doctor detects specific registered failure modes and garbage-collects
their debris safely; it does not currently flag every top-level root outside
the preferred layout. Where this page and ADR-0016 disagree, the ADR wins.
The ephemeral-dir contract
Any writer that mints a queue- or run-shaped directory under .agents/ MUST
follow this contract:
- Mark abandonment by renaming, never by deleting. A directory that is
done or abandoned is renamed to
<name>.stale-<timestamp>with a UTC timestamp inYYYYMMDDTHHMMSSZform, e.g.land-queue-age-h433.19.stale-20260711T221032Z. - Retry attempts get
-retry<N>suffixes, e.g.land-queue-age-h433.22-native-retry2. A retry directory may itself later be staled; both suffixes can appear on one name. - GC is
ao doctor's job. Writers never delete workspace directories — they only rename. Doctor collects stale and retry-chain directories whose newest content is older than the TTL.
The GC TTL defaults to 14 days, measured against the newest regular-file
modification time inside the directory. Override it with
AO_DOCTOR_WS_TTL_DAYS (a positive integer day count; invalid values fall
back to the default).
Canonical directory names
Skills and sessions have historically spelled the same top-level directories several ways. Doctor normalizes toward one canonical name per concept:
| Canonical | Known drift aliases |
|---|---|
postmortem |
post-mortem, post-mortems |
pre-mortem-checks |
pre-mortem, pre-mortems, premortem-checks |
ao/handoff |
handoffs |
retro |
retros |
proofs |
proof |
tests |
test |
This table is a projection of workspaceCanonicalAliases in
cli/internal/doctor/fix_workspace.go — the Go table is the source of truth.
If the two disagree, the Go table wins and this page is stale.
.agents/mto-handoff/ is not a handoff spelling alias. It is a distinct live
recurrence protocol consumed at that exact path by
scripts/assay/consume-mto-recurrence.sh; Doctor preserves it in place.
How to clean up
ao doctor # detect-only: report findings, change nothing
ao doctor --fix # apply fixers (routes through mutate())
ao doctor --only fm-ws-empty-dirs # scope to one detector (repeatable)
ao doctor --only workspace # or scope to the whole subsystem
The workspace detectors are:
| Detector ID | Finds | Fix behavior |
|---|---|---|
fm-ws-stale-queue-dirs |
.stale-* / -retry<N> directories past TTL |
quarantine (rename) |
fm-ws-empty-dirs |
empty top-level dirs (abandoned scaffolding) | quarantine (rename) |
fm-ws-naming-drift |
drift-alias directory names (table above) | merge/rename to canonical |
fm-ws-dual-store |
learnings split across .agents/learnings and .agents/ao/learnings |
report-only; defers to ao doctor --fix --only fm-knowledge-orphaned-flywheel-learnings (moves top-level *.md/*.jsonl only) |
fm-ws-nested-tree |
a .agents runtime tree nested under a repo subdirectory |
report-only; manual review (may be an intentional nested project) |
fm-ws-oversize |
unexpectedly large directories | report-only |
Every mutating run writes receipts under
.doctor/runs/<timestamp>__<runid>/: a report.json describing findings and
applied fixes, an actions.jsonl journal of every mutation, and an undo.sh
wrapper for reversing them (see the exact undo contract below).
.doctor/latest is a symlink to the most recent run.
Safety envelope
- Doctor has no delete operation by design. What reads as "deletion" is an
atomic rename into the run's
quarantine/directory (.doctor/runs/<run>/quarantine/workspace/<dirname>). Final disposal of quarantined content is always the human's call — for old runs,ao doctor gcrequires explicit--yesand--before <date>. - Ambiguous merges are refused, not guessed. When a drift-alias merge (or the knowledge subsystem's learnings consolidation) would collide with existing content and the right merge is not mechanically certain, the finding lands in a Skipped list with the reason, and nothing moves.
- What undo can and cannot reverse. Detectors are pure reads; every disk
write flows through the audited mutation path and lands as a journal line.
FILE mutations are content-hashed and backed up verbatim before they change,
and undo restores them from those backups. DIRECTORY renames (quarantines
and drift merges) carry no byte backup — the renamed/quarantined tree
itself is the preserved copy, byte for byte — and undo reverses them by
replaying the rename backwards. A crash in the narrow window between a
rename landing and its journal line being written leaves that one mutation
invisible to undo; recovery is manual, from the run's
quarantine/directory (the run receipts list what moved, and nothing is ever deleted). - Concurrent external writers are bounded, not excluded. Doctor's locks are per-process. A writer outside doctor racing a fix can slip content into a directory in the moment it is quarantined; that content is moved with the directory — journaled, restorable via undo, never destroyed.
What doctor will NOT decide
Disposal of large archives and eval payloads is a judgment call, not a
mechanical one. fm-ws-oversize reports them — path, size, file count — and
stops there. Doctor never quarantines, moves, or deletes content solely for
being big; deciding what a large artifact is worth is the operator's job.