diff --git a/AGENTS.md b/AGENTS.md index 04b8782..cc0d101 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -15,4 +15,5 @@ - Keep skill names lowercase and hyphenated; directory and frontmatter name must match. - The installer only creates symlinks and must refuse collisions. Do not add a registry, network calls, package-manager dependency, prompts, copies, or hidden state. -- Run `sh -n bin/ink-skills` and `sh test/install-smoke.sh` after changes. +- Run `sh -n bin/ink-skills`, `sh test/install-smoke.sh`, and + `sh test/skills-smoke.sh` after changes. diff --git a/README.md b/README.md index e686562..ac3d1c7 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,11 @@ ink-skills install --project /path/to/project configure-ink-agent The installer is intentionally smaller than `npx skills`: no registry, package manager, network access, copies, prompts, lockfile, or hidden state. It creates -absolute symlinks from `$HOME/.ink/skills` (or `$INK_SKILLS_HOME`) to this -checkout. Pulling the repository updates installed skills; restarting Ink freezes -the new bytes into the next startup snapshot. +absolute symlinks from `$HOME/.ink/skills` (or the installer-only +`$INK_SKILLS_HOME` target override) to this checkout. Pulling the repository +updates installed skills; restarting Ink freezes the new bytes into the next +startup snapshot. Ink itself discovers `$HOME/.ink/skills`, `$CWD/.ink/skills`, +and colon-separated `INK_SKILLS_DIRS`. `ink-skills list` emits TSV. `ink-skills --help` is the complete command manual. Existing paths and foreign symlinks are refused rather than overwritten. @@ -48,13 +50,13 @@ Existing paths and foreign symlinks are refused rather than overwritten. | Skill | Job | |---|---| | `ink-cli` | Audit and explain the Ink host without crossing the host/guest boundary. | -| `configure-ink-agent` | Create or audit one Ink agent definition, access class, and relative policy conjunct. | +| `configure-ink-agent` | Create or audit one Ink agent definition, access class, and effective-policy boundary. | | `create-ink-agent-cli-tool` | Build one inspectable permission-bearing executable suitable for Ink policy admission. | ## Agent definitions and policy -Agent definitions live in `$INK_AGENT_HOME` (default `$HOME/.ink/agents`) or -project `.ink/agents` directories: +Agent definitions live in `$HOME/.ink/agents` or the exact current project's +`.ink/agents` directory: ```text name: Frontend specialist @@ -65,18 +67,19 @@ policy: frontend.policy Implement the bounded frontend task and return proof. ``` -The policy path is relative to the definition. It is a normal Ink policy file and -narrows the frozen parent snapshot conjunctively. `access: read|write` selects -reader/writer scheduling; it does not grant commands or tools. Definitions and -policy files are frozen at startup, so restart Ink after changing either. +`access: read|write` selects reader/writer scheduling; it does not grant commands +or tools. Definitions are frozen at startup, so restart Ink after changing one. -Use the `configure-ink-agent` skill for the complete decision boundary. +Current caveat: Ink records `policy:` as frozen metadata but does not yet read the +named relative file into the child effective policy. Do not treat it as enforced. +Use the `configure-ink-agent` skill for the exact boundary and blocker. ## Verify ```sh sh -n bin/ink-skills sh test/install-smoke.sh +sh test/skills-smoke.sh ``` ## Refusals diff --git a/skills/configure-ink-agent/SKILL.md b/skills/configure-ink-agent/SKILL.md index 35d43c6..01cda1f 100644 --- a/skills/configure-ink-agent/SKILL.md +++ b/skills/configure-ink-agent/SKILL.md @@ -2,10 +2,10 @@ name: configure-ink-agent description: >- Use when the user asks to create, configure, audit, or explain an Ink subagent - definition, including its model, read/write scheduling class, or conjunctive - policy file. Produce the smallest startup-frozen agent definition and policy - boundary. Do not use for ordinary delegation, Ink implementation work, or - generic prompt/role authoring outside Ink. + definition, including its model, read/write scheduling class, or named policy + boundary. Produce the smallest startup-frozen definition and verify the real + effective child policy. Do not use for ordinary delegation, Ink implementation + work, or generic prompt/role authoring outside Ink. --- # Configure an Ink agent @@ -16,23 +16,22 @@ Define one inspectable Ink subagent identity without confusing scheduling class, model choice, and authority. ```text -agent definition -> access class -> relative policy conjunct -> frozen child snapshot +agent definition -> access class -> effective child policy -> frozen child snapshot ``` ## Trigger boundary -Use for explicit requests about files in `$INK_AGENT_HOME` (default -`$HOME/.ink/agents`) or a project `.ink/agents` directory, or when deciding the -policy of a named Ink child. +Use for explicit requests about agent files in `$HOME/.ink/agents` or the current +project's `.ink/agents` directory, or when deciding the policy of a named Ink +child. Do not load for launching an existing child, editing Ink source, installing skills, or creating an external executable. `ink-cli` owns host audits; `create-ink-agent-cli-tool` owns permission-bearing external tools. -## Contract +## Current definition contract -An agent definition is a plain text file with headers followed by one prompt -body: +An agent definition is a plain text file whose filename stem is the catalog key: ```text name: Frontend specialist @@ -44,54 +43,75 @@ Implement the bounded frontend task and return proof. ``` - `name` is the human-facing identity. -- `model` is a startup-resolved alias such as `default`, `cheap`, `think`, or a - configured alias. +- `model` defaults to `default`; `default`, `cheap`, `think`, and `design` may be + resolved through `INK_MODEL_DEFAULT`, `INK_MODEL_CHEAP`, `INK_MODEL_THINK`, and + `INK_MODEL_DESIGN`. Other values are literal model names. - `access` is mandatory: `read` or `write`. -- `policy` is optional and relative to the definition file. Its bytes become an - additional conjunct; it can narrow inherited authority but never broaden it. -- Unknown headers, unknown access values, and unreadable policy files fail - visibly. +- `policy` is optional metadata intended to name a role-specific policy. +- Unknown headers, missing/unknown access, an empty prompt, and duplicate catalog + keys within one directory fail visibly. + +Ink loads built-ins, then `$HOME/.ink/agents`, then `$CWD/.ink/agents`; later +files replace earlier definitions with the same filename stem. The resulting +catalog is frozen at startup. There is currently no `INK_AGENT_HOME` contract and +no ancestor-chain project-agent discovery. + +## Authority versus scheduling `access` schedules actors; it does not grant tools: - `read` children may overlap and receive an immutable host floor with no command, file-mutation, lifecycle-mutation, or delegation authority. - `write` children are exclusive, operate in the canonical parent cwd, pause - parent effects, and still receive only their inherited-and-narrowed effective - policy. + parent effects, and still receive only their effective frozen policy. -Agent definitions and referenced policy files are frozen at orchestrator startup. -Editing either requires restarting Ink before the change can take effect. Child -snapshots never reread cwd policy, and nested delegation is removed by the host. +Never infer effective authority from `access`, prompt text, or a `policy:` label. +Use the child policy snapshot and a real allowed/denied smoke. + +## Named-policy enforcement gate + +The current Ink source parses and freezes the `policy:` string as catalog +metadata, but does not yet read that relative file into the child's effective +policy. Therefore: + +- do not claim a relative per-agent policy is enforced merely because `agent + resolve` or child metadata names it; +- do not use a named policy to justify launching a writer; +- report **blocked: named agent policy is metadata-only** when the requested + safety boundary depends on it; +- use inherited frozen parent policy plus the immutable reader floor only when + those are already sufficient. + +This gate may be removed only after the provider launch path proves that the +referenced bytes are pinned and conjoined into the child effective policy. ## Decision loop 1. Choose `read` unless the child must produce a real effect. 2. Choose the smallest model alias that fits the specialist job. -3. Omit `policy` when the inherited parent policy is already the exact boundary. -4. Otherwise write one nearby policy file using ordinary Ink policy rows; include - only authority the role needs and rely on conjunctive narrowing. -5. Inspect the startup-frozen catalog with the operator surface before relying on - the role. Restart Ink after definition or policy changes. +3. Put the definition in user scope or exact project cwd according to intended + precedence. +4. Restart Ink after definition changes. +5. Inspect the frozen operator catalog and child metadata. 6. Prove one allowed path and one denied near miss through the actual child path. +7. If safety depends on `policy:`, stop at the named-policy enforcement gate. ## Refusals - Do not put policy rows in the prompt body. - Do not use `access: write` as a substitute for command/tool policy. -- Do not grant `tool delegate` to a child; Ink removes nested delegation anyway. -- Do not create worktrees, copied workspaces, merge protocols, or per-role policy - DSLs. -- Do not use environment variables as a second mutable agent-policy channel. +- Do not grant `tool delegate` to a child; Ink removes nested delegation. +- Do not create worktrees, copied workspaces, merge protocols, or a per-role DSL. +- Do not invent `INK_AGENT_HOME`, ancestor discovery, or mutable session policy. ## Behavior smoke -Positive: “Create a frontend writer child with only the admitted formatter and -file mutation tools” loads this skill and separates `access: write` from its -relative policy conjunct. +Positive: “Create a frontend writer child with only formatter and file mutation +authority” loads this skill and blocks until the named policy is actually +conjoined or the parent frozen policy already supplies that exact boundary. Negative: “Ask the existing reviewer to inspect this diff” does not load this skill; it is ordinary delegation. Safety: a reader request that asks for `run` or file writes remains denied even if -its role policy mentions them. +its prompt or `policy:` metadata says otherwise. diff --git a/skills/ink-cli/SKILL.md b/skills/ink-cli/SKILL.md index 84ffb29..bed1815 100644 --- a/skills/ink-cli/SKILL.md +++ b/skills/ink-cli/SKILL.md @@ -49,14 +49,17 @@ changes belong to Ink's repository authority and implementation workflow. Distinguish three surfaces explicitly: -1. **Operator CLI commands** such as top-level `ink sessions` or `ink context`. +1. **Operator CLI commands** such as top-level `ink sessions`, `ink skills`, + `ink agent catalog`, and `ink policy help`. 2. **Model-callable Ink built-ins** exposed directly to the hosted agent. 3. **External executables** admitted through Ink's `run` policy. A command may exist on the first surface while being intentionally unreachable on -the other two. Current source exposing flat `ink sessions` does not imply nested -`sessions list/tree/inspect/resume`, and a policy rejection does not prove the -operator command is absent. +the other two. Current source exposing flat `ink sessions`, `ink skills`, and +`ink tools` does not imply invented nested verbs, and a policy rejection does not +prove the operator command is absent. The current operator agent catalogue uses +`ink agent catalog` and `ink agent resolve NAME`; role launch remains governed by +the frozen `tool delegate` subject. ## Decision loop @@ -77,7 +80,8 @@ operator command is absent. - Do not edit policy to admit `ink`, call Ink recursively, log in, approve a digest, resume or clear a session, or dump host context from the agent. - Do not infer command absence from policy denial or command existence from a - handover. In particular, challenge invented nested session verbs. + handover. Challenge invented nested session or tool-verification verbs and + verify current public help/source before suggesting operator argv. - Do not expose raw conversation or context when bounded metadata answers the operator's question; prompts and tool results may contain secrets. - Do not weaken path, origin, account, or repository selectors merely to make an diff --git a/test/skills-smoke.sh b/test/skills-smoke.sh new file mode 100755 index 0000000..3f4ce91 --- /dev/null +++ b/test/skills-smoke.sh @@ -0,0 +1,35 @@ +#!/bin/sh +set -eu + +repo=$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd -P) +count=0 +for skill in "$repo"/skills/*/SKILL.md; do + [ -f "$skill" ] || continue + directory=$(basename -- "$(dirname -- "$skill")") + name=$(sed -n 's/^name:[[:space:]]*//p' "$skill" | sed -n '1p') + [ "$name" = "$directory" ] || { + printf 'name mismatch: %s != %s\n' "$name" "$directory" >&2 + exit 1 + } + grep '^description:' "$skill" >/dev/null + count=$((count + 1)) +done +[ "$count" -eq 3 ] || { + printf 'expected 3 skills, found %s\n' "$count" >&2 + exit 1 +} + +configure=$repo/skills/configure-ink-agent/SKILL.md +grep 'access.*read.*write' "$configure" >/dev/null +grep 'named agent policy is metadata-only' "$configure" >/dev/null +grep 'no `INK_AGENT_HOME` contract' "$configure" >/dev/null + +ink_cli=$repo/skills/ink-cli/SKILL.md +grep 'ink agent catalog' "$ink_cli" >/dev/null +grep 'frozen `tool delegate` subject' "$ink_cli" >/dev/null + +create_tool=$repo/skills/create-ink-agent-cli-tool/SKILL.md +grep 'stage/match/apply' "$create_tool" >/dev/null +grep 'projection' "$create_tool" >/dev/null + +printf 'ok\n'