Files
Bo efcf4879c8 feat(gc): port gc-maintainer-ops into the ao gc command family (#1016)
## What

Ports `scripts/gc-maintainer-ops.sh` (425 lines of bash: prepare / check
/ recover-affinity for stock Gas City rigs) into the Go CLI as **`ao gc
prepare|check|recover-affinity`**, per ADR-0016 (skill logic ships in Go
via `ao`; shell stays thin glue).

**Why:** skills ship via plugin/npx as SKILL.md only — a user without a
repo checkout could not run the commands the shipped `using-gc` skill
teaches. The skill said "From an AgentOps checkout", which was disclosed
but weak.

## Changes

- **`cli/internal/gcmaintainer`** — full port: rig/import pin
verification, bundled pack-cache resolution, PyYAML-capable python
selection, atomic runtime staging, managed check wrappers, skill links
into city/rig Codex sinks, macOS LaunchAgent + doctor/status health
checks, bounded affinity recovery. Output and refusal-message parity
with the shell script (incl. refuse-before-mutation ordering).
- **`cli/internal/commands/gc` + `cmd/ao/gc_composition.go`** — cobra
module on the shared `clicontract.HostOptions` seam; global `--dry-run`
always overrides `--apply`.
- **Skills source resolution without a checkout**: `--skills-source` >
enclosing agentops checkout > installed skills root (`~/.agents/skills`,
`~/.claude/skills`). Existing rigs stay recognized: the `managed-by:
agentops gc-maintainer-ops` wrapper marker is unchanged.
- **Tests migrated**: `tests/python/test_gc_maintainer_ops.py` (7 cases)
→ Go L2 tests in `cli/internal/gcmaintainer` with the same fake-`gc`
harness, plus module wiring tests. `scripts/check-gc-executor.sh` no
longer runs the python suite.
- **`scripts/gc-maintainer-ops.sh`** reduced to a thin wrapper exec'ing
`ao gc`, pinning `--skills-source` to its checkout to preserve
historical semantics (`--ao-bin` now selects the ao binary).
- **Docs/projections**: `skills/using-gc/SKILL.md` now teaches `ao gc
...`; codex, gemini, and executor-pack projections regenerated via their
owning generators; spine/COMMANDS.md/surface artifacts regenerated.

## Verification

- `go build ./... && go vet ./... && go test ./...` — 2923 passed, 73
packages
- `golangci-lint run` on new/touched packages — clean
- `shellcheck -S warning` on wrapper + gate script — clean
- `bash scripts/check-gc-executor.sh` — OK
- Smoke: built `ao`, ran wrapper → `ao gc` delegation end-to-end
2026-07-30 13:16:53 -04:00

4.9 KiB

Gas City reliability boundary

AgentOps ships skills, evidence contracts, and small operational guidance, not an orchestration backend or a competing Gas City pack.

Fact Authority
Work and dependency readiness Beads
Sessions, routing, formulas, scoped control dispatch, dashboard/API, OTEL official Gas City
Pack discovery and resolved import locks Gas City registries and packs.lock
Candidate identity Git commit and tree
Semantic acceptance AgentOps subject-manifest.v1 and verdict.v2
Pull request, hosted checks, merge GitHub

Caller-owned work flows through the upstream pack selected by the operator. AgentOps prefers the official gascity build pack used by Maintainer City, but also supports the Agentic Coding Flywheel as an independent factory choice. Agents inside either factory consume AgentOps skills through their provider runtime's normal skill discovery.

Operational guidance must remain bounded. It may install a pinned upstream pack, configure providers, invoke native commands, observe runs, and stop a city. It must not mirror Beads, Gas City, Git, or GitHub state in an AgentOps schema family, daemon, reducer, receipt graph, formula, or role pack.

For Gas City, install the registry-pinned gascity workflow at city scope and its sibling role pack at rig scope, preserving the stock gc.* namespace. Every graph-owning scope retains an unsuspended core.control-dispatcher. The upstream gc.mayor skill and gc.run-operator own guided coordination and formula launch.

AgentOps-owned operational adapter

The supported adapter is the shipped CLI command family ao gc (scripts/gc-maintainer-ops.sh remains as a thin compatibility wrapper over it). It has three deliberately narrow operations:

  • prepare verifies the accepted official workflow and rig-role commit, snapshots upstream validation assets unchanged under the selected rig's ignored .gc runtime, installs AgentOps-owned check-path wrappers, selects an already available PyYAML-capable Python, and links AgentOps skills into the city and rig provider sinks;
  • check replays the same pin/runtime/skill/service/Doctor checks without writing adapter files or issuing state-changing Gas City commands;
  • recover-affinity is dry-run by default and can clear only a ready formula bead's stale required-session assignee when explicitly passed --apply.

This is not a pack, formula, role, daemon, supervisor, or GC compatibility fork. The contained runtime keeps the official maintainer bytes and directory shape intact so its validator finds its own schemas. AgentOps owns only the wrappers that place those official checks at the rig-local paths declared by the formula. A foreign file at one of those paths is a conflict; preparation fails rather than replacing it.

The adapter does not install PyYAML, edit LaunchAgents, kill processes, restart the supervisor, patch managed hooks, close beads, re-sling work, or reinterpret GC outcomes. These operations either belong to the host operator or to Gas City. On macOS, check fails if an existing supervisor LaunchAgent points to a missing or different GC binary, preventing the login/reboot failure from being discovered after a run.

Compatibility qualification uses deterministic toolchain, registry, import lock, config, formula, scoped-dispatcher, and worktree checks before any model session. A live canary is disposable, uses required OTEL, and is repaired only from outside Gas City. It qualifies the selected upstream pack plus AgentOps skill availability; it does not qualify an AgentOps-owned factory.

The 1.4 maintainer canary established additional operational boundaries:

  • formula artifact gates require a rig-local check surface even though the implementation lives in the upstream pack;
  • skill links prove availability, not invocation, and free-form source-bead skill requirements are not necessarily propagated into decomposed work;
  • a workflow root can pass while its caller-owned input bead stays open;
  • disabled publishing is a successful no-op and leaves the approved commit in its source anchor;
  • a drained session can remain on a future affinity-bound bead, so recovery clears that one ready assignee rather than re-slinging the workflow;
  • gc status can disagree with gc session list; roster/pane evidence governs liveness, bead/run state governs workflow progress, and Doctor governs city metabolism.

Doctor warnings for upstream formula deprecations, managed-hook projection drift, local-only backup, retention, or unused local providers stay visible. The adapter does not downgrade them to PASS or claim that a successful canary proves heavy-load readiness.

Development is two-speed by design. The offline unit and lint contract is the inner loop. Native import, formula preview and route, doctor, and quiescent teardown checks run at a candidate boundary. Live model sessions remain an explicit compatibility test.