Repository navigation
Implement FORCE_GRAPH_REBUILD, which five documents already promised - #300
Merged
Merged
Conversation
…ised The variable was described in CLAUDE.md, ADR-002, DEPLOYMENT_GUIDE.md, RUNBOOK_STALE_GRAPH.md and TROUBLESHOOTING_GUIDE.md, and read by no code anywhere since a43595b (2026-03-28, the "production readiness sprint"). RUNBOOK Option B instructs an operator to `export FORCE_GRAPH_REBUILD=1` while recovering from a stale-graph incident. That command did nothing. Closes item 2 of #296. This started as a one-line correction to ENABLE_CI's documented default and did not stay one, because auditing the rest of the flags table found a flag with no implementation rather than a flag with a wrong default. The two are not the same severity: a wrong default confuses a reader, while a recovery procedure that silently fails is consulted precisely when something has already gone wrong. The choice was to implement it or delete it from five documents. Implementing is what makes the documents true, and deleting would have meant asserting that ADR-002's stated mitigation had never existed -- a claim about the past rather than a correction to the present. refresh_call_graph_if_stale() now rebuilds unconditionally when the variable is "1", checked before the stale flag so it genuinely bypasses it. Only the literal "1" enables it, matching the ENABLE_* convention used across the engine; "0", unset and "true" all leave the existing behaviour untouched. The case this covers is the one ADR-002 names as its own risk, and it is exactly the case the stale flag cannot reach: when call_graph_stale is never set -- because the node that sets it crashed -- the guard cannot fire and Step 5 reviews an outdated snapshot in silence. There is no in-state signal for an operator to correct, so the override has to come from outside the state. That is why this is an environment variable and not another state key. Every test leaves call_graph_stale unset and supplies a usable cached snapshot, so without the override the cache is what the function returns. The cases are paired: "forced rebuild returns fresh data" is satisfied by a function that always rebuilds, so unset, "0" and "true" each assert the cache is still honoured. Proven by deleting the check and confirming only the forced case went red while the other four stayed green, then restoring it. RUNBOOK Option A is corrected as well. It implied FORCE_GRAPH_REBUILD=0 disabled stale-based rebuilding and that unsetting it re-enabled it. Stale rebuilding is driven by the state flag and by no environment variable; the override only ever ADDS a rebuild. A test pins the independence of the two paths so the wording cannot drift back. ADR-002, DEPLOYMENT_GUIDE.md and TROUBLESHOOTING_GUIDE.md needed no edits: implementing the variable made them true as written. ENABLE_CI's row is corrected from `false` to `true` and now records that it is a GitHub repository variable rather than a process environment variable -- ci.yml reads vars.ENABLE_CI and falls back to true, so CI runs unless explicitly disabled. That distinction is why it behaves unlike every other flag in the table, and its absence is probably how the default came to be recorded backwards. The remaining flags in the table were checked against their read sites and match: ENABLE_JIRA, ENABLE_FIGMA, ENABLE_HEALTH_SERVER, ENABLE_METRICS, ENABLE_TRACING, ENABLE_RUNTIME_VERIFICATION and STRICT_RUNTIME_VERIFICATION. ENABLE_JENKINS and ENABLE_SONARQUBE resolve through core/config_loader.py rather than a direct getenv, which is why a first grep pass reported them missing. Counts move to 490 / 102 / 93 / 123, caught by the doc-count enforcement test rather than by hand for the third time since it landed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes item 2 of #296 — the last of the four.
This was supposed to be a one-line fix
The issue described item 2 as correcting
ENABLE_CI's documented default. Auditing the rest of the flags table instead turned up a flag with no implementation at all.FORCE_GRAPH_REBUILDis described in five files —CLAUDE.md,ADR-002,DEPLOYMENT_GUIDE.md,RUNBOOK_STALE_GRAPH.md,TROUBLESHOOTING_GUIDE.md— and read by no code anywhere sincea43595b(2026-03-28, the "production readiness sprint").RUNBOOK_STALE_GRAPH.mdOption B tells an operator to run this while recovering from a stale-graph incident:That did nothing. A wrong default confuses a reader; a recovery procedure that silently fails is consulted precisely when something has already gone wrong.
Implement, not delete
Deleting it from five documents would have meant asserting that ADR-002's stated mitigation never existed — a claim about the past rather than a correction to the present. Implementing makes the documents true.
refresh_call_graph_if_stale()now rebuilds unconditionally when the variable is"1", checked before the stale flag so it genuinely bypasses it. Only the literal"1"enables it, matching theENABLE_*convention used across the engine.Why an environment variable and not another state key
This covers exactly the case the stale flag cannot reach — the one ADR-002 names as its own risk. When
call_graph_staleis never set, because the node that sets it crashed, the guard cannot fire and Step 5 reviews an outdated snapshot in silence. There is no in-state signal for an operator to correct, so the override has to come from outside the state.Tests
Every test leaves
call_graph_staleunset and supplies a usable cached snapshot, so without the override the cache is what the function returns.The cases are paired on purpose: "forced rebuild returns fresh data" is satisfied by a function that always rebuilds, so
unset,"0"and"true"each assert the cache is still honoured. A fifth test pins thatFORCE_GRAPH_REBUILD=0does not switch off ordinary stale-based rebuilding.Proven by deleting the check: only the forced case went red; the other four stayed green. Then restored.
Documentation
RUNBOOKOption A was wrong in its own right and is corrected. It impliedFORCE_GRAPH_REBUILD=0disabled stale-based rebuilding and that unsetting it re-enabled it. Stale rebuilding is driven by the state flag and by no environment variable — the override only ever adds a rebuild. The fifth test pins that so the wording cannot drift back.ADR-002,DEPLOYMENT_GUIDE.mdandTROUBLESHOOTING_GUIDE.mdneeded no edits — implementing the variable made them true as written.ENABLE_CIgoesfalse→true, and the row now records that it is a GitHub repository variable, not a process environment variable:ci.ymlreadsvars.ENABLE_CIand falls back totrue. That distinction is why it behaves unlike every other flag in that table, and its absence is probably how the default came to be recorded backwards.The remaining flags were checked against their read sites and match.
ENABLE_JENKINSandENABLE_SONARQUBEresolve throughcore/config_loader.pyrather than a directgetenv, which is why a first grep pass reported them missing — that pass was wrong, not the flags.Verified
28 targeted tests, all six CI gates, counts caught by the doc-count enforcement test rather than by hand (third time since it landed), no line-ending flips. Pre-commit ran fully this time and ruff / black / isort all passed locally — the lint caveat carried in #298 and #299 does not apply here.
🤖 Generated with Claude Code