Repository navigation
fix(slm): declaration-gate every group the update playbook deploys to (#14552) - #14555
Conversation
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml.
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
Review — BLOCKThe runtime fix is correct and the reach claim is accurate for the file it covers. What blocks is the mechanism that is supposed to stop a fourth recurrence: the "derivation" is not a derivation. Reach, re-derived independently: 6/6 for this playbookDeploy-gated groups in Critical — the guard matches 33 of 60 occurrences and cannot detect its own under-derivation
return set(re.findall(r"when:\s*\"'([a-z_]+)' in group_names\"", text))That matches only the inline double-quoted spelling. Measured against the playbook: Both scans return the same five names today — but only by accident of file history. The majority of the The two tests cannot catch this:
So a future PR adding a sixth deploy-gated group in the block-scalar style — the dominant style in this very file, and the natural thing to copy — is not seen by the derivation, and both tests report green while #14513/#14552 recur a fourth time. This is the "guard narrower than its own subject" shape, and it matters more here than usual because the PR's whole argument is "hand-maintaining a list that mirrors a playbook is what let Fix before merge:
High — file this, do not fold it in: the same defect class is still open one playbook over
Your title says "the update playbook", singular, and that is honest. But the body closes as though the bug class is shut, and it is not — please open the follow-up before this merges so the closure of #14552 does not read as closure of the class. Low —
|
Review responseFinding 1 accepted and fixed — this was the real risk, and it was mine to catch. Verified before acting: That is worse than the failure this PR fixes: a loud playbook error is recoverable, a silent no-op after an explicit operator action is not. And I had flagged exactly this shape as "the real risk in this change" when dispatching the review, then did not check the write path. Fixed at the source rather than by exempting the path:
Finding 3 fixed while the file was open. You are right that it worked only by coincidence. The regex anchored on Now matches Finding 2 filed as #14567. Confirmed live rather than hypothetical: I did not fold it into this PR: the right fix there is probably to collapse the sibling aliases rather than add four more names to a gate list — which is the same enumerate-versus-derive lesson, and a larger change than unblocking the fleet update. Finding 4 noted, no change. Agreed |
Applied Black, isort, and autoflake to match code-quality checks. Triggered by workflow auto-fix-formatting.yml.
Re-review — block cleared, APPROVEThe derivation is fixed at head ( return set(re.findall(r"'([a-z_]+)'\s+in\s+group_names", text))Re-derived against the playbook at PR head:
Two residual nits, neither blocking:
The follow-up for the provisioning half is #14560 — Approving. I'll merge once the required contexts are confirmed green against the current head. |
Thinking Path
The fleet update failed again on the same
[vnc, slm-agent]node, with a different task:No Node.js, because it is not a frontend node. It was handed the frontend deploy anyway.
This is #14513 recurring, and the reason is my scoping. That fix gated
slm_server,backendandmainbecause I had checked what those plays do. I read one range ofupdate-all-nodes.yml, saw two gates, and gated one of them — leaving the other half of the same play open.Deriving the set rather than reading it immediately showed the problem was bigger than the report:
Three more than the failure that prompted this. Hand-maintaining a list that mirrors a playbook is what let
frontendthrough, and it would have let the next one through too.What Changed
_DECLARED_ONLY_GROUPSnow covers every group the update playbook deploys to:{slm_server, backend, main, frontend, aiml, npu, browser}.services/inventory_deploy_groups_test.pyderives the expected set fromupdate-all-nodes.yml— thewhen: "'x' in group_names"gates plus Play 1'shosts: slm_server— and fails if the runtime constant omits any of them. Add a component to that play without listing it here and the guard fails.The constant stays in code: production must not parse a playbook to build an inventory. Only the assertion is derived.
Verification
Not run from the checkout, by instruction — CI verifies correctness, the deployment verifies runtime.
Mutation: with #14513's constant the guard reports
missing: [aiml, browser, frontend, npu]and fails.Deliberate boundary
The node still joins sibling group names the deploy gates do not test —
ai_stack,npu_worker,browser_worker,llm_nodes. Those are not gated here because the criterion is whatupdate-all-nodes.ymlactually deploys to, and widening to every similarly-named group would be a guess rather than a derivation. If another playbook gates deploys on those names, that is a separate derivation and a separate change.redis/databaseremain reachable by detection on purpose — a node genuinely running redis should keep receiving redis updates, pinned bytest_detected_roles_merged_with_roles.Model Used
Claude Opus 5
Closes #14552