Commit Graph

940 Commits

Author SHA1 Message Date
Bo bb8d747d3a feat(ao): add harness-neutral bead ports (#810)
## Summary
- add harness-neutral BR/git ports: `ao ready`, `ao close ...`, and bare
`ao claim <id>` while preserving `ao claim bind/list`
- keep top-level gate commands from #809 as the shared gate surface; `ao
close` reuses the typed commit-persist close path and returns success
when the ledger already proves the bead is closed
- regenerate command docs, command surface inventory, matrix counts, and
registry metadata

## Validation
- `go test ./cmd/ao -run TestTick -count=1`
- `bash scripts/regen-all.sh && bash scripts/regen-all.sh --check`
- `go test ./cmd/ao -count=1`
- `go vet ./...`
- `go test ./... -count=1 -short`
- `git diff --check`
- `/tmp/ao-cp-ljp ready` from `/Users/bo/dev/control-plane` returned
`state_source=br`, `ready_count=20`, and counts `{ready:20, open:30,
in_progress:1, closed:19}`
- `/tmp/ao-cp-ljp claim cp-ljp` from `/Users/bo/dev/control-plane`
succeeded and did not dirty the Git ledger
- `PRE_PUSH_SKIP_MKDOCS=1 bash scripts/pre-push-gate.sh --fast` passed;
warning-only executable-spec drift remains pre-existing (`scenarios
--lint`, `trace --orphans`)

BR: cp-ljp
2026-06-07 02:03:39 -04:00
Bo a617df3028 feat(ao): expose membrane gate aliases (#809)
## Summary
- expose the membrane gate surfaces as top-level `ao` aliases:
`verdict-gate`, `council-gate`, `install-guards`, `guard-status`, and
`chaos-test`
- keep the existing `ao tick ...` implementation as the shared backend,
so the shell-oracle port and public aliases cannot drift
- regenerate CLI command docs, surface inventory, matrix counts, and
registry metadata

## Validation
- `go test ./cmd/ao -run TestTick -count=1`
- `bash scripts/check-cmdao-surface-parity.sh --write-surface`
- `bash scripts/regen-all.sh --check`
- `go test ./cmd/ao -count=1`
- `go vet ./...`
- `go test ./... -count=1 -short`
- `/tmp/ao-cp-ltj chaos-test` from `/Users/bo/dev/control-plane` =>
`SMOKE PASS`
- `./tick.sh smoke` from `/Users/bo/dev/control-plane` => `SMOKE PASS`
- top-level alias exit matrix from `/Users/bo/dev/control-plane`:
`council-gate` mixed => 8, unverified => 6
- `PRE_PUSH_SKIP_MKDOCS=1 bash scripts/pre-push-gate.sh --fast` passed;
warning-only executable-spec drift remains pre-existing (`scenarios
--lint`, `trace --orphans`)

BR: cp-ltj
2026-06-07 01:45:16 -04:00
Bo 9ddffab410 feat(ao): add typed tick oracle port (#808)
## Summary
- add `ao tick` as a typed Go port of the control-plane tick oracle
- preserve BR/git boundaries for next/status/claim/reopen/close and port
verdict/council/guard/smoke behavior
- update generated command inventory, registry, CLI docs, and
command-surface fixtures

## Validation
- cd cli && go test ./cmd/ao -run TestTick -count=1
- cd cli && go test ./cmd/ao -count=1
- cd cli && go vet ./...
- cd cli && go test ./... -count=1 -short
- bash scripts/check-cmdao-surface-parity.sh
- bash scripts/regen-all.sh --check
- git diff --check
- PRE_PUSH_SKIP_MKDOCS=1 bash scripts/pre-push-gate.sh --fast
- built /tmp/ao-cp7k6 and compared against
/Users/bo/dev/control-plane/tick.sh for verdict-gate, council-gate,
next; `ao tick smoke` returned SMOKE PASS

Note: fast pre-push gate still reports existing warn-only
executable-spec drift for scenarios --lint and trace --orphans.
2026-06-07 01:26:56 -04:00
Bo f4162649b8 fix(agentops): remove api-print worker paths (#804)
## Summary
- replace AgentOps headless Claude print worker paths with Codex exec or
non-CLI providers
- switch compile auto-detect/default headless CLI runtime from
claude-cli to codex-cli
- fix two shellcheck blockers from the merged skill corpus and refresh
generated docs/hash metadata

## Validation
- bash /Users/bo/acfs/skill-pipeline/scripts/no-api-print-scan.sh
/Users/bo/dev/agentops/skills /Users/bo/dev/agentops/scripts
/Users/bo/dev/agentops/hooks
- shellcheck --severity=error skills/compile/scripts/compile.sh
scripts/eval-agent-harness.sh scripts/run-rpi-phases.sh
skills/agy-rules-workflows/scripts/close-guard.sh
skills/rch/scripts/mine_rch_history.sh
- cd cli && go test ./cmd/ao -run
'TestResolveCompileRuntime|TestPreflightCompileRuntime|TestCompileScriptOptionsPassesBatchFlags|TestRunCompile_EndToEnd_FixtureCorpus'
-count=1
- bash scripts/regen-all.sh --check
- PRE_PUSH_SKIP_MKDOCS=1 bash scripts/pre-push-gate.sh --fast

Note: unscoped
/Users/bo/acfs/skill-pipeline/scripts/no-api-print-scan.sh still flags
ACFS conformance-selftest fixture strings outside this repo; AgentOps
scoped roots are clean.
2026-06-07 00:08:59 -04:00
Bo 6d5d111faf feat(skills): land four-vendor skill corpus (#803)
## Summary
- Lands the AgentOps skill corpus across source and Codex wrappers,
including Gemini image targets.
- Integrates the skill branch onto current origin/main and refreshes
generated manifests/catalogs.
- Normalizes local next-work queue enum drift and fixes shellcheck in
alias-collision validation.

## Validation
- bash scripts/regen-all.sh --check
- bash scripts/validate-skill-schema.sh
- bash tests/skills/test-token-budgets.sh
- bash tests/skills/run-all.sh
- bash scripts/audit-codex-parity.sh && bash
scripts/validate-codex-override-coverage.sh && bash
scripts/validate-codex-generated-artifacts.sh --scope worktree && bash
scripts/validate-codex-api-conformance.sh && bash
scripts/validate-codex-runtime-sections.sh && bash
scripts/lint-codex-native.sh
- bash skills/heal-skill/scripts/heal.sh --strict
- PRE_PUSH_SKIP_MKDOCS=1 bash scripts/pre-push-gate.sh --fast

MkDocs strict remains skipped locally via the documented
PRE_PUSH_SKIP_MKDOCS=1 override because the repo currently emits
repo-wide strict-link warnings unrelated to this landing; CI remains
authoritative.
2026-06-06 23:44:42 -04:00
Bo 3bab897009 test(cli): migrate dedup_test.go to t.Chdir + lock isolation ratchet 163->151 (ag-4nif #dedup-tchdir) (#773)
## What

Migrate `dedup_test.go`'s 6 manual `origDir, _ := os.Getwd();
os.Chdir(tmp); defer os.Chdir(origDir)` blocks to Go 1.20+
`t.Chdir(tmp)` — auto-restores cwd **and** refuses `t.Parallel`,
pre-empting the latent flake class flagged by **ag-4nif** / **ag-k38x**
(raw forms mutate process-global cwd). No `t.Parallel` in the file;
behavior-preserving.

Each block held two `os.Chdir(` references (the call + the defer
restore), so 6 blocks drop the repo-wide test-isolation count by **12
(163→151)**. I lowered `BASELINE_CHDIR` in
`scripts/check-test-isolation.sh` to **lock the gain** (the ratchet is
only-goes-down).

## Scope / no-collision note

`os.Chdir` only. The 3 redundant `os.Setenv` restores in
`rpi_reliability_test.go` are **already handled by the open PR #772
(ag-k38x)** — I deliberately do **not** touch that file here, to avoid a
merge collision (an earlier draft of this branch did; it was reverted).
**ag-k38x is the canonical tracker; ag-4nif/ag-y4ac are duplicates** —
worth closing as dup-of-ag-k38x during triage.

## Evidence

```
bash scripts/check-test-isolation.sh        # PASS (os.Chdir=151/151, os.Setenv=22/22)
cd cli
go build ./...                              # OK
go vet ./cmd/ao/                            # No issues found
go test ./cmd/ao/ -run 'TestRunDedup|TestMergeDedupGroups' -count=1   # 17 pass
gofmt -l                                     # clean
```

Net: −19 / +8 across `dedup_test.go` + the ratchet baseline.

Closes-scenario: ag-4nif#dedup-tchdir
Bounded-context: BC5-Runtime
Evidence: ratchet PASS + go build/vet/test (17 pass) + gofmt clean
2026-06-06 11:23:54 -04:00
Bo def8fc9bd4 chore(doctor): drop dead ao agentopsd/GasCity strings (ag-9hhm #dead-text-removed-surfaces) (#793)
## What

Two dead-text remnants pointed operators at **retired surfaces**
(discovered in the 2026-06-03 feature-map analysis). Pure string hygiene
— no behavior change.

1. **`cli/internal/doctor/fix_bridges.go`** — `openclawHealthReport` and
`snapshotStaleReport` instructed `ao agentopsd start/status/restart` and
`ao agentopsd projection rebuild --consumer openclaw`. The in-repo
daemon was **removed (ADR-0009)** and no such command exists. Reworded
to: bring the OpenClaw daemon up out-of-band / regenerate the projection
from the out-of-session substrate, naming the daemon removal instead of
a dead command. (Also fixed two stale code comments naming `agentopsd`.)
2. **`cli/cmd/ao/forge.go:43`** — the `--legacy-local-llm` deprecation
warning named *"AgentWorker/GasCity-backed Codex or Claude workers"*.
Gas City was pruned (ag-124p) and the GasCity worker adapter removed.
Reworded to point at a **Dream worker queue** (consistent with the
existing language elsewhere in `forge.go`).

## Why

These strings mislead operators toward commands/substrates that no
longer exist; the `gc`/`agentopsd` prune was code-clean but the
user-facing strings survived.

## Verification

- `gofmt` clean; `go build ./...`; `go vet ./internal/doctor/ ./cmd/ao/`
- `go test ./internal/doctor/` → 76 passed; `go test ./cmd/ao/ -run
Forge` → 379 passed
- `git grep "ao agentopsd"` / `GasCity` over live (non-test) code in
touched files → empty
- Updated the two doctor tests that asserted the old substrings.

Closes-scenario: ag-9hhm#dead-text-removed-surfaces
Bounded-context: BC5-Runtime
Evidence: cli/internal/doctor/fix_bridges_test.go
2026-06-06 10:17:48 -04:00
Bo 3df599df75 fix(ao): case-insensitive dedup in pool_ingest candidate IDs (soc-xn5s #dedup-case-insensitive) (#792)
## What

`buildCandidateFromLearningBlock` (cli/cmd/ao/pool_ingest.go) dedups the
three ID
components (`base`, `sessionHint`, `learningID`). `learningID` is
lowercased before
the check, but `base`/`sessionHint` keep their source-filename case. So
any source
filename with an uppercase character (e.g.
`2026-04-30-fix20260430T090800-1.md`)
**skipped** the `strings.Contains` dedup branch and produced a
**doubled** candidate ID:

```
pend-2026-04-30-fix20260430t090800-1-ag-test-fix20260430t090800-1   (id appears twice)
```

The final ID is lowercased by `Slugify` anyway, so the comparisons
should be
case-insensitive. Fix normalizes both sides of the equality/`Contains`
checks for
`base`↔`sessionHint` and `joined`↔`learningID`.

## Why it mattered

Verified live 2026-04-30 (soc-xn5s): a mixed-case source filename
re-amplified its
ID on each close-loop pass. Production residue was all-lowercase so
unaffected, but
the edge case remained uncovered.

## Test

Two regression subtests in
`TestPoolIngest_buildCandidateFromLearningBlock` reproduce
the exact doubled ID and the mixed-case session-hint duplication. Both
fail against the
old code, pass with the fix:

```
--- FAIL .../mixed-case_filename_does_not_double_the_learning_id
    learning id token "fix20260430t090800-1" appears 2 times ... want exactly 1
--- FAIL .../mixed-case_session_hint_matching_base_is_not_appended
    session hint token appears 2 times ... want exactly 1
```

`go build ./...`, `go vet ./cmd/ao/`, `gofmt -l` all clean.

Closes-scenario: soc-xn5s#dedup-case-insensitive
Bounded-context: BC1-Corpus
Evidence:
cli/cmd/ao/pool_metrics_test.go::TestPoolIngest_buildCandidateFromLearningBlock

🤖 Autonomous factory tick.
2026-06-06 10:17:45 -04:00
Bo 9af54d589a feat(corpus): per-session corpus-reuse receipt at session close (ag-469h #corpus-reuse-receipt) (#778)
## What

Makes the knowledge-flywheel moat **felt**. `ao session close` now
renders a per-session **corpus-reuse receipt**: a `Reused: N` table line
plus a loud `♻ Compounding: reused N prior learnings (titles…); corpus
now X entries` line. Closes the perception gap behind bead **ag-469h** —
users perceive the WRITE tax every session but never the READ payoff, so
they churn before the aha.

## How

- `computeReuseReceipt(cwd, sessionID)` reads
`.agents/ao/citations.jsonl` (via `ratchet.LoadCitations`), filters to
the closing session, dedupes by canonical artifact path, and excludes
`reference` (manual-pointer) citations — counting only
`retrieved`/`applied` read-payoffs.
- Corpus size counts durable `.md` under `.agents/{learnings,patterns}`.
- New `SessionCloseResult` fields
`Reused`/`ReusedTitles`/`CorpusEntries` (all `omitempty`, so JSON output
is backward-compatible).
- Best-effort: a missing ledger or corpus yields a zero receipt and
never fails the close. Cold runs print nothing.
- No new CLI command (avoids the surface-matrix canary churn documented
in ag-lkxx) — purely additive output on an existing command.

## Test

`cli/cmd/ao/session_close_receipt_test.go` — dedup, cross-session
isolation, reference-exclusion, empty/no-ledger zero-receipt,
singular-noun, cold-run silence, date-prefix title stripping, and
`printCloseTable` integration. Full `cmd/ao` suite green (`go test`/`go
vet`/`gofmt` clean).

Closes-scenario: ag-469h#corpus-reuse-receipt
Bounded-context: BC1-Corpus
Evidence: cli/cmd/ao/session_close_receipt_test.go
2026-06-06 10:17:33 -04:00
Bo bc9a846dbd feat(eval): ao eval outcomes ingest --manifest-out emits eval-run.v1 manifest (ag-fy6e #manifest-out-eval-run) (#774)
## What

Adds `--manifest-out <dir>` (+ `--run-id`) to `ao eval outcomes ingest`.
When set, ingest **also** writes `<dir>/<run-id>/manifest.json`
conforming to `schemas/eval-run.v1.schema.json`, so the existing verdict
pipeline (`docs/contracts/eval-verdict-pipeline.md`) can pick it up and
feed the Knowledge Flywheel — closing the **Outcomes→Flywheel loop** for
the eval-run.v1 surface. Bead: **ag-fy6e**.

## How

- New file `cli/cmd/ao/eval_outcomes_manifest.go` (diversifies off
`eval_outcomes_ingest.go`); ingest.go only wires the thin flag.
- Reuses the existing `eval.RunRecord` struct rather than hand-rolling
the schema shape.
- Band mapping: `PASS→{status:pass, verdict:pass}`, `FAIL→{fail, fail}`,
`WARN→{inconclusive, advisory}`.
- `dimension_scores` filtered to the 7 schema-permitted keys (foreign
criterion dropped; `minProperties:1` guarded by synthesizing
`correctness=aggregate`).
- Out-of-process grade → honest schema-valid stubs for
`git`/`runtime`/`environment` + a `notes` provenance entry.

## Deviation from bead text (source-of-truth precedence)

The bead said `status=complete`, but that value belongs to the **other**
SCHEMA.md run-manifest. eval-run.v1's `status` enum is
`pass/fail/error/skipped/inconclusive` — no `complete` member — so this
follows the schema (per CLAUDE.md precedence: executable contracts >
narrative).

## Scope boundary (honest)

This delivers **only** the eval-run.v1 emission named in the bead's
ACCEPTANCE CRITERIA. The downstream `eval-verdict-compiler.sh` /
SCHEMA.md run-manifest path (rc3 struct verdict + holdout-sensitive
`ground_truth_ref/hash`) remains **BLOCKED** on the external compiler
contract + a holdout-safety decision, per the bead's own 2026-05-30
SCOUT CORRECTION. eval-run.v1 carries no holdout-sensitive fields, so
that blocker does not apply to this slice. Bead left open for the
compiler-path follow-up.

## Verification

- `go build ./... && go vet ./cmd/ao/` clean
- 77 eval/outcomes tests pass (`go test ./cmd/ao/ -run 'Eval|Outcomes'`)
- Real CLI run writes a manifest that validates against
`eval-run.v1.schema.json` via python `jsonschema`
- `cli/docs/COMMANDS.md` regenerated for the two new flags

Closes-scenario: ag-fy6e#manifest-out-eval-run
Bounded-context: BC2-Validation
Evidence: cli/cmd/ao/eval_outcomes_manifest_test.go
2026-06-06 10:17:25 -04:00
Bo 7265fda1b1 chore(test): migrate redundant os.Setenv to t.Setenv; lower isolation ratchet 22->12 (ag-k38x #bulk-migration-setenv) (#772)
## What

Advances the test-isolation ratchet (`scripts/check-test-isolation.sh`,
shipped in #699) by driving the `os.Setenv` baseline from **22 → 12**.
Pure test-hygiene; no production-code change.

`t.Setenv` auto-restores on cleanup **and** fails fast under
`t.Parallel` — the raw `os.Setenv` form is the latent-flake landmine the
ratchet exists to retire (cf. ag-jfzs).

## Sites migrated (10 eliminated)

| File | Δ | How |
|---|---|---|
| `internal/config/config_test.go` | -6 | Collapse the `origX :=
os.Getenv(...)` + `defer os.Setenv(... restore)` block into 3×
`t.Setenv` |
| `internal/goals/measure_test.go` | -1 | Delete the redundant
`origPath`/`defer os.Setenv` — the existing `t.Setenv("PATH","")`
already auto-restores |
| `cmd/ao/rpi_reliability_test.go` | -3 | Delete 3 redundant
restore-defers wrapped around existing `t.Setenv` calls |

## The remaining 12 are intentional (not migratable)

Documented inline in the ratchet script. They cannot use `t.Setenv`:
- **TestMain** (`cmd/ao/main_test.go`,
`internal/doctor/doctor_testmain_test.go`) — no `*testing.T` in scope
(4)
- **String-literal fixtures** (`internal/doctor/fix_cliconfig_test.go`)
— `os.Setenv` appears inside generated test-program source, not a real
call (2)
- **Unset-semantics helpers** (`cmd/ao/handoff_test.go`,
`internal/lifecycle/repo_readiness_test.go`,
`internal/wiki/locator_test.go`) — these `os.Unsetenv`+restore vars;
`t.Setenv` has no unset form (6)

## Verification

- `bash scripts/check-test-isolation.sh` → `PASS … os.Setenv=12/12`
- `go build ./...` ✓ · `go vet` (touched pkgs) ✓
- `go test ./internal/config/ ./internal/goals/` → 526 passed; touched
cmd/ao tests → 3 passed

Closes-scenario: ag-k38x#bulk-migration-setenv
Bounded-context: BC5-Runtime
Evidence: scripts/check-test-isolation.sh PASS 12/12
2026-06-06 10:17:21 -04:00
Bo ba2d0380b2 feat(eval): holdout-leak key-name backstop for outcomes compile (ag-uux93 #holdout-key-guard) (#770)
## What

Adds the key-name leak backstop that **ag-uux93** explicitly calls for
on `ao eval outcomes compile`: re-parse the emitted Outcomes rubric
payload and refuse any object key (at any depth) matching
`/target|ground_truth|expected_output|answer/i`.

## Why

The compile command itself already shipped (under sibling **ag-hdqu0** /
#601) with a value-based guard (`ContainsAny` scans for known holdout
*values* — layer 3). ag-uux93 additionally requires a **structural
key-name** backstop: "re-parse the emitted payload and assert no key
matches `/target |ground_truth|expected_output|answer/i`; abort +
non-zero exit if found."

This is **additive defense-in-depth** at a load-bearing boundary —
Managed Agents are not ZDR, so a holdout leak across the cloud boundary
is permanent. The allowlisted `Rubric` struct can't carry such a key
today; the guard *pins that invariant* so a future field that smuggles
ground truth fails closed instead of leaking silently.

## Changes

- `evalsubstrate.ScanForbiddenKeys(payload []byte)` — walks decoded JSON
for leak keys at any nesting depth
- `Rubric.HasForbiddenKey()` — marshals + scans (mirrors the existing
`ContainsAny`)
- Wired into `compileOutcomesRubric` after the value-scan, before return
(structured non-zero error on hit)
- Unit tests: positive (nested / case-insensitive / substring), clean
rubric, unparseable input, allowlist-by-construction invariant

## Verification

`cd cli && go build ./... && go vet ./... && go test` — build + vet
clean; evalsubstrate 92 pass, cmd/ao outcomes/rubric 30 pass; gofmt
clean.

## Scope note for maintainer

ag-uux93's spec also describes a `--suite/--split/--out` **flag
interface** reading locked YAML from `~/.agents/evals/`. The shipped
#601 command deliberately took a **JSON-input** design (`ao eval
outcomes compile <input.json>`) instead. I did **not** unilaterally
rewrite that working interface in an unattended tick — that's an
intentional design fork needing your call. Queued to `bo-queue`. This PR
closes only the `#holdout-key-guard` scenario.

Closes-scenario: ag-uux93#holdout-key-guard
Bounded-context: BC4-Evals
Evidence: cli/internal/evalsubstrate/holdout_guard_test.go
2026-06-06 10:17:18 -04:00
Bo 22543558a4 refactor(cli): convert beads verdict os.Exit calls to typed exit-error (ag-x22j #beads-exit-as-typed-error) (#769)
## What

`ao beads verify|lint|audit` used exit-code-as-verdict but signalled it
by calling `os.Exit(1)` **inside** RunE. `os.Exit` skips deferred
cleanup (temp files, lock release) and kills the test binary, so the
verdict branches were untestable.

This converts the 3 sites to return a typed `*beadsExitError`, which
`Execute()` maps to the process exit code at the single root exit point
— the established CLI idiom (mirrors `gateExitError` in `validate.go`
and `doctorExitError` in `doctor_surface.go`).

## Sites converted
- `runBeadsVerify` (`beads.go`) — stale citations → exit 1
- `runBeadsLint` (`beads.go`) — stale beads → exit 1
- `runBeadsAudit` (`beads_audit_cluster.go`) — `--strict` + flagged →
exit 1

## How
- New `beadsExitError{code}` type (`Error()` empty, `ExitCode()`), since
the verdict text already went to stdout.
- The returning RunE sets `cmd.SilenceErrors` (nil-guarded) so cobra
prints no spurious `Error:` line; **genuine** errors returned elsewhere
in those commands still print normally.
- `Execute()` gains a `beadsExitError` branch before the generic
fallback.

## Evidence
- `cli/cmd/ao/beads_exit_test.go` — pins the verdict contract: verify +
lint stale → `*beadsExitError` exit 1 + `SilenceErrors`; clean → nil;
nil-cmd no panic.
- `cd cli && go build ./... && go vet ./cmd/ao/` clean; `gofmt -l`
clean.
- Full `go test ./cmd/ao/` → **9314 passed**.

## Bead
`ag-x22j` (P3, child of the Go idiomatic audit `ag-tcf0`). No more
`os.Exit` outside `main`/root in the beads command paths.

Closes-scenario: ag-x22j#beads-exit-as-typed-error
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/beads_exit_test.go
2026-06-06 10:17:15 -04:00
Bo 5f69150169 test(cli): migrate curate_test.go os.Chdir to t.Chdir (ag-y4ac #curate-tchdir-w2) (#786)
## What
Collapse 7 identical `os.Getwd()` + `os.Chdir(tmpDir)` +
`t.Cleanup(restore)` blocks in `cli/cmd/ao/curate_test.go` to
`t.Chdir(tmpDir)`. Bare downstream `err =` first-uses promoted to `err
:=` (the removed block held the only function-scope `err`).

## Why
`t.Chdir` auto-restores cwd on cleanup **and** refuses to run under
`t.Parallel` — pre-empting the process-global-cwd flake class (cf.
ag-jfzs). This is one wave of the ag-k38x / ag-y4ac test-isolation sweep
(`finish-the-job`: ~133 sites already use the safe idiom).

## Scope / collision-safety
- `os.Chdir` in `_test.go`: **163 → 149 (-14)**. `os.Setenv` untouched
(owned by in-flight setenv ratchet **PR #772**).
- **`BASELINE_CHDIR` intentionally NOT lowered** — that single line is
the contention point with in-flight **PR #772/#773**; the ratchet stays
green because count-below-baseline is a NOTE, not a failure (`149 <=
163`). A later wave locks the baseline once those settle.
- Non-overlapping with PR #772 (rpi setenv) and PR #773 (dedup_test.go
os.Chdir).

## Verification
- `gofmt -l` clean · `go build ./...` · `go vet ./cmd/ao` clean
- `go test ./cmd/ao -run TestCurate` → 14 pass
- `scripts/check-test-isolation.sh` → PASS (os.Chdir=149/163,
os.Setenv=22/22)
- `scripts/pre-push-gate.sh --fast` → passed

Closes-scenario: ag-y4ac#curate-tchdir-w2
Bounded-context: BC5-Runtime
Evidence: .ntm/bo-queue/2026-06-06-ag-y4ac-pr-curate-tchdir.md
2026-06-06 09:44:51 -04:00
Bo f56f5b1521 chore(lint): clear 13 golangci-lint findings (ag-tcf0.1 #golangci-lint-clear) (#777)
## What

Clears all outstanding `golangci-lint run` findings in `cli/` — **13
issues** across 8 files. Pure mechanical lint cleanup, no behavior
change. (Several were hidden behind golangci-lint's default
`max-same-issues=3` display cap; running with caps disabled surfaced the
full set.)

| Linter | Count | Fix |
|---|---|---|
| errcheck | 7 | unchecked `f.Close()` → `defer func() { _ = f.Close()
}()` / `_ = f.Close()` |
| staticcheck S1016 | 2 | struct-literal copy → type conversion
(`reconcileCIRun(run)`, `reconcileBeadSummary(issue)`) |
| staticcheck QF1012 | 2 | `b.WriteString(fmt.Sprintf(...))` →
`fmt.Fprintf(&b, ...)` |
| staticcheck QF1001 | 1 | De Morgan: `!(a && b)` → `a' || b'` |
| copyloopvar | 1 | drop redundant `fx := fx` (Go 1.22+ per-iteration
loop var; go.mod is 1.26) |

Files: `loop_blocked.go`, `loop_next_work.go`, `cron_self_adjust.go`,
`reconcile.go`, `provenance_trace_test.go`, `internal/canon/ledger.go`,
`internal/scenarios/validate.go`, `internal/skills/catalog.go`.

## Verification

- `go build ./...` ✅  ·  `go vet ./...` ✅
- `golangci-lint run --max-same-issues 0 --max-issues-per-linter 0` →
**0 issues** ✅
- `go test ./internal/canon/... ./internal/scenarios/...
./internal/skills/...` → 116 passed ✅
- targeted `cmd/ao` tests (provenance/reconcile/cron/blocked/next-work)
→ ok ✅
- `scripts/pre-push-gate.sh --fast` → passed ✅

Closes-scenario: ag-tcf0.1#golangci-lint-clear
Bounded-context: BC5-Runtime
Evidence: golangci-lint run --max-same-issues 0 → 0 issues (verified
locally; CI re-runs the omnibus gate)
2026-06-06 09:44:48 -04:00
Bo 8611b71f0f feat(corpus): auto-snapshot before destructive defrag prune (ag-32xk #defrag-autosnapshot) (#771)
## What

`ao defrag` now takes an **automatic, recoverable corpus snapshot**
before it applies a destructive `--prune` (anything that deletes from
`.agents/`). It reuses the same tar.gz machinery as `ao corpus
snapshot`, so an over-aggressive cleanup is always rehydratable via `ao
corpus restore`.

## Why (ag-32xk — moat durability)

The corpus moat is switching-cost / data-gravity, not tech — and it is
one accidental delete away from losing its receipts. Directive #11
history: routine maintenance wiped `.agents/` subdirs, counts went to
~0. A moat you can lose to routine cleanup doesn't feel like a moat.
This closes the most common loss path (the destructive defrag) first.

## Behavior

- **Apply-mode prune** → snapshot first, then prune.
- **Safe-by-default**: a snapshot failure *aborts* the prune — we refuse
to delete files we couldn't first back up.
- **No-op** for: dry-runs (nothing deleted), dedup-only runs (flags,
never deletes), and `--no-snapshot` (new escape hatch).

## Changes

- `corpus_snapshot.go`: extract `createCorpusSnapshot()` as the shared
engine behind both the `ao corpus snapshot` command and the defrag
auto-snapshot (no behavior change to the command).
- `defrag.go`: `autoSnapshotBeforeDefrag()` + `--no-snapshot` flag +
help text.
- `defrag_autosnapshot_test.go`: 6 tests (apply-prune snapshots;
dry-run/no-snapshot/dedup-only skip; missing-corpus aborts; L2
`runDefrag` leaves a recoverable tarball).
- `cli/docs/COMMANDS.md`: regenerated for the new flag (passes
`generate-cli-reference.sh --check`).

## Scope

Slice of ag-32xk. **Remaining (bead stays open):** `ao curate`
auto-snapshot + a canon/promoted-entry delete guard.

## Verification

- `cd cli && go build ./... && go vet ./cmd/ao/` — clean
- `go test ./cmd/ao/` — 9315 pass
- `scripts/pre-push-gate.sh --fast` — passed (CLI docs parity green)

Closes-scenario: ag-32xk#defrag-autosnapshot
Bounded-context: BC1-Corpus
Evidence: cli/cmd/ao/defrag_autosnapshot_test.go
2026-06-06 09:05:37 -04:00
Bo 784fcfca4e fix(test): convert goals integration lifecycle to subprocess isolation (ag-x2gv #flaky-goals-integration) (#762)
## Summary
- Convert `TestGoals_Integration_FullLifecycle` from in-process
`rootCmd.Execute()` + `captureStdout` + `chdirTemp` to subprocess
`exec.Command(bin, ...)` with isolated `t.TempDir()`
- Eliminates process-global state sharing (`os.Chdir`, `os.Stdout`,
cobra `rootCmd`) that caused intermittent empty-stdout failures under
`-race -shuffle=on`
- Same fix pattern as ag-z8c4 (PR #761) which fixed the analogous
`TestFlagMatrix_QuietMode` flake

## Test plan
- [x] 20/20 passes in isolation (`-run
TestGoals_Integration_FullLifecycle -race -shuffle=on -count=20`)
- [x] Full package suite green (`go test -race -shuffle=on -count=1
./cmd/ao/`)
- [x] `go vet ./cmd/ao/` clean
- [x] `go build ./cmd/ao/` clean

Closes-scenario: ag-x2gv#flaky-goals-integration
Bounded-context: BC5-Runtime
Evidence: 20/20 pass with -race -shuffle=on -count=20
2026-06-06 08:56:10 -04:00
Bo 6489da8fab chore(cli): gofmt agent_bundle.go struct-literal indentation (ag-tcf0.2 #gofmt-agent-bundle) (#783)
Fixes a gofmt drift that landed on `main`: `cmd/ao/agent_bundle.go` had
an extra-tab indent on the `Bootstrap` field of the codex-ntm bundle
struct literal (unformatted since df3cc98, ~24h ago). Whitespace-only —
the `Bootstrap` string value is unchanged, behavior identical.

## Why it slipped through
CI runs `golangci-lint run` with **default linters**, which exclude the
`gofmt` formatter, so source-formatting drift is not gated and lands on
`main` undetected. This is the same class as the ag-tcf0 audit "14 files
unformatted" finding; the earlier sweep (ag-ozac) fixed files but did
not add an enforcement gate. **The missing gofmt CI gate is drafted to
the operator queue** (it interacts with the gate-target/path-filter
system and the active ag-tcf0.1 golangci worktree, so it warrants
operator confirmation rather than a unilateral CI-gate change).

## Test
The paired `agent_bundle_test.go` change strengthens the existing
`Bootstrap` substring assertion to **exact equality** (per
`.claude/rules/go.md`: "assert exact expected values, not just
substring"), guarding the struct literal against silent drift at the
exact site of the fix.

- `gofmt -l cli/` clean
- `go build ./...`, `go vet ./cmd/ao/` ok
- `go test ./cmd/ao/ -run TestBuildAgentBundle_CodexNTM` pass (8 bundle
tests green)
- `scripts/pre-push-gate.sh --fast` passed

Closes-scenario: ag-tcf0.2#gofmt-agent-bundle
Bounded-context: BC5-Runtime
Evidence: pre-push-gate --fast passed; gofmt -l clean; bundle tests
green
2026-06-06 08:06:44 -04:00
Bo a6958e6ff0 feat(memory): enforce reach always error budget (#752)
## Summary
- enforce a computed `reach=always` error budget over established canon
learnings during maturity reconciliation
- immediately demote independently refuted canon entries to `pull` while
preserving budget and archiving the demoted artifact
- spend budget on independent harmful outcomes and 45-day citation
droughts, reset budget on fresh cross-engineer positive citations, and
carve out foundation contracts from blind drought spends
- append provenance ledger evidence for runtime demotions

## Validation
- PASS: `cd cli && go test ./cmd/ao -run
'TestMaturity_reachAlwaysBudget|TestMaturity_runMaturityScanAll_reconcilesHelpfulCitationBeforeApply|TestMaturity_runMaturityScanAll_retrievedCitationDoesNotChangeUtility'
-count=1`\n- PASS: `cd cli && go test ./cmd/ao -run
'TestMaturity_reachAlwaysBudget' -count=1`\n- PASS: `cd cli && go test
./cmd/ao -count=1`\n- PASS: `cd cli && go test ./internal/lifecycle
./internal/provenancegraph ./internal/search ./internal/ratchet
-count=1`\n- PASS: `git diff --check`\n- RAN: `bash
scripts/regen-all.sh` (known CMDAO `wiki query` red only; restored
timestamp-only registry drift)\n- RAN: `PRE_PUSH_FAIL_FAST=false bash
scripts/pre-push-gate.sh` (blocked on known main-red set: fixture
parity, goals validate, corpus freshness,
contract-canaries/pre-push-bats governance, dirty canonical
root/worktree disposition)\n\n## Coordination\n- Bead: ag-v81n\n-
Cross-model review required before merge; no self-grade.\n
2026-06-06 05:07:56 +00:00
Bo df3cc9803b fix(memory): reconcile W6 cutover commands
Reconcile W6 memory cutover surfaces so runnable/doc commands match the real CLI. Cross-model ACKs: WindyCastle Agent Mail #225, RubyMoose Agent Mail #226. Required checks green; Cursor Bugbot non-required pending under recorded exception.
2026-06-05 04:50:49 -04:00
Bo ec11c9a7f5 fix(memory): relabel negative citation feedback
Relabel harmful/refuted citation feedback decisions as penalized while preserving the reward=0 soft-penalty behavior. Cross-model ACKs: WindyCastle Agent Mail #220, RubyMoose Agent Mail #221. Required checks green; Cursor Bugbot non-required pending under recorded exception.
2026-06-05 04:31:18 -04:00
Bo d135c56f5c feat(memory): reconcile citation outcomes before maturity
Implements ag-sx9c W3.2. Cross-model ACKs recorded in Agent Mail: WindyCastle #208 and RubyMoose #209. Required checks green; merge proceeds under the recorded Cursor Bugbot non-required instability exception.
2026-06-05 03:47:54 -04:00
Bo 466842d9bd feat(memory): add citation outcome taxonomy (#741)
## Summary

Implements `ag-rbx8` W3.1 citation outcome taxonomy and reviewer
identity plumbing for memory feedback.

- Adds canonical citation types: `retrieved`, `used-in-final-artifact`,
`helpful`, `harmful`, `refuted`, while preserving legacy `applied`,
`reference`, and `skill_loaded`.
- Extends citation events with `artifact_author_id`,
`cited_by_agent_id`, and `cited_by_model_family`.
- Parses legacy identity aliases in citation JSONL (`author_id`,
`reviewer_id`, `judge_id`, `model_family`).
- Updates `ao metrics cite` with non-author identity flags and rejects
self-authored outcome judgments.
- Teaches citation pipeline and feedback-loop logic that retrieved-only
is exposure, helpful/final-artifact/applied are rewardable, and
harmful/refuted are explicit non-reward outcomes.
- Regenerates CLI command docs and `registry.json`.

## Validation

Focused tests after rebase over #737 (`origin/main` @ `7bbc6569`):

- `go test ./internal/types ./internal/ratchet ./internal/quality
./internal/lifecycle`
- `go test ./cmd/ao -run
'TestMetricsCite|TestProcessCitationFeedback|TestCitationConfidenceBuckets|TestValidateFeedbackCitationType|TestFlagCompletions_Registered|TestComputeCitationPipeline|TestCanonicalCitationType'`
- `go test ./cmd/ao`
- `git diff --check`

Required regen/gate:

- `bash scripts/regen-all.sh` completed generated artifact updates, then
exited on known CMDAO surface parity red (`wiki query`; 30 uncovered
commands).
- `PRE_PUSH_FAIL_FAST=false bash scripts/pre-push-gate.sh` completed
with 5 known non-diff failures:
  - test-fixture parity: `scripts/gc-stale-worktrees.sh`
  - worktree disposition: canonical root dirty/untracked operator state
  - goals validate: local `GOALS.md` lookup red
  - corpus freshness: newest local snapshot over threshold
- contract canaries: known `agentops-core.pre-push-gate-governance`
failure

Diff-owned checks passed in the gate, including go build, go vet,
changed-scope race tests, command/test pairing, registry drift, CLI docs
parity, mkdocs strict build, contract compatibility, CI policy parity,
context-map drift, shellcheck, skill citation parity, and
executable-spec link integrity.

## Review Gate

No self-grade: requesting cross-model review from WindyCastle and
RubyMoose before merge.

Bead: `ag-rbx8`
2026-06-05 02:00:10 -04:00
Bo 0c5554f1bc build(factory): one-shot regenerator for ao-command-landing surfaces (ag-jy12 #command-regen) (#742)
## What

A ONE-SHOT regenerator (`scripts/regen-command-surfaces.sh`) for the
`ao`-command-landing derived surfaces. When an `ao` command is
added/removed/renamed, these surfaces must update in lockstep — the
multi-surface tax that bit #720 (ag-sz3h, ~4 extra CI rounds):

- the two cobra `expectedCmds` literals in
`cli/cmd/ao/cobra_commands_test.go`
- the `cli-command-surface` heading counts:
`evals/agentops-core/fixtures/cli-command-surface-smoke.sh` +
`evals/agentops-core/cli-command-surface-matrix.json`

## How

- **Single source of truth for the command set**: the LIVE cobra tree,
dumped by the new `TestDumpRegisteredTopLevelCommands` (set
`AO_DUMP_REGISTERED_CMDS=1`). It walks `rootCmd.Commands()` (sorted,
`help` excluded) — exactly the membership the two `expectedCmds`
literals must match. A *test*, not a subcommand, so emitting the list
does not itself add a registered command (which would be circular).
- **Single source of truth for the counts**: `cli/docs/COMMANDS.md`
(itself generated from the cobra tree). `top = ### \`ao `, `sub = ####
\`ao `, `all = #{3,4} \`ao `.
- Rewrites all three derived surfaces deterministically. The
`expectedCmds` literals are reformatted one-element-per-line
(gofmt-stable) so output is canonical forever — **membership is
identical to before (pure reformat)**.
- **Idempotent**: a no-change tree is a no-op; `--check` fails on drift
without writing.
- Wired into `regen-all.sh` (both regen + `--check`) right after the
COMMANDS.md step.

## Deletion ⊃ Addition (completeness note)

Removing/renaming a command is a SUPERSET of adding one. This
regenerator covers the ADD surfaces. A delete/rename ALSO disturbs
surfaces it does NOT auto-regenerate — these need manual/quorum
attention:

- (a) codex-contract gates that hardcode command literals
(`validate-codex-rpi-contract.sh`)
- (b) skills-codex twins/overrides (`export-claude-skills-to-codex` +
`regen-codex-hashes`)
- (c) VERBATIM-PRESERVE template SHAs (`cron-loop-mode.md` frontmatter)

A "deletion ⊃ addition" note is present in the regenerator header
comment AND the `regen-all.sh` operator reminder.

## Verification

- `scripts/regen-command-surfaces.sh` → no-op on clean tree; `--check`
green.
- `go build ./cmd/ao`, `go vet ./cmd/ao`, `go test ./cmd/ao` (full
package) all pass; `gofmt -l` clean.
- `cli-command-surface-matrix` canary: pass. Smoke fixture: pass.
- Pre-push gate's only reds are pre-existing, unrelated canaries
(`context-packet-ab-wave0`, `agentops-core.pre-push-gate-governance`
bats) — not touched by this diff.

Closes-scenario: ag-jy12#command-regen
Bounded-context: BC4-Factory
Evidence: scripts/regen-command-surfaces.sh
2026-06-05 05:15:41 +00:00
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 853d781e66 feat(memory): inject canon memory during session bootstrap (#740)
## Summary

Implements ag-11bi W2.3 canon-gated T2 bootstrap memory for `ao session
bootstrap`.

- Selects only canon-promoted established learnings plus high/critical
severity canon anti-patterns from `.agents/canon/learnings`.
- Ranks by `utility * confidence * freshness`, caps selection at 1200
estimated tokens, and warns on stderr when eligible canon memory exceeds
the cap.
- Computes reach through the existing search reach helper, so
established canon memory surfaces as `reach=always` without author-set
reach.
- Emits the memory payload in JSON/robot output and prints selected
memory in the default human bootstrap output.
- Extends `schemas/session-bootstrap.v1.schema.json` and tests the
filter, budget, warning, and human-output paths.

## Validation

Focused checks:

- `go test ./cmd/ao -run 'TestSessionBootstrap'`
- `go test ./internal/search ./internal/types`
- `git diff --check`
- `python3 -m json.tool schemas/session-bootstrap.v1.schema.json`

Required regen:

- `bash scripts/regen-all.sh` regenerated artifacts but exited on the
known CMDAO `wiki query` parity red.

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
Go race tests, command/test pairing, goals validate, registry drift, CLI
docs parity, command-surface matrix canary, schema checks, CI policy
parity, and shellcheck.

## Review Ask

Requesting cross-model ACK/AMEND/BLOCK before merge. Primary attacks to
run:

- Does any non-canon or candidate/provisional learning enter bootstrap
memory?
- Can the 1200-token cap be bypassed or hidden from callers?
- Does the default human path actually expose the selected memory, not
just JSON?
- Is the generated CLI-surface drift acceptable as regen output on this
branch?

No self-grade for merge.
2026-06-04 23:31:05 -04:00
Bo 01dc1568cf feat(memory): compute always reach from canon maturity
Implements ag-oqha W2.2. Authored reach=always is sanitized to pull; effective always is computed only from canon-collected maturity=established learnings. Cross-model ACKs recorded in Agent Mail messages #70 and #71.
2026-06-04 21:16:16 -04: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 092b99dad9 feat(skills): add skill-eval.sh ms-backed quality gate (ag-yzoz #skill-eval) (#726)
## What

Adds `scripts/skill-eval.sh` — a thin gate that runs one skill's
`SKILL.md` through Jeff Emanuel's `ms` (meta_skill) `lint` + `validate`.
The wrapper decides the gate purely on whether a **blocking** `rule_id`
appears (ms's own severity/exit is not trusted):

- Blocking: `no-secrets`, `no-injection`, `safe-paths`,
`required-metadata`, `no-cycle`, `valid-version` → non-zero exit.
- Everything else → annotated only (`::warning::`), never fails.
- `ms` not on PATH (or `MS_BIN` missing) → **loud `::error::` hard-fail
(exit 3)**, never skip-and-pass — a silent skip would recreate the
no-evaluation gap this gate closes.

Planted `skills/_fixtures/{bad,good}-skill/SKILL.md` fixtures +
`tests/scripts/skill-eval.bats` (10 tests) exercise the
bad/good/ms-absent contracts.

## Fixture scoping

`skills/_fixtures/` is non-skill scaffolding (planted fixtures). The
skill-enumerating gates/generators are taught to skip `skills/_*` so the
fixtures never inflate the skill count (stays 82) or fail registry /
schema / wiring / runtime / count gates. Touched enumerators:
`generate-registry`, `validate-skill-schema`, `sync-skill-counts`,
`validate-skill-count`, `generate-skill-catalog`,
`generate-context-map`, `validate-manifests`, `check-wiring-closure`,
`validate-headless-runtime-skills`, `lib/sku_catalog.py`,
`check-alias-collisions`, `run-all`, `test-runtime-opencode-smoke`,
`release-smoke-test`, `smoke-test`, `validate-skill`.

Does **not** touch `validate.yml` — wiring skill-eval.sh into CI is a
separate bead.

Epic: ag-czzf

## Verification

- `bats tests/scripts/skill-eval.bats` → 10/10 pass (ms 0.1.2 on PATH)
- `shellcheck scripts/skill-eval.sh` → clean
- `check-registry-drift`, `validate-skill-schema`, `sync-skill-counts`,
`run-all`, opencode-smoke → all PASS with fixtures present, skill count
= 82 (fixtures excluded)

Closes-scenario: ag-yzoz#skill-eval
Bounded-context: BC2-Validation
Evidence: scripts/skill-eval.sh

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-04 16:38:57 -04: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 288943ad49 refactor(cli): decouple mine.go + handoff.go from rpi symbols (ag-362v #decouple) (#718)
**Prerequisite for removing `ao rpi` / `ao evolve`** (epic ag-llni /
soc-1gbpz). Operator directive: out-of-session orchestration moves to
the substrate (NTM + background agents + `/discovery`); the in-CLI loop
engines are removed.

A full-repo map confirmed the **only 2 non-rpi/evolve production
callers** of rpi symbols:
- `mine.go` → `RPIC2Event` + `loadRPIC2Events`
- `handoff.go` → `shellQuote`

This PR relocates both into files that survive the `rpi*.go` deletion
glob, **zero behavior change**:
- **`shell_quote.go`** — POSIX single-quote helper moved out of
`rpi_parallel.go` (body inlined, drops the `internal/rpi.ShellQuote`
hop). Both `handoff.go` and `rpi_parallel.go` keep using it.
- **`c2_event_reader.go`** — `RPIC2Event` type + `loadRPIC2Events`
reader moved out of `rpi_c2_events.go` (events.jsonl path inlined to
mirror `internal/rpi.RPIRunRegistryDir`). `mine.go` unchanged; the
rpi-internal **writer** `appendRPIC2Event` stays and is deleted with the
engine.

After this, **nothing outside `rpi*.go`/`evolve*.go` references any
rpi-internal symbol** — the engine deletion (next PR) is unblocked.

## Verification
- `go build ./...` + `go vet` clean
- 3 new reader tests + 21 existing handoff/mine/c2/shellQuote tests pass
- `rg` confirms `mine.go`/`handoff.go` carry no rpi-internal symbol refs

Closes-scenario: ag-362v#decouple
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/c2_event_reader.go

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-06-04 10:15:51 -04:00
Bo 6bd788d8af chore(cli): gofmt sweep — 14 unformatted files (ag-ozac #gofmt-sweep) (#717)
Mechanical `gofmt -w` across `cli/` — fixes the 14 unformatted files
surfaced by the **ag-tcf0** Go-cohesion audit. No logic change (diff is
whitespace/grouping only; `git diff -w` is ~empty).

## Verification
- `gofmt -l cli/` → empty
- `go build ./...` → passes
- Rebased onto current `origin/main`

Closes-scenario: ag-ozac#gofmt-sweep
Bounded-context: BC5-Runtime
Evidence: cli/internal/eval/types.go
2026-06-04 09:11:11 -04:00
Bo 81aec49b1c chore(tests): test-isolation ratchet + migrate testutil chdir helpers to t.Chdir (ag-k38x #lint-ratchet) (#699)
## What
Lands the durable mechanism for ag-k38x (codebase-audit P2-1/P2-2): raw
`os.Chdir`/`os.Setenv` in tests mutate process-global cwd/env — a latent
flake landmine once any test opts into `t.Parallel` (cf. **ag-jfzs**,
the memory-sync flake of exactly this class).

The repo has ~163 `os.Chdir` + 22 `os.Setenv` inline sites across 34
files in **non-uniform patterns** (`defer` / `t.Cleanup` / `defer
func(){}()` with reversed order) — a blind sweep would break the suite.
So this ships the **durable gate + the canonical lever**, not a risky
80-site mass-edit:

- **`scripts/check-test-isolation.sh`** — a baseline ratchet (163/22)
wired into the **process-hygiene** job: raw `os.Chdir`/`os.Setenv` in
`*_test.go` may not **grow**; new code must use `t.Chdir`/`t.Setenv` or
the testutil helpers. This pre-empts the flake class from spreading.
- **`testutil_test.go`** — `chdirTemp`/`chdirTo` migrated from
`os.Chdir`+`t.Cleanup` to `t.Chdir` (the shared helpers most tests
delegate to; canonical example).

## Follow-up
The bulk inline-site migration drives the baseline toward zero
incrementally — **ag-k38x stays open** for it (scenario
`#bulk-migration`). Decomposing an ~80-site, non-uniform migration into
*gate-the-growth now + reduce-incrementally* is the responsible path; a
one-shot sed would be the reckless one.

## Verification
Ratchet PASSes at baseline AND catches a synthetic increase; shellcheck
+ `validate-ci-policy-parity` green; `go vet` clean; chdir-helper-caller
tests pass under `-race`.

Closes-scenario: ag-k38x#lint-ratchet
Bounded-context: BC2-Validation
Evidence: scripts/check-test-isolation.sh
2026-06-03 15:14:36 +00:00
Bo 28b18a8523 fix(corpus): handle silent G104 parse/write swallows (ag-chvc #silent-error-handling) (#697)
## What
Fixes the behavioral `#nosec G104` silent-error swallows surfaced by the
codebase-audit (ag-chvc).

- **Sscanf** (`lifecycle/temper.go` ×3, `cmd/ao/temper.go` ×4,
`storage/searchindex.go`): a *malformed*
Utility/Confidence/SchemaVersion silently parsed to **zero**, corrupting
decay-rank inputs. Now parse into a local and assign only on a clean
parse → malformed value keeps the prior default. The error is handled,
so the `#nosec G104` is gone.
- **WriteFile/MkdirAll** (`cmd/ao/quickstart.go`, `cmd/ao/demo.go` ×6):
silently dropped write failures while still printing ✓. Now warn to
stderr on failure, print ✓ only after the write lands.
- **Left untouched** (legitimate best-effort): `rpi_phased_processing`
`os.Remove` cleanup, demo interactive `Scanln`.

## Verification
`go build` + `go vet` clean; 276 lifecycle/storage tests pass under
`-race`; 3 new regression tests (malformed → keeps default; valid →
parses). Zero G104 left in the 5 touched files.

Closes-scenario: ag-chvc#silent-error-handling
Bounded-context: BC1-Corpus
Evidence: cli/internal/lifecycle/temper.go
2026-06-03 12:46:26 +00:00
Bo 3237679495 fix(memory): missing sessions dir no-op + isolate flag-matrix memory-sync test (ag-jfzs #memsync-test-isolation) (#695)
## What
Fixes the ag-jfzs flake: `TestFlagMatrix_QuietMode/memory-sync` ran `ao
memory sync` against the real repo root, mutating the shared repo-root
MEMORY.md under `-race` (flaky), and `ao memory sync` hard-errored when
`.agents/ao/sessions` was absent (clean checkout / CI).

## Changes
- `memory.go`: `readNLatestSessionEntries` treats a missing sessions dir
as no data (`os.IsNotExist` -> `nil, nil`) — sync is a clean no-op
instead of erroring.
- `flag_matrix_test.go`: redirect the memory-sync subprocess write to a
per-test `t.TempDir()` via `--output-file` — never touches the real repo
MEMORY.md.
- `memory_test.go`: `TestReadNLatestSessionEntries_NoDir` asserts the
no-op contract.

## Verification
- `go vet` clean
- `TestFlagMatrix_QuietMode` 15/15 under `-race -count=5`
- 80 memory/flag-matrix tests pass under `-race`

This is the recurring red on main's `correctness(ubuntu)` — should make
it reliably green.

Closes-scenario: ag-jfzs#memsync-test-isolation
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/memory.go
2026-06-03 11:47:07 +00:00
Bo fbc80c68b9 feat(canon): tier-aware promotion gate (ag-n2r4 #heuristic-earns-on-citations) (#675)
## What

gap#2 — **not all knowledge is mechanically verifiable.** A learning
declares `canon_tier:` in its frontmatter; the promotion gate branches
on it:

| Tier | Requirement | Why |
|---|---|---|
| `falsifiable` (default) | 1 cross-engineer citation **AND** 1
independent verification | claims about code/gates are checkable — and
must be checked |
| `heuristic` | 3 cross-engineer citations, no verification | judgment
can't be mechanically verified; breadth of independent reuse is the only
honest trust signal |

- **Default is the stricter tier** (`falsifiable`) — `heuristic` is an
explicit opt-out, so an unmarked learning can't dodge verification by
omission.
- Refutation **hard-blocks both** tiers; unknown tier (no path) falls
back to strict falsifiable.
- `status`/`promote` surface the tier; `EvaluateEntry` resolves it from
the learning file.

## Verification
- 8 new unit tests (TierOf default/garbage/missing, per-tier thresholds,
heuristic-earns-on-3-citations, falsifiable-still-needs-verification,
unknown-path-strict) + a `cmd/ao` CLI test; canon package green (43)
- e2e: heuristic EARNED on 3rd citation (0 verifs); falsifiable PENDING
on 3 citations until verified
- pre-push gate clean except the pre-existing bd-closeout/AGENTS.md
drift (untouched file, out of scope)

Closes-scenario: ag-n2r4#heuristic-earns-on-citations
Bounded-context: BC1-Corpus
Evidence: cli/internal/canon/tier.go
2026-06-02 01:49:51 -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 f47a7c5727 fix(test): canonicalize chdirTemp paths for macOS symlink resolution (ag-vcu4 #chdir-temp-macos-symlink) (#670)
## Summary

- Return `os.Getwd()` from `chdirTemp()` instead of the raw
`t.TempDir()` path so test fixture paths match production `Getwd()`
output on macOS (`/var` → `/private/var` symlink resolution)
- Fixes `TestCronSelfAdjust_RoundTrip` failure where `RenderedTemplate`
in cron history did not match the expected path
- Central fix in the shared test helper benefits all 30+ callers; full
`go test ./...` green locally (12109 tests)

## Test plan

- [x] `go test -count=1 -run TestCronSelfAdjust ./cmd/ao/` — 5 passed
- [x] `go test -count=1 ./...` — 12109 passed, 66 packages
- [x] `go vet ./...` — clean
- [ ] CI validate workflow green on push

Closes-scenario: ag-vcu4#chdir-temp-macos-symlink
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/testutil_test.go


Made with [Cursor](https://cursor.com)
2026-05-31 21:17:09 -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 d543ab91e7 fix(rpi): skip batch-consumed entries in next-work materialize (ag-mjlg #batch-consumed-skip) (#652)
## What

`ag-9jle.3` shipped `ao next-work materialize` with an idempotency gap.
`isMaterializable()` checked only the **per-item**
`NextWorkItem.Consumed` flag, but the real `.agents/rpi/next-work.jsonl`
records consumption at the **batch level** (`NextWorkEntry.Consumed` /
`consumed_by`, set after the `items[]` array). Every
historically-consumed entry therefore looked fresh.

**Impact:** a dry-run against the live queue planned **44 beads** when
only **16 items are genuinely available** — ~28 stale duplicates for
work done months ago (the `soc-9xn0` AOP-CLAIM audit, practice-backfill
passes 2–12, etc.). Running `ao next-work materialize` as-is would have
flooded the tracker.

## Fix

- Gate `enumerateMaterializeCandidates` on entry-level consumption via a
new `isEntryConsumed()` helper that mirrors the entry half of the
existing `rpi.IsFullyConsumed`.
- The per-item `isMaterializable` checks remain as a second gate.

## Why the original test missed it

`TestNextWorkMaterialize_SkipsConsumedAndHeld` set `consumed` at the
**item** level — a shape the real queue never uses. Classic test-shape
mismatch giving false confidence.

## First failing test (regression contract)

> Given a next-work.jsonl entry whose batch-level consumed is true with
an item that has no item-level consumed flag · When materialize
enumerates candidates · Then that entry's items are skipped and produce
zero bd-create calls.

`TestNextWorkMaterialize_SkipsBatchConsumedEntry` — **fails without the
gate** (creates 1 stale bead), passes with it. Proven by reverting the
gate and re-running.

## Evidence

- Real-queue dry-run: **44 → 16** planned beads.
- `go build ./...`, `go vet ./cmd/ao/`, full materialize suite (6 tests)
all green.

Closes-scenario: ag-mjlg#batch-consumed-skip
Bounded-context: BC5-Runtime
Evidence: cli/cmd/ao/next_work_materialize_test.go
2026-05-31 14:54:20 -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 81c9f305ac feat(ci): witness Dolt->JSONL re-derive + hash-compare (ag-lmdx.3 #witness-crosscheck) (#656)
## What

The **watcher that watches the watcher** (ag-lmdx.3). A blocking CI gate
that re-derives the hash-chained JSONL witness **from the Dolt
projection** and hash-compares it against the git-committed witness
ledger — proving Dolt is a *faithful projection* of the canonical event
log.

This is the **inverse** of drrebuild (#646): drrebuild rebuilds the Dolt
context graph FROM the witness (JSONL + git blobs); this rebuilds the
witness FROM Dolt rows. Together they pin both ends of the projection.
Dolt history is rewritable (reset/--force/root) so it is NOT
tamper-evidence; the git-anchored JSONL witness is. A Dolt rewrite that
did not also rewrite the committed witness is caught here, and nowhere
else.

## How

- `cli/internal/drwitness/` — re-derive + cross-check core. Reuses
drrebuild's exact hash discipline (`ComputeEventHashes`: payload_hash =
sha256(canonical-JSON), hash = sha256(payload_hash + "\n" + prev_hash))
and canonical edge ordering. Re-derivation is order-independent
(canonical sort then re-seal), which is what makes the hash-compare
meaningful.
- `cli/cmd/witness-crosscheck/` — standalone helper main (like
`cmd/skill-frontmatter-json`), **NOT** an `ao` subcommand (no CLI
surface added).
- `scripts/witness-dolt-jsonl-crosscheck.sh` — hermetic CI gate. CI has
no live Dolt, so it drives committed fixtures through the helper and
asserts the gate fires correctly.
- `tests/scripts/` + `tests/fixtures/witness/` — bats behavioral proof.

## Scenarios (acceptance)

- **Clean state passes** — the faithful Dolt projection re-derives to
the committed witness; gate exits 0.
- **Tampered Dolt fails** — a rewritten Dolt row no longer matches the
committed witness head; helper reports `DIVERGED`, gate exits non-zero.

## Verification

- `go build ./... && go vet ./... && go test ./...` (full suite: 11948
passed / 71 pkgs; drwitness: 7/7)
- `bats tests/scripts/witness-dolt-jsonl-crosscheck.bats` — 6/6
- `shellcheck -S warning` — clean
- `scripts/validate-ci-policy-parity.sh` — PASS
- diff is scope-only (10 files, additions); the sibling wave-pregate
(#655) is present and untouched.

Closes-scenario: ag-lmdx.3#witness-crosscheck
Bounded-context: BC4-Factory
Evidence: scripts/witness-dolt-jsonl-crosscheck.sh
2026-05-31 14:39:13 -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 31efa5acf4 feat(eval): warn when judge_content_hash parity goes unchecked on ingest (ag-ejet) (#629)
## What

Closes the **second and last open caveat from the 2026-05-30 council
post-mortem (WARN)**: `--expect-judge-hash` (gate #2, #627) is fully
opt-in, so a score graded against a **drifted rubric** ingests
**silently** if the caller omits the flag.

Now, when a score carries a non-empty `judge_content_hash` but no
`--expect-judge-hash` was configured, `ao eval outcomes ingest` emits a
**gate #2 advisory to stderr** (non-fatal) naming the hash and how to
enforce parity. The unenforced-parity gap is **loud instead of silent**
— the council's sanctioned minimum bar — without requiring the operator
to remember a flag.

Full auto-resolution of the active suite hash (so parity enforces
automatically, not just warns) remains a follow-on — it needs a
suite-hash resolution path, tracked on ag-62g68.

## Scope

Behavior-only — **no new flag**, so `COMMANDS.md`/registry are
unchanged. Two files (impl + test).

## Evidence
- `TestUncheckedJudgeHashWarning` (4 cases: hash+no-expect warns;
hash+expect / no-hash / no-hash+expect do not).
- `TestRunEvalOutcomesIngest_WarnsWhenParityUnchecked` — command-level:
warns on stderr, still emits a verdict; matching `--expect-judge-hash` →
no warning.
- `go build ./...` ✓ · `go test ./cmd/ao/` 9235 pass ✓ · `go vet` clean
· gocyclo no findings.

Council caveats now both retired: #1 (gate #3 unwired → #628) and #2
(gate #2 silent → this PR). Verdict:
`.agents/council/2026-05-30-evolve-624-627-postmortem/verdict.md`.

Closes-scenario: ag-ejet#gate2-warn-when-parity-unchecked
Bounded-context: BC4-Evaluation
Evidence: cli/cmd/ao/eval_outcomes_ingest.go
2026-05-30 08:22:45 -04:00