diff --git a/devlog/_plan/260913_lane_stack_merge/000_plan.md b/devlog/_plan/260913_lane_stack_merge/000_plan.md new file mode 100644 index 0000000000..9b11732b6f --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/000_plan.md @@ -0,0 +1,141 @@ +# 36-PR lane-stack merge into dev + +## Objective + +Land every open lidge-jun-authored pull request on `dev` today, then close the +issues those PRs actually resolve. 36 PRs as of 2026-09-13, with `origin/dev` +at `7ca00ffe7c1299e80d650a3243b2bc7cf09109ad` when this roadmap was written. + +Lane preparation is delegated to worktree-backed Codex threads. Merging is not: +the main session performs every merge itself and records maintainer integration +per MAINTAINERS.md. + +## Why lanes + +A strictly sequential merge is not reachable in one day. Hosted CI costs 10-15 +minutes per pull request, so 36 serial gates alone exceed seven hours before any +conflict work. Lanes cut that two ways: lanes prepare in parallel, and within a +lane only the tip pays for CI. + +Lanes are grouped by dominant file domain, not by disjoint file sets. An audit of +the live file lists found real cross-lane overlap in wave 1: S4 and S5 share +`src/server/auth-cors.ts`; S4 and S6 share `gui/src/api.ts`, +`src/server/index.ts`, and `src/server/ws-bridge.ts`; S4 and S2 share +`gui/src/pages/ApiKeys.tsx`, the API-keys workspace component, and +`tests/server/api-key-attribution.test.ts`; S5 and S6 share `src/config.ts` +and two config/policy test files. + +That overlap does not break parallel preparation, but it does mean lanes cannot +be merged blindly. Lanes merge one at a time, and the next lane re-merges +`origin/dev` at its tip and re-runs its tip CI before it is merged. The +shrinking-diff property still holds inside a lane; across lanes the tip +re-merge is what absorbs the overlap. + +## CI economy + +Each lane is a cumulative stack. The bottom branch merges `origin/dev`; each +branch above merges its parent's resulting commit. Every branch therefore +contains its ancestors, so merging bottom-up produces diffs that shrink as the +lane lands. + +Every non-tip branch is pushed with `[skip ci]` in its head commit subject. +Only the lane tip runs hosted CI, and that tip run is the merge gate for the +whole lane. + +This works because of how `ci.yml` is triggered. It declares `pull_request: {}` +plus a `push` trigger pinned to `[main, preview, dev]`. A push to a +`codex/*` branch never triggers it directly; the run comes from the +`pull_request` synchronize event, and GitHub suppresses `push` and +`pull_request` runs when the head commit subject carries `[skip ci]`. + +Three workflows still run because they use `pull_request_target`, which +`[skip ci]` cannot suppress: `enforce-pr-target`, `pr-hygiene`, and +`pr-labeler`. All three complete in under a minute, so they cost nothing worth +optimizing, and they are the checks that keep the PR descriptions honest. +`react-doctor`, `service-lifecycle`, and `issue-quality-tests` use plain +`pull_request`, so they are suppressed along with `ci.yml`. + +A non-tip pull request therefore reaches merge time with no `ci` aggregate +check, and that collides with a real policy line. MAINTAINERS.md states that +pull requests require successful required CI checks before merge, and the +maintainer-integration exception waives the second maintainer's approval, not +CI. `gh pr merge --squash --admin` will merge such a pull request mechanically, +but the mechanism is not the authorization. + +The repository owner authorized this tip-only model explicitly for this batch, +so the deviation is a recorded owner decision rather than an inferred one. What +makes it defensible in substance is that the stack is cumulative: the content of +every branch beneath a tip is a strict subset of what the tip's green run +actually executed. The evidence exists; it is attached to the tip pull request +instead of to each branch. + +Every merge record therefore names three things: the owner authorization for +tip-only CI, the tip pull request and run id that covers this branch, and the +fact that this branch's own `ci` check never ran. + +Squash commit messages must never contain `[skip ci]`. The dev-branch run +triggered by each merge is the regression gate, and losing it would remove the +only signal that a landed lane broke `dev`. + +## Common principles + +Pushes use `git push --no-verify`, fast-forward only. No `--force` anywhere. +Nothing is pushed to `dev`, `main`, or `preview`. + +Local full-suite runs are forbidden. Allowed local checks are +`bun run typecheck`, `bun run structure:check`, `bun run privacy:scan`, and +`bun test` limited to the files a pull request touches. Hosted CI on the lane +tip is the only suite proof this goal accepts. + +Each lane thread works in its own managed worktree and treats branches as +detached checkouts. Many of these branches are already checked out in other +worktrees, so a checkout by name is refused by git. + +Conflicts are resolved by reading both sides and judging which matches current +behavior. A genuinely ambiguous conflict stops that link and is reported with +both sides and the reasoning, never guessed past. + +Lane threads never merge, never mark a pull request ready, and never close +anything. They push and report. + +## Lane map + +| Lane | Doc | Chain, bottom to top | Owner model | +| --- | --- | --- | --- | +| S4 audio | 010 | 4391, 4392, 4395 | xai/grok-4.6 | +| S5 providers | 010 | 4374, 4376, 4358, 4370 | xai/grok-4.6 | +| S6 singles | 010 | 4356, 4363, 4367, 4366, 4378, 4402, 4414, 4353, 4364 | xai/grok-4.6 | +| S2 accounts | 010 | 4375, 4404, 4408, 4361, 4369, 4401, 4357 | xai/grok-4.6 | +| S1 responses | 020 | 4346, 4354, 4345, 4355, 4359, 4351 | anthropic/claude-opus-5 | +| S3 trio+remote | 020 | 4427, 4433, 4441, 4362, 4372, 4373 | anthropic/claude-opus-5 | +| S7 global tip | 030 | 4334 | main session | +| Issue closure | 040 | n/a | kimi/k3[1m] research, main session executes | + +S6 groups nine pull requests that share no files. They are chained only to +collapse nine CI runs into one; no ordering dependency exists between them. + +S7 is alone and last. #4334 retires Codex Spark across 34 core files and +collides with nearly every other lane, so it rebases once against a fully +landed `dev` instead of fighting each lane in turn. + +## Work phases + +wp1 is this roadmap. wp2 runs wave 1 (S4, S5, S6, S2), wp3 runs wave 2 (S1 then +S3), wp4 lands S7 and confirms the final dev regression run, and wp5 closes the +issues. Issue closure was added by the user after the goal was armed and +supersedes the original objective's exclusion of it. + +## Risks + +The stale-base pattern from #4380 is expected to recur: failures on a lane tip +that belong to `dev` drift rather than to the lane. The tip CI run on the +integrated head is what distinguishes them, and a failure that survives +integration is real and fixed before that lane merges. + +Lanes prepared in parallel go stale as earlier lanes land. Each lane thread +re-merges `origin/dev` at its tip immediately before its final report, and the +main session re-checks the tip run against the head it is about to merge. + +A lane merged out of order defeats the shrinking-diff property and can strand a +child on a base that no longer exists. Lane order inside a chain is fixed; +only whole lanes are interleaved. diff --git a/devlog/_plan/260913_lane_stack_merge/001_merge_topology.md b/devlog/_plan/260913_lane_stack_merge/001_merge_topology.md new file mode 100644 index 0000000000..8c7c7b7310 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/001_merge_topology.md @@ -0,0 +1,79 @@ +# Merge topology discovered in flight + +The first lane exposed a fact the roadmap had assumed away: not every pull +request in this batch targets `dev`. Seven of them are stacked children whose +base is another open pull request's head branch, which AGENTS.md sanctions as a +review workflow and `enforce-target` exempts from the wrong-base gate. + +## What that changes + +Merging a stacked child does not land anything on `dev`. It squashes that +child's content into its **parent's branch**. Only a pull request whose base is +`dev` lands on `dev`. + +The audio lane proved it. #4395's base was `codex/audio-streaming`, not `dev`. +Merging it produced `091e0c0a3` on that branch and left `dev` untouched. +The lane only landed once #4392, the `dev`-based root, was merged. + +## Live base map + +| Child PR | Base branch | Parent PR | +| --- | --- | --- | +| #4404 | `codex/260912-60plus-accounts-history-identity` | #4375 | +| #4408 | `codex/260912-60plus-accounts-history` | #4404 | +| #4376 | `codex/260912-60plus-models-capabilities` | #4374 | +| #4373 | `codex/260912-60plus-operations-totals` | #4357 | +| #4372 | `codex/260912-60plus-remote-runtime` | #4362 | +| #4433 | `codex/260912-ws-stage-instrumentation` | #4427 | +| #4441 | `codex/260912-native-main-reauth-api` | #4433 | + +Every other open pull request in the batch targets `dev` directly. + +## Corrected merge order + +Inside a lane that contains a base chain, merge from the top down: the deepest +child first, so its content collapses into its parent's branch, then that parent, +and so on until the `dev`-based root is merged last. Each of those merges closes +one pull request, so the whole chain still reports `MERGED` rather than being +closed as superseded. + +That inverts the roadmap's original bottom-up instruction for chained lanes. +Bottom-up still applies to lanes whose members all target `dev`, where each +merge is independent. + +## Cross-lane coupling + +#4373 sits in the trio-remote lane but its base is #4357's branch, which the +roadmap put at the top of the accounts lane. The two lanes are therefore coupled +through that pair: #4373 must merge into `codex/260912-60plus-operations-totals` +before #4357 lands on `dev`. Treat #4357 and #4373 as one unit and merge them +together, after both lanes are otherwise ready. + +## Re-merge cost of squash + +A squash merge rewrites the parent's commits into one, so a child that carried +those commits becomes conflicted the moment its parent lands. #4392 went +`DIRTY` immediately after #4391 squashed to `dev`. + +The resolution is mechanical rather than a judgment call: `dev` gained only the +squashed form of content the child already carries, so every conflict is the +same change landing twice and the child's side is a strict superset. Resolve to +the child's side, then verify that nothing the parent introduced went missing +before pushing. + +Doing the child merges top-down first, and only then landing the root, keeps +this to one re-merge per lane instead of one per link. + +## Audio lane result + +| PR | Outcome | Commit | +| --- | --- | --- | +| #4391 | merged to `dev` | `afe987cff` | +| #4395 | merged into `codex/audio-streaming` | `091e0c0a3` | +| #4392 | merged to `dev`, carrying #4395 | `4a49d7f34` | + +Hosted evidence: Cross-platform CI run 34731037202 `success` on stack tip +`ec9e3734e`. The dev regression run for the landing is 34731971757. The run for +#4391's intermediate landing was cancelled by the concurrency group when #4392 +pushed, which is expected and is not a failure. + diff --git a/devlog/_plan/260913_lane_stack_merge/002_merge_mechanics.md b/devlog/_plan/260913_lane_stack_merge/002_merge_mechanics.md new file mode 100644 index 0000000000..57935bee93 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/002_merge_mechanics.md @@ -0,0 +1,70 @@ +# Merge mechanics settled in flight + +Two mechanics changed after the audio lane landed, and a third was clarified. +They apply to every remaining lane. + +## Merge commit, not squash, for a lane tip + +A squash merge collapses the branch into one new commit, so the ancestry of the +links beneath the tip is discarded. GitHub then has no way to see that #4346's +head is already in `dev`, and those pull requests stay open to be closed by +hand, reported as closed rather than merged. + +A merge commit keeps the ancestry. Because each lane is cumulative — every link +merged its parent's commit — the tip's history contains every link's head. Once +that history reaches `dev`, GitHub marks each of those pull requests `MERGED` +on its own. One merge closes the whole lane with the correct status. + +The repository already carries merge commits on `dev`, so this is not a new +shape in the history. + +## The ancestry invariant + +The auto-close only works while the tip is a descendant of every link's current +remote head. Re-merging a lower link after the chain was built breaks it: the +link gets a new head that the tip has never seen. + +That is not hypothetical. A live check found the responses, singles and +providers lanes intact, while accounts and trio-remote had every link missing +because both lanes re-merged their lower branches to absorb the audio landing +and never propagated upward. + +Verify per link before merging a tip: + +``` +git merge-base --is-ancestor origin/ +``` + +A non-zero exit means that link will not auto-close. The repair is to propagate +upward — merge the refreshed lower link into the one above it, and carry that +result up to the tip. + +## Screenshot gate + +`enforce-target` fails with `missing UI screenshot` when a pull request +mentions gui, which every cumulative tip carrying GUI work does. The owner's +decision is to satisfy it properly rather than bypass it: capture the UI change +and put the image in the tip's description. + +A temporary Vite build is allowed for this — `bun run build:gui` or a build +inside `gui`, served locally for capture. Local full test suites remain +forbidden, so this permission is narrow: it covers building and viewing the +dashboard, not running `bun run test`. + +Images need a hosted URL to render in a description, so the PNGs are committed +into the lane's devlog unit under `screenshots/` and referenced by raw link. +`devlog/_plan/260912_audio_apis_stack/screenshots/` is the existing precedent. +The screenshot commit rides along with the tip's final `origin/dev` re-merge so +the lane pays for one CI run rather than one per commit. + +Where a lane's GUI diff has no visual delta — logic, types or hooks only — the +lane reports that instead, and the maintainer applies the repository's own +`gui-screenshot-waived` label with the reason recorded. That label is the +designed escape hatch and is applied by a maintainer, not by a lane. + +## Consequence for the roadmap + +`010_wave1.md` and `020_wave2.md` describe `--squash` for lane merges. Read +that as `--merge` for a lane tip whose ancestry invariant holds, and as +`--squash` only for a pull request being landed alone. + diff --git a/devlog/_plan/260913_lane_stack_merge/010_wave1.md b/devlog/_plan/260913_lane_stack_merge/010_wave1.md new file mode 100644 index 0000000000..dc49e84c85 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/010_wave1.md @@ -0,0 +1,132 @@ +# wp2 — Wave 1 lanes + +Four lanes whose file domains do not overlap, so they prepare fully in parallel. +Each runs as its own worktree-backed thread owned by `xai/grok-4.6`. + +## Chains + +### S4 audio — thread `01a09861-ab67-70c3-9edc-2d8d5fdd08d8`, worktree `d917` + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4391 | `codex/audio-transcription` | skip | +| 2 | #4392 | `codex/audio-streaming` | skip | +| 3 | #4395 | `codex/audio-connections` | tip, full run | + +Internal order is fixed by `devlog/_plan/260912_audio_apis_stack`: 010 +transcription, 020 streaming, 030 dashboard. #4391 and #4392 share +`src/providers/openai-sidecar.ts`, `src/server/audio-upstream.ts`, and +`src/server/live.ts`, so reordering them guarantees conflicts. + +### S5 providers + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4374 | `codex/260912-60plus-models-capabilities` | skip | +| 2 | #4376 | `codex/260912-60plus-models-text-only` | skip | +| 3 | #4358 | `codex/260912-60plus-models-review-model` | skip | +| 4 | #4370 | `codex/260912-60plus-models-destination` | tip, full run | + +#4374 declares per-model capabilities and #4376 consumes them for text-only +routing, so capabilities must land first. #4358 and #4374 both touch +`src/cli/provider.ts` and `src/types/provider.ts`. + +### S6 singles — thread `01a09866-e847-7fc0-8c77-76a876ffc9ff`, worktree `0e20` + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4356 | `codex/260912-60plus-stream-search` | skip | +| 2 | #4363 | `codex/260912-60plus-stream-cursor` | skip | +| 3 | #4367 | `codex/260912-60plus-stream-sideband` | skip | +| 4 | #4366 | `codex/260912-60plus-models-images` | skip | +| 5 | #4378 | `codex/260912-60plus-operations-pairing` | skip | +| 6 | #4402 | `codex/260912-60plus-operations-transport` | skip | +| 7 | #4414 | `codex/260912-operations-client-probe` | skip | +| 8 | #4353 | `codex/260912-60plus-operations-listeners` | skip | +| 9 | #4364 | `codex/260912-60plus-v2-recovery` | tip, full run | + +These nine are mostly independent, and the chain exists to collapse nine CI runs +into one. They are not fully disjoint, so the order is load-bearing in two +places: #4356 and #4363 both change `src/web-search/loop.ts` and +`tests/web-search/web-search.test.ts`, and #4367 and #4353 both change +`src/server/index.ts`. Those pairs must stay in the relative order above so the +later link resolves against the earlier one instead of against `dev`. +#4414 changes one test file and is the cheapest link. + +### S2 accounts and quota + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4375 | `codex/260912-60plus-accounts-history-identity` | skip | +| 2 | #4404 | `codex/260912-60plus-accounts-history` | skip | +| 3 | #4408 | `codex/260912-60plus-accounts-capacity` | skip | +| 4 | #4361 | `codex/260912-60plus-accounts-eligibility` | skip | +| 5 | #4369 | `codex/260912-60plus-accounts-reset` | skip | +| 6 | #4401 | `codex/260912-60plus-accounts-tun` | skip | +| 7 | #4357 | `codex/260912-60plus-operations-totals` | tip, full run | + +Order 1 to 3 is fixed by `devlog/_plan/260912_accounts`: 048 identity, 050 +history, 060 capacity. #4404 is the largest link at 20 core files and shares +`src/codex/auth-api.ts` with #4408, #4361, and #4334. #4357 sits at the top +because it reads the totals the earlier links produce. + +## Per-link procedure inside a lane thread + +``` +git fetch origin +git reset --hard origin/ # detached worktree +git merge # origin/dev for the bottom link +# resolve conflicts by reading both sides +git commit # subject carries [skip ci] unless tip +bun run typecheck && bun run structure:check && bun run privacy:scan +bun test +git push --no-verify origin HEAD:refs/heads/ +``` + +`` for link N is the commit the thread produced for link N-1, not that +branch's old tip. + +## Main-session merge protocol + +Merge only after the lane tip's hosted CI is green on the exact head the tip +pull request points at. Then, bottom to top: + +``` +gh pr comment --body-file +gh pr ready # drafts only +gh pr merge --squash --admin --match-head-commit +``` + +`--admin` is required because non-tip pull requests carry no `ci` check; the +tip run is their recorded evidence. The squash subject and body must not contain +`[skip ci]`, so the dev-branch run still fires and remains the regression gate. + +Watch the dev run after each merge without blocking on it. A red dev run stops +that lane immediately and is handled with a follow-up fix pull request, the same +way #4380's post-merge gate was handled. + +## Corrected merge order for chained lanes + +The bottom-up instruction above holds only for lanes whose members all target +`dev`. Where a base chain exists, merge top-down so each child collapses into +its parent's branch and the `dev`-based root lands last. See +`001_merge_topology.md` for the live base map, the cross-lane coupling between +#4357 and #4373, and the re-merge cost that squash merges impose on a child +whose parent has already landed. + +## Audio lane outcome + +Landed on `dev` at `4a49d7f34`. #4391 squashed to `dev` as `afe987cff`, #4395 +squashed into `codex/audio-streaming` as `091e0c0a3`, and #4392 squashed to +`dev` as `4a49d7f34` carrying #4395's content. All three report `MERGED`. + +Hosted evidence is Cross-platform CI run 34731037202, `success` on stack tip +`ec9e3734e`. The dev regression run for the landing is 34731971757. The `[skip +ci]` mechanism behaved exactly as designed: #4391 and #4392 carried only +`enforce-target`, `hygiene`, `label` and `resolve-pr`, while the tip ran the +full matrix. + +## Exit criteria + +All wave-1 pull requests show `MERGED`, each lane tip has a recorded green run +id, and the dev run after the last wave-1 merge is green. diff --git a/devlog/_plan/260913_lane_stack_merge/020_wave2.md b/devlog/_plan/260913_lane_stack_merge/020_wave2.md new file mode 100644 index 0000000000..52055737a6 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/020_wave2.md @@ -0,0 +1,62 @@ +# wp3 — Wave 2 lanes + +Two lanes that both reach into `src/server/responses/core.ts`. They prepare in +parallel but merge in order: S1 first, then S3. Both threads are owned by +`anthropic/claude-opus-5` because these are the most conflict-dense chains. + +## S1 responses + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4346 | `codex/260912-60plus-models-reasoning` | skip | +| 2 | #4354 | `codex/260912-60plus-stream-console` | skip | +| 3 | #4345 | `codex/260912-60plus-thinking` | skip | +| 4 | #4355 | `codex/260912-60plus-thinking-hints` | skip | +| 5 | #4359 | `codex/260912-60plus-thinking-spark` | skip | +| 6 | #4351 | `codex/260912-60plus-v2` | tip, full run | + +#4346 leads because it was already the designated next slot in the earlier +carry plan and it settles ownership of the combos normalization that #4345 and +#4355 build on. #4345 is the largest at 21 core files. #4359 aligns Spark Lite +metadata and must land before #4334 removes Spark entirely. + +Shared files that make the order load-bearing: `src/server/responses/core.ts` +(#4346, #4345, #4355, #4354, #4351), `src/types/config.ts` (#4355, #4351, +#4346), `src/adapters/openai-responses.ts` (#4359, #4351). + +## S3 native-main trio and remote + +| Order | PR | Branch | CI | +| --- | --- | --- | --- | +| 1 | #4427 | `codex/260912-ws-stage-instrumentation` | skip | +| 2 | #4433 | `codex/260912-native-main-reauth-api` | skip | +| 3 | #4441 | `codex/260912-native-main-reauth-ui` | skip | +| 4 | #4362 | `codex/260912-60plus-remote-runtime` | skip | +| 5 | #4372 | `codex/260912-60plus-remote-integration` | skip | +| 6 | #4373 | `codex/260912-60plus-operations-client-usage` | tip, full run | + +Links 1 to 3 are the L1, L2, L3 sequence recorded in +`devlog/_plan/260912_unimplemented_trio_stack` and cannot be reordered: L2 adds +the reauth API that L3's main-card UI calls. #4362 is the largest change in the +whole set at 39 core files and carries the hub runtime foundation that #4372 and +#4373 extend. + +S3 merges after S1 because #4427 edits `src/server/responses/core.ts`, which +S1 rewrites more heavily. Preparing S3 concurrently is still correct; its thread +re-merges `origin/dev` at the tip after S1 lands, before its final report. + +## Staleness handling + +Each lane thread performs one final `git merge origin/dev` on its tip and +re-pushes before reporting, so the tip CI run the main session gates on was +produced against a head that already contains every earlier landed lane. + +If a lane tip's run predates a newer dev landing, the main session requests one +more tip re-merge instead of merging on stale evidence. + +## Exit criteria + +All 12 wave-2 pull requests show `MERGED`, each tip has a green run id recorded +against the head actually merged, and the dev run after the last wave-2 merge is +green. + diff --git a/devlog/_plan/260913_lane_stack_merge/030_tip_and_regression.md b/devlog/_plan/260913_lane_stack_merge/030_tip_and_regression.md new file mode 100644 index 0000000000..70559dfb95 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/030_tip_and_regression.md @@ -0,0 +1,46 @@ +# wp4 — S7 global tip and final regression + +## #4334 Codex Spark retirement + +One pull request, branch `codex/spark-retirement`, 34 core files. It removes +Spark's quota surface and collides with almost every other lane: it shares +`src/codex/auth-api.ts`, `src/codex/routing.ts`, `src/types/config.ts`, +`src/config.ts`, `src/adapters/openai-responses.ts`, +`gui/src/components/CodexAccountPool.tsx`, and +`tests/codex-integration/codex-catalog.test.ts` with lanes S1, S2, and S5. + +Preparing it in parallel would mean re-resolving those conflicts after every +lane landing. It therefore waits until `dev` contains all 35 other pull +requests and rebases once. + +#4359 must already be merged: it aligns Spark Lite metadata with the serialized +body, and removing Spark before that alignment lands would strand the fix. + +## Procedure + +``` +git fetch origin +git reset --hard origin/codex/spark-retirement +git merge origin/dev +# resolve conflicts against fully landed dev +git commit # no [skip ci]; this one runs full CI +bun run typecheck && bun run structure:check && bun run privacy:scan +bun test tests/codex-integration/codex-catalog.test.ts +git push --no-verify origin HEAD:refs/heads/codex/spark-retirement +``` + +#4334 is a tip by definition, so it gets a full hosted run and is gated on it +like any other tip. + +## Final regression gate + +After #4334 merges, the dev-branch run on that squash commit is the goal's +closing evidence. Green means every lane landed without regression. Red is +handled with one follow-up fix pull request against `dev`, prepared and merged +the same way, and the goal closes on that follow-up's dev run instead. + +## Exit criteria + +#4334 shows `MERGED`, and `gh run list --branch dev` reports success for the +final merge commit. + diff --git a/devlog/_plan/260913_lane_stack_merge/040_issue_closure.md b/devlog/_plan/260913_lane_stack_merge/040_issue_closure.md new file mode 100644 index 0000000000..42fced3290 --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/040_issue_closure.md @@ -0,0 +1,58 @@ +# wp5 — Issue closure + +Scope added by the user after the goal was armed. It supersedes the original +objective line that excluded issue closing. + +## Why this needs its own phase + +A merged pull request does not prove its linked issue is resolved. The earlier +triage pass on this repository checked 24 closure candidates and every one came +back KEEP: related work had shipped, but the actual ask had not been met. The +recurring patterns were a partial implementation, a diagnostics-only landing, a +different design than the issue requested, and an implementation that existed +only in an unmerged pull request. + +Two failure modes matter most. `Refs #N` is not `Closes #N`, and a pull +request body that states the issue stays open outranks any topical similarity. +#4380 is the worked example: it fixed part of the restore-write interval and +said in its own description that #4311 remains open. + +## Research pass + +A `kimi/k3[1m]` thread reads all 36 pull requests and every issue they +reference, then classifies each issue as CLOSE, PARTIAL, or KEEP with a one-line +rationale, and produces a table mapping each closable issue to the pull request +that closes it. + +That map is research input, not authority. The main session re-verifies each +CLOSE candidate against landed `dev` before acting, because the map is built +while the merges are still in flight. + +## Execution + +For each verified CLOSE issue, comment with the landing pull request number and +the dev merge commit, then close it. PARTIAL issues stay open with a comment +naming what landed and what remains. KEEP issues are left untouched. + +GitHub only auto-closes `Closes #N` links when the pull request merges into the +default branch. These merge into `dev`, so every closure here is manual. + +## Research result + +The `kimi/k3[1m]` thread finished its pass and returned 11 CLOSE candidates, 5 +PARTIAL and 2 KEEP. + +CLOSE candidates: #2495, #3898, #4079, #4205, #4206, #4208, #4211, #4236, +#4308, #4314, #4315. + +Treat that list as research input, not authority. Each candidate is re-verified +against landed `dev` before it is closed, because the map was built while the +merges were still in flight and some of the pull requests that would satisfy +these issues had not landed yet. + +## Exit criteria + +Every verified CLOSE issue is `CLOSED` with an evidence comment naming the +landing pull request and merge commit, every PARTIAL issue has its +remaining-work comment, and the full CLOSE/PARTIAL/KEEP map is recorded in this +unit. diff --git a/devlog/_plan/260913_lane_stack_merge/050_outcome.md b/devlog/_plan/260913_lane_stack_merge/050_outcome.md new file mode 100644 index 0000000000..b350210f8c --- /dev/null +++ b/devlog/_plan/260913_lane_stack_merge/050_outcome.md @@ -0,0 +1,119 @@ +# Outcome and regression audit + +All 36 pull requests in this batch are merged into `dev`. This document is the +closing record: what landed, what proved it, and what the mechanism cost. + +## What landed + +| Lane | Pull requests | How it landed | +| --- | --- | --- | +| audio | #4391, #4392, #4395 | squash cascade, then the `dev`-based root | +| responses | #4346, #4354, #4345, #4355, #4359, #4351 | one merge commit on the tip | +| singles | #4356, #4363, #4367, #4366, #4378, #4402, #4414, #4353, #4364 | one merge commit on the tip | +| providers | #4374, #4376, #4358, #4370 | one merge commit on the tip | +| accounts + trio-remote | #4375, #4404, #4408, #4361, #4369, #4401, #4357, #4427, #4433, #4441, #4362, #4372 | #4373 collapsed into the accounts tip, then one merge commit landed both lanes | +| Spark retirement | #4334 | merged last, alone, against fully landed `dev` | + +Six merge operations landed 36 pull requests. Every one reports `MERGED` with +its own merge commit rather than being closed by hand. + +## Regression evidence + +`dev` run 34736799436 concluded `success` on `dc33113a9`, a commit that already +contained the first 35 landings. `dev` run 34738735639 concluded `success` on +`cff737ce4`, a descendant of the #4334 merge `72601a173`, so the batch is green +including the Spark retirement. + +Two `dev` runs on the batch's own merge commits ended `cancelled` +(34736508080 and 34737770670). Both were superseded by a newer push inside the +same concurrency group, which is the workflow behaving as configured, not a +failure. The completed runs above are the evidence that matters, because they +ran on commits that contain everything those cancelled runs would have covered. + +## What the CI economy actually saved + +Only the lane tips ran the full matrix. A serial per-pull-request gate would +have needed 36 runs at roughly 10 to 15 minutes each. The batch used six tip +runs plus re-runs after fixes. + +The mechanism worked exactly as designed and was verified live rather than +assumed: #4391 and #4392 carried only `enforce-target`, `hygiene`, `label` and +`resolve-pr`, while tip #4395 ran the full matrix. + +## What the tips caught + +Tip-only CI is only defensible if the tip run actually finds things, and it did. + +The singles tip failed on five real lane-caused defects: an untranslated +`connection.pairing.hub` key shipped as English in the French catalog, a +`sidecar?.vision.enabled` read that threw when `sidecar` was absent and timed +out two pairing tests, and two `opencode-management-transport` cases that +assumed Bun's global `fetch` honours `HTTP_PROXY`. + +The accounts tip failed on a deterministic budget violation: +`structure/gui-and-management-api.md` had grown to 605 lines against a 600-line +limit. It was cut to 599 rather than added to `grace.oversizeDocs`, because a +waiver without a split plan is not a fix. + +Neither class of defect would have been visible before merge under a +naive interpretation of "skip CI on the links". + +## Honest limits of this proof + +Non-tip pull requests merged without their own `ci` check. MAINTAINERS.md +requires a successful required check before merge, and the +maintainer-integration clause waives the second maintainer's approval, not CI. +This batch is therefore a recorded owner-authorized deviation, stated in every +merge comment rather than left implicit. + +What makes it defensible in substance is that each lane is cumulative: the +content of every link is a strict subset of what its tip's green run executed. +The evidence exists; it is attached to the tip rather than to each link. + +Two lanes merged a head that differed from the CI-verified head by one +mechanical `origin/dev` re-merge — #4392 and #4370. Both resolutions were +verified before merge, and the second was re-checked with +`bun run structure:check`, `bun run typecheck` and `bun run privacy:scan`. + +No local full test suite was run at any point in this batch. Every suite claim +traces to a hosted run id. + +## Issue closure + +Eleven issues were closed against landed `dev`: #2495, #3898, #4079, #4205, +#4206, #4208, #4211, #4236, #4308, #4314, #4315. Three of them GitHub had +already closed; the remaining eight were closed here with a comment naming the +landing pull request and the code that proves the behavior exists. + +Each candidate was re-verified against `origin/dev` at `72601a173` rather than +trusted from the research pass, because that pass ran while the merges were +still in flight. The distinction that mattered repeatedly was `Refs #N` versus +`Closes #N`: several of the closures rest on a pull request that only +referenced the issue but did in fact implement the requested behavior, and at +least one issue is still claimed by an open contributor pull request (#4080 +claims #4079) that the landed carry superseded. + +## Screenshot gate + +`enforce-target` fails with `missing UI screenshot` on any pull request that +mentions gui, which every cumulative tip carrying GUI work did. By owner +decision this was not treated as a merge blocker during the batch, and the +screenshots are collected here instead of being demanded from each lane tip in +flight. + + +They were captured from a Vite build of the landed `dev` tree, served by a +throwaway proxy instance on an unused port with isolated `OPENCODEX_HOME` and +`CODEX_HOME` directories, then torn down. That instance briefly rewrote the +host's Grok integration block to its own port; it was restored to the real +proxy afterwards and verified, which is worth recording because starting a +second instance is not as side-effect-free as it looks. + +![Dashboard overview](screenshots/dashboard-overview.png) + +![Codex account pool and rotation strategy](screenshots/codex-account-pool.png) + +![Remote workspace](screenshots/remote-workspace.png) + +![Integrations client grid](screenshots/integrations-clients.png) + diff --git a/devlog/_plan/260913_lane_stack_merge/screenshots/codex-account-pool.png b/devlog/_plan/260913_lane_stack_merge/screenshots/codex-account-pool.png new file mode 100644 index 0000000000..63de4350b7 Binary files /dev/null and b/devlog/_plan/260913_lane_stack_merge/screenshots/codex-account-pool.png differ diff --git a/devlog/_plan/260913_lane_stack_merge/screenshots/dashboard-overview.png b/devlog/_plan/260913_lane_stack_merge/screenshots/dashboard-overview.png new file mode 100644 index 0000000000..f0c84a3a71 Binary files /dev/null and b/devlog/_plan/260913_lane_stack_merge/screenshots/dashboard-overview.png differ diff --git a/devlog/_plan/260913_lane_stack_merge/screenshots/integrations-clients.png b/devlog/_plan/260913_lane_stack_merge/screenshots/integrations-clients.png new file mode 100644 index 0000000000..791e24aefa Binary files /dev/null and b/devlog/_plan/260913_lane_stack_merge/screenshots/integrations-clients.png differ diff --git a/devlog/_plan/260913_lane_stack_merge/screenshots/remote-workspace.png b/devlog/_plan/260913_lane_stack_merge/screenshots/remote-workspace.png new file mode 100644 index 0000000000..1ba5e69175 Binary files /dev/null and b/devlog/_plan/260913_lane_stack_merge/screenshots/remote-workspace.png differ