chore(phase0): name validation, golden frames, daemon into library tree, TSan daemon lane

Item 8a — ConfigValidator warns on non-identifier variable/event/alarm/
command names ([A-Za-z_][A-Za-z0-9_]*): language bindings expose names as
kwargs/attributes, so 'Chamber Pressure' would be unusable in the planned
Python client. Warning not error — the wire doesn't care. Tested (4 warning
sites + good-name negative).

Item 4 tail — golden frames for S5F1 (Binary ALCD / U4 ALID / ASCII ALTX)
and a composed S6F11 (the production-critical report shape), bytes hand-
computed from E5 encoding rules: external pins on message composition.

Item 7 — equipment_service.hpp moved to include/secsgem/daemon/ (apps/
include-path hack removed) and a TSan daemon lane added locally + in CI.
tools/tsan.supp suppresses races whose accesses sit entirely inside the
UNinstrumented system libgrpc/libgpr/libabsl (epoll wakeups, absl Mutex
GraphCycles bookkeeping); our frames stay fully checked. The lane earned its
keep on first run: it caught a REAL threading-contract violation — a daemon
test reading model stores from the test thread while the io thread serviced
posted writes — fixed to use read_sync, exactly per the documented contract.
Now TSan-clean under halt_on_error=1 in the full production threading shape.

Suites: core 470/3068, daemon Release+TSan 125/125 each.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 22:28:33 +02:00
parent e6ee927900
commit cf230d4119
11 changed files with 159 additions and 26 deletions
+18 -16
View File
@@ -48,8 +48,9 @@ host and stays conformant while the tool software restarts/upgrades/crashes.
-~~**Interop harnesses are manual.**~~ `tools/run_interop.sh` runs all nine
validation steps with one command (verified green); CI lanes added, pending
first-push verification (Phase 0 item 2).
- **TSan lane doesn't cover the daemon.** `secs_gemd_tests` should also be
built/run under `-DSECSGEM_TSAN=ON` once the control-state mirror lands.
- ~~**TSan lane doesn't cover the daemon.**~~ Covered locally + in CI
with `tools/tsan.supp` (third-party-only suppressions). Caught + fixed a
real test-side contract violation on its first run.
- ⚠️ **macOS bind-mount staleness can break Docker builds mid-edit** (a build
reading a half-synced source file). Not a product bug; re-run the build.
@@ -101,12 +102,10 @@ debts tax every later phase, and the most valuable tests aren't automated.
localhost, no docker-in-docker). ⬜ Verify the lanes on the first push.
3.**Fix `CompleteCommand` proto comment** — it described the rejected
blocking model; now states the HCACK-4 contract.
4. 🚧 **Table-driven handler conformance test**
`tests/test_handler_conformance.cpp`: one ordered scenario drives 53 of the
56 handlers through `router.dispatch` in-process (236 assertions), asserting
paired replies, control-state landings, and the SxF0 abort fallback.
Message-level golden frames: seeded with a hand-computed (E5-rules, not
codec-derived) S1F13 pin — ⬜ extend to S5F1 + composed S6F11 (TODO in file).
4. **Table-driven handler conformance test**one ordered scenario
drives 53 of the 56 handlers through `router.dispatch` (236 assertions).
Golden frames: S1F13, S5F1, and a composed S6F11, all hand-computed from
E5 rules (external pins, not codec-derived).
5.**Decompose `register_default_handlers` per GEM capability** (it is a
relocated main(), not a designed component) and replace magic constants
(SVIDs 1/2 `refresh()`, CEIDs 400/401) with YAML role bindings
@@ -115,14 +114,17 @@ debts tax every later phase, and the most valuable tests aren't automated.
6.**Standardize the mutable-read pattern**`EquipmentRuntime::read_sync`
(post-to-io + future with deadline; nullopt => UNAVAILABLE at the RPC edge).
Precedent set by `GetVariables`; every future mutable read copies it.
7. ⬜ Move `apps/equipment_service.hpp` into the library tree
(`include/secsgem/daemon/`) once Phase B grows it; add a TSan-built
`run_async` + concurrent-RPC daemon test (today's daemon tests only poll()).
8. 🚧 Validate names are identifier-safe in `ConfigValidator` (the Python
client's kwargs API depends on it) — ⬜. Generalized format-compliance
property test (iterates ALL configured variables via gRPC, asserts each
keeps its declared wire format) — ✅, plus an unset-`Value` guard at the
RPC edge (was silently writing ASCII "").
7. `equipment_service.hpp` moved to `include/secsgem/daemon/` (apps/
include-path hack removed). TSan daemon lane added locally + in CI
(`tools/tsan.supp` suppresses UNinstrumented system libgrpc/libabsl
internals only — our frames stay checked). The lane caught a real
contract violation on its first run (a test reading the model from the
test thread under run_async — fixed to read_sync); now TSan-clean with
halt_on_error=1.
8. ✅ Identifier-safe name validation: `ConfigValidator` warns (not errors)
on non-identifier variable/event/alarm/command names — bindings expose
names as kwargs/attributes. Format-compliance property test ✅; unset-
`Value` guard ✅.
### Phase A — finish the universal daemon surface (small, unblock vendors)
1.`GetVariables``from_item` reverse conversion (scalar for 1-element