fix(consumer-drift): stop runtime main going red on every release (propagate set + token-scope reconcile) #160

Merged
core-devops merged 3 commits from fix/propagate-consumers-derive-from-drift-ssot into main 2026-06-22 04:02:48 +00:00
Member

Problem

consumer-drift / runtime-ssot-consumers (push) reds runtime main after a release for two independent reasons, neither of which is a real runtime regression:

Bug 1 — propagate set narrower than the guard set (no automation to converge)

  • check_consumer_runtime_drift.py DEFAULT_CONSUMERS (the guard) enforces 10 templates + molecule-core.
  • propagate_runtime_version.py TEMPLATE_CONSUMERS (the auto-bump bot) was a hand-maintained 4-template subset (claude-code, hermes, openclaw, codex).

So langgraph / autogen / google-adk / crewai / deepagents / gemini-cli all pin .runtime-version, are FAILED by the guard when they drift, but the bot never opened a bump PR for them — main stays red after every release with no path to green but hand-authoring. (Those 6 + an un-merged hermes/openclaw auto-PR stack are exactly what was red at 0.3.44.)

Bug 2 — reconcile hard-fails on a token-scope gap (exit 2 = red)

The job log showed the real failure was not drift findings (exit 1) but exit 2:

error: org repo listing failed (HTTP 403): token does not have at least one of
required scope(s), required=[read:organization], token scope=write:repository

reconcile_org_consumers/orgs/molecule-ai/repos needs read:organization; the CI DISPATCH_TOKEN only has write:repository. A token-scope/config gap was being conflated with a runtime regression — the same anti-pattern consumer-drift.yml already guards against for an absent token, but not for a present-but-underscoped one.

Fix

Bug 1 — derive TEMPLATE_CONSUMERS from the guard's DEFAULT_CONSUMERS (template subset, minus EXEMPT_CONSUMERS). The two lists now share one SSOT and can never diverge again. molecule-core excluded by construction; over-inclusion is safe (plan_consumer skips a no-pin repo).

Bug 2 — raise ReconcileUnavailable on 401/403 from the org listing; main() warns loudly + skips the (advisory) blind-spot reconcile, falling through to the pin-drift check (which reads each enumerated consumer directly, no org scope needed). SSOT enforcement is unaffected. The durable complement is to grant the CI token read:organization (operator/owner action) — see follow-up below.

Verification

  • tests/test_propagate_runtime_version.py, scripts/test_propagate_runtime_version_dual_pin.py, tests/test_consumer_runtime_drift_guard.py25 passed locally (incl. 2 new tests for the 403 → warn/skip path).
  • TEMPLATE_CONSUMERS resolves to all 10 templates.
  • Live drift guard run with a read:organization-capable token: Runtime SSOT drift guard passed for 11 consumer repo(s) (all pins now 0.3.44).

Out-of-band remediation already done (clears the current red)

All 10 templates merged to runtime 0.3.44 (8 bump PRs reviewed + merged; stale superseded auto-PRs closed). This PR prevents recurrence.

Follow-up (operator)

Grant DISPATCH_TOKEN (the molecule-runtime-release-bot identity) read:organization so the blind-spot reconcile runs for real instead of being skipped. Until then it degrades gracefully (warn, not red).

🤖 Generated with Claude Code

## Problem `consumer-drift / runtime-ssot-consumers (push)` reds runtime `main` after a release for **two independent reasons**, neither of which is a real runtime regression: ### Bug 1 — propagate set narrower than the guard set (no automation to converge) - `check_consumer_runtime_drift.py` `DEFAULT_CONSUMERS` (the **guard**) enforces **10 templates + molecule-core**. - `propagate_runtime_version.py` `TEMPLATE_CONSUMERS` (the **auto-bump bot**) was a hand-maintained **4-template subset** (claude-code, hermes, openclaw, codex). So `langgraph / autogen / google-adk / crewai / deepagents / gemini-cli` all pin `.runtime-version`, are FAILED by the guard when they drift, but the bot **never opened a bump PR** for them — main stays red after every release with no path to green but hand-authoring. (Those 6 + an un-merged hermes/openclaw auto-PR stack are exactly what was red at 0.3.44.) ### Bug 2 — reconcile hard-fails on a token-scope gap (exit 2 = red) The job log showed the real failure was **not** drift findings (exit 1) but **exit 2**: ``` error: org repo listing failed (HTTP 403): token does not have at least one of required scope(s), required=[read:organization], token scope=write:repository ``` `reconcile_org_consumers` → `/orgs/molecule-ai/repos` needs `read:organization`; the CI `DISPATCH_TOKEN` only has `write:repository`. A token-scope/config gap was being conflated with a runtime regression — the same anti-pattern `consumer-drift.yml` already guards against for an *absent* token, but not for a *present-but-underscoped* one. ## Fix **Bug 1** — derive `TEMPLATE_CONSUMERS` from the guard's `DEFAULT_CONSUMERS` (template subset, minus `EXEMPT_CONSUMERS`). The two lists now share one SSOT and can never diverge again. `molecule-core` excluded by construction; over-inclusion is safe (`plan_consumer` skips a `no-pin` repo). **Bug 2** — raise `ReconcileUnavailable` on 401/403 from the org listing; `main()` warns loudly + **skips the (advisory) blind-spot reconcile**, falling through to the pin-drift check (which reads each enumerated consumer directly, no org scope needed). SSOT enforcement is unaffected. The durable complement is to grant the CI token `read:organization` (operator/owner action) — see follow-up below. ## Verification - `tests/test_propagate_runtime_version.py`, `scripts/test_propagate_runtime_version_dual_pin.py`, `tests/test_consumer_runtime_drift_guard.py` — **25 passed** locally (incl. 2 new tests for the 403 → warn/skip path). - `TEMPLATE_CONSUMERS` resolves to all 10 templates. - Live drift guard run with a `read:organization`-capable token: `Runtime SSOT drift guard passed for 11 consumer repo(s)` (all pins now 0.3.44). ## Out-of-band remediation already done (clears the current red) All 10 templates merged to runtime `0.3.44` (8 bump PRs reviewed + merged; stale superseded auto-PRs closed). This PR prevents recurrence. ## Follow-up (operator) Grant `DISPATCH_TOKEN` (the `molecule-runtime-release-bot` identity) `read:organization` so the blind-spot reconcile runs for real instead of being skipped. Until then it degrades gracefully (warn, not red). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
core-devops added 1 commit 2026-06-22 03:55:47 +00:00
fix(propagate): derive TEMPLATE_CONSUMERS from the drift guard's DEFAULT_CONSUMERS
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
ci / lint (pull_request) Successful in 15s
ci / build (pull_request) Successful in 32s
ci / smoke-install (pull_request) Successful in 50s
ci / unit-tests (pull_request) Successful in 1m18s
ci / responsiveness-e2e (pull_request) Successful in 1m52s
1d78589f7d
The propagate bot's hand-maintained 4-template list was a strict subset of
the consumer-drift guard's 10-template DEFAULT_CONSUMERS. langgraph/autogen/
google-adk/crewai/deepagents/gemini-cli all pin .runtime-version and are
FAILED by the guard when they drift, but the bot never opened a bump PR for
them -- so runtime main went (and stayed) RED on every release that
out-paced those pins, with no automation to converge them.

Derive TEMPLATE_CONSUMERS from DEFAULT_CONSUMERS (template subset, minus
EXEMPT) so the propagate set can never again be narrower than the set the
guard enforces. molecule-core is excluded by construction (no -template- in
its name / no .runtime-version pin); a behind-but-pinless repo is handled at
runtime by plan_consumer's no-pin skip, so over-inclusion is safe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops added 1 commit 2026-06-22 04:00:22 +00:00
fix(drift): degrade org-scan reconcile to warn+skip on token-scope 403 (not red main)
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 7s
ci / lint (pull_request) Successful in 19s
ci / build (pull_request) Successful in 35s
ci / smoke-install (pull_request) Successful in 1m2s
ci / unit-tests (pull_request) Successful in 1m22s
ci / responsiveness-e2e (pull_request) Successful in 1m50s
0c09dfe5cb
The consumer-drift guard hard-failed (exit 2) when secrets.DISPATCH_TOKEN lacked
read:organization: reconcile_org_consumers -> /orgs/molecule-ai/repos 403 ->
RuntimeError -> red main. A token-scope/config gap is not a runtime regression.

Raise ReconcileUnavailable on 401/403 from the org listing and have main() warn
loudly + skip the (advisory) blind-spot reconcile, falling through to the
pin-drift check which reads each DEFAULT_CONSUMERS repo directly (no org scope
needed). Mirrors the absent-token graceful-degradation already in
consumer-drift.yml. The durable fix is to grant the CI token read:organization.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops added 1 commit 2026-06-22 04:00:25 +00:00
test(drift): cover org-scan 403 -> ReconcileUnavailable + main warn/skip
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
ci / lint (pull_request) Successful in 17s
ci / build (pull_request) Successful in 47s
ci / smoke-install (pull_request) Successful in 1m3s
ci / unit-tests (pull_request) Successful in 1m28s
ci / responsiveness-e2e (pull_request) Successful in 1m46s
df355e1f12
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
core-devops changed title from fix(propagate): derive TEMPLATE_CONSUMERS from drift guard DEFAULT_CONSUMERS (no more lists diverging -> red main) to fix(consumer-drift): stop runtime main going red on every release (propagate set + token-scope reconcile) 2026-06-22 04:00:47 +00:00
molecule-code-reviewer approved these changes 2026-06-22 04:02:43 +00:00
molecule-code-reviewer left a comment
Member

Reviewed both root-cause fixes. (1) TEMPLATE_CONSUMERS now derived from the guard's DEFAULT_CONSUMERS so the propagate set can never again be narrower than what the guard enforces (the divergence that left 6 templates un-bumped + main red). Import is robust to both in-repo and spec-loader (test) paths, registers the module in sys.modules before exec so the dataclass resolves. (2) Org-scan 403 (token lacks read:organization) now raises ReconcileUnavailable -> main() warns+skips the advisory blind-spot reconcile instead of exit 2, while the actual pin-drift SSOT check still runs against the enumerated set. New tests cover both. 25 tests green. LGTM.

Reviewed both root-cause fixes. (1) TEMPLATE_CONSUMERS now derived from the guard's DEFAULT_CONSUMERS so the propagate set can never again be narrower than what the guard enforces (the divergence that left 6 templates un-bumped + main red). Import is robust to both in-repo and spec-loader (test) paths, registers the module in sys.modules before exec so the dataclass resolves. (2) Org-scan 403 (token lacks read:organization) now raises ReconcileUnavailable -> main() warns+skips the advisory blind-spot reconcile instead of exit 2, while the actual pin-drift SSOT check still runs against the enumerated set. New tests cover both. 25 tests green. LGTM.
core-security approved these changes 2026-06-22 04:02:44 +00:00
core-security left a comment
Member

Security review: changes are CI-tooling only. No new secret handling; the import fallback reads a same-repo sibling file by file-relative path (no traversal/injection). The 403 soft-skip narrows to 401/403 only and degrades the ADVISORY reconcile, not the enforcing pin-drift check, so SSOT enforcement is preserved. Approve.

Security review: changes are CI-tooling only. No new secret handling; the import fallback reads a same-repo sibling file by __file__-relative path (no traversal/injection). The 403 soft-skip narrows to 401/403 only and degrades the ADVISORY reconcile, not the enforcing pin-drift check, so SSOT enforcement is preserved. Approve.
core-devops merged commit dcd16f9ba0 into main 2026-06-22 04:02:48 +00:00
core-devops deleted branch fix/propagate-consumers-derive-from-drift-ssot 2026-06-22 04:02:48 +00:00
Sign in to join this conversation.
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-ai-workspace-runtime#160