Repository navigation
Procedure trigger audit: measure which operations procedures fire, then fix the worst #164
Description
What: Measure how often each operations procedure fires when it should, then fix the worst triggers, starting with wording.
Why: Most procedure triggers are judgments ("substantive prose editing", "material work", "when the action changes") one or two links below the only description the host shows, and nothing tests whether they fire.
So what: An agent builds the trigger table and the baseline from real transcripts now; rewording follows in one PR, and a hook only where rewording measurably fails.
Outcome
A trigger-coverage table for every row of skills/operations/SKILL.md, each with an observable trigger event, the file that must be opened, and its skip rate in real sessions on Claude Code, Codex, Grok and OpenCode. The worst rows get reworded; a re-measurement after a week of real work shows the change.
Evidence (2026-09-29)
- Research report on building-agentskills and the AgentsMD writing guides (Opus 5.5, T3 thread "Research: building-agentskills + AgentsMD writing guides"):
- Only Project Direction loading is enforced by mechanism (hook). Every other row depends on the agent noticing a phase change.
- Rated weak:
technical-writing->references/prose.md("when revising unclear or padded text"),writing-for-agents->prose.md("substantive prose editing"), Software design, Artifact placement, Reflection (friction), Finalization, Wayfinder, Repository setup, Reconciliation. Medium: Writing for agents (loses to the Implementation row on an edit),SKILL-MECHANICS.md("before choosing frontmatter" misses editing an existing description), Verification ("claim readiness" passes unnoticed), Elon method ("material"). - AgentsMD chains references two hops deep (SKILL -> writing-for-agents -> SKILL-MECHANICS or prose); building-agentskills
docs/05-authoring/line-budget.mdsays one level, never nested. - Existing tests pin wording and links only (
tests/test_operations_contract.py); none tests firing.
- Research report on obra/superpowers (Opus 5.5, T3 thread "Research: obra/superpowers description guidance"):
- Firing comes from an always-injected bootstrap plus decision-time rationalization tables, not from descriptions. Descriptions are short (92-392 chars), start "Use when", and anchor in time ("before writing implementation code").
- Test method: naive prompts that do not name the skill; deterministic transcript checks (skill called, not called, called before the first Edit/Write); negative over-trigger scenarios; a no-guidance control; five or more runs; the models users actually run (one skill went 5/5 on Opus 4.8 to 0/5 on Opus 5).
- This session: the planner skipped
SKILL-MECHANICS.mdandprose.mdwhile rewriting T3 tool descriptions (Delegation tools are always loaded and name the shell CLIs they replace chromeria#66). - Elon method rarely triggers: gated on Project Direction and tied to artifacts #138 method for the Elon method: 9 of 11 Issue-creating sessions skipped it before rewording, 5 of 13 after.
Elon record
- Requirements and who asked: the user (2026-09-29) asked how to verify that every AgentsMD procedure triggers when it should, naming skill mechanics and prose as examples, and agreed to measure first, reword second, and use hooks last.
- Deleted: hooks as a first fix; synthetic scenario runs as a release gate (the Objective does not require them); rewriting rows that measure fine.
- Bottleneck: no measurement exists, so nobody knows which procedures are dead weight.
- Checked myself: read both research reports in full and the routing table rows they cite; reran the Elon method rarely triggers: gated on Project Direction and tied to artifacts #138 count for the Elon method today.
Acceptance criteria
- Trigger table: every routing row has an observable trigger event and required file, retained under
docs/work/. - Baseline skip rate per row from the last 14 days of real transcripts, per host where transcripts exist, with the counting script retained.
- One PR rewords the worst rows: observable "before " triggers, no reference deeper than one hop where a trigger was missed.
- Re-measurement one week after release, reported per row.
Non-goals
- Hooks for procedures that have not failed after rewording.
- Making synthetic model runs a release gate.
Blockers
None.
Required proof
The retained script and both measurements; full suite and versionctl release-check for the rewording PR.
Activity
wip: #164 trigger table and baseline skip rates
feat: name the edit in missed procedure triggers, one hop from routing (
fix: clean up trigger-test temp state, guard ignored files, commit pe…
fix: trigger-test setup removes its own partial state on failure (#164)
test: four-host trigger benchmark with the user's models (#164)
wip: #164 trigger table and baseline skip rates
feat: name the edit in missed procedure triggers, one hop from routing (
fix: clean up trigger-test temp state, guard ignored files, commit pe…
fix: trigger-test setup removes its own partial state on failure (#164)
test: four-host trigger benchmark with the user's models (#164)
feat: release procedure triggers one hop from routing as 14.2.0 (#164)
feat: name the edit in missed procedure triggers, one hop from routing (
fix: clean up trigger-test temp state, guard ignored files, commit pe…
fix: trigger-test setup removes its own partial state on failure (#164)
test: four-host trigger benchmark with the user's models (#164)
6 remaining items
lukemaj commentedon Oct 1, 2026
Baseline, real sessions on the current routing rows (measured 2026-10-01 17:50 UTC). Frozen definition: previous comment. Sessions started 2026-09-30 or later: Claude Code 33, Codex 23, Grok 9, OpenCode 4 (all top-level; 353 Claude and 20 OpenCode sessions under tmp/eval directories dropped). Cells read skipped/trigger sessions.
entryelonelon>algorithmtickets>decompimplimpl>veriftest-designorchverifreviewreview>methodartifactswfawfa>mechanicswfa>prosetwtw>prosevcdelivery-profiledeliveryfinalRows with no trigger in the window are left out (pd, grilling, domain, wayfinder, spec, orch>bounded, proj-verif, reflection, vc>bump, delivery>final, repo-setup, grok).
Worst rows, by the frozen rule (8 or more triggers):
delivery: 11/12 skipped. Atgh pr createagents readverification.md(9/12 fired) but notdelivery.md, which holds the PR-body rules (delivery.mdline 34: "Before opening or updating a PR, read CONTRIBUTING.md ... Open the PR body with the core's Human summary"). The row says "Deliver a PR, merge, release"; it never names opening one.orch: 8/10 skipped. The row "Delegate or coordinate dependencies, recover a handoff" never names spawning an agent or thread.vc: 9/15 skipped, mostly Claude Code (9/14). The row "Change tracked files or make an explicit WIP checkpoint" never names committing.The
verif(25%),impl(20%) andentry(15%) rows improved sharply since #174 and #182 (they were at 68%, 73% and 67%).Case study (correcting my previous comment): session
86d43ea9made 18 file edits, about 29git commitcalls, 9gh pr createand 4gh issue create, and researched and recommended a monitoring design. Its only Skill call wasupdate-config; it never openedoperations. The session-start pointer reached it on 3 of its roughly 30 hook deliveries (theSessionStart:resumeevents). Every later user turn started a new kind of action with no reminder: new tasks arrive as user turns, not as session starts. The single-prompt harnesses (#174, #182) cannot see this, because their pointer is always fresh.Limits. The window is about 1.5 days, so samples are small; Codex, Grok and OpenCode have 1 to 3 triggers per row. Most Claude triggers come from a few long T3 sessions.
elontriggers include false positives:gh issue creatematched inside a Python transcript reader and inside agh pr commentbody.Next: reword the
delivery,orchandvctriggers as "before " clauses, the shape #182 proved. Measure before and after with a multi-turn run on all four harnesses, with the pass condition frozen here before it runs.lukemaj commentedon Oct 1, 2026
Before/after measurement, frozen before running (2026-10-01). Pass condition and scenario are fixed here; nothing below changes after results come in.
Instrument:
docs/work/164-trigger-audit/multiturn.pyatee9c93d(sha256f8266d5f…69b21). It reusestrigger_test.py's confinement, real plugin loading per host, fixture repository,ghstub and read scoring. New: one session, five user turns, continued the way each host continues a session (Claude Code: oneclaude -p --input-format stream-jsonprocess, a new user message per turn, so SessionStart fires once, as in T3; Codexexec resume --last; Grok-p --continue; OpenCoderun --continue). Each routed action is scored against reads made before it anywhere in the session.Turns (prompts never name a procedure):
divide()does when b is 0, from the code onlyresearch/index.mdbefore the turn endsdivide()raise ValueError, then commit; scoredimplementation.mdbefore the first edit andversion-control/index.mdbeforegit commitgh pr create; scoredverification.mdanddelivery.mdbeforegh pr createorchestration.mdbefore the first spawn (Agent/Task/spawn_agent/spawn_subagent/tasktool, or a shellcodex exec/claude -p/opencode run/grok -p)Arms:
main=8cad98f(origin/main),branch=2b04650(the three reworded rows only),none= no-guidance control (login only: no rules file, no plugin). Arms interleaved.Harnesses: Claude Code
claude-opus-5-5effort medium; Codexgpt-6.1-solmedium; Grokgrok-4.7medium; OpenCodeopencode/muse-spark-1.3-contributor-free(default variant). 5 valid runs per arm and host. A run is invalid when fewer than 5 turns ran or it made no tool call; invalid runs are kept in the records and replaced, at most 3 replacements per cell.Canaries (passed, before this freeze): every host, every arm: main and branch quote
# Global Agent Rulesand see theoperationsSkill; Claude Code and OpenCode also see the session pointer;nonesees neither.Pass condition:
version-control/index.mdat commit,delivery.mdatgh pr create,orchestration.mdat spawn): branch reads the file before the action in at least 4 of the runs where the action happened, with at least 3 such runs; otherwise the cell is "not measurable". A row ships when it passes on every measurable host.research/index.md,implementation.mdandverification.mdon branch at most 1 run below main, per host.nonecontrol fires 0 everywhere.Decision rule: a row that fails P1 on a host where
operationswas never loaded in the session is a candidate for a hook. Any hook gets its own frozen measurement before it ships. Records (per run: call list up to the end, anonymized paths, verdicts, usage) go todocs/work/164-trigger-audit/multiturn.jsonl.wip: pin the delivery, orchestration and version-control triggers (#164)
feat: name the action in the delivery, orchestration and version-cont…
lukemaj commentedon Oct 1, 2026
Before/after result, PR #200 open (2026-10-01). The three worst rows are reworded as "before " in #200 (v14.6.1). Results:
results-2026-10-01.md, recordsmultiturn.jsonlon the branch.Frozen pass condition applied as frozen:
codex exec --jsondoes not emit the spawn (post hoc,orchestration.mdwas read before the firstwaitin 10/10 runs), and OpenCode.Not shown: the five-turn session does not reproduce the real Claude Code skips of version control and orchestration (main already 5/5). Those rows need the real-session re-measurement one week after release (acceptance criterion 4), with the same
measure.py --since <release date>.Open:
Rate limit exceeded. A retry loop launches the replacement runs (main 2, branch 3, control 3) when it clears; their result goes here.828e89ais running (review/independentpending).Handoff: branch
feat/164-routing-firing, worktreeagentsmd-worktrees/164-routing-firing, head828e89a, base8cad98f;python3 -m unittest discover -s tests396 OK;bin/versionctl release-checkOK for v14.6.1. Merge is the user's.wip: count only reads that returned the file, only completed turns; k…
lukemaj commentedon Oct 1, 2026
Re-measurement after independent review, frozen before running (2026-10-01). The review of #200 found three blocking defects, all confirmed. The first run's results are withdrawn as evidence; it stays in the history as run 1.
Fixes, at
c0e91ab(multiturn.pysha256793672072a9a2579fd561463ab84e0eeb4cbf335a98d76b89790075ea0c7e306):cats,echo cat <path>and refused reads. Checked on a direct read on Claude Code, Codex and Grok; OpenCode could not be checked (rate limit). Pinned intests/test_trigger_harness.py.versionctl doctorbefore edits.Arms:
main8cad98f,branchc0e91ab,none. Scenario, models, 5 valid runs per cell, at most 3 replacements, and pass conditions P1 to P4 are unchanged from the earlier freeze. Codex orchestration stays "not measurable" (no spawn event). OpenCode runs when its free route stops returningRate limit exceeded. Records:multiturn-r2.jsonl.wip: multiturn parser skips events whose message is a string (#164)
docs: re-measured before/after under the reviewed scorer (#164)
wip: a read counts only when its output arrived before the action; Gr…
lukemaj commentedon Oct 1, 2026
Round 3, frozen before running (2026-10-01). The re-review of #200 found two more scorer defects and one control limitation, all confirmed. Round 2 (
multiturn-r2.jsonl) is withdrawn as evidence: its records keep call order but not when outputs arrived, so they cannot be rescored.Fixes, at
afac243(multiturn.pysha2566baeef7c…46836683), pinned intests/test_trigger_harness.py:is_error: false, Codex needsturn.completed, OpenCode needsstep_finish. Whitespace no longer matters.Arms:
main8cad98f,branchc0e91ab(row text unchanged since round 2),none. Scenario, models, 5 valid runs per cell, at most 3 replacements, and P1 to P4 are unchanged from the first freeze. Records:multiturn-r3.jsonl. OpenCode runs when its free route answers.wip: a help lookup (gh pr create --help) is not the action (#164)
lukemaj commentedon Oct 1, 2026
Round 3 result under its frozen rule, and a Grok-only rerun frozen before running (2026-10-01).
Round 3 (
multiturn-r3.jsonl, instrumentafac243), main / branch, file read before the action:Cause of the Grok delivery result: in 12 of Grok's 15 runs (every arm), the first match for
gh pr createwasgh pr create --help, a help lookup issued alongside the read. The realgh pr createcame later. Both branch "misses" and main's one miss are such runs; in each,delivery.mdhad been read before the real call. No Claude Code or Codex record has a--helpmatch, so their cells cannot change.Fix at
e66bd84(multiturn.pysha2564fe0dd26…9aa7dfa7): a shell command performs the action only if the matched command segment does not ask for--help/-h(quoted words ignored). Pinned intests/test_trigger_harness.py. Rerun: Grok only, arms main8cad98f/ branchc0e91ab/ none, 5 valid runs each, same scenario, model and P1 to P4. Its result replaces Grok's round-3 cells; records go tomultiturn-r3-grok-rerun.jsonl. Round 3 stays the record for Claude Code and Codex.docs: round-3 before/after, Grok rerun blocked by its usage balance (#…
lukemaj commentedon Oct 1, 2026
State (2026-10-01 23:30 CEST). PR #200 head
39b71ec, v14.6.1. 403 tests OK, release-check OK. Third independent review running.Round 3 under its frozen rule:
gh pr create --helpas the PR action; fixed ine66bd84.Details: results.
Blockers (external):
402 Payment Required: Grok Build usage balance exhausted. The fixed Grok rerun stopped at 2 valid runs per arm, below the 3 P1 needs. Topping up the balance is the user's call; after that, rerun withgo.sh-equivalentpython3 multiturn.py run --host grok --model grok-4.7 --effort medium --arms main=8cad98f,branch=c0e91ab,none --runs 5.muse-spark-1.3-contributor-freehas returnedRate limit exceededsince about 19:00 UTC. Same command with--host opencode --model opencode/muse-spark-1.3-contributor-free --jobs 3.Still open in #164: re-measure on real sessions one week after release (
measure.py --since <release date>), especially version control, which the five-turn session cannot reproduce.