diff --git a/.agents/skills/quality/SKILL.md b/.agents/skills/quality/SKILL.md index 32792b7cb..a63f609a5 100644 --- a/.agents/skills/quality/SKILL.md +++ b/.agents/skills/quality/SKILL.md @@ -39,26 +39,108 @@ synthesis step owns all edits so dedupe and severity-gating happen in one place. teammate per track, lead runs synthesis. Per the global git-worktrees policy, do **not** pass worktree isolation. Never *require* a team to run this skill. -Each reviewer receives the same scoped context: `git diff main` plus the full +Each reviewer receives the same scoped context: `git diff "$QUALITY_REVIEW_BASE"` +plus the full contents of the changed files — **including new untracked files**, which -`git diff main` omits — so it evaluates without guessing. +the tracked diff omits — so it evaluates without guessing. --- ## Setup +**Invocation:** `/quality [feature] [--base ]`. `--base` is the explicit +direct-parent binding for a stacked layer. Resolve the base in this order: + +1. a validated `--base ` argument; +2. an existing PR's `baseRefName`; +3. the current entry's parent from non-interactive `gh stack view --json`; +4. `ADE_REVIEW_BASE_REF` from a trusted ship state file; +5. `main` for the unchanged ordinary workflow. + +Do not stop discovery after reading an existing PR. Still inspect `gh stack +view --json`: a current branch present in that stack makes its PR base exact, +including a bottom layer based on `main`. A PR with a non-default base is also +an exact direct-parent binding. Only an ordinary unstacked PR targeting the +repository default branch keeps `QUALITY_EXACT_BASE=false` and the historical +merge-base behavior. When both PR and stack metadata exist, their parent names +and SHAs must agree. + +Normalize `refs/heads/`, `refs/remotes/origin/`, `origin/`, and +plain `` to one plain branch name. Reject another remote, symbolic refs, +revision syntax (`..`, `~`, `^`, `:`), an empty value, or a name that fails +`git check-ref-format --branch`; never concatenate an unvalidated ref into a +command. Fetch the normalized name into its exact remote-tracking ref: + ```bash -git diff main --name-only # tracked changes vs main +# QUALITY_BASE_REF is the validated, normalized plain branch name selected +# above. QUALITY_EXACT_BASE is true for --base, stack metadata, or trusted +# stack ship state, a non-default PR base, or a PR confirmed in gh-stack; +# ordinary unstacked /quality against the default branch keeps it false. +git check-ref-format --branch "$QUALITY_BASE_REF" +git fetch origin "refs/heads/$QUALITY_BASE_REF:refs/remotes/origin/$QUALITY_BASE_REF" +QUALITY_BASE_SHA=$(git rev-parse "origin/$QUALITY_BASE_REF") +if [ "$QUALITY_EXACT_BASE" = true ]; then + git merge-base --is-ancestor "$QUALITY_BASE_SHA" HEAD || { + echo "stack-coordinator-sync-required: direct parent is not an ancestor of HEAD" + exit 1 + } + QUALITY_REVIEW_BASE="$QUALITY_BASE_SHA" +else + QUALITY_REVIEW_BASE=$(git merge-base HEAD "$QUALITY_BASE_SHA") +fi +git diff "$QUALITY_REVIEW_BASE" --name-only git status --short # NEW (untracked) files — git diff omits these -git diff main --stat | tail -20 -git log main..HEAD --oneline +git diff "$QUALITY_REVIEW_BASE" --stat | tail -20 +git log "$QUALITY_REVIEW_BASE"..HEAD --oneline ``` +For stack metadata, also require its reported parent SHA to equal +`QUALITY_BASE_SHA`; a name match alone is insufficient. The base must be the +**direct parent** of the current stack entry, not `main` +and not the root of the stack. Record the normalized parent branch, fetched +parent SHA, merge-base, reviewed head SHA, and content-tree SHA. If the parent +cannot be fetched or sources disagree, stop; silently widening or narrowing a +stacked review is not valid evidence. A parent-head or branch change invalidates +this result and every result above it in the stack. + +Run quality once per layer against its direct parent. For the fifth/top layer, +also run both review tracks cumulatively against `origin/main`; the layer passes +only when both the incremental and cumulative gates are empty. Record both +bindings. A lower-parent change cascades invalidation through all higher-layer +bindings, so the coordinator must sync/rebase the stack and rerun them in order. + A new service or module added but not yet committed will not appear in -`git diff main`. Fold the untracked files from `git status` into the review set +the tracked diff. Fold the untracked files from `git status` into the review set and read their full contents — an unreviewed new file is the easiest place for a Blocker to hide. +### Windows parity rules + +When the scoped diff touches filesystem paths, process launch, executable +resolution, IPC, SQLite/native modules, startup services, or Computer Use: + +- Treat Windows as a first-class runtime. Verify drive letters, native and mixed + separators, UNC paths, quoting, `PATHEXT` and executable discovery. Audit + PowerShell, `cmd.exe`, and Git Bash invocation separately for argument loss, + shell injection, and environment drift. Require process-tree termination, + per-user/per-channel named-pipe ACL isolation, Stable/Beta identity isolation, + semantic runtime readiness (not merely a live supervisor PID), stale-PID + cleanup, bounded supervisor restart/backoff, and packaged native dependencies. +- Trace installer, updater, signing, Windows Firewall, Relay, and capability-gate + effects. Verify IPC/preload/shared contracts, CLI/RPC, SQLite/CRR, mobile, + hosted web, and release-manifest compatibility rather than treating a native + host fix as isolated. +- Require platform gates to state the capability, not infer the whole product + is unsupported. Native screenshot/video/OS GUI automation may be blocked on + Windows while App Control and proof-file ingestion remain available. +- Trace the same change through macOS and Linux owners and tests. A Windows fix + that regresses launchd, Unix sockets, POSIX executable lookup, or graceful + Linux capability degradation is a correctness finding. +- Separate code-backed evidence from external proof. Native Windows tests and + CI can prove contracts; installed Stable/Beta isolation, second-account pipe + denial, clean-host restart, and GUI evidence remain explicit blockers until + captured on the corresponding hosts. + --- ## Phase 1: Thermo Dual-Review → Synthesize + Fix diff --git a/.agents/skills/quality/references/ade-review-rules.md b/.agents/skills/quality/references/ade-review-rules.md index 70cd9e44e..e8777e6d4 100644 --- a/.agents/skills/quality/references/ade-review-rules.md +++ b/.agents/skills/quality/references/ade-review-rules.md @@ -125,6 +125,28 @@ rejected for non-linear history; the fallback is a local admin-bypass push. This isn't a code finding but flag any automation that assumes a plain merge will succeed. +## 10. Windows foundation parity + +**Check:** When a lane touches path construction, process launch, executable +lookup, local IPC, SQLite/native artifacts, service lifecycle, or Computer Use: + +- Windows service health means the channel runtime answers on the expected + per-user pipe with the recorded PID; a live supervisor alone is not health. + Verify bounded restart backoff, stale/reused PID diagnostics, and Stable/Beta + identity after packaged channel defaults are applied. +- Named pipes must be scoped by canonical ADE home, channel/service, and current + user identity and retain intended-user listen restrictions. `.exe` resolution + and structured argv must not be replaced with shell-string parsing. +- Native Windows CI must load the actual CR-SQLite DLL and exercise a CRR + mutation when packaging/native paths change. Preserve the macOS dylib, Linux + graceful-degrade, launchd/systemd, and Unix socket contracts. +- Gate exact capabilities: Windows can block native screenshot/video/OS GUI + control while App Control and proof ingestion remain available. Do not widen + a native Computer Use limitation into a product-wide platform block. +- Treat clean-host Stable/Beta coexistence, second-account pipe denial, + reboot/restart, installed updates, and GUI artifacts as external proof. Code + or mocked tests cannot close those gates. + --- ## Output diff --git a/.agents/skills/quality/references/correctness-security-review.md b/.agents/skills/quality/references/correctness-security-review.md index 1923ff519..9e314c31e 100644 --- a/.agents/skills/quality/references/correctness-security-review.md +++ b/.agents/skills/quality/references/correctness-security-review.md @@ -14,14 +14,15 @@ and security/safety issues. Be rigorous — nothing should slip through. ## Scope - ONLY report issues in code being **added or modified** on this lane. Focus on - the diff against `main`. + the diff against the resolved `QUALITY_REVIEW_BASE` (`main` ordinarily; the + direct parent for a stacked PR). - Do NOT report pre-existing issues in untouched code. - Trace cross-module side effects of the changed code even into unchanged files, but the *finding* must trace back to something this lane changed. ```bash -git diff main -git diff main --name-only +git diff "$QUALITY_REVIEW_BASE" +git diff "$QUALITY_REVIEW_BASE" --name-only ``` --- diff --git a/.agents/skills/quality/references/thermo-nuclear-review.md b/.agents/skills/quality/references/thermo-nuclear-review.md index 20ee0ab89..98d3fed02 100644 --- a/.agents/skills/quality/references/thermo-nuclear-review.md +++ b/.agents/skills/quality/references/thermo-nuclear-review.md @@ -4,8 +4,9 @@ Seven structural standards. The primary question for every finding: does a "code judo" move exist — a smaller change that makes the code fundamentally simpler, not just cleaner? -Tone: direct and demanding, not rude. Scope to the diff against `main` — do not -restructure untouched code. +Tone: direct and demanding, not rude. Scope to the diff against the resolved +`QUALITY_REVIEW_BASE` (`main` ordinarily; the direct parent for a stacked PR) — +do not restructure untouched code. --- diff --git a/.agents/skills/ship/SKILL.md b/.agents/skills/ship/SKILL.md index 05b2be90e..3d938f88f 100644 --- a/.agents/skills/ship/SKILL.md +++ b/.agents/skills/ship/SKILL.md @@ -7,7 +7,9 @@ description: >- only CI. Pure loop — it does not replace the baseline /quality or /test runs; run those first. It does revalidate quality after any ship-loop mutation so the final result is bound to the exact reviewed PR head and content tree. - Full phase logic lives + Opt-in --stack-ready runs the same loop for one layer of a coordinator-owned + stack: it fixes and pushes its own layer but stops at ready-stacked instead of + merging, rebasing, or running any gh stack command. Full phase logic lives in docs/playbooks/ship-lane.md. --- @@ -25,7 +27,66 @@ Print a compact status line each iteration (no banner): ship · iter 2/5 · PR #184 · POLL → DECIDE → FIX → MERGE · FIXING CI (test-desktop 3) + 2 comments ``` -**Invocation:** `/ship` (auto-detect state) or `/ship `. +Stack mode prints the layer and its terminal instead of `MERGE`: + +``` +ship · stack 12 layer 2/5 · iter 2/5 · PR #1007 · POLL → DECIDE → FIX → READY · FIXING CI (windows-foundation) + 1 comment +``` + +**Invocation:** `/ship` (auto-detect state), `/ship `, or the opt-in +`/ship --stack-ready [] --base `. + +### Stack-ready mode (opt-in only) + +`--stack-ready` drives one layer of a coordinator-owned stack to *ready*, not to +*merged*. It is the same loop — Phase 0 through Phase 5, the same poll/fix +machinery, the same 5-iteration budget — with merging and every stack-wide +operation removed. Resolve the direct parent from `--base`, an existing PR's +`baseRefName`, then non-interactive `gh stack view --json`; normalize it with +the `/quality` rules. Persist `mode: "stack"` plus the complete stack binding: +stack number, size, position, expected parent branch, validated head SHA, base +SHA, content-tree SHA, test-evidence SHA, required and deferred proof scenarios, +proof links, and quality/test status. + +**The lane owns its layer; the coordinator owns the stack.** The lane commits +and pushes its own layer branch, opens its PR against the resolved direct parent +when none exists, polls CI and review bots, fixes red CI and verified findings on +its own layer, reruns commit-bound quality revalidation against the exact +resulting head, and repeats until the layer is genuinely clean. A red check on +its own code is work to do, not a reason to stop. + +The lane never merges, never enables auto-merge, never deletes a branch, never +rebases or restacks (`git rebase`, `gh stack sync --remote origin`, `gh stack +rebase --upstack --remote origin`, `gh stack push --remote origin`, and `gh +stack submit --auto --remote origin` are all coordinator-only), never retargets +a PR base, never touches another layer's branch or files, and never enters +force-finalize or any bypass-review path. Before any cap, force-finalize, +rebase, merge, or branch-deletion decision, branch on `mode == "stack"`. + +Escalate only what the lane genuinely cannot do, with exact evidence: +`stack-coordinator-sync-required` (a restack or base retarget is needed — the +parent moved, a lower layer changed, or the PR base is not the direct parent), +`stack-coordinator-fix-required` (the fix belongs to a lower layer, or the +iteration budget is spent and the layer is still red), +`stack-coordinator-pr-required` (the parent branch is missing on `origin`, or PR +creation failed on auth or an unusable base ref), and +`stack-coordinator-merged` (the coordinator already landed it). The playbook's +**Stack escalation states** table is authoritative. None of them is a general +stop at the first red check. + +Write `status: "ready-stacked"` only when the exact head is green, +review-terminal, quality-clean, test-clean, and every mandatory proof scenario +either has a current evidence link bound to the validated head or is recorded in +`deferredProofScenarios` against a named higher layer that exists in this stack. +The top layer defers nothing, and cumulative clean-host/cross-client/release +scenarios never masquerade as lower-layer evidence. A known-missing mandatory +scenario is `blocked` with the scenario ids listed — never `ready-stacked` with +a caveat. Missing or ambiguous stack metadata is `blocked`, not a fallback to +`main`. + +Without `--stack-ready`, every existing `/ship` default and merge behavior is +unchanged: the base is `main`, green work proceeds through Phase 3c, and the +terminal success state is `done-clean` only after merge confirmation. --- @@ -35,7 +96,11 @@ ship · iter 2/5 · PR #184 · POLL → DECIDE → FIX → MERGE · FIXING CI (t commands, decision rules, and bot-ping rules live there. This skill is the runtime-neutral entrypoint and the ADE-specific deltas below. If re-invoked by a scheduled wake, read the state file first; if `status == running`, skip Phase 0 -and go to Phase 1. +and go to Phase 1. If `status == ready-stacked`, revalidate the complete binding +first: when it holds, print the persisted coordinator handoff and exit without +scheduling or mutating anything; when it is stale, external movement exits +`stack-coordinator-sync-required` and this lane's own newer head re-enters the +loop at Phase 1. The playbook's Phase 0 is **checkpoint → commit-bound quality revalidation → push → open PR**. Baseline test generation and the local-CI gate are NOT part @@ -60,7 +125,11 @@ change this branch was not asked to make. Both need the author. - If `/quality` was never run on this lane, or its final gate result is not available in the lane handoff, stop with `blocked`; unknown is not empty. - Any base movement, rebase, conflict resolution, Phase 3b edit, or - force-finalize edit clears all three quality binding fields. Run the + force-finalize edit clears all three quality binding fields. In stack mode, + this lane's own Phase 3b edit clears them and is rebound by revalidation on + the head it then pushes; external movement of the parent, base, or head + instead clears the complete stack binding and returns + `stack-coordinator-sync-required` without rebasing. Run the playbook's single canonical **Commit-bound quality revalidation** procedure before pushing that mutation. - Never enter Phase 3c with a missing or mismatched binding. Revalidate first; @@ -98,10 +167,24 @@ only user-visible output is the per-iteration status line and the final summary. `gh pr checks` / the `ade-pr-workflows` skill — do not hardcode. - **PR creation:** prefer the `ade` CLI (registers the PR in ADE's tracking — lane ↔ PR link, check/comment inventory). `gh pr create --base main --head - --fill` is the fallback, not the default. See the playbook's discovery protocol. + --fill` is the ordinary fallback; stack mode substitutes the persisted direct + parent for `main`. See the playbook's discovery protocol. - **State file:** `.ade/shipLane/.json`. `status`: - `running` | `done-clean` | `done-max` | `blocked`. Rebase rebates the iteration - counter by 2 (floor 0). + `running` | `ready-stacked` | `done-clean` | `done-max` | `blocked`; it also + records `mode` and the complete stack binding. Rebase rebates the iteration counter by 2 + (floor 0). + +**Windows proof gate.** For a Windows-relevant stack entry, require the native +Windows foundation check to be terminal-green on the bound head. Require the +packaged Windows check when packaging or native bundle contents changed. +Computer Use evidence is capability-specific: native OS capture/control may be +explicitly blocked while App Control and proof ingestion remain supported and +tested. Clean-host Stable/Beta coexistence, second-account pipe denial, +restart/reboot, installed-update, and GUI artifacts remain named external proof +blockers until captured; never mark them proven from simulated tests. A stack +entry cannot reach `ready-stacked` while any of them is required at its position +and still uncaptured — record it as `blocked` with the scenario id, or defer it +to a named higher layer in `deferredProofScenarios`. --- @@ -135,11 +218,14 @@ terminal-neutral, and continue. Record it under `inactiveReviewBots`, never If branch protection requires an absent check, Phase 3c will surface that as a merge-policy block. -**Rebase only on real conflicts or a stale quality base.** `behindMain` alone +**Rebase only on real conflicts or a stale quality base.** `behindBase` alone does not normally trigger a rebase. The one safety exception is base movement after quality validation: the final tree is no longer the reviewed head tree, -so rebase and rerun the canonical quality procedure even when GitHub reports a -clean merge. Otherwise, skip needless rebases. +so ordinary merge mode rebases and reruns the canonical quality procedure even +when GitHub reports a clean merge. Stack mode instead invalidates the current +and upstack bindings and returns `stack-coordinator-sync-required`; it never +rebases and never pushes another layer, though it does push its own layer branch +in Phase 0 and Phase 3b. Otherwise, skip needless rebases. **Bot pings by iteration.** Never ping GitHub Copilot and never treat Copilot as an expected review signal; quota exhaustion otherwise leaves the loop waiting @@ -221,6 +307,10 @@ self-resume signal. Either: CI, never delete/skip tests or weaken lint/tsconfig, then merge on green. - **Phase 4/5:** post the iteration's `@codex review` ping after a fix push, update state, schedule the next wake (or stop per harness above). +- **Stack mode:** the same phases run, minus 3a, 3c.1–3c.5, and 3d. Phase 2 + routes remaining fix work to 3b and terminal-green to 3c.0 + (`ready-stacked`); the spent iteration budget escalates via + `stack-coordinator-fix-required` instead of forcing. --- @@ -228,10 +318,11 @@ self-resume signal. Either: | Status | Meaning | |--------|---------| +| `ready-stacked` | Opt-in stacked layer is green, review-terminal, quality/test-clean, and every mandatory proof scenario is linked to the validated head or validly deferred to a named higher layer. The lane fixed its own layer; the coordinator owns restacking, base retargeting, submission, and landing | | `done-clean` | PR merged on main | | `done-max` | 5 normal + 1 force-finalize exhausted, merge genuinely blocked | -| `blocked` | Unrecoverable conflict, gate failure, API error, force-finalize CI failed, or a non-empty `/quality` gate awaiting an author decision | +| `blocked` | Unrecoverable conflict, gate failure, API error, force-finalize CI failed, a non-empty `/quality` gate awaiting an author decision, a missing mandatory proof scenario, or a `stack-coordinator-*` escalation | Always print the final summary (PR, branch, iterations, status, reason, per-iteration log, unaddressed items) on exit. Do NOT schedule a wake when -`status` is `done-clean` / `done-max` / `blocked`. +`status` is `ready-stacked` / `done-clean` / `done-max` / `blocked`. diff --git a/.agents/skills/test/SKILL.md b/.agents/skills/test/SKILL.md index 1c805b341..5660e1fa6 100644 --- a/.agents/skills/test/SKILL.md +++ b/.agents/skills/test/SKILL.md @@ -66,7 +66,65 @@ Run end-to-end without user interaction. Do not ask, pause, or request clarifica **Do all the work yourself in the main loop.** Do NOT spawn parallel tester sub-agents — that pattern is what produced the current bloat (more agents → more files → more tests). One agent, one judgment. -**Argument:** `$ARGUMENTS` — optional feature hint (e.g. `/test prs` or `/test orchestrator, focus on merge queue`). If empty, infer the feature from `git diff main --name-only` **plus `git status --short`** (the latter catches new untracked test/source files that `git diff main` omits). +**Arguments:** `$ARGUMENTS` — optional feature hint plus optional +`--base ` (for example `/test prs --base codex/stack-parent`). If the +feature hint is empty, infer it from `git diff "$TEST_REVIEW_BASE" --name-only` +**plus `git status --short`** (the latter catches new untracked test/source files +that the tracked diff omits). + +### Review scope (ordinary and stacked PRs) + +Resolve the review base once before Pass 1. Use the same precedence and +normalization as `/quality`: explicit `--base`, existing PR `baseRefName`, the +current parent from `gh stack view --json`, trusted `ADE_REVIEW_BASE_REF`, then +`main`. Normalize `refs/heads/`, `refs/remotes/origin/`, `origin/`, and plain +branch spellings to a validated plain name. Reject other remotes, symbolic refs, +revision syntax, or anything failing `git check-ref-format --branch`. + +Do not stop after an existing PR supplies `baseRefName`; still query `gh stack +view --json`. Set `TEST_EXACT_BASE=true` when the current branch is in that +stack (including a bottom layer targeting `main`) or when the PR targets a +non-default branch. Only an ordinary unstacked PR targeting the repository +default branch retains merge-base behavior. PR and stack parent names/SHAs must +agree when both exist. + +```bash +# TEST_BASE_REF is the validated, normalized plain branch name selected above. +# TEST_EXACT_BASE is true for --base, stack metadata/trusted state, a +# non-default PR base, or a PR confirmed in gh-stack. +git check-ref-format --branch "$TEST_BASE_REF" +git fetch origin "refs/heads/$TEST_BASE_REF:refs/remotes/origin/$TEST_BASE_REF" +TEST_BASE_SHA=$(git rev-parse "origin/$TEST_BASE_REF") +if [ "$TEST_EXACT_BASE" = true ]; then + git merge-base --is-ancestor "$TEST_BASE_SHA" HEAD || { + echo "stack-coordinator-sync-required: direct parent is not an ancestor of HEAD" + exit 1 + } + TEST_REVIEW_BASE="$TEST_BASE_SHA" +else + TEST_REVIEW_BASE=$(git merge-base HEAD "$TEST_BASE_SHA") +fi +``` + +Every pass below uses `TEST_REVIEW_BASE`. It must be the current stack entry's +direct parent. When stack metadata is available, require its parent SHA to +equal `TEST_BASE_SHA`; a branch-name match is insufficient. Record parent +branch/SHA, merge-base, exact tested head SHA and +tree SHA, test-evidence SHA, status, and proof links in the summary. Any commit, +rebase, branch change, or lower-parent movement invalidates that evidence and +all evidence above it. A missing or unfetchable parent is a blocker, not +permission to fall back to `main`. + +### Host parity and evidence binding + +Classify affected behavior across **Windows**, **macOS**, **Linux/headless**, +**iOS**, and **hosted web**. Mark each host applicable, capability-blocked, or +not applicable with a concrete reason; do not use one desktop run as proof for +the matrix. GUI proof requires both (1) direct UI observation and (2) an +independent corroborating log, database, process, IPC, or network signal. Bind +every artifact/link to the exact tested commit SHA and content-tree SHA. A new +commit or rebase makes prior GUI and Computer Use evidence stale even when the +visible diff looks unrelated. --- @@ -238,13 +296,13 @@ Spawn a general-purpose agent with this prompt: ``` You are the documentation updater for the ADE project. -Analyze all changes on the current branch vs main and update relevant internal +Analyze all changes on the current branch vs the resolved review base and update relevant internal docs under `docs/`. The public Mintlify site (docs.json + root-level .mdx files) is out of scope — do NOT touch it. Step 1: Get changed files - git diff main --name-only - git diff main --stat | tail -30 + git diff "$TEST_REVIEW_BASE" --name-only + git diff "$TEST_REVIEW_BASE" --stat | tail -30 Step 2: Map changed source to internal docs @@ -311,14 +369,14 @@ Spawn a general-purpose agent with this prompt: ``` You are the mobile parity reviewer for the ADE project. -Analyze all work on the current branch vs main, including changes that are +Analyze all work on the current branch vs the resolved direct review base, including changes that are already under review and any simplifications made during `/finalize`. Determine whether the iOS companion app under `apps/ios/` needs matching updates. Step 1: Get branch context - git diff main --name-only - git diff main --stat | tail -30 - git log main..HEAD --oneline + git diff "$TEST_REVIEW_BASE" --name-only + git diff "$TEST_REVIEW_BASE" --stat | tail -30 + git log "$TEST_REVIEW_BASE"..HEAD --oneline Step 2: Identify cross-platform changes - Shared contracts: apps/desktop/src/shared/**, preload IPC types, sync payloads, @@ -386,9 +444,9 @@ must change with it. Your job is to detect drift on this branch and patch apps/ade-cli/ so the CLI stays in lockstep with desktop. Step 1: Get branch context - git diff main --name-only - git diff main --stat | tail -30 - git log main..HEAD --oneline + git diff "$TEST_REVIEW_BASE" --name-only + git diff "$TEST_REVIEW_BASE" --stat | tail -30 + git log "$TEST_REVIEW_BASE"..HEAD --oneline Step 2: Identify CLI-relevant desktop changes Treat anything under these paths as a candidate for new / changed / removed @@ -467,8 +525,8 @@ commonly because a new git/lane/PR action becomes available, a slash command is renamed, or a lane summary field is added. Step 1: Get branch context - git diff main --name-only - git diff main --stat | tail -30 + git diff "$TEST_REVIEW_BASE" --name-only + git diff "$TEST_REVIEW_BASE" --stat | tail -30 Step 2: Identify TUI-relevant changes. Treat as candidates: - apps/desktop/src/shared/types/lanes.ts, /chat, /sync — TUI imports these directly. @@ -510,6 +568,32 @@ Report: Wait for all four parity agents to complete before moving to Verification. +### Windows parity and Computer Use evidence + +If the review scope touches paths, processes, executables, local IPC, native +SQLite, startup services, or Computer Use, the test summary must include a +Windows evidence ledger: + +- Run the narrow contract tests locally with injectable `win32`, `darwin`, and + `linux` cases. Native Windows CI must repeat the Windows-sensitive files on a + `windows-latest` runner; a Linux simulation alone is insufficient. +- Prove Stable/Beta service identity, per-user/channel pipe naming and listen + restrictions, `.exe` and argument-array launch resolution, supervisor + restart/backoff, runtime readiness and stale-PID diagnostics, and packaged + SQLite/CRR loading whenever those owners changed. +- For Computer Use, list evidence by capability. Windows may explicitly report + native screenshot/video/OS GUI control as unavailable; do not treat that as + evidence that App Control or proof-file ingestion is unavailable. Test those + platform-neutral paths independently. +- Record external evidence honestly. Installed Stable/Beta coexistence, + second-account named-pipe denial, reboot/restart recovery, signed/installed + upgrades, and real GUI captures require the corresponding Windows hosts. + Attach artifact paths when available and list the missing proof as a blocker + when it is not. Never replace host proof with a mocked assertion. +- Preserve macOS/Linux parity with parameterized contract tests and the + existing CI shards. A Windows-specific pass does not waive regression + coverage for launchd, Unix sockets, or Linux capability degradation. + --- ## Verification diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d41d0f75b..e892af225 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,8 +104,10 @@ jobs: apps/push-relay/node_modules key: nm-v2-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/webhook-relay/package-lock.json','apps/push-relay/package-lock.json') }} - run: cd apps/ade-cli && npm run typecheck - - name: Test release runtime archive and packaging guards - run: node --test apps/ade-cli/scripts/native-archive-verification.test.mjs apps/desktop/scripts/mac-runtime-archive-mode.test.mjs apps/desktop/scripts/packaged-ade-cli-resources.test.mjs + # scripts/validate-docs.test.mjs covers the docs validator that the + # validate-docs job runs. + - name: Test release runtime archive, packaging, and docs-validator guards + run: node --test apps/ade-cli/scripts/native-archive-verification.test.mjs apps/desktop/scripts/mac-runtime-archive-mode.test.mjs apps/desktop/scripts/packaged-ade-cli-resources.test.mjs scripts/validate-docs.test.mjs scripts/validate-platform-gates.test.mjs typecheck-web: needs: install @@ -405,6 +407,110 @@ jobs: if-no-files-found: error compression-level: 0 + # Native Windows source gate for the filesystem, process, IPC, SQLite, and + # capability contracts that Linux-hosted unit jobs cannot exercise. + windows-foundation: + runs-on: windows-latest + timeout-minutes: 25 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: | + apps/desktop/package-lock.json + apps/ade-cli/package-lock.json + + - name: Install ADE CLI dependencies + run: npm --prefix apps/ade-cli ci + + - name: Install desktop dependencies + run: npm --prefix apps/desktop ci + + - name: Typecheck Windows runtime foundations + run: | + npm --prefix apps/ade-cli run typecheck + npm --prefix apps/desktop run typecheck + + - name: Test Windows service, layout, and IPC contracts + run: >- + cd apps/ade-cli && npx vitest run + src/bootstrap.test.ts + src/serviceManager/common.test.ts + src/serviceManager/installWindows.test.ts + src/serviceManager/windowsSupervisor.test.ts + src/services/builtInBrowser/desktopBridgeClient.test.ts + src/services/modelPickerStore.test.ts + src/services/projects/machineLayout.test.ts + src/services/projects/projectIconResolver.test.ts + src/services/projects/projectRegistry.test.ts + src/services/sync/syncHostSingleton.test.ts + src/services/sync/syncLoopbackCollision.test.ts + src/services/sync/syncService.test.ts + + - name: Test intended-user named-pipe listener contracts + run: cd apps/ade-cli && npx vitest run src/services/runtime/localIpcListenOptions.test.ts + + # Sync-port holder identification is win32-gated: on Windows the holder + # lookup goes through PowerShell rather than lsof/ps, and a broken lookup + # silently disables stale-port reclaim instead of failing. Only a Windows + # runner executes that gate. + - name: Test Windows sync-port holder identification + run: cd apps/ade-cli && npx vitest run src/services/sync/sharedSyncListener.test.ts + + # `trustedWindowsTools` is a security control whose only substantive case + # is win32-gated, so before this step it ran on no runner at all. The + # credential store and the `ade://` deeplink command-injection guard are + # both filesystem- and argument-quoting sensitive, which is exactly what + # a Linux-hosted job cannot exercise. + - name: Test Windows trusted-tool, credential, and deeplink guards + run: >- + cd apps/ade-cli && npx vitest run + src/commands/deeplinks.test.ts + src/lib/trustedWindowsTools.test.ts + src/services/credentials/credentialStore.test.ts + + - name: Test Windows desktop, SQLite, and capability contracts + run: cd apps/desktop && npx vitest run src/main/packagedRuntimeSmoke.test.ts src/main/services/computerUse/localComputerUse.test.ts src/renderer/lib/platform.test.ts + + # The suites docs/development/windows-port-lane.md names as the Windows + # validation set. Before this step the intersection with this job was + # empty, so the documented gate was never actually run on Windows. + - name: Test Windows path, spawn, window, and update contracts + run: >- + cd apps/desktop && npx vitest run + src/main/services/appControl/appControlService.test.ts + src/main/services/shared/processExecution.test.ts + src/main/services/updates/autoUpdateService.test.ts + src/main/windowAppearance.test.ts + src/renderer/lib/pathUtils.test.ts + + # Covers the Windows runtime startup timing and `connectSpawnedRuntime` + # retry path. This suite spawns real `ade serve` daemons and connects to + # them over the platform transport, so on this runner it is the only gate + # that exercises the named-pipe endpoint end to end. + - name: Test Windows local runtime connection pool contracts + run: cd apps/desktop && npx vitest run src/main/services/localRuntime/localRuntimeConnectionPool.test.ts + + # The CRDT/sync layer. These suites are gated on `isCrsqliteAvailable()`, + # which resolves `vendor/crsqlite/-/`. Only darwin-arm64, + # darwin-x64, and win32-x64 are vendored, so on the ubuntu-latest + # `test-desktop` job the gate is false and 57 tests skip silently — a + # green checkmark over unrun CRR replication, sync host, sync service, + # and device registry coverage. windows-latest is the only runner in this + # workflow that has the extension, so this is where those tests actually + # execute. + - name: Test Windows CRDT, sync, and device registry contracts + run: >- + cd apps/desktop && npx vitest run + src/main/services/state/kvDb.test.ts + src/main/services/state/kvDb.migrations.test.ts + src/main/services/state/kvDb.rebuildRecovery.test.ts + src/main/services/state/kvDb.sync.test.ts + src/main/services/sync/deviceRegistryService.test.ts + src/main/services/sync/syncHostService.test.ts + src/main/services/sync/syncService.test.ts validate-docs: needs: install runs-on: ubuntu-latest @@ -426,6 +532,11 @@ jobs: apps/push-relay/node_modules key: nm-v2-${{ hashFiles('apps/desktop/package-lock.json','apps/ade-cli/package-lock.json','apps/web/package-lock.json','apps/webhook-relay/package-lock.json','apps/push-relay/package-lock.json') }} - run: node scripts/validate-docs.mjs + # Asserts that every platform-gated test assertion has a runner in this + # workflow that actually executes it, and bans the vacuous + # `if (process.platform === ...) return;` form that reports green while + # asserting nothing. Pure source + workflow parsing, no dependencies. + - run: node scripts/validate-platform-gates.mjs # ── Gate: all jobs must pass ────────────────────────────────────────── ci-pass: @@ -449,6 +560,7 @@ jobs: - test-account-directory - build - build-runtime-binaries + - windows-foundation - validate-docs runs-on: ubuntu-latest steps: diff --git a/AGENTS.md b/AGENTS.md index efa0e0507..433384c9f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ Utilities (run when relevant, not part of the core loop): **/audit** (targeted b ## Playbooks - `docs/playbooks/ship-lane.md` — autonomous PR-to-merge driver (poll → fix → rebase → merge). Baseline `/quality` and `/test` run before it; mutation-specific commit-bound quality revalidation runs inside it. Any agent CLI can follow it directly; Claude Code invokes it via the `/ship` skill. +- `docs/playbooks/windows-signed-release.md` — maintainer handoff for taking the gated Windows x64 build through signing, clean-host and installed-update proof, draft verification, publication, and website enablement without changing the macOS or iOS release paths. ## Working norms @@ -49,6 +50,7 @@ Utilities (run when relevant, not part of the core loop): **/audit** (targeted b - `npm --prefix apps/ade-cli run build` - Run the smallest relevant subset first when iterating, then finish with the broader checks that cover the touched surfaces. - Run full desktop tests with the root `npm run test:desktop:sharded` command; use single-file or single-shard Vitest commands for iteration. +- Installing deps: use `npm run install:apps` from the repo root, or `cd apps/ && npm install`. Never `npm --prefix apps/ install`. `--prefix` only redirects where npm writes `node_modules`; the package npm treats as "the one being installed" is still the one in the *current working directory*. From the repo root that is the root package `ade`, so npm installs the repo into the sub-app: it writes `"ade": "file:../.."` into the app's `package.json` and `package-lock.json` and leaves an `apps//node_modules/ade` symlink back to the root. Revert that churn if you hit it. `npm --prefix apps/ run