chore(plan): close the record — prior follow-ups annotated, closeout as-built noted
The prior plan's "Follow-ups recorded, not started" block described pre-branch
behavior; every item is now closed, narrowed, or restated with its evidence, so
the block no longer misleads a reader who lands on it first. The stale Rust-guard
bullet is corrected in place rather than deleted: Check 3 always existed, and
saying so is the only way the reader learns what the real (one-directional) gap was.
Also fixes the CLAUDE.md apikey sample, which named a verb the parser has never
accepted ('create'; only 'create-key' exists, no alias), omitted the required
--key-id, and listed non-canonical scope strings that GatewayScopes now rejects
at create time — the sample could not have run.
This commit is contained in:
@@ -71,7 +71,7 @@ dotnet test src/ZB.MOM.WW.MxGateway.Worker.Tests/ZB.MOM.WW.MxGateway.Worker.Test
|
|||||||
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj
|
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj
|
||||||
|
|
||||||
# API-key admin CLI (same exe, "apikey" subcommand)
|
# API-key admin CLI (same exe, "apikey" subcommand)
|
||||||
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj -- apikey create --display-name "dev" --scopes session,invoke,event,metadata,admin
|
dotnet run --project src/ZB.MOM.WW.MxGateway.Server/ZB.MOM.WW.MxGateway.Server.csproj -- apikey create-key --key-id dev --display-name "dev" --scopes session:open,session:close,invoke:read,invoke:write,invoke:secure,events:read,metadata:read,admin
|
||||||
```
|
```
|
||||||
|
|
||||||
Single test by name (xUnit `--filter`):
|
Single test by name (xUnit `--filter`):
|
||||||
|
|||||||
@@ -416,22 +416,44 @@ gateway 1123/1123, live MXAccess smoke 8/8 — green first try, no stale-obj qui
|
|||||||
had diverged from Contracts (masked by the in-repo build path); refreshed byte-identical,
|
had diverged from Contracts (masked by the in-repo build path); refreshed byte-identical,
|
||||||
and the client protoset descriptors were regenerated.
|
and the client protoset descriptors were regenerated.
|
||||||
|
|
||||||
Follow-ups recorded, not started:
|
Follow-ups recorded here — all closed or narrowed on 2026-08-18 (branch
|
||||||
- `IGatewayAlarmService.StreamAsync` / `AlarmFeedMessage` does not carry the truncation
|
`feat/followup-closeout`, plan `docs/plans/2026-08-17-followup-closeout.md`).
|
||||||
signal — live central-feed consumers (lmxopcua, ScadaBridge) cannot see snapshot
|
Each bullet states what it said when written, then its closure:
|
||||||
degradation; add if those consumers need completeness reasoning.
|
|
||||||
- No guard keeps `clients/rust/protos/` in sync with Contracts (a `diff` check in
|
- `IGatewayAlarmService.StreamAsync` / `AlarmFeedMessage` did not carry the truncation
|
||||||
`scripts/check-codegen.ps1` would close it).
|
signal — live central-feed consumers (lmxopcua, ScadaBridge) could not see snapshot
|
||||||
- `EffectiveDashboardConfiguration` (dashboard settings page) doesn't display
|
degradation. **Closed 2026-08-18 — shipped.** `AlarmSnapshotStatus` with
|
||||||
`GroupToTag` / `UntaggedSessionVisibility`, though it shows `GroupToRole`.
|
`snapshot_status = 5` on the feed: gateway-side edge emission plus open-time priming
|
||||||
- ApiKeysPage's `ConstraintText` neither offers tag input nor lists `DashboardTags`,
|
(`fccf753`, `312e072`), rolled out to all five clients (`c748361`, `4d4f443`).
|
||||||
and since `IsEmpty` now counts tags, a tags-only key renders `-` where a truly
|
- "No guard keeps `clients/rust/protos/` in sync with Contracts." **Was stale when
|
||||||
unconstrained key renders `unconstrained` — two spellings of one meaning.
|
written.** `scripts/check-codegen.ps1` Check 3 already existed and already byte-compared
|
||||||
- `AlarmsHubPublisher` broadcasts alarm transitions with `current_value`/`limit_value`
|
by SHA-256 in CI. The real gap was narrower — the sweep ran in one direction only, so a
|
||||||
unredacted — the `ShowTagValues` redaction covers only the events hub mirror
|
canonical proto with no vendored copy passed. **Closed 2026-08-18** by `711793f`: Check 3
|
||||||
(pre-existing; now noted in `docs/GatewayConfiguration.md`).
|
now sweeps both directions and a canonical proto missing its vendored copy fails.
|
||||||
- The alarm probes' remaining questions (ack-leg GUID stability, `@COUNT` semantics)
|
- `EffectiveDashboardConfiguration` (dashboard settings page) didn't display
|
||||||
unblock via the paths in `docs/AlarmProbeFindings.md`.
|
`GroupToTag` / `UntaggedSessionVisibility`, though it showed `GroupToRole`.
|
||||||
|
**Closed 2026-08-18 — shipped** (`094f2ff`; comparer propagation fixed in `ff16c69`).
|
||||||
|
- ApiKeysPage's `ConstraintText` neither offered tag input nor listed `DashboardTags`,
|
||||||
|
and since `IsEmpty` counts tags, a tags-only key rendered `-` where a truly
|
||||||
|
unconstrained key rendered `unconstrained`. **Closed 2026-08-18 — shipped** (`c037d99`),
|
||||||
|
with the create form de-duplicating typed tags ordinal-ignore-case to match the CLI
|
||||||
|
(`fab600d`).
|
||||||
|
- `AlarmsHubPublisher` broadcast alarm transitions with `current_value`/`limit_value`
|
||||||
|
unredacted — the `ShowTagValues` redaction covered only the events hub mirror.
|
||||||
|
**Closed 2026-08-18** together with the `/browse` residual: `ShowTagValues` now gates
|
||||||
|
all three seams (`eff17d1`, `1ea6f60`, `e50ec49`).
|
||||||
|
- The alarm probes' remaining questions (ack-leg GUID stability, `@COUNT` semantics).
|
||||||
|
**Narrowed 2026-08-18** by a second probe attempt that used the secured-write verb the
|
||||||
|
first attempt lacked (`90331b6`, `540a118`):
|
||||||
|
- `@COUNT` — **answered.** It reports records in the reply, not matching records in the
|
||||||
|
provider, so exact truncation detection through it is permanently refuted; the
|
||||||
|
heuristic in `IsTruncatedFetch` stays as the only available signal.
|
||||||
|
- clear-then-re-raise GUID — **answered.** The re-raise carries a new GUID that replaces
|
||||||
|
the record rather than coexisting with it, which makes the existing `Clear`+`Raise`
|
||||||
|
handling correct as written.
|
||||||
|
- ack leg — **still blocked**, now with a sharper cause: the ack returns `rc=0` and is
|
||||||
|
accepted but inert, so the GUID never moves. Remaining unblock paths are recorded in
|
||||||
|
`docs/AlarmProbeFindings.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -448,3 +448,56 @@ clear obj/bin and rebuild, not a regression. Record all counts.
|
|||||||
payload-case row), closeout-plan follow-ups→Task 9 only.
|
payload-case row), closeout-plan follow-ups→Task 9 only.
|
||||||
- Task 7/8 run against windev over `ssh windev` (PowerShell); psbridge is fallback.
|
- Task 7/8 run against windev over `ssh windev` (PowerShell); psbridge is fallback.
|
||||||
- Do not merge to `main` without user instruction.
|
- Do not merge to `main` without user instruction.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## As-built notes (execution record, 2026-08-18)
|
||||||
|
|
||||||
|
All 9 tasks completed on `feat/followup-closeout`; every classification-driven review
|
||||||
|
chain resolved **Approved**. The final integration review came back *Ready with
|
||||||
|
reservations* — all of its non-blocking findings were fixed in `c3c603f`, leaving no
|
||||||
|
open review item.
|
||||||
|
|
||||||
|
Verification: macOS `NonWindows.slnx` 0W/0E; gateway filtered suites green (dashboard
|
||||||
|
276/276, alarm suites green); `scripts/check-codegen.ps1` 4/4 on macOS. **The full
|
||||||
|
five-language client matrix ran locally for the first time** — dotnet 133 passed /1
|
||||||
|
skipped, Go clean, Rust 108, Python 168 passed /1 skipped, Java 131. Windev at `90331b6`:
|
||||||
|
full `slnx` 0W/0E (one transient MSB4166 node crash, clean on retry), worker x86 523
|
||||||
|
passed / 11 skipped plus **one pre-existing deterministic failure that reproduces on
|
||||||
|
`main`** (`WorkerPipeSessionTests.RunAsync_LongInFlightCommandThatKeepsPumping_DoesNotFaultAndDeliversReply`),
|
||||||
|
gateway 1151/1151 on rerun (the known windev load flake), live MXAccess smoke 8/8. The
|
||||||
|
comment- and prose-only commits landed after `90331b6` are unverified on windev by
|
||||||
|
design; they need only a cheap tip re-build there.
|
||||||
|
|
||||||
|
- **A session-limit outage interrupted four reviewers mid-run.** All four resumed
|
||||||
|
cleanly on retry; no review was lost or silently truncated.
|
||||||
|
- **Task 2 surfaced two plan defects, both absorbed rather than deferred.** The spec
|
||||||
|
claimed the Java CLI renders alarm frames as generic JSON; it is in fact an exhaustive
|
||||||
|
`switch` that does not compile without an arm for the new case. The spec's work list
|
||||||
|
also omitted the Java generated-bindings tree.
|
||||||
|
- **A reviewer's Minor-2 was refuted, and the doc defect behind it fixed instead.** The
|
||||||
|
finding assumed alarm records coexist after a re-raise; fetch evidence from the probe
|
||||||
|
rig shows the new GUID replaces the record. The prose that implied coexistence was
|
||||||
|
corrected rather than the code.
|
||||||
|
|
||||||
|
Follow-ups recorded, not started:
|
||||||
|
|
||||||
|
- `WorkerPipeSessionTests.RunAsync_LongInFlightCommandThatKeepsPumping_DoesNotFaultAndDeliversReply`
|
||||||
|
fails deterministically on windev **and on `main`** — pre-existing, needs its own
|
||||||
|
investigation.
|
||||||
|
- `check-codegen.ps1` Check 4 is unrunnable on Windows: the `protoc-gen-go` version banner
|
||||||
|
carries a `.exe` suffix that the exact-string compare in
|
||||||
|
`clients/go/generate-proto.ps1:10,55` does not tolerate.
|
||||||
|
- Windev has `protoc-gen-go-grpc` 1.6.1 against the repo's pinned 1.6.2.
|
||||||
|
- `clients/java`'s `checkGeneratedClean` is dead under Gradle 9 (`Project.exec` was
|
||||||
|
removed); it needs `ExecOperations` injection to work again.
|
||||||
|
- `SettingsPage` renders every `EffectiveDashboardConfiguration` member except
|
||||||
|
`RecentFaultLimit` / `RecentSessionLimit` (pre-existing, predates this branch).
|
||||||
|
- The ack-leg probe stays blocked; unblock paths are in `docs/AlarmProbeFindings.md`.
|
||||||
|
- The dashboard `AlarmsPage` truncation banner is still poll-driven — it could consume
|
||||||
|
the new `snapshot_status` feed frame instead.
|
||||||
|
|
||||||
|
Explicitly decided, not an omission: **`../scadaproj/CLAUDE.md` needs no update.** The
|
||||||
|
umbrella index records the *set* of `.proto` files this repo owns, and that set is
|
||||||
|
unchanged — Task 1 added a message and a field inside an existing proto, not a new
|
||||||
|
contract file.
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
{"id": 5, "subject": "Task 5: Settings page — GroupToTag + UntaggedSessionVisibility rows", "status": "completed", "commits": ["094f2ff", "ff16c69"], "review": "Approved; comparer-propagation fix applied and re-verified"},
|
{"id": 5, "subject": "Task 5: Settings page — GroupToTag + UntaggedSessionVisibility rows", "status": "completed", "commits": ["094f2ff", "ff16c69"], "review": "Approved; comparer-propagation fix applied and re-verified"},
|
||||||
{"id": 6, "subject": "Task 6: ApiKeysPage — DashboardTags in ConstraintText + create-form input", "status": "completed", "commits": ["c037d99", "fab600d"], "review": "Spec compliant; code review Approved after dedupe/help-text fix, re-review Approved"},
|
{"id": 6, "subject": "Task 6: ApiKeysPage — DashboardTags in ConstraintText + create-form input", "status": "completed", "commits": ["c037d99", "fab600d"], "review": "Spec compliant; code review Approved after dedupe/help-text fix, re-review Approved"},
|
||||||
{"id": 7, "subject": "Task 7: Wnwrap alarm-probe retry — secured-write path (windev)", "status": "completed", "commits": ["90331b6", "540a118"], "review": "Approved after ack-comment fix; Q2 answered (@COUNT = records-in-reply, exact detection refuted), clear/re-raise GUID answered (new instance), ack leg re-blocked (accepted-but-inert)"},
|
{"id": 7, "subject": "Task 7: Wnwrap alarm-probe retry — secured-write path (windev)", "status": "completed", "commits": ["90331b6", "540a118"], "review": "Approved after ack-comment fix; Q2 answered (@COUNT = records-in-reply, exact detection refuted), clear/re-raise GUID answered (new instance), ack leg re-blocked (accepted-but-inert)"},
|
||||||
{"id": 8, "subject": "Task 8: Windev full verification", "status": "completed", "verifiedAt": "90331b6", "result": "slnx 0W/0E; worker x86 523 pass + 1 pre-existing deterministic failure (reproduces on main); gateway 1151/1151 on rerun; live smoke 8/8; check-codegen 1-3 pass, Check 4 blocked by pre-existing Windows .exe pin bug (substance validated out-of-band); final comment-only tip re-check pending after bookkeeping"},
|
{"id": 8, "subject": "Task 8: Windev full verification", "status": "completed", "verifiedAt": "90331b6", "result": "slnx 0W/0E; worker x86 523 pass + 1 pre-existing deterministic failure (reproduces on main); gateway 1151/1151 on rerun; live smoke 8/8; check-codegen 1-3 pass, Check 4 blocked by pre-existing Windows .exe pin bug (substance validated out-of-band); final comment-only tip re-check pending after bookkeeping", "residual": "pending-external: cheap windev tip re-build covering the comment/prose-only commits landed after 90331b6"},
|
||||||
{"id": 9, "subject": "Task 9: Bookkeeping — follow-ups closure + plan record", "status": "pending", "blockedBy": [8]}
|
{"id": 9, "subject": "Task 9: Bookkeeping — follow-ups closure + plan record", "status": "completed", "commit": "self (this bookkeeping commit)", "review": "None (trivial classification); closes the prior plan's follow-up block, appends the as-built record, and corrects the CLAUDE.md apikey sample"}
|
||||||
],
|
],
|
||||||
"lastUpdated": "2026-08-17"
|
"lastUpdated": "2026-08-18"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user