You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This is a SHAFT maintenance-controller defect, not an upstream Graphifyy install break. Graphify extract completed and wrote graph.json (38,685 nodes). Our audit then failed closed.
Reproduced on primary main @ 03261bfc92 after py -3 tools/repository-map/graphify_maintenance.py refresh --root .
🐛 Describe the Bug
graphify_maintenance.py refresh deletes .shaft-source-revision.json, extracts successfully, then run_audit raises Graphify coverage audit found actionable parser gaps. Cluster and --record-current never run. resolve_graph_out.py --check stays stale - cache has no indexed revision marker.
The fail-closed bucket is unclassified_unexpected. On 2026-08-19 that bucket is 115 tracked files:
Count
Suffix
Meaning
111
.json
Graphify CODE type. Zero-node JSON is already policy expected_data_onlywhen the path is in the manifest. Live extract leaves them out of the manifest, so they fall through to unexpected.
2
.css
Javadoc vendor CSS. Noise. Should be .graphifyignore.
2
.java
Real remaining gap: shaft-intellij/src/test/java/com/shaft/intellij/build/IntellijBuildJdkContractTest.java and IntellijRunIdeCrashLogContractTest.java are tracked, not ignored, and not in the manifest.
expected_data_only is 0 on this cache. unexpected_parser_gap is 0. covered is 2159 (every manifest path has a node). Extract also printed 105 source file(s) produced zero nodes and are absent from the graph (marketplace.json, settings.json, …) and skipped credentials.json as sensitive.
So the standing rule in chaos-engine/profiles/shaft/references/graphify.md — “JSON sources with no emitted nodes remain visible expected data-only inputs; SQL or other parser gaps stop the refresh before the marker” — is inverted in code. JSON that Graphify dropped from the manifest look like a new suffix and block freshness.
This is fixable entirely in SHAFT: tools/repository-map/graphify_maintenance.py, .graphifyignore, and tests/scripts/test_graphify_maintenance.py. Do not wait on a Graphifyy pin bump (#5120 is closed/upstream).
🔁 Steps to Reproduce
On the primary checkout at current origin/main, run py -3 tools/repository-map/graphify_maintenance.py refresh --root .
Extract finishes and writes graphify-out/graph.json.
Audit prints a large unclassified_unexpected list dominated by .json.
Refresh exits 1: Graphify coverage audit found actionable parser gaps.
py -3 tools/repository-map/resolve_graph_out.py --check reports no indexed revision marker.
Tracked .json that Graphify did not put in the manifest (zero-node or sensitive skip) classify as expected_data_only. They stay visible. They do not fail the audit.
Javadoc .css is ignored by policy.
Only a new unexpected family, unexpected_parser_gap, or a tracked first-class source Graphify should have ingested (the two .java files until explained) fails the audit.
After a green audit, refresh clusters and records .shaft-source-revision.json for 03261bfc92 (or whatever HEAD was).
test_88_json_data_files_are_visible_but_nonfatal still passes, and a new test covers JSON absent from the manifest.
Refresh unlinks the marker first (refresh()), then dies in run_audit, so a successful extract still leaves the cache officially stale.
💻 Minimal Reproducible Code
Current classification hole (tools/repository-map/graphify_maintenance.py):
# Manifest JSON with zero nodes → expected_data_only (nonfatal)elifPath(path).suffix.lower() ==".json":
classification="expected_data_only"# Tracked JSON not in the manifest → unclassified_unexpected (fatal)elifunclassified_allowlisted(path):
result["unclassified_allowlisted"].append(path)
else:
result["unclassified_unexpected"].append(path)
UNCLASSIFIED_ALLOWLIST_SUFFIXES has .properties / .toml / .xml / .feature / … and not.json, because JSON is supposed to be a Graphify code type.
🌍 Environment
Field
Value
SHAFT_ENGINE version
main03261bfc92
Graphify
pinned graphifyy via uv tool run in graphify_maintenance.py
Check: stale - cache has no indexed revision marker.
User Scenarios & Testing
User Story 1 - JSON omitted from the manifest does not block freshness (Priority: P1)
Independent Test: Fixture with a tracked data/config.json that is not a manifest key. Audit exit 0; path in expected_data_only.
Acceptance Scenarios:
Given a tracked .json file Graphify left out of manifest.json, When audit runs, Then it is expected_data_only and the process exits 0 if nothing else is unexpected.
Given the same file in the manifest with no graph node, When audit runs, Then it remains expected_data_only (existing test).
User Story 2 - Refresh records the marker on current main (Priority: P1)
Independent Test: After the classification fix (and CSS ignore), one primary refresh --root . at current origin/main. resolve_graph_out.py --check prints the HEAD SHA.
Acceptance Scenarios:
Given only the 111 JSON + 2 CSS leftovers from this ticket, When refresh runs, Then audit is green and .shaft-source-revision.json matches git rev-parse HEAD.
Given a new tracked suffix that is not allowlisted and not .json, When audit runs, Thenunclassified_unexpected still fails (existing new.weirdsuffix test).
User Story 3 - The two missing Java tests stay fail-closed until explained (Priority: P2)
Independent Test: Audit still lists those two paths unless they enter the manifest or a written ignore/allowlist with a comment.
Acceptance Scenarios:
GivenIntellijBuildJdkContractTest.java and IntellijRunIdeCrashLogContractTest.java still absent from the manifest, When JSON/CSS are reclassified, Then they remain unclassified_unexpected (or unexpected_parser_gap if they appear in the manifest with zero nodes) until a follow-up proves Graphify should skip them.
Given a later extract that covers them, When audit runs, Then they move to covered and no longer fail.
Edge Cases
credentials.json is Graphify-sensitive. Classifying it as expected_data_only is correct. Do not copy its contents into the graph or into Memory.
Hidden paths (.claude/settings.json, .github/issue-taxonomy.json) are tracked; they must use the same JSON rule.
package-lock.json and doctor/capture fixtures are data. Do not invent a second JSON family without a test.
Do not add *.json to .graphifyignore. That would hide product JSON we may want if Graphify later emits nodes.
Do not treat .java as expected data-only.
Do not record the freshness marker when unexpected_parser_gap or a true unexpected suffix remains.
refresh() already deletes the marker at start. A failed audit must leave it absent (current). A green audit must recreate it.
Functional Requirements
FR-001: Tracked .json not present in manifest.json classify as expected_data_only.
FR-002: Add the two javadoc *.css paths (or **/*.css if no product CSS should be graphed) to .graphifyignore.
FR-003: Keep fail-closed unclassified_unexpected for new suffixes and for the two IntelliJ Java tests until they are covered or explicitly justified.
FR-004: Tests in tests/scripts/test_graphify_maintenance.py: JSON-absent-from-manifest is nonfatal; CSS ignore; weird suffix still fatal; existing 88-JSON-in-manifest test still passes.
FR-006: Keep taxonomy labels valid (one primary, one lifecycle, at least one subsystem/module).
Success Criteria
SC-001: audit on today’s cache reports expected_data_only >= 111 and unclassified_unexpected is only the two Java files (or empty if those get a justified disposition in the same PR).
SC-002: refresh --root . records .shaft-source-revision.json when the only leftovers are expected JSON (and ignored CSS).
SC-003: Removing the JSON-absent-from-manifest rule makes a focused unit test fail.
Assumptions
Graphifyy 0.9.43 still treats .json as CODE and drops zero-node JSON from the graph (and currently from the manifest). SHAFT will not wait for an upstream manifest change to record freshness.
Related to #5087 (unclassified buckets), #4687 (zero-node JSON is expected data-only), #4809 (nightly refresh owner). Does not reopen those.
✅ Pre-submission Checklist
unclassified_unexpectedas fail-closed. Restore complete Graphify extraction coverage for SQL and zero-node sources #4687 said JSON with zero nodes is expected data-only. No open ticket covers the live 2026-08-19 failure: Graphify omits those JSON files frommanifest.json, so SHAFT audit treats them as unexpected and never records the freshness marker.graph.json(38,685 nodes). Our audit then failed closed.main@03261bfc92afterpy -3 tools/repository-map/graphify_maintenance.py refresh --root .🐛 Describe the Bug
graphify_maintenance.py refreshdeletes.shaft-source-revision.json, extracts successfully, thenrun_auditraisesGraphify coverage audit found actionable parser gaps. Cluster and--record-currentnever run.resolve_graph_out.py --checkstaysstale - cache has no indexed revision marker.The fail-closed bucket is
unclassified_unexpected. On 2026-08-19 that bucket is 115 tracked files:.jsonexpected_data_onlywhen the path is in the manifest. Live extract leaves them out of the manifest, so they fall through to unexpected..css.graphifyignore..javashaft-intellij/src/test/java/com/shaft/intellij/build/IntellijBuildJdkContractTest.javaandIntellijRunIdeCrashLogContractTest.javaare tracked, not ignored, and not in the manifest.expected_data_onlyis 0 on this cache.unexpected_parser_gapis 0.coveredis 2159 (every manifest path has a node). Extract also printed105 source file(s) produced zero nodes and are absent from the graph(marketplace.json, settings.json, …) and skippedcredentials.jsonas sensitive.So the standing rule in
chaos-engine/profiles/shaft/references/graphify.md— “JSON sources with no emitted nodes remain visible expected data-only inputs; SQL or other parser gaps stop the refresh before the marker” — is inverted in code. JSON that Graphify dropped from the manifest look like a new suffix and block freshness.This is fixable entirely in SHAFT:
tools/repository-map/graphify_maintenance.py,.graphifyignore, andtests/scripts/test_graphify_maintenance.py. Do not wait on a Graphifyy pin bump (#5120 is closed/upstream).🔁 Steps to Reproduce
origin/main, runpy -3 tools/repository-map/graphify_maintenance.py refresh --root .graphify-out/graph.json.unclassified_unexpectedlist dominated by.json.Graphify coverage audit found actionable parser gaps.py -3 tools/repository-map/resolve_graph_out.py --checkreports no indexed revision marker.py -3 tools/repository-map/graphify_maintenance.py audit --root .and count suffixes (2026-08-19: 111 json / 2 css / 2 java).✅ Expected Behavior
.jsonthat Graphify did not put in the manifest (zero-node or sensitive skip) classify asexpected_data_only. They stay visible. They do not fail the audit..cssis ignored by policy.unexpected_parser_gap, or a tracked first-class source Graphify should have ingested (the two.javafiles until explained) fails the audit..shaft-source-revision.jsonfor03261bfc92(or whatever HEAD was).test_88_json_data_files_are_visible_but_nonfatalstill passes, and a new test covers JSON absent from the manifest.❌ Actual Behavior
Refresh unlinks the marker first (
refresh()), then dies inrun_audit, so a successful extract still leaves the cache officially stale.💻 Minimal Reproducible Code
Current classification hole (
tools/repository-map/graphify_maintenance.py):UNCLASSIFIED_ALLOWLIST_SUFFIXEShas.properties/.toml/.xml/.feature/ … and not.json, because JSON is supposed to be a Graphify code type.🌍 Environment
main03261bfc92graphifyyviauv tool runingraphify_maintenance.pypy -3 tools/repository-map/graphify_maintenance.py refresh --root .📋 Console Logs
Extract (2026-08-19): 244 code files AST-extracted; 219 unclassified skips (Dockerfile /
.feature/ … — those correctly land inunclassified_allowlisted= 251); 1 sensitive skipcredentials.json; 105 zero-node sources absent from the graph.Audit:
covered=2159,expected_data_only=0,unclassified_unexpected=115.Check:
stale - cache has no indexed revision marker.User Scenarios & Testing
User Story 1 - JSON omitted from the manifest does not block freshness (Priority: P1)
Independent Test: Fixture with a tracked
data/config.jsonthat is not a manifest key. Audit exit 0; path inexpected_data_only.Acceptance Scenarios:
.jsonfile Graphify left out ofmanifest.json, When audit runs, Then it isexpected_data_onlyand the process exits 0 if nothing else is unexpected.expected_data_only(existing test).User Story 2 - Refresh records the marker on current main (Priority: P1)
Independent Test: After the classification fix (and CSS ignore), one primary
refresh --root .at currentorigin/main.resolve_graph_out.py --checkprints the HEAD SHA.Acceptance Scenarios:
.shaft-source-revision.jsonmatchesgit rev-parse HEAD..json, When audit runs, Thenunclassified_unexpectedstill fails (existingnew.weirdsuffixtest).User Story 3 - The two missing Java tests stay fail-closed until explained (Priority: P2)
Independent Test: Audit still lists those two paths unless they enter the manifest or a written ignore/allowlist with a comment.
Acceptance Scenarios:
IntellijBuildJdkContractTest.javaandIntellijRunIdeCrashLogContractTest.javastill absent from the manifest, When JSON/CSS are reclassified, Then they remainunclassified_unexpected(orunexpected_parser_gapif they appear in the manifest with zero nodes) until a follow-up proves Graphify should skip them.coveredand no longer fail.Edge Cases
credentials.jsonis Graphify-sensitive. Classifying it asexpected_data_onlyis correct. Do not copy its contents into the graph or into Memory..claude/settings.json,.github/issue-taxonomy.json) are tracked; they must use the same JSON rule.package-lock.jsonand doctor/capture fixtures are data. Do not invent a second JSON family without a test.*.jsonto.graphifyignore. That would hide product JSON we may want if Graphify later emits nodes..javaas expected data-only.unexpected_parser_gapor a true unexpected suffix remains.refresh()already deletes the marker at start. A failed audit must leave it absent (current). A green audit must recreate it.Functional Requirements
.jsonnot present inmanifest.jsonclassify asexpected_data_only.*.csspaths (or**/*.cssif no product CSS should be graphed) to.graphifyignore.unclassified_unexpectedfor new suffixes and for the two IntelliJ Java tests until they are covered or explicitly justified.tests/scripts/test_graphify_maintenance.py: JSON-absent-from-manifest is nonfatal; CSS ignore; weird suffix still fatal; existing 88-JSON-in-manifest test still passes.resolve_graph_out.py --checkcurrent. Nightly Add safe nightly Graphify and MemPalace refresh #4809 uses the same controller.Success Criteria
auditon today’s cache reportsexpected_data_only >= 111andunclassified_unexpectedis only the two Java files (or empty if those get a justified disposition in the same PR).refresh --root .records.shaft-source-revision.jsonwhen the only leftovers are expected JSON (and ignored CSS).Assumptions
.jsonas CODE and drops zero-node JSON from the graph (and currently from the manifest). SHAFT will not wait for an upstream manifest change to record freshness.Out of scope
CODE_EXTENSIONS(Upstream Graphifyy CODE_EXTENSIONS for SHAFT unclassified families #5120)..properties/ SPI / Docker into the graph (allowlisted; MemPalace Promote SHAFT properties, SPI, Docker, and plugin XML into MemPalace #5119).graphify-out/.📝 Additional Context
Policy already written, not implemented for the not-in-manifest case:
tests/scripts/test_graphify_maintenance.pytest_88_json_data_files_are_visible_but_nonfatalonly covers JSON keys in the manifest.