321 lines
16 KiB
Markdown
321 lines
16 KiB
Markdown
---
|
|
name: create-ink-tool
|
|
description: >-
|
|
Use when creating, implementing, splitting, or reviewing a compiled,
|
|
permission-bearing executable intended for admission through Ink policy.
|
|
Produce one inspectable executable per authority boundary with discoverable
|
|
help/contract and falsifiable safety proof. Do not use for Ink's own commands,
|
|
ordinary or one-off CLIs/scripts, or skill authoring.
|
|
---
|
|
|
|
# Create an Ink tool
|
|
|
|
## One job
|
|
|
|
Design a **permission-sized executable** whose name and argv expose its reachable
|
|
reads and mutations so Ink can discover, digest-pin, and grant it without granting a platform.
|
|
One executable need not mean one source file: share private build-time modules
|
|
when that does not widen runtime authority.
|
|
|
|
## Inspect and decide
|
|
|
|
Read only what can change the boundary:
|
|
|
|
- the exact job and every reachable mutation;
|
|
- Ink's current specification, policy grammar, and mutation protocol;
|
|
- neighboring tools and existing executables that may already satisfy the job;
|
|
- resource, credential, selector, target, packaging, and proof contracts;
|
|
- repository specification and tests.
|
|
|
|
Project authority outranks this skill. Missing selector semantics, credentials,
|
|
recovery rules, or external contracts are blockers—not adapter opportunities.
|
|
|
|
```text
|
|
JOB -> REUSE? -> READS/MUTATIONS -> BOUNDARY -> CONTRACT -> ARTIFACT -> PROOF
|
|
```
|
|
|
|
1. Reuse a directly inspectable executable only when granting its whole reachable
|
|
surface is honest; otherwise build the coherent missing boundary, not a wrapper.
|
|
2. Enumerate reads, mutations, network calls, secrets, state, children, and
|
|
config/plugin discovery; split where approval, blast radius, or recovery differ.
|
|
3. Define argv, selectors, streams, errors, dependencies, exhaustive versus
|
|
bounded output, and any tool-owned semantic presentation without recreating
|
|
shell grammar or a host-wide mutation ontology.
|
|
4. State the runtime artifact honestly, then falsify its boundary, behavior,
|
|
presentation, and portability claims through Ink's real run entry point.
|
|
|
|
## Permission boundary
|
|
|
|
A digest proves **which executable** runs; it does not narrow that executable's
|
|
accepted argv, config, plugins, or helpers. Reuse is correct only when granting
|
|
the entire reachable surface is honest.
|
|
|
|
Split executables when approval, blast radius, credentials, selectors, drift, or
|
|
recovery differ. In particular:
|
|
|
|
- read and mutation are separate executables even when they share transport;
|
|
- upload and deletion split when recovery or approval differs;
|
|
- a generic request, cloud-admin, SDK, MCP, or `call NAME JSON` dispatcher is not
|
|
a permission boundary;
|
|
- one executable per endpoint is needless fragmentation;
|
|
- several read verbs may share one executable only when resource family,
|
|
credential scope, selectors, trust, and output contract are the same.
|
|
|
|
A script/dispatcher is the authority of its interpreter and every reachable
|
|
helper unless the host pins and revalidates all of them. Resolve first-party
|
|
helpers beside the script, constrain ambient lookup, and inventory dependencies.
|
|
If the whole boundary is too broad, admit the narrower child executable instead.
|
|
|
|
## Readers and selectors
|
|
|
|
A read executable must be structurally unable to mutate through any accepted
|
|
flag, subcommand, config, plugin, callback, helper, credential refresh, cache,
|
|
history, or telemetry path. For HTTP this normally means fixed GET/HEAD behavior,
|
|
not an arbitrary method flag. “Dry run” does not turn a mutator into a reader.
|
|
|
|
Use executable-level policy when every accepted invocation is one honest grant.
|
|
Add semantic selectors only for variable resources users may reasonably grant
|
|
differently, such as canonical path/root prefix, origin, repository, or account.
|
|
|
|
- Canonicalize selector values before access and matching.
|
|
- Prove an adjacent no-match causes no resource access.
|
|
- Do not expose invariant facts such as `action=read` or `method=GET`; the
|
|
executable already says that.
|
|
- Stdin-only transforms normally need no selectors because they acquire only
|
|
supplied input.
|
|
- A pattern is a selector only when two patterns are genuinely different grants,
|
|
not merely different queries below the same approved root.
|
|
|
|
Share read/mutation parsing or transport only through private build-time modules.
|
|
|
|
## Patterns and search
|
|
|
|
Shell globs, path filters, and content patterns are different contracts.
|
|
|
|
- Never reinterpret argv as shell syntax or perform ambient shell expansion.
|
|
- Accept a glob, fixed string, regex, or domain expression only through an
|
|
explicit mode such as `--include-glob`, `--fixed`, or `--ere`; never infer the
|
|
mode from punctuation.
|
|
- Keep matching as a separate stdin filter when all candidate bytes or records
|
|
have already been supplied.
|
|
- Put matching inside a resource reader only when it avoids opening,
|
|
transferring, traversing, or disclosing irrelevant resources inside that same
|
|
reader boundary. Canonical root/path selectors remain the authority boundary.
|
|
- Define one small established grammar. State root-relative versus basename
|
|
matching, separators, case/dotfile/symlink behavior, pattern combination,
|
|
include/exclude precedence, and invalid-pattern failure. Validate before access
|
|
where possible.
|
|
|
|
`find`/`fd` forms that execute or delete and `rg` forms that launch preprocessors
|
|
are not read boundaries merely because one intended invocation only searches. A
|
|
narrow native search tool is justified when it removes reachable mutations, adds
|
|
canonical path selectors, or supplies the required static portable artifact.
|
|
Implement the coherent missing subset, not a compatibility facade or renamed
|
|
wrapper.
|
|
|
|
## Mutators
|
|
|
|
Ink mutators use its elected semantic protocol:
|
|
|
|
```text
|
|
tool stage <operation argv...> # pure; emit pinned manifest
|
|
tool match <manifest> <selectors> # deterministic policy decision
|
|
tool apply <id> <hash> # revalidate; perform once
|
|
```
|
|
|
|
Required properties:
|
|
|
|
- `stage` performs no mutation and resolves no secrets;
|
|
- the manifest pins executable identity, canonical mutation, authority subject,
|
|
non-secret inputs, and drift-sensitive hashes;
|
|
- selectors are variable semantic facts, never argv prefixes, shell text, or
|
|
executable invariants;
|
|
- `apply` revalidates executable, manifest, policy snapshot, inputs, and drift;
|
|
- credentials resolve only at apply;
|
|
- retries cannot duplicate success; use upstream idempotency or reconciliation;
|
|
- an indeterminate upstream outcome is reported as indeterminate, never retried
|
|
blindly or called exactly-once;
|
|
- receipts are stable, secret-free, and sufficient for recovery;
|
|
- no direct mutation path remains beside the protocol unless project authority
|
|
explicitly places it outside policy.
|
|
|
|
## Semantic presentation
|
|
|
|
When Ink must show a domain operation better than raw stdout, the executable must
|
|
construct one canonical staged operation and derive a deterministic, bounded
|
|
**presentation projection** from it. The projection is not a second description:
|
|
the manifest binds the operation and exact semantic projection bytes. Ink owns
|
|
layout; the tool owns meaning.
|
|
|
|
Use the repository's elected projection envelope and bounds exactly; never invent
|
|
per-tool presentation formats. Within that contract, use a small display
|
|
vocabulary rather than universal mutation kinds:
|
|
|
|
- headline and canonical target;
|
|
- ordered key/value facts;
|
|
- bounded text or unified diff;
|
|
- outcome and optional evidence reference.
|
|
|
|
Filesystem, HTTP, cloud, and infrastructure tools express their own semantics
|
|
with those primitives. For example, an HTTP mutator supplies method, canonical
|
|
origin/path, bounded body summary, status, and final URL as facts; it does not ask
|
|
Ink to understand an `http` mutation type. An infrastructure tool supplies account,
|
|
resource, region, and requested change as facts; it does not create a renderer
|
|
branch for its provider.
|
|
|
|
The projection is presentation evidence, never authority:
|
|
|
|
- canonicalize argv and pinned inputs once into the staged operation;
|
|
- derive projection records purely from that value and hash their exact semantic
|
|
bytes with the manifest;
|
|
- render approval from the exact staged projection stored under that identity;
|
|
- apply accepts only staged id plus hash, never replacement target, body, mutation,
|
|
or projection inputs;
|
|
- the receipt identifies the exact staged manifest and may add only outcome and
|
|
evidence facts; it cannot rewrite approved records;
|
|
- final rendering reuses the staged projection and overlays only validated receipt
|
|
facts, so there is no independently generated “final projection” to drift;
|
|
- it grants no policy selector, credential, retry, or execution semantics;
|
|
- it is secret-free, width-independent, stable across locale/timezone, and
|
|
bounded with an explicit omission or evidence handle;
|
|
- hash semantic records, never width-, theme-, or expansion-dependent rows;
|
|
- tools never emit colors, cursor control, borders, wrapping, or terminal layout;
|
|
- legacy and ordinary commands without a projection remain valid and use Ink's
|
|
generic argv/stdout/stderr fallback.
|
|
|
|
Do not add presentation metadata when argv plus bounded output already answers
|
|
what happened. Do add it for permission-bearing operations whose approval would
|
|
otherwise expose manifests, hashes, encoded payloads, or provider internals.
|
|
|
|
## CLI and stream contract
|
|
|
|
`tool --help` is the tested human contract: mutations, argv, streams, ordering,
|
|
exits, environment/config precedence, credential timing, dependencies, pattern
|
|
semantics, and one realistic pipeline. Explicit help succeeds on stdout; usage
|
|
errors fail on stderr. Selector help gives value grammar, canonicalization, and a
|
|
least-authority policy row, including AND within one row and alternatives across
|
|
rows.
|
|
|
|
Keep argv unsurprising: options before operands, `--` ends options, `-` denotes a
|
|
natural stream, secrets never enter argv, and unknown, incompatible, or trailing
|
|
arguments fail. No hidden account, cwd, config, resource search, prompt, pager,
|
|
color, progress, or `/dev/tty` access. Stdout is records, stderr diagnostics;
|
|
records stream with bounded memory, ordinary SIGPIPE stays quiet, and machine
|
|
output is independent of display width, locale, and timezone unless documented.
|
|
|
|
Choose one output posture:
|
|
|
|
- **Exhaustive:** emit every record and let ordinary pipes narrow it; no hidden
|
|
limit, ranking, or cursor.
|
|
- **Bounded:** bound before generation, disclose omissions, and provide a stable
|
|
cursor, detail command, omitted count, or retrievable handle.
|
|
|
|
Line-oriented TSV contains no literal TAB, LF, CR, or NUL; use a documented
|
|
versioned codec or NUL-record mode only when round-trip values require it. JSON is
|
|
opt-in when structure is the payload, never the default adapter around records.
|
|
|
|
Add `tool contract` only for Ink capability discovery. Keep it deterministic,
|
|
versioned, and machine-readable, with exactly one row per variable selector and
|
|
none for invariants. Metadata never chooses policy defaults. Ink pins executable
|
|
and contract bytes; schema versions belong in output, not executable, policy, or
|
|
skill names.
|
|
|
|
Make the executable name and one-line job obvious on the collection's normal
|
|
install or help surface; do not create a parallel catalog that repeats `--help`
|
|
or `contract`. In a recommended Ink policy row, make text after `--` useful to
|
|
the provider: lead with one copyable invocation, then the job. For semantic
|
|
mutators advertise only `NAME stage ...` and state that Ink owns match/apply.
|
|
|
|
## Portability and artifact
|
|
|
|
Name the claim precisely:
|
|
|
|
- **POSIX-specified:** behavior is actually specified by POSIX;
|
|
- **POSIX-portable custom:** a custom CLI using portable POSIX facilities;
|
|
- **POSIX-shaped:** composes with POSIX streams but uses extensions;
|
|
- **platform-specific:** requires named platform facilities.
|
|
|
|
Cross-compilation proves compilation, not runtime portability. Test representative
|
|
targets and ordinary hostile conditions: missing HOME/config, non-writable cwd,
|
|
EOF/short I/O, non-TTY streams, interruption, and relevant locale/timezone
|
|
variation. Avoid assumptions about `/proc`, GNU userland, bash, writable state,
|
|
or fixed path lengths.
|
|
|
|
“Single executable” permits only documented runtime libraries; “static” permits
|
|
none. Inspect the produced artifact. Do not add daemons, plugins, sidecars,
|
|
interpreters, or generated-client machinery unless they are the named job.
|
|
|
|
## Proof
|
|
|
|
Use the smallest matrix that can falsify the actual claims:
|
|
|
|
- help/contract agree with accepted argv and selectors;
|
|
- main path and one forbidden near miss with zero unintended mutation;
|
|
- each reader selector has an adjacent no-match before access;
|
|
- each mutator has admitted, approval-required, refused, drift, duplicate, and
|
|
indeterminate/recovery outcomes as applicable;
|
|
- a projected operation renders the exact staged target and change before and
|
|
after apply, with only receipt-bound outcome/evidence added; mutation of stored
|
|
projection bytes, replacement apply inputs, malformed or oversized records,
|
|
secret-bearing data, and receipt identity mismatch all fail closed;
|
|
- exhaustive output preserves every fixture record; bounded selection proves its
|
|
bound, omission signal, and continuation/detail path;
|
|
- stdout/stderr redirection, `tool | head`, empty/malformed input, `-`, and a path
|
|
beginning with `-`;
|
|
- selected record codec edge cases, including TAB/LF/CR/backslash/NUL as relevant;
|
|
- clean environment, non-TTY, target runtime smoke, and artifact dependency
|
|
inspection;
|
|
- Ink discovers, pins, decides, and executes the tool end to end.
|
|
|
|
Do not build a universal harness. One meaningful forbidden-near-miss test is worth
|
|
more than broad ceremonial coverage.
|
|
|
|
## Refusals
|
|
|
|
Do not:
|
|
|
|
- force one source file when one executable is the requirement;
|
|
- wrap or partially clone a utility whose whole admitted surface already fits;
|
|
- combine read and mutation for code reuse;
|
|
- expose a generic request/admin/registry platform;
|
|
- invent invariant selectors, a universal mutation ontology, or executable-name
|
|
branches in Ink's renderer;
|
|
- let tools choose terminal styling or let presentation metadata grant authority;
|
|
- treat argv prefixes, globs, or regexes as canonical path authority;
|
|
- add pagination where a complete stream and pipes are simpler;
|
|
- dump bodies from a bounded selection or silently truncate a complete stream;
|
|
- claim read-only, portable, static, or exactly-once without falsifiable proof;
|
|
- register or recommend the tool before its help, boundary, near miss, policy,
|
|
composition, and target artifact are proven.
|
|
|
|
## Handoff
|
|
|
|
Report only:
|
|
|
|
- `job` and `boundary`;
|
|
- argv/stream/output posture;
|
|
- authority selectors and credential timing;
|
|
- artifact and runtime dependencies;
|
|
- proof through Ink's real run path, including staged-projection/receipt identity;
|
|
- remaining blast radius or untested target.
|
|
|
|
## Evaluation
|
|
|
|
- **Mutator:** an S3 uploader splits from deletion, uses `stage/match/apply`,
|
|
resolves credentials at apply, and proves duplicate and indeterminate outcomes.
|
|
- **Reader:** repository reads use canonical `path-prefix` plus an adjacent
|
|
no-access proof; executable identity makes `action=read` an invalid selector.
|
|
- **Reuse:** grant existing `grep` only if its whole surface is honest; otherwise
|
|
build the missing bounded reader, not a compatibility wrapper.
|
|
- **Pattern:** a reader may own a root-relative glob only to avoid access;
|
|
matching supplied records remains a stdin filter, not path authority.
|
|
- **Presentation:** an HTTP mutator derives method, canonical target, and bounded
|
|
body summary from one staged operation; Ink renders the exact stored projection
|
|
without an `httpsend` branch and overlays receipt-bound status/final URL only.
|
|
- **Presentation sprawl:** “support AWS changes” does not add an AWS mutation enum or
|
|
renderer branch; the permission-sized infrastructure tool projects account,
|
|
region, resource, requested change, outcome, and evidence through the elected
|
|
generic vocabulary.
|
|
- **Portability/sprawl:** a cross-build alone proves neither runtime portability
|
|
nor static linkage; refuse daemons, SDKs, dispatchers, and policy editing.
|