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
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
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
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
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
## 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>
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
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
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
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
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
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
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
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
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
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
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).
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.
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.
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).
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.
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.
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.
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()
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'.
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'.
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'.
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.
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.
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.
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'.
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).
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.
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.
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.
Prevents accidental wiki deletion from regex bugs in orphan detection.
--repair now reports orphans only; --force-repair actually deletes.
Adds TestRepairCompileOutput_DryRunByDefault and _ForceFlagDeletes.
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.
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.
--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.
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.
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.
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.