SCF 2026.2: rename-resilient sheet/column resolution - #3
Merged
Merged
Conversation
Upstream renamed the "Authoritative Sources" tab to "Focal Documents" and shortened the compensating-controls "SCF Control #" header to "SCF #", breaking the weekly autopilot twice (issues #1, #2). The validation gate is fail-fast, so only the tab rename ever reached the filed report. Resolution is now alias-capable rather than single-pattern: - SHEET_PATTERNS maps each logical kind to an ordered tuple of accepted names (current first, historical after), so one parser reads every supported release. Sheet.col() takes the same treatment via `alts`. - Tab names match with norm_key, the normalization column headers already got, so invisible whitespace drift no longer summons a human. - overrides.json gains a `resolution` section carrying sheets[]/columns[] fallbacks, so a rename found in production is a JSON edit rather than a release. Operator aliases are tried only after the built-in patterns miss and raise a resolution-alias WARN, so they get folded into the parser later; a built-in pattern always wins over an alias. Three latent bugs that would have surfaced immediately after the rename fix: - The version-named catalog was never pruned on a bump. find_catalog takes the first sorted scf-*.json, so a leftover scf-2026-1-1.json would have shadowed scf-2026-2.json and published the previous release's data. - The summon-a-human step filed a new issue every cron run instead of commenting on the open one (hence two identical issues), and a failing hermetic gate filed nothing at all. - _structural_failure labelled every ColumnResolveError as sheet-resolve (every such message contains the word "sheet"), str() on these KeyError subclasses emitted the quoted repr, and the report's workbook sha256 was hardcoded empty. validate_overrides_file was dead code; it now runs as part of `validate`. The MON-03.3 evidence-request typo persists at 2026.2, so its override is no longer version-scoped — autopilot stays hands-off until upstream fixes it. Dataset: 1,534 controls across 34 families (new QTS, Quantum Security), 252 crosswalk frameworks, 5,956 AOs, 316 ERLs, 1,371 compensating controls, 42 threats (new MT-28). All 252 source rows now reconcile exact, where 2026.1.1 needed fuzzy matching and produced 5 warnings. Parsing the 2026.1.1 workbook still reproduces the committed 2026.1.1 dataset byte-for-byte: the aliases are additive, not a cutover.
The new catalog-prune test called `parse` without --report, whose default is the repo's validation/validation-report.json — so running the suite overwrote the committed release report with fixture output. Also regenerate the 2026.2 report with the workbook at the relative path autopilot uses, instead of a local absolute path. Same sha256, and data/ is byte-identical.
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 #1. Closes #2.
Upstream renamed the
Authoritative Sourcestab toFocal Documentsand shortened the compensating-controlsSCF Control #header toSCF #, breaking the weekly autopilot twice. The gate is fail-fast, so only the tab rename ever reached the filed report —reconciliationin the run artifact was empty.I measured the blast radius against the real 2026.2 workbook rather than inferring it. Those two renames were the entire code-level break.
Rename resilience
SHEET_PATTERNSmaps each logical kind to an ordered tuple of accepted names (current first, historical after), so one parser reads every supported release.Sheet.col()gets the same viaalts=.norm_key— the normalization column headers already got — so invisible whitespace drift no longer summons a human.overrides.jsongains aresolutionsection (sheets[]/columns[]), so a rename found in production is a JSON edit, not a release. Operator aliases are tried only after the built-in patterns miss and raise aresolution-aliasWARN so they get folded into the parser later; a built-in pattern always wins over an alias.Latent bugs fixed
These would all have surfaced immediately after the rename fix:
find_catalogtakes the first sortedscf-*.json— a leftoverscf-2026-1-1.jsonwould have shadowedscf-2026-2.jsonand published the previous release's data, failing the hermetic gate confusingly with no issue filed._structural_failurelabelled everyColumnResolveErrorassheet-resolve(every such message contains the word "sheet"),str()on theseKeyErrorsubclasses emitted the quoted repr — the escaped mess visible in SCF 2026.2: validation gate failed #1 — and the report'sworkbook_sha256was hardcoded empty.validate_overrides_filewas dead code; it now runs as part ofvalidate.The MON-03.3 evidence-request typo persists at 2026.2, so its override is no longer version-scoped — autopilot stays hands-off until upstream fixes it, at which point the existing
override-unusedWARN surfaces it.Dataset
1,534 controls across 34 families (new
QTS, Quantum Security) · 252 crosswalk frameworks · 5,956 AOs · 316 ERLs · 1,371 compensating controls · 42 threats (newMT-28).Reconciliation is better than 2026.1.1: all 252 source rows match
exact, where 2026.1.1 needed fuzzy matching and produced 5 warnings — upstream added explicit year suffixes to framework headers. Crosswalkapi_ids come from the Sources sheet FDI column, not header text, so no slugs were renamed by that churn.Two fields drift heavily but legitimately, verified against the raw sheets rather than assumed:
threatson 99.9% of controls (MT-28 maps to nearly everything) andmaturityon 77% (prose edits).Verification
nix fmtclean,nix flake check -Lgreen.{'fail': 0, 'warn': 3, 'resolved': 1, 'ok': True}— the 3 warns are the non-framework MCR/DSR columns.Follow-up (not in this PR)
praxis/scripts/generate-facts.py:50hardcodesscf-2026-1-1.json. The catalog filename carries the version, so it is nowscf-2026-2.json. Nothing breaks today — praxis, policypress and grc are all pinned to the2026.1.1tag — but praxis needs de-hardcoding before it bumps. Noted inCLAUDE.md.After merge: dispatch Update SCF with tag
2026.2to confirm autopilot completes unattended.