* docs: add go cli quality discovery plan
* fix(cli): harden gc bridge version parsing
* docs(go): align complexity policy with gates
* docs(cli): classify command surface
* fix(cli): enforce json output contracts
* feat(cli): expand completion coverage
* refactor(cli): pilot badge options writer
* refactor(cli): inject contradict output writer
* refactor(cli): inject notebook update writer
* fix(cli): enable gc runtime smoke coverage
* fix(cli): align gc bridge with gascity v1
* Release v2.39.0
* fix(tests): align next-work schema test with widened type enum
Commit ed35c47f added `docs` and `chore` to the script's VALID_TYPES,
but tests/scripts/check-next-work-schema-rows.bats:42 still used
`type:"docs"` as its supposed-to-be-rejected example, so the test
now passes through the validator — `bats-tests` has been red on
main since.
Switch the rejected-type example to `finding`, which is still
outside the enum and is also the canonical example cited in the
script's own header comment ("type=finding ... caught at push time").
Local: 11/11 in tests/scripts/check-next-work-schema-rows.bats pass.
* docs: capture finding generator postmortem (#157)
* fix(rpi): sort verdicts deterministically in context and status helpers (#158)
BuildPhaseContext and JoinVerdicts iterated the verdicts map directly,
producing non-deterministic output across runs. Sort keys before
rendering so prompt injection and status logs are reproducible.
Closes council finding W-7 (context-orchestration-leverage batch). The
related buildHandoffContext path was already fixed to delegate to
FormatVerdicts (which sorts); this completes the remaining helpers.
- cli/internal/rpi/status.go: sort keys in JoinVerdicts
- cli/internal/rpi/phased_context.go: sort keys in BuildPhaseContext
- tests: replace "ordering not deterministic" waivers with exact-order
asserts and add a 50-run stability check
https://claude.ai/code/session_01Qw4bZvXUNuLs8Rp1j1homB
Co-authored-by: Claude <noreply@anthropic.com>
* fix(codex): audit noisy hook injections (#159)
* docs: add competitive radar (#160)
* feat(agents): harden operator control plane (#161)
* docs(discovery): plan agents control plane hardening
* feat(agents): harden operator control plane
* Release v2.39.0
* fix(tests): align next-work schema test with widened type enum
Commit ed35c47f added `docs` and `chore` to the script's VALID_TYPES,
but tests/scripts/check-next-work-schema-rows.bats:42 still used
`type:"docs"` as its supposed-to-be-rejected example, so the test
now passes through the validator — `bats-tests` has been red on
main since.
Switch the rejected-type example to `finding`, which is still
outside the enum and is also the canonical example cited in the
script's own header comment ("type=finding ... caught at push time").
Local: 11/11 in tests/scripts/check-next-work-schema-rows.bats pass.
* docs: capture finding generator postmortem (#157)
* fix(rpi): sort verdicts deterministically in context and status helpers (#158)
BuildPhaseContext and JoinVerdicts iterated the verdicts map directly,
producing non-deterministic output across runs. Sort keys before
rendering so prompt injection and status logs are reproducible.
Closes council finding W-7 (context-orchestration-leverage batch). The
related buildHandoffContext path was already fixed to delegate to
FormatVerdicts (which sorts); this completes the remaining helpers.
- cli/internal/rpi/status.go: sort keys in JoinVerdicts
- cli/internal/rpi/phased_context.go: sort keys in BuildPhaseContext
- tests: replace "ordering not deterministic" waivers with exact-order
asserts and add a 50-run stability check
https://claude.ai/code/session_01Qw4bZvXUNuLs8Rp1j1homB
Co-authored-by: Claude <noreply@anthropic.com>
* fix(codex): audit noisy hook injections (#159)
* docs: add competitive radar (#160)
* fix(merge): drop unused AgentsDoctorError ref and prune allowlist entries without production refs
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(merge): resolve conflicts by taking PR's refactored contradict/codex_runtime, main's overnight test schema
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(goals): preserve diagnostic tail in truncateOutput
Head-only truncation at 500 runes cut the operator hint off the end of
gate output. The 2026-04-26 nightly retrospective showed flywheel-
compounding's "sessions must use 'ao lookup --cite ...'" tail being lost
mid-word for verbose runs.
New shape: when input exceeds 500 runes, keep first 200 + truncation
marker + last 200 runes. Short inputs untouched. Verified against live
ao goals measure --json — six long-output gates (flywheel-proof,
hook-preflight, go-cli-tests, contract-compatibility, install-smoke,
flywheel-lifecycle) now expose both their failure label and their
trailing fix-it.
Source: 2026-04-26 nightly retro task 1.
* feat(ci): add standards-injector reference completeness gate
hooks/standards-injector.sh fails open when a mapped <lang> is missing
its reference file. That's how `.js` lost standards inject for weeks
until the 2026-04-26 nightly caught it. New gate parses the case
statement, asserts every mapped lang has skills/standards/references/
<lang>.md, and runs in pre-push and CI.
Wires:
- scripts/check-standards-injector-completeness.sh (new)
- scripts/pre-push-gate.sh (slot 27b under hook category)
- .github/workflows/validate.yml (parallel job + summary deps)
- tests/scripts/check-standards-injector-completeness.bats (6 cases:
happy path, missing-named, |-alternation, parser-empty, real-repo)
Removing skills/standards/references/javascript.md temporarily makes
the gate FAIL with a clear message; restoring makes it PASS.
Source: 2026-04-26 nightly retro task 2.
* feat(rpi): add probed_stale_at + probed_by + ao rpi mark-probed
When a nightly probes a queue item and finds it stale, the knowledge
dies in the digest — tomorrow's run re-probes the same item. Adds
optional probed_stale_at (RFC3339) and probed_by fields to the v1.3
item schema, the Go round-trip type, the schema-rows acceptance test,
and a new `ao rpi mark-probed --id=... --by=...` subcommand for future
nightlies to write these without hand-editing JSON.
- docs/contracts/next-work.schema.md (Item table)
- cli/internal/rpi/types.go (NextWorkItem fields, omitempty)
- cli/internal/rpi/types_test.go (round-trip + omitempty)
- cli/cmd/ao/rpi_mark_probed.go (new subcommand)
- cli/cmd/ao/rpi_mark_probed_test.go (4 cases)
- cli/docs/COMMANDS.md (regenerated)
- tests/scripts/check-next-work-schema-rows.bats (acceptance case)
validate-next-work-contract-parity.sh stays green.
Source: 2026-04-26 nightly retro task 3.
* fix(overnight): probe Dream packets for staleness before emit
Three nightlies in a row emitted the same two stale packets ("philosophy
doc", "next-work schema v1.3") because no gate verified the cited
surface wasn't already shipped. Curator now runs a 5-second tractability
probe against each candidate's TargetFiles + scripts/ refs in the
morning command before writing it. Conclusively-stale packets are
suppressed; the suppression is recorded as a dream-curator-suppressed
entry on the run summary so operators see what was skipped instead of a
silent gap.
- cli/cmd/ao/overnight.go (overnightSummary.CuratorSuppressed)
- cli/cmd/ao/overnight_packets.go (probeDreamPacketStaleness,
extractScriptsRef, suppression plumbed through
buildDreamMorningPacketPlans return)
- cli/cmd/ao/overnight_packets_test.go (suppression-on-existing-target +
emits-when-inconclusive integration tests)
Source: 2026-04-26 nightly retro task 4.
* chore(scripts): add install-bd.sh installer
bd has been "unavailable" in three consecutive nightly runs. Upstream
(steveyegge/beads) publishes signed cross-platform binaries for
darwin/linux on amd64/arm64. New installer detects the platform,
downloads the matching tarball, installs to ~/.local/bin/bd, and
verifies via `bd version`. Idempotent: short-circuits when the
requested version is already present (use --force to override).
- scripts/install-bd.sh (new, executable)
- tests/scripts/install-bd.bats (4 offline-safe cases)
Verified end-to-end on darwin/arm64: download + extract + verify all
green.
Source: 2026-04-26 nightly retro task 5.
* fix(ci): align bats stub and AGENTS table with new standards-injector gate
PR #156 introduced scripts/check-standards-injector-completeness.sh
without:
- adding a make_stub for it in tests/scripts/pre-push-gate.bats setup;
three pre-push-gate bats tests (404, 414, 415) used a FAKE_REPO that
lacked the new script and the gate fired "missing executable", so
status came back non-zero
- listing it in the AGENTS.md CI table; validate-ci-policy-parity.sh
diffs the table against validate.yml summary.needs and flagged 2
drift groups
Both gates pass locally now.
Cycle 1 added Tags to GOALS.md (notably long-cycle, corpus-state on
flywheel-compounding) but operators still had no way to ask "what's
the project's score if we set aside the corpus-state goals I cannot
move in this commit?".
Add --exclude-tag <tag> to `ao goals measure`. The flag drops any
goal whose Tags include the value before the snapshot runs. Combines
with --goal (goal-id filter applies first, then tag filter).
Demonstration:
$ ao goals measure --json | jq .summary.score
92.66
$ ao goals measure --exclude-tag long-cycle --json | jq .summary.score
94.06
Implementation:
- MeasureOptions.ExcludeTag (new field)
- applyMeasureFilters helper extracted from RunMeasure body so both
filter steps live together and RunMeasure stays under the cli/
CC=20 ceiling (was 17, would have grown to 21 inline → extracted)
- goalHasTag predicate on Goal.Tags
- Wired via --exclude-tag string flag (with help text) on goals_measure
- cli/docs/COMMANDS.md regenerated
Tests:
- TestGoalsMeasure_ExcludeTagFilter: long-cycle goal excluded; total=1,
failing=0, score=100
- TestGoalsMeasure_ExcludeTagPlusGoalIDInteraction: --goal narrows to a
tagged goal first, then --exclude-tag drops it → empty snapshot, no
error (named goal was found and then filtered)
74736e7a regenerated COMMANDS.md with a stale .tmp/ao-docgen binary,
silently deleting 152 lines covering ao agents inspect/lint, ao scenario
init/list/validate, ao sessions index, and ao trace. The pre-push gate
caught this via the doc-release CLI_SKILLS_MAP check (declared headings
56, file had 51).
Re-run scripts/generate-cli-reference.sh against a freshly built binary
to restore the catalog and add the new ao agents doctor entry.
Fourth subcommand under ao agents. Rolls up the existing inspect (#141)
and lint (#143) outputs and adds two new cross-surface checks:
- "orphan skills" — skills/<name>/ exists but no .agents/<name>/
- "undocumented dirs" — .agents/<name>/ is neither catalogued nor a skill
Default exit policy mirrors lint: non-zero only when the lint script
fails. --strict also fails on any orphan or undocumented entry, suitable
for CI gates.
Reuses parseAgentsAllowlist + discoverActiveSkills from agents.go and
re-implements the lint exec path inline (so doctor can keep going past
a lint failure and still collect the cross-surface report). JSON output
via the AgentsDoctorReport shape.
9 tests cover registration, helper functions (sorted output, hidden-dir
skipping, missing-agents-dir tolerance), text/JSON shapes, and exit
codes (lint-failure pass-through, --strict orphan exit). COMMANDS.md
regenerated. using-agentops skills (Claude + Codex) updated to mention
the new subcommand; codex hashes regenerated; parity drift + audit
both pass.
Closes agentops-zw6.
* feat(cli): add ao agents inspect for .agents/ surface introspection
Cycle 3 of the .agents/ hygiene goal — concretize the "ao agents"
namespace decision with the smallest useful subcommand.
* cli/cmd/ao/agents.go introduces the parent agents command and its
first subcommand inspect. Reads docs/contracts/agents-write-surfaces.md
(cycle 1 contract), parses the BEGIN/END allowlist block, joins it
with active skills/<name>/SKILL.md entries, and emits a structured
summary. Defaults to text output; --json emits the AgentsInventory
shape.
* cli/cmd/ao/agents_test.go adds 9 tests (1 cmd registration, 1 subcmd
registration, 5 table-driven parse cases, 2 skill discovery cases,
3 runtime cases — text/JSON/missing-contract).
* cli/cmd/ao/cobra_commands_test.go adds "agents" to expectedCmds so
TestCobraExpectedCmdsMatchRegistration stays green.
* cli/docs/COMMANDS.md regenerated via scripts/generate-cli-reference.sh.
Establishes the namespace pattern (mirrors ao goals / ao ratchet /
ao flywheel). Future cycles can layer ao agents lint, doctor, migrate
without re-litigating the namespace decision.
The default contract path docs/contracts/agents-write-surfaces.md
lands with PR #139 (cycle 1). When that PR is unmerged the command
errors clearly: "reading contract docs/contracts/agents-write-surfaces.md:
no such file or directory" — operators can pass --contract <path> or
wait for cycle 1 to land.
* docs(cli): bump declared CLI command headings count to 56
ao agents adds one top-level heading to cli/docs/COMMANDS.md.
validate-cli-skills-map.sh requires the cli-skills-map.md banner
plus summary table to match. Update both 55-> 56.
--reset: delete .agents/compiled/ and .hashes.json before compiling.
Use to force a full rebuild or clean up stale output from a prior
broken compile run. Safe on missing dirs (idempotent).
--repair: scan .agents/compiled/ and remove article .md files with
zero inbound [[wikilinks]]. Infrastructure files (index.md, log.md,
lint-report.md) are always preserved. Addresses the 2026-04-15
scenario where a failed compile run left fallback stubs that later
had no natural way to be cleaned up.
Both flags are standalone-capable (no LLM runtime required) and
compose with --full / --compile-only.
Tests:
- TestResetCompileOutput_RemovesDirectory
- TestResetCompileOutput_IdempotentOnMissingDir
- TestRepairCompileOutput_RemovesOrphansOnly
Regenerated COMMANDS.md for the new flag entries.
Closes na-pmx1.8.
Fixes three onboarding defects that surfaced on 2026-04-15 when running
against a global ~/.agents corpus from a non-git home directory:
1. `ao compile --full` failed in the installed bundle with
"open skills/compile/scripts/compile.sh: file does not exist". The
Makefile's sync-hooks target never copied compile.sh into
cli/embedded/skills/, so the //go:embed all:skills fallback had
nothing to fall back to. Fix: sync-hooks now seeds the script and a
regression test (TestMaterializeCompileScriptFallsBackToEmbedded)
asserts the embedded copy resolves when no local skills/ exists.
2. Headless compile implicitly required OLLAMA_HOST / OPENAI_API_KEY /
ANTHROPIC_API_KEY with no actionable error when none were set, and
ignored a locally installed `claude` CLI. Fix: resolveCompileRuntime
auto-detects the `claude` binary and selects claude-cli runtime (no
API key needed — inherits Claude Code auth). preflightCompileRuntime
fails fast with an actionable error naming the exact env var to set.
compile.sh gains a matching claude-cli backend and a --preflight flag.
3. The headless compiler concatenated all changed files into one LLM
prompt, which is unworkable on 2000+ file corpora. Fix: compile.sh
batches by --batch-size (default 25) and --max-batches (default
unlimited); ao compile propagates both flags.
Onboarding gaps:
- $quickstart now detects a global ~/.agents corpus (GLOBAL_AGENTS=true)
and, when GIT=false + AO=true, surfaces a harvest → compile →
knowledge-activation path instead of only suggesting `git init`.
- Harvest SKILL.md clarifies that promotion writes to ~/.agents/learnings/
(not ~/.agents) and adds a "which skill do I need?" decision table.
- Compile SKILL.md documents the claude-cli runtime and batching flags.
- skills-codex/compile/scripts/compile.sh wrapper now falls back through
multiple candidate paths and finally delegates to `ao compile`, so an
installed bundle without a sibling skills/ checkout still works.
Tests added:
- TestMaterializeCompileScriptFallsBackToEmbedded
- TestResolveCompileRuntime
- TestPreflightCompileRuntimeErrors (5 subtests)
- TestPreflightCompileRuntimeSuccess
- TestCompileScriptOptionsPassesBatchFlags
- TestQuickstartSkillCoversGlobalCorpus (both parity surfaces)
Verification:
cd cli && go vet ./... && go test ./... # all green
bash skills/heal-skill/scripts/heal.sh --strict # clean
bash tests/docs/validate-doc-release.sh # PASS
bash scripts/check-contract-compatibility.sh # 0 failures
bash scripts/regen-codex-hashes.sh # 3 skills rehashed
bash scripts/generate-cli-reference.sh # COMMANDS.md regenerated
Three subcommands under a new 'ao beads' command group that complement
the bd CLI without replacing it. All commands degrade gracefully when
bd is not on PATH (warn + exit 0).
## Commands
### ao beads verify <bead-id>
Reads 'bd show <id>', extracts file paths, function references, and
backticked symbols from the description (or close reason for closed
beads), and checks each citation against HEAD. Reports FRESH / STALE /
UNKNOWN status per citation.
Bare filenames (e.g. 'loop.go' with no path) are treated specially:
the verifier walks cli/, skills/, docs/, scripts/, .agents/ to find
matches and reports FRESH if unique, UNKNOWN if ambiguous (with the
resolved path suggestions), STALE if zero matches.
Exit codes: 0 clean, 1 stale found, 2 bd error.
### ao beads lint [--status=open]
Batch-runs verify across every bead matching a status filter and
aggregates results. Exit code signals overall staleness — suitable
as a weekly audit or pre-release gate.
### ao beads harvest <bead-id>
Materialises a closed bead's close reason as a learning file at
.agents/learnings/YYYY-MM-DD-<id>-<slug>.md with frontmatter. Only
operates on closed beads. --dry-run prints to stdout instead of writing.
Compresses the bd-close → learnings pipeline into one command, so
operators running 'bd close <id> --reason "..."' can immediately
promote the reason into the knowledge flywheel.
## Design
* execBD / bdAvailable are package-level function vars so tests can
inject fakes without touching PATH (pattern consistent with existing
cmd/ao test hooks).
* parseBDShow is tolerant of both open and closed bead formats. Closed
beads hide their original description and surface a 'Close reason:'
line instead; the parser captures both and Body() prefers CloseReason.
* listBeadIDs uses a permissive <rig>-<ident> grammar so it handles
flat rows, hierarchy rows (├──/└── tree chars), and future bd shape
tweaks without breaking.
* No hard dependency on bd for tests — all L2 tests mock execBD and
drive the parse/verify/render logic directly.
## Tests
cli/cmd/ao/beads_test.go (L2, 23 tests):
* beadSlugify: basic, punctuation, truncation, empty, punct-only
* parseBDShow: canonical open, closed with Close reason, empty errors
* bdShowParsed.Body: prefers CloseReason, falls back to Description
* extractCitations: file+line, function refs, backticked symbols, dedup
* verifyFileCitation: fresh, stale, line-suffix handling
* isClosedStatus: substring matching across bd status-field shapes
* listBeadIDs: flat + tree output, non-bead lines skipped
* verifyBead: bd-absent graceful degradation + end-to-end with fake
* renderLearningBody: frontmatter + body composition
cli/cmd/ao/cobra_commands_test.go:
* beads added to expectedCmds + parentExpectations (verify|lint|harvest)
## Smoke test
Ran against na-h61 (the bead closed earlier today). Found:
- 4 FRESH, 0 STALE, 1 UNKNOWN (ambiguous bare 'types.go' — resolved
to 6 candidates, correctly suggested full paths)
- harvest --dry-run produced a clean learning file with frontmatter
- lint --status=closed found 49 clean / 1 genuinely stale (na-63f.8
references summary.json/summary.md which doesn't exist anywhere)
## Files
cli/cmd/ao/beads.go (new, ~550 LOC)
cli/cmd/ao/beads_test.go (new, ~280 LOC)
cli/cmd/ao/cobra_commands_test.go (+3 lines — beads in registry)
cli/docs/COMMANDS.md (regenerated)
## Why
Closes the last 3 items of the 7-item summary table from today's
retrospective. The first 4 items (--evidence, --commit-ready,
--discovery-artifact, stale-scope planning rule) landed in 63c8b81a.
The na-h61 session earlier today showed that bead descriptions drift
from HEAD within hours of being filed. Two of the five beads I closed
today had factually wrong descriptions (na-h61 cited a nonexistent
cli/cmd/ao/fitness.go; na-khk cited a non-reproducible env leak).
'ao beads verify' catches exactly this pattern before a new session
commits to the wrong mental model.
Introduces the C3 warn-only ratchet from the Dream RunLoop Correctness
series. Warn-only mode now consumes a bounded rescue budget and falls back
to strict halting behaviour once exhausted, preventing the "warn-only
protects me forever" anti-pattern that enabled the 2026-02-22 evolve
overnight 115-cycle runaway.
New package-local primitives in cli/internal/overnight/warn_only_budget.go:
- WarnOnlyBudgetState on-disk schema v1
- ReadBudget with rescue matrix (missing → default, corrupt → default,
out-of-range → clamp)
- WriteBudget atomic (CreateTemp → Sync → Rename)
- ResetBudget / DecrementBudget helpers
- Default rescue ceiling = 3
Loop integration via RunLoopOptions.WarnOnlyBudget (*WarnOnlyRatchet).
Pointer-based opt-in preserves legacy infinite-warn-only behaviour for
the existing L1/L2 test suites that pass WarnOnly=true without a ratchet.
At most one rescue is consumed per iteration even when both regression
and plateau fire on the same pass. Halt reasons are annotated
"(warn-only budget exhausted)" when the ratchet was the reason strict
mode kicked in.
Cmd-layer wiring in cli/cmd/ao/overnight.go reads .agents/overnight/
warn-only-budget.json at run start, passes a live ratchet with a
persistence callback, and stamps WarnOnlyBudgetInitial/WarnOnlyRemaining
into overnightSummary for the morning report.
New subcommand `ao overnight warn-only reset [--initial N] [--json]`
restores the budget file to a fresh state after an operator investigates
a burned-through budget.
Tests:
- L1 warn_only_budget_test.go covers all 4 rescue-matrix paths, atomic
write round-trip, Reset semantics, and Decrement countdown.
- L1 overnight_test.go additions cover the reset subcommand (default +
--initial override + --json emission) and the ratchet literal wire-in
to RunLoopOptions, mirroring Micro-epic 2's RunID regression guard.
CLI docs regenerated via scripts/generate-cli-reference.sh. Pre-existing
failures in internal/rpi toolchain_test.go (unrelated "bushido" env var
leak on main HEAD) are out of scope.
Refs: .agents/handoff/2026-04-10-dream-runloop-correctness.md §Micro-epic 4
Rename the athena skill to compile — a descriptive name that communicates
what it actually does. Extend with Karpathy's LLM Knowledge Bases architecture:
- New compile phase: LLM reads raw .agents/ artifacts and produces interlinked
markdown wiki at .agents/compiled/ with index.md, log.md, backlinks
- New lint phase: detects contradictions, orphan pages, stale claims, coverage gaps
- Pluggable compute backend via AGENTOPS_COMPILE_RUNTIME (ollama|claude|openai)
- Hash-based incremental compilation (only recompile changed sources)
- compile.sh engine with security fixes (no shell injection, path traversal guard)
- Full rename across 93 files: skills, docs, CLI, tests, hooks, scripts, codex
Existing Mine → Grow → Defrag cycle preserved as phases within the larger
compile workflow. No vector DB — compiled wiki IS the retrieval layer.
Add --mixed CLI flag to ao rpi phased. Extend executionPacket and
phaseHandoff structs with mixed_mode_requested, mixed_mode_effective,
planner_vendor, reviewer_vendor, and mixed_mode_degraded_reason fields.
Provenance is carried across phase boundaries via handoff and persisted
in the execution packet. Downstream phases (MR-2..MR-5) will populate
effective/vendor fields. Regenerated CLI docs.