feat(skills): Skills & Health — skill runtime, instruction resolver, doctor v2, support bundle - #71
feat(skills): Skills & Health — skill runtime, instruction resolver, doctor v2, support bundle#71AetherAI3 wants to merge 16 commits into
Conversation
…urce-of-truth map Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…very, resolution, lazy loading, policy Strict-JSON manifest validation (closed keys, reserved aether/* namespace, tool/permission vocabulary checks, safe relative paths), canonical length-prefixed SHA-256 package digest, committed-safe lock file, local digest-bound trust store, metadata-only discovery, explicit/automatic resolution with ambiguity + dependency-cycle refusals, TOCTOU-checked lazy body loading, intersection-only policy enforcement, and the bounded typed skill context packet. Stable skill.* refusal codes throughout. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…and conflicts; network becomes an explicit gate category InstructionResolver: bounded read-only discovery of .aether/instructions.md, root and nested AGENTS.md, user-level instructions, and CLAUDE.md / GEMINI.md / copilot / Cursor-rule compatibility imports; nested scoping, explicit precedence (nearest nested > canonical project > root > user > imports), test-command conflict detection with the effective winner and reason, honest unsupported-syntax warnings (a Cursor rule we cannot parse is NOT applied globally), truncation and binary-file skips with visible reasons, and a typed instruction context packet carrying per-source digests. Gate repair: web_search/web_fetch now map to a 'network' gate action instead of falling through ungated; doctor's tools.gates check and autonomy tests pin the new mapping. Gate prompts show url/query detail. Includes skill-core unit tests (schema, digest, trust/lock, discovery, resolution, lazy-loading proof with TOCTOU refusal, policy, context packet). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…legacy refusal Additive DevSessionWireRequest fields (capability_contract_version, skill_context, instruction_context — absent for legacy runs), CloudBrain DevSessionContext, skill_context_ack / instruction_context_ack frame decoding and visible acknowledgement rendering, and the skill-session assembly used by 'aether agent': discover, resolve explicit (--skill) plus bounded automatic candidates, lazy-load, packet build, visible Skills/Rules/Conflict header, and per-call policy enforcement in the tool gate ahead of the permission gate. A legacy server with skill context present refuses with skill.server_unsupported unless --no-skills deliberately opts out. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…x built-in skills, offline eval engine aether skills list/show/explain/create/install/enable/disable/trust/untrust/ lock/check with stable exit codes; trust requires --yes or interactive confirm and fails closed without a TTY; show/explain never print skill bodies. /skills mirrors the read-only family through one registry entry and routes trust to the deliberate CLI action. Built-ins (aether/review-pr, fix-ci, ship, doctor-project, research-and-implement, frontend-from-screenshot) ship as strict manifests + SKILL.md copied into dist/src/skills/builtin by the new deterministic post-build asset step. skill_eval.ts runs the schema/resolution/policy eval layers offline — zero model calls, zero UVT — with JUnit output for CI. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…skills eval subcommand Every built-in declares evals/cases.json exercising resolution (automatic trigger selection) and policy (allowed tools pass, denied tools refuse with codes) with max_uvt 0 pinned — offline evals are zero-spend by construction. 'aether skills eval [id|--all] [--json] [--junit <path>]' runs them; the asset copy step now ships evals/ and templates/ alongside references/. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…solver src/generated/agent_capabilities.ts pins the canonical AETHER-CLOUD contract (source repo, commit, version, canonical sha256 — digest verified equal to the Python contract_digest byte-for-byte). resolveCapabilities() prefers the server manifest, falls back to the snapshot with a visible warning, refuses an incompatible major contract version, and never mixes vocabularies. Renderer separates static support from runtime availability. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…t of a sentence The single greedy regex let 'pytest tests/api. Never cargo test --all here.' consume everything after pytest, hiding the cargo command and polluting the extracted value with prose. Runner commands are now token-scanned: args must look like flags, paths, or scoped targets, and sentence punctuation ends the command. All 11 instruction tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lity contract guides Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…-gated automatic selection at scale Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s, and rollback path Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pro/Team now lists Claude Opus 5, the GPT-5.6 family, Kimi K3, and Gemini 3.6 Flash first, with the previous generation (Opus 4.8, GPT-5.5, DeepSeek V4 Pro, Kimi K2.6, Gemma 4 31B) labeled as still selectable rather than presented as the flagship set. Frontier table unchanged — already current. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…oud SHA Snapshot now cites AetherAI3/AETHER-CLOUD@97eacd3e (PR #1065, merged and deployed dark). Contract content and canonical digest unchanged (8da09423…). Release record updated with the merge evidence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ostics, backup-first repair, redacted self-verifying bundle Doctor v2: diagnostics split into cohesive category modules with a check registry and executor (old ids/behaviors preserved verbatim; diagnostics.ts is now a re-export shim). New skills/instructions checks, --network (--deep alias), --category, --failed, --junit, --schema v1|v2 (v1 default for existing JSON consumers), and --live driving one synthetic dev session end to end (auth, negotiation, pause/resume acks, sandboxed write-read round trip, sequence check, teardown, no-residue) with zero model spend. Safe repair: --fix dry-runs a plan; --fix --yes applies with backup-first atomic transactions, verify, rollback on failure, and metadata-only receipts in repair-receipts.jsonl. Only skill-index rebuild, config-dir creation, and stale-tmp pruning are implemented; destructive classes are documented as forbidden. Support bundle: aether support-bundle stages 8 allowlisted metadata-only entries in a 0700 temp dir, packages with a minimal deterministic ustar writer, reopens and re-parses the candidate, enforces the entry allowlist, runs the canonical secret scanner (redaction.ts consolidates session_log's helpers plus JWT/bearer/hex/userinfo/homedir detectors), verifies per-file hashes, and only then renames into place — any failure deletes the candidate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… /why explanations, skills eval flags aether capabilities [--json] [--available] renders static support separately from runtime availability (packaged snapshot offline, server manifest when signed in). /why replays the bounded explanation log — skill selections with reasons and confidence, refusals, permission denials, and instruction conflicts record entries at the moment they happen. aether agent gains --skill <id> and --no-skills; skills eval gains --json/--junit passthrough. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PR A0.2 of the SC-A0 Skills & Health integration rescue, part 1 of 2. Recovers PR #71's capability matrix and support bundle onto main: src/core/capabilities.ts capability matrix src/generated/agent_capabilities.ts packaged offline fallback snapshot src/core/support_bundle.ts redacted, self-verifying bundle src/core/redaction.ts shared redaction vocabulary src/core/tar.ts dependency-free tar writer capabilities, redaction, and tar applied unmodified. support_bundle needed a two-line port: it called PR #71's doctorReportV2, which this rescue drops in favour of main's diagnosticReport (see the conflict matrix in _loopstate/LOOP-01/sc-a0-2026-08-19/AUDIT-ARTIFACT.md, decision C1). The swap is a strict improvement for the bundle. PR #71's report modelled configured/reachable/verified as plain booleans, so a fast-mode bundle — which performs no network I/O — had to serialize verified:false for every remote axis, indistinguishable from checked-and-failed. main's HealthReport carries per-axis "not-checked", so an unexercised probe stays visibly unexercised in the artifact a user sends to support. Still owed for A0.2 part 2: porting PR #71's skills and instructions checks (the only two it has that main lacks) from its CheckSpec onto main's CheckOutcome, and the safe-repair reconciliation. Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail (1007 before this commit) npm run smoke 3 pass / 3 skip / 1 fail The smoke failure is `cloud turn` HTTP 401 "Invalid or expired session token". Verified pre-existing: the identical failure reproduces at 1521199 with this commit's changes stashed. It is an expired local credential requiring `aether auth login`, not a code regression, and no file in this commit is on the cloud-turn path. Recorded as an operator-owned gate, not a pass. Bundle safety is covered by the ported suites, both passing here: a seeded canary secret is rejected rather than shipped, and an interrupted generation leaves no misleading "complete" artifact behind.
…ontract PR A0.2 of the SC-A0 Skills & Health integration rescue, part 2 of 2. Ports the only two checks PR #71 has that main lacks — skills and instructions — from PR #71's CheckSpec onto main's DiagnosticCheckSpec/CheckOutcome, and wires them into fast mode. Six checks land: skills.index store integrity and index errors skills.lock lockfile presence and drift skills.trust project skills untrusted or changed skills.evals declared eval manifests instructions.graph source count and parse warnings instructions.conflicts detected conflicts, by topic This is a shape translation, not a rewrite. PR #71 modelled a result as a status plus a detail string with configured/reachable/verified as plain booleans; each axis here carries its own state and evidence. The translation is where the contract decision earns its keep. These checks touch only the filesystem, so `reachable` is n/a — there is no remote to reach — rather than a pass borrowed from a probe that never ran. Under PR #71's booleans there was no way to say that: `reachable: false` would have read as unreachable. `verified` is a genuine yes/no because these checks do exercise the files they report on during the run. The axis mapping lives in one helper (`localOutcome`) rather than being repeated per check, so a future check cannot quietly claim a `verified` it did not earn. Extends, rather than relaxes, the frozen check-ID inventory in test/diagnostics.test.ts. That assertion exists to catch unintended drift; this drift is intended, so the expected list grows and the deep-equal stays exact. Adds an assertion that both new categories report `reachable: "na"`, so a later change cannot silently upgrade them to a pass. Live proof against the built CLI at this commit — `aether doctor --json`: skills.index cfg=yes reach=na ver=yes 0 skill(s) indexed skills.lock cfg=yes reach=na ver=yes no project skills, no lock required skills.trust cfg=yes reach=na ver=yes no project skills awaiting trust review skills.evals cfg=yes reach=na ver=yes no skills discovered instructions.graph cfg=yes reach=na ver=yes 0 instruction source(s), no parse warnings instructions.conflicts cfg=yes reach=na ver=yes no instruction conflicts detected The emitted payload contains no boolean-shaped `verified` field, confirming PR #71's competing schema-v2 contract is absent rather than merely unused. "0 skill(s) indexed" is truthful, not a defect: the built-in skill assets are not packaged until A0.3. Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail
…ged assets PR A0.3 of the SC-A0 Skills & Health integration rescue. Makes the work in A0.1 and A0.2 reachable. Until this commit the skill runtime existed but no user could invoke it. aether skills <subcommand> list, show, check, trust, lock aether capabilities capability contract, with --available aether support-bundle redacted metadata-only diagnostic archive Built-in skill assets (SKILL.md, skill.json, evals) ship as data, which tsc does not emit, so `build` now runs scripts/copy-skill-assets.js after compile. The existing `files: ["dist/src"]` entry already carries them into the tarball; verified below rather than assumed. Deliberately NOT taken from PR #71: * Its cli_registry entry rewriting `doctor [--deep]` to `[--network] [--fix]`. `--deep` stays read-only per decision C2 in the conflict matrix; aliasing it to a network-performing mode would silently change what an existing habit does. `--network` is a separate, explicitly named mode when it lands. * Its main.ts doctor flags (--network, --schema, --category, --failed), which belong to the doctor engine this rescue drops. main's doctor parses its own argv, so it needs none of them. * Its `aether agent --skill` / `--no-skills` flags and dev-session skill context. Those touch code.ts, chat.ts, brain_cloud.ts and stream.ts, which SC-A1, SC-A4 and SC-A5 own. Deferred rather than raced. COMMANDS.md gains real sections, not just index entries. The parity test only asserts the index, but an index pointing at nothing is a doc that lies by omission. Every subcommand documented was then invoked to confirm it exists: skills list -> 6 built-ins listed skills show -> aether/ship@1.0.0 — Ship skills check -> ok skills trust -> "aether/ship is a builtin skill — already trusted" skills lock -> locked 0 project skills Live proof at this commit, against the built CLI: aether skills list 6 built-in skills, scope and trust shown aether capabilities contract v1, falls back to the packaged snapshot and states that it did so, and why aether support-bundle 25600-byte tar written, sha256 reported aether doctor --json skills.index now reports "6 skill(s) indexed" (it read 0 before the assets were packaged) Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail npm run verify:production exit 0 — ok:true, 518 packed files, 2315934 bytes npm pack --dry-run 18 dist/src/skills/builtin/** entries present The COMMANDS.md parity test failed first on this change, correctly: the CLI registry had grown and the doc had not. Fixed by documenting the commands, not by relaxing the assertion.
…upport bundle [SC-A0.1 + A0.2a] (#72) * feat(skills): skill runtime and instruction resolver foundation PR A0.1 of the SC-A0 Skills & Health integration rescue. Recovers the skill and instruction subsystems from PR #71 (a868f7d) onto current main (41a7e26) without importing PR #71's parallel doctor engine. PR #71 branched at b98ef26 (2026-08-12) and never saw PR #66 (27c100d, 2026-08-14), which landed its own doctor v2 on main. Both declare doctor schema version 2 with incompatible payloads: main's health.ts models configured/reachable/verified as Axis objects with a "not-checked" state, PR #71's contracts.ts models them as plain booleans. A boolean cannot express "not checked", so in fast mode — which performs no network I/O — every remote axis would have to report false, which is indistinguishable from checked-and-failed. main's contract is both newer and safer, so it stays canonical and PR #71's is dropped rather than merged. This commit lands only the part of PR #71 that main has no equivalent of, and which turned out to apply to main unmodified: src/core/skills/* schema, digest, lock, trust, discovery, loader, resolver, policy, bounds, eval, session, settings, context packet, permission vocabulary src/core/instructions/* AGENTS.md discovery and resolution with provenance src/core/why_log.ts capability explanation log Capabilities, support bundle, and the skills/instructions doctor checks follow in A0.2; CLI wiring and packaged skill assets follow in A0.3. The full conflict matrix, including the four textual conflicts and the decision record for each contested surface, is in _loopstate/LOOP-01/sc-a0-2026-08-19/AUDIT-ARTIFACT.md. Gates at this commit, run against this exact tree: npm run typecheck exit 0 npm test 1006 pass / 0 fail (baseline on 41a7e26 was 922/0) * fix(instructions): escape every glob metacharacter, not just the first CodeQL js/incomplete-sanitization (high) on PR #72, at src/core/instructions/instruction_resolver.ts:18 — the metacharacter escape in globToRegExp used a non-global regex. Not exploitable as written: `char` is `glob[index]`, always a single code unit, so there is never a second occurrence to miss. But the safety of that escape rests on an invariant nothing in the function states, and a future change to a multi-character token would silently widen every glob's match set. Fixed rather than dismissed, since the fix is free. Adds a regression test that pins the property rather than the implementation: a glob containing . + ( ) | { } [ ] $ ^ must match itself literally and must not match a decoy path, while ** and * keep working. Mutation-checked: replacing the escape with a bare `pattern += char` fails the new test with "unescaped . would match axts"; restoring it passes. Gates at this commit: npm run typecheck exit 0 npm test 1007 pass / 0 fail * feat(capabilities): capability matrix and redacted support bundle PR A0.2 of the SC-A0 Skills & Health integration rescue, part 1 of 2. Recovers PR #71's capability matrix and support bundle onto main: src/core/capabilities.ts capability matrix src/generated/agent_capabilities.ts packaged offline fallback snapshot src/core/support_bundle.ts redacted, self-verifying bundle src/core/redaction.ts shared redaction vocabulary src/core/tar.ts dependency-free tar writer capabilities, redaction, and tar applied unmodified. support_bundle needed a two-line port: it called PR #71's doctorReportV2, which this rescue drops in favour of main's diagnosticReport (see the conflict matrix in _loopstate/LOOP-01/sc-a0-2026-08-19/AUDIT-ARTIFACT.md, decision C1). The swap is a strict improvement for the bundle. PR #71's report modelled configured/reachable/verified as plain booleans, so a fast-mode bundle — which performs no network I/O — had to serialize verified:false for every remote axis, indistinguishable from checked-and-failed. main's HealthReport carries per-axis "not-checked", so an unexercised probe stays visibly unexercised in the artifact a user sends to support. Still owed for A0.2 part 2: porting PR #71's skills and instructions checks (the only two it has that main lacks) from its CheckSpec onto main's CheckOutcome, and the safe-repair reconciliation. Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail (1007 before this commit) npm run smoke 3 pass / 3 skip / 1 fail The smoke failure is `cloud turn` HTTP 401 "Invalid or expired session token". Verified pre-existing: the identical failure reproduces at 1521199 with this commit's changes stashed. It is an expired local credential requiring `aether auth login`, not a code regression, and no file in this commit is on the cloud-turn path. Recorded as an operator-owned gate, not a pass. Bundle safety is covered by the ported suites, both passing here: a seeded canary secret is rejected rather than shipped, and an interrupted generation leaves no misleading "complete" artifact behind. * feat(doctor): skill and instruction health checks on the three-axis contract PR A0.2 of the SC-A0 Skills & Health integration rescue, part 2 of 2. Ports the only two checks PR #71 has that main lacks — skills and instructions — from PR #71's CheckSpec onto main's DiagnosticCheckSpec/CheckOutcome, and wires them into fast mode. Six checks land: skills.index store integrity and index errors skills.lock lockfile presence and drift skills.trust project skills untrusted or changed skills.evals declared eval manifests instructions.graph source count and parse warnings instructions.conflicts detected conflicts, by topic This is a shape translation, not a rewrite. PR #71 modelled a result as a status plus a detail string with configured/reachable/verified as plain booleans; each axis here carries its own state and evidence. The translation is where the contract decision earns its keep. These checks touch only the filesystem, so `reachable` is n/a — there is no remote to reach — rather than a pass borrowed from a probe that never ran. Under PR #71's booleans there was no way to say that: `reachable: false` would have read as unreachable. `verified` is a genuine yes/no because these checks do exercise the files they report on during the run. The axis mapping lives in one helper (`localOutcome`) rather than being repeated per check, so a future check cannot quietly claim a `verified` it did not earn. Extends, rather than relaxes, the frozen check-ID inventory in test/diagnostics.test.ts. That assertion exists to catch unintended drift; this drift is intended, so the expected list grows and the deep-equal stays exact. Adds an assertion that both new categories report `reachable: "na"`, so a later change cannot silently upgrade them to a pass. Live proof against the built CLI at this commit — `aether doctor --json`: skills.index cfg=yes reach=na ver=yes 0 skill(s) indexed skills.lock cfg=yes reach=na ver=yes no project skills, no lock required skills.trust cfg=yes reach=na ver=yes no project skills awaiting trust review skills.evals cfg=yes reach=na ver=yes no skills discovered instructions.graph cfg=yes reach=na ver=yes 0 instruction source(s), no parse warnings instructions.conflicts cfg=yes reach=na ver=yes no instruction conflicts detected The emitted payload contains no boolean-shaped `verified` field, confirming PR #71's competing schema-v2 contract is absent rather than merely unused. "0 skill(s) indexed" is truthful, not a defect: the built-in skill assets are not packaged until A0.3. Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail * feat(cli): aether skills, capabilities and support-bundle, with packaged assets PR A0.3 of the SC-A0 Skills & Health integration rescue. Makes the work in A0.1 and A0.2 reachable. Until this commit the skill runtime existed but no user could invoke it. aether skills <subcommand> list, show, check, trust, lock aether capabilities capability contract, with --available aether support-bundle redacted metadata-only diagnostic archive Built-in skill assets (SKILL.md, skill.json, evals) ship as data, which tsc does not emit, so `build` now runs scripts/copy-skill-assets.js after compile. The existing `files: ["dist/src"]` entry already carries them into the tarball; verified below rather than assumed. Deliberately NOT taken from PR #71: * Its cli_registry entry rewriting `doctor [--deep]` to `[--network] [--fix]`. `--deep` stays read-only per decision C2 in the conflict matrix; aliasing it to a network-performing mode would silently change what an existing habit does. `--network` is a separate, explicitly named mode when it lands. * Its main.ts doctor flags (--network, --schema, --category, --failed), which belong to the doctor engine this rescue drops. main's doctor parses its own argv, so it needs none of them. * Its `aether agent --skill` / `--no-skills` flags and dev-session skill context. Those touch code.ts, chat.ts, brain_cloud.ts and stream.ts, which SC-A1, SC-A4 and SC-A5 own. Deferred rather than raced. COMMANDS.md gains real sections, not just index entries. The parity test only asserts the index, but an index pointing at nothing is a doc that lies by omission. Every subcommand documented was then invoked to confirm it exists: skills list -> 6 built-ins listed skills show -> aether/ship@1.0.0 — Ship skills check -> ok skills trust -> "aether/ship is a builtin skill — already trusted" skills lock -> locked 0 project skills Live proof at this commit, against the built CLI: aether skills list 6 built-in skills, scope and trust shown aether capabilities contract v1, falls back to the packaged snapshot and states that it did so, and why aether support-bundle 25600-byte tar written, sha256 reported aether doctor --json skills.index now reports "6 skill(s) indexed" (it read 0 before the assets were packaged) Gates at this commit: npm run typecheck exit 0 npm test 1017 pass / 0 fail npm run verify:production exit 0 — ok:true, 518 packed files, 2315934 bytes npm pack --dry-run 18 dist/src/skills/builtin/** entries present The COMMANDS.md parity test failed first on this change, correctly: the CLI registry had grown and the doc had not. Fixed by documenting the commands, not by relaxing the assertion. * chore: drop .aether/skills.lock.json committed by mistake This file was written into the worktree by running `aether skills lock` while verifying that the documented subcommand actually exists. It is a byproduct of that check, not source, and it was swept in by `git add -A` in the previous commit. `aether skills lock` is a project-scoped command: the lockfile belongs to whichever repository a user runs it in, generated on demand. Shipping this repo's own lockfile would pin an empty project skill set into the package for no reason.
|
Superseded by #72, now merged. The valuable half of this PR is on The doctor engine here was deliberately not carried forward. This branch was authored from The two checks this branch had that Full reasoning and the per-surface decision record: |
This lane had been sitting unpushed on a local worktree with no branch on origin and no CI ever run against it. Opening it so it gets reviewed and exercised.
Sixteen commits, 100 files, roughly +9157/-343. What is in it:
aether.skill/v1schema, digest, lock, trust, discovery, resolution, lazy loading, and policy.aether skillsCLI family, the/skillsREPL command, six built-in skills, and an offline eval engine withskills eval.InstructionResolverwith provenance and conflict reporting; network becomes an explicit gate category. Includes a fix where test-command extraction swallowed the rest of a sentence.aether capabilitiescommand with/whyexplanations.Notes for review: the branch is six commits behind main and will need a rebase, and CI has never run on this code, so expect the first run to be informative rather than green.
🤖 Generated with Claude Code