diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 91c1dc947..d36915de3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -398,6 +398,12 @@ "category": "design", "tags": ["miro", "mcp", "boards", "sticky-notes", "eventstorming", "diagramming", "visual-collaboration"], "defaultEnabled": false + }, + { + "name": "autonomy", + "source": "./plugins/autonomy", + "category": "autonomy", + "tags": ["autonomy", "adoption", "governance", "topology", "binding", "guided-setup"] } ], "renames": { diff --git a/README.md b/README.md index f6f796910..17284c3a5 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,10 @@ user opts in with `/plugin enable`; an existing install is never flipped by cata - [`claude-ops`](plugins/claude-ops) — Claude Code operations toolkit. Five skills: observability (read locally captured telemetry — OTEL store, collector, hook-event JSONL, ccusage — with trend reports and store pruning), known-issues (search known Claude product GitHub bugs, check service health, maintain a persistent tracked-issue registry), changelog (ingest Claude Code changelog entries and integrate them into the current repo), plugins (bring a machine's plugin fleet current on demand — marketplace refresh, effective-scope updates including in-repo project/local installs, new-plugin install per policy, scope-divergence detection and explicit convergence), and a re-runnable setup action that settles where the known-issues registry lives. Plus a family of seven advisory *-audit telemetry-emitter hooks (API errors, config changes, instruction loads, permission denials, pre-compaction, skill usage, tool failures) that emit the shared hook-telemetry envelope, and a reference sink that maps envelopes into the hook-events.jsonl the observability skill reads. - [`skill-quality`](plugins/skill-quality) — Skill-authoring QA tooling: a static contract checker that runs seventeen deterministic checks over a Claude Code skill (frontmatter, listing-budget cap, trigger-keyword preservation, line caps, broken internal refs, markdownlint, gotchas surface, evals presence) and a bundled evals.json schema for validation. Runs against any repo's skills directory via the convention-resolution ladder — no baked layout. +### Autonomy + +- [`autonomy`](plugins/autonomy) — Governed autonomous agent operation: role-topology, binding-seam, and wiring-vs-advisor contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state and writes its schema-versioned binding. + ### Security - [`guardrails`](plugins/guardrails) — Eight safety guards that block secret/credential writes, hardcoded machine-specific paths, git hook-bypass attempts, irreversible git operations (force-push, reset --hard, worktree-wide checkout/restore discards), Bash file-write workarounds that circumvent Write/Edit hooks, (advisory) hallucinated CLI flags, (advisory) un-throttled Workflow fan-out that risks burst 529s, and (advisory) direct git commit/gh pr create calls bypassing this marketplace's own commit/pull-request skills — each independently toggleable. diff --git a/docs/CATALOG-TAXONOMY.md b/docs/CATALOG-TAXONOMY.md index b8a317d26..d3db39bf5 100644 --- a/docs/CATALOG-TAXONOMY.md +++ b/docs/CATALOG-TAXONOMY.md @@ -47,6 +47,7 @@ Domain-and-cross-cutting tier — filed by subject: | Category | Scope | |---|---| | `claude-code` | Operating Claude Code itself: its config, memory, telemetry, session plumbing, usage playbooks. Membership requires the subject to *be* Claude Code, not merely to run on it. | +| `autonomy` | Governed autonomous agent operation: adoption discovery, guardrail and sandbox contracts, standing routines, autonomy telemetry and return-accounting conventions. | | `security` | Secret, path, and bypass guarding. | | `workflow` | Conducting the development session and process: staging, handoff, retrospective, orchestration priming. | | `project-management` | Tracking, triaging, and decomposing the work backlog. | @@ -78,6 +79,7 @@ Category-level: |---|---| | First deployment plugin lands | Populate the `deployment` category (already reserved above). | | A non-music creative plugin lands | Broaden `music` — rename to `creative` or add a sibling creative category — rather than filing the newcomer under `music`. | +| A broader automation plugin lands (automation that is not governed-autonomy-scoped) | Broaden `autonomy` or add a sibling category rather than filing the newcomer under `autonomy`. | Plugin-scoped (owned by the named plugin's README): diff --git a/docs/topics/ai-ladder-wp1-packaging/PLAN.md b/docs/topics/ai-ladder-wp1-packaging/PLAN.md index aa29f809c..1d1024115 100644 --- a/docs/topics/ai-ladder-wp1-packaging/PLAN.md +++ b/docs/topics/ai-ladder-wp1-packaging/PLAN.md @@ -71,7 +71,7 @@ topology contract docs only (per-capability contracts land with WP2–WP6); guid as a v0 discovery-phase skill; the fleet binding instance lands in the org-policy home (`melodic-software/standards`) via a separate small PR that merges after the plugin PR. -### Phase 1: Category vocabulary + generator [TODO] +### Phase 1: Category vocabulary + generator [DONE] | File | Action | What changes | |---|---|---| @@ -83,7 +83,7 @@ as a v0 discovery-phase skill; the fleet binding instance lands in the org-polic - `grep -c '\`autonomy\`' docs/CATALOG-TAXONOMY.md` ≥ 1 - `grep -n 'autonomy' scripts/generate-catalog.mjs` shows a `CATEGORY_ORDER` entry -### Phase 2: Plugin scaffold [TODO] +### Phase 2: Plugin scaffold [DONE] First work item — migration-gate step 1: re-fetch the official plugins/plugin-manifest docs (fresh-docs mandate) before authoring. Migration-gate step 6 (PII/secrets strip) runs before @@ -102,7 +102,7 @@ the first commit. - `node scripts/generate-catalog.mjs` reports in-sync - `grep -riE 'melodic-software|ci-workflows|github-iac' plugins/autonomy/ --exclude=plugin.json` returns empty (author metadata in plugin.json is the only allowed occurrence) -### Phase 3: Topology contract docs [TODO] +### Phase 3: Topology contract docs [DONE] All three docs in `plugins/autonomy/reference/` are tool-agnostic contract markdown: roles and surface classes only. Vendor and fleet names banned outright — real instances live in the @@ -112,7 +112,7 @@ path) live in SKILL.md/README, never in `reference/`. | File | Action | What changes | |---|---|---| | `plugins/autonomy/reference/role-topology.md` | Create | D1 five roles (capability-distribution, CI-orchestration, settings-as-code, org-policy, runner-execution [unborn; birth trigger = T4 build trigger]); D3 adapter split rule (handler logic → CI-orchestration home; enabling settings incl. admission policy → settings-as-code home); D5 composition stance (composes existing plugin seams — work-item queue/lease/dispatch, deterministic guardrail hooks, verification gates, session observability; near-duplicate skills banned). | -| `plugins/autonomy/reference/binding-seam.md` | Create | Binding SHAPE + resolution ladder: repo-local binding override → org binding at the org-policy home → setup interview. Must specify: (a) the org-policy-home pointer persists in repo-local/user-global config and is the prerequisite of rung 2; (b) fetch mechanism = the host CLI with the consumer's own auth; (c) no-org terminal default = repo-local binding + free-tier defaults; (d) written bindings carry a schema-version from v0; (e) known limitation: pointer staleness when an org moves its policy home. Layout convention stated as shape only — one contract doc per capability lands in `reference/` with its owning WP; no future-filename enumeration. | +| `plugins/autonomy/reference/binding-seam.md` | Create | Binding SHAPE + resolution ladder: repo-local binding override → org binding at the org-policy home → setup interview. Must specify: (a) the org-policy-home pointer persists in repo-local/user-global config and is the prerequisite of rung 2; (b) fetch mechanism = the host CLI with the consumer's own auth; (c) no-org terminal default = repo-local binding + free-tier defaults; (d) written bindings carry a `schema_version` field from v0; (e) known limitation: pointer staleness when an org moves its policy home. Layout convention stated as shape only — one contract doc per capability lands in `reference/` with its owning WP; no future-filename enumeration. | | `plugins/autonomy/reference/wiring-vs-advisor.md` | Create | D6 verbatim: WIRE when the target surface is machine-editable + local + reviewable, always landing as reviewable changes, never silent mutation; ADVISE (steps + cost surfaced) when org-external, entitlement-gated, paid, or GUI-only; paid anything = advisory + explicit opt-in first, regardless of wireability. | **Sanity Check:** @@ -122,11 +122,11 @@ path) live in SKILL.md/README, never in `reference/`. - `grep -ci 'reviewable' plugins/autonomy/reference/wiring-vs-advisor.md` ≥ 1 and `grep -ci 'opt-in' …` ≥ 1 - lychee lane passes (anchors valid; no dead cross-links) -### Phase 4: guided-setup v0 skill [TODO] +### Phase 4: guided-setup v0 skill [DONE] | File | Action | What changes | |---|---|---| -| `plugins/autonomy/skills/setup/SKILL.md` | Create | Setup contract (name `setup`, `disable-model-invocation: true`, `check` + `apply` actions, idempotent, non-interactive when complete arguments supplied). Scope: D7 discovery/interview of the adopting org's state (role homes present, substrate availability, budget posture); `apply` writes the discovered binding as tracked config `.claude/autonomy/` — **concern-named from day one** (concern = governed autonomous operation; a plugin split/rename leaves it valid; the versioned `docs/conventions/autonomy/` contract is deferred with trigger: second plugin consumes the config). Enumerated argument surface + pinned headless defaults (free tier everywhere per Brief cost constraint). Seam-2 obligations: `*.local.*` overlay, recommended `.gitignore` line (`.claude/autonomy/**/*.local.*`), user-global → project → local resolution, infer-and-persist convention ladder. Written binding carries `schema-version`. | +| `plugins/autonomy/skills/setup/SKILL.md` | Create | Setup contract (name `setup`, `disable-model-invocation: true`, `check` + `apply` actions, idempotent, non-interactive when complete arguments supplied). Scope: D7 discovery/interview of the adopting org's state (role homes present, substrate availability, budget posture); `apply` writes the discovered binding as tracked config `.claude/autonomy/` — **concern-named from day one** (concern = governed autonomous operation; a plugin split/rename leaves it valid; the versioned `docs/conventions/autonomy/` contract is deferred with trigger: second plugin consumes the config). Enumerated argument surface + pinned headless defaults (free tier everywhere per Brief cost constraint). Seam-2 obligations: `*.local.*` overlay, recommended `.gitignore` line (`.claude/autonomy/**/*.local.*`), user-global → project → local resolution, infer-and-persist convention ladder. Written binding carries `schema_version`. | | `plugins/autonomy/skills/setup/evals/evals.json` | Create | Warranted (setup precedent: `codebase-health/setup`). Cases: trigger/routing, discovery happy path, no-org terminal default, one non-interactive argument-supplied run, one refusal/guardrail (never assumes fleet shape). | **Sanity Check:** @@ -135,7 +135,7 @@ path) live in SKILL.md/README, never in `reference/`. - `claude plugin validate` exit 0 - `grep -c 'disable-model-invocation: true' plugins/autonomy/skills/setup/SKILL.md` = 1 -### Phase 5: Acceptance gates (in-repo) [TODO] +### Phase 5: Acceptance gates (in-repo) [DONE] | File | Action | What changes | |---|---|---| @@ -153,7 +153,7 @@ reviewed-and-accepted (first-party, MIT). **Sanity Check:** - All gate scripts exit 0 -- Scratch consumer repo contains `.claude/autonomy/` with a `schema-version` after the non-interactive run, with zero prompts issued +- Scratch consumer repo contains `.claude/autonomy/` with a `schema_version` field after the non-interactive run, with zero prompts issued - Security-review record present in the PR body ### Phase 6: Fleet binding dogfood — standards PR [TODO] diff --git a/plugins/autonomy/.claude-plugin/plugin.json b/plugins/autonomy/.claude-plugin/plugin.json new file mode 100644 index 000000000..ada396579 --- /dev/null +++ b/plugins/autonomy/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", + "name": "autonomy", + "version": "0.1.0", + "description": "Governed autonomous agent operation: role-topology, binding-seam, and wiring-vs-advisor contracts for climbing the AI-adoption ladder, plus a guided-setup skill that discovers an adopting org's state and writes its schema-versioned binding.", + "author": { + "name": "Melodic Software", + "email": "info@melodicsoftware.com" + }, + "license": "MIT", + "keywords": ["autonomy", "adoption", "governance", "topology", "binding", "guided-setup"] +} diff --git a/plugins/autonomy/README.md b/plugins/autonomy/README.md new file mode 100644 index 000000000..0f13af1de --- /dev/null +++ b/plugins/autonomy/README.md @@ -0,0 +1,44 @@ +# autonomy + +Governed autonomous agent operation. This plugin is the capability-distribution home for the +AI-adoption-ladder contract set: it ships the tool-agnostic contracts an adopting org binds to +its own repositories, tools, and policies, plus a guided-setup skill that discovers the org's +state and records that binding. + +## Shipped capability (0.1.0) + +- **Topology contracts** (`reference/`): role topology for the repositories an adoption spans, + the binding-seam shape that maps contract roles to an org's real instances, and the + wiring-vs-advisor principle governing how setup lands changes. +- **Guided setup** (`/autonomy:setup`): discovery-first interview of the adopting org's state — + role homes, substrate availability, budget posture — writing a schema-versioned binding under + `.claude/autonomy/` as reviewable changes. Never assumes any particular org or repo shape. + +## Roadmap (deferred, trigger-gated) + +Each capability below lands with its own work package; none ships before its contracts are +locked (no step-skipping — trust before scale). + +| Capability | Trigger | +|---|---| +| Telemetry contract + setup slice | Telemetry work package build lands. | +| Return-accounting convention + capture slice | Return-accounting work package build lands. | +| Trigger/dispatch adapters | Trigger-layer work package build lands. | +| Guardrail matrix + sandbox-ladder binding | Guardrails work package build lands. | +| Standing-routine catalog + v1 definitions | Routines work package build lands. | +| Runner charter execution pack | The runner build trigger fires (charter's own conditions). | + +## Trigger register (plugin-scoped) + +| Trigger | Action | +|---|---| +| Role vocabulary changes in `reference/role-topology.md` | Update the org-policy home's binding instance doc to the new vocabulary version. | +| A second plugin consumes `.claude/autonomy/` config | Graduate the binding schema to a versioned concern contract per the marketplace's concern-named-folder convention. | + +## Configuration + +Setup writes tracked config to `.claude/autonomy/` in the consuming repo (concern-named — the +config outlives any plugin restructure). Personal overlays follow the marketplace overlay +convention: `.claude/autonomy/**/*.local.*` stays gitignored; layers resolve per the +binding-seam ladder — user-global → org binding (when pointed) → project → local overlay — +additively. diff --git a/plugins/autonomy/reference/binding-seam.md b/plugins/autonomy/reference/binding-seam.md new file mode 100644 index 000000000..e07439e90 --- /dev/null +++ b/plugins/autonomy/reference/binding-seam.md @@ -0,0 +1,53 @@ +# Binding seam + +Normative contract for how an adopting org maps the roles in `role-topology.md` (and every +per-capability contract that follows) to its real instances — repositories, trackers, tools, +policies. The contract defines the SHAPE of a binding; every concrete value is org-supplied. + +## Binding shape + +A binding is a written, schema-versioned record mapping contract vocabulary to org instances: +each topology role to a repository, each capability's seam to the org's chosen instance, plus +the org's declared postures (budget, substrate availability). Bindings carry a +`schema_version` field from v0 — consumers read the version before the body, and schema +changes are reviewed migrations. A role MAY be bound to null: an unborn role (one whose birth +trigger has not fired) or a role the org has no instance for stays explicitly unbound — +consumers treat null as absent capability and never invent an instance. + +The org-policy-home pointer is a repository locator that MAY carry an explicit document path +(`#`). Absent a path, the consumer discovers the binding instance +document at bind time (by its schema-versioned binding shape, per the org repo's own layout) +and PERSISTS the resolved path alongside the pointer so later fetches are deterministic. + +## Resolution ladder + +A consumer resolves the effective binding as an ADDITIVE layer merge — a later layer adds to +or refines earlier layers per value, never wholesale replacement: + +1. **User-global base** — the consumer's own machine-level binding config. +2. **Org binding at the org-policy home** — the org's binding instance document. Reaching this + layer requires an org-policy-home pointer, which persists in repo-local or user-global + config; the fetch mechanism is the hosting platform's own CLI with the consumer's own + authentication (the contract grants no credentials). +3. **Repo-local binding** — tracked config in the consuming repository (the concrete location + is a tool-specific detail the setup capability documents). Per value, this layer overrides + the org binding. +4. **Local overlay** — the consumer's untracked personal refinement of the repo-local layer. + +A value no layer answers falls to the **setup interview**, which asks and persists the answer +into the repo-local layer so the next resolution is deterministic. + +Terminal default when no org exists (solo adopter, no org-policy home): the merge degenerates +to the local layers, populated with free-tier defaults — zero paid dependencies. + +## Known limitation + +The org-policy-home pointer can go stale when an org moves its policy home. A consumer that +fails to fetch the org layer surfaces that failure (warned as not-considered) and falls to the +remaining layers or the interview rather than silently reusing a cached org binding; setup +re-records the pointer. + +## Layout convention + +Each capability this plugin ships lands exactly one contract document in `reference/` with its +owning work package. The convention states shape only — it enumerates no future filenames. diff --git a/plugins/autonomy/reference/role-topology.md b/plugins/autonomy/reference/role-topology.md new file mode 100644 index 000000000..61bf05704 --- /dev/null +++ b/plugins/autonomy/reference/role-topology.md @@ -0,0 +1,38 @@ +# Role topology + +Normative contract for the repository roles an autonomy adoption spans. Contract text — here +and in every sibling contract document — names roles only; the mapping from each role to an +adopting org's real repository lives in that org's binding instance document (see +`binding-seam.md`). + +## Roles + +| Role | Owns | +|---|---| +| capability-distribution home | The distributable capabilities and their contract documents — this plugin's own home. | +| CI-orchestration home | Reusable pipeline execution logic: event handlers, emission steps, verification lanes. | +| settings-as-code home | Declarative platform settings: labels, permissions, runner-policy admission, repository configuration. | +| org-policy home | Org-wide policy and conventions, including the org's binding instance document. | +| runner-execution home | The autonomous-runner execution substrate. **Unborn**: this role is created only when the runner charter's build trigger fires; until then no repository holds it. | + +## Adapter split rule + +A signal adapter (an event or schedule that starts governed autonomous work) splits by role: + +- **Handler logic** — the executable steps a pipeline runs — lands in the CI-orchestration home. +- **Enabling settings** — labels, permissions, admission policy that let the handler fire — + land in the settings-as-code home. Admission-policy changes are reviewed contract changes, + never silent edits. + +## Composition stance + +This plugin composes existing capability seams rather than duplicating them: + +- the work-item queue, lease, and dispatch seam; +- deterministic guardrail hooks; +- verification gates; +- session observability. + +A capability here that near-duplicates a capability an adopting deployment already has is a +defect: compose the seam, or route a change to the seam's own home. Orchestration that +composes existing capabilities is the sanctioned model. diff --git a/plugins/autonomy/reference/wiring-vs-advisor.md b/plugins/autonomy/reference/wiring-vs-advisor.md new file mode 100644 index 000000000..1b2c583fb --- /dev/null +++ b/plugins/autonomy/reference/wiring-vs-advisor.md @@ -0,0 +1,21 @@ +# Wiring vs advisor + +Normative principle governing how guided setup (and every capability slice that extends it) +lands change in an adopting org. + +## WIRE + +Setup WIRES a target — writes the change itself — when the surface is machine-editable, local, +and reviewable: repository files, settings files, pipeline definitions, infrastructure code. +Wiring always lands as reviewable changes; silent mutation of any surface is a defect. + +## ADVISE + +Setup ADVISES — emits the steps and surfaces the cost, but does not write — when the surface +is org-external, entitlement-gated, paid, or GUI-only. + +## Paid is always advisory first + +Anything that costs money is advisory + explicit opt-in first, regardless of wireability. The +cost is surfaced before the opt-in question is asked; a declined opt-in falls back to the free +default, never to silence. diff --git a/plugins/autonomy/skills/setup/SKILL.md b/plugins/autonomy/skills/setup/SKILL.md new file mode 100644 index 000000000..52ee19d12 --- /dev/null +++ b/plugins/autonomy/skills/setup/SKILL.md @@ -0,0 +1,91 @@ +--- +name: setup +description: "Configure the autonomy plugin for this repository: discover the adopting org's state (role homes, substrate availability, budget posture), interview where discovery cannot infer, and write the schema-versioned binding under .claude/autonomy/. Use when: 'set up autonomy', 'autonomy setup', 'configure autonomy', 'bind the autonomy contracts', or another autonomy capability reports a missing binding. Re-runnable — safe to invoke again to reconfigure." +argument-hint: "check | apply [--org-policy-home |none] [--budget-posture free|paid-opt-in]" +user-invocable: true +disable-model-invocation: true +--- + +## Purpose + +Discovery phase of autonomy adoption (v0). Maps the roles in +[`${CLAUDE_PLUGIN_ROOT}/reference/role-topology.md`](${CLAUDE_PLUGIN_ROOT}/reference/role-topology.md) to this org's real instances +and records the result as the schema-versioned binding the resolution ladder in +[`${CLAUDE_PLUGIN_ROOT}/reference/binding-seam.md`](${CLAUDE_PLUGIN_ROOT}/reference/binding-seam.md) reads at the repo-local layer. Never assumes +any org, repo, tracker, or fleet shape — discovery reads what exists, the interview fills what +it cannot infer, and every landed change is reviewable per +[`${CLAUDE_PLUGIN_ROOT}/reference/wiring-vs-advisor.md`](${CLAUDE_PLUGIN_ROOT}/reference/wiring-vs-advisor.md). + +## Actions + +- **`check`** (read-only): resolve the effective binding across ALL rungs of the binding-seam + resolution ladder — user-global (`~/.claude/autonomy/`) → project (`.claude/autonomy/`) → + local overlay (`.claude/autonomy/**/*.local.*`), additive, PLUS the org rung when the merged + layers carry an `org_policy_home` pointer: fetch the org binding via the host CLI with the + consumer's own auth and fold it in at its ladder position. Report what is bound, what is + missing, and which layer or rung contributes each value; an unreachable org-policy home is + WARNED as not-considered, never silently omitted. No writes. +- **`apply`** (idempotent): run discovery, then write or update the project binding. Re-running + reads the existing binding and proposes deltas; it never overwrites blind and never touches + unrelated user content. All project paths anchor at the PROJECT ROOT — resolve + `${CLAUDE_PROJECT_DIR}` (fall back to the repository toplevel) before writing; invoking the + skill from a subdirectory must never create a nested `.claude/autonomy/`. + +## Argument surface (enumerated) + +| Argument | Values | Headless default | +|---|---|---| +| action | `check` \| `apply` | — (required) | +| `--org-policy-home` | repository locator, optionally `#` to the binding document \| `none` | `none` | +| `--budget-posture` | `free` \| `paid-opt-in` | `free` | + +A locator without `#` triggers document discovery at bind time (the binding instance +document is found by its schema-versioned shape per the org repo's own layout) and the +resolved path is persisted alongside the pointer so later fetches are deterministic. + +`apply` with every argument supplied runs non-interactively — no prompts — so automation and +headless use work. With arguments missing, discovery infers first and interviews only the +gaps (convention ladder: config present → use it; absent → infer and persist; cannot infer → +ask and offer to persist; otherwise → safe free-tier default). + +## Discovery (apply) + +1. **Role homes**: inspect the repository and, when a host CLI with the consumer's own auth is + available, the org — which repositories hold the CI-orchestration, settings-as-code, and + org-policy roles. A solo/no-org adopter terminates at the binding-seam contract's terminal + default: the repo-local binding is the whole binding, free-tier defaults throughout. +2. **Substrate availability**: what execution surfaces exist (local machine, CI runners, + self-run infrastructure) — recorded as declared posture, not probed destructively. +3. **Budget posture**: `free` unless the user explicitly opts into `paid-opt-in`; anything + paid is advisory + explicit opt-in with cost surfaced first (wiring-vs-advisor). + +## Written binding + +`apply` writes `.claude/autonomy/binding.json` with these serialized keys: + +- `schema_version` (string, from `"1.0"`); +- `roles` — an object keyed by the kebab-case role names of the role-topology contract + (`capability-distribution-home`, `ci-orchestration-home`, `settings-as-code-home`, + `org-policy-home`, `runner-execution-home`); a value MAY be null (unborn role, or no org + instance — never invented); +- `org_policy_home` — the pointer (or `null`), with its resolved document path when + discovered; +- `budget_posture` — `free` | `paid-opt-in`; +- `substrate` — an object with kebab-case surface keys (`local-machine`, `ci-runners`, + `self-run-infrastructure`), boolean values. + +Capability slices add their own sections under their slice name. The same file name is the +shape at EVERY layer: the user-global layer is `~/.claude/autonomy/binding.json`, the project +layer `.claude/autonomy/binding.json`, and each layer's personal overlay +`binding.local.json` beside it. The project file is tracked (team-shared); recommend the +consumer `.gitignore` line: `.claude/autonomy/**/*.local.*`. Layers resolve per the +binding-seam ladder — user-global → org binding (when pointed) → project → local overlay — +additively. + +## What this skill does NOT do + +- Wire any capability slice (telemetry, capture, adapters) — those land with their own work + packages and extend this skill when they ship. +- Mutate platform settings, user settings, or `pluginConfigs`. +- Assume the shape of any particular org or fleet — a run against an unknown repo asks or + defaults; it never guesses silently. diff --git a/plugins/autonomy/skills/setup/evals/evals.json b/plugins/autonomy/skills/setup/evals/evals.json new file mode 100644 index 000000000..26aed554f --- /dev/null +++ b/plugins/autonomy/skills/setup/evals/evals.json @@ -0,0 +1,70 @@ +{ + "skill_name": "setup", + "evals": [ + { + "id": 1, + "name": "routes-on-setup-trigger-and-runs-discovery-first", + "prompt": "/autonomy:setup apply", + "expected_output": "Runs the discovery phase before asking anything: inspects the repository (and the org where a host CLI with the consumer's own auth is available) for role homes, substrate availability, and budget signals, summarizes what it inferred, then interviews only the gaps discovery could not fill, one decision at a time with a recommendation. It does not start by asking questions discovery could have answered.", + "files": [], + "expectations": [ + "Discovery (repo/org inspection) runs before any interview question", + "Interviews only the gaps discovery could not infer, one at a time with recommendations", + "Summarizes inferred values transparently before writing", + "Does not prompt for values it could infer from the repository" + ] + }, + { + "id": 2, + "name": "no-org-terminal-default-free-tier", + "prompt": "/autonomy:setup apply --org-policy-home none", + "expected_output": "Applies the binding-seam terminal default: the repo-local binding is the whole binding, org_policy_home is null, budget_posture is free, and no paid dependency is introduced anywhere. The written binding carries schema_version and is tracked project config.", + "files": [], + "expectations": [ + "org_policy_home recorded as null (no invented org)", + "budget_posture defaults to free with zero paid dependencies", + "Written binding carries schema_version", + "Binding written as tracked project config, not machine-local state" + ] + }, + { + "id": 3, + "name": "non-interactive-when-arguments-complete", + "prompt": "/autonomy:setup apply --org-policy-home none --budget-posture free", + "expected_output": "Completes without issuing a single prompt: every argument is supplied, so discovery fills the rest from repo signals and pinned headless defaults, and the schema-versioned .claude/autonomy/binding.json is written (or idempotently updated) with a transparent summary of what was inferred, changed, or skipped.", + "files": [], + "expectations": [ + "Zero interactive prompts issued", + "Writes or idempotently updates .claude/autonomy/binding.json with schema_version", + "Reports what was inferred, changed, and skipped", + "Re-running produces no spurious diff (idempotent)" + ] + }, + { + "id": 4, + "name": "check-is-read-only-across-layers", + "prompt": "/autonomy:setup check", + "expected_output": "Resolves the effective binding additively across every rung of the ladder: user-global, project, and local overlay layers, PLUS the org layer when the merged layers carry an org_policy_home pointer (fetched via the host CLI with the consumer's own auth). Reports which layer or rung contributes each value and what is missing, warns when a layer or the org-policy home could not be read (not-considered, never silently omitted), and performs no writes of any kind.", + "files": [], + "expectations": [ + "No file writes in check mode", + "Reports per-layer contributions (user-global, org rung when pointed, project, local overlay)", + "Fetches and folds in the org binding when an org_policy_home pointer exists", + "Warns when a layer or the org-policy home exists but could not be read, never silently omitting it" + ] + }, + { + "id": 5, + "name": "never-assumes-fleet-or-org-shape", + "prompt": "/autonomy:setup apply\n\nThis repo belongs to an org you have never seen; assume our CI lives in the usual place and just write the binding.", + "expected_output": "Refuses to guess: 'the usual place' is not a discoverable fact, so it inspects what actually exists, and for any role home it cannot infer it asks (or, non-interactively, records the value as unbound) rather than writing an assumed repository name into the binding. No fleet, vendor, or org shape is ever invented.", + "files": [], + "expectations": [ + "Does not write an assumed/invented repository name into the binding", + "Asks or records unbound for role homes it cannot infer", + "Grounds every bound value in an inspected signal or an explicit user answer", + "Never treats any particular org/fleet layout as 'the usual place'" + ] + } + ] +} diff --git a/scripts/generate-catalog.mjs b/scripts/generate-catalog.mjs index 875fea528..6e49a0f85 100755 --- a/scripts/generate-catalog.mjs +++ b/scripts/generate-catalog.mjs @@ -30,6 +30,7 @@ const CATEGORY_ORDER = [ "maintenance", "deployment", "claude-code", + "autonomy", "security", "workflow", "project-management", diff --git a/scripts/validate-plugin-contracts.mjs b/scripts/validate-plugin-contracts.mjs index 414e53581..81f7bc3dc 100755 --- a/scripts/validate-plugin-contracts.mjs +++ b/scripts/validate-plugin-contracts.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; -import { join, relative } from "node:path"; +import { join, relative, sep } from "node:path"; import process from "node:process"; const root = process.cwd(); @@ -198,6 +198,33 @@ if (existsSync(aiBriefingBrandOverlay)) { } } +// The autonomy plugin's contract text is tool- and fleet-agnostic: the org +// token and bare fleet repo names may not appear anywhere under it. Author +// metadata in plugin.json is the single allowed occurrence. The normative +// reference/ docs additionally ban vendor names outright — surface classes +// replace them; tool-specific detail lives in SKILL.md/README. +const autonomyRoot = join(pluginRoot, "autonomy"); +if (existsSync(autonomyRoot)) { + const fleetTokens = /melodic-software|ci-workflows|github-iac/i; + const vendorTokens = /github|gitlab|bitbucket|slack|anthropic|claude|openai|copilot|cursor|devin/i; + const autonomyReference = join(autonomyRoot, "reference") + sep; + for (const path of filesUnder(autonomyRoot)) { + let content = read(path); + if (path.endsWith(`${sep}.claude-plugin${sep}plugin.json`)) { + // Only the author block is exempt — description/keywords/etc. stay gated. + const manifest = JSON.parse(content); + delete manifest.author; + content = JSON.stringify(manifest); + } + if (fleetTokens.test(content)) { + fail(path, "autonomy plugin must not name the org or fleet repos (binding-seam owns instances)"); + } + if (path.startsWith(autonomyReference) && vendorTokens.test(content)) { + fail(path, "autonomy reference/ contracts must use surface classes, never vendor names"); + } + } +} + if (failures.length > 0) { console.error("Plugin contract validation failed:"); for (const failure of failures) console.error(`- ${failure}`);