541 lines
30 KiB
Markdown
541 lines
30 KiB
Markdown
# 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 1–4 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 1–5 (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 1–4 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 8–9 need the wave-1/2 tips).
|
||
|
||
---
|
||
|
||
## As-built record (2026-08-18)
|
||
|
||
All 10 tasks completed on `feat/followups-tickets`; every classification-driven review
|
||
chain resolved **Approved** (Tasks 5, 7, and 8 after fix rounds; Task 7's high-risk chain
|
||
took two rounds, ending with the mid-window auto-release race made structurally
|
||
unreachable — 30 s `BlockedDispatchSafetyNet` > every test's 20 s cancellation). The
|
||
final integration review returned *Ready with reservations*; every reservation was fixed
|
||
on-branch: I-2 (`6b5c737` — `ToolchainLinks.md` recorded `@latest` for plugins the
|
||
regeneration script hard-pins), I-3 (`db7b1db` — three doc sites claimed full-DN
|
||
`GroupToRole`/`GroupToTag` keys work; the Server-040 restoration proves they never match
|
||
the pre-stripped groups; rows were pre-existing on `main`), M-2/M-5 (`1605f54`), M-3
|
||
(`fb68bdb`), M-4 (`753d070`), and M-1 was discharged by evidence (a
|
||
`--configuration-cache --rerun-tasks` discriminator proved `providers.exec` evaluates at
|
||
execution time after `generateProto`; no change needed).
|
||
|
||
Verification (Task 9, windev CI clone at `5417222`, post-fix commits re-verified
|
||
individually by their owners at their pushed tips): full `slnx` 0W/0E; worker x86 **524
|
||
passed / 11 skipped / 0 failed** — the suite's first fully green windev run; gateway
|
||
1155/1155 with no reruns; `check-codegen.ps1` **4/4 on Windows** under pwsh 7, and after
|
||
`8ae0c2f` also end-to-end under Windows PowerShell 5.1; Gradle 9.4.1
|
||
`checkGeneratedClean` green on windev (and 9.5.1 on macOS); live MXAccess smoke 8/8;
|
||
toolchain pins now real on windev (`protoc-gen-go v1.36.11`, `protoc-gen-go-grpc 1.6.2`).
|
||
|
||
Notable findings made along the way:
|
||
|
||
- **Check 4 had a second Windows-only blocker** behind the `.exe` banner: Windows
|
||
PowerShell 5.1 strips embedded double quotes when marshaling native-exe args, breaking
|
||
the Python `grpcio-tools` version probe (`clients/python/generate-proto.ps1:39`).
|
||
Fixed (`8ae0c2f`) by swapping quote nesting; no other native-exe invocation in the
|
||
codegen scripts carries the pattern.
|
||
- **The ack leg is blocked by galaxy security configuration**, not a missing verb: the
|
||
probe fixtures carry `MxSecurityOperate`, and no `AlarmAckByName` overload can convey
|
||
an authenticated identity (inferred from mechanism, honestly caveated). `gateway.md`
|
||
and `docs/Grpc.md` now carry the "acceptance is not application" caveat on the
|
||
acknowledge RPC.
|
||
- **The `fca978d` tracking-marker sweep deleted substantive prose** along with markers in
|
||
at least one place (Server-040's precedence comment plus an operator-facing RDN
|
||
paragraph that was never part of the finding). A targeted re-read of that commit's
|
||
larger comment deletions is ticket-worthy (203 files swept; only the audited file was
|
||
examined).
|
||
- **`generateProto`'s up-to-date check does not notice an out-of-band deletion** of a
|
||
single generated file (protobuf-gradle-plugin behavior, orthogonal to this branch) — a
|
||
manually deleted generated file stays missing until a `.proto` change or
|
||
`--rerun-tasks` invalidates the task.
|
||
- Windev process notes: the CI clone sat on a detached HEAD, so bare `git pull` silently
|
||
no-ops (use `git pull origin <branch>`); concurrent MSBuilds on the clone can kill each
|
||
other's child nodes (MSB4166) — `-m:1` avoids it; ssh can transiently refuse with "Too
|
||
many authentication failures" under agent concurrency (back off and retry).
|
||
|
||
Follow-ups recorded, not started (deliberately small) — *first two closed 2026-08-18 on
|
||
`feat/sweep-reread-tag-summary`*:
|
||
|
||
- The `fca978d` sweep re-read (above) — the one genuinely ticket-worthy item.
|
||
*Closed in `f82dac1`: a mechanical pre-pass narrowed the sweep's 1,383 deletions to
|
||
68 files / 816 residual prose lines; a judged review of every one found 17 collateral
|
||
deletions across 10 files (rationale prose with no surviving equivalent — SessionManager
|
||
metrics invariants, HubTokenService hollow-token guard, AcknowledgeAlarm routing
|
||
remarks, LmxSubtagAlarmSource unsecured-write/idempotency notes, WnWrapAlarmConsumer's
|
||
WIN-911 precedent, DashboardSessionAdminService error-boundary catches,
|
||
WorkerPipeSession factory-null throw, three test-design rationales) — all restored,
|
||
markers left stripped. Everything else flagged verified benign (`<inheritdoc/>`
|
||
resolves to equal-or-richer interface docs, or the substance survives relocated).
|
||
Verified: NonWindows slnx 0W/0E, touched gateway test classes 65/65; windev worker
|
||
x86 build 0W/0E, worker suite 524/11/0.*
|
||
- `DashboardGroupTagMapping`'s class-level `<summary>` still describes the lookup as
|
||
"full DN first, leading-RDN fallback" — accurate mechanics, but it could point at the
|
||
short-name-keys consequence the inline comment now records.
|
||
*Closed in `4b6f907`: the summary now states full-DN keys can never match and points
|
||
at the lookup comment in `MapGroupsToTags`.*
|
||
- wwtools `mxa read` human-readable formatter throws `RuntimeBinderException` on a failed
|
||
read (`ReadCommand.cs:137`); `--llm-json` works. Different repo, noted here so it isn't
|
||
lost.
|
||
|
||
Explicitly decided, not an omission: **`../scadaproj/CLAUDE.md` needs no update** — no
|
||
`.proto`, contract, command, or architecture fact the umbrella index records changed on
|
||
this branch.
|