Skip to content

feat(github): Cloud Action Rail commands under aether github (UX-01) - #141

Merged
AetherAI3 merged 1 commit into
mainfrom
feat/agent-action-rail-cli
Sep 5, 2026
Merged

feat(github): Cloud Action Rail commands under aether github (UX-01)#141
AetherAI3 merged 1 commit into
mainfrom
feat/agent-action-rail-cli

Conversation

@AetherAI3

Copy link
Copy Markdown
Owner

Lane

UX-01 — Packet B of the Control Plane P0 fanout. The Agent-side half of the GitHub Action Rail.

Base: main @ 6c61ea17203a181ef5c04f682e57b634f24f1bf9 (verified three ways: gh api, git ls-remote, git rev-parse — and it matches the SHA the spec recorded).

Cloud counterpart: AetherAI3/AETHER-CLOUD#1510 (AR-01).

What this adds

Extends the existing aether github tree — it does not fork it:

aether github pr list --repo <owner/name>
aether github pr view <number> --repo <owner/name>
aether github pr prepare --repo <owner/name> --head <ref> [--base <ref>] --title <text> --body-file <path> [--draft]
aether github pr create --plan <plan_id> --approve create-draft-pr
aether github pr update --plan <plan_id> --approve update-pr
aether github checks --repo <owner/name> --pr <number>
aether github ci rerun --plan <plan_id> --approve rerun-failed-checks
aether github workflow dispatch --plan <plan_id> --approve dispatch-workflow
aether github action view <action_id>

status / connect / disconnect are carried over unchanged.

The four rules, each asserted rather than documented

1. Custody is always stated. The account-link commands stay local; everything new runs under Aether Cloud custody — the backend's GitHub App, a JIT token scoped to one repository, an ActionRun that leaves a receipt. There is no fallback between the two, because a silent fallback is how someone believes they published under one identity and actually published under another. Two tests read the module sources and assert no child_process / spawnSync / execFile reference exists, so "never shells out to gh" is structural, not a promise.

2. Approval is exact. --approve create-draft-pr authorizes creating a draft PR and nothing else, so a phrase typed for one action cannot be replayed against another. Parametrized tests confirm yes, y, true, approve, CREATE-DRAFT-PR and a trailing space all fail. --yes is not authority. A TTY is not consent either — interactive mode still refuses until the confirmation is actually answered.

3. Non-interactive callers get a structured refusal, never a prompt. AUTHORIZATION_REQUIRED plus the plan to show a human. No hanging on a TTY that isn't there, and no confirming on the human's behalf.

4. Values stay inert typed data. --title --force yields the string "--force". Nothing re-parses it and nothing builds a shell string, so command substitutions, embedded newlines, quotes and Windows paths are just strings in a JSON body — the same discipline src/core/review_actions.ts applies to paths.

Registry work follows the existing architecture

The shell:github manifest entry gains the new args, summary, detailedHelp and ownedFlags; docs/generated/commands.md is regenerated with npm run docs:generate, not hand-edited. scripts/generate-docs.ts needed the new args string allowlisted in COMMAND_PLACEHOLDERS — its markdown-injection guard rejects any bracketed token it does not recognise, which is the guard working correctly.

⚠ A spec claim that does not hold in this repository

The spec requires this lane to "preserve aether ship exactly" and lists its fake-gh canaries as an exit gate.

aether ship does not exist at 6c61ea17 — no command, no manifest entry, no docs entry, no such tests. I checked src/, test/, COMMANDS.md and docs/. That requirement is therefore vacuously satisfied rather than met, and I'd rather say so than let a green checkbox imply a canary ran. The custody distinction it exists to protect is enforced above regardless.

Test plan

npm run typecheck        # clean
npm run docs:check       # checked 6 documentation outputs, no drift
node --test dist/test/github_action_rail.test.js dist/test/github.test.js \
            dist/test/cli_registry.test.js dist/test/command_docs_parity.test.js \
            dist/test/command_registry.test.js

56 pass, 0 fail — including the pre-existing docs-parity and command-registry guards.

The full npm test suite exceeds my local run window, so CI is the authoritative check on this PR.

Known limits

  • Server routes not live yet. The client calls /cloud/actions/github/*, which lands with AR-01 (#1510); until that merges these commands will get a 404 from the backend. Nothing is enabled by this PR alone.
  • No aether gh alias. The spec permits one as an exact parser alias; it is not shipped here rather than shipped half-done.
  • action watch currently behaves as view — no streaming poll loop yet.
  • Interactive confirmation is plumbed (checkApproval accepts confirmed) but no prompt is wired; today every mutation needs the explicit --approve, which is the stricter path.

READY_FOR_MERGE=true · MERGED=false · ENABLED=false — not self-merging, per the fanout handoff contract.

Extends the existing `aether github` tree with the Cloud Action Rail: pr
list/view/prepare/create/update, checks, ci rerun, workflow dispatch, and
action view. status/connect/disconnect are carried over unchanged.

The boundary between the two halves is the point of the change. The
account-link commands stay local. Everything new runs under Aether Cloud
custody — the backend's GitHub App, a JIT token scoped to one repository,
and an ActionRun that leaves a receipt. Nothing here invokes a local `gh`
binary or touches the user's own GitHub session, and there is no fallback
between the two: a silent fallback is how someone believes they published
under one identity and actually published under another. Two tests read the
module sources and assert no child_process/spawnSync/execFile reference
exists, so that claim is structural rather than asserted.

Approval is exact. `--approve create-draft-pr` authorizes creating a draft
pull request and nothing else, so a phrase typed for one action cannot be
replayed against another. `--yes` is not authority, a generic `--approve`
is not authority, and neither is a trailing space or different casing.
A TTY is not consent either: interactive mode still refuses until the
confirmation is actually answered.

Non-interactive callers get a structured AUTHORIZATION_REQUIRED plus the
plan to show a human. They never get a prompt, never hang on a TTY that
isn't there, and cannot confirm on the human's behalf.

Values stay inert typed data. `--title --force` yields the string
"--force"; nothing downstream re-parses it and nothing builds a shell
string, so command substitutions, embedded newlines, quotes and Windows
paths are just strings in a JSON body. Same discipline
src/core/review_actions.ts applies to paths.

Registry work follows the repo's existing architecture rather than
side-stepping it: the shell:github manifest entry gains the new args,
summary, detailed help and ownedFlags, and docs/generated/commands.md is
regenerated with `npm run docs:generate` rather than hand-edited.
scripts/generate-docs.ts needed the new args string allowlisted in
COMMAND_PLACEHOLDERS — its markdown-injection guard rejects any bracketed
token it does not recognise, which is the guard working correctly.

NOTE FOR REVIEW: the specification says this lane must "preserve
`aether ship` exactly" and lists its fake-gh canaries as an exit gate.
`aether ship` does not exist in this repository at main 6c61ea1 — no
command, no manifest entry, no docs, no such tests. That requirement is
vacuously satisfied rather than met, and the gap is reported instead of
papered over. The custody distinction it exists to protect is still
enforced above.

Tests: 56 new in test/github_action_rail.test.ts. Typecheck clean; docs
drift check clean (6 outputs).
@AetherAI3
AetherAI3 merged commit ddafb46 into main Sep 5, 2026
7 checks passed
@AetherAI3
AetherAI3 deleted the feat/agent-action-rail-cli branch September 5, 2026 23:48
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.

1 participant