feat(autonomy): trigger-dispatch package - contract, setup slice, envelope check (WP4)#372
Conversation
… obligations, governed dispatch WP4 Phase 1 per its PLAN: the T1 contract as tool-agnostic normative text. Four contract-active signal-surface classes; provenance and transport attributes with normative push-lifecycle expiry backstop; six adapter obligations including atomic-or-reconciled dedup with a live-duplicate drain guard, admission enforcement (absent binding fail-closes human-gated), and closed-loop acknowledgment; work-class classification stamped from the security surface with lease-verified agent-internal parent provenance; the signal envelope (marker-record serialization, schema_version 1.0); kick + drain dispatch through the one work-item queue entrypoint with runtime-attested execution-surface verification; executor surface classes with the vendor-hosted human-gate cap on security-surface data. Research gaps carried in surface-class vocabulary; zero vendor names in normative text. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…es, ack template, signal-envelope check Phase 2 of the WP4 trigger-dispatch package: the setup skill gains the discovery-first trigger/dispatch slice (DIY-floor kick + hourly drain through the one queue entrypoint, plan-gated surfaces advisory with cost, execution- surface recording with the attestation caveat, fail-closed absent-admission wiring, additive `triggers` binding section with the shared `surfaces` map shape), per-surface-class adapter templates carrying the six obligations inline, the one-line acknowledgment template, and check-signal-envelope.mjs (exit contract 0/1/2) validating the marker record against the binding with deterministic raw-link origin branching. The contract doc pins the envelope marker token `<!-- autonomy:signal:v1 -->` (return-accounting precedent: serialization is contract-owned). Evals grow five trigger-slice cases; plugin 0.4.0. Vendor facts re-verified this session per the fresh-docs mandate: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows and https://code.claude.com/docs/en/headless Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h (envelope, ack, seam lease, fail-closed refusal) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 4m 8s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4)
Overall: The contract vocabulary, obligation set, dispatch invariants, and enforcement script are all internally consistent. The fail-closed semantics (absent admission binding → human-gated; unclassified → human-gated) are correctly threaded through the contract, skill, and templates. Three correctness issues in Findings1.
// current
if (statSync(target).isDirectory()) {Use function filesUnder(target) {
const stat = lstatSync(target);
if (stat.isSymbolicLink()) return []; // skip symlinks entirely
if (stat.isDirectory()) {
return readdirSync(target)
.map((entry) => join(target, entry))
.flatMap(filesUnder);
}
return [target];
}2. When // current
if (args[i] === "--binding") {
bindingPath = args[i + 1];
i += 1;
}Guard the value before assigning: if (args[i] === "--binding") {
if (i + 1 >= args.length) {
console.error("usage: check-signal-envelope.mjs <item-body-file-or-dir> [...more] [--binding <binding.json>]");
process.exit(2);
}
bindingPath = args[i + 1];
i += 1;
}3. Zero envelopes checked exits 0 — silently passes on empty or wrong-path targets · If every target resolves to an empty directory (wrong path, empty fixture dir), // after the loop, before the findings check
if (envelopesChecked === 0 && findings.length === 0) {
console.error("no envelope marker records found in the supplied targets");
process.exit(1);
}This matches how Observations (no action required)
Contract vocabulary is clean. Vendor and fleet names are absent from Fail-closed chain is coherent end-to-end. Absent admission binding → human-gated (never dropped) is stated in the contract (Adapter obligations §5), enforced in the skill (step 6), carried into the template (step 5 of Evals 14–18 cover the trigger slice well. Discovery-first, DIY-floor single-entrypoint, plan-gated advisory refusal, absent-admission fail-closed, and non-interactive apply are all covered. The 3 issues (1 medium, 2 low). All are in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eac5145813
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ow-list, traceparent v00 only, empty-input failure Local-scheduler raw links now conform only on file:, https:, or a scheme the binding surface entry declares via artifact_schemes (data:/javascript: and other ephemeral schemes never pass); traceparent requires the supported version 00 (ff is spec-forbidden); a run that finds zero envelopes fails instead of reporting vacuous success. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 13s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Follow-up
The hardening commit ( Resolved ✓Scheme allow-list for Traceparent Zero-envelopes exits 1 rather than 0 (Claude #3, Codex P2) Still open1. Unchanged from the original addition commit. import { lstatSync, readdirSync, readFileSync, statSync } from "node:fs";
// ...
function filesUnder(target) {
const st = lstatSync(target);
if (st.isSymbolicLink()) return []; // skip — can't be a conformant item body
if (st.isDirectory()) {
return readdirSync(target)
.map((entry) => join(target, entry))
.flatMap(filesUnder);
}
return [target];
}2. Unchanged. When if (args[i] === "--binding") {
if (i + 1 >= args.length) {
console.error("usage: check-signal-envelope.mjs <item-body-file-or-dir> [...more] [--binding <binding.json>]");
process.exit(2);
}
bindingPath = args[i + 1];
i += 1;
}SummaryThe security-relevant findings (scheme allow-list, traceparent version hardening, vacuous-success on empty input) are all correctly addressed. Two robustness items from the first-pass review remain: the symlink guard in |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9bdf3e7da6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…require temporal source surface, exec bit A binding-declared artifact scheme cannot launder data:/javascript: (deny set applies even when declared); a temporal signal's source_surface must resolve to a class temporal surface with a known scheduler_class before the local-URI branch is selectable; script gains the executable bit the hygiene exec-bit lane requires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 27s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Third Pass
The Resolved in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0624fe8713
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…t supported schema-version set Trace flags restricted to 00/01 (version-00 defines only the sampled bit); schema_version validated against the explicit supported set (1.0) so an unknown 1.x cannot certify semantics the checker cannot see. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 2m 1s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Fourth Pass
Resolved in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 982d9480af
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…l surfaces, exit 2 on unreadable targets Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 47s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Fifth Pass
Resolved in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7c23b63f78
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ce, ambiguous duplicate surface ids rejected Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 24s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Sixth Pass
Resolved in
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 84b5954ec7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…e (one-entrypoint event-fired dispatch) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Claude finished @kyle-sexton's task in 1m 25s —— View job PR Review — feat(autonomy): trigger-dispatch package (WP4) — Seventh Pass
Resolved in
|
…#377) WP5 guardrail-enforcement package (T2 + T3 + the security-review policy resolution), per the merged PLAN. Phases 1–3 complete; Phase 4's positive probe runs in this PR's CI (transcript below after the run). No linked issue ## Related - #359 (WP4–WP7 plans, incl. this package's PLAN as merged) - #372 (WP4 trigger-dispatch — the admission seam this package's policy content binds; envelope/classification cross-links land here) - #343 (WP2 telemetry — the promotion-evidence base and trace-tree payloads) ## What ships - **Guardrail hub** (`reference/guardrails.md`): five-class × five-column matrix (T3 verbatim), glance-layer routing to four leaves, D8 permission-posture note, escalation event classes + org-bound routing + work-item/trace payload over the governed queue (no second channel). - **Leaves** (`reference/guardrails/`): `isolation-ladder.md` (L0–L3, attendance × input-provenance axes, rejected trigger-source axis with falsification reason, L2 unattended floor, fail-closed no-silent-degrade), `work-classes.md` (risk bundles; C1 repo-read-only with queue writes permitted; C5→L3 floor; promotion/demotion discipline with shipped default predicates — C2 auto-merge ≥20 completions/≥14 days/100% gates/0 reverts, C3 ai-review ≥30 advisory/0 missed-blocking; demotion one-event-suffices), `security-review.md` (two layers, per-class blocking knobs as floors — no override escape on verification), `admission-policy.md` (3-axis decision table with `"*"` wildcard + most-specific-wins, equal-specificity tie = invalid binding, shipped defaults C1/C2 autonomous within caps regardless of provenance, C3 per-item human, C4/C5 human-gated; caps `autonomous_concurrency: 1`, `items_per_run: 3`). - **Security-binding schema** (`skills/setup/schemas/guardrails-security-binding.schema.json`): draft 2020-12, `additionalProperties: false`, fail-closed semantics in-schema; `executor_class`, `dispatch_posture`, per-surface `isolation_bindings` (level → substrate + `probe_evidence` + `runtime_markers`), merge/verification knobs, `promotion_state`, `escalation_routes`, `admission` (classification + rules + caps). - **Semantic validator** (`skills/setup/scripts/check-security-binding.mjs`, exit 0/1/2): matrix merge caps, vendor-hosted human-gate cap, marker non-emptiness + pairwise joint-satisfiability ambiguity rejection, class-aware per-surface isolation verdicts (C5 requires L3), missing-L2 error under `autonomous-enabled` vs declared verdict under `human-gated-only`, non-promotable promotion entries rejected, admission weakening requires `override_justification` (admission-only escape), promotion-gated knobs require their ratified `promotion_state` entry, equal-specificity ties invalid, evaluation mode resolving EFFECTIVE promotion state against contrary evidence without writing the binding. 91 binding fixtures plus 67 transcript and 2 evidence files, each negative producing exactly one finding. Note: egress-probe targets must be multi-label DNS names or public literal IPs — encoded loopback/private forms are normalized and rejected. - **Setup guardrail slice** (SKILL.md): two-surface resolution (security binding in settings-as-code home behind an agent-unwritable org-policy-home bootstrap; repo-local non-security remaps), substrate detection per surface, probe-before-bind live validation, detect-diff-reconcile against existing org guardrail surfaces, security review folded in (one slice, no near-duplicate capability), paid SKUs advisory + cost surfaced, fail-closed no-L2. Plus `templates/isolation-probe.md` and five new evals; plugin 0.5.0. ## Live-validation demonstration (Phase 4) **Positive probe** — this PR carried a one-off workflow (`wp5-isolation-probe-demo.yml`, since removed per the PLAN; evidence persists in the run log below) that PROVISIONED a genuine egress-denied boundary on the hosted runner (nested `docker run --network none` container) and executed the probe recipe INSIDE it, with a networked-outer-job control step. Transcript from the PR run: From [run 29675620833](https://github.com/melodic-software/claude-code-plugins/actions/runs/29675620833): ```text outer-egress: HTTP 200 (networked, as expected) egress-denial: PASS - external fetch failed inside the boundary (exit 4) credential-absence: PASS - every tested credential path absent inside the boundary (exit 3) captured live transcript: "schema_version": "1", "probed_at": "2026-07-19T05:56:39Z", "assertions": { "egress_denied": { "host": "example.com", "exit_code": "4", "outcome": "denied" }, "credentials_absent": { "path": "$HOME/.ssh,$HOME/.netrc,$HOME/.docker/config.json", "exit_code": "3", "outcome": "absent-or-denied" } }, "outer_context_networked": true Security binding OK: live-evidence/demo-binding.json - surface "ci-pool-a": C1 eligible, C2 eligible, C3 eligible, C4 eligible, C5 blocked (requires L3, proven L2) ``` The final line is `check-security-binding.mjs` exit 0 against a binding whose `probe_evidence` is the transcript captured seconds earlier in the same job — the checker passes only against live-captured, identity-matched evidence. (The recorded transcript predates the capture-contract fields that later review hardening added to the checker — per-target credential exit codes, host-side expansions, transport outcomes, and the outer-context reachability/existence codes on both assertions; it is preserved verbatim as the run's historical record.) **Negative path** — `check-security-binding.mjs` on the missing-L2 `autonomous-enabled` fixture (exit 1): ```text - isolation_bindings: no bound surface reaches L2 under dispatch_posture autonomous-enabled — autonomous dispatch is blocked fail-closed; compliant paths: bind an L2-capable substrate (whole-process OS-sandbox wrap, default-deny-egress container) on an execution surface, or declare dispatch_posture human-gated-only ``` **Declared posture** — the same missing-L2 shape under `human-gated-only` validates (exit 0) with the blocked-dispatch verdict reported as the DECLARED posture, not a defect. **Demotion evaluation** — promoted-C2 binding + contrary evidence (exit 0, binding file unchanged): ```text - C2-auto-merge: bound promoted -> effective unpromoted — ceiling lowered by contrary evidence (reverted-merge at 2026-07-10T12:00:00Z) WITHOUT modifying the binding; demotion files an escalation item on route "queue:escalations/demotion" requesting the human-ratified binding update ``` ## Near-duplicate audit Security review is folded into the ONE guardrail slice of guided-setup (no separate security-review capability); escalation composes the governed queue itself via the trigger contract's acknowledgment symmetry — no second claim, dispatch, or escalation channel was created anywhere in this package. ## Gates `validate-plugins.sh`, `validate-plugin-contracts.mjs` (vendor deny-list clean over `reference/`), catalog `--check` in sync, markdownlint 0 errors, typos clean, lychee offline 0 errors, `claude plugin validate --strict` pass, skill-quality `check-skill.sh setup` PASS (2 pre-existing WARNs), evals.json schema-valid (ajv draft2020), actionlint clean on the demo workflow. Full plugin test suite rides CI. Validator exercised over the full fixture set with expected exit codes and single-finding assertions. ## Boris check Step-3 guardrails cell covered (agent sandboxing = isolation ladder; automatic code + security review = verification layers; token/model management = cost tiers); the step-4 sentence ("enforcing the right guardrails for each type of work") is instantiated as the matrix; the step-3 trap honored (human-ratified promotion, automatic evaluation-time demotion); step-1 guardrail untouched. Auto-mode classifier tuning + allowlist distribution explicitly deferred with named triggers. <details> <summary>PLAN.md (as merged, tags advanced)</summary> # ai-ladder-wp5-guardrails ## Brief ### TLDR Guardrail-enforcement package (T2 + T3 + #241 instance): one guardrail contract shaped as a progressive-disclosure hub (matrix doc as index/spine) with on-demand leaf docs, an org-binding seam split by governance sensitivity, evidence-gated promotion with automatic demotion, and the guardrail slice of guided-setup (detect → bind → live-validate → fail-closed). ### Goal Any adopting org can bind the five-class guardrail matrix to its own instances and get per-work-class enforcement — isolation floor, verification layers, merge policy, cost tier, escalation — with the security-sensitive bindings outside agent blast radius, promotion earned on queryable evidence, and no silent degrade anywhere. ### Locked decisions | # | Decision | |---|---| | D1 | ONE guardrail contract, hub-and-leaves progressive disclosure: matrix doc = index/spine (table + one-line class/column definitions, glance layer resolves the common question); leaf docs load on demand — e.g. isolation-ladder levels, per-class detail + promotion triggers, security-review policy, admission policy (examples, not a fixed list; split at architect). Plus the guardrail slice of guided-setup. Both in the capability-distribution home. Fleet materializations = /work-items backlog. | | D2 | Org-binding seam: contract-owned schema, one logical binding, two governance surfaces split by sensitivity. Security-sensitive axes (isolation substrates per level, merge policy, per-layer blocking knobs, promotion-trigger state, escalation routes, admission policy) bind in the settings-as-code home — outside the blast radius of agents working in the consuming repo (agent-writable guardrail binding = bypass channel). Non-security mappings (class→label strings, cost-tier→model names) may bind repo-locally per the work-item-tracker binding pattern. Layered resolution: org-policy-home defaults → settings-as-code per-repo binding → repo-local non-security remaps; contract defines resolution order, materialization at architect. Fail-closed on absent/invalid security-sensitive binding; documented defaults only for non-security axes. | | D3 | Promotion/demotion discipline: contract defines the trigger SHAPE — an evidence predicate over queryable telemetry (verification outcomes per the telemetry contract are the evidence base). Promotion = human-ratified knob flip, recorded as a reviewable change on the governance surface — never automatic. Demotion = automatic fail-closed on contrary evidence (gate failure, human-reverted merge), re-earn from there. Org binds threshold values; suggested defaults at architect. Near-term promotable cells: C2 auto-merge, C3 AI-review advisory→blocking (#241); C4/C5 merge never promotes. | | D4 | Sandbox-ladder setup slice: detect available substrates per level per machine surface → bind level→substrate in the security binding → live-validate BEFORE recording (empirical probe, e.g. denied-egress smoke test inside the boundary; a binding lands only after the sandbox provably blocks) → fail-closed verify (no L2 substrate → autonomous dispatch blocked for that surface, compliant paths named — no silent degrade). | | D5 | #241 three-part resolution folds into the matrix structure, nothing ships separate: security-review policy contract (two layers, per-layer blocking knob) = verification-column leaf; security-review setup = part of the one guardrail slice of guided-setup (near-duplicate capability ban); the adjustment/audit layer generalizes matrix-wide — guided-setup always detect-diff-reconcile against existing org guardrail surfaces (sandbox configs, branch protections, review workflows), never greenfield-assume, never silently overwrite. | | D6 | Escalation column sharpened from DIRECTIONAL: contract defines escalation event classes (gate failure, verification divergence, admission rejection, demotion event, structural-class plan approval, untrusted-provenance always), routing obligation (org-bound routes in the security binding), and payload (work-item ref + trace link, one causal tree). Mechanism reuses the governed queue: escalation lands as a human-gated work item + optional channel notification via the trigger-contract acknowledgment symmetry — no second channel. Only interactive escalation UX stays deferred; trigger: WP7 runner design pack. | | D7 | Admission-policy content (the trigger contract's admission seam enforces it; this package owns it): decision-table shape — signal-surface class × initiator provenance × work class → disposition (autonomous-eligible / human-gated / audited rejection) — plus caps (autonomous concurrency, per-run items). Shipped defaults: C1/C2 autonomous-eligible within caps regardless of provenance (blocking agent provenance would sever the agent-kicks-off-agent loop), C3 per-item human admission, C4/C5 human-gated. Provenance is recorded input to gating, never trusted as isolation; caps bound total autonomous fan-out. | | D8 | Permission posture: attended ergonomics (auto-mode classifier tuning, safe-command allowlist distribution) is NOT a matrix column — unattended runs replace per-action prompts with the whole-process boundary; the ladder leaf carries a one-line permission-posture note (L1 = attended ergonomics tier, L2+ = the boundary is the control). Classifier tuning + allowlist distribution = backlog seeds with trigger, not silent drops. | ### Constraints - Any fleet repo or vendor name in normative contract text is a defect; vendor names appear only as marked examples and in binding docs. - Security-sensitive bindings never live where the agents they govern can edit them. - Fail-closed everywhere: unavailable substrate, absent/invalid security binding, contrary promotion evidence — block and name the compliant path, never degrade silently. - No new cost by default: free-path scanners and substrates default; entitlement-gated tools (e.g. paid code-scanning SKUs) are advisory + explicit opt-in with cost surfaced. - One queue, one escalation channel (the queue itself); no second claim, dispatch, or escalation mechanism. - Boris-alignment is the standing acceptance criterion (no step-skipping, trust before scale). ### Acceptance criteria - Matrix doc's glance layer alone answers "what governs class X" — leaf docs only for depth (progressive-disclosure convention conformance). - Binding schema names the two governance surfaces + layered resolution order; security axes fail closed when unbound. - Promotion is human-ratified + evidence-gated; demotion is automatic; both leave a reviewable audit trail on the governance surface. - Guided-setup slice detect-diff-reconciles existing guardrail surfaces, live-validates isolation before binding, and has zero paid dependencies on its default path. - Admission decision-table defaults match the matrix class defaults verbatim; agent provenance is not blocked by default; caps are org-bindable. - Boris check: step-3 guardrails cell covered (agent sandboxing = ladder; automatic code + security review = verification layers; token/model management = cost tiers); step-4 sentence ("enforcing the right guardrails for each type of work") instantiated as the matrix; step-3 trap honored (human-ratified promotion, automatic demotion); step-1 guardrail untouched. - Auto-mode classifier tuning + allowlist distribution explicitly out-of-scope with named triggers, not dropped. ### Captured assumptions - Verification-outcome telemetry (WP2 contract) is queryable at promotion-evaluation time; until wired, promotion evaluation is manual over the same evidence definition. - Free-path deterministic scanners remain available on the default path; paid code-scanning SKUs stay entitlement-gated (re-verified: private-repo CodeQL requires a paid license). - The work-item-tracker binding precedent (contract-owned schema, org-supplied values, stop-and-report on invalid binding) remains the fleet's binding idiom. ### Out-of-scope (deferred with triggers) - Interactive escalation UX — trigger: WP7 runner design pack. - Auto-mode classifier tuning + safe-command allowlist distribution — trigger: 1→2 residue sweep / standards allowlist-distribution work (backlog seeds). - Cost ENFORCEMENT (hard spend caps) — trigger: 3→4 transition work; cost tiers here are policy vocabulary only. - Fleet materializations (binding instances, workflow gates, scanner wiring) — /work-items backlog post-graduation. ### Deferred questions - Exact class/level/column/attribute tokens + binding schema fields — `/architect` (with the plugin naming pass). - Leaf-doc file split + glance-layer wording — `/architect`. - Promotion-trigger default values (run counts, windows) + demotion evidence set — `/architect`. - Setup-slice probe mechanics per substrate class — `/architect`. - Admission decision-table serialization format — `/architect`. ## Plan Recommendation-locked this round under the user's standing pre-authorization (same basis as the WP2/WP3 round): hub doc `reference/guardrails.md` with leaf docs under `reference/guardrails/` (`isolation-ladder.md`, `work-classes.md`, `security-review.md`, `admission-policy.md` — the D1 example split, adopted); class tokens `C1`–`C5` and level tokens `L0`–`L3` carried verbatim from T3/T2; security binding = a contract-owned JSON document in the settings-as-code home (`schema_version` from `"1.0"`); non-security remaps = an ADDITIVE `guardrails` section of the WP1 repo-local binding; admission decision table serialized as JSON inside the security binding. Prerequisite: the WP4 implementation PR merged (`reference/trigger-dispatch.md` exists — the admission leaf cites its seam; the interim is safe via WP4's fail-closed absent-binding clause). ### Phase 1: Guardrail hub + leaf docs [DONE] | File | Action | What changes | |---|---|---| | `plugins/autonomy/reference/guardrails.md` | Create | The hub/spine (D1): the five-class × five-column matrix table imported from T3 unchanged (C1 read-only / C2 mechanical / C3 scoped / C4 structural / C5 untrusted-provenance × min-isolation / verification / merge policy / cost tier / escalation), one-line class and column definitions, and the glance-layer rule — this table alone answers "what governs class X"; every deeper question routes to a named leaf. Permission-posture note per D8 (one line: L1 = attended ergonomics tier; L2+ = the boundary is the control). Escalation event classes per D6 (gate failure, verification divergence, admission rejection, demotion, structural-class plan approval, untrusted-provenance always) with the routing obligation (org-bound routes in the security binding) and payload (work-item ref + trace link); mechanism = the governed queue itself (human-gated item + optional channel notification via the trigger contract's acknowledgment symmetry — no second channel). Boris step-4 sentence cited as the matrix's source framing. Zero vendor/fleet names. | | `plugins/autonomy/reference/guardrails/isolation-ladder.md` | Create | T2 ladder normative text: L0–L3 level definitions, attendance × input-provenance axes, the falsified trigger-source axis recorded as rejected (with reason), L2 unattended floor, fail-closed rule where L2 unavailable (block + name compliant paths, no silent degrade), permission-posture line per D8. Instances org-supplied; free-path substrate CLASSES named as marked examples only. | | `plugins/autonomy/reference/guardrails/work-classes.md` | Create | Per-class detail: risk-property bundle per class (blast radius, reversibility, provenance, verifiability) — stating explicitly that C1 "read-only" scopes REPO surfaces and permits governed-queue/tracker writes (work-item filing, no repo mutation; the WP6 stress-test's F8 disambiguation) and that C5's min-isolation cell is the L3 floor WP7's C5-dispatch gate cites, per-cell promotion triggers (D3 shape: evidence predicate over queryable telemetry per the telemetry contract; promotion = human-ratified knob flip recorded as a reviewable change on the governance surface; demotion = automatic fail-closed on contrary evidence, re-earn from there). Suggested default predicates (org-bindable values): C2 auto-merge — ≥ 20 autonomous C2 completions over ≥ 14 days with 100% deterministic-gate pass and 0 human-reverted merges; C3 AI-review advisory→blocking — ≥ 30 advisory reviews with 0 human-confirmed missed-blocking findings. C4/C5 merge never promotes. Demotion evidence set: any post-merge gate failure, any human-reverted merge, any verification divergence — one event suffices. | | `plugins/autonomy/reference/guardrails/security-review.md` | Create | The #241 security-review policy contract as the verification-column leaf: two layers (deterministic scanners + AI security review), per-layer blocking knob (advisory/blocking per class, bound on the governance surface), free-path scanners default, paid SKUs advisory + explicit opt-in with cost surfaced. | | `plugins/autonomy/reference/guardrails/admission-policy.md` | Create | The D7 admission-policy content the WP4 seam enforces: decision-table shape (signal-surface class × initiator provenance × work class → disposition `autonomous-eligible` / `human-gated` / `audited-rejection`) + caps (`autonomous_concurrency`, `items_per_run`). Shipped defaults verbatim from D7: C1/C2 autonomous-eligible within caps regardless of provenance; C3 per-item human admission; C4/C5 human-gated; provenance recorded, never trusted as isolation. Default cap values (org-bindable): `autonomous_concurrency: 1`, `items_per_run: 3` — conservative Boris trust-before-scale floor. Serialization: the `admission` object of the security binding (Phase 2 schema). | **Sanity Check:** - Hub glance test: `grep -c '| C[1-5] |' plugins/autonomy/reference/guardrails.md` = 5 (matrix rows present) and hub contains zero level-definition prose (`grep -c 'microVM' plugins/autonomy/reference/guardrails.md` = 0 — depth lives in leaves) - `grep -c 'audited-rejection' plugins/autonomy/reference/guardrails/admission-policy.md` ≥ 1 - `grep -ci 'fail-closed' plugins/autonomy/reference/guardrails/isolation-ladder.md` ≥ 1 - Promotion defaults present: `grep -c '20 autonomous C2 completions' plugins/autonomy/reference/guardrails/work-classes.md` ≥ 1 and `grep -c '14 days' plugins/autonomy/reference/guardrails/work-classes.md` ≥ 1 - Vendor+fleet deny-list sweep exit 0; lychee lane passes ### Phase 2: Binding schema (two governance surfaces) [DONE] | File | Action | What changes | |---|---|---| | `plugins/autonomy/skills/setup/schemas/guardrails-security-binding.schema.json` | Create | Contract-owned JSON Schema for the security binding document that lives in the settings-as-code home (outside agent blast radius per D2): `schema_version` (const "1.0"), `executor_class` (`self-operated` \| `vendor-hosted` — the merge-policy gating input, security-surface data per the trigger contract), `dispatch_posture` (`autonomous-enabled` \| `human-gated-only`, default `autonomous-enabled` — an org may intentionally record its security settings while keeping every dispatch human-gated; the PR-review finding that a missing L2 entry must not conflate that deliberate posture with an invalid binding), `isolation_bindings` keyed by EXECUTION SURFACE (org-named surface id — e.g. the CI pool, a self-run host class; the PR-review finding that a flat level→substrate map lets one surface's L2 binding satisfy the check while a different dispatch surface has no boundary at all) → level token → substrate instance id + probe evidence ref + `runtime_markers` (the per-surface TRUSTED identifying markers the trigger contract's dispatch seam matches platform-attested runner context against — a key/value set drawn from metadata the executing workload cannot forge, e.g. attested runner-pool/label identity; serialized HERE, on the agent-unwritable security surface, so surface attestation never falls back to the repo-local recorded id), `merge_policy` (per class, capped per matrix), `verification_blocking` (per layer per class), `promotion_state` (per PROMOTABLE cell only: state + ratifying change ref + evidence window), `escalation_routes` (event class → org route), `admission` (classification rules — the signal→work-class mappings the trigger contract's adapters stamp from, e.g. label→class for tracker-vcs-event — plus decision-table rules with a `"*"` wildcard per axis and most-specific-wins precedence (full triple > two axes > one axis > default), optional per-rule `override_justification`, and caps `autonomous_concurrency` / `items_per_run`). `additionalProperties: false`; fail-closed semantics documented in-schema (absent/invalid security binding blocks autonomous dispatch). | | `plugins/autonomy/skills/setup/scripts/check-security-binding.mjs` | Create | Validates a security-binding document against the schema + semantic rules the schema cannot express: merge-policy caps never exceed the matrix (C4/C5 human always; `executor_class: vendor-hosted` caps every class at human-gated); every surface bound under `dispatch_posture: autonomous-enabled` must declare non-empty `runtime_markers` (else the dispatch seam could never attest the actual surface and every dispatch would fail closed as unattestable), and marker sets must be PAIRWISE UNAMBIGUOUS under joint satisfiability — two conjunctive predicates are compatible (and the binding REJECTED) unless they require CONFLICTING values for at least one shared key, since a runtime context carrying the union of two non-conflicting predicates matches both (subset/identity checks alone miss e.g. `{pool: blue, region: us}` vs `{pool: blue, os: linux}`); the dispatch seam still requires EXACTLY ONE matching surface at runtime, failing closed on zero or multiple matches — the validator-level joint-satisfiability rejection is what keeps that runtime rule from turning validly bound fleets into ambiguity outages; isolation verdicts are PER SURFACE and CLASS-AWARE: each bound surface resolves, per work class, eligible (its bound level meets that class's min-isolation matrix cell — L2 is the floor for any autonomous dispatch, C5 requires L3) or blocked — admission/executor resolution must name the surface a dispatch would run on AND the item's work class, and verifies the surface meets the class's minimum at dispatch time, so an L2-only surface is never selected for a C5 item (even after human admission or a later policy change) and an unbound surface is blocked even when a sibling surface is bound; under `dispatch_posture: autonomous-enabled` it is an ERROR when NO bound surface reaches L2 (fail-closed, compliant paths named) — under `human-gated-only` the binding validates and the verdict reports blocked autonomous dispatch as the declared posture, not a defect; a `promotion_state` entry for a non-promotable cell (C4/C5 merge) is rejected; admission defaults not weaker than shipped defaults without the rule's `override_justification` (declared in the schema, so a justified override validates); every bound isolation level carries probe evidence. Plus an EVALUATION mode: given a binding + an evidence source, resolves the EFFECTIVE promotion state — the bound knob is a ceiling; live contrary evidence (gate failure, reverted merge, verification divergence) lowers it at evaluation time without writing the binding (automatic demotion has no write-back actor by design: the binding is agent-unwritable; the demotion event additionally files an escalation item requesting the human-ratified binding update). This evaluation is a LIVE-PATH OBLIGATION, not a demo artifact: the admission seam and the merge disposition MUST resolve the effective state (querying the promotion-evidence telemetry) before every autonomous dispatch/merge decision — reading the raw `promotion_state` alone is non-conforming, and unavailable evidence telemetry fail-closes to the unpromoted default — the checker's evaluation mode exists to exercise the same resolution mechanically. Exit contract 0/1/2. | | `plugins/autonomy/skills/setup/SKILL.md` | Modify | Document the two-surface split + layered resolution order (org-policy-home defaults → settings-as-code per-repo security binding → repo-local non-security remaps in the WP1 binding's additive `guardrails` section: class→label strings, cost-tier→model names) — INCLUDING the security binding's resolvable locator: the org-policy home carries the repo→security-binding-document registry the dispatch seam resolves the binding through when settings-as-code is a separate repository — and for SECURITY resolution the org-policy-home identity itself must come from an agent-unwritable bootstrap, never the repo-local binding (the binding seam's known limitation that `org_policy_home` may persist repo-locally is tolerable for non-security defaults only; a repo-writable pointer would let an agent redirect the whole chain to a forged policy repository with a forged registry, binding, and matching runtime markers): the seam pins the org-policy-home identity from org-level platform configuration outside repo blast radius (an org-level setting/variable repo agents cannot write) or the executor's trusted deployment config, and any security resolution that would depend on a repo-writable pointer — or an unresolvable locator — fail-closes autonomous dispatch with the compliant path named. Fail-closed on absent/invalid security binding; documented defaults only for non-security axes. | **Sanity Check:** - `check-security-binding.mjs` exit 0 on a fixture-valid document, exit 1 on each negative fixture: C4 merge_policy `auto`, `executor_class: vendor-hosted` with any non-human-gated merge row, `promotion_state` entry for a C4 cell, missing probe evidence, missing L2 under `dispatch_posture: autonomous-enabled`, admission weakened without `override_justification`; exit 0 with a blocked-dispatch verdict on the missing-L2 + `human-gated-only` fixture (deliberate posture, not a defect) (fixtures under `evals/fixtures/`) - Evaluation mode: a promoted-C2 binding + contrary-evidence fixture resolves the EFFECTIVE state demoted (exit output asserts the ceiling was lowered without modifying the binding file) - Schema declares `executor_class` and `override_justification` (`grep -c 'executor_class' …schema.json` ≥ 1; `grep -c 'override_justification' …schema.json` ≥ 1) - Schema `additionalProperties: false` present (`grep -c '"additionalProperties": false' …schema.json` ≥ 1) - `claude plugin validate --strict` exit 0 ### Phase 3: Guided-setup guardrail slice [DONE] Extends the `setup` skill: detect → bind → live-validate → fail-closed (D4), always detect-diff-reconcile against existing org guardrail surfaces (D5 — sandbox configs, branch protections, review workflows), never greenfield-assume, never silently overwrite. | File | Action | What changes | |---|---|---| | `plugins/autonomy/skills/setup/SKILL.md` | Modify | Guardrail slice: detect available substrates per level per machine surface; bind level→substrate into the security binding; LIVE-VALIDATE before recording — the empirical probe per substrate class: denied-egress smoke test inside the boundary (a network fetch to a well-known external host MUST fail) + host-credential-path read attempt (MUST be absent/denied); a binding lands only after the probe transcript proves the boundary; probe evidence ref recorded in the binding. Fail-closed verify: no L2 substrate on a surface → autonomous dispatch blocked for that surface, compliant paths named. Security-review slice folded here per D5 (one guardrail slice, no separate near-duplicate capability). Paid scanner SKUs advisory + opt-in with cost surfaced. | | `plugins/autonomy/skills/setup/templates/isolation-probe.md` | Create | Probe recipe per substrate class (container / OS-sandbox wrap / VM-microVM), parameterized, vendor names as marked examples only: egress-denial probe command shape, credential-absence probe shape, expected-failure assertions, transcript capture shape. | | `plugins/autonomy/skills/setup/evals/evals.json` | Modify | Add guardrail-slice cases: substrate detection interview, probe-before-bind ordering, fail-closed no-L2 path, detect-diff-reconcile against a pre-existing branch-protection surface, paid-SKU advisory refusal-to-default. | | `plugins/autonomy/README.md` + `plugins/autonomy/.claude-plugin/plugin.json` | Modify | Capability list + description + minor version bump. | **Sanity Check:** - `/skill-quality:check` + `validate-evals` pass - `grep -c 'probe' plugins/autonomy/skills/setup/templates/isolation-probe.md` ≥ 3 - `grep -ci 'detect-diff-reconcile' plugins/autonomy/skills/setup/SKILL.md` ≥ 1 - Fleet-name sweep exit 0 ### Phase 4: Live-validation demonstration + gates [DONE] Acceptance probe, BOTH paths mandatory: (positive) run the Phase 3 probe recipe against a REAL, PROVISIONED egress-denied boundary — the CI job on this public repo (free minutes) launches a nested no-network container via the container runtime present on the hosted Linux runner image (network mode none / internal-only, no host env or secrets passed in) and executes the probe INSIDE it; the outer job keeps normal networking, so the probe's egress-denial assertion is satisfied by a genuine boundary the job itself created — never by the bare runner (which has outbound access and would fail the assertion) and never by a hardcoded "denied" — transcript shows denied egress + absent credentials inside the boundary, proving the probe DETECTS a genuine boundary; (negative) the fail-closed path is demonstrated by validating an `autonomous-enabled` binding with a MISSING L2 entry and observing the blocked-dispatch error; the deliberate `human-gated-only` posture validates separately with its blocked-dispatch verdict reported as declared, not as a defect. A fixture security binding referencing the positive probe's evidence passes `check-security-binding.mjs`; the demotion evaluation fixture (Phase 2) runs here as part of the demo record. Then the full gate roster (WP2 Phase 4): `validate-plugins.sh`, `run-plugin-tests.sh`, `validate-plugin-contracts.mjs`, markdown/typos/lychee, `claude plugin validate --strict`, catalog regen. Near-duplicate audit statement: security review folded into the one guardrail slice (D5); escalation composes the governed queue — no second channel created. **Sanity Check:** - Positive probe transcript (provisioned no-network container on the CI runner: egress denied + credentials absent inside; outer job networked) attached to the PR body - Negative path: `check-security-binding.mjs` exit 0 on the demo binding, exit 1 on the missing-L2 `autonomous-enabled` fixture, exit 0 + declared blocked-dispatch verdict on the `human-gated-only` fixture - Demotion evaluation output (effective state lowered, binding file unchanged) in the demo record - All gate scripts exit 0; catalog in-sync - Near-duplicate audit statement present in the PR body ## Blast radius MEDIUM-HIGH — one plugin's files, but this contract governs every autonomous dispatch decision (admission), every isolation binding, and the promotion path to the closed loop; security-posture content raises the review bar. Fully git-revertible; the security binding itself ships as schema + fixtures only (no live org binding lands in this package). ## Stress-test summary Fresh-context plan review (WP4+WP5 batch): 14 findings, verdict FIX-THEN-SHIP, all folded. WP5's share — F1b (HIGH, cross-package): the hosted human-merge-gate cap keyed on an executor class the schema never carried → `executor_class` added to the security schema and enforced by the check; F9 (MED): `override_justification` was required by the check but undeclared under `additionalProperties: false` → declared in the admission-rule sub-schema; F10 (MED): automatic demotion had no write-back actor against an agent-unwritable binding → eval-time evidence-gating (bound state is a ceiling; live contrary evidence lowers the effective state; the demotion event files an escalation item requesting the human-ratified update), with an evaluation-mode fixture in the check and the demo; F11 (MED): the fail-closed pivot alone never proved the probe detects a real boundary → positive probe mandatory against a free-tier CI container, negative path demonstrated separately; F12 (LOW): fragile `grep -c '20'` → full-phrase grep; F13 (LOW): 3-D admission table gains a `"*"` wildcard + most-specific-wins precedence, and promotion entries for non-promotable cells are rejected; F14 (LOW, symmetry): the D8 backlog seeds are echoed in Open questions. The classification-rules half of F1a lands here as the `admission.classification` schema content (the WP4 contract's adapters stamp from it). ## Execution shape Fully sequential 1 → 2 → 3 → 4 — Phase 2's schema serializes Phase 1's admission leaf; Phase 3 wires Phase 2's binding; Phase 4 probes Phase 3's recipe. Cross-package: after the WP4 implementation PR (admission seam exists to cite). | Phase | Surface | Basis | |---|---|---| | 1 | main-session | normative security-policy authoring, tightly coupled T2/T3 imports | | 2 | main-session | schema + semantic-validator judgment | | 3 | main-session | setup-skill judgment, probe design | | 4 | main-session | live probe + gate runs | ## Open questions - Fleet materializations (live org security binding, workflow gates, scanner wiring) — /work-items backlog post-merge (Brief out-of-scope, triggers recorded). - Interactive escalation UX — WP7 runner design pack (Brief trigger). - D8 backlog seeds (auto-mode classifier tuning + safe-command allowlist distribution) — carried in the Brief with named triggers; filed via the work-items flow. ## Decisions made (gate-passed) | Decision | What it changes in the plan | Basis (evidence) | |---|---|---| | Leaf split: 4 leaves under `reference/guardrails/` | Phase 1 file set | Brief D1 names exactly these four as the example split; each maps to one matrix column/axis | | Hub-subdirectory layout (first `reference/` subdir) | Phase 1 paths | Progressive-disclosure hub needs its leaves grouped; flat names would couple hub prose to 4 sibling filenames | | Promotion defaults: C2 ≥20 runs/≥14 days/100% gates/0 reverts; C3 ≥30 advisory/0 missed-blocking | work-classes leaf | D3 requires suggested defaults at architect; values conservative per Boris trust-before-scale (org-bindable) | | Cap defaults `autonomous_concurrency: 1`, `items_per_run: 3` | admission leaf + schema | D7 requires caps; floor values bound total fan-out at the trust-earning stage | | Security binding as JSON + JSON Schema + semantic validator | Phase 2 deliverables | WP1 binding precedent (schema-versioned JSON, stop-and-report on invalid); schema alone cannot express matrix caps | | `executor_class` + classification rules live on the security surface; promotion is eval-time evidence-gated (ceiling semantics) | Phase 2 schema + check | Stress-test F1b/F10: gating inputs must sit outside agent blast radius; an agent-unwritable binding cannot receive an automated demotion write | | Demo pivots to fail-closed path when no container substrate exists | Phase 4 | D4 makes fail-closed the required behavior — the pivot demonstrates the contract, not a skipped demo | [FALLBACK — confirm or override] `check-security-binding.mjs` as a NEW deliverable (Phase 2) — invented beyond the Brief on the WP2/WP4 enforcement-surface precedent. Flag if unwanted. ## Handoff to implementation ### User-approval gates - The binding-check script above is [FALLBACK] — surface before authoring if contested. - Any change to the shipped admission defaults or matrix cells during implementation → STOP (these are T3/D7 user-locked content, not implementation discretion). - Any scope expansion beyond the four phases re-enters `/architect review`. ### Execution shape ([EXEC-SHAPE] tagged) Sequential 1→4, all main-session (table above). PLAN.md phase tags advance in the same commit as each phase; live probe per Phase 4 with the documented fail-closed pivot. ### Mechanical work Commit per phase on the implementation branch (suggest `feat/autonomy-guardrails`); gates re-run in full at Phase 4; PR body carries the probe transcript + near-duplicate audit statement + this PLAN in a `<details>` block at close-out. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ice (WP6) (#600) WP6 standing-routines package (T7), per the merged PLAN. All four phases complete; the novel-class derivation probe and near-duplicate audit statement are recorded below. No linked issue ## Related - #359 (WP4–WP7 plans, incl. this package's PLAN as merged) - #242 (standing scheduled routines — the decision node this package graduates: which maintenance classes, hosted where) - #239 (WP map: AI adoption ladder, step-3 unlock) - #372 (WP4 trigger-dispatch — routines are scheduled `temporal` adapters behind its queue contract) - PR #377 (WP5 guardrail matrix + security binding — the matrix the mapping rules derive into; the `admission.classification.temporal` home the routine slice binds) ## What ships - **Routine catalog hub** (`reference/routines.md`): routine = scheduled trigger adapter behind the governed queue, never a private execution/merge path; two-family repetition language (session-scoped: loop, goal, batch, dynamic workflow; standing: schedule, routine — with the corrected batch definition); schedule/event/continuous trigger taxonomy; output contract (report OR governed-queue work item; direct change only through the matrix merge policy); the contract-owned catalog-to-matrix mapping rules including the refined axes (DET → plain cron, `not-a-routine`; hybrid DET-detect + AGT-judgment split; AGT rules to C1–C3; structural/config-surface direct change → C4; attacker-writable external input → C5; compose-to-highest; access axis → prerequisites); 40-row glance catalog — ten `v1` rows, seven `not-a-routine` rows, named join triggers on every deferred row. - **Ten v1 definition leaves** (`reference/routines/`): uniform shape (purpose, trigger and cadence, access scope, output contract, derived guardrail row, admission and escalation, precedent). Every guardrail row is derived step-by-step through the mapping rules, none hand-assigned: six pure-advisory classes at C1/L2 floor; `doc-freshness-sweep`'s optional gated docs-change portion C3; `dependency-update-wave` hybrid with two explicit postures (mechanical-only C2; changelog-reading posture provenance-composed to C5); `tech-debt-sweep` hybrid with human-gated prioritization; `ci-health-review`'s optional CI-config change portion C4 (structural surface). Cadence defaults org-bindable; governance imported by citation only. - **Guided-setup routine slice** (`skills/setup/SKILL.md`): discovery-first surface interview + budget posture; free defaults wired as reviewable changes across role homes; paid/preview surfaces advisory with cost surfaced; detect-diff-reconcile of existing schedulers and bots (a live judgment-bearing bot IS a catalog-class instance — recorded under its class token, never duplicated; DET stays cron through reconciliation). Binding-home split by governance sensitivity: the routine→work-class mapping is admission data and lands only in the security binding's `admission.classification.temporal` home (settings-as-code, agent-unwritable, reconciled bots included); the repo-local `routines` section carries cadence/enablement/surface only, reusing the envelope checker's `surfaces` map shape. - **`templates/routine-definitions.md`**: per-surface-class wiring shapes (CI-cron / local scheduler / self-run / vendor-preview as marked examples), every shape enqueuing through the trigger contract's `temporal` adapter — no direct execution path. - **Protected routine identity (review wave 1)**: routines bind under a routine identity (`<class-token>` or `<class-token>/<posture-token>`; multi-posture classes bind per posture, never the bare token); `admission.classification.temporal` entries are object form `{class, source_surface}` — the identity↔surface association on the security binding, one identity per emitting surface; the temporal envelope carries a required `signal.routine` claim admission validates before stamping (`check-security-binding.mjs` + `check-signal-envelope.mjs` enforce; five new fixtures; sweep 9 pass / 89 reject). - **Evals**: five routine-slice cases (surface interview first, free-default wiring home split, existing-bot reconciliation record-not-duplicate, paid-surface advisory refusal-to-default, selector-is-a-claim protected association). Plugin 0.5.0 → 0.6.0. ## Novel-class derivation probe (Phase 4 acceptance) Classifying a class the catalog does not contain, using only the shipped mapping rules — an untrusted-provenance case, exercising the C5 reach: **Candidate class: community-forum feedback triage** — a standing sweep that reads public community-forum threads about the product, clusters recurring complaints, and files candidate work items. 1. **Judgment axis**: semantic clustering, salience ranking, and routing are agent judgment → `AGT` (not `DET`; no deterministic detection half worth a hybrid split — a keyword pre-filter could route to plain cron, leaving the judgment portion as the routine, per the hybrid branch). 2. **Output axis**: report + work items into the governed queue, no repository mutation → `C1` base (`AGT` + report → `C1`; `AGT` + work item → `C1`). 3. **Direct-change axes**: no direct change → the `C2`/`C3` mechanical-checkability rule and the `C4` structural-surface rule do not lift the class. 4. **Input-provenance axis**: public forum posts are attacker-writable external content → derives `C5` (untrusted provenance). 5. **Composition**: multiple matches compose to the highest-risk class — `C5` > `C1` → the class is **C5**. 6. **Access axis → prerequisites**: `ext` access → connector prerequisite + entitlement resolved in the org binding, join trigger "named intel need"; the `C5` matrix row then fixes the floor — L3 isolation, full gates + zero secret exposure, human merge always, escalation always. The read-only class's exfiltration-surface caveat arrives as the class outcome, not a footnote. End-to-end: axes → guardrail row → prerequisites, with zero contract changes. The acceptance criterion holds, including the C5 reach the plan's stress-test added the provenance axis for. ## Near-duplicate audit Routines compose the WP4 `temporal` adapter and the WP5 guardrail matrix; deterministic checks stay plain cron filing work items through the same trigger adapters; no second scheduling, execution, or merge path is created by this package. ## Plan deviation (recorded) The PLAN's Phase-1 row-count gate said 39 (one row per research-taxonomy class). The research table contains only 9 of the 10 locked v1 classes — `pr-queue-tending` has no research row (the nearest research row, stale grooming, is pure-DET and locked `not-a-routine`). The catalog therefore ships 40 rows = 39 research rows + the fleet-proven `pr-queue-tending`. The D2 mapping rules, the seven `not-a-routine` rows, and the ten-class v1 roster are untouched. ## Gates validate-plugins.sh, validate-plugin-contracts.mjs (33 setup skills / 1777 plugin files), markdownlint-cli2, typos, `claude plugin validate --strict`, catalog regen in-sync — all green at head. Reference-doc vendor-name sweep: zero hits (hub + all ten leaves). run-plugin-tests.sh and lychee validate in CI. <details> <summary>WP6 PLAN (full)</summary> # ai-ladder-wp6-routines ## Brief ### TLDR Standing-routines package (T7): catalog doc as a progressive-disclosure hub (class table = glance layer), contract-tier definition docs for the v1 subset only, contract-owned catalog-to-matrix mapping rules, and the routine slice of guided-setup. A routine stays a scheduled trigger adapter behind the governed queue — never a private execution or merge path. ### Goal Any adopting org can stand up governed background maintenance — the step-3 unlock: "maintenance and cleanup that used to wait for someone to find the time now runs continuously in the background" — with every routine's guardrail row derived from catalog axes, free hosting defaults, and existing org schedulers reconciled rather than duplicated. ### Locked decisions | # | Decision | |---|---| | D1 | Scope: catalog doc (hub-and-leaves — class table with judgment/output/access scores = glance layer) + per-class definition leaf docs for the v1 subset ONLY (non-v1 classes stay table rows with precedent pointers until their join trigger fires) + catalog-to-matrix mapping rules + routine slice of guided-setup. All capability-distribution home. v1 definitions are contract-tier tool-agnostic docs; vendor-binding capability templates are build-stage artifacts. Fleet routine stand-up = /work-items backlog. | | D2 | Catalog-to-matrix mapping rules are contract-owned (orgs classify new routine classes without republication): judgment DET → not a routine — plain cron, zero agent tokens, failures file work items through trigger adapters; AGT + report → C1; AGT + work-item output → C1 (governed-queue write, no repo mutation); AGT + direct change → C2 where truth is mechanically checkable, C3 otherwise; AGT/HUM → agent prepares, disposition human-gated always. Access axis → prerequisites: repo-scoped = L2 unattended floor; prod/product/org/ext = connector prerequisite + entitlement resolved in the org binding; external-watch classes read attacker-writable content — the read-only class's exfiltration-surface caveat applies; unattended GUI actuation = highest isolation tier. *REFINED 2026-07-18 (WP6/WP7 stress-test F3+F4): (a) hybrid branch — a DET-detect + AGT-judgment class (catalog rows 7/14/16/28, incl. v1 dependency-update-wave) splits: the detection portion routes to plain cron, the judgment portion IS the routine and derives through the AGT rules; the binary DET/AGT reading could not classify hybrids. (b) Pure-DET classes carry an explicit `not-a-routine` catalog flag (rows 2/12/20/22/25/27/34) instead of silent exclusion. (c) Two axes added so derivation reaches the full matrix: input provenance — a routine consuming attacker-writable external content derives C5 (untrusted provenance), upgrading the external-watch caveat to a class outcome; structural blast radius — direct change to structural/config surfaces derives C4. Overlapping matches compose to the highest-risk class (C5 > C4 > C3 > C2 > C1). Without them derivation topped out at C3 and the novel-class acceptance criterion was unmeetable for C4/C5 cases.* | | D3 | v1 definition doc shape (each leaf): purpose (toil addressed), trigger-taxonomy slot with suggested cadence (org binds values), access scope, output contract (report shape / work-item filing / gated change), guardrail row derived via D2, admission + escalation notes imported from the guardrail contract, precedent pointer. No vendor scheduling detail baked in — surfaces are preview-stage moving targets; setup researches them live. | | D4 | v1 subset (repo-scoped, agent-judgment, proven manual patterns): issue triage sweep, duplicate-detection sweep, backlog readiness check, PR-queue tending, doc-freshness sweep, dependency update wave, advisory/CVE triage, tech-debt sweep, ~~dead-code sweep~~ eng-metrics digest, CI health review. *CORRECTED 2026-07-18 (review evidence, RESEARCH-routine-catalog row 25): dead-code sweep is classified DET detect — per D2's rule it is not a routine; detection routes to plain cron filing work items through trigger adapters, and any judgment-bearing triage of its findings belongs to the tech-debt sweep. The WP6 architect round confirms the final v1 roster.* Deferred classes keep T7's named join triggers (prod/product/org/ext access, GUI actuation). | | D5 | Routine slice of guided-setup is discovery-first: interview the org for scheduling surfaces (CI cron, dev machine, self-run infra, vendor-hosted preview) and budget posture; wire free defaults as reviewable changes (CI-cron handler = CI-orchestration home, enabling settings = settings-as-code home); advise paid/preview surfaces with cost surfaced; detect-diff-reconcile EXISTING org schedulers and bots — a live dependency bot or stale bot IS a catalog-class instance: record it in the binding, never stand up a second mechanism for the same concern. | | D6 | Ubiquitous language imported from the routine contract thread with one correction: batch = session-scoped parallel fan-out over decomposed units (vendor command verified against the official commands doc; research had conflated it with bulk-inference Batch APIs). Contract carries the two-family distinction: session-scoped (loop, goal, batch, dynamic workflow) vs standing (schedule, routine). Routine output = advisory report OR work item into the governed queue; direct change only through the guardrail matrix's merge policy. | | D7 | Hosting stance imported unchanged (deployment-owned binding): contract fixes invariants only — queue contract, per-class isolation floor, merge-policy caps including hosted-stays-human-gated, cost surfaced before any paid binding. Profiles are non-normative examples; no machine/org-size/budget assumption. | ### Constraints - Any fleet repo or vendor name in normative contract text is a defect; vendor names appear only as marked examples and in binding docs. - No second execution, merge, or scheduling path: routines enter work through the trigger contract's queue and are governed by the guardrail matrix from there. - Deterministic checks are never routines — no agent tokens on script work. - No new cost by default; paid/preview scheduling surfaces are advisory + explicit opt-in. - Boris-alignment is the standing acceptance criterion (no step-skipping, trust before scale). ### Acceptance criteria - Catalog glance layer alone answers "what classes exist, what governs each" — leaf docs only for v1 depth (progressive-disclosure conformance). - Every v1 leaf derives its guardrail row through the D2 rules; no row is hand-assigned outside them. - Mapping rules let an org classify a novel routine class end-to-end (axes → guardrail row → prerequisites) without contract changes. - Setup slice wires only reviewable changes, reconciles existing schedulers instead of duplicating them, and has zero paid dependencies on its default path. - Research gaps carried visibly: dependency-update-wave precedent detail rests on official docs whose page bodies were not all fetched in full (re-verify at architect); support→bug conversion class remains emerging/unverified (not in v1); one vendor goal-primitive claim unconfirmed (not load-bearing for v1). - Boris check: step-3 unlock sentence instantiated by the v1 subset running as governed background work; "break up your work into loops and routines" cell covered with the session-scoped vs standing distinction; step-3 trap honored (catalog-now, instantiate-on-trust — v1 dispatch gated by the admission policy and promotion discipline); token-efficiency bottleneck addressed (deterministic-stays-cron + cost tiers); step-1 guardrail untouched. ### Captured assumptions - The v1 classes' manual patterns remain proven in the first adopting instance (fleet dogfood evidence: recurring manual sweeps exist for each). - Vendor scheduling surfaces remain preview-stage moving targets; contract-tier docs stay binding-free and setup researches surfaces live (re-verify at setup time, not bake-in). - The guardrail matrix (WP5) and trigger contract (WP4) land before or with any v1 routine instantiation — routines have no governance path without them. ### Out-of-scope (deferred with triggers) - Non-v1 class definition leaves — trigger: T7's named join triggers per group (telemetry connector, analytics/feedback connected, org systems connected, named intel need, GUI-only system + isolation tier). - Vendor-binding capability templates (routine/workflow files) — build stage, post-architect. - Fleet routine stand-up + existing-scheduler reconciliation execution — /work-items backlog post-graduation. - Interactive escalation UX for routine-filed items — WP7 (inherited from guardrail package). ### Deferred questions - Exact class tokens + catalog table serialization — `/architect` (with plugin naming pass). - Suggested cadence defaults + per-class output templates — `/architect`. - Leaf-doc file split + glance wording — `/architect`. - Adapter shapes per scheduling-surface class — `/architect` (live-verified then). ## Plan Recommendation-locked this round under the user's standing pre-authorization: catalog hub `reference/routines.md` with v1 definition leaves under `reference/routines/` (one file per v1 class, kebab-case class tokens below); class tokens for the v1 roster — `issue-triage-sweep`, `duplicate-detection-sweep`, `backlog-readiness-check`, `pr-queue-tending`, `doc-freshness-sweep`, `dependency-update-wave`, `advisory-cve-triage`, `tech-debt-sweep`, `eng-metrics-digest`, `ci-health-review` (ten classes; dead-code sweep stays off the v1 roster per the D4 correction — the architect-round confirmation the correction asked for — and appears in the catalog as a `not-a-routine` row; hybrid v1 classes, e.g. `dependency-update-wave`, classify through the D2 hybrid branch: detection portion cron, judgment portion the routine); catalog table serialization = one markdown table (class token × judgment × output × access × derived guardrail row × v1/deferred + join trigger). Prerequisites: WP4 implementation merged (`reference/trigger-dispatch.md` — routines are scheduled trigger adapters behind its queue contract) and WP5 implementation merged (`reference/guardrails.md` — the D2 mapping rules derive rows into its matrix). This package merges after both. ### Phase 1: Catalog hub + mapping rules [DONE] | File | Action | What changes | |---|---|---| | `plugins/autonomy/reference/routines.md` | Create | The T7 routine contract as the catalog hub: routine definition (a scheduled trigger adapter behind the governed queue — never a private execution/merge path); the two-family ubiquitous language imported per D6 (session-scoped: loop, goal, batch, dynamic workflow; standing: schedule, routine — with the corrected batch definition); trigger taxonomy (schedule / event / continuous); output contract (advisory report OR work item into the governed queue; direct change only through the matrix merge policy); the D2 catalog-to-matrix mapping rules verbatim, INCLUDING the refined axes (DET → plain cron, never a routine — flagged `not-a-routine` in the catalog; hybrid DET-detect + AGT-judgment → split: detection portion cron, judgment portion IS the routine, derived through the AGT rules; AGT+report → C1; AGT+work-item → C1; AGT+direct-change → C2 where mechanically checkable else C3; structural/config-surface direct change → C4; input provenance: attacker-writable external content → C5, the untrusted-provenance class, not a caveat; when multiple rules match, the derivation COMPOSES TO THE HIGHEST-RISK class (C5 > C4 > C3 > C2 > C1 — a structural change driven by attacker-writable input derives C5 and its L3 floor, never C4's lower floor); AGT/HUM → human-gated disposition; access axis → prerequisites incl. L2 floor for repo-scoped, connector+entitlement for prod/product/org/ext, highest tier for unattended GUI actuation — without the C4/C5 axes the Phase 4 untrusted-provenance derivation could not run from the shipped contract); the glance-layer catalog table: all 39 classes as rows (class token, judgment, output, access, derived row, and a status flag — `v1`, a named join trigger, or `not-a-routine` for the seven pure-DET rows incl. dead-code sweep, which stay as rows with their cron/tooling precedent pointers rather than silent exclusions); hybrid rows show the D2 split (detection portion cron, judgment portion the routine); non-v1 rows carry precedent pointers only, no leaves; hosting stance per D7 (invariants only, profiles as non-normative examples). Zero vendor/fleet names in normative text. | **Sanity Check:** - `grep -c 'DET' plugins/autonomy/reference/routines.md` ≥ 1 and mapping rules present (`grep -ci 'plain cron' …/routines.md` ≥ 1) - Catalog rows: table row count = 39 (`grep -cE '^\| [a-z][a-z0-9-]+ \|' plugins/autonomy/reference/routines.md` = 39) - v1 rows = 10 (`grep -c '| v1 |' plugins/autonomy/reference/routines.md` = 10, or the chosen flag token) - `not-a-routine` rows = 7 (`grep -c 'not-a-routine' plugins/autonomy/reference/routines.md` ≥ 7); the dead-code-sweep row carries the flag (`grep 'dead-code' plugins/autonomy/reference/routines.md | grep -c 'not-a-routine'` = 1 — flagged, never a silent exclusion, and never a v1 leaf) - Vendor+fleet deny-list sweep exit 0; lychee lane passes ### Phase 2: v1 definition leaves [DONE] | File | Action | What changes | |---|---|---| | `plugins/autonomy/reference/routines/<class-token>.md` × 10 | Create | One leaf per v1 class, uniform D3 shape: purpose (toil addressed); trigger-taxonomy slot + suggested cadence default (issue-triage-sweep, duplicate-detection-sweep, backlog-readiness-check, pr-queue-tending: daily; doc-freshness-sweep, tech-debt-sweep, ci-health-review: weekly; dependency-update-wave, advisory-cve-triage: weekly with event-riding on advisories; eng-metrics-digest: weekly — all org-bindable values); access scope (repo); output contract (report vs work-item filing per class); guardrail row DERIVED via the D2 rules with the derivation shown (no hand-assigned rows); admission + escalation notes imported by citation from the guardrail contract; precedent pointer (the proven manual pattern). No vendor scheduling detail. | **Sanity Check:** - `ls plugins/autonomy/reference/routines/*.md | wc -l` = 10 - Every leaf shows its derivation: `grep -lc 'derived' plugins/autonomy/reference/routines/*.md | wc -l` = 10 - Every leaf names a cadence default: `grep -lc 'cadence' plugins/autonomy/reference/routines/*.md | wc -l` = 10 - Vendor+fleet deny-list sweep exit 0 ### Phase 3: Guided-setup routine slice [DONE] | File | Action | What changes | |---|---|---| | `plugins/autonomy/skills/setup/SKILL.md` | Modify | Routine slice (D5): interview scheduling surfaces (CI cron, dev machine, self-run infra, vendor-hosted preview) + budget posture; wire free defaults as reviewable changes (CI-cron handler shape = CI-orchestration home role, enabling settings = settings-as-code home role); advise paid/preview surfaces with cost surfaced; DETECT-DIFF-RECONCILE existing org schedulers and bots — a live dependency/stale bot IS a catalog-class instance: record it in the binding (class token + surface), never stand up a second mechanism. Binding home split by governance sensitivity (stress-test F1, the WP5 D2 split): the routine→work-class mapping each temporal signal's `signal.work_class` stamp derives from is ADMISSION data — it lands in the WP5 security binding's `admission.classification` (settings-as-code home, reviewable change, agent-unwritable), for reconciled bots too; only non-security keys (cadence, enablement, surface choice) land as the additive repo-local `routines` section of the schema-versioned binding. A repo-local class source would be the exact agent-writable bypass WP4's classification obligation forbids. Research scheduling surfaces live at setup time (preview-stage moving targets), never from this doc. | | `plugins/autonomy/skills/setup/templates/routine-definitions.md` | Create | Per-surface-class routine wiring shapes (CI-cron / local scheduler / self-run / vendor-preview as marked examples), parameterized by class token + cadence; every shape enqueues through the trigger contract's temporal adapter — no direct execution path. | | `plugins/autonomy/skills/setup/evals/evals.json` | Modify | Routine-slice cases: surface interview, free-default wiring, existing-bot reconciliation (record-don't-duplicate), paid-surface advisory refusal-to-default. | | `plugins/autonomy/README.md` + `plugins/autonomy/.claude-plugin/plugin.json` | Modify | Capability list + description + minor version bump. | **Sanity Check:** - `/skill-quality:check` + `validate-evals` pass; `claude plugin validate --strict` exit 0 - `grep -ci 'detect-diff-reconcile' plugins/autonomy/skills/setup/SKILL.md` ≥ 2 (guardrail slice + routine slice each state it) - `grep -c 'temporal' plugins/autonomy/skills/setup/templates/routine-definitions.md` ≥ 1 (queue-entry via the trigger contract) - Fleet-name sweep exit 0 ### Phase 4: Derivation demonstration + gates [DONE] Acceptance probe: classify one NOVEL routine class end-to-end using only the shipped mapping rules (axes → guardrail row → prerequisites) — the acceptance criterion that an org can classify without contract changes; the chosen novel class MUST be an untrusted-provenance case (attacker-writable external input) so the probe exercises the D2 provenance axis and the C5 reach the stress-test found missing, not just the easy C1–C3 path; record the worked example in the PR body (not in the contract — it is evidence, not normative text). Then the full gate roster (`validate-plugins.sh`, `run-plugin-tests.sh`, `validate-plugin-contracts.mjs`, markdown/typos/lychee, `claude plugin validate --strict`, catalog regen). Near-duplicate audit statement: routines compose the WP4 temporal adapter + WP5 matrix; deterministic checks stay plain cron; no second scheduling or merge path created. **Sanity Check:** - Worked novel-class derivation present in the PR body - All gate scripts exit 0; catalog in-sync - Near-duplicate audit statement present in the PR body ## Blast radius MEDIUM — one plugin's files; the catalog constrains WP7 and every adopting org's background maintenance, but all governance content is imported from WP4/WP5 by citation, not redefined. Fully git-revertible. ## Stress-test summary Fresh-context plan review (WP6+WP7 batch): 10 findings, verdict FIX-THEN-SHIP, WP6's share folded — F1 (HIGH, cross-package): the routine→class mapping had no security-surface home; the only binding home named was the repo-local additive-section idiom, which is exactly the agent-writable class source WP4's classification obligation forbids → the mapping lands in the WP5 security binding's `admission.classification` (reconciled bots included); only cadence/enablement/surface stay repo-local. F3 (MED-HIGH): binary DET/AGT mapping could not classify hybrid classes (a v1 class among them), and two Phase-1 gates were mutually unsatisfiable (39 rows vs dead-code = 0) → D2 hybrid branch + explicit `not-a-routine` catalog flag for the seven pure-DET rows; gates reconciled. F4 (MED-HIGH): derivation reached only C1–C3, silently breaking the novel-class acceptance criterion for C4/C5 → provenance and structural-blast axes added; the Phase 4 probe now must exercise an untrusted-provenance case. F8 (LOW) routes to WP5's work-classes leaf (C1 permits governed-queue writes, no repo mutation). ## Execution shape Fully sequential 1 → 2 → 3 → 4 — leaves derive through Phase 1's rules; the slice wires Phase 2's classes; Phase 4 proves Phase 1's rules on a novel class. Cross-package: after the WP4 and WP5 implementation PRs. | Phase | Surface | Basis | |---|---|---| | 1 | main-session | normative catalog + mapping-rule authoring | | 2 | main-session | ten uniform leaves, derivation judgment per class | | 3 | main-session | setup-skill judgment | | 4 | main-session | derivation probe + gate runs | ## Open questions - Fleet routine stand-up + existing-scheduler reconciliation execution — /work-items backlog post-merge (Brief out-of-scope, trigger recorded). ## Decisions made (gate-passed) | Decision | What it changes in the plan | Basis (evidence) | |---|---|---| | v1 roster confirmed at 10 classes (dead-code sweep stays out) | Phase 1 table + Phase 2 leaf count | D4's correction is evidence-backed (RESEARCH-routine-catalog row 25: DET detect); D2's rule excludes DET mechanically | | Class tokens = kebab-case descriptive names (list in preamble) | Phases 1–2 | Matches every shipped contract token set; one token per catalog row | | Hub-and-leaves under `reference/routines/` | Phase 1–2 paths | Same layout decision as WP5's guardrail leaves (subdir per hub) | | Cadence defaults: daily for queue-tending sweeps, weekly for the rest | Phase 2 leaves | Proven manual patterns' observed frequency; org-bindable values, not contract | | Novel-class derivation as the Phase 4 acceptance probe | Phase 4 | Brief acceptance criterion stated verbatim; cheapest mechanical proof of the mapping rules | ## Handoff to implementation ### User-approval gates - Any change to the D2 mapping rules or the v1 roster during implementation → STOP (user-locked content). - Any scope expansion beyond the four phases re-enters `/architect review`. ### Execution shape ([EXEC-SHAPE] tagged) Sequential 1→4, all main-session (table above). PLAN.md phase tags advance in the same commit as each phase. ### Mechanical work Commit per phase on the implementation branch (suggest `feat/autonomy-routines`); gates re-run in full at Phase 4; PR body carries the derivation example + near-duplicate audit statement + this PLAN in a `<details>` block at close-out. </details> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Summary Effort-end housekeeping for the AI-adoption-ladder effort: all seven work packages are merged, so the per-WP topic slices come out per the close-out convention (publish the PLAN, then prune the slice). - WP4–WP7 PLANs were already spliced into their PR bodies at each close-out (#372, #377, #600, #676). - WP1–WP3 PLANs are now published the same way into #333, #343, and #356 (each merged PR body gained a `PLAN (published at effort-end close-out)` details block), so every slice's content has a durable home on its merged PR before the directory is removed. - The WP map in `docs/topics/ai-adoption-ladder/index.md` now records delivery PRs instead of live slugs. The effort hub itself (index, design evidence, research docs) stays. No content is lost: every deleted PLAN.md is byte-recoverable from git history and readable in its merged PR's details block. ## Related No linked issue (effort-end housekeeping; the WP map issue #239 is already closed). - #239 (WP map: AI adoption ladder — the effort this closes out) - #333 / #343 / #356 (WP1–WP3 delivery PRs, PLANs published into their bodies in this close-out) - #372 / #377 / #600 / #676 (WP4–WP7 delivery PRs, PLANs published at their own close-outs) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ger row (#698) ## Summary D1 sweep for #353: the WP1–WP7 PLAN out-of-package notes (mined from the merged PR bodies #333/#343/#356/#372/#377/#600/#676) contained 31 deferred notes; every note that named no tracking issue and had no contract-recorded trigger now has a durable home: - **README roadmap** gains four trigger-gated rows: fleet guardrail materializations (WP5), fleet routine stand-up + existing-scheduler reconciliation (WP6), vendor-binding capability templates (WP6), cost enforcement / hard spend caps (WP5, 3→4 trigger). - **Trigger register** gains the second-binding-consumer cross-repo drift check (WP1). - **`reference/return-accounting.md`** records the per-work-class precision-graduation deferral beside its band-stability rule (WP3). - **CHANGELOG.md created** for the autonomy plugin (first entry; 0.1.0–0.7.0 history stays in the WP PR bodies) + version bump to 0.7.1 — starts the CHANGELOG-parity posture #663 gates on. All other mined notes were already covered: tracked issues (#351, #352, #694–#697), contract-recorded triggers (telemetry immutable-ID, return-accounting expansion/revisit), delivered sibling WPs, resolved `/architect` questions, or the user-held dormant triggers (runner build T4, L3 backend, merge serialization, mid-run interrupt, org-enablement, cross-team). Full disposition table lands as a comment on #353 at close. Documentation only — no contract semantics change. ## Related - #685 (effort-end prune that moved the PLAN records into the merged PR bodies) - #663 (CHANGELOG-parity gate this PR's new CHANGELOG.md starts satisfying) - WP delivery PRs mined: #333, #343, #356, #372, #377, #600, #676 Closes #353 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
WP4 trigger-dispatch package (T1), per the merged PLAN (all four phases complete on this branch).
No linked issue
Related
What ships
plugins/autonomy/reference/trigger-dispatch.md(new contract): four signal-surface classes (tracker-vcs-event/temporal/agent-internal/channel-feed), two recorded attributes (provenance, transport incl.push-lifecycleexpiry fail-close + poll-detector backstop), six class-generic adapter obligations (normalize+enqueue only; layered idempotent dedup with create-then-reconcile and a live-duplicate drain guard; provenance + durable raw link; traceparent propagation; admission enforcement at the seam with absent-binding fail-close; closed-loop ack), security-surface work-class stamping with verifiedagent-internalparent provenance and higher-of class resolution, the schema-versioned signal envelope (marker<!-- autonomy:signal:v1 -->), kick + drain dispatch with the one-entrypoint invariant and execution-surface attestation, executor surface classes with security-ownedexecutor_class.skills/setup/SKILL.md): discovery-first per-class surface interview, DIY-floor kick + hourly drain as reviewable changes, plan-gated surfaces advisory with cost, execution-surface recording with the attestation caveat stated, fail-closed absent-admission wiring, additivetriggersbinding section (sharedsurfacesmap shape that later scheduling sections reuse).templates/trigger-adapters.md(per-class adapter shapes, six obligations inline),templates/ack-reply.md(one-line ack, item URL first).scripts/check-signal-envelope.mjs— validates the envelope marker record (required keys, enums, W3C traceparent, agent-internalsignal.parent_itemas normalized canonical URL, deterministic raw-link origin branching via the binding's surfaces maps), exit contract 0/1/2 mirroringcheck-emission-conformance.mjs.Conforming-path demo (scratch repo, live)
Run on
kyle-sexton/autonomy-demo-scratch(private throwaway) with the work-items capability's tracker seam installed (tools/work-item-tracker/copied per its documented install path) and a repo-local binding carrying thetriggerssection. No admission binding present → the fail-closed path is what the demo proves. No paid service touched.autonomy:triggerapplied to source issue #2; labeled-event id28172185501fetched from the issue timeline (surface-native unique event id →signal.identity).[](no existing item).work-item-tracker.sh create-item): queue item #3 created carrying the full envelope — class/transport/provenance, identity, raw link (https://github.com/ghapi/repos/kyle-sexton/autonomy-demo-scratch/issues/events/28172185501), traceparent00-dab5c21d6389b65c950f6b13f14af7e3-de1b63c53960913b-01(the trigger-hop trace context observed on the enqueue record), NOsignal.work_class(no classification rules bound → unclassified), labelneeds-human.Queued as https://github.com/kyle-sexton/autonomy-demo-scratch/issues/3 (autonomy: tracker-vcs-event signal).check-signal-envelope.mjs <item-3-body> --binding .claude/autonomy/binding.json→Signal-envelope conformance OK: 1 envelope(s) checked.exit 0.work-item-tracker.sh claim github:kyle-sexton/autonomy-demo-scratch#3→ lease acquired (holder: kyle-sexton, lease comment5014153239), exit 0.OPEN, labels["needs-human"], assignees[]. This also exercises the unclassified → human-gated path (no security binding → nosignal.work_class).No second dispatch path was exercised anywhere: the event kick and the drain both funnel into the seam lease.
Near-duplicate audit
The dispatch entrypoint composes the work-item queue capability's EXISTING autonomous drain mode and race-safe seam lease — no second claim or dispatch mechanism was created anywhere in this package. The adapter templates normalize and enqueue only; the drain-side live-duplicate guard and the create-then-reconcile rule reuse the queue's own primitives.
Gates
scripts/validate-plugins.shexit 0;node scripts/validate-plugin-contracts.mjsexit 0 (vendor deny-list clean overreference/);node scripts/generate-catalog.mjs --checkin sync.claude plugin validate plugins/autonomy --strictpass; skill-qualitycheck-skill.sh setupPASS (2 pre-existing WARNs: line soft-target, gotchas surface);evals.jsonvalid against the bundled schema (ajv draft2020).--binding, ci-cron origin withfile:link, unrecorded surface, agent-internal missingsignal.parent_item→ 1 each.Boris check
The 2→3 cell ("break up your work into loops and routines; let Claude kick off Claude") is instantiated as governed queue + kick; the step-3 trap is honored (audit trail before scale — envelope, ack, lease, refusal are all tracker-resident audit records; no agent-count machinery); the step-1 guardrail is untouched; the telemetry trace tree is the kick-off audit trail (traceparent on the enqueue record).
Fresh-docs mandate: GitHub Actions events reference and the Claude Code headless reference re-fetched this session before the SKILL.md edit (cited in the Phase 2 commit).
PLAN.md (as merged, tags advanced)
ai-ladder-wp4-trigger-dispatch
Brief
TLDR
Trigger-dispatch package (T1): a contract doc for signal adapters and dispatch, plus the
trigger/dispatch slice of guided-setup. Adapters normalize signals from four surface classes
into the governed work-item queue; one dispatch entrypoint drains it; the executor is
swappable behind the invocation-adapter seam.
Goal
Any adopting org can wire its signal surfaces into the governed queue and get autonomous
dispatch without a human kick — contract vocabulary only, free by default, audit trail
intact end to end.
Locked decisions
Constraints
only as marked examples and in binding docs.
anywhere in the package.
org's own systems own event definition and raising.
scale).
Acceptance criteria
transport enum, and kick + drain dispatch spec — in contract vocabulary only.
surfaces with cost, and has zero paid dependencies on its default path.
initiate-vs-notify UNVERIFIED; the two channel-agent vendor surfaces relied on are
alpha/beta moving targets; no first-party Discord trigger found (UNVERIFIED-absence).
off Claude") is instantiated as governed queue + kick; the step-3 trap is honored (audit
trail before scale, no agent-count machinery); the step-1 guardrail is untouched; the T6
trace tree is the kick-off audit trail.
Captured assumptions
entrypoint (live-verified against the installed capability this session).
and manual-dispatch events (official README fetched this session).
(RESEARCH-channel-adapters.md, Jul 2026; flagged as moving targets).
Out-of-scope (deferred with triggers)
/work-items backlog post-graduation.
matrix instance).
Deferred questions
/architect(with the plugin naming pass)./architect./architect./architect.Plan
Recommendation-locked this round under the user's standing pre-authorization (same basis as
the WP2/WP3 round): surface-class tokens
tracker-vcs-event/temporal/agent-internal/channel-feed; transport enumpush/push-lifecycle/poll; provenance enumhuman/agent/system; signal envelope = a JSON-fenced marker record on the queued item (the WP3marker-record precedent), keys
signal.class,signal.transport,signal.provenance,signal.identity,signal.raw_link,signal.traceparent, plus optionalsignal.work_class(the T3 risk class the adapter STAMPS from the security-surface classification rules — see
Phase 1 (3bis); absent = unclassified = fail-closed human-gated) and
signal.parent_item(REQUIRED when
signal.classisagent-internal: the canonical URL of the emittingsession's admitted source item — the machine-verifiable provenance the stamped class is
checked against; a stamped class is never trusted on its own) and
signal.source_surface(REQUIRED when
signal.classistemporal: the originating scheduling surface's id asrecorded in the
triggersbinding — the discriminator raw-link form validation brancheson, since
temporalalone spans both CI-cron and local-scheduler surfaces).Authoring constraint (cross-package link hygiene): this package merges before WP5, so
normative text references the admission seam and classification rules by contract CONCEPT
only — never a file link to a not-yet-existing guardrail doc; WP5 adds the file cross-links
back to the then-existing
trigger-dispatch.md.Prerequisite: WP2 telemetry PR merged (
reference/telemetry.mdexists — this contract importsits trace-tree obligation) and WP3 PR merged (marker-record precedent + return-accounting
capture rides the same close boundary the dispatch drain crosses). Implementation ordering
with WP5: this package's PR merges FIRST; admission enforcement here fail-closes when no
admission binding exists (absent policy → every dispatch human-gated), and WP5 then ships the
policy surface that relaxes it — no dead link in either direction, no dispatch window ever
runs ungoverned.
Phase 1: Trigger-dispatch contract doc [DONE]
plugins/autonomy/reference/trigger-dispatch.mdtracker-vcs-event(tracker/VCS-host events: label, assignment, @-mention, PR event),temporal(schedule + poll-fallback detectors),agent-internal(a session emits follow-up work),channel-feed(chat mention + continuous monitor) — ALL contract-active; per-org availability is a binding outcome, entitlement gaps route to the advisory path (WP1 D6), never a contract deferral; carried research gaps stated in SURFACE-CLASS vocabulary in this doc (channel-monitor ambient initiate-vs-notify UNVERIFIED; the relied-on channel-agent vendor surfaces are alpha/beta moving targets; one major chat platform has no first-party trigger, UNVERIFIED-absence) — vendor names for these gaps live in SKILL.md only, per the contract validator's vendor deny-list. (2) Two recorded signal attributes: initiator provenance (human/agent/system— audit + guardrail input, recorded never trusted as isolation, per T2) and transport (push/push-lifecycle/poll; push preferred where offered, poll universal fallback viatemporal;push-lifecyclecarries subscription expiry + renewal + validation handshake obligations, and EXPIRY semantics are normative: everypush-lifecyclewiring is backed by atemporalpoll-detector backstop for the same surface, or the subscription-health lapse fail-closes — files a human-gated alert item — so a lapsed subscription can never silently drop signals). (3) Six class-generic adapter obligations imported from the Brief D4 verbatim: normalize+enqueue only; idempotent dedup keyed onsignal.identity(surface-native unique event id where the surface issues one; the FALLBACK identity is never a bare content hash — it composes source scope (surface class + origin locator) + an event-instance discriminator (delivery id or event timestamp) + the content hash, so two legitimate repeated signals with identical payloads stay distinct instances; for state-based poll detectors that re-observe a continuing condition and have no instance identity, dedup retention is BOUNDED to items still open — the same finding may re-enqueue once its prior item closes (a re-detected regression is a new signal). Dedup enforcement is NOT a bare read-then-write: concurrent at-least-once deliveries can both pass a search before either item exists, so the adapter uses an atomic identity-keyed create/upsert or queue-side uniqueness guarantee where the tracker offers one; where none exists, search-before-create is backed by CREATE-THEN-RECONCILE — after creating, re-search bysignal.identityand, on finding an older item with the same identity, close the newer one as an audited duplicate (oldest wins, deterministic) — plus a drain-side guard scoped to LIVE duplicates only: the drain never claims an item whosesignal.identitymatches another currently-open item (claimed or awaiting reconciliation) — completed items are excluded from the guard, consistent with the bounded-retention rule, so a fixed-then-regressed condition re-enqueues AND executes, while an unreconciled concurrent twin still cannot run the same work twice); provenance capture + durablesignal.raw_link; trace-context propagation (signal.traceparent, one causal tree per the telemetry contract); admission enforcement at the seam (policy content owned by the guardrail matrix; the adapter enforces, never defines; unadmitted → human-gated item or audited rejection; ABSENT admission binding → fail-closed: enqueue human-gated, never drop silently); closed-loop acknowledgment (bidirectional surfaces echo the item reference back; reply-less surfaces satisfy viasignal.raw_linkalone). (3bis) Work-class classification obligation: admission and the whole guardrail matrix key on the T3 risk class, so a queued item needs one — the adapter STAMPSsignal.work_classfrom the classification rules on the org's SECURITY governance surface (owned by the guardrail contract; the adapter stamps, never defines): tracker-vcs-event resolves via the security-bound label→class rules; temporal signals carry the class their bound routine/detector definition derives; agent-internal items must PROVE protected provenance — the envelope SERIALIZES the emitting session's own admitted source item assignal.parent_item(required for this class), and the admission seam VERIFIES the session-to-parent association against protected dispatch data (the queue's own lease/claim record of which item the emitting session was dispatched on — an agent-supplied URL alone proves nothing: any session could cite an unrelated C1 item to launder higher-risk follow-up work); an association the seam cannot verify is treated as NO provenance (unclassified, fail-closed human-gated). Admission then RESOLVES the verified parent's class from its own protected classification rather than trusting the stamped value: the effective class is the HIGHER of that inherited class and the class the security-surface rules derive for the target — a session never self-assigns a lower class for its follow-up work (self-classification would bypass admission: stamp C1 on C4 work and the table admits it); an agent-internal signal that cannot prove that provenance stays UNCLASSIFIED (recorded withagentprovenance, admission-checked like any input); channel-feed and any signal the rules cannot resolve stay UNCLASSIFIED → fail-closed human-gated. No repo-local (agent-writable) surface may supply the class used for admission. (4) Signal envelope serialization: JSON-fenced marker record on the queued item, the six requiredsignal.*keys plus optionalsignal.work_class,schema_versionfrom"1.0"— additive evolution, same governance as every contract schema. (5) Dispatch spec: push kick where the platform offers it + a standing scheduled drain as universal fallback and catch-up net for ENQUEUED items (default cadence hourly, org-bindable; the drain never re-scans a source surface — missed enqueues are the poll-detector backstop's job per (2)); one-entrypoint invariant — every kick funnels into the work-item queue capability's autonomous drain mode via the invocation-adapter seam; the race-safe lease makes concurrent kicks harmless; concurrency and per-run caps are guardrail-policy knobs — named descriptively here, serialized tokens (autonomous_concurrency,items_per_run) owned by the admission policy. (6) Executor surface classes imported from T4 unchanged (self-operated CLI/SDK incl. SDK-embedded pull daemons vs vendor-hosted with human-merge-gate cap); the executor-class determination that gates merge policy is SECURITY-surface data (the guardrail binding'sexecutor_class), never a repo-local value; other executor hosting config stays deployment-owned (T7 stance) — contract fixes isolation floor L2+, credential scoping, queue contract only. Constraints restated normatively: no queue bypass, no second dispatch mechanism, no invented event bus, no new cost by default, vendor names only as marked examples.Sanity Check:
grep -c 'tracker-vcs-event' plugins/autonomy/reference/trigger-dispatch.md≥ 1 and same fortemporal,agent-internal,channel-feedgrep -c 'push-lifecycle' plugins/autonomy/reference/trigger-dispatch.md≥ 1 and expiry backstop stated (grep -c 'poll-detector' …/trigger-dispatch.md≥ 1)grep -c 'signal.identity' plugins/autonomy/reference/trigger-dispatch.md≥ 1,grep -c 'signal.work_class' …/trigger-dispatch.md≥ 1, andgrep -c 'signal.parent_item' …/trigger-dispatch.md≥ 1grep -ci 'fail-closed' plugins/autonomy/reference/trigger-dispatch.md≥ 2 (absent-admission AND unclassified behavior stated)grep -ci 'UNVERIFIED' plugins/autonomy/reference/trigger-dispatch.md≥ 2 (research gaps carried in surface-class vocabulary, not laundered)grep -c 'guardrails' plugins/autonomy/reference/trigger-dispatch.mdcounts concept references only —grep -cE '\]\((\./)?(guardrails|.*guardrails/.*)\.md' …/trigger-dispatch.md= 0node scripts/validate-plugin-contracts.mjsexit 0)Phase 2: Guided-setup trigger/dispatch slice [DONE]
Extends the
setupskill (discovery-first per D7/WP1 D7). First work item — fresh-docsmandate (repo CLAUDE.md): re-fetch the official docs for any vendor surface the slice names
(CI events, schedule syntax, headless invocation) before editing SKILL.md.
plugins/autonomy/skills/setup/SKILL.mdchannel-feedwhere wanted); advise plan-gated native integrations with cost surfaced; bind the drain (cadence default hourly, org override recorded); EVERY kick/drain wiring records its named execution surface — the same surface id the guardrail security binding's per-surface isolation entries key on — but the RECORDED id is repo-local (agent-writable) convenience only: the admission/executor seam derives the ACTUAL execution-surface identity from trusted dispatch/runner context (platform-attested runtime metadata, matched against per-surface identifying markers the SECURITY binding's isolation entries declare) and verifies it against the recorded id, consulting the ACTUAL surface's isolation verdict — a mismatch, an unattestable actual surface, or a surface with no L2 binding all fail-close to human-gated, so rewriting the recorded id to a bound sibling cannot launder execution onto an unbound runner; record the trigger binding as an ADDITIVEtriggerssection of the WP1 schema-versioned binding (absent-section tolerance; no major bump). Admission enforcement wiring points at the guardrail admission seam and states the fail-closed absent-binding behavior. All vendor env vars/event names live here, never inreference/.plugins/autonomy/skills/setup/templates/trigger-adapters.mdplugins/autonomy/skills/setup/templates/ack-reply.mdQueued as <item-url> (autonomy: <class> signal)— one line, item URL first (the join key), class token for audit; reply-less surfaces documented as satisfied bysignal.raw_link.plugins/autonomy/skills/setup/scripts/check-signal-envelope.mjssignal.*marker record — six required keys present, enum values legal (incl.signal.work_class∈ C1–C5 when present),signal.parent_itemREQUIRED (well-formed absolute https URL) wheneversignal.classisagent-internal— its absence fails validation, since an unverifiable self-stamped class would bypass admission; the class-resolution itself (inherited-vs-derived max) is the admission seam's job at dispatch time, the checker enforces the serialized provenance it needs — the ITEM URL normalized per the telemetry contract's strip rule,signal.raw_linkvalidated as a DURABLE absolute reference, form branched DETERMINISTICALLY by serialized origin: the checker takes the schema-versioned binding as an input and resolvessignal.source_surfaceagainst BOTH its additive sections that record scheduling surfaces —triggers(kick/drain wiring) androutines(each routine's recorded scheduler/surface choice), since routine signal producers record their surface in the latter, not as trigger entries — a temporal signal whose source surface the binding records as local-scheduler class may carry a durable local/artifact URI (an absolutefile:URI or org artifact-store locator — such surfaces have no web origin); every other signal (including temporal from CI-cron or any remote surface) requires a well-formed absolute https URL (query/fragment PRESERVED — comment anchors and permalinks need them; the strip rule is the join key's, not the raw link's); a temporal signal missingsignal.source_surface, or naming a surface the binding does not record, fails validation; relative or ephemeral references fail either branch,schema_versionknown. Mirrorscheck-emission-conformance.mjs(same exit contract 0/1/2).plugins/autonomy/skills/setup/evals/evals.jsonplugins/autonomy/README.mdplugins/autonomy/.claude-plugin/plugin.jsonSanity Check:
/skill-quality:check+validate-evalspass (skills_root=plugins/autonomy/skills)claude plugin validate --strictexit 0grep -c 'signal.identity' plugins/autonomy/skills/setup/templates/trigger-adapters.md≥ 1node plugins/autonomy/skills/setup/scripts/check-signal-envelope.mjs(no args) exits 2 with usagevalidate-plugin-contracts.mjs) exit 0Phase 3: Conforming-path demonstration [DONE]
Acceptance probe on a scratch consumer repo (NOT this repo, WP2 Phase 3 precedent — the ban
there is on FLEET bindings/plugins, not on composing a sibling CAPABILITY: the scratch repo
INSTALLS the work-items capability plugin, whose queue + race-safe lease IS the seam under
demonstration; stubbing the lease would weaken the one-entrypoint proof to nothing): a
tracker-vcs-event stub (label applied) runs the adapter shape → item lands in the governed
queue carrying the full signal envelope + ack echo on the source surface; the scheduled-drain
entrypoint claims it via the seam lease (autonomous drain mode) — demonstrating kick + drain
funneling into ONE entrypoint; TRACEPARENT from the trigger hop is observed on the enqueue
record (telemetry-contract import).
check-signal-envelope.mjspasses against the createditem. Absent-admission fail-closed is demonstrated live: with no admission binding present,
the drained item is refused autonomous execution and remains human-gated, with the refusal
audited on the item (this also exercises the unclassified → human-gated path: no security
binding means no classification rules, so the item carries no
signal.work_class).Sanity Check:
signal.*marker record (check-signal-envelope.mjsexit 0)Phase 4: Gates [DONE]
Full in-repo gate run (WP2 Phase 4 roster):
scripts/validate-plugins.sh,scripts/run-plugin-tests.sh,node scripts/validate-plugin-contracts.mjs, markdown/typos/lychee lanes,
claude plugin validate --strict, catalog regen check. Near-duplicate auditstatement: the dispatch entrypoint composes the work-item queue capability's existing
autonomous drain — no second claim/dispatch mechanism was created.
Sanity Check:
node scripts/generate-catalog.mjsreports in-syncBlast radius
MEDIUM — one plugin's files, but the contract constrains WP5/WP6/WP7, every future adopter,
and the admission seam WP5 binds to; new convention + cross-package coupling triggers match.
Fully git-revertible; automated gates cover shared surfaces.
Stress-test summary
Fresh-context plan review (WP4+WP5 batch): 14 findings, verdict FIX-THEN-SHIP, all folded.
WP4's share — F1a (HIGH, cross-package): the envelope carried no T3 work class, leaving the
autonomous happy-path non-functional (safe but dead) →
signal.work_class+ the (3bis)classification obligation, class source pinned to the security surface; F2 (HIGH): verbatim
vendor names in reference/ collide with the contract validator's vendor deny-list →
surface-class vocabulary in reference/, vendor specifics in SKILL.md; F3 (MED-HIGH): the
telemetry strip rule misapplied to
signal.raw_linkwould corrupt durable event links →strip rule confined to the item URL; F4 (MED): push-lifecycle expiry had no fail-closed
semantics → poll-detector backstop / fail-closed lapse, and the drain's no-rescan limit
stated; F5 (MED): the Phase 3 seam demo now explicitly installs the work-items capability
(sibling capability ≠ banned fleet binding); F6 (MED, latent): forward file-links to
not-yet-existing guardrail docs → concept-only references, WP5 back-links; F7 (LOW): cap
knob token ownership pinned to the admission policy's serialized names. F8 (LOW) accepted:
the no-second-dispatch and Boris checks stay prose PR-body audits, consistent with the
WP2/WP3 round.
Execution shape
Fully sequential 1 → 2 → 3 → 4 — Phase 2 cites Phase 1's contract; Phase 3 exercises Phase 2's
templates and check script; Phase 4 gates the tree. Cross-package: this PR merges before the
WP5 implementation PR (WP5's admission policy binds the seam this contract defines; the
fail-closed absent-binding clause makes the interim safe).
Open questions
/work-items backlog post-merge (Brief out-of-scope, trigger recorded).
Decisions made (gate-passed)
tracker-vcs-event/temporal/agent-internal/channel-feedpush/push-lifecycle/pollpush-lifecyclecompresses PUSH-WITH-LIFECYCLE without semantic losssignal.*keys,schema_version"1.0"signal.work_classoptional envelope key + security-surface classification obligation; unclassified → human-gatedexecutor_class, never repo-local data[FALLBACK — confirm or override]
check-signal-envelope.mjsas a NEW deliverable (Phase 2) —invented beyond the Brief on the WP2 precedent (a pinned contract with no enforcement surface
is undetectable drift). Flag if unwanted.
Handoff to implementation
User-approval gates
path, STOP and re-surface — the one-entrypoint invariant is non-negotiable.
/architect review.Execution shape ([EXEC-SHAPE] tagged)
Sequential 1→4, all main-session (table above). PLAN.md phase tags advance in the same commit
as each phase; scratch-repo demo per Phase 3; divergence escalation applies to every phase.
Mechanical work
Commit per phase on the implementation branch (suggest
feat/autonomy-trigger-dispatch);gates re-run in full at Phase 4; PR body carries the demo transcript + near-duplicate audit
statement + this PLAN in a
<details>block at close-out.🤖 Generated with Claude Code