Repository navigation
feat(rc): #229 release-gate prerequisites — real producers, host pump, truthful status - #292
Merged
Merged
Conversation
AetherAI3
added a commit
that referenced
this pull request
Oct 6, 2026
…scan Self-hosted Linux CI on #292 failed four tests that pass on Windows. rc_action_receipts (3 tests): test defect, no leak. On Linux the temp project root was found ONLY at the outbox record's top-level `project_root` -- the field the harness itself writes via createOutbox, which loadOutbox needs to re-relativize paths after a restart and flushOutbox never sends (the append body is device_id + events only). No queued event payload and no append body carried it (probed on VPS6: wire hit = false, queue empty, disk hit = $.project_root only). The check scanned the raw file text, so it failed on Linux for local bookkeeping and was vacuous on Windows, where JSON escapes the root's backslashes and a raw-path search can never match. The leak check now scans every append body whole (not just event_type/payload), plus the outbox record minus `project_root`, for the root both raw and JSON-escaped; asserts `project_root` is exactly the local root; and asserts no append body names `project_root`. Strictly stronger on both platforms. rc_preview "a cancelled start publishes stopped only once the supervisor's state is gone": product bug, Unix only. slow.mjs never exits on its own (it times out normally, empty log). The "exit 1" was the supervisor's own cancel: SIGTERM reaches the detached supervisor, which SIGTERMs the child's group; the signal death closes with no code and `closed = code ?? 1` records it as 1. Two defects followed: - preview_supervisor: a stop before readiness (signal or POST /stop) fell into the not-ready branch, wrote phase "failed: dev command exited before readiness (exit 1)" and never removed its state. So `preview stop` while starting reported "cleanup was not confirmed" (exit 25, on every platform) and the next status/start republished a failure. It now waits for the child and runs the same identity-checked cleanup as a stop after readiness, exiting 0. - preview start: the wait loop checked `cancelled` only at its top, so a cancel landing during sleep() or the /status request let the next read report the supervisor's reaction to that cancel -- exit 23 and RC phase "failed" instead of 130 and "stopping". It now re-checks after each await. Windows never reached either: taskkill /F kills the supervisor before it can write anything. Verified on VPS6 at SIGTERM +0/50/250/400ms: exit 130, state removed, then "stopped". New preview.test covers the POST /stop path (RED before the fix on Linux: 25 !== 0). Gates: Linux (VPS6, Node 24.21) rc_* 380/380, preview 15/15; Windows (int229) build, rc_* 380/380, preview 15/15, generate-docs --check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
src/core/rc/viewer_profile.ts has long cited test/rc_viewer_profile.test.ts as the pin against the Cloud viewer manifest, but the file never existed, so the Agent's observe-only profile and Aether Code's /rc routes could drift apart silently. - test/fixtures/rc-viewer-profile-v1.json is a byte-identical copy of Aether Code's site/components/remote-session/rc-viewer-profile-v1.json (sha256 57731a2b...ab7a1, LF-pinned in .gitattributes). - test/rc_viewer_profile.test.ts pins the raw bytes, asserts viewerManifest() and EXCLUDED_EVENT_TYPES equal the manifest, re-checks every viewer route and API path token by token against FORBIDDEN_VIEWER_TERMS, allows only one non-GET call (redeeming the observe grant), and proves the link `aether rc` prints lands on a manifest viewer route while operator surfaces stay gated and outside /rc. - docs/REMOTE_VIEWING.md documents the observer-only viewer and the separate operator route, and adds the missing `aether rc viewers` row. Refs #228 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 6, 2026
Publish a display/1 diff_summary to the active RC session when a real
checkout diff is established: in `rc start`'s opening batch, and after a
local coding run settles. Paths come from `git status`; counts come only
from git numstat and bounded direct reads, never from prose or model
output. Publication is best-effort: every failure is swallowed and never
changes the local result.
Final behaviour:
- All three counts (files_changed, insertions, deletions) are always
present, as Cloud's display/1 contract requires. A clean tree is 0/0/0.
- A binary file is a changed file with 0 lines; git has no line count
for binary ("-" in numstat).
- Untracked text files are counted as git would count them once added,
with bounded direct reads (8 MiB per file, 32 MiB and 1000 files per
snapshot) that never follow a link out of the checkout and that honour
.gitattributes (-diff/binary, diff, filter, working-tree-encoding)
through `git check-attr`, chunked to fit the Windows command line.
- Any unknown count means no summary rather than a misstated one: a
failed git read, a changed tracked path numstat did not measure, or an
untracked file that is too large, external, unreadable or
filter-converted, or bounds exceeded.
- Unsafe roots and paths are refused: a project root that is not the
checkout toplevel, and any absolute, traversal, drive-qualified or
leading-"~" path (Cloud reads a leading "~" as a home path).
- `files` is a prefix of the sorted paths, bounded to 64 entries and to
16 KiB of JSON as the broker measures it; files_changed counts every
path.
- The outbox sanitizer refuses a diff_summary without valid
non-negative integer counts or with an unsafe file path, and sizes
every payload the way the broker does (ensure_ascii JSON, not UTF-8),
so no payload Cloud would answer 400 reaches durable storage and
wedges the batch behind it.
Supersedes #240.
Closes #218.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The host's own verifier readings now reach an active RC session's Tests panel (#219): the final gate of `aether agent` (through the run's own coding observer), `aether review verify`, and stored readings shown by `aether review` (the source of "stale"). - testsEvent publishes the verifier's status plus a closed-vocabulary reason and, for a failure, the exit code; no command, output, local reason or parsed counts. - Killed runs (130/124 or an aborted signal), a tree that moved during the run, and a tree that cannot be fully identified are "unknown" and are never recorded or read back as a pass. - A frame field the outbox sanitizer would rewrite is dropped; without its status the reading is not sent, so no 400 can wedge the outbox. - The coding observer reloads the outbox before enqueueing when no upload is in flight, so a standalone review's queued reading is not saved over. - RC failures never change verdicts or exit codes; nothing is awaited. Integrated onto #289 (874b36b): whether a check completed is the CheckReading the agent footer renders (verify_gate readCheck), so a kill is read from the executor's own markers or an aborted signal, never an exit code alone; verifyAndRecord maps cancelled/timed_out onto these causes. In `aether agent` the gate's reading is the one verifyAndRecord records for the review rail (recordingRunner hands it over), published by verifyCodeTurnInCheckout; without a recorded run the CheckReading alone is published (no command, not run, could not start, killed, or unattributed). A check that cannot start reads "the check could not start" (new launch_failed cause). publishDiff (#218) shares the observer's single reload-before-enqueue path. The review-rail kill test follows #289's executor-marker rule: a command that exits 124/130 by itself is a completed, recorded failure; only an executor kill ([aborted]/[timeout after Ns]) is unknown and unrecorded. Integrated with #299: recordingRunner keeps #299's onResult callback, which receives the whole VerifyRunResult. verifyCodeTurnInCheckout captures the recorded {reading, written} once, publishes the reading to RC as before, and returns it as recordedCheck; cmdCode hands that receipt to the session log and to the goal-phase runObserver exactly as #299 did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When `aether github pr create|update` or `ci rerun` executes an approved
Action Rail plan and the Cloud returns its receipt, queue that receipt's
CI or PR identity for the active RC session of this checkout through the
existing adapters (ciEvent, prStatusEvent) and the existing outbox seam:
sanitize at durable enqueue, and move the cursor only on proven receipts.
- Only a receipt that binds to the plan just executed is published:
plan_id, action_digest and action_type must match, the action must be a
rail mutation, and `reconciled` must be a real boolean. Issued versus
reconciled is the one status a receipt states, so it is never guessed.
- ci carries {provider: "github", status: issued|reconciled, run_id} with
a numeric run id only. pr_status carries {repo, number,
state: issued|reconciled, url}; the link is built from the validated
owner/name and the numeric number, never copied from html_url. No
title, checks, deployment or merge state is inferred.
- Provider payloads, html_url, refs, workflow paths, the provider's own
status, actor, token fingerprint, PR body and logs never reach the
outbox. A workflow dispatch receipt has no run identity and publishes
nothing.
- A field the sanitizer would rewrite is dropped rather than published
altered, and a PR link survives only while it still matches repo and
number, so no payload the broker would answer 400 can wedge the outbox.
- Fire-and-forget: the projection is durable before the command returns,
the upload is started and never awaited, and every RC failure is
swallowed. The command's output and exit code are unchanged. Without an
active, unrevoked session for this checkout nothing is written.
Closes #220.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A tool_activity whose target is "~" or begins with "~" (list_dir("~"), an
Office lock file such as "~$Report.docx", "~user/x") kept target:"~…" through
the outbox sanitizer. Cloud's display/1 identifier rule refuses ANY leading
"~" ("absolute local paths are forbidden"), and RC_EVENT_REJECTED keeps the
rejected batch at the head of the outbox, so a single such event wedged
delivery for the rest of the session.
sanitizePathIdentifier now maps a leading "~" to "[external-path]", the same
identifier every other external path gets, before durable enqueue. A "~"
inside a project-relative name (src/a~b.ts) is unaffected. The regression
test drives the real producer (mapBrainEventToRc) into the real outbox, and
the reviewer's repro now passes Cloud's own validator for all three shapes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When a media-history entry is durably committed, the active RC session for the launch project receives one `artifact` display/1 event: a session-scoped artifact id, the kind, a safe display label and (only when measured) the size. - Commit seam: appendEntry takes an optional onCommitted observer that runs after the verified commit and outside the file lock; a failed append is never observed and an observer throw cannot undo the commit. recordOutput threads it, and every production call site (aether image/video and the /photogen, /re-frame, /videogen, /animate, /re-cut and storyboard render paths) passes rcArtifactObserver(ctx); a source guard pins that. - Identity: artifact_id = artifact-<24 hex of sha256(session, entry id)>, so a resend or replay updates one viewer card and the local history id stays local. - Privacy: path, URL, prompt, model and metadata never leave. The title is the file's bounded leaf name, cut on a code-point boundary, and falls back to a generic "Image #N" when it could carry what is dropped on purpose: the model, ANY meaningful prompt word (reordered, article-less or concatenated slugs included), or a capability-shaped object key (UUID, long random run, hex digest) taken from a media URL. - No outbox wedge: title is never empty, unmeasured sizes send no summary, and a payload whose required keys would not survive sanitization is never enqueued. - Delivery is fire-and-forget through the existing loadOutbox -> enqueueEvent -> saveOutbox -> flushOutbox seam: durable before any network call, never awaited by the media command, no timers. Overlapping commits in one process share one in-memory record and delivery loop; an idle publisher always starts from the outbox on disk, so it never erases another writer's queue. Tests (test/rc_artifacts.test.ts, 19): commit seam, projection and leak canaries, prompt-slug and capability-key labels, surrogate-safe truncation, offline/lost-response replay deduped to one artifact, in-flight overlap, coexistence with the coding observer while the broker is offline, and /photogen end to end, all against a broker stub that enforces the Cloud display/1 contract. Closes #221. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`aether preview` (and /preview in the REPL) now reports the phases it
observes or causes to the active RC session for the project: starting, ready
(only once the control channel verifies it), failed, stopping and stopped
(only once the supervisor's state file is gone). Payloads are
{projection_version, phase, instance_id[, url]}; instance_id is
preview-<24 hex of sha256(session, supervisor instance id)>, stable across one
launch. Loopback URLs, ports, PIDs, argv and child error text never leave.
- Viewer link: only for the declared preview while it is ready, and only from
an operator-declared `publicUrl` in .aether/preview.json that passes
previewDisplayUrl: HTTPS origin + path; no userinfo, query or fragment; no
IP literal; no single-label, private, tailnet (.ts.net), reserved
(.test/.invalid), onion or loopback-resolving host (localtest.me, nip.io
style, names spelling an IPv4 address); no capability-shaped segment (long
random runs, UUIDs, hex digests); nothing the inline scrubber would rewrite.
- A refused publicUrl stops `preview start` only while an RC session is
active; without one it is ignored with a warning that never echoes it.
- `preview start` publishes a stale supervisor failure (e.g. exit after ready)
before replacing its state, so the viewer stops showing it as ready.
- Cancel and the concurrent-stop timeout publish `stopping`, then `stopped`
from a bounded, unref'd poll once cleanup is proven; a forced kill that
leaves the state file keeps the viewer at `stopping`.
- previewOptionsFromFlags passes `args` only when --arg is given; the empty
list used to override the declaration, launching the bare executable on the
real CLI path so a declared publicUrl could never match.
- Publisher: durable enqueue through the existing outbox seam, upload never
awaited, no timers held. Between uploads it re-reads the outbox before every
enqueue, so it never saves a stale copy over events another writer (the
coding observer, orchestra or media publishers) queued meanwhile.
Tests (test/rc_preview.test.ts, 18) run the real supervisor and the real CLI
registry flag path against a broker stub enforcing the Cloud display/1
contract: loopback, declared public, malformed declaration (with and without
an RC session), malformed state, failed launch while the broker is down,
stale failure via status and via start, cancel, two writers sharing one
outbox while offline, and the URL projection.
Refs #222 — closes together with the AETHER-CLOUD PreviewPanel no-link state.
Integrated after #221: both lanes added a private capabilityShaped() to
producers.ts with different rules (artifact filename stems vs URL path
parts); kept both, renamed stemCapabilityShaped / urlPartCapabilityShaped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`rc start` now reports a session live only after a valid register, a valid attach, durable local state and a receipted first append. An outage on the first append leaves the session pending (opening events durable, delivered later) and exits non-zero; a terminal answer, an attach failure or any local write failure rolls the Cloud session back, and a revoke the Cloud cannot confirm leaves a durable pending-revoke tombstone that the next rc command retries. loadOutbox no longer turns an unreadable, unparseable or incompatible file into a silent blank: it returns an explicit recovery condition that saveOutbox refuses to write over and `rc start` refuses to start past. `rc off` revokes the session the damaged bytes still name and sets the file aside, never claiming a revocation it did not get. `rc off` with nothing running no longer writes a tombstone naming no session (which blocked every later start and made the next rc command claim a Cloud revocation that never happened). When the local revoke marker cannot be written, the Cloud revoke is still attempted, because the active record left on disk means nothing local keeps publication off; the result names which half happened instead of promising RC "will not resume automatically". A second start over a pending session says it is pending, not running. A read or rename that fails on a transient Windows lock (EBUSY, EPERM, EACCES, EAGAIN) is retried briefly, at most 60 ms, before anything is concluded. State that still cannot be READ is an I/O condition that nothing acts on (no start over it, no `rc off` setting it aside); only unparseable or incompatible bytes take the recovery path. Only the Cloud's own 404 "session not found", or a 409, confirms a revoke; any other 404 keeps the tombstone. `rc link`, and a failed observer-link mint, on a session the Cloud refuses say so and name `aether rc off` instead of reporting "pending" or "running". Also classifies real ApiClient HttpErrors by FastAPI's body.detail, which previously turned every 409 into RC_SESSION_TERMINAL. Integrated after #218/#219: the measured checkout diff_summary (#218) rides in the opening batch of the new fail-closed start, best-effort as before; the coding observer keeps #219's shared publish path and gates on isPublishable. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A coding run that finds an attached RC session now binds one host lifetime to itself (#223). The new host pump (src/core/rc/pump.ts) heartbeats the session every 5 s (the Cloud's HEARTBEAT_INTERVAL_S), drains the durable outbox in at most 32 bounded batches per tick whenever events are queued, backs off on retryDelayMs's capped jittered curve (1 s to 60 s) on outages, rate limits and unproven receipts, and stops for good on terminal, revoked/expired/closed or ownership answers and on a local revoke. Every timer is unref'd, nothing throws into the run, and the run's `finally` makes one final flush bounded by a 1.5 s deadline before aborting whatever is still in flight. Events left queued stay durable, and the next run resumes delivering them once a heartbeat proves the session is still this host's. The outbox file is now the single truth between its writers. The coding observer read-modify-writes the file instead of saving a long-lived copy, the pump adopts the file on every tick, and flushOutbox reloads it before sending and again before committing receipts (receipts are validated against the cursor at send time; the cursor never moves backwards). Previously the observer, /orchestra and a receipt landing mid-batch could each save over events another writer had queued, losing them for good while the broker was offline. Heartbeat and delivery run on independent clocks. Delivery backing off (a refusing or rate-limited events route) never silences the 5 s heartbeat, a long drain heartbeats between batches, and delivery only runs on a session a heartbeat proved live. A local read or receipt write that fails on a lock backs off instead of stopping the host; damaged bytes still stop it. The close deadline timer is ref'd so a run cannot exit mid-await. Tests run on a virtual clock: a 90 s outage with backoff and no lost events, reconnect and duplicate-safe replay (host_event_id dedupe, cursor advancing only on full receipts), termination on revoked/expired/not-found, a bounded final flush against a hung broker, resume on the next run, and both multi-writer races with the broker offline and with a receipt in flight. Further regression tests: heartbeat cadence under delivery backoff, an unreadable-then-restored and then damaged outbox, an unwritable receipt that is replayed, and two writers flushing overlapping batches without wedging the cursor. Integrated after #218/#219: the observer's publish() (the verify gate's tests reading) and publishDiff() use the same read-modify-write-the-file path as feed() and wake the pump, replacing #219's reload-unless-flushing copy. The run-end diff measurement is no longer awaited by the run; close() waits for it only inside its one 1.5 s deadline, then makes the final flush (tests: close delivers a diff that finishes in time; a measurement that never finishes cannot hold close past the deadline). `aether review` publishes through a one-shot seam (rc/publish.ts: load, isPublishable, enqueue, save, flushOutbox) instead of opening a coding observer, whose pump would heartbeat for the life of a REPL. #219's broker stub answers heartbeats. Integrated with #299: the hoisted rcObserver sits beside #299's external abort-signal wiring, and the outer finally removes that abort listener before the bounded rcObserver.close(). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`rc status`, `rc exposure` and `rc viewers` now ask the Cloud's owner-scoped
GET /remote/sessions/{id}/status (#226) with a short bound (3 s, backed by an
unref'd local deadline) instead of rendering placeholders. A verified answer
(schema aether.remote_session_status.v1, matching session id, a known state,
ISO-8601 Z datetimes, non-negative counts) is combined with the local record:
`active` needs both a receipted first append locally and the Cloud saying
live, and reconnecting, offline, revoked, expired, closed, pending,
pending-revoke and not-found are each named. A timeout, an outage, a Cloud
without the route (404 "Not Found"), disabled remote sessions, a refusal, a
rate limit or an answer that does not verify is an explicit `unknown` with a
reason, never `active` and never zero observers. A missing or foreign session
(404 "session not found") is reported as not-found.
The surfaces show real expiry, last heartbeat, the last accepted receipt
against the Cloud's last sequence, queued/dropped/quarantined counts, the
event categories exposed right now, and observers as count / cap. `rc
viewers` exits non-zero when the Cloud could not answer. JSON output for all
three adds the verified Cloud fields, the unknown reason, the counters and the
exposed categories, and is still built field by field from a view that holds
no credential, grant token, absolute path or event payload.
"Viewer events N / M available" no longer overclaims: diff_summary, tests,
ci, pr_status, artifact and preview have adapters but no production caller on
this base, so they move to RC_UNPRODUCED_EVENT_TYPES (7 / 13). A source test
now requires a type to be listed as produced exactly when production code uses
its adapter, so the producer lanes (#218-#222) must move their types back when
they land.
While the session is pending or the Cloud cannot be asked, the exposed
categories are labelled as not confirmed by the Cloud (`exposure_confirmed`
in JSON). The source test also checks that mapBrainEventToRc really emits each
type it is credited with.
Integrated after the producer lanes (#218-#222), which are all on this
branch: diff_summary, tests, ci, pr_status, artifact and preview each have a
production caller, so they are listed as produced again and coverage reads
13 / 13; the source test proves each caller, and main's "all thirteen viewer
event types now have a producer" test is restored beside it.
docs/REMOTE_VIEWING.md states `rc viewers` once (Cloud count / cap, `unknown`
never zero, exit 1 when the Cloud cannot answer) and keeps the #219 tests
and #223 heartbeat bullets.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Converge every standalone RC producer on #223's outbox model: the outbox FILE is the single truth, producers read it, queue into what they read and save it, and flushOutbox re-reads it before sending and again before committing receipts. - rc/publish.ts (introduced with #223 for `aether review`) is now the one seam outside a coding run: isPublishable (#227), read-modify-write the file, durable before any upload, delivery never awaited. Within a process there is one delivery loop per outbox; a publication made while it runs joins it (never its record), and the loop re-reads the file once more before ending so a late joiner is still carried. - Action Rail receipts (#220), media-history artifacts (#221) and preview phases (#222) publish through it. Deleted: #221's shared in-memory record and per-outbox record joining, #222's reload-unless-flushing copy and its own flush loop, and the three ad-hoc "active session" checks, which would have published from a session `rc start` had only registered. - Every lane's multi-writer regression test passes unchanged (artifact committed while an upload is in flight, upload succeeding after a later commit, idle publishers never erasing another writer's events), as do LIFE's rc_pump multi-writer tests. New: an unattached session publishes nothing from the standalone producers either. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…scan Self-hosted Linux CI on #292 failed four tests that pass on Windows. rc_action_receipts (3 tests): test defect, no leak. On Linux the temp project root was found ONLY at the outbox record's top-level `project_root` -- the field the harness itself writes via createOutbox, which loadOutbox needs to re-relativize paths after a restart and flushOutbox never sends (the append body is device_id + events only). No queued event payload and no append body carried it (probed on VPS6: wire hit = false, queue empty, disk hit = $.project_root only). The check scanned the raw file text, so it failed on Linux for local bookkeeping and was vacuous on Windows, where JSON escapes the root's backslashes and a raw-path search can never match. The leak check now scans every append body whole (not just event_type/payload), plus the outbox record minus `project_root`, for the root both raw and JSON-escaped; asserts `project_root` is exactly the local root; and asserts no append body names `project_root`. Strictly stronger on both platforms. rc_preview "a cancelled start publishes stopped only once the supervisor's state is gone": product bug, Unix only. slow.mjs never exits on its own (it times out normally, empty log). The "exit 1" was the supervisor's own cancel: SIGTERM reaches the detached supervisor, which SIGTERMs the child's group; the signal death closes with no code and `closed = code ?? 1` records it as 1. Two defects followed: - preview_supervisor: a stop before readiness (signal or POST /stop) fell into the not-ready branch, wrote phase "failed: dev command exited before readiness (exit 1)" and never removed its state. So `preview stop` while starting reported "cleanup was not confirmed" (exit 25, on every platform) and the next status/start republished a failure. It now waits for the child and runs the same identity-checked cleanup as a stop after readiness, exiting 0. - preview start: the wait loop checked `cancelled` only at its top, so a cancel landing during sleep() or the /status request let the next read report the supervisor's reaction to that cancel -- exit 23 and RC phase "failed" instead of 130 and "stopping". It now re-checks after each await. Windows never reached either: taskkill /F kills the supervisor before it can write anything. Verified on VPS6 at SIGTERM +0/50/250/400ms: exit 130, state removed, then "stopped". New preview.test covers the POST /stop path (RED before the fix on Linux: 25 !== 0). Gates: Linux (VPS6, Node 24.21) rc_* 380/380, preview 15/15; Windows (int229) build, rc_* 380/380, preview 15/15, generate-docs --check. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
AetherAI3
force-pushed
the
feat/rc-release-gate-229
branch
from
October 6, 2026 16:46
2ab15dd to
90f6810
Compare
AetherAI3
added a commit
that referenced
this pull request
Oct 6, 2026
Evidence for the deployed remote-viewing gate, run 2026-10-06 against exact revisions: the Agent at PR #292's head (same packed bytes on a Windows and a Linux host), the Cloud API and the observer-only Aether Code web build. - Journey J1-J6 (enroll, start, phone scan, live coding events, viewers, revoke) passed on both hosts. - P1-P5, P8, P10 passed; P6 (duplicate-safe reconnect) passed after the Cloud CORS fix for Last-Event-ID; P7 was not exercised live and P9 only partly - both listed as follow-ups. - Two owner phone screenshots with the account chip redacted. - Follow-up issues: #300-#303 and four AETHER-CLOUD issues. Account names, user/device/session ids and infrastructure hostnames are deliberately omitted from this public record. Refs #229 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Lands every Agent-side prerequisite of the #229 live CLI-to-phone gate in one integration branch, one commit per issue (merge with rebase to keep them). Each lane was implemented test-first, adversarially reviewed, then replayed onto current main (incl. #289) with conflicts resolved semantically.
test(rc): pin the viewer profile…publish measured checkout diff summariespublish test verification outcomesunknown(follows #289's marker rule)publish CI and PR status from Action Rail receiptsrefuse home-relative tool targets~-prefixed tool targets → Cloud 400publish generated artifact metadataartifact_id, prompt/model/path/signed-URL never leavepublish preview lifecyclemake rc start and revoke fail closedkeep host heartbeat and outbox delivery alivereport authoritative exposure, viewers, healthrc status/exposure/viewersread CloudGET …/status(AETHER-CLOUD#1931, deployed);unknown, neveractive, when Cloud can't answerreconcile producers with the host pumprc/publish.ts), outbox file = single truth (cross-writer races fixed), coverage 13/13Closes #218, closes #219, closes #220, closes #221, closes #222, closes #223, closes #226, closes #227. Refs #228, #229 (closed after the live journey).
Test plan
pc_browser_inspect, untouched area; 9/9 on re-run)generate-docs --checkcleanvalidate_event_payloadfor every producer payloadAETHER_CI_SELF_HOSTED_PR)Known follow-ups (not regressions):
checkoutDiffSummarystarts with synchronous git calls at run end; cross-process load→rename window has no file lock; concurrentrc startcan orphan a Cloud session without an idempotency key.🤖 Generated with Claude Code