898 Commits

Author SHA1 Message Date
Bo 1113f0900a Merge pull request #132 from boshu2/claude/analyze-test-coverage-QDrbK
test: expand coverage across lifecycle, goals, rpi, context, search, quality, overnight, python
2026-04-22 08:39:12 -04:00
Claude 4e19a9796c test: add overnight_setup and overnight_council unit tests
Extend cmd/ao coverage with tests for pure helpers in overnight
subcommand files. L1 unit tests on parsers, formatters, validators.

https://claude.ai/code/session_01EMusoGMSUSbtPqkotJy3U7
2026-04-22 11:56:21 +00:00
Claude 21dd11a9fe test: add knowledge_* subcommand unit tests
Add tests for pure helpers in cli/cmd/ao/knowledge_*.go that were
previously untested. Covers parsing, formatting, and classification
helpers without touching real filesystems or external commands.

https://claude.ai/code/session_01EMusoGMSUSbtPqkotJy3U7
2026-04-22 11:55:51 +00:00
Claude 5240d0dcda test: expand coverage for context, quality, search, overnight, python
Add tests for previously untested surface area:
- internal/context: pure helpers for readiness classification, action
  mapping, display formatting, timestamp parsing, tmux target parsing,
  file-tail reading (23.9% -> 41.2%)
- internal/quality: golden signals math (linear regression, Gini coefficient,
  top-10/bottom ratio, citation counting, verdict computation) (15.3% -> 27.9%)
- internal/search: findings parsing (frontmatter, title extraction,
  freshness scoring), bead context resolution (env + cache + keywords),
  constraint index (load, save, lock, filter stale) (8.7% -> 22.4%)
- internal/overnight: loop helpers (validateRunLoopOptions, summary
  round-trips, snapshot partitioning, test fault injector hook) (77.4% -> 78%)
- internal/goals: platform-specific process-group tests for
  measure_unix.go (track/untrack/killAllChildren)
- tests/python: unittest-based tests for generate-context-shards.py
  covering is_binary, count_lines, units_for_file, pack_shards,
  verify_manifest, build_manifest (22 tests; runs under vanilla python3)
- tests/scripts: behavioral tests for ci-local-release.sh flag parsing
  and validation (17 bats cases)

https://claude.ai/code/session_01EMusoGMSUSbtPqkotJy3U7
2026-04-22 11:53:01 +00:00
Claude 0ed30f83fe test: expand coverage for lifecycle, goals, and rpi packages
Add tests targeting previously untested pure helpers:
- lifecycle: seed, temper, curate, dedup, defrag, maturity, feedback,
  task_sync (4.1% -> 71.3% statement coverage)
- goals: RunValidate, RunMigrate, RunPrune, RunInit, RunSteerAdd/Remove/
  Prioritize, DirectivesFromPillars, FindMissingPath, SplitCommaSeparated,
  DetectGates, AutoDetectTemplate, LoadTemplate, BuildInteractiveGoalFile
  (58.8% -> 84.6%)
- rpi: artifacts (classification, path safety, content type), complexity
  (scoring, classification), handoff (markdown parsing, dedup, truncation),
  evaluator (verdict logic, finding generation), cancel (signal parsing,
  PID filtering, process tree), cleanup (stale detection, state patching,
  worktree validation) (18.1% -> 40.5%)

https://claude.ai/code/session_01EMusoGMSUSbtPqkotJy3U7
2026-04-22 11:43:49 +00:00
Claude 895ed026be feat(cli): add shell completion for enumerated-value flags
Register cobra flag completions so `ao <cmd> --<flag> <TAB>` surfaces
the valid enumerated values instead of falling back to file completion:

- ao --output: json, table, yaml
- ao seed --template: generic, go-cli, python-lib, rust-cli, web-app
- ao goals init --template: same set (derived from lifecycle.ValidTemplates)
- ao inject --format: json, markdown
- ao inject --session-type: brainstorm, career, debug, implement, research

The seed/goals-init template lists are derived from lifecycle.ValidTemplates
so completion stays in lockstep with validation. L2 integration tests
exercise the registered cobra completion functions directly and assert the
exact value set + ShellCompDirectiveNoFileComp directive.

https://claude.ai/code/session_01KvJRtiQRZY38PVxNZ7S6Rm
2026-04-22 04:54:14 +00:00
Claude b69a00f4a7 fix(flywheel): unblock close-loop auto-promotion and loop-dominance signal
Nightly dream cycle (issue #117) reported 43 candidates ingested with zero
auto-promoted, indexed, or citation-rewarded every night, yielding sigma=0,
rho=0, escape_velocity=false. Loop-dominance also reported "B1" dominant
even when r1=b1=0.

Three linked fixes:

1. checkPromotionCriteria demanded >=2 citations for every candidate. The
   function is shared between the manual pool batch-promote path (where
   citation signal is the right gate) and the automated flywheel close-loop
   path (where scoring tier already established signal). Fresh candidates
   cannot be cited before they surface, so the gate was a deadlock: nothing
   promoted -> nothing indexed -> nothing cited -> nothing promoted. Add a
   requireCitations parameter; batch-promote passes true (preserved
   behavior), close-loop passes false.

2. computeLoopDominance defaulted Dominant to "B1" and only flipped to "R1"
   when r1 > b1, so an all-zero or tied state lied about which loop was
   winning. Use a switch that reports "none" when neither strictly dominates.

3. Add a learning capturing the chicken-and-egg-gate pattern, which also
   closes the "performance" query gap in retrieval-bench (was 0 hits, now 1;
   live-retrieval coverage: 90% -> 100%).

End-to-end verification with scripts/nightly-dream-cycle.sh:
  auto_promote.promoted: 0 -> 46
  store.indexed:         0 -> 46
  loop_dominance:        "B1" -> "none"
  retrieval coverage:    0.90 -> 1.00

https://claude.ai/code/session_01SJeBFbtnMJMbUgwZPx4xqg
2026-04-22 02:21:35 +00:00
Claude 767de4e12c fix(hooks): narrow git-worker-guard -A pattern to avoid selective-flag false blocks
The worker guard's bulk-staging block lowercased the command before matching
`*git add -a*`, which could match `-a` + `-u` clusters or (more subtly) any
text starting with `-a` after `git add `. Legitimate selective patterns like
`git add -u` (update-only) happened to slip past only because `-u` doesn't
contain `-a`, but the intent was already murky.

Split the patterns:
  - commit/push/--all/. match against the lowercased command (subcommand
    names can reasonably be case-insensitive in alias contexts)
  - `-A` is checked against the raw command, so only the uppercase bulk flag
    trips the block

Adds four regression tests: `-A` still blocks, `--all` still blocks,
selective `-u` is allowed, filenames containing `-a` are allowed.

Embedded hook copy resynced via `make sync-hooks`.

https://claude.ai/code/session_01TZeRkm97SrPchDwPsD41rd
2026-04-21 06:52:58 +00:00
Claude dd2eadc3ad chore: resync embedded hooks and codex artifact hashes
Unblocks CI on this PR. Both were pre-existing drift on main:

- cli/embedded/.../test-pyramid.md: re-ran `cd cli && make sync-hooks`
  to match source under skills/standards/references/. Fixes the
  embedded-sync and go-build `Verify embedded hooks in sync` gates.
- skills-codex/{crank,quickstart,standards}/.agentops-generated.json
  and skills-codex/.agentops-manifest.json: re-ran
  `scripts/regen-codex-hashes.sh`. Fixes the codex-runtime-sections
  `Validate Codex generated artifacts` gate.

https://claude.ai/code/session_01W5b6kSdX9wEDgXfVGCXTZa
2026-04-21 01:23:47 +00:00
Boden Fuller 1e073d8363 chore: purge archived AO↔Olympus bridge integration + prefer local Go in precommit hook
Olympus (ol) was the power-user daemon predecessor to AgentOps, archived
in strategic-direction.md. The bridge contracts (SPEC_ONLY, never
enforced), MemRL policy surface, OL quarantine constraints collector, and
supporting scripts/tests all referenced a now-dead integration. Purging
them removes ~2400 lines of stale surface area.

## Deleted files
- docs/ol-bridge-contracts.md
- docs/architecture/ao-olympus-ownership-matrix.md
- docs/plans/2026-02-13-ao-olympus-bridge-next-steps.md
- docs/contracts/memrl-policy-integration.md
- docs/contracts/memrl-policy.schema.json
- docs/contracts/memrl-policy.profile.example.json
- cli/cmd/ao/inject_ol_test.go
- skills/vibe/scripts/ol-validate.sh + codex mirror
- skills-codex/swarm/scripts/ol-ratchet.sh, ol-wave-loader.sh
- tests/ol-integration/ (entire directory)

## CLI code stripped
- internal/search/types.go: OLConstraint struct + InjectedKnowledge.OLConstraints field
- internal/search/inject_run.go: CollectOLConstraints, WriteConstraintsSection, quarantineRelPath const, all related trim/render logic
- cmd/ao/inject.go: gatherOLConstraints, collectOLConstraints wrapper, olConstraint type alias, init-time call site
- Package comments in provenance/storage/types/formatter updated "olympus" → "agentops"
- formatter/markdown.go: session tag "olympus" → "agentops" (+ test update)
- Tests: deleted TestCollectOLConstraints + TestInject_collectOLConstraints, stripped OL subtests from TestFormatKnowledgeMarkdown and TestInject_*, renamed "olympus" literal test data to "other-repo"
- rpi_phased_context.go: stripped "matches Olympus DefaultTimeout" and "Modeled on Olympus internal/git/worktree.go" comments

## Docs + skills stripped
- mkdocs.yml nav: 3 entries removed (AO-Olympus Matrix, MemRL Policy, OL-AO Bridge)
- docs/INDEX.md, contracts/index.md, architecture/index.md, index.md: cross-refs removed
- strategic-direction.md: retirement rationale compressed to one paragraph
- docs/context-packet.md, positioning/devops-for-vibe-coding.md, plans/README.md: OL-specific content removed
- skills/vibe/SKILL.md + codex mirror, swarm/SKILL.md: OL script refs removed
- skills/vibe/references/deep-checks.md, plan/references/planning-rules.md (+ codex), pre-mortem/references/council-fail-patterns.md (+ codex): OL rule/pattern entries removed
- skills-codex hashes regenerated (plan, pre-mortem, swarm, vibe)

## Precommit hook fix
- hooks/go-test-precommit.sh: prefer ~/.local/go/bin/go when present. Hooks
  don't inherit interactive-shell PATH, so apt-provided go-1.22 would
  otherwise override a user-local 1.26 install. Mirrors the zshrc pattern.

## Retained (historical / useful)
- strategic-direction.md: 1-paragraph archival note
- Historical release notes (docs/releases/*.md) — immutable past-release record
- cli/testdata/transcripts/real-2.4mb.jsonl — captured test fixture
- docs/brownian-ratchet.md ol-rg3p bead-ID refs — internal bead prefix, not Olympus

## Verification
- go build ./... && go vet ./... clean
- go test ./... clean except pre-existing TestGCBridgeVersion_Live panic
- scripts/docs-build.sh --check (mkdocs strict) clean
- scripts/check-contract-compatibility.sh passes
- scripts/regen-codex-hashes.sh run — 4 skills updated
2026-04-19 14:33:00 -04:00
Boden Fuller cc6750c4e8 feat(docs,skills): rebuild Pages site with MkDocs Material + harden RPI orchestrator delegation contract
## Docs site — boshu2.github.io/agentops

- Replace Jekyll minima with MkDocs Material (slate dark palette default)
- Landing hero scoped to .landing-hero (no gradient bleed on inner pages)
- Section landings (getting-started, architecture, reference, contracts, levels) with narrative ledes
- Generate skill catalog + per-skill pages from skills/*/SKILL.md at build
- Generate CLI reference from cli/docs/ at build (fails build on missing source)
- Fix every internal link via one-shot migration (scripts/migrations/2026-04-19-fix-docs-links.py)
- Wire mkdocs build --strict into pre-push gate (Check 25a)
- Replace .github/workflows/pages.yml with .github/workflows/docs.yml (actions/deploy-pages)
- README + docs/CONTRIBUTING updated with docs-build toolchain on-ramp
- tests/docs/validate-links.sh patched to understand mkdocs-generated paths

## RPI orchestrator strict-delegation contract

- Strict sub-skill delegation is now the default for /rpi, /discovery, /validation. No --full flag — always on.
- Canonical contract in skills/shared/references/strict-delegation-contract.md (plus Codex mirror)
- Forged learning with detection phrases + corrective actions (ao-inject-ready)
- Rename --no-lifecycle -> --no-scaffold in /discovery (Claude + Codex) with deprecation alias through v2.40.0
- Harden inline-work hotspots with orchestrator-owned markers in /crank STEPS 3a.3, 6.5, 8.7 + Inline Work Policy footer
- Flag consistency: --auto and --interactive scopes documented; --complexity syntax formalized
- CHANGELOG [Unreleased] documents + scaffolds; runtime hook enforcement explicitly deferred

## fix(hooks): go-test-precommit skips when no Go files staged

- Previously ran go vet + go build unconditionally, blocking non-Go commits when Go toolchain was missing or at a different version
- Now checks staged diff and exits cleanly if no *.go or go.{mod,sum,work} files are staged
- Applied to both hooks/ (source) and cli/embedded/hooks/ (synced)

Provenance: full RPI lifecycle with strict delegation at every phase boundary (Phase 1 Skill(discovery) -> Phase 2 Skill(crank) -> Phase 3 Skill(validation) -> Skill(vibe), Skill(post-mortem), Skill(retro)).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 10:37:58 -04:00
Claude fc42b0aa63 fix(overnight): findings-router emits claim_status=available, not pending
tests/smoke-test.sh validates every next-work.jsonl entry against
docs/contracts/next-work.schema.md; the entry-level claim_status
enum is {available, in_progress, consumed}. Dream's
findings_router.go was opening brand-new entries with "pending",
which the schema doc explicitly says writers must emit as
"available" (§Lifecycle, §Enums). Today's Dream run appended such
a line, tripping smoke-test on PR #116.

- Flip the writer literal to "available".
- Patch the one offending line already on disk
  (.agents/rpi/next-work.jsonl line 56).
- Extend TestRouteFindings_EmitsSchemaCompliantEnums to assert the
  entry-level claim_status against a validNextWorkClaimStatuses
  set so this exact regression is caught at test time, not push
  time.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:32:58 +00:00
Claude 535dc4ac7f test(compile): cover runCompilePreflightActions done-signal contract
The extracted preflight helper signals 'done=true' only when a
standalone --reset or --repair should short-circuit the pipeline.
Added focused tests for standalone --reset, composed --reset --full,
and the no-op path so the contract stays guarded if future flags
shift the short-circuit rules.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:18:08 +00:00
Claude 9eaa489df5 refactor(compile): split runCompile into phase + preflight helpers
runCompile sat at CC 30, well over the cli/ ceiling of 20. Extracted:

- runCompilePreflightActions: standalone --reset / --repair flow
  and the composed "reset then exit" / "repair then exit" logic.
- runCompileMinePhase / runCompileScriptPhase / runCompileDefragPhase:
  per-phase runners that return a phase result or wrapped error.

The top-level orchestrator now reads as linear step-by-step
sequencing and drops to CC 14. No behavior change — full
TestRunCompile_EndToEnd_FixtureCorpus and sibling compile tests
still pass.

With this, bash scripts/check-go-absolute-complexity.sh --dir cli/
--threshold 20 reports "All functions in cli/ are below complexity
20." for the first time since the ceiling was raised back to 20.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:11:11 +00:00
Claude e2879235ea refactor(compile): extract article scan / inbound count / prune from repair
repairCompileOutput grew a nested scan, an O(n²) inbound-count
loop, and a prune loop inline, driving CC to 24 (over the cli/
ceiling of 20). Split into three pure helpers:

- collectCompileArticles: scan a directory into compileArticle
  records, skipping the preserved-set.
- computeInboundCounts: build the slug -> inbound-link map.
- pruneOrphanArticles: apply the dry-run / --force-repair policy.

The caller becomes a thin orchestrator well under the CC ceiling
while preserving dry-run semantics and existing compile tests.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:08:53 +00:00
Claude 2f6105bc52 refactor(overnight): split dream packet corroboration per source epic
dreamBuildPacketCorroboration carried one switch with three nested
evidence branches, pushing CC to 24 (over the cli/ ceiling of 20).
Split each SourceEpic case into a dedicated builder and made the
caller a thin orchestrator. Behavior preserved; existing
TestExecuteDreamMorningPackets_AppliesPacketCorroboration stays
green.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:07:24 +00:00
Claude f7804f8e0d refactor(overnight): extract dream yield emptiness guard
appendDreamYieldSection carried an 8-field compound guard that pushed
cyclomatic complexity to 21, one over the cli/ CC ceiling of 20.
Extracting the guard into dreamYieldIsEmpty drops the renderer below
the ceiling and makes the "nothing to render" rule testable in
isolation.

Added table-driven coverage for the new helper plus golden-path and
empty-path assertions for the renderer.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 07:05:11 +00:00
Claude df095d89c9 test(quality): add direct unit tests for stale-refs helpers
Bumped direct coverage of CheckStaleReferences and CountUniqueFiles
from 0% to 100% by testing them against the quality-package API
rather than only through cmd/ao/doctor_test.go. Keeps regression
signal close to the function under test.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 06:58:28 +00:00
Claude 577af4143e feat(doctor): scan skills-codex and skills references for stale commands
The stale-refs scanner missed two real surfaces: skills-codex/*/SKILL.md
(codex mirrors) and skills/*/references/*.md (skill reference docs).
Both are documentation paths users consult, and drift there is just as
harmful as drift in skills/*/SKILL.md.

Added a focused test covering the new globs.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 06:56:43 +00:00
Claude f6f03f1bf8 fix(quality): skip rename-doc lines in stale-refs scan
Lines documenting command renames (`ao old` → `ao new`) intentionally
mention the deprecated form; treat them as doc, not stale usage. Fixes
the recurring warn from ao doctor on docs/CHANGELOG.md.

https://claude.ai/code/session_01FDsQrm2EbQ8ubcJcJren9N
2026-04-19 06:41:34 +00:00
Bo e2e353e4f3 Merge pull request #114 from boshu2/nightly/2026-04-17
Nightly 2026-04-17 — 6 cycles, ASCII fast-path sweep + bootstrap-maturity JSONL fix
2026-04-17 08:12:42 -04:00
Claude ac90b95508 test(demo): zero the pacing sleep in demo tests, save ~7s per suite run
quickDemo uses a 500 ms terminal-pacing sleep between its 5 'cards'
for human readability. Under test, that pause is pure dead time:
2.5 s × 3 tests (TestDemo_QuickDemo, TestDemo_RunDemoDispatch_Quick,
TestCobraDemoQuickCommand) = ~7.5 s of waste per suite run.

Refactor: introduce a package-level demoStepDelay time.Duration (500 ms
default — same behavior in production) and a test helper
withoutDemoDelay(t) that zeroes it for the duration of a test. No
production code path changes behavior.

Observed impact (go test -count=1 -run ...):
- TestDemo_QuickDemo: 2.50 s -> 0.00 s
- TestDemo_RunDemoDispatch_Quick: 2.50 s -> 0.00 s
- TestCobraDemoQuickCommand: 2.52 s -> 0.00 s
- Full ./cmd/ao suite: 62 s -> 55 s
2026-04-17 06:58:04 +00:00
Claude 2da07bb0f2 fix(overnight): collapse critical/blocker severity to high for next-work v1.3
mapSeverity could return "critical" but the next-work v1.3 severity
enum is strictly {low, medium, high} (see
docs/contracts/next-work.schema.md §Enums → Severity). A finding
frontmatter with severity: critical or severity: blocker would have
produced a next-work.jsonl line that the pre-push contract parity
gate rejects.

Collapse "critical" and "blocker" to "high" — they represent the
strongest signal the next-work contract supports, so the mapping is
lossless at that ceiling.

Also extend the cycle-2 schema guard test to check severity, and add
TestMapSeverity_CollapsesCriticalToHigh to lock in the full mapping
table (low/minor, medium/significant, high/major/critical/blocker,
default, case-insensitive, whitespace-tolerant).
2026-04-17 06:48:23 +00:00
Claude bcd6c336ca test(overnight): build-time guard that findings router emits schema-compliant enums
Cycle 1 fixed the router's schema-invalid enum values; this adds a
guard test so the same drift cannot recur without a deliberate test
update.

TestRouteFindings_EmitsSchemaCompliantEnums hard-codes the Type and
Source enum sets from docs/contracts/next-work.schema.md and asserts
that RouteFindings emits values from them. If the schema doc genuinely
grows a new enum value, the test comment directs the reader to update
both the doc and this test together — same shape as other contract
parity checks in this repo.

Previously the only drift surface was validate-next-work-contract-parity.sh,
which runs at push time against the concrete .agents/rpi/next-work.jsonl
file. This test catches the drift at 'go test' time.
2026-04-17 06:44:16 +00:00
Claude 271d4de41d fix(overnight): emit valid next-work v1.3 enums in findings router
RouteFindings was emitting type="finding" and source="finding-router",
neither of which is in the next-work v1.3 enums (see
docs/contracts/next-work.schema.md). Every Dream run was tripping the
pre-push gate's next-work contract parity check, forcing an auto-revert.

Map the router output to the closest valid enums:
- type: "finding"         -> "tech-debt"
- source: "finding-router" -> "council-finding"

Original finding provenance is still carried in SourcePath and the
finding file's own frontmatter; no information is lost for downstream
consumers (triage, swarm, flywheel).

Test updated to assert the new enum values explicitly.
2026-04-17 06:36:42 +00:00
Claude cbeb606831 perf(cli): finish ASCII fast-path sweep across remaining truncate sites
Completes the sweep started in cycles 1-4. Applies the same byte-length
upper-bound fast-path to the four remaining rune-safe truncation sites:

- internal/search/search_cass.go TruncateContext
- internal/parser/parser.go Parser.truncate
- cmd/ao/contradict.go truncateSnippet
- cmd/ao/extract.go truncateForPrompt

All call sites now follow the same pattern: if len(s) <= max, return s
without allocating a []rune slice. Behavior is unchanged for inputs
whose byte length exceeds max (the rune-count branch still fires).
2026-04-17 02:58:14 +00:00
Claude 2d766145b6 perf(goals,pool): ASCII fast-path sweep for byte-length rune truncation
Applies the same fast-path introduced in cycles 1-3 to the remaining
call sites in goals/measure.go (truncateOutput) and pool/pool.go
(truncateAtWordBoundary). The existing measure.go comment explicitly
flagged the opportunity ("ASCII-only strings could use a len(s)
fast-path") — honoring that note.

Adds a multibyte regression case to truncateAtWordBoundary so the
rune-count fallback is exercised under the fast-path gate.
2026-04-17 02:51:46 +00:00
Claude 74dfab3a27 perf(search): ASCII fast-path for TruncateText
Third application of the byte-length-upper-bound fast path for
rune-safe truncation (after truncateForError and TruncateRunes).
When len(s) <= maxLen, the rune count cannot exceed maxLen either,
so the []rune allocation is skipped. Adds multibyte tests covering
all three branches: fast-path, rune-count fallback, truncation.
2026-04-17 02:50:33 +00:00
Claude 9a1e5c9f19 perf(rpi): ASCII fast-path for TruncateRunes
Same pattern as cycle 1 (cli/internal/parser/parser.go truncateForError):
byte length is an upper bound on rune count, so when len(s) <= cap we can
skip the []rune allocation. Adds a multibyte test that proves the
rune-count fallback still fires when byte length exceeds cap but rune
count fits, ensuring the fast-path does not change behavior.
2026-04-17 02:49:17 +00:00
Claude 8dca64ffb1 perf(parser): ASCII fast-path for truncateForError
When the input byte length already fits within maxLen, the rune count
cannot exceed it either (bytes >= runes). Short-circuit before the
[]rune allocation. Adds multibyte test cases proving both the fast-path
(bytes==runes), the rune-count fit case (bytes>maxLen, runes<=maxLen),
and the truncation case.

Closes finding: ag-yht / Add ASCII fast-path comment to truncate()
2026-04-17 02:45:14 +00:00
Boden Fuller 48bee957b0 fix(hooks): pre-mortem gate locks on ambiguous state (deny by default)
Previously the pre-mortem gate fell back to UNLOCKED on missing,
unparseable, stale, or unrecognized state. A broken state file
silently disabled the safety gate. Flips default fallback to strict:
lock on any ambiguity, unlock only on explicit unambiguous evidence.

Strict default rules:
- Missing state file: BLOCKED
- Unparseable state (jq -e . fails): BLOCKED with clear error
- Verdict not in allow-list (passed|locked|ok|ratchet|approved): BLOCKED
- Missing epic-id or missing bd CLI in strict mode: BLOCKED
- Valid recognized verdict: ALLOWED
- Run-scoped orchestration log / epic-scoped council artifact / ratchet
  status locked|skipped / chain.jsonl pre-mortem done: ALLOWED

Bypass: AGENTOPS_PREMORTEM_GATE_BOOTSTRAP=1 unlocks with mandatory
stderr warning 'WARN: pre-mortem-gate bypassed via ...'. Distinct
from the silent AGENTOPS_HOOKS_DISABLED=1 kill switch.

Legacy AGENTOPS_PREMORTEM_FALLBACK=open restores the previous silent-
allow behavior for operators who need the old semantics during the
transition.

Adds 8 new behavioral tests (20 total pass): missing/unparseable/
passed/expired/unknown-verdict/bootstrap/kill-switch/legacy-open.
Synced to cli/embedded/hooks/pre-mortem-gate.sh.

Consumes ag-yhr 'Harden pre-mortem gate fallback to locked-state
semantics'.
2026-04-15 16:48:31 -04:00
Boden Fuller 7f9a6bfb38 feat(hooks): enforce lead-only worker git guard as PreToolUse hook
Adds hooks/lead-only-worker-git-guard.sh: mechanical PreToolUse guard
that blocks destructive git commands (commit, push, reset, rebase,
merge, cherry-pick, branch -D, checkout -B, worktree remove) when
running inside a swarm worker context.

Worker detection (in order): AGENTOPS_SWARM_ROLE=worker,
AGENTOPS_ROLE=worker (legacy), CLAUDE_AGENT_NAME=worker-*,
.agents/swarm-role file, CWD under /.claude/worktrees/. Allows all
non-destructive git (status, diff, log, show, blame, fetch) and safe
variants (branch -d, checkout -b). Allows everything in lead context.

Respects AGENTOPS_HOOKS_DISABLED=1 (global kill) and
AGENTOPS_LEAD_ONLY_GUARD_DISABLED=1 (local disable). Catches chained
commands (foo && git commit) via leading-token regex.

Registered under PreToolUse / Bash matcher in hooks/hooks.json.
Synced to cli/embedded/hooks/ via make sync-hooks. Adds 30-assertion
test file covering block paths, allow paths, kill switches, legacy
signals, and native-team worker naming.

Consumes ag-yhr 'Enforce lead-only worker git guard before spawn'.
Mechanical enforcement of the swarm skill's lead-only commit policy
which was previously informational only.
2026-04-15 16:48:09 -04:00
Boden Fuller 729eaf2783 feat(shellutil): sanitize bash invocations to bypass user shell aliases
Adds cli/internal/shellutil package with:
- SanitizedBashCommand(ctx, script) — bash --noprofile --norc -c with
  BASH_ENV and ENV stripped from the environment.
- SanitizedEnv(env) — exported for composition.

Wires two goals-package callsites through the helper:
- MeasureOne (cli/internal/goals/measure.go) — user-authored goal
  check strings run via bash -c and could be silently rewritten by
  aliases defined in BASH_ENV + shopt -s expand_aliases.
- AddGoal dry-run check (cli/internal/goals/commands.go).

New tests cover env filter correctness, flag shape, BASH_ENV stripping
from cmd.Env, context cancellation, and a regression guard that writes
a real BASH_ENV rcfile aliasing echo -> 'printf HIJACKED' and asserts
the alias does NOT leak into the sanitized command.

Consumes swarm-post-mortem-findings 'Sanitize agent shell environment
to bypass aliases'.
2026-04-15 15:53:15 -04:00
Boden Fuller d03ff5620b chore(bd): default BEADS_DOLT_SERVER_PORT=3307 in hook helpers + bd scripts
lib/hook-helpers.sh exports BEADS_DOLT_SERVER_PORT with override via
${BEADS_DOLT_SERVER_PORT:-3307} so users can still override per shell.
All hooks that source hook-helpers.sh automatically inherit the port.

scripts/bd-audit.sh and scripts/bd-cluster.sh are standalone bd callers
that do not source hook-helpers.sh, so they get the same export inline.

cli/embedded/lib/hook-helpers.sh synced via make sync-hooks.

Consumes ag-co7 'Export BEADS_DOLT_SERVER_PORT=3307 in shell env'.
2026-04-15 15:53:15 -04:00
Boden Fuller 47c88061f7 fix(mine-test): isolate HOME in new mine --json tests
The two new regression-guard tests from c83628bd ran with the real
HOME, so when 'ao mine --sources agents' executed it walked the
user's ~/.agents/ corpus and the agents-hub content-hash gate
detected mutation. t.Setenv HOME to t.TempDir() for both tests,
matching the convention in autodev_test.go and cobra_commands_test.go.
2026-04-15 15:13:39 -04:00
Boden Fuller 05b17e8b72 refactor(handoff): extract field rendering helpers from renderHandoffEntry
buildHandoffContext was already clean (complexity 8) from a prior
refactor. The actual inlined per-field rendering lived in
renderHandoffEntry (complexity 13) as 8 consecutive fieldAllowed
blocks + narrative. Extracts there.

Ten new private helpers, all in rpi_phased_handoff.go:
renderHandoffHeader, renderVerdictsSection, renderEpicSection,
renderArtifactsSection, renderAppliedFindingsSection,
renderPlanningRulesSection, renderKnownRisksSection,
renderDecisionsSection, renderOpenRisksSection, renderNarrativeSection.

Complexity: renderHandoffEntry 13 -> 1. Max new helper: 4.
Zero test modifications (byte-for-byte output preserved).

Consumes 'Extract buildHandoffContext field-rendering helpers' from
context-orchestration-leverage epic. discoverPhaseArtifacts at
complexity 15 flagged as next refactor target.
2026-04-15 15:04:31 -04:00
Boden Fuller 56c4462d77 fix(rpi-serve): relax --run-id flag pattern to accept legacy 8-hex
ao rpi serve --run-id <id> rejected legacy bare 8-hex run IDs (e.g.
0aa420a9, 3e298842) that still exist on disk at .agents/rpi/runs/<id>/,
forcing users to either pass them positionally (where they misroute
as goal strings and trigger a new orchestration instead of watching
the existing run) or not at all. Commit e8572ee8 (2026-03-05)
tightened ServeRunIDPattern to avoid false positives with short git
SHAs typed as positional goals, which was correct for the positional
branch but wrong for the explicit --run-id flag where user intent is
unambiguous.

Splits the regex:
- ServeRunIDPattern (positional, strict) — unchanged
- ExplicitServeRunIDPattern (flag path, permissive) — adds the
  8-hex form: ^(rpi-[a-f0-9]{8,12}|[a-f0-9]{8}|[a-f0-9]{12})$

ClassifyServeArg flag branch and ValidateExplicitServeRunID now use
the permissive pattern. Error message updated to mention <8 hex>.

New cli/internal/rpi/serve_test.go covers flag-vs-positional
asymmetry, numeric/slug/UUID/invalid shapes, and a proper-superset
invariant. Existing positional classifier test unchanged.

Consumes 'Fix rpi serve --run-id classification for run IDs'
from v2.9.3..HEAD epic.
2026-04-15 15:04:31 -04:00
Boden Fuller c83628bdaa fix(mine): emit JSON in --dry-run path + exactly-once stdout guard
ao mine --json --dry-run previously emitted the human-readable
printMineDryRun output and ZERO JSON, making the --dry-run flag
unusable by any piped consumer (jq, scripts, tests).

Reorders runMine so the JSON-output check is evaluated inside the
dry-run branch; adds encodeMineDryRunJSON helper emitting a single
structured document with {dry_run, sources, window, output_dir, note}.

Normal (non-dry-run) JSON path was not double-writing, but adds
TestMineCommandJSON_ExactlyOnePayloadOnStdout as a strict regression
guard against the class of bug the original ag-co7 report described
(json.Decoder.Decode followed by second-decode-must-fail + no trailing
non-whitespace bytes). Also adds TestMineCommandJSON_DryRunEmitsSingleJSONDocument.

Consumes ag-co7 'Fix ao mine --json stdout double-write'.
2026-04-15 15:04:31 -04:00
Boden Fuller 0b03ce3907 fix(overnight): raise Dream Council runner cap to 180s + make configurable
Real overnight runs repeatedly logged 'claude council timed out after
1m30s' while Codex completed successfully. The Claude lane's cold-start
plus structured-output envelope routinely lands in the 90-150s band, so
the hard-coded 90s runner cap was structurally too tight.

- dreamCouncilRunnerTimeout default: 90s -> 180s (evidence-backed).
- withDreamCouncilRunnerTimeout now takes a per-run configured
  duration (zero = default); still clamps to parent deadline.
- overnightSettings.CouncilRunnerTimeout + new
  resolveDreamCouncilRunnerTimeout helper plumb the duration through.
- config.go: new DreamConfig.CouncilRunnerTimeout field
  (yaml: dream.council_runner_timeout) + merge wiring.

Adds 3 tests: config parse paths, default=180s invariant +
configured-wins + parent-deadline-wins, and a regression guard
asserting the runner lane deadline is >90s.

Consumes na-jox1 from .agents/rpi/next-work.jsonl (source_epic=na-22xi).
2026-04-15 14:50:26 -04:00
Boden Fuller a07f7c93d8 docs(cli): regenerate COMMANDS.md for --force-repair
Syncs CLI reference after adding --force-repair flag in
9218b7ba (fix(compile): default --repair to dry-run).
2026-04-15 13:55:25 -04:00
Boden Fuller 04f8b4563c fix(compile): strand-safe hashes + strip markdown-fence cruft
Two related compile.sh bugs fixed together (both in the batching script).

1. Incremental + --max-batches corrupted hash state. compile_single_batch
   persisted hashes for every file in the inventoried batch regardless of
   whether an article was actually written; when the outer loop hit
   MAX_BATCHES, uncompiled files got their hashes saved and were skipped
   forever on the next --incremental run. Now save_hashes runs only when
   the batch produced output, and only for files with an emitted article.

2. claude-cli wraps its output in ```markdown ... ``` fences. Every article
   from real smoke runs inherited the cruft. Fixed in two places:
   - system prompt now explicitly forbids markdown fences
   - article splitter skips lines matching the fence regex as defense in depth

Adds skills/compile/scripts/tests/test-fence-stripping.sh fixture.
Syncs cli/embedded/ copy via make sync-hooks.
2026-04-15 13:52:18 -04:00
Boden Fuller 34647fc6fa fix(harvest): slim ExcludedCandidates JSON output (schema v2)
BuildCatalog previously serialized full Artifact structs (including
heavy Frontmatter map) for every excluded candidate. With >100
exclusions the JSON ballooned. Projects to ExcludedArtifactMeta
{id, title, confidence} on serialization; keeps excludedFull
in-memory for TopExcludedNearMiss(). Bumps CatalogSchemaVersion to 2.

BREAKING: catalog JSON consumers reading excluded_candidates.*
fields beyond id/title/confidence must update. Verified no
in-repo consumers affected.
2026-04-15 13:46:18 -04:00
Boden Fuller dbd5ed3ad5 fix(config): surface YAML parse errors to stderr
Invalid YAML in ~/.agentops/config.yaml previously fell through silently
to auto-detect. Now warns 'Warning: invalid YAML in <path>: <err>' and
still falls back (backward compat). Missing files remain silent.
Adds TestLoadConfig_SurfacesYAMLErrorToStderr.
2026-04-15 13:46:14 -04:00
Boden Fuller 9218b7ba62 fix(compile): default --repair to dry-run, require --force-repair to delete
Prevents accidental wiki deletion from regex bugs in orphan detection.
--repair now reports orphans only; --force-repair actually deletes.
Adds TestRepairCompileOutput_DryRunByDefault and _ForceFlagDeletes.
2026-04-15 13:46:06 -04:00
Boden Fuller fd919b715c feat(compile): runtime preference in ~/.agentops/config.yaml (na-pmx1.9)
Users who have 'claude' on PATH but prefer Ollama (for privacy, cost,
or latency) were forced to export AGENTOPS_COMPILE_RUNTIME=ollama in
every shell. Add a persistent preference:

    ~/.agentops/config.yaml
    compile:
      preferred_runtime: ollama

Precedence (high → low):
  1. --runtime flag
  2. AGENTOPS_COMPILE_RUNTIME env var
  3. compile.preferred_runtime config
  4. auto-detect (claude binary → claude-cli)
  5. empty (preflight error)

Tests cover all 5 precedence levels including a graceful fallback
when the config load itself errors.

Closes na-pmx1.9.
2026-04-15 12:52:31 -04:00
Boden Fuller d579a32197 refactor(compile): flatten batching recursion into explicit outer loop
The prior implementation used recursive self-invocation with env-var
prefix assignment (BATCH_SIZE=$total MAX_BATCHES=0 compile_articles < "$tmp")
to disable further batching inside each sub-call. That worked but was
opaque and hard to test.

Split into two clear functions:

- compile_articles() — chunks changed_files into BATCH_SIZE slices and
  iterates compile_single_batch per slice via an explicit for-loop.
  No recursion, no env prefix trick.
- compile_single_batch() — compiles one chunk via a single LLM call;
  caller is responsible for chunking.

The integration test (TestRunCompile_EndToEnd_FixtureCorpus) exercises
multi-batch behavior end-to-end and still passes. No flag surface
changes. Embedded copy re-synced.

Closes na-pmx1.6.
2026-04-15 12:47:29 -04:00
Boden Fuller 2b4d200b94 feat(compile): add --reset and --repair flags (na-pmx1.8)
--reset: delete .agents/compiled/ and .hashes.json before compiling.
Use to force a full rebuild or clean up stale output from a prior
broken compile run. Safe on missing dirs (idempotent).

--repair: scan .agents/compiled/ and remove article .md files with
zero inbound [[wikilinks]]. Infrastructure files (index.md, log.md,
lint-report.md) are always preserved. Addresses the 2026-04-15
scenario where a failed compile run left fallback stubs that later
had no natural way to be cleaned up.

Both flags are standalone-capable (no LLM runtime required) and
compose with --full / --compile-only.

Tests:
- TestResetCompileOutput_RemovesDirectory
- TestResetCompileOutput_IdempotentOnMissingDir
- TestRepairCompileOutput_RemovesOrphansOnly

Regenerated COMMANDS.md for the new flag entries.

Closes na-pmx1.8.
2026-04-15 12:45:40 -04:00
Boden Fuller 59104040a1 feat(harvest): surface min-confidence exclusion count + top-5 near-miss
ao harvest previously reported only "Promoted N artifacts" and silently
dropped every deduped winner that fell below --min-confidence. Users
could not see how much signal the threshold was cutting or whether a
slight tune (0.5 → 0.45) would materially change the promotion set.

Catalog now carries ExcludedCandidates (sorted by confidence desc).
outputHarvestCatalog prints:

    Excluded N candidate(s) under confidence threshold 0.50
      Top near-miss candidates (closest to threshold):
        0.48  promotable-learning-one  (learning)
        ...

Tests:
- TestBuildCatalog_TracksExcludedCandidates: sort order, n-bounds, nil on n<=0

Closes na-pmx1.10.
2026-04-15 12:44:14 -04:00
Boden Fuller 91da626e48 test(compile): end-to-end integration test with fixture corpus
TestRunCompile_EndToEnd_FixtureCorpus exercises the full ao compile
--full path against a tiny 8-file .agents/ fixture, using a PATH-shim
claude binary that echoes a canned ARTICLE+INDEX response. Batch size
is set to 3 so the 8 files split across multiple batches, exercising
the batching logic end-to-end.

This catches the 2026-04-15 regression class at integration level:
  - embed miss ("open skills/compile/scripts/compile.sh: file does not exist")
  - runtime preflight silently skipped
  - single-giant-prompt on large corpora (batching proven by fan-out)

Closes na-pmx1.3.
2026-04-15 12:37:25 -04:00
Boden Fuller c9134d1aaf test(harvest): update malformed fixtures after salvage pass
The salvage pass added in the previous commit rescues frontmatter where
a scalar value contains ": " mid-string. Two tests that used that exact
shape as their "malformed" fixture now correctly succeed at extraction.

Switch those fixtures to tab-indented YAML, which is genuinely
unparseable and salvage-proof, so the "continues after malformed
frontmatter" and "malformed file becomes warning" assertions still
exercise the failure path.

Follow-up to na-pmx1.2.
2026-04-15 12:36:10 -04:00