Skip to content

Implement FORCE_GRAPH_REBUILD, which five documents already promised - #300

Merged
piyushmakhija28 merged 1 commit into
mainfrom
fix/implement-force-graph-rebuild
Aug 7, 2026
Merged

piyushmakhija28 merged 1 commit into
mainfrom
fix/implement-force-graph-rebuild

Conversation

@piyushmakhija28

Copy link
Copy Markdown
Collaborator

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_REBUILD is described in five files — CLAUDE.md, ADR-002, DEPLOYMENT_GUIDE.md, RUNBOOK_STALE_GRAPH.md, TROUBLESHOOTING_GUIDE.md — and read by no code anywhere since a43595b (2026-03-28, the "production readiness sprint").

RUNBOOK_STALE_GRAPH.md Option B tells an operator to run this while recovering from a stale-graph incident:

export FORCE_GRAPH_REBUILD=1
python scripts/3-level-flow.py --task "your task" --start-step 10

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 the ENABLE_* 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_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.

Tests

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 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 that FORCE_GRAPH_REBUILD=0 does 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

RUNBOOK Option A was wrong in its own right and is corrected. 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. The fifth test pins that 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 goes false → true, and the row now records that it is a GitHub repository variable, not a process environment variable: ci.yml reads vars.ENABLE_CI and falls back to true. 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_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 — 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

…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>
@piyushmakhija28
piyushmakhija28 merged commit d18e1a8 into main Aug 7, 2026
6 checks passed
@piyushmakhija28
piyushmakhija28 deleted the fix/implement-force-graph-rebuild branch August 7, 2026 16:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant