377 Commits

Author SHA1 Message Date
Bo 7bbc6569c4 feat(liveness): enforce author-judge quorum gate (ag-xdrw) (#737)
## Summary

Implements `ag-xdrw` independent-verification gates for claim-evidence
and significant orchestration actions.

- Adds `author_id` / `judge_id` metadata to claim-evidence bindings.
- Rejects self-graded bindings at the ClaimEvidenceBinder port.
- Requires non-empty, distinct author/judge metadata for high-assurance
PG3/PG4 evidence while preserving legacy anonymous PG1/PG2 behavior.
- Carries reviewer metadata through in-memory + production
claim-evidence derive paths and durable JSONL binding records.
- Adds `liveness.CheckSignificantAction` for orchestration-tier
significant actions requiring two distinct non-actor ACKs across at
least two model families.
- Regenerates CLI docs/surface/registry after adding `ao claim bind
--author-id/--judge-id` flags.

## Validation

Current head: `e4456538d89660c954ca12d347a186bd1cf68edf` rebased over
#740 (`853d781e`).

Focused checks after rebase:

- `go test ./internal/liveness ./internal/ports`
- `go test ./cmd/ao -run
'TestProductionClaimEvidenceBinder|TestClaimBind|TestClaimList|TestProductionClaimEvidence|TestClaim'`

Required regen after rebase:

- `bash scripts/regen-all.sh` regenerated artifacts, then exited nonzero
on the known CMDAO surface parity red: `wiki query` / 30 uncovered
commands.

Required pre-push after rebase:

- `PRE_PUSH_FAIL_FAST=false bash scripts/pre-push-gate.sh` completed
with 4 known non-diff failures:
  - test-fixture parity: `scripts/gc-stale-worktrees.sh`
  - worktree disposition: canonical root dirty/untracked operator state
  - corpus freshness: newest local snapshot older than threshold
- contract canaries: known `agentops-core.pre-push-gate-governance` Bats
failure

Diff-relevant gates passed in the pre-push run, including changed-scope
build/vet/race, command/test pairing, `goals validate`, registry drift,
CLI docs parity, command-surface matrix canary, shellcheck, mkdocs,
contract compatibility, CI policy parity, and context-map drift.

## Review Gate

No self-grade: cross-model ACKs are recorded in Agent Mail.

- WindyCastle re-confirmed ACK on `547eb36f` and then explicitly called
to rebase once more over #740 and merge this keystone next (#134).
- RubyMoose re-confirmed ACK on `547eb36f`; current `e4456538` is the
same reviewed semantic diff rebased over #740 plus regenerated registry
timestamp.

Review attacks covered:

- PG3/PG4 anonymous reviewer metadata rejects; PG1/PG2 anonymous legacy
remains allowed.
- `AuthorID == JudgeID` rejects.
- Significant-action actor ACK does not count toward quorum.
- Two same-family ACKs do not satisfy quorum.
- Non-quorum path returns `NeedsAdmission`, never `Allowed`.

Bead: `ag-xdrw`
Evidence: TestProductionClaimEvidenceBinder_SelfGradeRejected
Evidence:
TestProductionClaimEvidenceBinder_HighAssuranceWithoutReviewersRejected
Evidence: TestCheckSignificantActionTwoCrossModelACKsAllowed
Evidence: TestCheckSignificantActionActorACKDoesNotCount
2026-06-05 04:05:51 +00:00
Bo 8ad6747913 chore(ao): delete the ao evolve CLI surface (ag-llni slice 1) (#724)
**Slice 1 of ag-llni** — retire `ao rpi`/`ao evolve` (orchestration is
no longer an in-CLI engine). This PR removes the **`ao evolve`** half;
the `rpi*.go` + phased web is slice 2.

**Architecture (operator-confirmed 2026-06-04):** orchestration is
two-tier — *inner loop* (one session) = skills/CLIs/native-subagents +
**Workflows** (`.claude/workflows/operating-loop.js` + `bead-crank.js` —
the Claude encoding of inner-orchestration that was mis-shaped as
`rpi`/`evolve`); *outer loop* (many sessions) = **NTM +
mcp-agent-mail**. Codex inner-loop = NTM + agent-mail for now. Leaf CLIs
(`corpus`/`goals`/flywheel/knowledge loops = BC1 corpus), NTM, and
agent-mail all stay.

### Changes
- Deleted `ao evolve` + 4 subcommands (`config`, `next-work`, `blocked`,
`write-stop-marker`) — 10 files.
- Relocated the one non-evolve coupling, `countCronHistoryRows`, into
its sole consumer `cron_self_adjust.go`.
- Removed `evolve` from the `cobra_commands_test.go` golden
`expectedCmds` list (2 sites).
- Regenerated `cli/docs/COMMANDS.md` (−125 lines) +
`docs/cli-skills-map.md`.

### Verification
- `go build ./...` green · `go vet ./cmd/ao` clean · **`go test
./cmd/ao` = 9251 pass**.
- Net: 13 files, −2192 LOC.

Bounded-context: BC5-Runtime
Closes-scenario: ag-llni#delete-evolve-cli
Evidence: cli/cmd/ao/cron_self_adjust.go
Test-Removal-Reason: ao evolve CLI surface retired (ag-llni) —
orchestration moved to .claude/workflows + NTM; the 5 deleted
evolve_*_test.go covered now-deleted command code, not kept behavior.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-05 00:21:53 +00:00
Bo 3d99d4bf69 fix(corpus): unify secret redaction + compile/CI git-safety (ag-sz3h ag-zi90 ag-y2xy #w1-git-safety) (#720)
Completes the **W1 git-safety arc** of epic ag-lhu0 (memory-system
re-architecture) — three beads, one coherent surface, after #719 wired
scrub-on-write into corpus_fs.

- **ag-zi90 (dedup):** `redactContent` (`ao context assemble`) now runs
canonical `llm.Redact` first — a single source of truth for credential
patterns (AWS / GitHub / GitLab / Slack / GCP / PEM, previously
uncovered in this path) — then keeps the supplements `llm.Redact` does
*not* cover: 2-segment app JWTs, sensitive env-var assignments,
high-entropy strings.
- **ag-sz3h (compile chokepoint):** new `ao redact` (stdin→stdout
canonical scrub) bridges the shell compile path; `compile.sh` pipes
every rendered article through it before `os.WriteFile` (loud fallback
if `ao` absent).
- **ag-y2xy (CI backstop):** `check-corpus-secret-scan.sh` scans
**git-tracked** (committed/allowlisted) `.agents` corpus + canon only —
repo-root `.agents` is local-only by policy, so scrub-on-write (#719 +
`ao redact`) is the primary defense and this is the backstop. Wired into
`validate.yml`; bats covers clean / leak-with-file:line /
untracked-not-scanned.

Scrub-on-write is primary; CI is the backstop. Verified: go
build/vet/test (incl. `TestRunRedact`, JWT-dedup regression test green),
bats 3/3, shellcheck clean, conformance regenerated.

Closes-scenario: ag-sz3h#compile-render-scrub
Closes-scenario: ag-zi90#dedup-redactor
Closes-scenario: ag-y2xy#ci-secret-scan
Bounded-context: BC1-Corpus
Evidence: scripts/check-corpus-secret-scan.sh

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-04 14:11:55 -04:00
Bo 7bd90eab3b feat(canon): verify --council for independent cross-vendor verdicts (ag-mluj #council-verdict-independent) (#674)
## What

gap#1 — the load-bearing **"earned by verification, not assertion"**
gap.

`ao canon verify --council` delegates the verdict to an **independent
cross-vendor judge** instead of taking an operator-asserted one:
- `CouncilVerifier` port + `CommandVerifier` adapter, configurable via
`AGENTOPS_CANON_VERIFIER_CMD` (codex = the cross-vendor lane in this
fleet)
- Verdict is attributed to the **judge**, not whoever ran the command →
counts toward promotion even when the author invokes it (independence by
construction)
- No judge configured → **loud error**, never fabricates a verdict
- Judge output must carry `VERDICT: confirmed|refuted`; absence is an
error (never defaults to confirmed)
- The judge transcript is recorded as the verification **receipt**

## Honest scope

This wires the cross-vendor **judgment** lane (council's P9 cross-vendor
rule). How deeply the judge gathers its own evidence is bounded by what
the judge command does — a codex judge has shell access and can read
cited files; the receipt records what it examined. The full
"gather-your-own-evidence" keystone (mo-qz3srw) remains a further step.

## Verification
- 8 new unit tests (verdict parse, empty-cmd-fails-loud,
confirmed/refuted exec, no-verdict-errors, council-is-independent);
canon package green
- e2e: no judge → loud error; fixture cross-vendor judge → independent
`confirmed` attributed to `codex:gpt-5.3`; learning EARNED even when
author-invoked
- command-surface canary unchanged (flag, not a new heading)

Closes-scenario: ag-mluj#council-verdict-independent
Bounded-context: BC1-Corpus
Evidence: cli/internal/canon/verifier.go
2026-06-02 01:22:33 -04:00
Bo 3702761e75 feat(canon): earned-promotion team-knowledge tier (ag-bw5s #earned-promotion-gate) (#672)
## What

A team-knowledge **canon**: learnings *earned* into a shared trusted
set, not self-asserted. Adapts Doug Dan's warmind (#671) to the
post-ACFS direction — moat = knowledge earned by verification, not
self-certification.

Promotion requires **two independent, self-guarded signals**:
- **citation** — another engineer/agent USED it (proves useful)
- **verification** — another engineer/agent CHECKED it (proves true;
receipt-bearing)

An independent refutation hard-blocks. The anti-self-certification guard
is generalized to *all* promotion signals: the factory may not grade its
own homework.

## Slices (3 commits, one coherent arc)
1. **Earned-promotion gate** — `internal/canon` (identity guard,
citation + verification JSONL ledgers, gate); `ao canon
cite/verify/status/promote`.
2. **Inject trusted tier** — `collectLearnings` surfaces
`.agents/canon/learnings/` deduped + boosted 1.15× over equal-utility
local; flagged `Canon`.
3. **IdentityResolver port** — `--as` > `AGENTOPS_ACTOR` env > git. Env
tier is the seam to Agent Mail (`whois`) with **no MCP coupling** — `ao`
stays portable. Makes the cross-actor guard hold for humans AND agents.

## Deliberately dropped from #671
Resurrected `hooks/` + session-end auto-sync (hookless regression), the
"Gas City Dispatch (CRITICAL)" doctrine (contradicts
prune-the-coordination-plane), the parallel scoring/maturity engine
(duplicated inject primitives), and the "warmind" name.

## Verification
- `go build`/`vet` clean, gofmt clean
- 9,388 tests green (`cmd/ao` + `internal/canon` + `internal/search`)
- e2e: apollo self-attesting excluded; witness (distinct agent via
`AGENTOPS_ACTOR`) earns promotion; `ao inject` ranks canon first at
equal utility

## Deferred (next)
Wire `verify` → `ao verify`/council (currently operator-attested — the
load-bearing gap); tier-aware gate (heuristics earn citation, not
verification).

Closes-scenario: ag-bw5s#earned-promotion-gate
Closes-scenario: ag-bw5s#inject-trusted-tier
Closes-scenario: ag-bw5s#agent-identity-resolver
Bounded-context: BC1-Corpus
Evidence: cli/internal/canon/promote.go
2026-06-01 00:57:12 -04:00
Bo a36abd3675 chore(provenance): align relation enum to W3C PROV-O vocabulary (ag-lmdx.7 #prov-o-vocabulary) (#660)
## What

Rename the provenance ledger relation enum from AgentOps-local
`subject_verb_object` names to the standard **W3C PROV-O / PROV-DM**
verbs so an external auditor recognizes the term, and document the
**columns-not-JSON-paths** guard-read principle in the schema. Schema
changes are single-writer on `main`. Implements ag-lmdx.7.

### Relation mapping (atomic contract change)

| Prior AgentOps-local | W3C PROV-O |
|---|---|
| `decision_produces_artifact` | `wasGeneratedBy` |
| `decision_authorizes` | `wasAssociatedWith` |
| `artifact_derived_from` | `wasDerivedFrom` |
| `scenario_covers_artifact` | `wasInformedBy` |
| `verdict_attests_artifact` | `wasAttributedTo` |
| `bead_scopes_decision` | `wasInfluencedBy` |
| `commit_implements_decision` | `wasRevisionOf` |
| `learning_revises_decision` | `wasInvalidatedBy` |

## Scenarios

- **Relations use PROV-O vocabulary** — the enum now contains only
PROV-O verbs; an edge with the colloquial `derives_from` (or the prior
`artifact_derived_from`) is REJECTED in favor of `wasDerivedFrom`.
Enforced by two new bats cases (accept PROV-O, reject legacy
vocabulary).
- **Guard-read field is a column not a JSON path** — the schema
description now states that guard-read/queryable fields (`from_id`,
`to_id`, `relation`, `trust_tier`, the hash-chain anchors) are
first-class top-level columns, never nested JSON payload paths, because
Dolt JSON-path generated-column indexing is unreliable. The `judge_id`
verdict guard already reads a first-class struct field in
`evidencedturn`; **no Dolt migration is invented** (no Dolt projection
schema exists in this repo — the principle is encoded in the contract).

## Ripple (every merged consumer, atomic)

- `schemas/agentops-sdlc-provenance.v1.schema.json` (enum + description)
- `cli/internal/provenancegraph/edge.go` (`Relations` + godoc)
- schema-driven validator via
`tests/scripts/validate-provenance-ledger.bats` (PROV-O accept + legacy
reject)
- consumers: `drrebuild`, `drwitness`, `evidencedturn`,
`cmd/ao/provenance_*`, `cmd/ao/turn_verify`
- all hash-chained fixtures **re-sealed via the canonical hasher**
(`drrebuild` ledger + frozen `expected-graph-hash.txt`,
`committed-witness.jsonl`); witness dolt-rows + provenance JSON fixtures
updated
- generated `cli/docs/COMMANDS.md` regenerated

## Verification

- `cd cli && go build ./... && go vet ./...` clean; **`go test ./...` →
11942 passed in 72 packages**
- `bats validate-provenance-ledger.bats
witness-dolt-jsonl-crosscheck.bats` → 20/20
- `bats provenance-orphan-fixtures.bats check-provenance-orphans.bats` →
7/7
- `scripts/check-contracts-structural-floor.sh` → PASS (45 contracts)
- `docs/provenance/ledger.jsonl` does not exist (no real entries to
migrate); `.agents/ao/provenance/graph.jsonl` is a separate
transcript-mining graph (no `relation` field) and out of scope.

Closes-scenario: ag-lmdx.7#prov-o-vocabulary
Bounded-context: BC4-Factory
Evidence: schemas/agentops-sdlc-provenance.v1.schema.json
2026-05-31 15:37:11 -04:00
Bo 554eb8c306 feat(turn): enforce author!=validator no-self-grading invariant (ag-lmdx.4 #author-validator-guard) (#658)
## What

Enforce the no-self-grading invariant (`ag-lmdx.4`): a Turn graded by
its own author is autocorrelated, so an Evidenced Turn is not done
unless an independent judge context produced the acceptance verdict.

- **`cli/internal/evidencedturn`** — new `author_neq_validator`
predicate + `AuthorID`/`JudgeID`/`AllowSelf` fields on `Input`. The
predicate FAILS when `judge_id == author_id` (self-graded) or when no
judge identity was recorded (independence that was never recorded cannot
be asserted); the documented, default-OFF `--allow-self` waives it for
the inline fallback. This fills the `TODO(ag-lmdx.4)` extension point
the #657 author left.
- **`cli/cmd/ao/turn verify`** — `--allow-self` flag +
`author_id`/`judge_id` on the turn-input file, wired into the DoD
predicate so a self-graded verdict fails the `validated->closed` guard.
- **`/pre-mortem` + `/vibe` SKILL.md** (and `skills-codex` twins) —
refuse a self-graded PASS verdict; require the judge to be a blind,
context-isolated sub-agent (`judge_id != author_id`); document
`--allow-self` as the default-OFF escape and point at `ao turn verify`
as the enforcement predicate.

## Tests (TDD-first)

`evidencedturn`: self-graded refused / independent accepted /
`--allow-self` waives / missing-judge refused. `cmd/ao`: `turn verify`
self-graded exits non-zero, `--allow-self` passes.

## Gates

`go build ./...` + `go vet` + `go test ./...` green (11845 pass). gosec:
0 new findings in touched files. Codex parity audit +
generated-manifest/artifacts validators pass. `tests/skills/run-all.sh`:
87/87. COMMANDS.md regenerated (the one `--allow-self` line); codex
manifest spliced to pre-mortem+vibe only (no drift-sweep).

## Dedup note (ag-9jle.5)

This bead is the **invariant/guard** (data shape + predicate + skill
refusal). The skill text states the blind-sub-agent requirement, but the
actual blind-judge SPAWNING MECHANISM in `/vibe` + `/validation`
(ag-9jle.5) is NOT implemented here. Partial overlap on the skill
contract; the runtime mechanism remains for ag-9jle.5.

Closes-scenario: ag-lmdx.4#author-validator-guard
Bounded-context: BC2-Validation
Evidence: cli/internal/evidencedturn/evidencedturn.go
2026-05-31 15:05:06 -04:00
Bo 486cb99be5 feat(turn): ao turn verify Evidenced-Turn DoD predicate (ag-lmdx.5 #ao-turn-verify) (#657)
## What

Adds `ao turn verify <bead>` — a legible Definition-of-Done predicate
for an **Evidenced Turn** (ag-lmdx.5). Where the live AP#7 gate checks
Evidence *presence*, this checks turn *sufficiency*: one verdict
answering "can this bead legally transition validated->closed?" with a
clear pass/fail per predicate and a reason for every gap (`--json` too).

## Definition of "Evidenced Turn"

A turn is DONE iff all six predicates pass:

| predicate | meaning |
|---|---|
| `chain_intact` | the bead's state_transition log hash-verifies and
folds (turnstate `FoldVerified`) |
| `terminal_state` | the folded lifecycle state is at the
validated->closed boundary |
| `scenarios_covered` | every Closes-scenario claim has a passing test |
| `evidence_resolves` | every Evidence line resolves to a gate log that
exercised THAT scenario (presence->sufficiency) |
| `provenance_event` | a provenance edge in the ledger references the
bead |
| `no_orphan` | no artifact in the turn's trace-graph is a provenance
orphan |

## How

- New pure, in-memory predicate core `cli/internal/evidencedturn` (no
Dolt / no shell-out) — the testable kernel that defines what "done"
MEANS.
- Builds directly on **#654** `cli/internal/turnstate`
(`Seal/Append/Fold/FoldVerified/VerifyChain`) and
`cli/internal/provenancegraph` (edges + `FindOrphans`).
- `cli/cmd/ao/turn_verify.go` wires `--input` (turn-input JSON: state
log + scenario coverage), `--ledger` (provenance EDGE ledger), and
`--graph` (optional trace-graph for orphan audit). Non-zero exit on
not-done makes it usable as a transition guard.

## Scope note (ag-lmdx.4)

The author!=validator guard is a **separate** concern and is NOT
implemented. It is left as a documented extension point (TODO on
`Evaluate`); the verdict shape already accommodates an extra predicate
row without a reshape.

## Tests

`cli/internal/evidencedturn` (14 cases: complete-turn-done, each missing
predicate fails with a legible reason, multi-gap, blank-bead error) +
`cli/cmd/ao/turn_verify_test.go`
(complete/incomplete/json/orphan/no-graph/missing-input/bead-mismatch).
`go build ./...`, `go vet`, full `cmd/ao`+`evidencedturn` suites (9288)
green.

## Derived surfaces regenerated

COMMANDS.md, cobra `expectedCmds`, command-surface smoke (top=75 sub=188
all=263) + matrix.json, registry.json (cli_commands 72->73),
cli-skills-map.

Closes-scenario: ag-lmdx.5#ao-turn-verify
Bounded-context: BC4-Factory
Evidence: cli/internal/evidencedturn/evidencedturn.go
2026-05-31 14:40:05 -04:00
Bo a6336b7c34 feat(provenance): ao provenance trace --orphans --strict gate (ag-x31t.6 #provenance-orphan-gate) (#653)
## What

Adds `ao provenance trace --orphans --strict`: detects provenance
orphans — artifact nodes with **no inbound authored/inferred edge** — by
generalizing the `goals_trace_orphans` no-inbound chain-gap detection
onto the provenance graph. Wires it as a blocking CI gate.

## How

- **`cli/internal/provenancegraph/orphans.go`** — `ReadGraphRecords`
(parses the goalstrace `Node`/`Edge` JSONL contract that the seeded
fixtures use) + `FindOrphans` (an artifact node is an orphan iff no
edge's `to_id` targets it; it flips green the moment any inbound edge is
added).
- **`cli/cmd/ao/provenance_trace.go`** — the `trace --orphans [--strict]
[--json] [--graph <path>]` subcommand. `--strict` exits non-zero when
orphans exist; `--json` emits one finding per line.
- **`scripts/check-provenance-orphans.sh`** + a new **blocking** step in
`.github/workflows/validate.yml` (in the existing goals/spec-linkage
job, right after the warn-only `ao goals trace --orphans`). The gate
asserts the strict audit **catches** each seeded orphan fixture and
**passes** once an inbound edge wires the artifact back to a directive —
so it is deterministically green while proving the detector is wired.

## Tests (TDD-first)

- `cli/cmd/ao/provenance_trace_test.go` (L2): drives the real
`tests/fixtures/provenance/` fixtures against `expected-orphans.json` —
`--strict` catches each of the 3 seeded orphans
(`gate:scenario-hash-stability`, `artifact:scripts/pre-push-gate.sh`,
`claim:65-jobs`), and a wired graph exits 0. Plus mode/flag-guard cases.
- `cli/internal/provenancegraph/orphans_test.go` (L1): detection,
non-artifact nodes never orphaned, deterministic sort, JSONL parsing +
malformed/missing-file rejection.
- `tests/scripts/check-provenance-orphans.bats`: gate-script behavior.

## Derived surfaces regenerated

- `cli/docs/COMMANDS.md` (new `ao provenance trace` subcommand,
cobra-conformance passes)
- CLI surface smoke fixture + matrix (sub 186→187, all 260→261)
- `registry.json` **unchanged** (provenance is already a top-level
command; subcommands aren't counted) — timestamp-only churn reverted.

## Gates

`go build ./... && go vet ./... && go test ./...` (11959 pass) · gosec
clean on new files (one `#nosec G304` on the operator/CI-supplied graph
path, with reason) · `scripts/validate-ci-policy-parity.sh` PASS.

Closes-scenario: ag-x31t.6#provenance-orphan-gate
Bounded-context: BC4-Factory
Evidence: .github/workflows/validate.yml
2026-05-31 14:07:45 -04:00
Bo 2fecf2b304 feat(provenance): ao provenance export deterministic hash-chained (ag-x31t.5 #provenance-export) (#651)
## What

Adds `ao provenance export`: a deterministic, hash-chained rendering of
the committed provenance ledger (`docs/provenance/ledger.jsonl`).

- Reads the ledger, **canonically sorts** edges by `(ts, from_id, to_id,
relation)` (with type/trust/evidence tie-breakers for a total order),
then **re-seals** them into a fresh per-record hash chain.
- Output is **byte-identical on re-run** regardless of the ledger's
physical append order. Default = JSONL (one compact edge per line);
`--json` = indented array; `--verify` = one-line OK summary, no varying
body.
- The re-chained export **verifies with no Dolt server** — the committed
JSONL is the audit authority, and re-chaining uses only the in-process
hashing in `cli/internal/provenancegraph` (same `prev_hash` discipline
as the rpi ledger). No reinvented Edge/hash logic: new
`CanonicalSort`/`ReChain` helpers reuse `Seal`/`VerifyChain`.

## Tests (TDD-first)

- `chain_test.go`: canonical-sort ordering + stability + non-mutation,
order-independent re-chain, byte-identical serialization, empty ledger,
invalid-edge rejection, tamper detection.
- `provenance_export_test.go`: deterministic bytes across runs, chain
verifies, empty ledger (`[]` not `null`), `--verify` summary,
tampered-ledger rejection.

## Derived surfaces regenerated

- `cli/docs/COMMANDS.md` (cobra conformance)
- `evals/agentops-core/fixtures/cli-command-surface-smoke.sh` +
`cli-command-surface-matrix.json` (sub 185→186, all 259→260)
- `registry.json` unchanged: it counts top-level commands only
(subcommands not tracked; top-level count = 72).

## Gates

`go build` / `go vet` / `go test ./...` green; gosec clean on the new
files; CLI-skills-map, JSON-flag-consistency, and
`generate-cli-reference.sh --check` all pass.

Closes-scenario: ag-x31t.5#provenance-export
Bounded-context: BC4-Factory
Evidence: cli/cmd/ao/provenance_export.go
2026-05-31 13:34:45 -04:00
Bo 427c25b248 feat(provenance): provenance_edges write-model + ao provenance add (ag-x31t.4 #provenance-write-model) (#649)
## What

Builds the WRITE side of the SDLC provenance/intent graph (ag-x31t slice
1). Adds `ao provenance add` / `ao provenance list` plus a new
`cli/internal/provenancegraph` package that seals typed, evidence-backed
edges onto the per-record hash-chained ledger at
`docs/provenance/ledger.jsonl`.

Consumes the schema merged in #637
(`schemas/agentops-sdlc-provenance.v1.schema.json`). Does not touch the
schema (.2), export (.5), or gate (.6).

## CQRS doctrine

Per CLAUDE.md and the council architecture, the committed JSONL ledger
is the **audit authority and source of truth**; any Dolt
`provenance_edges` table is a rebuildable projection that loses on
disagreement. So this command appends the JSONL ledger directly. Hashing
reuses the `cli/internal/rpi/ledger.go` discipline: `payload_hash =
sha256(canonical payload)`, `hash = sha256(payload_hash + "\n" +
prev_hash)`, genesis `prev_hash=""`.

## Behavior

- `ao provenance add <from-id> <to-id> --relation ... [--from-type
--to-type --trust-tier --evidence --ts --json]` — seals + appends one
schema-valid edge. **Idempotent** on edge identity
(endpoints+relation+evidence+trust-tier), so a re-run with a different
timestamp is a no-op.
- `ao provenance list [--json --from-id --relation]` — reads edges back
in chain order.

## Tests (TDD, table-driven, exact-value asserts)

- `edge_test.go`: field/enum validation, deterministic hash chain,
schema-version forcing, tamper + chain-link detection, identity
stability, JSON field-name parity with the v1 schema.
- `store_test.go`: append→read round-trip + persisted-chain verify,
idempotency, reject-invalid-before-write (no file created), corrupt-line
rejection, and **validation of emitted edges against the merged schema
via `scripts/validate-provenance-ledger.sh`**.
- `provenance_add_test.go`: add produces a schema-valid sealed edge +
list reads it back, idempotent no-op, invalid-relation error, list
filters.

## Gates

- `cd cli && go build ./... && go vet ./... && go test ./...` — all
green (11949 pass).
- Command-surface bumped: regenerated `cli/docs/COMMANDS.md`, updated
`cli-command-surface-smoke.sh` + `cli-command-surface-matrix.json` (top
73→74, sub 183→185, all 256→259), added `provenance` to cobra
expectedCmds; `validate-cli-skills-map.sh` PASS.

Closes-scenario: ag-x31t.4#provenance-write-model
Bounded-context: BC4-Factory
Evidence: cli/cmd/ao/provenance_add.go
2026-05-31 13:02:55 -04:00
Bo 1307eb0e0a feat(skills): queryable skill catalog JSON + ao skills (soc-vuu6.4 #skill-catalog-json) (#640)
## What

Slice 2 of the skill-catalog bead (soc-vuu6.4): the **queryable
surface** over the generated `skills/catalog.json`. Slice 1 (the
generator, schema, drift gate, and `catalog.json` itself) landed in PR
#379; this adds the `ao skills` query commands the bead's acceptance
criterion calls for.

- New `cli/internal/skills/catalog.go` — loads `skills/catalog.json` and
a **pure, table-tested** query engine
(`List`/`Consumers`/`Producers`/`Mermaid`). No SKILL.md re-parsing;
reads the committed, CI-synced catalog.
- New `cli/cmd/ao/skills_query.go` — four cobra subcommands:
- `ao skills list [--role --produces --consumes --practice
--user-invocable] [--json]`
  - `ao skills consumers <skill> [--json]`
  - `ao skills producers <output> [--json]`
  - `ao skills graph [--format mermaid]`
- Regenerated `cli/docs/COMMANDS.md`; bumped the cli-command-surface
fixtures (`sub` 179→183, `all` 252→256) for the 4 new subcommands.

## Why

`registry.json` and `catalog.json` existed but had no query API — agents
grepping "which skill produces X?" had to scan markdown. This turns the
catalog into a queryable surface (the bead blocks `ao skill new`, which
needs sibling-skill defaults).

## Acceptance

> `ao skills list ... --json` returns the matching skill set in <100ms.

Verified: `ao skills list --produces result.json --json` → `[beads,
discovery]`; `ao skills consumers rpi` → 5 skills; `ao skills graph`
emits deterministic Mermaid. Latency well under 100ms.

## Scope notes

- Did **not** regenerate `skills/catalog.json` — it carries pre-existing
drift on `main` (the `check-skill-catalog-drift` gate is I0/advisory).
This PR only *consumes* the catalog; regenerating would sweep in
unrelated drift.
- The bead's illustrative `--tier judge` example does not map to the
real generated schema (which has no `tier` field — it uses
`hexagonal_role`, `produces`, `consumes`, `practices`). Filters
implemented against the actual catalog fields.

## Tests

`cli/internal/skills/catalog_test.go` (engine, exact-value table tests +
load round-trip) and `cli/cmd/ao/skills_query_test.go` (JSON shape, sort
invariants, flag validation, mermaid header) — all green. `go build`/`go
vet`/`go test ./...` pass; conformance + command-surface smoke pass.

Closes-scenario: soc-vuu6.4#skill-catalog-json
Bounded-context: BC1-Corpus
Evidence: cli/internal/skills/catalog.go
2026-05-31 12:36:45 -04:00
Bo 4131e7c720 feat(rpi): materialize harvested next-work into durable beads (ag-9jle.3 #lessons-to-beads) (#633)
## What

Closes the missing half of the BDD-Gherkin-wave loop — **lessons →
beads**. Harvested follow-ups landed in `.agents/rpi/next-work.jsonl` as
a queue but never became durable beads, so the flywheel executed without
compounding into the tracker.

New **`ao next-work materialize`**:
- Reads unmaterialized items from `next-work.jsonl` and `bd create`s one
durable bead each.
- Carries `source_epic` + `proof_ref` on **bd's native `--metadata`** —
*not* a forked provenance format. The real provenance graph edge is
deferred to `ag-x31t.4`'s future `ao provenance add`.
- **Idempotent** via the existing per-item `bead_id` back-reference.
Re-runs skip already-materialized / consumed / held-for-review items.
- Flags: `--file`, `--dry-run`, `--json`, `--source-epic`,
`--materialized-by`.
- `skills/post-mortem/references/harvest-next-work.md` now invokes it
after the queue-write (CLI owns the deterministic core; the skill just
calls it).

## First failing test (Gherkin contract)

> Given a completed wave with a harvested follow-up · When materialize
runs · Then a durable bead exists via `bd create` carrying
`source_epic`/`proof_ref`, not only a `next-work.jsonl` queue line.

`TestNextWorkMaterialize_CreatesDurableBeadWithProvenance` + 6 more
(idempotency, skips-consumed/held, dry-run-no-mutation, source-epic
filter, 2 mapping tables) — all green.

## Compass-not-maps pivots vs the locked plan

Verifying each premise before executing (the W1-reconciliation
overstated-scope lesson) caught two beats that were **already done**:
1. The schema fields `source_epic`/`proof_ref` already exist in
`next-work-{item,batch}.v1.schema.json` — dropped the "add fields" beat.
2. The idempotency anchor `NextWorkItem.BeadID` already exists — no new
field invented.

## Boundary

Zero `ag-x31t` files touched; `validate.yml` untouched (parked per
handoff). `ao next-work` is collision-free (distinct from the existing
`ao evolve next-work` ladder recommender).

## Validation

- `cli/cmd/ao` package: all tests green except pre-existing
`TestCronSelfAdjust_RoundTrip` (macOS-only `/tmp` vs `/private/tmp` path
bug — fails identically on clean `main`, passes on CI Linux).
- `TestCobraConformance` + `TestCobraExpectedCmdsMatchRegistration`
updated for the new command; `COMMANDS.md` regenerated.
- `go vet` + `gofmt` clean.

Closes-scenario: ag-9jle.3#lessons-to-beads
Bounded-context: BC3-Loop
Evidence: cli/cmd/ao/next_work_materialize_test.go
2026-05-30 13:55:39 -04:00
Bo fb7ed11780 feat(eval): wire gate #3 holdout-burn enforcement into ao eval outcomes ingest (ag-vbwx) (#628)
## What

Closes the **#1 caveat from the 2026-05-30 council post-mortem (WARN)**:
`registerOutcomesBurn` (gate #3, shipped in #626) was a
**correct-but-unwired primitive** — `runEvalOutcomesIngest` never called
it, so `ao eval outcomes ingest` enforced **zero holdout quota at
runtime**. This makes it a **live guard**.

Adds a **`--burn-ledger <path>`** flag (opt-in, mirroring
`--expect-judge-hash`). When set and the score is a holdout grade,
ingest:
1. loads the persisted `HoldoutBurnLedger` JSON,
2. calls `registerOutcomesBurn` (which **refuses** on exhausted
`(suite,gt)` quota),
3. persists the updated ledger **atomically** (temp+rename) across
invocations.

So a cloud/Codex Outcomes run can no longer re-observe a spent holdout
split. **Managed Agents are NOT ZDR.** Empty path = no-op (dev/legacy
flows preserved); dev-split scores never burn.

## Scope

The **local-file persistence slice** of ag-vbwx. The global-Dolt
transport (cross-host shared ledger) remains a follow-on slice tracked
on ag-62g68. Two files + COMMANDS.md.

## Evidence (command-level, not just the primitive)
- `TestApplyOutcomesBurn_RefusesAtCommandLevel_WhenQuotaExhausted` —
`runEvalOutcomesIngest` refuses + leaves the ledger unmutated.
- `TestApplyOutcomesBurn_PersistsAcrossInvocations` — two burns under
budget 2 persist to disk, third refuses.
- `TestApplyOutcomesBurn_EmptyPathNoOp` +
`TestApplyOutcomesBurn_DevSplitNoBurn`.
- `go build ./...` ✓ · `go test ./cmd/ao/` 9229 pass ✓ · `go vet` clean
· gocyclo no findings · `generate-cli-reference.sh --check` up-to-date ·
`generate-registry.sh --check` OK (no new heading → surface-matrix
canary unchanged; SKU `cmd:ao.eval` unchanged).

Verdict that motivated this:
`.agents/council/2026-05-30-evolve-624-627-postmortem/verdict.md`
(local).

Closes-scenario: ag-vbwx#gate3-runtime-wiring-local-ledger
Bounded-context: BC4-Evaluation
Evidence: cli/cmd/ao/eval_outcomes_ingest.go
2026-05-30 08:07:10 -04:00
Bo e2b4dcad2c feat(eval): gate #2 judge_content_hash parity on outcomes ingest (ag-62g68 #gate2-hash-parity) (#627)
## What

`ao eval outcomes ingest` ignored the score's `judge_content_hash`
entirely — so a score graded against a **drifted rubric** would still
ingest into the council verdict and feed the Knowledge Flywheel. Gate #2
(SCHEMA rc2 drift key): a stale rubric must **self-invalidate**, exactly
as a stale local judge does.

Adds an optional **`--expect-judge-hash`** flag: when set, ingest
**refuses** (emits no verdict) if the score's `judge_content_hash`
differs from the active rubric's hash. Empty (default) = parity not
configured → legacy/dev flows unaffected.

## Scope

Second of **ag-62g68**'s gate scenarios (gate #3 write-side refuse
landed in #626). Pure guard `requireJudgeHashParity` is unit-tested; the
flag is exercised end-to-end through `runEvalOutcomesIngest`.
**Remaining bead slices stay open:** the rc3 Run-manifest write into the
eval-verdict-compiler pipeline, the `--score/--suite/--bead` interface,
and global-Dolt ledger persistence (wiring `registerOutcomesBurn` into
the command).

**Flag-only CLI change** — `COMMANDS.md` regenerated; the flag adds **no
new `###`/`####` heading**, so the `cli-command-surface-matrix` canary
counts are unchanged and the registry SKU (`cmd:ao.eval`) is unchanged
(verified `generate-registry.sh --check` green).

## Evidence
- `TestRequireJudgeHashParity` (match / mismatch-refuses / empty-no-op /
empty-score-vs-configured) +
`TestRunEvalOutcomesIngest_RefusesOnHashMismatch` (command-level:
mismatch refuses & emits no verdict, match ingests, unset = no check).
- `go build ./...` ✓ · `go test ./cmd/ao/` 9225 pass ✓ · `go vet` clean
· gocyclo no findings · `generate-cli-reference.sh --check` up-to-date ·
`generate-registry.sh --check` OK.

Closes-scenario: ag-62g68#gate2-judge-hash-parity
Bounded-context: BC4-Evaluation
Evidence: cli/cmd/ao/eval_outcomes_ingest.go
2026-05-30 07:42:46 -04:00
Bo c4552483be feat(cli): ao mcp serve --print-tools — curated MCP tool-descriptor surface (ag-h1mk #mcp-print-tools) (#619)
## What

New `ao mcp` noun + `serve` verb exposing **`--print-tools --json`** —
the curated, read-mostly MCP tool surface a hosted/SDK Claude loop uses
to orient and self-check. It's the descriptor `ao agent bundle --runtime
managed` (#618) already references. **Claude-only** (Codex shells `ao`
directly).

**Slice 1 of ag-3ucpd** (split per the evolve scope filter — the full
MCP server is genuinely oversized: a from-scratch JSON-RPC stdio
transport + live integration test, no MCP library vendored). This ships
the deterministic, high-value, fully-testable descriptor surface now;
the live transport + transport-round-trip integration test is carved to
the **ag-3ucpd** umbrella for a fresh context.

- `mcpToolDescriptors()` — 6 curated tools (`session_bootstrap`,
`inject`, `corpus_inject`, `standards`, `validate`, `goals_measure`)
with name + description + input_schema + holdout-sensitive flag.
- `mcpToolDenied()` — deterministic **NOT-ZDR holdout refusal**: a tool
call whose args reference `holdout` / `ground_truth` / `.agents/evals`
is denied (cloud MCP surface; eval substrate LOCKED;
`AGENTOPS_HOLDOUT_EVALUATOR` does **not** unlock the cloud surface).
- Bare `ao mcp serve` (no `--print-tools`) errors loudly pointing at the
ag-3ucpd live-transport follow-up — never a silent no-op.

## Tests (TDD-first)

`cli/cmd/ao/mcp_serve_test.go` — 6 cases: curated surface (6 tools,
schemas present), holdout refusal (query + path-escape), clean-call
allowed, `--print-tools` JSON shape, live-transport-not-yet error. `go
vet` clean, gocyclo < 15, full `cmd/ao` suite **9210 pass**.

## New-`ao`-command surfaces (all 5 regenerated up-front)

The corpus-encoded checklist from #618's three-round discovery — applied
in one shot, expecting one-shot-green:
1. `expectedCmds` ×2 (`mcp` inserted alphabetically) ·
2. `cli/docs/COMMANDS.md` (TestCobraConformance) ·
3. `registry.json` (69→70 commands, 164→165 SKU) ·
4. `cli-command-surface-matrix` eval fixtures (71/177/248 → 72/178/250;
smoke PASS) ·
5. `docs/cli-surface.md`+`.json` (`ao mcp serve` covered).

Scope-only diff (10 files, all mcp-attributable; no unrelated drift).

Closes-scenario: ag-h1mk#mcp-print-tools
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/mcp_serve_test.go
2026-05-30 01:51:19 -04:00
Bo 7fc46eb76e feat(cli): ao agent bundle — emit runtime-specific AgentOps-native Agent definitions (ag-eguw0 #agent-bundle) (#618)
## What

New `ao agent` cobra noun + `bundle` verb — emits a **runtime-specific
Agent definition** that stitches the AgentOps skill set + the `ao` tool
surface so an out-of-session loop (Managed Agent / Codex-NTM swarm) runs
under the same guardrails. Third child of epic **ag-7s9fo** (after
ag-2wln skill #616, ag-mptr CI gate #617).

- `--runtime managed` → Managed Agents JSON (model + stitched
instructions + `skills[]` + `ao` MCP tool descriptor;
self-hosted-sandbox block on `--sandbox self-hosted`).
- `--runtime codex-ntm` → NTM bundle (skills-codex/agent-native ref +
pane bootstrap running `ao session bootstrap` + `ao inject`); no MCP —
Codex shells `ao` directly.
- Default skills: `session-bootstrap, standards, validation,
provenance`. Flags: `--skills`, `--sandbox`, `--out`, `--json`.

## Security — HARD REFUSAL (NOT-ZDR)

Managed Agents are **not** ZDR. `buildAgentBundle` refuses (non-zero
exit + explicit message) if any selected skill **names/paths a
holdout/eval surface** or its `SKILL.md` body carries a holdout marker
(`private_holdout` / `ground_truth` / `holdout target`).
`AGENTOPS_HOLDOUT_EVALUATOR` does **not** authorize leaking holdout to a
cloud agent. The eval substrate stays LOCKED (no relitigation of
`~/.agents/evals/SCHEMA.md`). Fails closed.

## Tests (TDD-first)

`cli/cmd/ao/agent_bundle_test.go` — 7 cases on the pure
`buildAgentBundle` seam: managed defaults + `ao` MCP descriptor;
self-hosted sandbox block; codex-ntm shape (bootstrap + reference, no
MCP); **holdout refusal**; unknown runtime; default-skill set; managed
JSON required-keys (= the schema contract). All funcs CC < 15 (budget
25). `go vet` clean; full `cmd/ao` suite 9204 pass.

## Gates handled

- Added `agent` to **both** `expectedCmds` lists
(TestCobraExpectedCmdsMatchRegistration).
- Regenerated `cli/docs/COMMANDS.md` (TestCobraConformance) +
`docs/cli-skills-map.md`.
- `ao agent bundle` reads as `public-tested`/`covered`.
`docs/cli-surface.md` left at its standing tolerated state (non-blocking
inventory; reverted to avoid sweeping in unrelated #613 eval-outcomes
drift).

## Notes for review

- **Naming**: `ao agent` (singular, new) sits beside `ao agents`
(plural, AGENTS.md doctor/lint) — different domains, but the
singular/plural proximity is a UX smell. Proceeded per epic spec;
trivial rename if you'd prefer (e.g. `ao agentdef bundle`).
- **Schema follow-up**: the managed-output structural contract is
test-enforced (required-keys); a formal
`schemas/agent-definition.schema.json` + validator wiring can land later
if a downstream consumer needs strict validation. Kept out to bound this
PR.
- Instructions are stitched as skill **names + preamble**, not full
bodies — bounded payload + the agent loads full skills at runtime via
the `ao` tool surface (also avoids over-inlining).

Closes-scenario: ag-eguw0#agent-bundle
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/agent_bundle_test.go
2026-05-30 01:28:41 -04:00
Bo 4ad5ea40f2 feat(eval): ao eval outcomes ingest — Outcomes score → one verdict record (ag-hdqu0 #ingest-verdict) (#603)
## What
Adds `ao eval outcomes ingest <score.json>` — the third slice of
ag-hdqu0. Maps an Outcomes grader score (aggregate + per-criterion) onto
the **one** council verdict record
(`skills/council/schemas/verdict.json`: PASS/WARN/FAIL +
`satisfaction_score` + `satisfaction_breakdown`), closing the **Outcomes
→ Knowledge Flywheel** loop without forking the verdict format. Pairs
with `ao eval outcomes compile` (#601).

Verdict bands: PASS ≥ threshold · FAIL < 70% of threshold · WARN
between.

## One-shot surface landing (compounding from #601/ag-lkxx)
`ingest` is a `#####` leaf, so heading counts are unchanged — only an
allowlist entry was needed. Ran
`scripts/test-agentops-contract-canaries.sh` locally **before pushing**:
`cli-command-surface-matrix verdict=pass aggregate=1`. No CI canary
round-trips.

## Tests (TDD)
- `TestIngestOutcomesScore_ProducesVerdictRecord` — PASS verdict,
satisfaction_score=aggregate, breakdown=criterion scores, schema_version
4, non-nil findings.
- `TestIngestOutcomesScore_VerdictBands` — PASS/WARN/FAIL banding.
- `go test ./cmd/ao` green, vet/build clean.

Closes-scenario: ag-hdqu0.2#ingest-verdict
Bounded-context: BC1-Corpus
Evidence: cli/cmd/ao/eval_outcomes_ingest_test.go
2026-05-29 16:15:02 -04:00
Bo a3900ad0dc feat(eval): ao eval outcomes compile — holdout-safe rubric payload (ag-hdqu0 #compile-strip) (#601)
## What
Adds `ao eval outcomes compile <input.json>` under `eval` — the second
slice of ag-hdqu0, building directly on the merged
`evalsubstrate.ProjectRubric` (#599). Projects a locked Task + criteria
into an Outcomes rubric payload.

## Holdout isolation (Managed Agents are NOT ZDR)
- Projection via `ProjectRubric` (strips ground truth by construction).
- **Deny-by-default re-scan** (`ContainsAny`, guard layer 3):
`compileOutcomesRubric` REFUSES to emit any rubric that would carry a
holdout value (input `holdout_values` feeds the scan; never copied to
output).
- Carries `judge_content_hash` for stale-rubric self-invalidation.

Outcomes is a derived projection of the locked SCHEMA.md — never an
alternate authority.

## Tests (TDD)
- `TestCompileOutcomesRubric_StripsHoldoutTarget` — criteria carried,
zero holdout leak.
- `TestCompileOutcomesRubric_RefusesLeak` — deny-by-default errors on a
leaking criterion, names the value.
- 9184 `cmd/ao` tests pass, `go vet`/`build` clean, `COMMANDS.md`
regenerated for the new subcommand.

## Next in ag-hdqu0
`.2` ingest→verdict; `.4` re-parse/stale-hash; `.9` gate#3 burn ledger
(remaining half); the future GT-loader slice wires `holdout_values` from
the substrate automatically.

Closes-scenario: ag-hdqu0.1#compile-strip
Bounded-context: BC1-Corpus
Evidence: cli/cmd/ao/eval_outcomes_test.go
2026-05-29 15:49:22 -04:00
Bo e4e32a3374 feat(orchestration): dual-runtime OrchestrationPort foundation (ag-nk67 #orchestration-foundation) (#598)
## What

The **dual-runtime OrchestrationPort foundation** — the seam that gates
the
`[EPIC] AgentOps × Claude Managed Agents integration (dual-runtime)`
application epics.
Implements the 3-category model (Claude Workflow / NTM swarm / plain
skill) behind a
single `OrchestrationPort` with safe degradation **NTM → Claude-native →
beads floor**
and a global `AGENTOPS_ORCHESTRATION=off` opt-out.

Derived from a live 3-legged spike (`~/dev/agentops-3cat-spike/`): safe
degradation is
solvable at the selection layer; parallel buys quality/independence not
wall-clock at
small N; NTM is a multi-vendor control plane that runs Claude/Codex as
panes.

## What's in it (10 slices, agent-team driven)

- **Port + adapters** — `cli/internal/ports/orchestration.go` (idiomatic
to existing ports pattern) + new `cli/internal/orchestration/` pkg:
`Selector`, `ntm_probe` (capability detection via `ntm
--robot-capabilities`, not `command -v`), `beads_floor` +
`OrchestrationResult` parity type, degradation-conformance test.
- **Live CLI** — `ao orchestrate select` (`--pin`/`--opt-out`/`--json`);
COMMANDS.md regenerated.
- **Contracts** — `schemas/orchestration-{backend,result}.v1` + paired
`docs/contracts/` docs (structural-floor gated: 41 contracts).
- **Prose** — swarm/shared/crank rewritten NTM>native>beads; **gc
demoted** (soc-2rtm0); `rpi_phased_stream.go` doc-comment fixed.
- **Skills** — `automation-shape-routing` (the 3-category router) +
`workflow-builder` + meta-skill authoring chain wired (`context_rel`);
skill counts synced.
- `lib/orchestrate-select.sh` selector seam.

## Verification (local)

`go build ./...` clean · `go vet` clean · **56 Go tests pass**
(orchestration 31, ports 10, cmd/ao orchestrate 15) · structural-floor
PASS (41) · `lib/orchestrate-select.sh --self-test` 6/6 · live command:
`--json`→claude (NTM absent), `--opt-out`→beads, `--pin claude`→claude.

## Notes

- Bundles the foundation + the routing/authoring skills as one coherent
dual-runtime arc.
- NTM/Claude executors are stubs (the parity contract is real); concrete
executors land in the application epics this gates.
- Two-ladders distinction preserved (spawn-backend ladder A vs CLI
phase-executor ladder B) — see `docs/contracts/orchestration-ports.md`.

Closes-scenario: ag-nk67#orchestration-foundation
Bounded-context: BC5-Runtime
Evidence: cli/internal/orchestration/conformance_test.go
2026-05-29 18:42:45 +00:00
Bo bd4d97c292 feat(beads): ao beads scenarios extract --write — operator-confirmed write-back (ag-3gm #write-confirm) (#571)
## Summary

Slice 2 of **ag-dwq** (the bead-acceptance → Gherkin extractor line).
Adds `--write` to `ao beads scenarios extract <id>`:

- After printing the candidate block and a `y/N` prompt, appends the
rendered `## Scenarios` block to the bead's description via `bd update`
— **only on confirmation**.
- Declining (or EOF / empty input) leaves the bead unchanged (safe
default).
- The block is **appended**, not replacing the description, so the
original acceptance prose is preserved (lossless).
- `--write` short-circuits behind the existing already-has-block guard,
so it won't duplicate an authored block unless `--force` is also passed.

Read-only `extract` (dry-run) and `validate` behavior are untouched.
Closes the `--write` slice of the extractor; the LLM-prose fallback
remains a separate later slice of ag-dwq.

## Test plan

- [x] Test-first: 2 new L2 tests map to the bead's single
Given/When/Then — confirm → lossless write (`bd update` carries `##
Scenarios` + preserves original prose); decline → no `bd update` (bead
unchanged).
- [x] `cd cli && go build ./... && go vet ./cmd/ao` clean.
- [x] `cd cli && go test ./cmd/ao` green (full package).
- [x] `scripts/generate-cli-reference.sh` regenerated `COMMANDS.md`
(only the `--write` flag line added).

Closes-scenario: ag-3gm#write-confirm
Bounded-context: BC1-Corpus
Evidence: cd cli && go test ./cmd/ao -run
'TestRunBeadsScenariosExtract_Write'
2026-05-28 05:38:45 -04:00
Bo 2c5ac99e4d feat(beads): ao beads scenarios validate <id> — well-formedness check (ag-5cz #scenarios-validate) (#569)
## Summary

Slice 4 of **ag-dwq** (child bead **ag-5cz**). Adds `ao beads scenarios
validate <bead-id>`: a read-only, **dependency-free** validator for an
authored `## Scenarios` Gherkin block. It is the inverse acceptance gate
of the `extract` slice (#567) — extract turns acceptance into a
candidate block; validate confirms an authored block is well-formed.

- **`cli/internal/scenarios/validate.go`** (new, pure domain):
`ParseBlock(text) ([]Scenario, error)` + `Validate(text) error`. A
deterministic in-repo parser — **no `github.com/cucumber/gherkin-go`
dependency** (the bead's open design question, resolved toward the
repo's established dependency-free pattern). Each scenario must declare
a name and a Given/When/Then step in that order with a non-empty body;
`And`/`But` lines are accepted as continuations. Refactored into
`groupScenarios`/`assemble`/`checkStepOrder` to stay under the
cyclomatic-complexity budget (max 14 < 25).
- **`cli/cmd/ao/beads_scenarios.go`**: `validate` subcommand under the
existing `ao beads scenarios` surface. Agent-ergonomic (Directive 13):
exits 0 when well-formed, non-zero **naming the parse error** otherwise;
`--json` emits a verdict object (`{bead_id,valid,scenarios}` on success
/ `{bead_id,valid:false,error}` on failure) on **stdout** with
diagnostics on **stderr**; `bd`-unavailable degrades gracefully (warn +
exit 0).
- **`cli/docs/COMMANDS.md`** regenerated. `capabilities` / `robot-docs`
pick the command up automatically via the live command-tree walk.
Surface heading counts are **unchanged** (validate is a `#####` leaf
under the existing beads-scenarios group, so the `#{3,4}`
command-surface canary is unaffected).

### Out of scope (remaining ag-dwq slices)
`--write` / operator-confirmed bead update (**ag-3gm**) · LLM fallback
for non-deterministic prose. Parent **ag-dwq** stays OPEN.

## Test plan

- [x] `cd cli && go test ./internal/scenarios/...` — 14-case
`TestParseBlock` table (well-formed single/multi, And/But continuations,
block bounded by following heading, prose-prefixed block, no-block,
no-Scenario, missing-name, missing Given/When/Then, out-of-order,
empty-body, And-before-primary) + Validate nil/error + Render round-trip
- [x] `cd cli && go test ./cmd/ao -run Scenarios` — L2 with stubbed
`bd`: well-formed exit 0, malformed error, no-block error, `--json`
success/failure verdicts, bd-unavailable graceful, read-only contract
(no `bd update`)
- [x] `cd cli && go build ./... && go vet ./... && go test ./...` —
11951 passed, 69 packages, 0 failures
- [x] Real binary: `ao beads scenarios validate ag-3gm` → exit 0 (1
scenario well-formed); `ag-dwq` (free-text) → exit 1 naming the missing
block; `--json` verdicts valid
- [x] `gocyclo -over 15 internal/scenarios/` — clean;
`scripts/generate-cli-reference.sh --check` — up to date;
command-surface smoke — `top=69 sub=174 all=243`, exit 0

Closes-scenario: ag-5cz#scenarios-validate
Bounded-context: BC1-Corpus
Evidence: cd cli && go test ./internal/scenarios/... ./cmd/ao -run
Scenarios
2026-05-28 04:31:40 -04:00
Bo 70b6d638dc feat(beads): idempotent '## Scenarios' guard + --force for scenarios extract (ag-kyn #scenarios-guard) (#568)
## Summary

Next slice of the open epic **ag-dwq** (Gherkin scenarios extractor).
Slice 1 (`ag-ljd`, the dry-run `extract` command) merged in #567; this
adds the **idempotent guard** (ag-dwq scenario 3):

`ao beads scenarios extract <id>` now **refuses** when the bead already
carries a `## Scenarios` block — prints a diagnostic to stderr naming
`--force` as the corrective action, exits 0, emits nothing on stdout —
**unless `--force`** is passed, in which case it re-extracts and prints
normally.

- New pure `scenarios.HasScenariosBlock(text)`: detects a markdown `##
Scenarios` section heading (plural-only, case-insensitive). A bare
`Scenario:` line or singular `## Scenario:` heading does **not** match.
- Fetch now returns the bead's acceptance **and** description
(`fetchedBead`) so the guard can inspect the description, where authored
scenario blocks live.
- Stays a **dry-run**; no bead mutation on any path.

Out of scope (remain in parent ag-dwq): `--write`, `validate` subcommand
+ Gherkin parser dep, LLM fallback.

> **Bead note:** the canonical follow-up bead for this work is
**ag-kyn** (filed by a concurrent RPI cycle as a child of ag-dwq). A
duplicate bead `ag-gam` was created before ag-kyn was visible; `ag-gam`
is closed as duplicate-of `ag-kyn`. Branch name retains the `ag-gam`
token but the work closes `ag-kyn`.

## Test plan
- [x] `cd cli && go build ./... && go vet ./... && go test ./...` —
clean (66 pkg ok, 0 fail)
- [x] `go test ./internal/scenarios/ ./cmd/ao` — targeted, incl. new
`TestHasScenariosBlock` (11 cases) + 2 new L2 guard tests
- [x] Real binary: refuses on `ag-ljd` (has block), extracts on `ag-dwq`
(free-text bullets)
- [x] `cli/docs/COMMANDS.md` regenerated; `cli-command-surface-matrix`
smoke passes (69/174/243 — flags aren't headings)

Closes-scenario: ag-kyn#scenarios-guard
Bounded-context: BC1-Corpus
Evidence: cd cli && go test ./internal/scenarios/ ./cmd/ao
2026-05-28 04:07:49 -04:00
Bo a31999a061 feat(beads): ao beads scenarios extract → Gherkin to stdout (ag-ljd #scenarios-extract-stdout) (#567)
## Summary

Slice 1 of **ag-dwq** (child bead **ag-ljd**). Adds `ao beads scenarios
extract <bead-id>`: a deterministic, dependency-free, **dry-run**
command that converts a bead's free-text acceptance criteria into a
candidate Gherkin `## Scenarios` block printed to stdout. The bead is
never modified.

- **`cli/internal/scenarios`** (new, pure domain): `Extract(acceptance)
([]Scenario, error)` + `Render([]Scenario) string`. Word-boundary
`given`/`when`/`then` matching so `forgiven`/`whenever` don't
false-match; each clause must carry a letter so prose merely
*mentioning* "Given/When/Then" isn't mistaken for a scenario.
- **`cli/cmd/ao/beads_scenarios.go`** (new): subcommand under the
existing `ao beads` surface, fetching via the `execBD show --json` seam.
Agent-ergonomic (Directive 13): `--json` emits data on stdout /
diagnostics on stderr; `bd`-unavailable degrades gracefully (warn + exit
0); errors name the corrective action (manual authoring).
- **`cli/docs/COMMANDS.md`** regenerated. `capabilities` / `robot-docs`
pick the command up automatically via the live command-tree walk.

### Out of scope (later slices of ag-dwq)
`--write` / operator-confirmed update · idempotent `## Scenarios` guard
+ `--force` · `scenarios validate` + Gherkin-parser dep · LLM fallback
for non-deterministic prose.

## Test plan

- [x] `cd cli && go test ./internal/scenarios/...` — 12 cases
(single/multi-bullet, sentence-form, mixed casing, skip-unparseable,
empty→error, no-keywords→error, word-boundary, prose-mention→error,
Render exactness, round-trip)
- [x] `cd cli && go test ./cmd/ao -run BeadsScenarios` — L2 with stubbed
`bd`: stdout Gherkin, `--json`, unparseable→error, bd-unavailable
graceful, `parseAcceptanceFromBDJSON`
(array/object/fallback/empty/malformed)
- [x] `cd cli && go build ./... && go test ./...` — 11950 passed, 68
packages, 0 failures
- [x] Real binary against `ag-dwq` → 5 clean scenarios; `--json` valid

Closes-scenario: ag-ljd#scenarios-extract-stdout
Bounded-context: BC1-Corpus
Evidence: cd cli && go test ./internal/scenarios/... ./cmd/ao
2026-05-28 03:42:43 -04:00
Bo 6c5a88694c feat(cli): ao skills find <intent> skill discovery (ag-a97 #find-ranks) (#563)
## Summary

Turns the 75-skill catalog from oral tradition into a queryable surface.
`ao skills find <intent>` scores every `skills/*/SKILL.md` against a
free-text
intent and returns the top matches (name, one-line description, score) —
so an
agent gets a discovery API instead of memorizing skill names.

- **Scoring engine** (`cli/internal/skills/find.go`) — pure,
deterministic
token-overlap. A query word hitting the skill **name** counts most, a
declared
  **trigger** next, a **description** word least, with light plural/stem
tolerance (`loop` ↔ `loops`). Scores normalized to `[0,1]`; ties broken
by
  name. No filesystem access, so it is fully table-testable.
- **Loader** (`cli/internal/skills/load.go`) — reads
`skills/*/SKILL.md`, parses
  frontmatter (`name`, `description`, best-effort top-level / `metadata`
triggers). No static index file: a newly added skill is found on the
next run.
- **Command** (`cli/cmd/ao/skills_find.go`) — `--json` / `--limit`
(default 5),
stdout-as-data / stderr-as-diagnostics. An unmatched intent **exits 0**
with a
stderr note, not an error. Unknown-flag typo hints inherited from the
root
  cobra config; `--limit < 1` returns a usage error naming the fix.
- Regenerated `cli/docs/COMMANDS.md`.

### Notes / corrections

- The bead's premise ("each SKILL.md has a populated `triggers:` array")
is
inaccurate — phase-1 discovery found **0/75** top-level `triggers:`
arrays;
intent lives as prose in `description`, with `metadata.triggers` in ~10
skills.
Scoring therefore treats name+description as the primary signal and
triggers as
  an optional boost.
- Bounded context: the slice reads the **skills corpus** (SKILL.md
content), so
`BC1-Corpus` (the plan's draft `BC4-Practice` does not exist — BC4 is
Evidence).
- Deferred to follow-ups: `ao skills list --by-trigger` /
`--list-triggers`
  (ag-0r0) and backfilling structured triggers across SKILL.md (ag-piv).

## Test plan

- [x] `cd cli && go test ./internal/skills/... ./cmd/ao` (green)
- [x] `cd cli && go build ./... && go vet ./internal/skills/...
./cmd/ao`
- [x] `scripts/generate-cli-reference.sh` (COMMANDS.md in conformance)
- [x] `bash skills/heal-skill/scripts/heal.sh --strict` (All clean)
- [x] `ao autodev validate --file PROGRAM.md --json` → `valid: true`
- [x] Manual smoke: `ao skills find "close the loop"`, `--json --limit
3`, unmatched intent

Closes-scenario: ag-a97#find-ranks-relevant-skills
Bounded-context: BC1-Corpus
Evidence: cli/internal/skills/find_test.go + go test
./internal/skills/... ./cmd/ao
2026-05-28 00:15:43 -04:00
Bo 3f4b67be49 feat(cli): add reconciliation report (#550)
## Summary
- add `ao reconcile` for a read-only git/GitHub release/beads/.agents
decision report
- emit both human and `--json` output with ranked findings and next
actions
- anchor external checks at the git root and disable bd Dolt auto-start
for this read-only scan
- update generated CLI docs and JSON flag consistency coverage

## Validation
- `cd cli && go test ./cmd/ao -run 'TestReconcile|TestCapabilities'`\n-
`cd cli && make build`\n- `cd cli && bin/ao reconcile --json | jq ...`
(live run detected green main Validate plus failed `v3.0.1` tag
Validate)\n- `bash tests/cli/test-json-flag-consistency.sh`\n- `bash
scripts/validate-ci-policy-parity.sh`\n-
`./scripts/generate-cli-reference.sh --check`\n- `git diff --check`\n-
`scripts/pre-push-gate.sh --fast` (passes; existing warn-only
executable-spec warnings for `scenarios --lint` and `trace
--orphans`)\n\nNote: a broad local `go test ./cmd/ao` run was stopped
after it appeared hung with no child test process; the focused command
tests and build passed, and CI should provide the full-package signal.
2026-05-26 17:03:27 -04:00
Bo 010ade393c feat(rip)!: carve the daemon — delete internal/daemon + agentopsd, GC is the substrate (soc-2rtm0 #daemon-carve wave5) (#515)
Wave 5 of 5 (the finale) of the orchestration-substrate retirement
(soc-2rtm0). AgentOps stops being the orchestration substrate; GC is the
substrate. The always-on out-of-session supervisor (the daemon) is
deleted; GC's controller replaces it. Features that ran AS daemon jobs
(wiki build, llmwiki loop) keep their BUILDER CORE (in-process /
GC-callable) but lose the daemon-job-executor wrapper.

## Deleted
- `cli/internal/daemon/` — the whole package (71 .go files + testdata +
AGENTS.md).
- `cli/cmd/ao/`: `agentopsd.go`, `daemon_jobs.go`, `daemon_soak.go`,
`daemon_fake_policy.go`, `rpi_phased_daemon.go` (+ their tests).
- Daemon-only tests: `rpi_status_daemon_test.go`,
`rpi_integration_test.go`, `doctor_product_runtime_test.go`.
- `internal/doctor/fix_daemon.go` (+test) + the daemon
runtime/ledger-health/telemetry/openclaw-consumer checks and the
`--product-runtime` flag in `doctor.go` (the `ao doctor` command itself
is KEPT).
- The `ao daemon`/`agentopsd` command registration (went with
`agentopsd.go`).
- Daemon test scripts: `scripts/validate-daemon-product-e2e.sh`,
`tests/scripts/test-daemon-product-e2e.sh`.

No daemon CI gates existed in `validate.yml` / `ci-jobs.yaml` to remove
(prior waves had already cleared them).

## Severed (KEEP features — builders stay, daemon-job-executor wrappers
removed)
- **wiki** (`internal/wiki/pipeline.go`): removed the
`daemon.JobExecutor` impl
(`JobTypes`/`RunJob`/`PipelineJobSpec`/`parsePipelineJobSpec`/`jobResultFromOutcome`)
+ the daemon import. The builder core — `WikiPipeline`, `RunStage`,
`SelectStage`, the ingest/query/lint runners — is untouched; `ao wiki
lint/query/promote` run in-process.
- **llmwiki** (`internal/llmwiki/executor.go`): removed
`LLMWikiLoopExecutor` + `LoopJobSpec` + the daemon import. KEPT the leaf
builder core in the same file (`Stage`, stage constants, `StageHandler`,
`StageResult`, `DefaultLintIntervalHours`, `SelectStage`,
`rawHasNewerFiles`, `lintIsStale`) — `stages.go` (the real handlers) and
`internal/scope` depend on these. No new package needed; the shared
types already lived in this leaf file.
- **rpi status** (`cmd/ao/rpi_status.go`): severed the
`--daemon`/`--daemon-url`/`--daemon-fallback` read path. `ao rpi status`
now reads only the local run registry (`.agents/rpi/runs/`) — a KEEP
feature. Relocated the tiny `cobraContext` helper here (it lived in the
deleted `agentopsd.go`, only consumer is this file).
- **rpi phased** (`cmd/ao/rpi_phased.go`, `rpi_phased_context.go`):
removed the `--daemon-submit` short-circuit + flags + the
`DaemonSubmit/URL/Token/Fallback` opts fields (only consumer was the
deleted submit path).

## Lost capabilities (intentional, daemon-only)
- `ao rpi status --daemon` (read RPI job state from agentopsd) and `ao
rpi phased --daemon-submit` (queue an RPI run to agentopsd).
- llmwiki `LoopJobSpec.AllowPlaceholderOutputs` opt-in gating (the
placeholder-output-disabled skip) — that gate lived in the removed
daemon-job wrapper, not the stage handlers.
- `ao doctor` daemon runtime/ledger-health/telemetry/openclaw-consumer
checks + `--product-runtime`.

## Verification (all green)
- `gofmt -l` clean (my files), `go build ./...`, `go vet ./...`, `go
test ./...` → 11920 passed, 0 failed, 65 packages.
- `scripts/release-smoke-test.sh` → 108 pass / 0 fail (EXPECTED_COMMANDS
never listed daemon; no scrub needed).
- `tests/windows/test-windows-smoke.ps1` → `Invoke-GoTest` list has no
`./internal/daemon` ref (nothing to scrub).
- `internal/quality/stale_refs.go` → no daemon/agentopsd
deprecated-command entries (`TestDoctorStaleReplacementsExist` passes).
- `tests/smoke-test.sh`, `tests/cli/test-json-flag-consistency.sh`
(15/0), `scripts/test-agentops-contract-canaries.sh` (failures=0 after
fixing the cli-command-surface heading-count fixture/eval 68/179/247 →
67/172/239 and the `ao daemon` refs in `skills/dream` +
`skills-codex/dream`).
- Regenerated `cli/docs/COMMANDS.md` + `registry.json`;
`regen-codex-hashes.sh --check` clean; `validate-ci-policy-parity.sh`
PASS; doc link/learning-ref checks pass; `heal.sh --strict` clean.

Diff stat: 113 files changed, 95 insertions, 34,978 deletions.

Closes-scenario: soc-2rtm0#cascade-rip
Bounded-context: BC5-Runtime
Evidence: .agents/discovery/2026-05-24-rip-caller-map.md
2026-05-24 17:11:20 -04:00
Bo d52144ed40 feat(cli): ao validate --gate — exit-code verdict for GC retry + CI (#ao-validate-gate) (#516)
## What

New top-level `ao validate` umbrella command with `--gate`
(exit-code-as-verdict) mode. This is the dependency the GC reference
City needs — GC's `check={max_attempts}` retry, CI steps, and `ao rpi`'s
internal validate phase can run one command and branch on the exit code.

Spec: `.agents/discovery/2026-05-24-gvkj6-finalize-runbook.md` ("`ao
validate --gate` — SPEC" section).

## Exit-code contract (`--gate`)

| Code | Meaning |
|---|---|
| `0` | PASS or WARN (gate passes) |
| `1` | FAIL (gate fails) |
| `2` | internal/setup error (distinct so a broken gate is never read as
a clean pass) |

- WARN exits `0` by default (advisory); `--strict` / `--warn-as-fail`
promotes WARN → exit `1`.
- Deterministic: **no network, no LLM**. Safe for GC
`check.max_attempts`, CI, and `ao rpi`.
- `--json` emits `{"verdict","issues","warnings","gate_exit"}` in both
modes.

## How (composed, not reinvented)

Reuses the existing `ratchet.Validator` (`ValidateWithOptions`) over the
resolved file set (`--changes`, `--bead`, or auto-located RPI outputs);
aggregates each result's `Valid`/`Issues`/`Warnings` into a single
PASS/WARN/FAIL verdict; maps verdict → exit code via a `gateExitError`
typed error caught in `Execute()` (same pattern as
`doctorExitError`/`AgentsLintError`). The existing `ao
ratchet/scenario/goals validate` sub-surfaces are untouched. New logic
is just the aggregation + mapping (well under the complexity budget).

## Residue handled (command-surface +1)

- Regenerated `cli/docs/COMMANDS.md`, `registry.json` (166 commands),
`docs/cli-skills-map.md`.
- Bumped `cli-command-surface` canary
(`cli-command-surface-matrix.json`) + smoke fixture heading counts: top
68→69, all 247→248 (sub unchanged — flat command).
- Added `validate` to release-smoke `EXPECTED_COMMANDS` + a `test_help
"ao validate --help"` line.
- Registered `validate` in `cobra_commands_test.go` `expectedCmds` (both
lists).

## Tests

L1 (`aggregateVerdict`, `gateExitForVerdict`, `gateExitError`) + L2
(full `runValidate` over real temp-dir artifacts) asserting exact exit
codes: PASS=0, WARN=0, WARN+`--strict`=1, FAIL=1, internal=2, and the
`--json` contract.

## Verified green

`gofmt` clean · `go build`/`go vet`/`go test ./cmd/ao
./internal/ratchet` · manual exit-code check (0/1/2) ·
`release-smoke-test.sh` (0 failed) · `test-json-flag-consistency.sh` (0
errors) · `validate-cli-skills-map.sh` (PASS) · `cli-command-surface`
canary (failures=0) · `regen-codex-hashes.sh --check` clean ·
`validate-ci-policy-parity.sh` PASS.

Closes-scenario: soc-ba1un#ao-validate-gate
Bounded-context: BC4-Evidence
Evidence: .agents/discovery/2026-05-24-gvkj6-finalize-runbook.md
2026-05-24 15:48:04 -04:00
Bo 1ba0ca8120 feat(rip)!: kill schedule/plans/watch/overnight commands+engines — GC owns scheduling (soc-2rtm0 #kill-schedule-overnight wave3-4) (#514)
Wave 3+4 of the soc-2rtm0 orchestration-substrate rip. Operator
decision: **kill the schedule/plans/watch/overnight commands AND their
engines entirely** — GC owns all scheduling/overnight/watch; AgentOps
keeps only rpi/evolve/crank/swarm as loops.

## Killed (commands + engines)
- `cli/cmd/ao/`: `overnight*.go` (overnight, council, curator, packets,
setup, longhaul, daemon, tier1), `dream_subcycle.go`, `schedule.go`,
`watch.go`, `plans*.go` (+ all tests).
- `cli/internal/schedule/` — clean delete (only `schedule.go` +
`agentopsd.go` imported it).
- `cli/internal/overnight/` — the overnight engine, deleted whole.
- Daemon refs severed (daemon stays, wave 5): removed `agentopsd.go`
dream-executor + crash-recovery + `--schedule-file` loading; removed the
`dream` soak scenario.
- Removed `ao init --with-schedule` (seeded config for the now-dead
daemon scheduler).
- Retired the `dream` skill (claude + codex) to a GC-pointer stub.

## KEEP-feature severance (HARD CONSTRAINT)
- **harvest** (KEEP): lock helpers
(`LockIsStale`/`ReadLockPID`/`ProcessAlive`/`WriteLockPID`)
**extracted** from `overnight/recovery.go` into new
`cli/internal/lockfile` package; `harvest.go` repointed. Generic
process-lock machinery, not overnight-specific.
- **forge** (KEEP): `writeJSONAtomic` + `buildCuratorID` + curator-queue
types **extracted** into `cli/cmd/ao/forge_curator_id.go`; forge
curator-queue enqueue path preserved.
- **evolve** (KEEP): the `--dream-first`/`--dream-only` Phase-1 dream
sub-cycle was **severed** (capability genuinely overnight/dream-only).
**LOST CAPABILITY** — flagged below.
- `forge/runmine.go` + `wiki/artifact.go` only referenced overnight in
comments (no real import) — untouched.

## CI gates / docs / fixtures reconciled
- Removed `dream-end-user-coverage` GOALS gate +
`scripts/check-schedule-example.sh` + `schedule.yaml.example` +
`goals-affects-files.yaml` entry; updated GOALS Directive #8.
- Fixed `cli-command-surface` canary: heading counts 72/194/266 →
68/179/247, dropped deleted `plans.go` artifact check.
- Deleted `dream-software-factory-operator` eval; dropped deleted
`plans.go` check from `session-continuity-context` eval.
- Regenerated `COMMANDS.md`, `cli-skills-map.md`, `registry.json`;
regenerated codex hashes.
- No dedicated validate.yml/ci-jobs.yaml job existed for these surfaces
(verified) — `validate-ci-policy-parity` stays PASS.

## Lost capability (flagged)
- `ao evolve --dream-first` / `--dream-only` (run the
knowledge-compounding dream loop before/instead of code cycles) is gone.
Knowledge compounding now runs out-of-session via GC. The nightly-CI
dream-cycle proof job (built on KEEP harvest/forge/inject/defrag
primitives) is unaffected.

## Verification (all green)
`cd cli && gofmt -l` clean · `go build ./...` · `go vet ./...` · `go
test ./...` (zero FAIL) · `tests/smoke-test.sh` ·
`tests/cli/test-json-flag-consistency.sh` (0 errors) ·
`scripts/test-agentops-contract-canaries.sh` (**failures=0**) ·
doc-release gate · `heal --strict` clean · skill-lint · codex parity ·
ci-policy-parity.

## Follow-up (out of scope, build-green-safe to defer)
Narrative docs (`docs/scheduling.md`, `docs/ARCHITECTURE.md`,
comparisons, etc.) still mention the killed `ao overnight`/`ao schedule`
commands in prose. No CI gate validates ao-subcommand existence in
narrative docs, so these do not block; a doc-sweep is a separate
follow-up.

Closes-scenario: soc-2rtm0#cascade-rip
Bounded-context: BC5-Runtime
Evidence: .agents/discovery/2026-05-24-rip-caller-map.md
2026-05-24 14:56:20 -04:00
Bo cd6a7031ad feat(rip): sever + delete gc-bridge glue, phased engine keeps non-gc backends (soc-2rtm0 #gcglue-rip wave2) (#513)
Wave 2 of 5 of the orchestration-substrate retirement (soc-2rtm0).
Severs the Gas City (gc) bridge glue from the phased RPI engine and
deletes it. The phased engine keeps its non-gc backends
(auto/direct/stream/tmux); the `gc` runtime mode is removed. Wave 1
(factory) merged as #512.

## What changed (Edge B sever, per recon map)

**Typedef relocation:** Moved the injectable `gcExecFn`/`gcLookFn`
func-typedefs out of the deleted `gc_bridge.go` into the KEEP file
`rpi_phased_context.go`, renamed to `execFn`/`lookFn` (they are no
longer gc-specific — used by `rpi_phased_setup.go`, `tracker_health.go`,
`rpi_phased_stream.go`, `rpi_phased_context.go` for runtime/binary
preflight). All callsites updated.

**Phased selector patched (drop `gc` backend):**
- `rpi_phased_stream.go` — removed the `case "gc":` executor + the
auto-gc-preferred branch (auto now goes straight to stream) + the
`gcExecutor` backendMode type-assertion.
- `rpi_phased.go` — removed the `gc` preflight branch, the auto-gc
branch, `preflightGCRuntimeAvailability`, and `gc` from the `--runtime`
flag help + completion list.
- `rpi_phased_context.go` — `validateRuntimeMode` drops `gc`; removed
the `GasCityClient`/`GCCityPath`/`GCCityName` opts fields (their type
died with `rpi_phased_gc.go`).
- `doctor.go` — removed the GasCity Bridge + GasCity Product Runtime
health checks (`checkGasCityBridge*`, `checkGasCityProductRuntime*`,
`formatGasCityDiagnostic`); rest of doctor (daemon/ledger/openclaw
checks) untouched.
- `rpi_phased_domain_enforce.go` — emptied `opaqueRuntimeModes` (gc was
the only opaque runtime); kept the `unavailable` enforcement schema
path.

**Files deleted:** `gc_bridge.go`, `gc_events.go`, `rpi_phased_gc.go` +
their `_test.go` sidecars + `gc_test_helpers_test.go`.

**Spillover fixes (both viral helpers defined in deleted files):**
- `codex_runtime.go` re-pointed to `bridge.CompareSemver` (was using
`compareSemver`, a thin wrapper that lived in `gc_bridge.go`).
- `writeSSEFrame` test helper relocated into `rpi_integration_test.go`
(the remaining daemon-gascity L3 smoke test that uses it).

**Docs/generated:** scrubbed the CLAUDE.md "Gas City (gc) bridge" line
and `cli/internal/gascity/AGENTS.md` cross-ref; regenerated
`cli/docs/COMMANDS.md` + `registry.json`.

## Scope boundary
The daemon's own GasCity client (`internal/gascity`, `internal/daemon`
`GasCityClientAdapter`, `agentworker`) is a separate surface — left
untouched (that's wave 5). No CI gate referenced the deleted glue
(confirmed).

## Verification (all green)
- `gofmt -l` clean (my files) · `go build ./...` · `go vet ./...` · `go
test ./...` (11834 passed, 68 pkgs)
- `tests/smoke-test.sh` · `tests/cli/test-json-flag-consistency.sh` (15
pass, 0 err)
- `scripts/test-agentops-contract-canaries.sh` (failures=0)
- `scripts/validate-ci-policy-parity.sh` (63 rows) ·
`scripts/generate-registry.sh --check` (up to date)
- `tests/docs/validate-doc-release.sh` (CLI headings 72) ·
`check-docs-learning-references.sh` · `cli-docs-parity` stable ·
`check-test-staleness.sh` (0 stale)

Diff stat: 23 files, +72 / -4099 (delete-heavy rip; insertions
concentrated in deliberately-edited files).

Closes-scenario: soc-2rtm0#cascade-rip
Bounded-context: BC5-Runtime
Evidence: .agents/discovery/2026-05-24-rip-caller-map.md
2026-05-24 12:57:37 -04:00
Bo ee748d580d feat(rip)!: retire factory command + contract corpus (soc-2rtm0 #cascade-rip) (#512)
**Wave 1 of the orchestration-substrate cascade rip** (soc-2rtm0). The
full cascade (daemon + dream/overnight + schedule/plans/watch + gc-glue
+ factory) is ~50K LOC across deeply-entangled packages — far past one
reviewable PR. Per the bead's slice directive and the recon map's
serial-by-wave verdict, this ships as sequential PRs by wave. **This is
the factory wave** (the only cleanly-disjoint slice — no KEEP-feature
entanglement).

## What this retires (operator-confirmed FULL factory retire)

**Go command surface** (no external non-test caller — clean delete):
- `cmd/ao/factory.go`, `factory_admit.go`, `factory_pilot.go`,
`factory_admission_adapter.go` (+ all tests)
- `internal/ports/factory_admission.go` +
`inmemory_factory_admission.go` (+ test) — the port was only wired to
the daemon by the deleted production adapter
- severed factory flag save/restore + command-tree assertions from
`cobra_commands_test.go` / `testutil_test.go`

**Contract corpus** (scripts, schemas, contract docs, runbooks,
fixtures, tests):
- `scripts/check-factory-{admission,claim-ledger,yield-ledger}.sh`
- `schemas/factory-{admission,work-order,yield}.v1.schema.json`
- `docs/contracts/factory-{admission,claim-ledger,yield-ledger}.*`
-
`docs/runbooks/{factory-manual-merge,daemon-factory-admission,cloud-frontier-pilot}.md`
- `tests/e2e/factory-operator-canary.sh`,
`tests/integration/test-factory-claim-ledger-ci-job.sh`,
`tests/scripts/test-factory-admission-contracts.py`,
`tests/fixtures/factory-*`
- orphaned `aggregate-observation-log` subsystem (script + integration
test + fixtures) — its only data source was the deleted
`factory-claim-ledger-strict` CI job

**CI / generated reconciliation:**
- removed 3 factory jobs from `validate.yml` (+ both `needs:` arrays +
verdict echoes); removed entries from `ci-jobs.yaml`; regenerated
`AGENTS-CI.md` (63 jobs)
- removed factory gate-ids from `GOALS.md`; trimmed
`contracts-structural-floor` factory references
- regenerated `registry.json` (177→174 CLI commands), `COMMANDS.md` (−70
lines), `cli-skills-map.md` (73→72 headings)
- updated `cli-command-surface-matrix` canary spec + fixture counts
(`top=72 sub=194 all=266`); scrubbed deleted-file links from
`documentation-index.md`, `SCHEMAS.md`, `contracts/index.md`,
`agentopsd-control-plane.md`, `insert-schema-practices.py`

## Deliberately left for the daemon-carve wave
Daemon-owned factory job types (`factory.admission`,
`factory.local-pilot`) and the `agentopsd` routing-policy schema remain
— they belong to `internal/daemon/` and retire when the daemon does.

## Gates verified green locally
`gofmt`/`go build`/`go vet`, full Go test (**11830 pass**),
`validate-ci-policy-parity`, `contract-compatibility`,
`contracts-structural-floor`, `registry --check`, `doc-release` (0
broken links), `smoke-test`, `json-flag-consistency` (0 errors),
**`contract-canaries` (failures=0)**, codex-hashes, `pre-push-gate.bats`
(55).

Diff: 66 files changed, ~16 insertions / ~5680 deletions.

Closes-scenario: soc-2rtm0#cascade-rip
Bounded-context: BC5-Runtime
Evidence: .agents/discovery/2026-05-24-rip-caller-map.md
2026-05-24 12:29:22 -04:00
Bo e431339c40 feat(hookless)!: remove all hooks — skills + CLI only (soc-57b7f #remove-all-hooks) (#511)
## What

AgentOps 3.0 goes **fully hookless**. This removes the entire hook
product surface — the product is now skills + the `ao` CLI with **zero
hooks**. CI is the authoritative gate.

## Deleted

- `hooks/` — 46 hook scripts + `hooks.json` + examples (whole directory)
- `cli/embedded/hooks/` + embedded `hooks.json` +
`embedded/lib/hook-helpers.sh`
- `lib/hook-helpers.sh`
- `ao hooks` CLI command: `cli/cmd/ao/hooks.go`, `hooks_run.go` + 5 hook
test files; `cli/internal/bridge/hooks.go`;
`cli/internal/doctor/fix_hooks.go` + test
- Codex hook manifest (`hooks/codex-hooks.json`) + handler/audit scripts
(`scripts/audit-codex-hooks.sh`, `install-dev-hooks.sh`,
`test-hooks-output.sh`, `test-codex-hookless-lifecycle.sh`,
`test-hookless-rpi-phased.sh`)
- Hook lease surface: `docs/contracts/hook-lease-*`,
`schemas/hook-lease.v1.schema.json`,
`scripts/check-hook-lease-inventory.sh`,
`check-hook-port-replacements.sh`, `generate-hook-lease-inventory.py`
- Hook docs: `docs/HOOKS.md`, `cli/docs/HOOKS.md`,
`docs/architecture/hook-noise-audit.md`,
`docs/contracts/hook-runtime-contract.md`
- `tests/hooks/` (whole dir) + hook test scripts; 5 hook eval suites +
fixtures

## CI gates removed

`hook-preflight`, `validate-hooks-doc-parity`, `hook-output-schema-lint`
(+ scripts/bats), the hook eval suites, the `test-runtime-codex-smoke` /
`test-codex-native-install` / `test-codex-plugin-install` hook-handler
assertions, the cli-integration hook-lifecycle steps, the bats
`tests/hooks/*` glob + orphan-hooks audit, and the `--with-hooks`
install path. `embedded-sync` now validates only lib/skills. Both
summary `needs:` lists + the summary echo +
`docs/contracts/ci-jobs.yaml` updated; `validate-ci-policy-parity`
passes (66 rows). `.codex-plugin/plugin.json` drops the "Hooks"
capability.

## Load-bearing PRESERVED (reported, not forced)

- **`cli/cmd/ao/codex_runtime.go`** — the codex runtime *lifecycle
profile* (`HookCapable`/`HookConfigured` detection) is a
runtime-capability abstraction used by 6 `validate-codex-*` CI gates,
distinct from the hook product. Deleting it is a separate refactor.
- **`hooks-authoring` skill** + `schemas/hooks-manifest.v1.schema.json`
— reframed as an opt-in "author your own hooks" guide. Deleting the
skill cascades into catalog/domain-map/dispositions/context-map golden
files (skill-count sync), out of scope for the hook-product removal.
- **`scripts/pre-push-gate.sh`** — retired local gate (not in CI);
self-skips deleted hook scripts via `[[ -x ]]` guards; its 55 bats tests
still pass.
- Domain-enforcement audit keeps the `enforced`-mode schema for
backward-compatible JSON readers, but the hookless resolver only returns
`audited`/`unavailable`.

## Verification

`go build` / `go vet` clean; `go test ./...` = **11849 passed, 0
failed** (68 pkgs). gofmt clean on all touched files. Green: registry
`--check` (0 hooks), `regen-codex-hashes --check`,
`validate-ci-policy-parity`, `check-wiring-closure`,
`validate-embedded-sync`, `validate-manifests`, `validate-agents-split`,
`doc-release` (0 broken links), `generate-cli-reference --check`,
`heal.sh --strict`, codex/claude/plugin smoke tests, baseline-audit (0
stale). Diff: 231 files, +354 / −39803.

Closes-scenario: soc-57b7f#remove-all-hooks
Bounded-context: BC5-Runtime
Evidence: .github/workflows/validate.yml
2026-05-24 11:19:37 -04:00
Bo 0759a740e6 feat(evolve): Phase 2 of /evolve --mode=loop — ladder + cron self-adjust + typed blocked events (soc-g2qd #phase-2) (#397)
## Why

Phase 2 of soc-g2qd: ship the CLI enforcement primitives the skill's
prompt-text alone can't guarantee.

| Bead | What it gives the operator-loop |
|---|---|
| soc-mlbm | `ao evolve next-work` — 5-step programmatic ladder; agent
stops guessing what to claim next |
| soc-un0m | `ao cron self-adjust` — renders cron template + emits JSON
spec; replaces manual CronList/Delete/Create per cycle |
| soc-g34d | `ao evolve blocked` — typed blocked events at
`.agents/evolve/blocked.jsonl`; agent logs rather than halts |

## What changed

| Surface | Change |
|---|---|
| `cli/cmd/ao/evolve_next_work.go` + `_test.go` | New subcommand + L2
integration tests |
| `cli/internal/evolve/ladder/` | 5-step ladder package (shape_filter,
grep_siblings, primitive_test, cross_hop_pickup, bug_fallback) with
table-driven unit tests |
| `cli/cmd/ao/cron.go` | New top-level `ao cron` command |
| `cli/cmd/ao/cron_self_adjust.go` + `_test.go` | New subcommand; calls
`evolve.VerifyMarkers` + `evolve.Render` from #394; writes audit row to
`.agents/evolve/cron-history.jsonl`; emits JSON spec to stdout (harness
orchestrates CronCreate) |
| `cli/cmd/ao/evolve_blocked.go` + `_test.go` | New subcommand:
`--reason` (write), `--list [--tail N] [--json]` (read), `--clear
<cycle>` (operator) |
| Generated: `cli/docs/COMMANDS.md`, `registry.json`,
`docs/cli-skills-map.md` | Regen for 3 new subcommands |
| `evals/agentops-core/cli-command-surface-matrix.json` + smoke fixture
| Counts bumped 73/199/272 → 74/202/276 |

## How tested

- L2 integration: each new subcommand has L2 tests using fixture
workspaces
- L1 unit: 5-step ladder per-step table tests + JSONL schema validation
on blocked records
- Mechanical: `go test ./cli/...` 0 → 0 failures;
cli-command-surface-smoke.sh `cli-help-matrix-ok`;
check-no-tracked-agents.sh exits 0

## Counts

CLI heading counts: top 73 → 74, sub 199 → 202, all 272 → 276.

Sibling pattern: `cron-history.jsonl` + `blocked.jsonl` follow the
cycle-history.jsonl JSONL append-only shape from soc-5qit. Ladder
structure mirrors the in-prompt cascade in `references/scout-mode.md` —
making it programmatic per §A5.

[no-sibling for cron-self-adjust] First-of-kind: no prior subcommand
emits a cron-spec JSON for harness orchestration. The CLI does the safe
work (template render + marker verify + audit row); the harness owns
CronCreate.

See: `docs/plans/2026-05-21-evolve-loop-epic-design.md` §A4, §A5, §A6

Closes-scenario: soc-mlbm#next-work-ladder
Closes-scenario: soc-un0m#cron-self-adjust
Closes-scenario: soc-g34d#typed-blocked-events
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/evolve_next_work.go
2026-05-21 14:09:41 -04:00
Bo 2ea3c12b7a feat(evolve): --mode=burst|loop flag + mechanical write-stop-marker refusal (soc-hwax #mode-loop-flag) (#395)
## Why
/evolve self-stops in operator-driven cron sessions because its defaults
assume agent-as-operator. `--mode=loop` flips the contract: operator
owns stop signals; CLI mechanically refuses self-written STOPs.

## What changed
| Surface | Change |
|---|---|
| `cli/cmd/ao/evolve.go` | New `--mode string` flag (burst\|loop,
default burst) + `validateEvolveMode` helper called from `runEvolve` |
| `cli/cmd/ao/evolve_write_stop_marker.go` | New subcommand; refuses
with exit 1 under `--mode=loop`, writes `.agents/evolve/<MARKER>` under
burst |
| `cli/cmd/ao/evolve_test.go` | L1: `--mode=invalid` rejected; mode flag
registered with burst default |
| `cli/cmd/ao/evolve_write_stop_marker_test.go` | L2 table tests: burst
writes (DORMANT body assertion), loop refuses {dormant,stop,kill}, no
file written under refusal |
| `skills/evolve/SKILL.md` | Mode-aware HTML comments in Steps 1/3/7 +
Flag table row (only edited Steps 1/3/7 + Flag table; verified via grep
`^### Step`) |
| `skills/evolve/references/loop-mode.md` | New reference doc with
invariants + CLI primitives |
| `docs/plans/2026-05-21-evolve-loop-epic-design.md` | Epic design memo
(consumed by all 6 sub-beads of soc-g2qd) |
| `cli/docs/COMMANDS.md` | Regenerated via
`scripts/generate-cli-reference.sh` |
|
`skills-codex/{.agentops-manifest.json,evolve/.agentops-generated.json}`
| Post-edit hook source-hash refresh; `check-codex-parity-drift.sh` PASS
|

## How tested
- `go test -count=1 ./cmd/ao/` — 10175 pass, 1 skip (full suite green
after CLI-docs regen; pre-regen `TestCobraConformance` correctly flagged
the doc drift)
- `go build ./... && go vet ./...` — green
- `grep '^### Step' skills/evolve/SKILL.md` confirms only Steps 1/3/7
edited (12 Step markers preserved at same positions)
- `bash scripts/check-codex-parity-drift.sh` — PASS
- Table tests assert exact marker filename (DORMANT/STOP/KILL) + body
content for burst; for loop they assert error substring AND that no
marker file was created.

## §A7 fallback decision
**Chose in-file HTML comments** (not two-file split). Each Step comment
is one line; no conditional sections needed. Two-file split would have
ballooned the surface for a single-line contract reminder. CLI
mechanical enforcement (`write-stop-marker` refusal) is the real
invariant; SKILL.md comments only document intent for human readers.

See: docs/plans/2026-05-21-evolve-loop-epic-design.md §A1 + §A7

## TODOs the worker leaves for the epic
- Wire `cli/internal/evolve.preferences.Load()` into
`detectEvolveWriteStopMarkerMode` once soc-6svt lands (currently
flag-only).
- `ao evolve operator-stop` (Wave 2) — separate code path explicit
operator override.
- `ao evolve blocked` (Wave 2) — typed blocked event instead of STOP.

Closes-scenario: soc-hwax#mode-loop-flag
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/evolve_write_stop_marker.go
2026-05-21 12:21:51 -04:00
Bo 2d2b806ab8 feat(evolve): per-repo preferences.yaml schema + loader + ao evolve config --show (soc-6svt #preferences-yaml) (#396)
## Why
Different repos have different /evolve physics. Hardcoded defaults force
ADRs per-repo to override. Per-repo YAML separates skill-doctrine from
repo-policy.

## What changed
| Surface | Change |
|---|---|
| `schemas/evolve-preferences.v1.schema.json` | New JSON Schema draft-07
for the v1 preferences shape |
| `cli/internal/evolve/preferences.go` | New loader: `Load(ctx) (*Prefs,
error)` with file:line error context |
| `cli/cmd/ao/evolve_config.go` | New `ao evolve config --show [--json]`
subcommand |
| `.agents/evolve/preferences.yaml.template` | New starter template |
| `.gitignore` | Negation rules for the template path |

## Resolution order
defaults < preferences.yaml < CLI flag (caller-applied)

Invalid keys/types -> exit 1 with `preferences.yaml:line:col` context.
Never silent fallback.

## How tested
- L2: malformed YAML -> `ao evolve config --show` exits 1, stderr has
file:line
- L1: `preferences.Load()` table tests cover defaults, valid, invalid
types, out-of-range, unknown keys
- `go test ./cli/internal/evolve/ ./cli/cmd/ao/` green (299+ tests
across both)

See: docs/plans/2026-05-21-evolve-loop-epic-design.md §A2 (created by
parallel worker W1a in soc-hwax PR)

Closes-scenario: soc-6svt#preferences-yaml
Bounded-context: BC5-Runtime
Evidence: cli/internal/evolve/preferences.go
2026-05-21 11:56:04 -04:00
Bo 1a7e2cb748 feat(ao session bootstrap): universal init prompt (soc-vuu6.25 #ao-session-bootstrap) (#390)
## Summary

`ao session bootstrap` — every agent spawned into an AgentOps repo runs
this first. Output shape is identical regardless of model (the
fungibility contract: swap Claude for Codex on any bead without
re-orienting).

## What

Four fail-open substeps emit fields in
`schemas/session-bootstrap.v1.schema.json`:

| Field | Source | Fallback |
|---|---|---|
| `agents_md_read` + `agents_siblings_read` | `AGENTS.md` +
post-soc-vuu6.3 siblings | `false` / empty array |
| `onboard_phase` | `onboard --auto` (soc-vuu6.9 future) |
`skipped:not-implemented` |
| `ready_beads_count` | `bd ready --json` | `null` |
| `mail_unread_count` | mcp-agent-mail | `null` (wiring is follow-up
bead) |

## Why

The fungibility philosophy mandates one init prompt for every agent
regardless of type. Today every operator hand-rolls orientation; two
agents spawned by two different operators don't share a starting frame.
`ao session bootstrap` is the operational primitive that closes the gap.
Tiny (~270 LOC including helpers), fail-open, deterministic.

## Files

- New: `cli/cmd/ao/session_bootstrap.go` + `_test.go` (8 cases)
- New: `schemas/session-bootstrap.v1.schema.json` (draft-07)
- New: `skills/session-bootstrap/SKILL.md` +
`skills-codex/session-bootstrap/SKILL.md` (mirrored)
- Catalog: `skills/SKILL-TIERS.md`, `skills/using-agentops/SKILL.md`,
`skills-codex/using-agentops/SKILL.md`
- Regenerated: `cli/docs/COMMANDS.md` (sub 196→197, all 269→270),
`registry.json` (cli_commands 173→174),
`PRODUCT.md`/`docs/ARCHITECTURE.md`/`docs/SKILLS.md` (count sync 79→80),
`evals/agentops-core/cli-command-surface-{matrix.json,fixtures/cli-command-surface-smoke.sh}`
(fixture bump)

## Verification

```
cd cli && go build ./... && go test ./cmd/ao/ -run TestSessionBootstrap -count=1
# 8 passed
bash evals/agentops-core/fixtures/cli-command-surface-smoke.sh
# cli-command-headings: top=73 sub=197 all=270 / cli-help-matrix-ok
bash tests/docs/validate-doc-release.sh
# PASS: doc-release gate succeeded
```

Closes-scenario: soc-vuu6.25#ao-session-bootstrap
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/session_bootstrap.go
Evidence: cli/cmd/ao/session_bootstrap_test.go
Evidence: schemas/session-bootstrap.v1.schema.json
Evidence: skills/session-bootstrap/SKILL.md
2026-05-21 04:27:09 +00:00
Bo f7cc505a7a fix(agentopsd): harden AI smell surfaces (soc-0l5z #ai-smell-hardening) (#389)
## Summary
- Replace the plans manifest shape-only stub with projection-backed
daemon state.
- Gate llmwiki placeholder outputs behind explicit opt-in and make fake
daemon execution test/demo-only.
- Refresh stale Olympus-ported docs and tune README/PRODUCT claims to
default-safe runtime behavior.
- Repair strict docs-site and hook-lease inventory drift surfaced by the
merge gate.

## Validation
- `scripts/pre-push-gate.sh --fast`
- `go test ./internal/daemon ./internal/llmwiki ./cmd/ao`
- `bash tests/docs/validate-doc-release.sh`
- `scripts/docs-build.sh --check`
- `bash scripts/validate-sovereignty-proof-citations.sh`
- `bash scripts/check-hook-lease-inventory.sh`

Closes-scenario: soc-0l5z#ai-smell-hardening
Bounded-context: BC5-Runtime
Evidence: validate-sovereignty-proof-citations: scanned 3 pages, 16
citations
2026-05-20 21:31:38 -04:00
Bo 3655c55356 feat(scripts): auto-resume-stale-claims driver (soc-vuu6.27 slice 4a #auto-resume-driver) (#385)
## Why

Slice 4a of soc-vuu6.27 (fungible-swarm death recovery). The bead
originally specified an "agentopsd job spec" but the operational essence
is a periodic driver that calls slice 2 + slice 3 — that intent is met
by a shell wrapper today, faster and with smaller surface than touching
agentopsd internals. Slice 4b (`` filed) tracks the agentopsd-native
variant for later.

**Stacked on slice 3's branch** (which is stacked on slice 2). When the
chain merges, this rebases onto main.

## What

`scripts/auto-resume-stale-claims.sh` — one-shot driver suitable for
cron, systemd timers, or future agentopsd job wrapping:

1. Calls `ao beads stale-claims --threshold=<N>h --json` (slice 2).
2. For each stale bead, calls `ao beads resume <id> --agent
<recovery-agent>` (slice 3).
3. Per-bead OK/FAIL output + summary line. Exit code summarizes the run.

Flags: `--threshold` `--agent` `--dry-run` `--max` (cap per-run
transfers; default 25) `--quiet`. Exit 0 = success or no candidates;
exit 1 = at least one failure; exit 2 = usage/env error.

## Test

9 bats tests, all passing. Stubs `ao` via PATH so the harness is
deterministic. Covers: zero candidates, multi-candidate happy path,
--dry-run, partial failure, --max cap, --threshold passthrough, --quiet
suppression, unknown-flag error, missing-ao error.

Self-dogfood: With slices 2 and 3 landed plus a stale-claim simulator,
running this driver against the real bd backend transfers stale claims
and emits one claim_transferred event per transfer (slice 3 contract).

## Follow-up

- Slice 4b (``) — agentopsd-native job spec wrapping this driver.

Closes-scenario: soc-vuu6.27#auto-resume-driver
Bounded-context: BC2-Loop
Evidence: scripts/auto-resume-stale-claims.sh
Evidence: tests/scripts/auto-resume-stale-claims.bats

---------

Co-authored-by: Codex <codex@example.invalid>
2026-05-21 00:37:27 +00:00
Bo 57b2eabd7e feat(ao beads): stale-claims list command (soc-vuu6.27 slice 2 #stale-claims-list) (#383)
## Why

Slice 2 of soc-vuu6.27 (fungible-swarm death recovery). The fungibility
doctrine ("Dead agent? Start another.") needs an operational primitive.
This is the read-only listing half — surfacing stale claims so an
operator (or future slice-4 daemon job) can decide what to do. Slice 1
(PR #382) shipped the contract; this slice consumes it.

## What

`ao beads stale-claims [--threshold=4h] [--json]` — walks `bd list
--status in_progress --json`, applies a staleness heuristic (last
activity > threshold), emits a sorted listing (oldest first) or a JSON
array of records conforming to
`schemas/stale-claim-event.v1.schema.json` (event_type
`stale_detected`).

Files:
- `cli/cmd/ao/beads_stale.go` — cobra subcommand + pure
`computeStaleEvents` helper + exec seam for tests
- `cli/cmd/ao/beads_stale_test.go` — 5 test functions, 7 table sub-cases
+ L2 integration via the seam
- `cli/cmd/ao/beads.go` — one-line registration
(`beadsCmd.AddCommand(beadsStaleCmd)`)

Read-only: this command DOES NOT transfer claims. Slice 3 handles that.

Slice scope honors complexity budget — the orchestration function is two
short helpers (RunE wrapper + pure `computeStaleEvents`), each well
under the warn-at-15 threshold.

## Test

`cd cli && go test ./cmd/ao -run
'TestComputeStaleEvents|TestRunBeadsStale'`:
- TestComputeStaleEvents_TableDriven (7 sub-cases)
- TestComputeStaleEvents_AssigneeDefaultUnknown
- TestRunBeadsStale_JSON_OutputShape (L2 integration via seam)
- TestRunBeadsStale_HumanOutput_HasZeroMessage
- TestRunBeadsStale_BdMalformed
- TestComputeStaleEvents_ClaimAgeRounding

All passing. `gofmt -l` clean, `go vet ./...` clean, `go build ./...`
clean.

Self-dogfood (in this worktree, smoke):
```
$ ao beads stale-claims --threshold=4
ao beads stale-claims: 47 in_progress bead(s) stale (threshold 4.0h)
  soc-t3z1  claim_age=21.5h last_touch=2026-05-19T22:30:00Z ...
```

## Follow-ups already filed

- Slice 3 — `ao beads resume <id>` atomic transfer (blocked on this PR)
- Slice 4 — agentopsd auto-resume daemon job (blocked on slice 3)

Closes-scenario: soc-vuu6.27#stale-claims-list
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/beads_stale.go
Evidence: cli/cmd/ao/beads_stale_test.go

---------

Co-authored-by: Codex <codex@example.invalid>
2026-05-21 00:22:31 +00:00
Bo c8edc80143 feat(wiki): wiki bounded context waves 1-4 (epic soc-behj) (#291)
## Summary

Phased strangler consolidating `ao`'s `.agents/`-touching logic into one
`cli/internal/wiki` bounded context. Waves 1-4 of epic **soc-behj** (8
beads, 5 waves). Strictly accretive — no `ao` command removed, legacy
command surface untouched.

Each wave boundary was gated on the 102 `cmd/ao` integration tests
staying green.

| Wave | Bead | Deliverable |
|------|------|-------------|
| W1 | soc-1lju | `FrontmatterCodec` — all 5 frontmatter parsers
delegate to one codec |
| W2 | soc-36lw | `CorpusLocator` — `agentsDirIn` moved, 17 call sites
migrated |
| W2 | soc-f4tr | `Artifact` + `Claim` domain types with subtype
invariants |
| W3 | soc-vot0 | persistent `WikiIndex` — JSONL, incremental by
content-hash |
| W3 | soc-r08p | `FreshnessPolicy` — claim-level, evidence-driven |
| W4 | soc-ijp8 | `WikiPipeline` — subsumes the llmwiki loop, 3 stages
wired |
| W4 | soc-q50a | port conformance suite |

Also anchors `.gitignore`'s bare `wiki/` pattern with a
`!cli/internal/wiki/` negation — the new package was being silently
ignored.

Wave 5 (`ao wiki` command group) is **not** in this PR.

## Test plan

- [x] `go build ./...` clean
- [x] `go vet ./internal/wiki/` clean
- [x] `go test ./internal/wiki/` — 96 pass
- [x] `go test ./cmd/ao/ -run Integration` — 102 pass (regression net
green)
- [ ] CI `validate.yml`

---------

Co-authored-by: Codex <codex@example.invalid>
2026-05-18 13:29:12 +00:00
Bo 1f0e545a51 Executable spec layer (epic soc-58nt, F2–F5): GOALS as BDD acceptance criteria (#292)
* feat(goals): F2.0 scenario-result artifact contract + producer/writer

bead: soc-58nt.2.6

* docs(goals): F4.0 trace link convention ADR-0005

bead: soc-58nt.4.8

* docs(goals): F3.0 domain-slice manifest contract + ADR-0004

bead: soc-58nt.3.8

- schemas/domain-slice-manifest.v1.schema.json: JSON Schema (draft/2020-12,
  additionalProperties:false) for domain-slice manifests. All required fields:
  domain, version, bounded_context, directive_ids (d-<slug> pattern), scenario_ids,
  context_roots, allowed_read_globs, denied_read_globs, validation_commands, owner.
- docs/adr/ADR-0004-domain-slice-manifest-contract.md: records four decisions:
  (A) command shape is ao rpi phased --domain <name>; (B) manifest is durable
  tracked artifact at docs/domains/<name>/manifest.yaml; (C) reconciles with the
  domain skill (vocabulary), context-map.md (architecture view), and skill
  frontmatter — no overlap; (D) Go model named domainSliceManifest, explicitly
  distinct from rpi_phased_manifest.go's phaseManifest. Cross-references ADR-0003.
- docs/domains/README.md: explains the directory, field table, relationship to
  the three pre-existing domain surfaces, and how to add a new slice.
- docs/domains/example/manifest.yaml: fully populated example; validated against
  the schema via jsonschema (PASSED).

* feat(goals): F2.1 scenario-result aggregation reader

bead: soc-58nt.2.1

* feat(rpi): F3.1 domainSliceManifest model + loader

Implements the domainSliceManifest Go model and loader in
cli/internal/domainslice/ per the F3.0 contract (ADR-0004, Decision D).
Uses gopkg.in/yaml.v3 with KnownFields(true) to reject unknown fields,
mirrors the schema's additionalProperties:false. Validates all required
fields, directive_id pattern (^d-[a-z0-9][a-z0-9-]*$), context_roots
minItems:1, and validation_commands sub-fields with field-named errors.
Explicitly distinct from phaseManifest (rpi_phased_manifest.go).
28 tests pass (L1 unit + L2 fixture round-trip via docs/domains/example/manifest.yaml).

bead: soc-58nt.3.1

* feat(goals): F4.1 read-only executable-spec trace graph walker

bead: soc-58nt.4.1

* feat(goals): F2.2 per-directive scenario_satisfaction + threshold verdict

bead: soc-58nt.2.2

* feat(rpi): F3.2 ao rpi phased --domain scoping

bead: soc-58nt.3.2

* feat(goals): F4.2/F4.3 ao goals trace --from / --orphans

beads: soc-58nt.4.2 soc-58nt.4.3

* feat(goals): F4.4 ao goals render — GOALS.md to Gherkin

bead: soc-58nt.4.4

* feat(rpi): F3.3a domain-scope audit evidence

bead: soc-58nt.3.3

* feat(goals): F2.3 scenario_satisfaction JSON field + --scenarios-only

Per-directive scenario satisfaction added to ao goals measure: 0 -> 8
new JSON fields and a --scenarios-only mode that skips gate execution.

bead: soc-58nt.2.3

* feat(rpi): F3.4 ao rpi phased --scaffold-domain

bead: soc-58nt.3.4

* test(goals): commit missing goals-trace scenario-results fixture

The F4.1/F4.2 walker tests reference a scenario-results.json fixture under
a gitignored .agents/ subpath; it was never force-added and a clean
checkout (and CI) lacks it, failing 5 scenario_result tests. Force-add it.

bead: soc-58nt.4.1

* fix(goals): F4.1 tighten goalstrace scenario-claim heuristic

bead: soc-58nt.4.9

- Tighten scenarioTokenRe: auto- tokens now require at least two
  hyphen-separated slug segments (auto-X-Y…), so single-word English
  compounds like auto-merge and auto-update are no longer matched.
- Downgrade broken_bead_scenario_claim from error to warning when the
  claim comes from a heuristic (ConfidenceLow) free-text match; per
  ADR-0005 §4.1 only an explicit Scenarios: line constitutes a broken
  explicit link and may produce an error.
- Add beads_test.go with table-driven tests covering: English auto-*
  false-positive rejection, real multi-segment auto-* ID detection,
  explicit missing scenario → error, heuristic missing → warning only,
  resolvable scenario → no defect, end-to-end Walk with auto-merge bead
  produces no error-severity finding.

* test(goals): F2.T1 regression-coverage audit + gap fill

bead: soc-58nt.2.4

* ci(goals): F1.6 wire executable-spec link lint + trace-orphans (warn-first)

bead: soc-58nt.1.9

* feat(rpi): F3.3b runtime hook enforcement of domain scope

bead: soc-58nt.3.9

* test(goals): F2.T2 e2e script for scenario-satisfaction gate

bead: soc-58nt.2.5

* docs(goals): F5.0 re-steer policy + mutation-safety contract (ADR-0006)

bead: soc-58nt.5.9

* docs(goals): fix streak-reset prose in ADR-0006 (cleanup)

bead: soc-58nt.5.9
[no-sibling] prose fixup only, no structural change
0 → 0 schema fields changed

* test(goals): F4.T1 regression-coverage audit + gap fill

bead: soc-58nt.4.6

* feat(wiki): land wiki bounded context waves 1-4 (epic soc-behj)

Phased strangler consolidating ao's .agents/-touching logic into one
cli/internal/wiki bounded context. Each wave gated by the 102 cmd/ao
integration tests staying green; legacy command surface untouched.

- W1 soc-1lju  FrontmatterCodec — all 5 frontmatter parsers delegate
- W2 soc-36lw  CorpusLocator — agentsDirIn moved, 17 call sites migrated
- W2 soc-f4tr  Artifact + Claim domain types with subtype invariants
- W3 soc-vot0  persistent WikiIndex — JSONL, incremental by content-hash
- W3 soc-r08p  FreshnessPolicy — claim-level evidence-driven freshness
- W4 soc-ijp8  WikiPipeline — subsumes llmwiki loop, 3 stages wired
- W4 soc-q50a  port conformance suite

Also anchors .gitignore's bare wiki/ pattern with a !cli/internal/wiki/
negation — the new package was being silently ignored.

Wave 5 (ao wiki command group) is not included in this commit.

* test(goals): F4.T2 e2e script for trace chain

bead: soc-58nt.4.7

* test(rpi): F3.T1 regression-coverage audit + gap fill

bead: soc-58nt.3.6

* test(rpi): F3.T2 e2e script for domain-scoped RPI

bead: soc-58nt.3.7

* feat(goals): F5.1 verdict ledger schema + writer

bead: soc-58nt.5.1

* feat(goals): F5.2 re-steer policy engine + verdict-ledger producer hookup

bead: soc-58nt.5.2

* feat(goals): F5.4 feedback-to-learning compiler

Adds cli/internal/feedbackcompiler — scans the verdict ledger for
fail->pass directive transitions and drafts a learning entry in
docs/learnings/ for each transition found. Drafts carry status: draft
and directive_id frontmatter (ADR-0005 §2.6). Never auto-promotes.
Idempotent: skips existing drafts on re-run.

Also documents the auto-draft workflow in
skills/forge/references/feedback-compiler-drafts.md with a compact
reference-link in skills/forge/SKILL.md (under 248-line limit).
Synced to skills-codex/forge/ and regenerated codex hashes.

bead: soc-58nt.5.4

* docs(goals): F5.5 compound-engineering retro in /post-mortem

Extend the /post-mortem skill with a Compound-Engineering Retro section
that compares iteration N vs N-1 for a domain slice using the F5.1 verdict
ledger (.agents/goals/verdict-ledger.json). The mode emits a comparative
delta — directives that improved (fail→pass, satisfaction up), regressed
(pass→fail, satisfaction down), or held stable — plus learning yield since
N-1, written as a status:draft learning to .agents/learnings/.

Detailed step-by-step procedure (CE.0–CE.5) lives in
references/compound-engineering-retro.md; SKILL.md carries a compact
trigger/commands summary and links to it. Both files synced to
skills-codex/post-mortem/ with updated codex hashes.

bead: soc-58nt.5.5

* feat(goals): F5.3 ao goals steer --auto with human-gated confirmation

bead: soc-58nt.5.3

* test(goals): F5.T1 regression-coverage audit + gap fill

bead: soc-58nt.5.7

* test(goals): F5.T2 e2e script for auto re-steer

bead: soc-58nt.5.8

* docs(goals): F2.4 docs regen for scenario-satisfaction gate + epic CLI artifacts

Regenerate cli/docs/COMMANDS.md and registry.json from a clean worktree at
HEAD so the soc-58nt command surface is documented without leaking unrelated
peer WIP into the generated docs.

F2 (soc-58nt.2.7): document scenario_satisfaction JSON shape, --scenarios-only,
result-artifact resolution, and exit codes in skills/goals/SKILL.md +
references/executable-spec-chain.md; cross-ref ao goals measure/trace from
skills/scenario/SKILL.md.

Epic-wide artifacts also landed here because they regenerate atomically across
F2-F5: COMMANDS.md/registry.json (all new ao goals/rpi surface), cli-skills-map
heading count, skills-codex hashes, ADR-0004/0005/0006 documentation-index
links, and the F2-F5 e2e CI lanes in validate.yml.

ao capabilities and ao robot-docs need no manual edits — both build their
command/flag surface from the live cobra tree, so new commands register
automatically (Global Rule G5 satisfied by construction).

bead: soc-58nt.2.7

* docs(domain): F3.5 domain-as-loop docs for domain-scoped RPI

Document the domain-slice runtime in skills/domain/SKILL.md (a "Domain as a
scoped RPI loop" section connecting the Slice primitive to ao rpi phased
--domain / --scaffold-domain / --force) and add the Domain-Slice row to the
modes table in skills/scaffold/SKILL.md, mirroring the existing Project/
Component/CI mode-row shape. The scaffold skill's Domain-Slice Mode section
already covered the workflow; this lands the missing modes-table entry so the
mode count is 3 -> 4.

The COMMANDS.md/registry/CI-lane regen for F3 landed in soc-58nt.2.7.
skills-codex hashes for the two changed skills refreshed via
scripts/regen-codex-hashes.sh; codex-parity audit clean.

bead: soc-58nt.3.5

* docs(goals): F5.6 re-steer loop docs in /post-mortem

Cross-reference the auto re-steer loop from the Compound-Engineering Retro
section of skills/post-mortem/SKILL.md: when the compound retro names a
chronically regressing directive, ao goals steer recommend prints
policy-driven directive mutations from the same verdict ledger and ao goals
steer apply writes the mutation to GOALS.md, human-gated via the non-lossy
patcher (ADR-0006). Mirrors the existing "closing the loop" cross-ref shape
used elsewhere in the skill's See Also prose.

The ao goals steer recommend/apply COMMANDS.md surface, the F5 e2e CI lane,
and the ADR-0006 documentation-index link landed atomically in soc-58nt.2.7's
epic-wide regen; the re-steer contract detail lives in
skills/goals/references/executable-spec-chain.md (also in 2.7).

skills-codex/post-mortem hash refreshed; codex-parity audit clean.

bead: soc-58nt.5.6

* docs(goals): F4.5 docs regen for goals trace + render (epic-subsumed)

F4's docs deliverables — the `ao goals trace` and `ao goals render` entries in
cli/docs/COMMANDS.md, the F4 trace-chain e2e CI lane in validate.yml, the
ADR-0005 documentation-index link, and the Trace/Render mode sections plus the
trace contract in skills/goals/SKILL.md + references/executable-spec-chain.md —
all co-landed atomically in soc-58nt.2.7's epic-wide CLI-reference regen
(commit 37798abe), because COMMANDS.md/registry.json regenerate as one unit
across F2-F5 and cannot be split per-bead.

This empty commit records soc-58nt.4.5 as complete with no further file
changes. Matches the epic-subsumed-bead convention: regen artifacts land once,
later beads point at the lead regen commit.

bead: soc-58nt.4.5

* test(goals): fix flag-global leak breaking full goals-measure run

TestGoalsIntegration ran 'goals measure --directives' via cobra, which
sets the package-global goalsMeasureDirectives and never resets it.
soc-58nt's new TestGoalsMeasure_FullMode* tests branch on that global,
so the unfiltered 'go test ./cmd/ao/...' run failed (gates skipped,
directives array emitted instead of the full snapshot+scenarios object).
-run filters masked it. Restore the global after Execute.

bead: soc-58nt.2.4

* fix(ci): correct soc-58nt skill/codex artifacts and CI-policy parity

Resolves 6 red CI checks on PR #292 (crank/soc-58nt) after merging main:

- skill-integrity: reword the "no scaffold subcommand" note in
  skills/scaffold/SKILL.md so it no longer trips the heal INVALID_AO_CMD
  substring check; copy skills/forge/references/examples.md into
  skills-codex/forge/references/ so the codex forge dead-link clears.
- validate-codex-generated-artifacts: strip leaked non-Codex frontmatter
  from skills-codex/forge/SKILL.md down to name + description.
- validate-codex-runtime-sections: remove the residual mixed-runtime
  "Cross-vendor analog" line (Anthropic Managed Agents) from codex forge;
  convert /forge slash-command refs to $forge Codex invocation style.
- validate-ci-policy-parity: add executable-spec-link-integrity
  (non-blocking) to the AGENTS.md non-blocking list so it matches the
  workflow's continue-on-error classification (warn-only F1.6 gate added
  by soc-58nt without parity co-update).
- doc-release-gate: the link-validation broken link was the same codex
  forge references/examples.md dead link, fixed above.
- agentops-contract-canaries: update the cli-command-surface canary
  fixture + eval JSON for the 185->187 / 257->259 subcommand count after
  soc-58nt added `ao goals render` and `ao goals trace`.

Also ports the soc-58nt "Closing the loop with re-steer" paragraph into
skills-codex/post-mortem/SKILL.md and regenerates codex artifact hashes
and registry.json after the merge.

* fix(test): set repo-local git identity in rpi-phased-domain e2e

CI runners have no global git identity; the temp-repo 'git commit' in the
F3.7 e2e exited 128 (empty ident name). Configure a repo-local identity.

bead: soc-58nt.3.7

* fix(goals): use canonical practice slug bdd-gherkin

soc-58nt files cited the practice slug 'bdd'; the canonical slug in
PRACTICE-REGISTRY.md is 'bdd-gherkin'. Clears the practice-citations
advisory check (9 invalid slug citations -> 0).

bead: soc-58nt.2.7

* fix(goals): add missing practices field to F5 gap-test file

goals_steer_auto_gap_test.go lacked a // practices: header; the
practice-citations --strict gate flags missing fields. Matches its
sibling goals_steer_auto_test.go.

bead: soc-58nt.5.7

* fix(security): resolve gosec G122 + golangci-lint errcheck/staticcheck HIGH findings

Drive scripts/security-gate.sh --mode quick to 0 HIGH findings.

gosec G122 (CWE-367 TOCTOU): goalstrace/artifacts.go reads files inside a
filepath.WalkDir callback via an os.Root-scoped handle (os.OpenRoot), closing
the check-to-use window and blocking symlink escape.

golangci-lint errcheck (~25): unchecked defer Close / os.Remove returns.
Resource-cleanup closes use explicit-ignore (defer func(){ _ = X.Close() }()).
Durability-gating closes before a rename (llm/review.go tmp.Close) are checked
and wrapped.

golangci-lint staticcheck (~21): ST1005 (drop trailing punctuation from error
strings), QF1001 (De Morgan's law), QF1002 (tagged switch), QF1012
(fmt.Fprintf over WriteString+Sprintf), S1016 (struct-literal -> conversion),
S1017 (strings.TrimSuffix), S1040 (drop redundant type assertion), SA1012
(context.TODO over nil Context), SA4032 (drop dead GOOS branch under build
constraint), SA9003 (remove empty branch).

* ci(security): make security-toolchain-gate blocking

Remove continue-on-error: true from the security-toolchain-gate job and drop
the "(advisory)" name suffix so a CRITICAL/HIGH security-gate finding fails
the validate summary job. The job is already in summary.needs, so removing
continue-on-error promotes it into the contains(needs.*.result,'failure')
fail-set.

AGENTS.md: drop security-toolchain-gate from the (non-blocking) prose list
and the Advisory Job Triage SLA table; update its CI-jobs-table row to
describe the blocking failure mode. validate-ci-policy-parity confirms the
AGENTS blocking set matches the workflow summary fail-set (7 non-blocking).

---------

Co-authored-by: Codex <codex@example.invalid>
2026-05-17 15:31:15 -04:00
Boden Fuller a0b9b21145 docs(goals): document ao goals scenarios; regenerate CLI reference
F1.5 of epic soc-58nt (terminal docs bead for F1).

- Regenerate cli/docs/COMMANDS.md and registry.json for the new
  'ao goals scenarios' command surface (list / --create / --lint).
- Document the directive↔scenario executable-spec link in skills/goals/
  SKILL.md and cross-reference it from skills/scenario/SKILL.md.
- 'ao goals scenarios' auto-registers in 'ao capabilities' and
  'ao robot-docs' (both walk the cobra command tree). Codex parity audit
  passes for goals and scenario.

Closes soc-58nt.1.5.
2026-05-17 07:18:41 -04:00
Boden Fuller dc14aeb439 feat(cli): add top-level capabilities and robot-docs agent surfaces
`ao doctor` already exposes `capabilities` and `robot-docs`, but an agent's
first introspection guess is the top-level command. Elevate that pattern:
`ao capabilities` emits the machine-readable CLI contract (command tree,
global flags, exit codes, env vars, robot-surface pointers) as JSON, and
`ao robot-docs` prints a paste-ready agent handbook. Both mirror the shape
of the doctor surface in cmd/ao/doctor_surface.go.

Top-level robot surfaces: 0 → 2. The command list and flags are walked from
the live cobra tree so the contract never drifts from registration.
2026-05-17 01:12:35 -04:00
Boden Fuller 54f1ba4568 feat(doctor): wire diagnose-and-repair subcommands into the ao CLI
Adds the doctor engine surface to the existing doctorCmd: subcommands
fix, undo, explain, capabilities, health, robot-docs, gc, ls, diff, plus
the additive flags --fix/--dry-run/--only/--skip/--since/--online/
--quick/--severity/--robot/--robot-triage/--explain.

Exit codes route through a typed doctorExitError that Execute() maps to
os.Exit via errors.As — mirrors the AgentsLintError pattern already in
root.go, no parallel exit mechanism.

Backward compatible: bare `ao doctor` and `ao doctor --json` keep the
legacy 17-line check table and checks JSON array unchanged. With the
empty registry the engine adds 0 findings, so the bare command's exit
semantics (0 → 1 on required failure) are untouched. CLI reference docs
regenerated for the 9 new subcommands.
2026-05-16 12:10:12 -04:00
Bo a4108a5503 fix: critical audit findings remediation (epic soc-ab5g) (#279)
* fix(session): sanitize init-step exec and correct BEADS_ACTOR

Two defects in `ao session spawn` runInitSteps, from the 2026-05-16
codebase audit (epic soc-ab5g):

- BEADS_ACTOR was exported as the expanded command string of the first
  init step instead of the session actor identity, so beads attribution
  for every init step was garbage. Thread tmpl.Identity.BeadsActorTemplate
  through runInitSteps and export the var only when non-empty.
- Init steps ran via raw exec.Command("bash","-c",...) (SEC-C1). Route
  init-step exec through shellutil.SanitizedBashCommand and add
  sanitizeHostname() to strip shell metacharacters from {{hostname}}-class
  template vars before substitution.

New regression tests: TestRunInitStepsSetsBeadsActor, TestSanitizeHostname.

Refs: soc-jhxr

* fix(autodev): replace vague 'validation failed' with a concrete summary

outputAutodevValidateResult returned fmt.Errorf("validation failed")
after already printing the detailed INVALID/ERROR lines -- a redundant,
content-free wrapper. Return a summary naming the file and the
validation-error count instead (COPY-C1, epic soc-ab5g).

Refs: soc-mpzu

* docs(eval): strip internal Day-N cadence jargon from user-facing text

eval_task.go exposed internal sprint labels ("Day-2 placeholder",
"Day-3 wires real launch", "Day-4 gate #4") in command Long
descriptions and flag help. Reword to describe current behavior
without the internal cadence (COPY-C2, epic soc-ab5g).

Refs: soc-iy7p

* fix(cli): reject unknown subcommands on group commands

Group-parent commands (daemon, beads, codex, constraint, factory,
goals, hooks, ratchet, rpi, session) had no Args validator, so an
unknown subcommand printed help to stdout and exited 0 -- breaking
`if ao rpi <bad>; then ...` scripting. Add Args: cobra.NoArgs to all
ten; unknown subcommands now exit 1 with the error on stderr
(CLI-C1, epic soc-ab5g).

Refs: soc-mlqe

* docs(cli): regenerate COMMANDS.md after eval-task help copy edit

* fix(daemon): contain dream output_dir against path traversal

DreamRunJobSpec/DreamStageJobSpec/DreamStageManifest Validate() only
TrimSpace-checked output_dir, leaving the operator-supplied job payload
free to redirect summary/log writes outside the intended tree.

validateOutputDir now rejects ".." traversal in all three Validate()
paths; outputDirContained rejects an absolute output_dir resolving
outside the daemon working tree, checked in DreamExecutor.RunJob before
MkdirAll. Containment checks: 1 (symlink only) → 3 (symlink + .. + abs).

Closes soc-ly33 (SEC-C2, epic soc-ab5g).

* fix(cli): wire ao --version flag and unify goals --json with -o

rootCmd had no Version field, so `ao --version` was unsupported even
though an `ao version` subcommand existed. Set rootCmd.Version = version
(the ldflags-injected build var) so the standard --version flag works.

goals registered its own local --json bool, disconnected from the
global -o/--output flag — `ao goals measure -o json` was ignored. Drop
goalsJSON; goalsJSONOutput() now reads GetOutput(), the sibling pattern
used by agentopsd.go, autodev.go, codex.go and ~40 other callsites. The
global --json persistent flag is inherited, so `ao goals --json` still
works; output paths honored by goals: 1 (local bool) → 2 (--json + -o).

Closes soc-nx1o (CLI-C2, epic soc-ab5g).

* perf(cli): collapse tmux probe storm in ao rpi status

checkTmuxSessionAlive forked `tmux has-session` up to 3 times per
non-terminal run, each with a 2s timeout — a status scan over N runs
issued 3N subprocesses and could stall ~6N seconds when tmux was slow
or absent.

probeTmuxSessions now runs one `tmux ls -F #{session_name}`, memoized
per process behind a mutex-guarded cache; tmuxSessionAlive filters the
snapshot in Go. resolveRPIToolchainDefaults collapses from per-run to
once. Subprocesses per status scan: 3N → 1.

Closes soc-d7v5 (PERF-C1, epic soc-ab5g). Mirrors the snapshot-then-
filter shape used elsewhere for batch probes.

* perf(cli): one-pass git capture + walk-once index in ao beads audit

ao beads audit re-shelled git per bead (one `git log --grep` per bead,
one `git log --since` per bead-path pair) and re-walked the worktree
per pattern (recordAuditStaleFinding probes up to 10 patterns/bead, so
up to 10N full-repo walks for N beads).

captureAuditCommits now runs a single `git log --all --name-only`,
parsed into auditCommit records; grepCommitsForID and
fileChangesSinceCommits filter that slice in Go. repoContentCache walks
the scoped roots once (lazily) and memoizes a path->content map shared
across every pattern probe. git subprocesses per audit: O(beads) → 1;
repo walks: O(10*beads) → 1. Mirrors the snapshot-then-filter shape
just applied to ao rpi status.

Closes soc-2grz (PERF-C2, epic soc-ab5g).

* fix(schemas): declare schema_version const in 15 unversioned schemas

15 of 34 schemas under schemas/ carried versioned filenames (or implied
a stable contract) without a machine-readable schema_version, so a
consumer could not detect the version from the payload alone.

Each now declares an optional schema_version integer const, mirroring
the shape in schemas/bead.v1.schema.json: const 1 for every schema
except skill-frontmatter.v2 (const 2). The field is intentionally left
out of "required" so existing documents without it still validate —
non-breaking. scenario.v1 keeps its legacy "version" field alongside.
Schemas declaring schema_version: 19/34 → 34/34.

Closes soc-wzgo (API-C2, epic soc-ab5g).

* docs(contracts): regenerate context-map after merging main

The merge of origin/main pulled a discovery SKILL.md description edit
without its companion context-map regeneration, so
validate-context-map-drift flagged 1 stale line. Regenerated via
scripts/generate-context-map.sh. Drifted lines: 1 → 0.
2026-05-16 11:05:36 -04:00
Boden Fuller c85b27f439 feat(loop): expose HypothesisLedgerPort + ConvergenceCheckPort via ao loop
Follows the ao loop append/history wiring shape (loop_append.go): each
command pairs an options struct with an injectable *Fn seam and a
*ViaPort production path.

- ao loop hypothesis {list,append} — typed BC3 HypothesisLedgerPort
  surface over .agents/evolve/hypotheses.jsonl (productionHypothesisLedger,
  soc-y5vh.6). append rejects empty/duplicate IDs; list emits one JSON
  HypothesisRecord per line.
- ao loop converged — pure BC3 ConvergenceCheckPort STOP predicate
  (productionConvergenceCheck, soc-y5vh.7). Takes caller-supplied
  evidence (green streak, unconsumed HIGH+MEDIUM, fitness-baseline),
  emits {converged, ci_green_streak, ..., reasons}.
- evolve docs (SKILL.md + convergence-mechanics.md) reference the new
  typed path instead of direct hypotheses/session-convergence reads.
- Cross-harness parity: ported convergence-mechanics.md into the codex
  evolve skill (was 9/19 reference files, now 10/19), harness-adapted
  (Step 7 while-loop STOP vs ScheduleWakeup). Broader evolve drift
  tracked in soc-an3v.
- CLI docs regenerated; cd cli go build/vet/test ./... green
  (11921 tests, 53 packages).

Tests: LoopConverged 5 cases (converged + each unmet reason + observed
streak), LoopHypothesis 4 cases (empty-id reject, stub mapping, JSONL
render, append->list round-trip).

Closes soc-y5vh.8
2026-05-16 10:56:23 -04:00
Boden Fuller fefd1dd65e feat(loop): record XP/BDD/TDD trace in evolve cycles
Evolve cycle history/report now carries the Olympus continuous-evolution
kernel as a structured `trace` object so a reviewer can reconstruct a
cycle without the transcript: goal hypothesis -> selected gap -> Gherkin
scenario (or explicit exemption) -> first failing proof -> red/green
evidence -> refactor note -> validation evidence -> ratchet action ->
goal reshape decision.

- ports: CycleTrace struct + CycleEntry.Trace (json:"trace,omitempty");
  TraceCompleteness pure helper reports exemption / missing fields.
- adapters: rawCycleRecord + loopWriterRecord round-trip the trace;
  trace-less entries stay byte-identical.
- ao loop append --trace-json <path|inline>; ao loop history emits it.
- evolve-log-cycle.sh --trace-json <path|json|-> validates the object
  and records it; malformed/non-object payloads rejected.
- Trivial one-shot cycles record trace.exemption_reason instead of
  false BDD/TDD ceremony. Completeness is advisory, never a gate.
- docs: skills/evolve + skills-codex/evolve cycle-history.md + Step 6.

Tests: TraceCompleteness table tests, adapter round-trip, loadCycleTrace,
bats trace + exemption + malformed. go test ./... green; bats 10/10.

Closes soc-y5vh.9
2026-05-16 10:25:08 -04:00
Boden Fuller d17a5eec87 docs(cli): regenerate COMMANDS.md after --oscillation-sweep removal
Pure regen output. The cycle-181 dead-code sweep removed the
--oscillation-sweep flag from `ao defrag` but did not regenerate the
CLI reference, leaving cli/docs/COMMANDS.md listing a flag that no
longer exists. This commit removes that single line.

Caught by the new STEP 1.7.5 "Release-Readiness Gates" check added to
the validation skill (this same session) — first validation that the
new mechanical gate works on real drift.
2026-05-14 07:14:24 -04:00
Boden Fuller 78ac885def feat(cli): ao loop verify — audit cycle-history integrity (uses cycle-161 widening)
Cycle 163. First NEW consumer of the cycle-161 CycleEntry widening
(soc-ckc4). NOT a template adapter wrap — adds new audit behavior
on top of the typed LoopReaderPort.

ao loop verify audits .agents/evolve/cycle-history.jsonl integrity:
  - monotonic Number ordering
  - no duplicate Number values
  - non-empty StartedAt on every entry (uses the cycle-161 field)
  - trailing IdleStreak < threshold (--max-idle, default 5)

Exit code 0 on clean ledger, non-zero with FAIL summary on any issue.
Useful as pre-commit gate or CI assertion against hand-edited ledgers.

Live smoke surfaced a REAL drift on first run: cycle 74 is duplicated
in the current ledger (lines 73-74, identical except unicode → vs
ASCII 'to'). Filed soc-(new) for operator-data cleanup; not fixed
in this cycle.

Implementation:
- cli/cmd/ao/loop.go: loopVerifyCmd + loopVerifyRun + checkLoopIntegrity
  pure-Go audit fn + loopVerifyViaPort wiring. Injectable verifyFn
  follows the cycle-147 CLI-wiring template testability pattern.
- cli/cmd/ao/loop_test.go: 7 new tests:
  TestCheckLoopIntegrity_{CleanLedger, NonMonotonicNumber,
  DuplicateNumber, MissingStartedAt, IdleStreakExceedsThreshold}
  + TestLoopVerifyRun_{StubPASS, StubFAIL}.

Distinguishing this from the cycle-155 banned pattern: the previous
adapter cycles wrapped a port method 1:1 in a CLI command. This
cycle composes Range + IdleStreak with NEW audit logic
(non-monotonicity check, duplicate detection, StartedAt presence)
that didn't exist anywhere before. It's a real consumer of the port,
not a trivial passthrough.

cli/docs/COMMANDS.md regenerated.
gofmt clean, go vet clean, full ./... PASS.
+143 LOC, 7 new tests, 1 real audit finding surfaced.
2026-05-12 22:45:45 -04:00
Boden Fuller d5772d97f8 feat(cli): ao claim bind/list — 10th adapter CLI-exposed
Cycle 154 applies the cycle-147 template to a 10th adapter:
productionClaimEvidenceBinder (cycle 116). Provides typed CLI for
the claim→evidence promotion ledger.

New surface:
  ao claim bind --claim AOP-CLAIM-X --path p.md --level PG2 [--anchor ...]
  ao claim list

Wraps productionClaimEvidenceBinder — appends EvidenceBinding records
to .agents/findings/evidence-bindings.jsonl with upgrade-only Level
enforcement (per the port contract; downgrade attempts return an
error surfaced through 'claim bind:' wrapping).

Template adherence vs cycle-147 spec: ✓. Two new nuances:
- Level validation (PG1-PG4) at the CLI layer via
  validateEvidenceLevelString helper. Catches typos before they
  reach the port.
- Default --level=PG1 via cobra default flag, so 'ao claim bind
  --claim X --path Y' is valid for first-time bindings.

New files:
- cli/cmd/ao/claim_cmd.go (170 lines) — claimCmd parent +
  claimBindCmd + claimListCmd + level validator + path resolver.
  Both ops share claimOptions struct + injectable bindFn / listFn.
- cli/cmd/ao/claim_cmd_test.go (115 lines) — 8 tests covering
  empty claim/path rejection, invalid level rejection, stub called
  with binding, default PG1 accepted, error wrapping, list returns
  bindings, empty list zero bytes.

cli/docs/COMMANDS.md regenerated.

10 of 14 production adapters now CLI-exposed (~71%):
  loop history/append + ci latest/recent + corpus inject/capture +
  operator record/list + harness status + gate run + citation verify
  + claim bind/list.

4 remain (FactoryAdmission, ClaimEvidence, EventBus, FindingCompiler).

Time: ~9 min. LOC: 285. Tests: 8. Within template band.

Cycle 154 / template-applied CLI-wiring 7th application.
2026-05-12 21:41:05 -04:00