merge: integrate all six supercluster lanes, prove the installed artifact [SC-INT] - #79
merge: integrate all six supercluster lanes, prove the installed artifact [SC-INT]#79AetherAI3 wants to merge 16 commits into
Conversation
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)
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.
…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.
…lind
Lane SC-A2, slice 1 of the project-continuity work.
`aether agent --repo owner/name` reused an existing local mirror on the
strength of one `existsSync` check:
if (existsSync(join(dir, ".git"))) return { dir, cloned: false };
There is no `git fetch` anywhere in this codebase, so that mirror was never
refreshed. `createWorktree` then branched off it with no start-point argument,
meaning a task could silently begin from a tip that was current days ago, and
`prCreateHint` would go on to invite a PR from it.
The mirror path is derived from the slug alone
(~/.aether-agent/repos/<owner>-<name>), so the remote was never validated
either: any directory sitting at that path was accepted as the requested repo.
Adds `refreshMirror`, called by `ensureLocalClone` before an existing mirror is
returned. It guarantees three things:
1. the mirror's origin really is the repo that was asked for — compared
through parseRepoSpec so https/ssh/.git/trailing-slash forms normalize
rather than needing a second, subtly different URL parser
2. the mirror is fetched, and the resulting tip is reported
3. when the fetch cannot happen — offline, auth expired, remote gone — the
result is "unknown" with the git error attached. It never degrades to
"fresh" as a convenience
Read-only with respect to the user's tree: it runs remote get-url, fetch and
rev-parse. Never checkout, reset, clean, merge, pull or rebase. A test asserts
each of those six verbs is absent from the recorded argv.
Auth stays the user's own git/gh configuration, inherited from the environment.
A test asserts no Aether credential shape (aek_, Authorization,
http.extraheader, GIT_ASKPASS, x-access-token) reaches the git argv.
`ensureLocalClone` now takes an injected Runner, defaulting to the existing
`defaultRunner()` from worktree.ts. It previously called spawnSync directly and
was therefore untestable; this is the same seam the gated-worktree flow already
uses, so no second runner abstraction is introduced.
The user-facing line stops rounding off. "(reusing local clone)" was equally
true of a mirror last fetched a week ago:
⎇ repo octocat/hello-world (fetched) @ a1b2c3d
⎇ repo octocat/hello-world (NOT REFRESHED — Could not resolve host: github.com)
! this worktree will branch off whatever the mirror already had;
its base is not known to match the remote.
Written test-first: the six tests were added and confirmed failing to compile
against the missing export before `refreshMirror` existed.
Mutation-checked: rewriting every `state: "unknown"` to `state: "fresh"` fails
"a failed fetch reports unknown, never fresh" (11 pass / 1 fail); restoring
gives 12 / 12.
Gates at this commit:
npm run typecheck exit 0
npm test 928 pass / 0 fail (922 on clean 41a7e26)
Noted, not fixed here: test/diagnostics.test.ts "a hanging backend cannot stall
the fast report" failed once under full-suite load at 1818ms and passes in
isolation at 82ms. Its 50ms budget is load-sensitive. Pre-existing fragility,
unrelated to this change, left for the lane that owns that file.
…s lying about HEAD
Lane SC-A3, slice 1 of the review-and-ship rail.
Three defects in two commands that discard the user's uncommitted work, and
which shipped with no tests at all.
1. `/rollback [n]` accepted a count that did nothing.
The count was parsed, range-checked to 1..50, and then never referenced
again — every invocation reverted the entire working tree, whatever number
was passed. The usage line advertised "revert last n filesystem changes",
a capability that does not exist: nothing in this codebase records per-step
filesystem state.
There is no checkpoint journal to hang a real `[n]` on, so this takes the
other option the contract allows and drops the argument. Passing one now
prints what it used to do and refuses, rather than silently ignoring it.
`/revert step-3` likewise stops saying "coming soon" for something no code
is working toward.
2. Both commands claimed changes were "restored to last commit". They were not.
`git checkout -- <path>` restores from the INDEX. With anything staged, the
content that comes back is the staged content — so the message was false
exactly when the user had staged work they might have been counting on.
Both commands now check the index and say which it was, and `/rollback`
prints the command that does go all the way back to HEAD. The confirmation
prompt names the same target as the completion line.
3. Neither command worked from a subdirectory.
Detection was `existsSync(join(cwd, ".git"))`, which reports "not a git
repository" for every subdirectory of one — where a REPL usually sits.
Now `git rev-parse --show-toplevel`.
Also: `git checkout --` becomes `git restore --`. Both restore from the index,
but restore only ever touches files, so it cannot be talked into switching
branches by a crafted pathspec.
Adds a testability seam. Both functions take an optional GitToolDeps
{ cwd, git } defaulting to the real runner. It reuses the existing GitRunner
from git_commit_guard.ts rather than introducing a third runner abstraction —
this repo already has two, which recon flagged as a hazard. The three call
sites in slash.ts are unchanged.
First behavioural coverage for these commands — 12 tests, previously zero:
the count is refused, and refusing mutates nothing
usage text no longer promises per-change undo
"last commit" is never claimed while the index is populated
"last commit" is claimed only when nothing is staged
untracked files are never removed (no `clean` in any argv)
declining the prompt issues no mutation
a clean tree issues no mutation
a subdirectory of a repo is still in the repo
outside a repo it refuses and mutates nothing
/revert passes the path after `--`, proven with a leading-dash filename
/revert on an untracked file refuses and mutates nothing
Mutation-checked: forcing restoreTargetLabel to always answer "last commit"
fails both truthfulness tests (10 pass / 2 fail); restoring gives 12 / 12.
Registry and COMMANDS.md updated to match — `/rollback` no longer documents an
argument, and both entries state the index-versus-HEAD behaviour rather than
leaving the reader to discover it.
Gates at this commit:
npm run typecheck exit 0
npm test 934 pass / 0 fail (922 on clean 41a7e26)
Not addressed here, and still open in this lane: hunk-level staging, the
commit and PR rail, and stage_diff.ts still parsing human `git diff --stat`
output with a regex.
…op faking steer
Lane SC-A4, slice 1 of local-brain parity.
Three ways the local Ollama brain quietly disagreed with the host.
1. Tool results were not correlated to tool calls.
The pending result was a single anonymous resolver and the id parameter was
literally discarded:
sendToolResult(_id: string, result: ToolResult): void {
if (this.pending) { ... }
}
Any result satisfied whichever call happened to be waiting. A result
carrying the wrong id was indistinguishable from the right one, and a
duplicate vanished with no trace. The loop awaits calls serially so only one
is ever in flight today, which is what kept this from corrupting runs — but
nothing enforced that, and nothing would have reported it if it broke.
Now keyed by id. An unknown id, or a second result for a call already
settled, emits an error event instead of advancing the loop on a result it
never asked for. close() drains every outstanding waiter rather than one.
Also fixes an ordering fragility: the waiter is now registered BEFORE the
tool_call event is emitted. Registering afterwards only worked because the
consumer resumes on a microtask — a host that replied synchronously would
have found no waiter at all.
2. The advertised tool schemas were hand-written and told the model nothing.
parameters: { type: "object", properties: {}, additionalProperties: true }
Argument names, types, required-ness and bounds were never advertised. The
only description of the real shapes lived in free-text strings that nothing
kept in step with TOOL_DEFINITIONS, and validateToolDefinitionCoverage only
checks name-set equality, not schema fidelity.
ollamaToolSchemas() now generates them from TOOL_DEFINITIONS: per-argument
types, maxLength from maxBytes, integer min/max, an accurate required set,
and additionalProperties:false — the host rejects unknown arguments, so
advertising them as allowed only invited a refusal. A test asserts the
advertised argument set, required set and bounds match the validator for
every tool, so the two cannot drift.
3. control() accepted pause/resume/steer and did nothing.
It returned normally, so the host believed the instruction landed. A dropped
steer then reads to the user as the model ignoring them. This brain runs a
single-pass loop with no interruption point and genuinely cannot honour
these, so it now says so in a visible monologue rather than reporting a
success it did not deliver.
Tests: 5 added — unknown id rejected, duplicate does not advance the loop
twice, close() cannot strand a waiter, control() is visibly honest, and
schemas match TOOL_DEFINITIONS argument-for-argument.
Mutation-checked: restoring the old behaviour (fall back to any waiter when the
id is unknown) fails "a tool result for an unknown id is rejected" — 8 pass /
1 fail; restoring gives 9 / 9.
Gates at this commit:
npm run typecheck exit 0
npm test 927 pass / 0 fail (922 on clean 41a7e26)
Not addressed here, and still open in this lane: chat.ts drops the AbortSignal
on local turns so Ctrl+C is inert until the turn ends; tool_executor.ts uses
blocking spawnSync with no process-group cleanup, so a timed-out test leaves
its children running; and chat.ts re-implements the host loop rather than
sharing code.ts's. The last two are SC-A1's surface and are deliberately not
raced here.
Lane SC-A1, slice 1 of the cancellable tool runtime.
The REPL builds an AbortController per turn and aborts it on Ctrl+C. runTurn
accepted that signal and then dropped it on the local branch:
const backend = await resolveBackend(ctx);
if (backend === "local") {
await runLocalTurn(ctx, prompt); // signal not passed
return;
}
So Ctrl+C did nothing to a local turn. The abort fired and nothing was
listening; the turn ran to completion regardless. Only the cloud path was ever
cancellable.
runLocalTurn now takes the signal and closes the brain on abort. close() is
what unblocks a loop parked on a tool result, so an abort arriving mid-turn is
observed rather than waiting the turn out. The listener is registered before
the loop starts, so a signal that is already aborted is honoured instead of
starting work that was cancelled before it began.
An aborted turn returns rather than throwing. The user asked for the stop; it
is not a failed turn and must not be reported as one.
Adds a LocalTurnDeps seam ({ brain, exec }) mirroring the one smoke.ts already
uses, so the abort path is testable without an Ollama server, a child process
or real tool execution. runLocalTurn becomes exported for the same reason.
Tests: 4 added, driving a brain that emits one tool_call and then parks exactly
as the real one does while awaiting sendToolResult — abort reaches the brain, an
already-aborted signal stops the turn, an aborted turn does not reject, and a
turn with no signal still completes.
Mutation-checked, and the failure mode is the interesting part: restoring the
old behaviour does not fail the tests, it HANGS them. The runner is killed by
timeout with exit 124, having produced no TAP summary at all, because the turn
never settles. Restored, the same run exits 0. That hang is precisely what a
user experienced when they pressed Ctrl+C.
Gates at this commit:
npm run typecheck exit 0
npm test 926 pass / 0 fail (922 on clean 41a7e26)
Scope note. This makes a local turn cancellable BETWEEN steps — during a model
request, or while parked awaiting a tool result. It does not interrupt a tool
already executing: tool_executor.ts still uses blocking spawnSync with no
AbortSignal, so Ctrl+C during a long `run_tests` is still not observed until
that command returns, and the command's children are still orphaned on timeout.
Fixing that requires spawn() with process-group cleanup, which requires
ToolExecutor.run to become async, which requires finalVerify to become async —
it is the synchronous ground-truth gate (verify_gate.ts:71, called from
code.ts:371, with 13 tests pinning its behaviour). That is a deliberate,
separate slice rather than something to graft onto this one.
…wn spend as zero
Lane SC-A5, slice 1 of truthful usage and continuity.
/limit was a control that enforced nothing.
`uvtSpent` was only ever written as `= 0` — at construction, in purge(), and
on snapshot restore. No usage frame ever incremented it. `checkUvtCap()` had
zero callers anywhere in the tree. So:
* `/limit 50000` printed "agent will pause and ask permission if ceiling
hit". Nothing paused. Nothing checked. The cap was never consulted.
* `/limit` reported "spent: 0" and a 0% bar for every session, whatever it
had actually cost, because the number was a constant.
* The field comment claimed it was "read from custody log". Nothing read it.
Three changes.
1. Usage is measured, or it is unknown. Never zero by default.
`uvtObserved: number | null` replaces the always-zero counter. null means no
authoritative frame has arrived, which is not the same as a measured zero
and is no longer rendered as one. Nothing estimates UVT from token counts —
only the server's own number is recorded.
`uvtSpent` survives as a getter for the HUD, which needs a number, and is
documented as reporting 0 when the answer is unknown so that anything which
must tell those apart reads `uvtObserved`.
2. Turns settle once, by id.
The terminal frame carries the turn total and a reconnect can replay it, so
`settleTurn(turnId, uvt)` is keyed rather than accumulated blindly. A
replayed done frame is ignored; distinct turns accumulate.
3. The cap is checked before a billable turn starts, and says what it is.
`runCloudTurn` consults `checkUvtCap()` before doing anything and refuses to
start when the observed spend has reached the cap. Two states deliberately
do not trip it: a local brain (Aether meters nothing, so it is marked
unmetered rather than zero-spend) and a session with no observed usage
(there is no evidence the cap was reached, and guessing either blocks free
work or waves through expensive work).
The wording is now accurate about what it can and cannot do:
no further turn will START once the server-reported spend reaches it.
a turn already in flight may still complete and be billed.
this is a local stop only — your plan and balance are unchanged.
A design change came out of the mutation pass rather than the plan. `remaining`
was a number, and an unmeasured session reported the full cap as headroom —
which is the same false zero in a different costume: it tells the user their
whole budget is intact when in truth none of it has been counted. The first
mutation run did not fail any test, which is what exposed it. `remaining` is now
`number | null`, null when unmeasured, and a test pins it.
Tests: 10 added. Unknown is not zero; a duplicate done frame does not
double-count; distinct turns accumulate; an unknown session is never reported
as capped; the cap trips on reaching it; no cap never caps; local sessions are
labelled unmetered; purge returns to unknown rather than to zero; and
unmeasured headroom is null rather than the full cap.
Mutation-checked, both guards: restoring the false-zero headroom and removing
the replay dedupe fails two tests (8 pass / 2 fail); restoring gives 10 / 10.
Gates at this commit:
npm run typecheck exit 0
npm test 932 pass / 0 fail (922 on clean 41a7e26)
Known limits. The HUD still renders `uvtUsed: reg.uvtSpent`, so it shows 0 for
an unknown session — hud.ts is SC-INT's surface and is deliberately not touched
here. Session-cap persistence across resume, and the cross-workspace rejection
that goes with it, are a later slice. The cap is enforced on the REPL cloud
path; `aether agent` has its own loop and is not yet gated.
… into supercluster/integration-release-gate
…o supercluster/integration-release-gate
…o supercluster/integration-release-gate
…to supercluster/integration-release-gate
…ory metadata
Lane SC-INT. Composes the six feature lanes onto one head and proves they do
not break each other. Nothing here is merged to main.
Merged in the stated integration order:
SC-A0 supercluster/a0-skills-health-integration clean
SC-A2 supercluster/a2-project-context clean
SC-A3 supercluster/a3-review-ship clean (COMMANDS.md auto-merged)
SC-A4 supercluster/a4-local-brain clean
SC-A1 supercluster/a1-tool-runtime clean
SC-A5 supercluster/a5-usage-continuity CONFLICT, resolved below
Conflict 1 — src/commands/chat.ts, runTurn local branch.
lane A1 threads the AbortSignal into runLocalTurn so Ctrl+C reaches a local turn
lane A5 marks the session local-unmetered so usage is not reported as zero spend
chosen both, A5's label first so an abort arriving immediately still finds the
session correctly classified
reason the two changes are independent; neither supersedes the other, and
taking either side alone silently drops a shipped contract
proof test/chat_local_abort.test.ts (A1) and test/usage_cap.test.ts (A5) both
pass on this head
Defect found by integration, not by any lane.
SC-A5's cap gate was written against a patch anchor that matched BOTH runTurn
and runCloudTurn, so the gate and the turn-id allocation were duplicated into
each. Every turn ran checkUvtCap twice and burned two ids from the counter.
Not a correctness bug — settleTurn keys off the id runCloudTurn actually uses,
and a doubled gate returns the same verdict — but it is duplicated control
flow on a spend boundary, and SC-A5's own PR body describes the gate as living
in runCloudTurn.
Removed the runTurn copy. The gate now exists once, in runCloudTurn, which is
also the only billable path: local turns are unmetered and must not be gated.
This is exactly what this lane is for. Every lane was individually green and
the duplication was invisible until they were composed.
Repository metadata (finding A0-F7 from the intake artifact).
package.json repository and bugs, the README CI badge and links, CONTRIBUTING
and docs/PRODUCTION_OPERATIONS pointed at DBarr3/aether-agent.
Verified rather than assumed before editing: gh repo view resolves
DBarr3/aether-agent, DBarr3/Unlimited-Context-LLM and DBarr3/aethercloud all
to their AetherAI3 equivalents, so all three are canonically AetherAI3 today
and the old paths only redirect.
Deliberately NOT rewritten: _loopstate/ audit artifacts and dated docs/specs
design records, which are historical records of what was true when written,
and NOTICE.md line 5, which is the author's personal profile URL rather than a
repository reference.
Gates on the integrated head:
npm run typecheck exit 0
npm test 1054 pass / 0 fail
1054 accounts for every lane exactly: 922 on clean 41a7e26, plus 84 (A0.1)
+ 1 (CodeQL regression) + 10 (A0.2) + 6 (A2) + 12 (A3) + 5 (A4) + 4 (A1)
+ 10 (A5). No test was lost or double-counted in the merge.
Update — cross-platform matrix landed, and the A5 fix moved to its own laneCI on That closes the largest known limit in the description above. The composed head is now proven on both platforms in CI, not only by a local Windows run. The duplicated cap gate has movedThe description records that SC-A5's cap gate was duplicated into both Worth recording why it matters beyond the tidy-up: the test suite could not see it. #78 is 932/932 both before and after the fix. Duplicated control flow on a spend boundary returns the same verdict twice, so nothing failed. It was visible only when six independently-green lanes were composed and read side by side. Re-verified on this head after merging the fix back: Still not proven
This branch remains proof, not a merge path. The lanes should still land individually in the stated order. |
|
Superseded by the lanes themselves. All six lanes plus the de-flake are now merged to This branch was never a merge path — it existed to prove the lanes compose, and it did its job: it caught a duplicated cap gate that was invisible to 932 passing tests and green per-lane CI on both platforms. That fix landed on #78 where it belonged. Closing rather than merging, as stated in the description. |
What this is
The six feature lanes composed onto one head, to answer the question none of them can answer alone: do they break each other?
Nothing here is merged to
main. This is a branch that exists to be proven, and to be thrown away once the lanes land individually.Merge results
COMMANDS.mdauto-merged)Conflict —
src/commands/chat.ts, the local branch ofrunTurnAbortSignalintorunLocalTurnso Ctrl+C reaches a local turntest/chat_local_abort.test.ts(A1) andtest/usage_cap.test.ts(A5) both pass on this headA defect integration found that no lane could
SC-A5's cap gate was duplicated.
Its patch anchor matched both
runTurnandrunCloudTurn, so the cap check and the turn-id allocation were written into each. Every turn rancheckUvtCap()twice and burned two ids from the counter.Not a correctness bug —
settleTurnkeys off the idrunCloudTurnactually uses, and a doubled gate returns the same verdict. But it is duplicated control flow on a spend boundary, and SC-A5's own PR body describes the gate as living inrunCloudTurn.Removed the
runTurncopy. The gate now exists once, inrunCloudTurn— which is also the only billable path, since local turns are unmetered and must not be gated.Every lane was individually green and CI-clean. This was invisible until they were composed. That is what this lane is for.
Repository metadata (finding A0-F7)
package.jsonrepository/bugs, the README CI badge and links,CONTRIBUTING.mdanddocs/PRODUCTION_OPERATIONS.mdpointed atDBarr3/aether-agent.Verified before editing, not assumed.
gh repo viewresolvesDBarr3/aether-agent,DBarr3/Unlimited-Context-LLMandDBarr3/aethercloudall to theirAetherAI3equivalents — so all three are canonicallyAetherAI3today and the old paths only redirect.Deliberately not rewritten:
_loopstate/audit artifacts and dateddocs/specs/design records — historical records of what was true when written.NOTICE.md:5— the author's personal GitHub profile, not a repository reference.Gates on the integrated head
npm run typechecknpm testnpm run verify:productionok:true, 518 files, 2343944 bytesnpm packaether-agents-0.1.0.tgz, 518 files, 563150 bytes1054 accounts for every lane exactly: 922 on clean
41a7e261, plus 84 (A0.1) + 1 (CodeQL regression) + 10 (A0.2) + 6 (A2) + 12 (A3) + 5 (A4) + 4 (A1) + 10 (A5). No test was lost or double-counted in the merge.Installed-artifact proofs
Run against the packed tarball installed into a temp prefix, not the source tree.
aether --version0.1.0aether --helpaether skills listdist/src/skills/builtininside the packageaether doctor --jsonverifiednot-checkedaxisaek_live_CANARY_…not present; noaek_token shape anywhere in the 25600-byte taruntrusted→ trust →trusted→ flip one byte →changed, and doctor reportswarning: 1 project skill(s) untrusted or changed--yes— printed the digest and permissions, recorded nothing. Fails closed.Proof 8 is the digest-bound trust contract demonstrated end to end on the installed package: a single byte of
SKILL.mdinvalidates prior trust.Known limits — what this head does NOT prove
npm run smokestill fails oncloud turn— HTTP 401, expired local session token. Verified pre-existing and operator-owned; it needsaether auth login, not a code change.gh(5), usage cap through a real reconnect (8), local-vs-Ollama transcript parity (9). These need a live brain, a real remote, or a fakeghharness.Merge order
#78will need thechat.tsrebase resolved as above, and the duplicated cap gate removed — it is a real fix, not a merge artifact, and should be applied to#78itself rather than left to integration.