Skip to content

feat(skills): skill runtime, instruction resolver, capabilities and support bundle [SC-A0.1 + A0.2a] - #72

Merged
AetherAI3 merged 8 commits into
mainfrom
supercluster/a0-skills-health-integration
Aug 19, 2026
Merged

feat(skills): skill runtime, instruction resolver, capabilities and support bundle [SC-A0.1 + A0.2a]#72
AetherAI3 merged 8 commits into
mainfrom
supercluster/a0-skills-health-integration

Conversation

@AetherAI3

@AetherAI3 AetherAI3 commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Problem

PR #71 ("Skills & Health") is CONFLICTING against main and cannot be rebased mechanically. It carries a large amount of genuinely new and valuable work — the skill runtime, the instruction resolver, the capability matrix, the support bundle — but it also carries a second, competing implementation of aether doctor v2.

Root cause

b98ef26d (2026-08-12) is the last commit main and PR #71 share.

Two independent "doctor v2"s were built in the same 48 hours. That is why only four files conflict textually while the semantic conflict is much larger.

Contract

The contested surface is the doctor report contract. Both sides declare schema version 2 with incompatible payloads:

mainsrc/core/health.ts PR #71src/core/diagnostics/contracts.ts
report HealthReport, camelCase DoctorReportV2, snake_case
configured / reachable / verified Axis objects, state in {yes,no,unknown,na,not-checked} plain boolean
mode fast | live | fix fast | network | live | fix
severity info | warning | error info | warning | critical

Decision: main's 3-axis model is canonical; PR #71's boolean triple is dropped, not merged.

A boolean verified cannot express not-checked. Fast mode performs no network I/O by contract, so every remote axis is unchecked — under PR #71's shape that must serialize as verified: false, which is indistinguishable from checked and failed. main's not-checked state keeps them distinct, and main's version is also the newer one. Shipping both would put two mutually incompatible payloads behind one schema_version: 2.

Two further decisions, recorded in full in the artifact:

Full conflict matrix, per-surface decision record, and findings table: _loopstate/LOOP-01/sc-a0-2026-08-19/AUDIT-ARTIFACT.md

Implementation

This PR is A0.1 — the foundation only. It lands the part of PR #71 that main has no equivalent of. Notably, it applied to main unmodified: the port required zero source edits, which is itself evidence that the conflict was confined to the doctor engine.

src/core/skills/*        20 files — schema, digest, lock, trust, discovery, loader,
                         resolver, policy, bounds, eval, session, settings,
                         context packet, permission vocabulary
src/core/instructions/*   3 files — AGENTS.md discovery + resolution with provenance
src/core/why_log.ts       capability explanation log

Nothing is wired into the CLI yet, so this PR changes no user-visible behaviour. It is additive and inert until A0.3.

Tests

Run against this exact tree at d1b75ab:

command result
npm ci --ignore-scripts exit 0, 0 vulnerabilities
npm run typecheck exit 0
npm test 1006 pass / 0 fail

Baseline re-measured on clean 41a7e261 in the same session: 922 pass / 0 fail. The 84 new tests are PR #71's skill and instruction suites, ported unchanged: trust/modify/re-trust lifecycle, lock drift, duplicate skill IDs across scopes, dependency cycles, lazy-loading token bounds, nested AGENTS.md precedence and provenance, conflicting-instruction detection.

No result here is inherited from PR #71's branch. PR #71 has no CI at allgh pr view 71 --json statusCheckRollup returns an empty rollup — so nothing on it could have been inherited even if we wanted to.

Security notes

  • No credential, token, or environment value is read, written, logged, or embedded by any file in this PR.
  • Skill trust is digest-bound; a content change invalidates prior trust. Covered by test/skill_trust_lock.test.ts.
  • Skill declarations restrict behaviour; they do not grant tools beyond the host's existing authority. Covered by test/skill_policy_packet.test.ts.
  • Instruction resolution fails conservatively on unsupported syntax rather than guessing.

Known limits

Dependency and merge order

A0.1  this PR            skill + instruction foundation
A0.2  depends on A0.1    capabilities, support bundle, redaction/tar,
                         skills+instructions checks ported onto health.ts
A0.3  depends on A0.2    CLI wiring, packaged skill assets, docs

SC-A1 (tool runtime) and SC-A5 (usage continuity) both start from the accepted A0 head — PR #71 also modifies src/core/stream.ts, which both of those lanes own.

PR #71 should stay open until A0.3 merges, then be closed as superseded rather than merged. Its doctor engine is deliberately not carried forward.


Update — scope now covers A0.1 and the first half of A0.2

This branch has grown past the "foundation only" description above. Recording the
change rather than leaving a stale body.

Commits on this PR

commit what
d1b75ab A0.1 — skill runtime + instruction resolver foundation
1521199 CodeQL high-severity fix + mutation-checked regression test
ba7d041 A0.2 part 1 — capability matrix + redacted support bundle

Why one PR instead of the planned three. A0.1 is inert on its own and A0.2's
support bundle depends on it directly, so splitting them would produce a first PR
no reviewer can exercise and a second that cannot be reviewed without the first.
At ~37 files this is still well under PR #71's 100, and the module boundaries make
it readable. A0.3 (CLI wiring, packaged skill assets, docs) stays a separate PR.

CodeQL finding, fixed

CI flagged js/incomplete-sanitization (high) at instruction_resolver.ts:18
the glob metacharacter escape used a non-global regex, so only the first
metacharacter was escaped.

Not exploitable as written: char is glob[index], always a single code unit,
so a second occurrence cannot exist. But the escape's safety rested on an invariant
the function never stated, and a future change to a multi-character token would
silently widen every glob's match set. Fixed rather than dismissed.

The regression test pins the property, not the implementation: a glob containing
. + ( ) | { } [ ] $ ^ must match itself literally and must not match a decoy,
while ** and * keep working. Mutation-checked — replacing the escape with a
bare pattern += char fails the test (unescaped . would match axts); restoring
it passes.

This defect arrived from PR #71 untouched. PR #71 runs no CI, so on its own branch
it would have shipped unseen.

A0.2 part 1 — the one real port

capabilities.ts, redaction.ts, tar.ts and the generated capability snapshot
applied unmodified. support_bundle.ts needed two lines: it called PR #71's
doctorReportV2, which this rescue drops in favour of main's diagnosticReport.

The swap strictly improves the bundle. Under PR #71's boolean model 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 hands to support.

Gates at ba7d041

command result
npm run typecheck exit 0
npm test 1017 pass / 0 fail
npm run smoke 3 pass / 3 skip / 1 fail

Test progression, each measured on the exact tree: 922 (clean 41a7e261) → 1006
(A0.1) → 1007 (CodeQL regression test) → 1017 (A0.2 part 1).

The smoke failure is operator-owned, not a regression. cloud turn returns
HTTP 401 "Invalid or expired session token". Verified by re-running smoke at
1521199 with this commit's changes stashed — identical failure. It needs
aether auth login. No file in this PR is on the cloud-turn path. Reported as a
fail, not rounded up to a pass.

Bundle safety suites pass: a seeded canary secret is rejected rather than shipped,
and an interrupted generation leaves no misleading "complete" artifact.

Still owed before this PR leaves draft

A0.3 (CLI wiring, built-in skill assets, aether skills, /skills,
aether capabilities, docs) remains a separate follow-up PR. Until it lands, none
of this is reachable from the command line — it is additive and inert.

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)
Comment thread src/core/instructions/instruction_resolver.ts Fixed
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
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.
@AetherAI3 AetherAI3 changed the title feat(skills): skill runtime and instruction resolver foundation [SC-A0.1] feat(skills): skill runtime, instruction resolver, capabilities and support bundle [SC-A0.1 + A0.2a] Aug 19, 2026
…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.
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.
@AetherAI3
AetherAI3 marked this pull request as ready for review August 19, 2026 12:42
@AetherAI3
AetherAI3 merged commit 51cb94c into main Aug 19, 2026
5 checks passed
@AetherAI3
AetherAI3 deleted the supercluster/a0-skills-health-integration branch August 19, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants