docs(plan): followups-and-tickets — 10 tasks closing every recorded follow-up
ci / nightly-windev (push) Has been skipped
ci / windows-x86 (push) Successful in 1m21s
ci / java (push) Successful in 2m11s
ci / portable (push) Successful in 8m53s

This commit is contained in:
Joseph Doherty
2026-08-18 06:36:47 -04:00
parent 45058d57a5
commit fd941c249e
2 changed files with 477 additions and 0 deletions
@@ -0,0 +1,461 @@
# Follow-ups and Tickets Implementation Plan
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development
> to execute this plan task-by-task in this session.
**Goal:** Close every follow-up recorded in `docs/plans/2026-08-17-followup-closeout.md`
(as-built notes, "Follow-ups recorded, not started") plus the two ticket-worthy windev
findings — leaving no recorded open item anywhere.
**Architecture:** No new components. Ten independent closures: two toolchain/script fixes
(Check 4 Windows pin, Gradle 9 `checkGeneratedClean`), two dashboard display gaps
(Settings recent-limit rows, push-driven Alarms truncation banner), two documentation
audits (Server-0xx resolution regression sweep, `Authentication.md` runnable-examples
pass), one worker-test investigation (the deterministic `WorkerPipeSessionTests` failure
on windev), one bounded rig probe (ack-leg unblock paths), a windev verification pass
(including the `protoc-gen-go-grpc` 1.6.2 pin install), and bookkeeping.
**Tech Stack:** PowerShell (codegen scripts), Gradle/Groovy (Java client), Blazor
server-side Razor + xUnit/bUnit-style render tests (dashboard), .NET Framework 4.8 x86
xUnit (worker tests, windev-only), wnwrap probe harness (windev rig), Markdown.
**Branch:** `feat/followups-tickets` off `main` (`45058d5`).
---
## Ground rules for every implementer subagent
- **Git discipline (Mac tree):** NEVER `git stash`, `git reset`, `git clean`, or
`git checkout <sha/branch>`. Commit with **pathspecs on the commit**:
`git commit -m "..." -- <paths>`. If `index.lock` blocks you, wait 5 s and retry.
- **Build/test lock:** before any `dotnet build`/`dotnet test`/`cargo`/`gradle`/`go test`
on the Mac, acquire the lock:
`mkdir /private/tmp/claude-501/-Users-dohertj2-Desktop-MxAccessGateway/f36938ae-bbca-4245-b5c9-fac512d69e22/scratchpad/buildlock`
(retry with backoff until it succeeds); `rmdir` it on ALL exit paths, including failure.
- **Quality gates:** `TreatWarningsAsErrors=true`, `Nullable=enable`. Follow
`docs/style-guides/CSharpStyleGuide.md` (file-scoped namespaces, `sealed` by default,
`Async` suffix). Update affected docs in the same commit as source.
- **MXAccess parity:** never synthesize events; the dashboard/gateway forwards only what
the worker/monitor emits. The `snapshot_status` frame is a gateway-status frame (like
`provider_status`), NOT a synthesized MXAccess event.
- **Never log or echo secrets, API keys, credentials, or tag values.**
- **Scope contract:** the task's `Files:` block is the `files_to_edit` contract. If the
task needs other files, that is a plan defect — surface it in your report, don't
silently expand scope. (Exception: Tasks 7 and 8 are investigations; their Files list
is starting points, and they must report every file they end up touching.)
- **Windev access (Tasks 7, 8, 9 only):** `ssh windev` lands in PowerShell. The CI clone
is `C:\build\mxaccessgw-ci`. Quirks: the first `slnx` build after a pull often fails on
stale Contracts obj artifacts (CS2001/CS0016) — clear the Contracts `obj`/`bin` and
rebuild, it is not a regression; the gateway suite is load-sensitive — rerun a filtered
subset before believing a flake. To get branch code there:
`git -C C:\build\mxaccessgw-ci fetch origin && git -C C:\build\mxaccessgw-ci checkout feat/followups-tickets && git -C C:\build\mxaccessgw-ci pull`
which requires the Mac side to have pushed the branch first (the controller pushes;
ask if the branch tip you need is not on origin).
---
### Task 1: check-codegen Check 4 — tolerate the Windows `.exe` version banner
**Classification:** small
**Estimated implement time:** ~4 min
**Parallelizable with:** Task 2, Task 3, Task 4, Task 5, Task 7
**Files:**
- Modify: `clients/go/generate-proto.ps1` (pin compare at `:52-67`, pin constants at `:10-11`)
- Modify: any doc that states Check 4 is unrunnable on Windows (grep `docs/` and
`code-reviews/` for "Check 4"; `docs/GatewayTesting.md` is the likely holder)
**Problem.** On Windows the plugins report their invoked name with an `.exe` suffix
(`protoc-gen-go.exe v1.36.11`), so the exact-string compare against
`'protoc-gen-go v1.36.11'` throws and Check 4 (which shells this script) is unrunnable on
any Windows host — including windev, the box where regeneration actually happens.
**Step 1: Normalize the banner before comparing.** Add a small helper and use it for both
plugin compares (protoc stays warn-only and gets the same normalization for a fair warn):
```powershell
function Get-NormalizedToolVersion {
# On Windows a plugin reports its argv[0] name, so the banner carries an `.exe`
# suffix ("protoc-gen-go.exe v1.36.11"). Strip it so the pin compare is
# host-independent; the version part must still match exactly.
param([string]$RawBanner)
return ($RawBanner -replace '\.exe(?=\s)', '')
}
```
Apply to `$protocGenGoVersion`, `$protocGenGoGrpcVersion`, `$protocVersion` at the point
each is read (`:54`, `:59`, `:64`), keeping the pinned constants unchanged. Update the
comment block at `:4-9` to note the normalization.
**Step 2: Verify on macOS.** Acquire the build lock, then run
`pwsh scripts/check-codegen.ps1` — all 4 checks must pass (banner has no `.exe` here, so
this proves no regression). Also assert the helper logic directly:
`pwsh -c "& { <paste helper> ; Get-NormalizedToolVersion 'protoc-gen-go.exe v1.36.11' }"`
must print `protoc-gen-go v1.36.11`, and a name-only match check for
`protoc-gen-go-grpc.exe 1.6.2``protoc-gen-go-grpc 1.6.2`.
**Step 3: Fix the stale prose.** Wherever docs state Check 4 cannot run on Windows,
rewrite to: Check 4 runs on Windows; the plugin pins must be installed
(`go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11`,
`go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.2`).
Do NOT edit `docs/plans/2026-08-17-followup-closeout.md` — Task 10 owns plan bookkeeping.
**Step 4: Commit** with pathspecs on the touched files:
`fix(codegen): normalize .exe off plugin version banners so Check 4 runs on Windows`.
Windows-side proof is deferred to Task 9 (windev runs check-codegen 14 after installing
the 1.6.2 pin).
---
### Task 2: Gradle 9 — revive `checkGeneratedClean` without `Project.exec`
**Classification:** small
**Estimated implement time:** ~4 min
**Parallelizable with:** Task 1, Task 3, Task 4, Task 5, Task 7
**Files:**
- Modify: `clients/java/zb-mom-ww-mxgateway-client/build.gradle` (`checkGeneratedClean`, `:71-92`)
- Modify: `docs/GatewayTesting.md` only if it describes the task's mechanism (grep
`checkGeneratedClean`)
**Problem.** The task's `doLast` calls the script-level `exec {}` (i.e. `Project.exec`),
removed in Gradle 9 — the task is dead on any Gradle 9 host.
**Step 1: Replace with `ProviderFactory.exec`** (available since Gradle 7.5, so it works
on both the current toolchain and Gradle 9, and is configuration-cache safe). Capture the
provider reference at configuration time, call `.get()` in `doLast` so the git status
runs at execution time:
```groovy
tasks.register('checkGeneratedClean') {
group = 'verification'
description = 'Fails if the committed generated Java tree differs from a fresh regeneration.'
dependsOn 'generateProto'
def generatedDir = 'clients/java/src/main/generated'
def repoRoot = rootProject.projectDir.parentFile.parentFile
// Project.exec was removed in Gradle 9; ProviderFactory.exec runs the probe lazily
// at doLast time and works on Gradle 7.5+.
def gitStatus = providers.exec {
workingDir = repoRoot
commandLine 'git', 'status', '--porcelain', '--', generatedDir
ignoreExitValue = true
}
doLast {
def dirty = gitStatus.standardOutput.asText.get().trim()
if (!dirty.isEmpty()) {
throw new GradleException(
"Generated Java is stale:\n${dirty}\n" +
"Regenerate and commit the Java client after a .proto change " +
"(gradle :zb-mom-ww-mxgateway-client:generateProto).")
}
}
}
```
Preserve the explanatory comment block above the task (`:61-70`) — amend it, don't delete.
**Step 2: Verify.** Acquire the build lock. From `clients/java`:
`gradle :zb-mom-ww-mxgateway-client:checkGeneratedClean` must pass (tree is clean), and
`gradle --version` must be recorded in your report. Then prove the failure path: touch a
scratch edit inside `clients/java/src/main/generated/` (append a comment line to one
generated file), rerun the task, confirm it fails with the stale message, then **revert
that file with `git checkout -- <that one file>`** (this narrow file-level checkout is the
one permitted use; the file is committed generated output).
**Step 3: Commit:**
`fix(java-client): checkGeneratedClean via ProviderFactory.exec — Project.exec is gone in Gradle 9`.
---
### Task 3: SettingsPage — RecentFaultLimit / RecentSessionLimit rows
**Classification:** small
**Estimated implement time:** ~4 min
**Parallelizable with:** Task 1, Task 2, Task 4, Task 5, Task 7
**Files:**
- Modify: `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/Pages/SettingsPage.razor`
(dashboard rows around `:98-102`)
- Test: `src/ZB.MOM.WW.MxGateway.Tests/` — extend the existing settings render coverage
(`SettingsPageTagVisibilityRenderTests` or its sibling settings render test class; find
with `grep -rl "SettingsPage" src/ZB.MOM.WW.MxGateway.Tests`)
- Modify: `docs/GatewayDashboardDesign.md` only if it enumerates settings rows (grep
`RecentFaultLimit` / "Snapshot interval" there first)
**Problem.** `EffectiveDashboardConfiguration` carries `RecentFaultLimit` and
`RecentSessionLimit` (already projected by `GatewayConfigurationProvider.cs:62-63`), but
`SettingsPage` renders every member except these two.
**Step 1: Write the failing test.** Extend the settings render test: rendered page
contains `Recent fault limit` with the configured value and `Recent session limit` with
the configured value (use non-default values in the arranged options so the assertion
proves plumbing, not defaults).
**Step 2: Run it, expect FAIL** (`dotnet test --filter "FullyQualifiedName~SettingsPage"`
under the build lock).
**Step 3: Add the two rows** next to the other Dashboard rows (after "Snapshot interval",
matching the existing `<tr><th scope="row">…</th><td>…</td></tr>` idiom):
```razor
<tr><th scope="row">Recent fault limit</th><td>@Snapshot.Configuration.Dashboard.RecentFaultLimit</td></tr>
<tr><th scope="row">Recent session limit</th><td>@Snapshot.Configuration.Dashboard.RecentSessionLimit</td></tr>
```
**Step 4: Run the filtered test, expect PASS.**
**Step 5: Commit:**
`feat(dashboard): settings page shows RecentFaultLimit and RecentSessionLimit`.
---
### Task 4: Authentication.md — runnable-as-written examples pass
**Classification:** small
**Estimated implement time:** ~5 min
**Parallelizable with:** Task 1, Task 2, Task 3, Task 5, Task 7
**Files:**
- Modify: `docs/Authentication.md`
**Problem.** The doc's CLI/scope examples were flagged during the previous branch as not
runnable as written — notably samples that grant only invoke/event scopes with no
`session:open`, which since Server-004's validation would produce keys that cannot open a
session (or, for unknown scope strings, be rejected at create time). The canonical scope
catalog is: `session:open`, `session:close`, `invoke:read`, `invoke:write`,
`invoke:secure`, `events:read`, `metadata:read`, `admin`; the verb is `apikey create-key`
with `--key-id` required.
**Step 1: Sweep every example** (` ```-fenced blocks and inline commands) in the doc. For
each, either (a) make it runnable as written — canonical verb, required flags, only
canonical scopes, scope sets that support what the surrounding prose says the key is for
(a key described as opening sessions needs `session:open`) — or (b) if it is deliberately
a fragment, mark it explicitly (e.g. "illustrative — not a complete command"). Prefer (a);
use (b) only where completeness would obscure the point being made.
**Step 2: Cross-check** each corrected scope list against
`src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/GatewayScopes.cs` (read-only) —
do not invent scope strings.
**Step 3: Commit:** `docs(auth): make Authentication.md examples runnable as written`.
---
### Task 5: AlarmsPage — push-driven truncation banner from the snapshot_status frame
**Classification:** standard
**Estimated implement time:** ~5 min
**Parallelizable with:** Task 1, Task 2, Task 3, Task 4, Task 7
**Files:**
- Modify: `src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/Pages/AlarmsPage.razor`
(in-process feed loop around `:199-230`, `_snapshotTruncated` at `:170`/`:401`)
- Test: `src/ZB.MOM.WW.MxGateway.Tests/` — the AlarmsPage/dashboard alarm test class
(find with `grep -rl "AlarmsPage" src/ZB.MOM.WW.MxGateway.Tests`); if no AlarmsPage
render/behavior test exists, add coverage at the level the existing dashboard tests use
- Modify: `docs/GatewayDashboardDesign.md` (truncation-banner description, if it says the
banner is poll-driven)
**Problem.** The truncation banner (`_snapshotTruncated`, rendered at `:39`) updates only
from the 3-second poll (`:401`), even though the page already holds an in-process
subscription to the alarm feed (the loop at `:211-213` that feeds the provider-status
badge). The feed now carries an edge-triggered `snapshot_status` frame
(`AlarmFeedMessage.PayloadCase.SnapshotStatus`, shipped `fccf753`) — consume it so the
banner flips on the edge instead of up to 3 s late.
**Spec:**
- In the existing in-process feed loop, add a case for
`AlarmFeedMessage.PayloadCase.SnapshotStatus`: set `_snapshotTruncated =
message.SnapshotStatus.Truncated` and re-render (same invoke/StateHasChanged pattern the
provider-status case uses).
- Keep the poll's assignment at `:401` — the poll is the reconcile baseline and both
sources derive from the same `GatewayAlarmMonitor` verdict, so they cannot disagree
except transiently. Do not remove or restructure the poll.
- Remember `StreamAsync` primes every subscriber with an unconditional `snapshot_status`
baseline frame after `provider_status` — so on attach the page gets the current verdict
push-side too. No extra priming logic needed in the page.
- Do NOT touch `GatewayAlarmMonitor` or the proto — gateway emission is done and shipped.
**Steps:** failing test first (a `SnapshotStatus` frame delivered through the in-process
subscription flips the banner state without a poll tick; a `false` frame clears it), then
implement, then filtered dashboard/alarm tests green under the build lock, then commit:
`feat(dashboard): alarms page consumes snapshot_status feed frame for the truncation banner`.
---
### Task 6: Server-0xx resolution audit — doc-only "Resolved" entries re-verified
**Classification:** standard
**Estimated implement time:** ~5 min (audit) + fixes as found
**Parallelizable with:** none (runs after Task 4 lands — both may touch `docs/Authentication.md`)
**Files:**
- Modify: `code-reviews/Server/findings.md` (annotations only — do not rewrite history)
- Modify: any file where a claimed correction is found absent (expected candidates:
`CLAUDE.md`, `docs/*.md`, XML doc comments named in findings — report each)
**Problem.** Server-012 was recorded *Resolved 2026-05-18* claiming two scope-list
corrections that were absent from the tree when `feat/followup-closeout` looked — a
resolution that regressed or was never applied. Findings that read Resolved are never
re-examined, so every *documentation/comment-only* resolution needs the same spot-check.
**Procedure:**
1. Enumerate every `Server-0xx` entry in `code-reviews/Server/findings.md` whose
Resolution describes documentation-only or comment-only changes (no test named, prose
like "Pure documentation change" — at minimum Server-011, Server-012, Server-013/014
remarks rewrites; sweep all entries, don't assume).
2. For each, verify the specific claimed text exists in today's tree (grep the exact
phrases/identifiers the resolution names).
3. Where present: append one line to that finding's Resolution:
`Re-verified present 2026-08-18 (feat/followups-tickets).`
4. Where absent: re-apply the correction in the target file, and append:
`Regressed or never applied; re-fixed 2026-08-18 in <file> (feat/followups-tickets).`
(Server-012 itself was already re-fixed on the previous branch — annotate it as such,
citing commits `a5f843c`/`f2a422b`, rather than re-fixing.)
5. Report a table: finding id → verified/regressed → action.
**Commit:** `docs(reviews): re-verify doc-only Server-0xx resolutions; re-fix regressions`.
---
### Task 7: WorkerPipeSessionTests deterministic failure — investigate and fix (windev)
**Classification:** high-risk
**Estimated implement time:** investigation timeboxed ~10 min; fix ≤5 min
**Parallelizable with:** Task 15 (only windev user in wave 1)
**Files (starting points — investigation task, report everything touched):**
- Test: `src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeSessionTests.cs`
(`RunAsync_LongInFlightCommandThatKeepsPumping_DoesNotFaultAndDeliversReply`)
- Suspect: `src/ZB.MOM.WW.MxGateway.Worker/` pipe-session / frame-protocol sources the
test exercises (follow the test's references)
**Problem.** The test fails deterministically on windev, and reproduces on `main` —
pre-existing, not introduced by any recent branch. Everything else in the worker suite
passes (523/523 otherwise).
**Procedure (all building/testing on windev over ssh; edits in the Mac tree, pushed by
the controller, pulled on the CI clone — coordinate via your report if you need a push):**
1. Repro on the CI clone at this branch:
`dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Tests.csproj -p:Platform=x86 --filter "FullyQualifiedName~RunAsync_LongInFlightCommandThatKeepsPumping"`.
Capture the full failure output (assertion text, timeout, stack).
2. Read the test and the code under test. Classify: (a) test defect (bad timing
assumption, races in the harness), (b) product defect in the pipe session under a
long in-flight command, or (c) environment-specific (windev timing/load).
**Timebox: if the cause is not isolated after ~10 minutes of investigation, stop and
report your best hypothesis with evidence — do not churn.**
3. Fix minimally per the classification. A product fix in the frame/pipe layer is
high-risk territory: preserve the frame protocol (`docs/WorkerFrameProtocol.md`), the
STA pumping rules, and MXAccess parity. A test fix must keep the scenario's intent —
a long in-flight command that keeps pumping must not fault the session and must
deliver its reply; don't weaken it into a sleep-and-hope.
4. Verify: the fixed test passes 3 consecutive runs on windev; then the full worker suite
(`-p:Platform=x86`) is green. If you touched product code, also build the full `slnx`
on windev and run the gateway suite filtered to any shared-surface tests.
5. Docs in the same commit if behavior/rules changed.
**Commit:** `fix(worker): <cause> — WorkerPipeSessionTests long-in-flight repro` (adjust
`fix(worker-tests)` if the defect is in the test).
---
### Task 8: Ack-leg probe — bounded unblock attempt (windev rig)
**Classification:** standard
**Estimated implement time:** timeboxed ~15 min of probing
**Parallelizable with:** Task 6 (runs after Task 7 — serialize windev use)
**Files (starting points — investigation task):**
- Modify: `docs/AlarmProbeFindings.md` (append a third-attempt section, whatever the outcome)
- Reference (read-only): the probe harness locations named in that doc's earlier attempts;
the mxaccess analysis project at `C:\Users\dohertj2\Desktop\mxaccess` (windev)
**Problem.** The acknowledge leg remains unobserved: every wnwrap ack surface is
accepted-but-inert (`rc=0`, state stays `UNACK_ALM`), and `.Acked` is write-rejected
(OperationalError 1007). Two recorded unblock paths remain
(`docs/AlarmProbeFindings.md`, "Remaining unblock paths for the acknowledge leg").
**Procedure — attempt path 2 first (it is inspectable), path 1 only if non-interactive:**
1. **Path 2 — ack-security configuration:** inspect whether the rig's galaxy/`alarmmgr`
is configured with an alarm-acknowledgement security requirement the wnwrap consumer
(operator *name* string, no authenticated identity) cannot meet. Look in galaxy
configuration (the `ZB` SQL Galaxy Repository — read-only queries only), area/object
security settings for the `TestMachine_00x` objects, and any alarm-security docs in
the mxaccess analysis project. **Read-only: do not change galaxy security config.**
2. **Path 1 — platform-side ack:** only if a *scriptable, non-interactive* path exists on
the rig as-installed (e.g. an existing harness or automation entry point). Do NOT
install software, do NOT drive GUI automation, do NOT change rig state beyond the
established raise/clear pattern on `TestMachine_001.TestAlarm001`. If only interactive
IDE/InTouch paths exist, record that and stop.
3. Whatever the outcome, append the third-attempt section: what was inspected, evidence,
and the leg's final status (observed / unavailable-by-configuration / still assumed
with the paths requiring a human). If the ack was actually observed, record whether
`STATE` reached `ACK_ALM` and whether the GUID survived — that answers the original
question and should update the findings table at the top of the doc.
4. **Stop condition:** at ~15 minutes of probing without a decisive result, write up what
was learned and conclude "still assumed" — the doc itself notes this is a
documentation gap, not a correctness one.
**Commit:** `docs(probe): ack-leg third attempt — <outcome>`.
---
### Task 9: Windev toolchain pin + full verification
**Classification:** verification (no review chain)
**Estimated implement time:** ~15 min wall (mostly build/test wait)
**Parallelizable with:** none (after all code tasks land and are pushed)
**Files:** none in-repo except possibly `docs/ToolchainLinks.md` (update the windev
`protoc-gen-go-grpc` entry if it records 1.6.1).
**Procedure (all over `ssh windev`, CI clone `C:\build\mxaccessgw-ci` at this branch tip):**
1. `go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.2` — then
`protoc-gen-go-grpc --version` must report 1.6.2. Update `docs/ToolchainLinks.md` if
it pins the old version (commit from the Mac tree).
2. Full `slnx` build — 0 warnings / 0 errors (clear Contracts obj/bin on CS2001/CS0016).
3. Worker tests `-p:Platform=x86` — expect the Task 7 outcome (fully green if fixed;
otherwise the documented known-failure only).
4. Gateway tests — full suite; rerun filtered on load flakes before believing a failure.
5. `powershell scripts/check-codegen.ps1` — **all 4 checks**, proving Task 1 on the
platform that had the bug.
6. `gradle --version` + `gradle :zb-mom-ww-mxgateway-client:checkGeneratedClean` from
`clients\java` if Gradle is installed there — record version and result either way.
7. Live MXAccess smoke: `MXGATEWAY_RUN_LIVE_MXACCESS_TESTS=1`, filter
`WorkerLiveMxAccessSmokeTests` — 8/8.
8. Report every result verbatim (counts, not "green").
---
### Task 10: Bookkeeping — close the follow-ups record
**Classification:** trivial
**Estimated implement time:** ~3 min
**Parallelizable with:** none (last)
**Files:**
- Modify: `docs/plans/2026-08-17-followup-closeout.md` (follow-ups block `:483-507`):
annotate each bullet closed with this branch's closing commit (or precisely narrowed,
e.g. ack leg if still assumed)
- Modify: `docs/plans/2026-08-18-followups-and-tickets.md` (this file): append as-built
notes — per-task commits, review outcomes, verification results, anything learned
- Modify: `docs/plans/2026-08-18-followups-and-tickets.md.tasks.json`: final statuses
**Commit:** `chore(plan): followups-and-tickets as-built record; prior follow-ups closed`.
---
## Execution notes for the controller
- **Model/review chains** (subagent-driven-development skill): Tasks 14 small → Sonnet
implementer + Haiku code review (diffs <100 LOC). Task 5 standard → Opus implementer,
spec (Haiku) ∥ code (Sonnet). Task 6 standard → Opus implementer, spec ∥ code. Task 7
high-risk → Opus implementer, serial spec (Haiku) → code (Sonnet). Task 8 standard →
Opus implementer, spec ∥ code. Task 9 verification → Opus, no review. Task 10 trivial →
Sonnet, no review.
- **Waves:** Wave 1 = Tasks 1, 2, 3, 4, 5, 7 (disjoint files; Task 7 alone on windev).
Wave 2 = Task 6 (after 4) and Task 8 (after 7). Wave 3 = push branch, Task 9.
Wave 4 = Task 10, final integration review, then hold for the user's merge decision.
- **Push cadence:** controller pushes the branch before any windev task needs its code
there (Task 7 investigates on-branch; Tasks 89 need the wave-1/2 tips).
@@ -0,0 +1,16 @@
{
"planPath": "docs/plans/2026-08-18-followups-and-tickets.md",
"tasks": [
{"id": 1, "subject": "Task 1: check-codegen Check 4 — Windows .exe banner normalization", "status": "pending"},
{"id": 2, "subject": "Task 2: Gradle 9 checkGeneratedClean via ProviderFactory.exec", "status": "pending"},
{"id": 3, "subject": "Task 3: SettingsPage RecentFaultLimit/RecentSessionLimit rows", "status": "pending"},
{"id": 4, "subject": "Task 4: Authentication.md runnable-as-written examples pass", "status": "pending"},
{"id": 5, "subject": "Task 5: AlarmsPage push-driven truncation banner (snapshot_status)", "status": "pending"},
{"id": 6, "subject": "Task 6: Server-0xx doc-only resolution audit", "status": "pending", "blockedBy": [4]},
{"id": 7, "subject": "Task 7: WorkerPipeSessionTests deterministic failure — investigate + fix (windev)", "status": "pending"},
{"id": 8, "subject": "Task 8: Ack-leg probe bounded unblock attempt (windev rig)", "status": "pending", "blockedBy": [7]},
{"id": 9, "subject": "Task 9: Windev toolchain pin + full verification", "status": "pending", "blockedBy": [1, 2, 3, 4, 5, 6, 7, 8]},
{"id": 10, "subject": "Task 10: Bookkeeping — close the follow-ups record", "status": "pending", "blockedBy": [9]}
],
"lastUpdated": "2026-08-18"
}