Commit Graph

2236 Commits

Author SHA1 Message Date
Bo 88cf6b0be1 fix(cli): migrate the two real JSONL readers to the blessed storage helpers
Closes the RG-KEEP failure by making the TREE stop tripping the heuristic
rather than by touching the snapshot. Both files mention a .jsonl path and both
invoked a raw bufio.NewScanner, so a normal --regenerate pinned them and grew
the grandfather 5 -> 7. Neither now invokes a raw scanner, so regeneration
reproduces the committed five-entry file exactly.

evidence/citations.go — LoadCitations keeps its own os.Open, so the
"open citation ledger: %w" wrapping and the (nil, nil) missing-ledger case are
untouched; only the scanner becomes storage.ScanJSONL. A read error is still
wrapped "scan citation ledger: %w" and still returns the PARTIAL citations
collected before it. Buffer policy widens from the local 1MB cap to the storage
package's 8MB cap with a loud ErrLineTooLong instead of silent truncation,
which is the point of the blessed helper.

provenancegraph/store.go — DecodeEdges keeps its line numbering and its exact
"ledger line %d: invalid JSON: %w" / "scan ledger: %w" errors. storage.ScanJSONL
cannot be aborted from its callback, so the first malformed line is captured and
later lines are skipped instead of decoded; the returned value is identical to
the previous early return, and the first error in stream order still wins over
any later scan failure.

No import cycle: cli/internal/storage depends on neither package (go list
-deps). No behavior change in either package's tests.

scripts/.jsonl-scanner-grandfather is NOT touched and is byte-identical at
a15f326f172e0346aadc17c0f4498861eb61e8c07df88c6e554f214bc90beb8e; neither the
gate script nor scripts/lib/ratchet.sh is modified.

Claude-Session: https://claude.ai/code/session_013fqQZAMZVFswA3oFrCDAFf
2026-07-28 01:12:08 -04:00
Bo 0ba51a5f6c Merge commit 'c338e0086e8defd7b6f8ed1fa0c1f0668136a7ea' into codex/skill-overhaul-20260724 2026-07-27 23:15:14 -04:00
Bo 88c2692f2b Merge remote-tracking branch 'origin/main' into codex/skill-overhaul-20260724
# Conflicts:
#	skills-codex/rpi/scripts/run_once.py
#	skills-codex/rpi/tests/test_run_once.py
#	skills/rpi/scripts/run_once.py
#	skills/rpi/tests/test_run_once.py
2026-07-27 22:58:07 -04:00
Bo f66ab953af test(gates): require every blocking gate to prove it can fail (#996)
P3 from the 2026-07-25 review: fix the inert-check CLASS, not just the two
instances #995 patched.

A gate verified only against a green tree is indistinguishable from `exit 0` --
delete its detector and CI stays green. Measured across the registry: 32 of 54
BLOCKING script-backed gates had no test anywhere under tests/ demonstrating
they FAIL on what they detect. 17 had no test at all, including skill.schema
and skill.triggers, which gate every SKILL.md in the corpus.

The closure lives in Go because the registry does. An earlier bash/Python
projection of seed.go undercounted by four -- some gates register outside that
file. The authority is gates.Default.All(), and reading it any other way
reproduces exactly the projection drift ADR-0016 is named after.

Mechanism is the repo's proven shrink-only ratchet: current gaps pinned in
scripts/.gate-negative-witness-grandfather; a NEW blocking gate must ship with a
witness; a gate that gains one must be pruned; a gate that leaves the registry
must be pruned; the growth guard reads HEAD so a change cannot exempt its own
new gate. The surviving count prints on every run.

Ratcheted down immediately to prove the mechanism moves rather than just counts:
tests/scripts/validate-skill-schema.bats is a real witness for skill.schema
(32 -> 31), driving the actual validator against a fixture tree.

Two things worth recording:

- The first draft's negatives passed for the WRONG reason. Descriptions in this
  corpus embed `Triggers: "x"`; unquoted, that colon makes YAML read a nested
  mapping, so the gate failed on a PARSE error rather than the schema violation
  each test claimed to witness. Caught only because the positive case failed
  too. A negative that fires for the wrong reason is not a witness.

- go.test-isolation is ON the allowlist as lacking a proven negative, and it
  caught a real defect in this very PR (a git exec without per-command env
  scrubbing). The pin measures TEST COVERAGE of a gate, not its efficacy. An
  unproven gate may work fine; the point is that nothing would tell us if it
  stopped.

All four of the meta-test's own failure directions were seeded and observed, so
the check-liveness ratchet is not itself an unproven check.
2026-07-26 10:31:43 -04:00
Bo aceeb6f10f feat(gates): enforce ADR-0016 shipped-Python rule; fix RPI/Validate digest disagreement (#995)
Two fixes from the 2026-07-25 skill-overhaul progress review, both independent
of the unlanded tranche fleet.

1. skill.python-ratchet — the gate ADR-0016 said it already had. Section 3
   fixed "Python never ships in skills" on 2026-07-18 and called its own
   violation "a gate failure, not a style nit", while zero checks enforced it.
   Now a blocking shrink-only ratchet over skills/*/scripts/**/*.py: the 24
   current files are pinned, new ones hard-fail, a promoted file must be
   pruned, and the growth guard rejects a change that allowlists itself. The
   surviving count prints on every run. ADR-0016 records the enforcing check by
   name and the skills/*/tests/** carve-out with its rationale, so the
   exemption cannot decay into the same inert prose.

2. The RPI/Validate acceptance-digest disagreement. run_once.py digested a
   canonical-JSON re-serialization of the parsed intent mapping, validate.py
   digested the raw intent bytes, and run_once.py hard-compared the two, so the
   composed contract could not succeed. Both suites were green because RPI's
   test mocked Validate with RPI's own digest function. RPI stops being a
   second digest authority: it carries the digest Plan declares over the bytes
   Plan snapshotted and cross-checks Validate's independently re-derived value.

3. The cathedral-cut probe, the same blind spot found again. It set
   intent_bytes = canonical_bytes(resolved_intent) — precisely the one input
   where the two digests coincide — so like the unit test it was built around
   the coincidence and could not observe the bug.

Test shape is liveness-first: every gate negative is a seeded witness the gate
must be shown to FAIL on (12/12 bats), and the digest fix landed against a RED
witness that reproduces the real production error.
2026-07-25 18:37:41 -04:00
Bo c338e0086e fix(subprocess): align the test wait budget with production and make the binding PASS chain durable
Closes W1 and W2 against the frozen intent in
go-g0-g2-landing-readiness-repair-intent.md. W3 stays an integration-only
obligation and is deliberately not resolved here. Production behaviour change:
none.

W1 - the test budget was one fifth of production.

The three WaitDelay sites in run_unix_test.go pinned 100ms while production uses
defaultWaitDelay = 500ms, so the tests asserted SIGKILL propagation plus pipe
drain inside a fifth of the real budget. They now reference defaultWaitDelay
directly rather than a second literal, so the two cannot drift apart again. No
production file changes and defaultWaitDelay keeps its value. The two elapsed
assertions in those tests bound at 2s, which leaves 4x headroom over 500ms.

Mutation lethality re-proven after the change, against the focused suite in
disposable copies with a green unmutated control first:

  never escalate to SIGKILL          killed (3 behavioural failures)
  defaultWaitDelay -> 0              killed (9 behavioural failures)
  never report a lingering group     killed (3 behavioural failures)
  SURVIVORS                          0

The third was confirmed to be a real test failure rather than a compile error.

Stability: six 50x shuffled runs of the focused suite under deliberate
contention - CPU spinners plus process-table churn plus I/O. The three repaired
tests appear in ZERO failures. One run did fail, and it is disclosed rather than
buried: TestRunDarwinFastExitUnderLoad, a DIFFERENT test that already used the
production 500ms budget, at a load average near 438 on 10 cores - roughly twenty
times the contention that produced the original report. Aligning the test bound
fixed the three tight tests; it does not make the suite immune at pathological
load, and that is recorded as a new residual rather than claimed as closed.

W2 - the PASS chain was erasable.

.gitignore excludes /.agents/ao/* except config.yaml, leaving both binding
verdicts and everything they reference untracked - the FAIL lineage durable and
the PASS that would authorize landing not. A tracked bundle now holds 32
byte-identical artifacts: both verdicts, two intent snapshots, the four
manifest/scope/effect refs, and all 24 check receipts.

manifest.json carries the content-addressed mapping. The original bytes are NOT
edited - rewriting the internal .agents/ao refs would break aef3a8a3 and
fadb23c2 - so the mapping supplies resolution instead, and the originals are
left in place untouched.

Two layout facts are recorded rather than papered over. First, the bundle uses
report-artifacts/ instead of reports/: .gitignore:44 carries a bare `reports/`
rule that matches at ANY depth, and the first attempt silently left 28 of 33
copies untracked with a manifest pointing at paths that were not in the tree -
exactly the broken-reference failure GLR-4 forbids. Second, the manifest
distinguishes what is NOT a reference: the exclusions[] field of the before/end
manifests lists .agents/ao/{intents,reports,verdicts} as subject-manifest
exclusion PATTERNS, not evidence refs, so they are neither bundled nor resolved.

One further exemption: .agents/ao/config.yaml
appears inside the before/end subject manifests. It is the single un-ignored
file under .agents/ao and appears there as an ordinary tracked repository entry,
not as an evidence reference, so it resolves at its original path and is
deliberately not bundled. Verified: 32/32 byte-identical, both verdicts
recompute canonically FROM THE BUNDLED COPIES, and zero broken references.

Verified on this tree: build, vet, pinned lint v2.11.4 (0 issues), go mod
tidy -diff, go mod verify, full race+shuffle 3381 tests in 71 packages with 0
races, and darwin/linux/windows amd64 vet plus cross-compile - all rc=0.

Claude-Session: https://claude.ai/code/session_018rWfFeZb4bKCJzxwKDYaFc
2026-07-25 12:20:58 -04:00
Bo 77b2dfef90 test(subprocess): synchronize finite stdin cancellation 2026-07-25 00:36:55 -04:00
Bo aa99cf94f2 fix(subprocess): filter Linux group probes by membership 2026-07-25 00:01:40 -04:00
Bo 070e53735d fix(cli): preserve process identity through cleanup 2026-07-24 23:37:53 -04:00
Bo c4b7b2dafc fix(cli): bound subprocess completion and stdin 2026-07-24 22:51:07 -04:00
Bo 2072c9ee7f fix(cli): own subprocess I/O lifecycle 2026-07-24 22:51:06 -04:00
Bo 65cb5c3bbb fix(cli): verify asynchronous process termination 2026-07-24 22:51:06 -04:00
Bo b2b97cb33c fix(cli): prove process tree termination 2026-07-24 22:51:06 -04:00
Bo 6690adab90 fix(cli): close cleanup propagation gaps 2026-07-24 22:51:06 -04:00
Bo d219650327 fix(cli): make process cleanup outcomes lossless 2026-07-24 22:51:06 -04:00
Bo 6e67d62a38 fix(cli): preserve process cleanup outcomes 2026-07-24 22:51:06 -04:00
Bo 6890e240c8 fix(cli): sync catalog fixture with compiler proof 2026-07-24 22:16:26 -04:00
Bo cb552c9787 fix(cli): preflight catalog UTF-8 deterministically 2026-07-24 22:14:24 -04:00
Bo 8f7bd9418c fix(cli): pin contract normalization and lexical preflight 2026-07-24 22:14:24 -04:00
Bo f83bff7684 fix(cli): match compiler proof lexical rules 2026-07-24 22:14:24 -04:00
Bo 50396d222c fix(cli): enforce skill contract v4 semantics 2026-07-24 22:14:24 -04:00
Bo 359a9e1b35 feat(cli): strictly decode versioned skill catalogs 2026-07-24 19:35:51 -04:00
Bo 5b6bf22dad fix(rpi): close exact kernel proof gaps 2026-07-24 18:35:47 -04:00
Bo bc711ea6fc fix(cli): bound subprocess lifecycle 2026-07-24 18:19:12 -04:00
Bo d1847ba261 feat(rpi): bind exact kernel proof epoch candidate 2026-07-24 17:54:40 -04:00
Bo 7d74854d8b feat(cli): declare effects and unify structured output 2026-07-24 17:52:59 -04:00
Bo 2e1f32a2d5 fix(cli): harden eval identifiers and rooted storage 2026-07-24 17:05:33 -04:00
Bo d9eeee9cb3 chore(release): prepare v3.3.0 changelog and version bump (#989)
## What

Prepares the **v3.3.0** release cut (last shipped tag: `v3.2.0`,
2026-07-03). No tag and no GitHub release are created here — tagging is
gated on the live GC canary and the operator's go.

- **Changelog reframe.** The `[3.3.0]` headline now leads with the
deliberate subtraction of the guardrail cathedral (strict verbose step
contracts, enforcement gates, injection hooks, command tombstones,
retired lifecycle surfaces, the heavy out-of-session orchestration
layer) — removed because that scaffolding degrades frontier-class models
rather than helping them. What remains is the judgment-boundary
membrane: caller-owned intent, one fresh independent validation, durable
verdicts, pinned provenance. Subtraction is git-concrete: `cli/internal`
packages **79 → 43**; reference orchestrator **~29k lines → ~1,300-line
thin pack** (one commit removed 29,418 lines for 1,334).
- **Post-07-17 delta folded in:** Gas City factory pack (preview) —
official checksummed GC/Beads binaries fetched by the installer (no
compile), mayor-driven door (human attach or agent drive via mail/sling)
with heartbeat shepherd, the `using-gc` mayor-orchestration skill +
four-layer visibility doctrine, rig-scoped dispatch intake, three
disclosed upstream v1.3.5 defects (#4586, the cross-store claim fix,
gastownhall/gascity#3985) with the preview→supported promotion criteria,
and plan/premortem ground-truth routing.
- **Version bump:** source fallback `3.3.0-rc → 3.3.0`
(`cli/cmd/ao/main.go`); plugin manifests were already `3.3.0`. Adds a
release-guard test asserting the fallback carries no pre-release marker
(resolves audit minor 1).
- **Release-gate fix:** trimmed the `using-gc` skill description (195 →
117 prose chars) to satisfy the 180-char budget — a blocker that landed
with the GC arc — and regenerated its Codex/Gemini/pack projections.

## Recommended version: **v3.3.0**

`v3.3.0` was an in-progress RC (`main.go` at `3.3.0-rc`, plugin
manifests at `3.3.0`, changelog `[3.3.0]` drafted) that was never
tagged; the GC pack + loop doctrine landed on the RC line. This cut
finalizes it.

## Go / No-Go checklist (deterministic bar:
docs/audits/release-readiness-3.3-2026-07-20.md +
docs/runbooks/release-process.md)

| # | Criterion | Status | Evidence |
|---|---|---|---|
| 1 | `go build ./...` | PASS | exit 0 |
| 2 | `go vet ./...` | PASS | exit 0 |
| 3 | Full Go test suite (`go test ./... -count=1`) | PASS | 63 packages
`ok`, 0 FAIL |
| 4 | Cathedral Cut conformance (`check-cathedral-cut-conformance.py`) |
PASS | "Cathedral Cut conformance: PASS" |
| 5 | Skill-mesh drift (`generate-skill-mesh.py --check`) | PASS | "up
to date (48 skills)" |
| 6 | Derived-artifact drift (`make regen-check`) | PASS | "All
generated projections are current." |
| 7 | Docs + release-doc gate (`make docs-check`) | PASS | CLI ref
current; 395 links, 0 broken; skill count 48; release freeze intact |
| 8 | `ao gate check --full` | PASS* | 66/67 pass. Sole fail =
`workflow.install-drift`, a worktree-local symlink artifact (tracked
`workflows/` is byte-identical to origin/main; passes in a clean CI
clone — the audit's 67/67) |
| 9 | Fast release gate (`ci-local-release.sh --quick`) | PASS | "LOCAL
CI QUICK SANITY PASSED": 44/44 install-surface smoke, 33/33 release
smoke, init smoke |
| 10 | Skill token budgets / lint | PASS | 100 budgets pass (fixed
`using-gc` 195→117 prose) |
| 11 | Install story smoke | PASS | README/MIGRATION document `ao skills
link`; all 6 curl installers are refusing tombstones; `ao version` runs;
npx-first |
| 12 | Plugin manifest ↔ corpus parity | PASS | manifests carry 48
skills, 0 drift |
| 13 | Version bump applied to owners | PASS | `main.go` 3.3.0-rc→3.3.0;
manifests already 3.3.0 |
| 14 | Changelog updated + synced | PASS | `CHANGELOG.md` ==
`docs/CHANGELOG.md` (changelog.sync gate) |
| 15 | Command/test pairing gate | PASS | main.go bump paired with
`version_test.go` release-guard test |
| 16 | Full release gate (`ci-local-release.sh`: race + security + SBOM
+ readiness) | PASS | "LOCAL CI PASSED [102s]"; release-artifact
manifest resolved `v3.3.0` |

\* `workflow.install-drift` is an environment artifact of running the
gate inside a nested worktree whose `.claude/workflows/` symlinks
resolve to the primary checkout. It is not release content: `git diff
origin/main -- workflows/` is empty, and the check passes in a fresh CI
checkout (audit reported 67/67 at origin/main).

## Operator items (require a human or a live tag — NOT attempted here)

- **Live GC canary** on the next official Gas City pin — the
preview→supported promotion gate.
- **Git tag `v3.3.0` + goreleaser publish** — gated on the canary and
operator go.
- **Docs-site publish** (mkdocs pipeline) — separate from this cut.

No tag. No release.
2026-07-24 00:01:31 +00:00
Bo cbe7ddbb5f feat(gc): ship thin native AgentOps factory pack (#980)
## Outcome

Ships AgentOps 3.3 as a thin native Gas City pack and removes the
accidental second orchestration control plane.

- Removes the custom GC delivery command/package, packet/schema family,
feeder/program engine, reducer/order, reliability registry, Beads
capability mirror, and fork-baseline runtime checks (about 29k deleted
lines).
- Retains six bounded adapters for pinned toolchain materialization,
clean bootstrap, native sling/status/doctor invocation, bead-isolated
worktrees, moving-main PR delivery, and teardown.
- Uses official Gas City and Beads behavior as the state owners, with
required OTEL configuration.
- Preserves Fable Mayor/Refiner, Sol-high planning and fresh validation,
Terra-high default implementation, Opus-medium overflow, and
support-only Luna.
- Supports automatic Refiner merge after hosted CI or a manual-review
toggle without locking `main`.

## Evidence

- Exact official GC: `8ffc009ded781a2ada2077f3a29bd712b2def0bf`
- Exact official BD: `8e4e59d39f3459a43cf21a3236a13eca4dd874f7`
- Full pinned native boundary: 9/9 passed in 114.047s
- Exact source-bead route replay: passed in 98.051s
- Release replay: manifest integrity, thin tests and both pack lints,
generated projections, contract compatibility, Bats contract,
ShellCheck, test-removal ratchet, and `go test ./...` all passed
- Fresh independent semantic verdict: PASS (Sol-high)
- Exact delivered head: `e805f0e26ce21c2eae9e720f57fff2a705f3185b`
- Subject manifest:
`8554d065076f70d0e639ca353409d2a75dca7ac01e4cb65cb4a118e98c0770fc`
- Verdict:
`3f89376b675de854832f38b46ccfa7ad7ca54079ffde578769c33e1f118c68c1`

## Release boundary

After this PR merges, the release qualification runs one fresh mixed
Terra/Opus canary from merged `main`, with at most one external repair
and one terminal retry. No live city repairs itself.

The only known local fast-gate exception is `workflow.install-drift`:
the installed workflow symlink resolves to the separate dirty primary
checkout, outside this candidate. All candidate-owned gates passed.
2026-07-22 19:08:02 -04:00
Bo faff55ac2a feat(gc): bind native 3.3 factory workflows (#972)
## Summary

- bind the GC 3.3 bead-native workflow: Fable Mayor, fresh Sol-high plan
and validation, Terra-high default or Opus-medium overflow
implementation, Luna support-only
- add the bounded one-shot graph feeder, strict packet/worktree
identity, semantic terminalization, and model-free protected delivery
sweep
- make official Gas City v1.3.5 plus Beads v1.1.0 bootstrap repeatable,
disable unstable event propulsion/hooks, and prove quiescent teardown

## Reliability boundaries

- no Gas City self-repair loop, daemon, private scheduler, model
fallback, or main mutex
- semantic completion is independent of delivery; protected
PR/CI/rebase/merge is deterministic and moving-main aware
- event hooks are disabled because 3.3 uses explicit program admission
and cooldown delivery; native controller bead observation remains active

## Evidence

- full local release CI PASS at
.agents/releases/local-ci/20260722T113355Z
- two pristine standalone-clone cycles with official GC v1.3.5 8ffc009d
and BD v1.1.0 8e4e59d: repeat bootstrap, exact rig Dolt context, absent
event hooks, bounded events, enabled sealed delivery, and delayed
zero-process teardown
- fresh independent Sol-high pre-delivery binding PASS over exact head
e8c1253b40
- binding verdict SHA-256:
4f8d81811cd5b2eb35159c98159db2f8917d3ef54167f3c0d59d8378de14b2d1

## Tracker

Beads: age-gc-scope-failclosed-release-gktia.2

The one bounded mixed-provider live canary remains a post-merge release
gate; this PR does not claim final 3.3 release readiness before that
proof.
2026-07-22 13:49:29 +00:00
Bo 4d631207cf feat(gc): add bead-native crash-only delivery kernel (#970)
## Summary

- replace the unreachable Python factory lifecycle with an optional
typed Go delivery reducer
- make Beads the delivery lifecycle authority with deterministic
moving-main successor epochs
- add native Git/worktree, PR, hosted-check, auto-merge, landing, and
cold-replay boundaries
- retire obsolete v1 role/factory schemas and preserve the pinned
historical capability harness

## Validation

- fresh Sol-high PASS over exact 43-path manifest
ec6a2a4bf0b180782411b1ae4afd4a2ad8384129ff037ce8c119480317626b02
- go test ./...
- go test -race ./internal/gcadapter/delivery
- go vet ./...
- scripts/check-gc-executor.sh
- GC 3.3 schema, migration, provenance, factory-doctor, and bootstrap
gates

Bead: age-gc-scope-failclosed-release-gktia.1
2026-07-22 02:24:58 -04:00
Bo 26120d8394 feat(gc): add crash-only delivery thin slice (#969)
## Outcome

Adds the bounded GC33-6 delivery slice outside core ao: a separately
built crash-only reducer, strict admission certificate binding,
immutable handoff and branch/PR receipts, native graph.v2 formulas,
one-step disabled Order glue, and one centralized stock claim wrapper.

Semantic bead: ag-agentops-33-gc-refinery-4km81.7 (closed after
independent validation).
Binding PASS:
d3ac5a566e0f5197814e2dbcf04d0540849f58636ed8bb49a0faca7d6af94639 over
manifest
155a089b4b461bda61a7f1ad1987c66f5755ac7c5628aa648e70740be2580a5a.

## Verification

- Full cli Go suite: PASS (2,887 tests)
- Focused race-enabled reducer suite: PASS
- GC factory/executor Python contracts: PASS
- Full executor gate: PASS (123 Python + 25 Bats)
- Exact official GC v1.3.5 pack lint and no-start discovery: PASS
- Cold replay: exactly one delivery, branch, and PR; six emitted
artifacts validate against shipped schemas
- Production Go surface: 922 lines, under 3,000 tripwire

## Boundaries

No ao command, core port, default install, live city, real
Git/forge/CI/merge adapter, daemon, scheduler, or factory.py import.
Real adapters and delivery completion remain GC33-7.
2026-07-22 00:34:05 +00:00
Bo e5ad3cfc94 test(cli): guard advertised ao commands against the live cobra tree
Walks every registered command Short/Long/Example plus quick-start printed
output, extracts advertised "ao ..." strings, and resolves each against the
assembled rootCmd (aliases included; Example fields get full group-descent).
Guards the defect class the 3.3 release audit fixed by hand: user-facing
output advertising a retired command.
2026-07-20 19:57:01 -04:00
Bo 238964fdd2 feat(cli): workflows are canonical product artifacts — workflows/ + ao workflows link (#945)
Workflows get the skills treatment (operator decision): canonical source
in the product tree, installed by a product verb, Claude-only labeled as
such.

**What moves:** all seven Claude workflow scripts + README migrate from
force-added exceptions inside the gitignored `.claude/` to a tracked
top-level `workflows/` (sibling of `skills/`) — the four existing
conveyors plus `audit-dimensions`, `verify-fixes`, `implement-wave`:
three thin, args-parameterized orchestration conveyors extracted from
this session's hand-rolled waves, contract-reviewed, and smoke-proven
through the real Workflow runtime (the smoke caught two contract gaps
static review could not: an `export default` wrapper the runtime never
invokes, and args arriving as a JSON string — both fixed, string-args
tolerance now built in).

**New verb:** `ao workflows link` / `unlink` mirror `ao skills link`
semantics — dry-run `--json`, refuse to replace real files or foreign
links, unlink only checkout-owned links — targeting the project-local
`.claude/workflows/` where Claude Code resolves named workflows
(`--into` overrides). Checkout identity reuses the skillsapp marker
discipline, fail-closed. Claude-only runtime adapter, same doctrine as
the Codex-only `skills-codex/`.

**Legacy surfaces repointed:** `install-workflows.sh` (user-global $HOME
installer), `check-workflow-drift.sh` + gate comment,
`check-bdd-foundry-markers.sh`; spine allowlist + YAML-probe excuse +
go-cli.md spine region gain the workflows group; COMMANDS.md,
cli-surface projections, and surface-count fixture regenerated; new
tests carry per-command git-env scrubbing (test-isolation ratchet back
at baseline).

**Built BY the workflow being canonized** — `implement-wave`
orchestrated its own canonization: two disjoint-ownership lanes plus a
seam-checking verifier that ran the real binary's link → resolve →
unlink cycle in the live tree (both lanes RESOLVED). The lanes correctly
*refused* to self-approve their command into the spine invariants and
handed integration three flagged edits instead.

**Expected local gate note:** `workflow.install-drift` correctly FAILS
on machines whose user-global `~/.claude/workflows` links still point at
the old location — that is the transition it exists to catch. CI stays
green (absent→skip). **Post-merge operator step:** `cd ~/dev/agentops &&
git pull && bash scripts/install-workflows.sh`.

**Verified:** full suite 63/63 pkgs; golangci-lint clean; `gate check
--full` over this range = 66/67 with only the documented install-drift
environment finding; workflows smoke-run evidence in session logs.
2026-07-20 19:39:16 -04:00
Bo 3d24ee0e9d fix(cli): residue wave — doctor dev-version coherence, diff --only, config-models removal, dual-root stragglers (#944)
Wave 4 (residue) of the new-user happy-path arc. Three scoped
implementer lanes + fresh adversarial verifier (4 RESOLVED; 1 INCOMPLETE
= stale generated projections, closed in integration).

**Doctor**: the dev-version detector now reuses the Binary Freshness
resolution — a from-source build matching its checkout is healthy (a
novice building from source can finally see `ao doctor` exit 0);
findings fire only on genuine drift, shadowed duplicate `ao` binaries,
or an informational from-source note outside any checkout. `ao doctor
diff` gains `--only` so the fix-plan preview can be scoped the way
remediation text implies.

**Config**: the dead `ao config models` surface is removed end-to-end
(lane re-verified zero consumers before deleting; `--show` proven
byte-identical before/after; removed-child hint + MIGRATION row;
existing `models:` config sections still parse and are ignored).

**Dual-root stragglers**: learning-coherence gate globs,
`quality.CountConstraints`, and the eval sandbox corpus deny-list now
cover canonical `.agents/ao/<section>` alongside legacy roots.

**Doc-link hygiene**: the strict docs-link backstop's allowlist was 100%
stale (53/53 entries referenced Cathedral-Cut-deleted docs) — refreshed
to 10 verified accepted-class entries; ROADMAP dead links fixed;
documentation-index generator emits GitHub URLs for repo-root targets;
doc-skill references instruct only shipped scripts; codex twins +
CLI-surface projections + surface-count fixture regenerated.

**Deferred by design**: the `3.3.0-rc` fallback version bump belongs
inside the v3.3.0 tag-cut commit.

**Verified**: full suite 61/61 pkgs (2825 tests); golangci-lint clean;
`ao gate check --full` 67/67 over this range; Test-Removal-Reason
trailer covers the 6 deliberately deleted models tests.
2026-07-20 19:05:57 -04:00
Bo 511370f5d9 fix(cli): doctor coherence, config truthfulness, pruned-verb hints — novice edges 4/6/7/8 (#942)
Wave 2 of the new-user happy-path fixes (fresh-eyes novice test).

**Edge 4 — doctor sub-surfaces contradicted each other.** Remediation,
`next_steps`, and robot-triage's `recommended_command` now instruct
`--fix` only when a fixer can actually act — non-fixable findings name
their real manual action (including the detect-only bridges class the
verifier caught as the same lie one level up). `ao doctor health`
reports every severity bucket present (it omitted P1 — the worst
actually present). `ao doctor diff` renders an explicit read-only fix
plan (would auto-fix vs manual action) instead of a findings copy. `ao
doctor explain` is now a superset of the finding's triage entry.

**Edge 6 — `ao config --show` rendered config for commands that don't
exist.** `rpi.*` and `dream.*` no longer serialize in human or JSON
output (struct fields retained `json:"-"` pending a follow-up removal
once nothing references them). `AGENTOPS_NO_SC` had zero behavioral
consumers (evidence: rg over all non-test cli/ — only config
plumbing/display) — undocumented and removed from the env panel; the
field stays parseable so existing config files don't error.

**Edge 8 — legacy-config gaslighting.** With only
`~/.agentops/config.yaml` present, `--show` used to print the
deprecation warning and then claim the new path was "(not found)" and
values came "(from flag)". It now shows the actually-read path labeled
"(deprecated location)" with correct attribution.

**Edge 7 — `ao beads` bare error.** The whole pruned 3.2 bookkeeping
family gets point-of-failure migration hints; a new guard test proves no
hinted verb is a live command (the `eval` trap: pruned in 3.2, returned
live in 3.3).

**Process:** three scoped implementers (disjoint files) + fresh
adversarial verifier. The verifier refuted two lanes as incomplete
(recommended-command layer, stale cmd/ao config tests asserting the old
defect) — closed in integration before this PR.

**Verified:** full suite 61/61 pkgs; golangci-lint clean; `ao gate check
--full` 67/67 over this range; per-fix RED→GREEN sandbox repros in the
workflow logs.
2026-07-20 16:05:40 -04:00
Bo 0cf626ce96 test(archcheck): behavioral coverage for semantic production gate
Add RED-provable behavioral tests for the semantic-production-gate paths in
cli/internal/archcheck/semantic.go, each pairing a crafted-BAD input (asserting
the SPECIFIC rejection message) with a happy-path GOOD input so the tests prove
the checks discriminate, not merely fail. semantic.go is unchanged (tests only).

Per-function coverage of semantic.go, before -> after:
  checkSemanticSeal              9.5% -> 57.1%
  checkEvidenceBinding           0.0% -> 75.9%
  validHexDigest                 0.0% -> 100.0%
  digestBytes                    0.0% -> 100.0%
  SemanticProductionGate         0.0% -> 78.6%
  violationsContainRule          0.0% -> 75.0%
  checkGeneratedEvidence         0.0% -> 81.2%
  collectCommandDefinition       0.0% -> 100.0%
  collectCommandGraphEdges       0.0% -> 93.8%
  collectCommandRoots            0.0% -> 88.9%
  checkRecursiveContractsSource  0.0% -> 93.9%
  cobraCommandLiteral            0.0% -> 100.0%
  cobraCommandRunnable           0.0% -> 75.0%
  collectStructuredFunctions     0.0% -> 100.0%
  collectOutputRunnable          0.0% -> 100.0%
  collectStructuredAttachment    0.0% -> 92.9%
  appendHumanTextViolations      0.0% -> 100.0%
  checkOutputSource              0.0% -> 95.0%
  checkUniversalEffectsSource    0.0% -> 90.0%
  collectExecAliases             0.0% -> 88.9%
  collectContextParams           0.0% -> 84.6%
  recordTrackerLaunch            0.0% -> 81.0%
  applyTrackerLaunchField        0.0% -> 80.0%
  checkTrackerExecutionSource    0.0% -> 96.7%
2026-07-20 15:41:21 -04:00
Bo a4c0f2d646 fix(cli): three novice-test majors — foreign-repo gates, symlinked skills, benign PATH shadow (#940)
Wave 1 of the new-user happy-path fixes (fresh-eyes novice test,
follow-up to the [3.3 release
audit](docs/audits/release-readiness-3.3-2026-07-20.md)).

**Edge 1 — `ao gate check` in a foreign repo.** Was: ~10 blocking
UNKNOWN rows naming agentops-internal scripts, a summary that counted
none of them, exit 1 — the product's own suggested next command failed
in every repo but this one. Now: a deterministic repo marker
(`cli/go.mod` module identity) routes those checks to first-class
not-applicable SKIPs outside agentops; human output aggregates them into
one honest line and suppresses the routing-skip wall (70 lines → 5); the
summary counts UNKNOWN in its own bucket everywhere; `--json` keeps
every row. Inside agentops, a missing backing script stays fail-closed
UNKNOWN (guard pinned by test).

**Edge 2 — permanent false `fm-skills-missing` P1.** The detector's
Lstat-based scan counted the canonical `ao skills link` symlink farm as
zero skills. Now resolves symlinked skill dirs; dangling links still
count as absent.

**Edge 3 — P1 on every default dev Mac.** Benign PATH shadowing
(Homebrew git + Apple `/usr/bin/git`) raised a P1 with a broken
empty-slot remediation ("Install the missing CLI yourself — —").
Shadow-only now produces no finding; the missing-CLI template renders
only with real names.

**Process:** three scoped implementer agents (disjoint file ownership) +
fresh adversarial verifier (4× RESOLVED, incl. dangling-symlink and
genuinely-missing-CLI negative cases). **Verified:** per-fix RED→GREEN
shell repros; foreign-repo first contact now 5 lines + exit 0 with JSON
parity; 61/61 test pkgs; golangci-lint clean; `ao gate check --full`
67/67 over this range.
2026-07-20 15:36:01 -04:00
Bo 780591fb48 fix(storage): correct retros artifact typing, bracketed-list quote trim order, and surface walk errors
Three behavioral bugs in cli/internal/storage/searchindex.go, each documented
by a B5 test that previously pinned the buggy behavior:

1. ArtifactTypeFromPath: ArtifactSubdirs lists 'retros' (the real production
   dir; doctor/fix_workspace.go maps 'retros' -> 'retro') but the type map only
   matched '/retro/', so retros/ artifacts were typed 'unknown'. Added a
   '/retros/' -> 'retro' entry (singular '/retro/' kept for compatibility).

2. ParseBracketedList: strings.Trim(quotes) ran before TrimSpace, so a
   space-guarded quoted token like " 'c'" kept a stray leading quote ('c).
   Swapped to TrimSpace first, then Trim(quotes).

3. WalkIndexableFiles: the walkFn returned nil on error, so a missing root
   returned (nil, nil) and masked the lstat failure. It now surfaces the walk
   error. No production caller relied on the leniency (only the definition and
   tests reference it).
2026-07-20 15:18:50 -04:00
Bo 6bb54bb347 fix(cli): ao init creates the complete evidence layout (#938)
A fresh `ao init` was immediately flagged by the CLI's own diagnostics —
`ao doctor diff` reported `fm-knowledge-missing-substructure` (sessions,
index missing) and the `.agents/ao/intents/sha256` store that `ao
status` reads was never created. This was the last first-run wart from
the [3.3 release-readiness
audit](docs/audits/release-readiness-3.3-2026-07-20.md) (agents-runtime
minor 3).

**Fix:** `evidenceDirs` now derives the knowledge substructure from the
storage package's exported contract (`DefaultBaseDir` +
`SessionsDir`/`IndexDir`/`ProvenanceDir`) instead of a parallel
hand-list, and adds the intents store — so init, status, and doctor
agree on one layout by construction.

**Verified:** RED reproduced in a sandbox (P2 finding on fresh init),
GREEN after fix (finding gone, status reads both stores); new initapp
tests assert both contracts on the created tree and that dry-run touches
nothing; full suite 61/61 packages; `ao gate check --full` 67/67 over
this range.
2026-07-20 14:41:16 -04:00
Bo d043390a0a fix(release): resolve 3.3 release-wrapper audit — blocker + 13 majors (#935)
Resolves every spellbreaking finding (the blocker + all 13 majors) from
the 3.3.0 release-readiness audit
([docs/audits/release-readiness-3.3-2026-07-20.md](docs/audits/release-readiness-3.3-2026-07-20.md),
included in this PR).

## Finding → fix map

**CLI self-documentation (M1–M3)**
- **M1** `ao robot-docs` prescribed removed `ao inject` → line removed
from the canonical agent workflow; `inject` added to the removed-command
hint table **and** the MIGRATION.md map (drift test
`TestRemovedVerbsHaveMigrationRows` enforces the pair).
- **M2** `ao config --help` documented ~14 env vars for removed
subsystems (RPI/Dream/Council/tiers) → help text and the `--show` env
panel pruned to the 5 vars the binary consumes; mirrored list in
`internal/config` pruned identically.
- **M3** `ao flywheel status` read only legacy `.agents/<section>` while
`ao doctor fix` migrates learnings to canonical `.agents/ao/learnings` →
new `quality.KnowledgeSectionDirs` dual-roots every knowledge reader
(tier counts, new/stale artifacts, retros, health delta, utility, loop
metrics, retrievable-citation stats — plus the golden-signals readers
`ComputeResearchClosure`/`ComputeReuseConcentration` that the fresh
verification pass caught as missed). Sandbox-proven twice: a learning
existing only under `.agents/ao/learnings` appears in all metrics, and a
research file only under `.agents/ao/research` flips closure from
`starved/0` to `unmined/1 orphan`.

**Release story (M4–M6)**
- **M4** CHANGELOG `[3.3.0]` omitted post-07-17 surfaces → folded in `ao
eval` (#921), default-build `ao flywheel`, the PreToolUse policy engine,
and the #919 cleanup; date moved to 2026-07-20; `docs/CHANGELOG.md`
re-synced (changelog.sync gate green).
- **M5** MIGRATION.md attributed `ao eval` to a nonexistent "3.4" → now
"returned in 3.3".
- **M6** four release surfaces claimed a 50-skill corpus vs 48
everywhere real → all counts now 48 (CHANGELOG ×2, docs/3.3.md,
release-notes page ×2).

**Install story (M7 — product decision by Bo)**
npx first (universal — installs into all coding agents), **plugins for
Claude Code/Codex encouraged**, checkout + `ao skills link` as the
source-tracked/contributor path; curl installers stay tombstones.
Harmonized across README, UPGRADING, install-day2-ops, MIGRATION,
3.3.md, CHANGELOG, the release-notes page, all six installer tombstone
messages (`install.sh` + claude/codex/agy/opencode/`codex.ps1`), and the
site's CLI page. All "legacy migration-only / not the recommended path"
plugin branding removed.

**Docs site (B1, M8–M12)**
- **B1** generated site CLI page instructed a tombstoned curl installer,
nonexistent `ao rpi phased`, and wrong skills dir → `emit_index()`
rewritten to the real install menu + a quickstart of commands that
exist; semantic loop correctly attributed to skills.
- **M8** deploy workflow's `--strict` contradicted mkdocs.yml's declared
non-strict policy and aborted the build → flag dropped (lychee +
validate-links.sh own link checking).
- **M9** site banner said "AgentOps 2.x" → now 3.3.
- **M10** ~176 internal files (audits/plans/handoffs/… + TEMP scratch
doc) published and dominated search → `exclude_docs` extended; built
site verified free of them; search index 4,751 → 2,444 entries.
- **M11/M12** newcomer-guide skill links and all six SCHEMAS.md links
404'd on the site → absolute GitHub URLs (resolve on both GitHub and the
site). The fresh verification pass found the same class on
`docs/contracts/index.md` (nav-listed),
`docs/contracts/corpus-learning-seam.md`,
`docs/templates/slice-validation.md`, and five
`docs/architecture/gas-city-factory.md` links into now-excluded
`docs/audits/` — all repointed to absolute GitHub URLs.
- Also from the verification pass: robot-docs exit-code table no longer
says "bead claimed" (removed concept), and the docs.yml comment now
cites the link checker that actually runs
(`tests/docs/validate-links.sh` via doc-release checks) instead of
lychee.

**Skills corpus (M13)**
- rch skill instructed 5 nonexistent scripts + 8 nonexistent reference
files as recovery steps → pruned to the 7 real references; the
wire-level `printf | rch` probe replaces the phantom `protocol_test.sh`;
codex twin regenerated (parity gates green).

## Verification

- `go vet` clean; **full test suite 60/60 packages pass** (includes the
new-shape flywheel/quality/config tests and the inject↔MIGRATION drift
test).
- **`ao gate check --full --scope worktree`: 67/67 pass, 0 warnings**
over this exact change set (changelog sync, shellcheck on the edited
installer, skill mesh + codex parity, manifests/schema/triggers,
provenance chain).
- `scripts/regen-all.sh --check`: all generated projections current.
- Rebuilt binary re-exercised: `robot-docs` clean, `ao inject` tombstone
live, config help clean, flywheel sandbox proof above.
- `mkdocs build` exit 0; warnings 84 → 46 (remainder is the accepted
out-of-tree-link class per mkdocs.yml's declared policy).
- Fresh-context adversarial verification workflow over all four fix
groups (results in session log).

## Known residuals (deliberately out of scope)

- `ao config models` subcommand still renders tier config (its two env
vars ARE consumed; `COUNCIL_CLAUDE_MODEL` in its display list is not —
follow-up).
- Same-class single-rooted readers off the flywheel path:
`learning.coherence` gate glob (`.agents/learnings/**` only),
`quality.CountConstraints`, config `Paths` defaults feeding eval sandbox
deny-lists.
- `scripts/docs-build.sh` still uses `--strict` with its own allowlist
(not wired into any workflow or gate).
- Audit minors 1–8 (rc fallback version string, `config --show`
legacy-fallback display, `ao init` vs doctor layout, doctor's `ao beads
dir` hint, dead `tracker:` key in the example config, doc-skill phantom
scripts, ROADMAP dead links, documentation-index root links).
2026-07-20 12:58:06 -04:00
Bo d7029685e8 fix(eval): crash-safe 0600 burn-ledger write immune to stale temp files (age-6j9ee.3) 2026-07-20 10:21:33 -04:00
Bo 74542f9a84 fix(eval): depth bound no longer silently truncates the symlink closure (age-6j9ee.3) 2026-07-20 10:21:33 -04:00
Bo fde56e1fbe fix(eval): symlink deny set is now a fixpoint closure with fail-closed overflow (age-6j9ee.3)
The corpus-deny Seatbelt sandbox enumerated symlink escapes with a single-pass
walk that only resolved the FIRST hop. The cross-family validator refuted this
class three times: (1) dir-symlink targets missing, (2) file-symlink targets
missing, (3) CHAINED escape — .agents/learnings -> ext1 (dir target added) but
ext1/pivot.md -> ext2/secret.txt: the walk never descended INTO the resolved
external target, so ext2's canonical path was not denied and reading the final
resolved target leaked 26 bytes with exit 0. Enumeration is not a closure.

Re-scope (design change, not another patch): the deny set is the full
symlink-reachability CLOSURE, computed as a fixpoint over a worklist. Every
symlink found (file or dir) resolves via EvalSymlinks to a canonical target
added to the deny set; every resolved DIRECTORY target is ALSO pushed onto the
worklist, so its interior symlinks resolve too. Repeat until the worklist is
empty. This kills the class structurally: there is no symlink shape that
escapes, because any shape either lands fully in the closure or trips the
refusal below.

Fail-closed overflow (the property, no silent truncation): the closure is
bounded. Directories reached THROUGH symlinks are dir-capped and total deny
entries are capped; a non-NotExist walk error also aborts. On ANY bound breach,
symlinkClosureDenyTargets returns errSymlinkClosureOverflow, corpusDenyPaths now
returns ([]string, error), and its callers (sandboxedCodexCmd via
scenario_ab_runtime, sandboxedShellCmd via workspaceCommand) propagate it so the
existing sandboxExecGuard refusal path makes the arm FAIL CLOSED — exactly like
the no-Seatbelt case. Either the complete closure is computed or the run refuses.

The bounds apply to EXTERNAL symlink-reached subtrees (where a pathological
graph — a fan-out, a deep chain, or a symlink to `/` — explodes), NOT the
legitimate corpus roots. This matters: a real machine's ~/.agents symlinks the
repo skill corpus (measured ~129 external dirs, ~58 entries), so a naive global
dir cap would false-close every real run. Corpus roots are walked
depth-bounded; only symlink-reached subtrees are dir-capped (1024 dirs / 2048
entries — generous vs the measured real closure, tiny vs a pathological graph).
Cycle-safe (seen-set dedup before worklist push; a->b->a terminates); dangling
symlinks and device/socket/fifo targets are skipped. Caps are function params so
overflow is unit-testable with tiny caps + tiny fixtures.

Alternative considered and REJECTED: inverting to an allow-list Seatbelt profile
(default-deny reads except workspace + system paths). Structurally cleaner, but
high compatibility risk for arbitrary eval commands (interpreters, /etc, language
toolchains, caches) — it would break legitimate arms. Closure-or-refuse achieves
the same fail-closed guarantee within the existing deny model.

Tests (RED captured for the chained probe before the fix — ext2 absent from the
deny set): chained 2-hop and 3-hop closure construction; darwin integration
reading the FINAL resolved target (denied, zero bytes); dir- and entry-overflow
fail-closed (nil set + errSymlinkClosureOverflow); overflow refusal propagates
through workspaceCommand end-to-end via the real const; cycle terminates with
both targets denied; dangling skipped. All prior tests (dir/file symlink,
redaction, env/config/local corpus, judge denial, workspace-allowed) stay green.
2026-07-20 10:21:33 -04:00
Bo 6eee6f869f fix(eval): deny nested FILE symlink targets in corpus Seatbelt profile (age-6j9ee.3) 2026-07-20 10:21:33 -04:00
Bo bb8f0cf9c5 fix(eval): redact model-bound runtime output; deny nested corpus symlinks; 0600 holdout ledger (age-6j9ee.3) 2026-07-20 10:21:33 -04:00
Bo 5303a816e3 fix(cli): gate failure stderr silenced; all modules on shared seam or reasoned-exempt; guard requires coverage (age-6j9ee.1) 2026-07-20 10:14:04 -04:00
Bo b4aa2f0d8b fix(cli): -o yaml is real everywhere json is — shared WriteYAML, zero silent fallbacks (age-6j9ee.2) 2026-07-20 10:14:04 -04:00
Bo 3a54d5ff53 refactor(cli): unify host-seam contract, shared writeJSON/ExitError, drift guards (age-6j9ee.1)
Collapse the four drifted cmd/ao host-seam shapes into one shared
clicontract.HostOptions consumed by all 17 command modules; delete the
positional-func and bespoke-struct seams and the doctor GlobalOptions bundle.

- clicontract/host.go: single HostOptions union (OutputMode/Verbose/Verbosef/
  DryRun/ProjectRoot/GoalsPath/LedgerPath/Version/Now/EnrichFlagErr), one shared
  WriteJSON (byte-identical to the three deleted copies), one shared ExitError
  (Label field preserves doctor's stderr surfacing; gate stays silent).
- config_module.go + doctor_module.go: package-level module/service singletons
  -> newConfigCommand()/newDoctorCommand() constructors; 7 allowlist rows removed.
- carveout_regression_test.go: three AST drift guards (shared seam type / no
  direct host effects / no module-service singleton vars), each RED-proven.
- docs/architecture/go-cli.md: document the intentional two-tier shape (5 full
  hexagonal, 12 app-seam); drop the stale opportunistic-extraction sentence.

Zero behavior change: ao -o json capabilities byte-identical; full suite +
-shuffle=on -count=2 green; 0 lint issues; gate check --full 67/67.
2026-07-20 10:14:04 -04:00
Bo b1811594eb fix(runtimecmd,eval): LAW-0 guard before any spawn; content-scanning tripwire; deterministic claude suites still score (age-6j9ee.4) 2026-07-20 09:20:37 -04:00