Lane A (A.14-A.16): delta ingestion, weekly self-heal, schema-drift handling - #12
Merged
Conversation
…andling Three of the eight packets in this batch. A.17-A.21 were killed by the Anthropic session cap before writing anything -- checked against the filesystem per IMPLEMENTATION-PLAN section 0.3, and unlike the R.2 case earlier in this build they left NO partial artifacts, so they are not-started rather than half-done. A.14 steady-state delta ingestion. Every clock comes from the A.1 feed row -- interval, reconcile and baseline -- and an AST test verified RED against synthetic source enforces that no duration is written in Go. Reconcile and baseline are WINDOW-BOUNDARY tests via time.Truncate rather than elapsed-time tests, because "end-of-day delta once daily" must fire when the UTC day changes: a feed polled at 23:50 and again at 00:10 crosses the boundary, and an elapsed-time test would never fire on a busy feed. The delta cursor is a QUERY rather than a stored column, so it cannot disagree with the data the way a cursor that advanced past a failed write would. Exactly one thing crosses from feed content into a request -- a CVE identifier that passed an allowlist -- and DeltaLogRecord has no field for the log's own githubLink, which a reflection test enforces so a later change cannot start following it. Two guards are allowlists rather than denylists, which is now this project's default after three denylists were defeated by a symbol, a verb and a wording nobody listed: a SQL allowlist that makes a full FTS rebuild unreachable (14 forbidden statements refused, including two a verb denylist misses), and a CVE-ID allowlist that refuses traversal in three encodings, scheme, authority, NUL, CRLF smuggling and Arabic-Indic digits. A defect its own guard caught mid-build, worth recording because it is the exact shape this project keeps finding: IsCVEID trims whitespace before judging, so the string CHECKED was not the string USED and "CVE-2024-0001 " was accepted. Now the input must already be canonical and is used byte-for-byte as accepted. A.15 weekly full-baseline self-heal. It REPORTS what it found rather than silently repairing, because a self-heal that repairs quietly is indistinguishable from one that never runs -- and a disagreement between the baseline and the delta-built cache is a signal about the delta path, not noise. A.16 feed-schema-drift. Branches on CVE dataVersion explicitly and makes an unknown version a loud degraded state carrying which fields were not understood, setting S6's parse_degraded. A parser that skips an unrecognised field and emits anyway produces a finding that looks whole and is not. One decision escalated, ruled in section 6.1 (G11) A.14 could not edit internal/ingest/bootstrap, whose decoders are unexported, so it re-derived CVE 5.x / OSV / KEV decoding -- two producers writing one table from one wire format. Worse than it looks: if they diverge, A.15's weekly self-heal restores the same rows FOREVER and nothing surfaces it. A self-healing system healing toward the wrong answer is quieter than one that breaks. A.14 did the right thing available to it -- a conformance test running both importers over the same fixtures and comparing every byte-derived column -- but two implementations agreeing today is a smoke alarm, not a fix. Ruling: extract one shared decode package, owned by A.21, the first step permitted to edit both. Also recorded and NOT a defect: SyncReconcile refuses by default and should stay that way. A.8's resolveArchiveURL picks the LARGEST archive asset, which is the 570 MB midnight baseline rather than the ~17 MB end-of-day delta, so wiring reconcile to it would cost 570 MB/day and would silently BE the re-download A.14's packet forbids. Evidence: gofmt, vet, build clean; go test -count=1 ./... green across all packages. No new dependencies, no t.Skip added, so internal/SKIPPED-CONTROLS.md is unchanged.
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.
Summary
Three of the eight packets in this batch.
A.17–A.21were killed by the session cap before writing anything — checked against the filesystem per §0.3, and unlike theR.2case earlier in this build they left no partial artifacts, so they're not-started rather than half-done.A.14— steady-state delta ingestionEvery clock comes from the
A.1feed row, enforced by an AST test verified RED against synthetic source. Reconcile and baseline are window-boundary tests viatime.Truncate, not elapsed-time tests — "end-of-day delta once daily" has to fire when the UTC day changes, and a feed polled at 23:50 then 00:10 crosses the boundary where an elapsed-time test would never fire on a busy feed.The delta cursor is a query, not a stored column, so it can't disagree with the data the way a cursor that advanced past a failed write would. Exactly one thing crosses from feed content into a request — a CVE id that passed an allowlist — and
DeltaLogRecordhas no field for the log's owngithubLink, enforced by a reflection test so a later change can't quietly start following it.Two guards are allowlists, not denylists — now this project's default after three denylists were defeated by a symbol, a verb and a wording nobody listed. A SQL allowlist makes a full FTS rebuild unreachable (14 forbidden statements refused, including two a verb denylist misses); a CVE-id allowlist refuses traversal in three encodings, scheme, authority, NUL, CRLF smuggling and Arabic-Indic digits.
A defect its own guard caught mid-build, and it's the exact shape this project keeps finding:
IsCVEIDtrims whitespace before judging, so the string checked was not the string used and"CVE-2024-0001 "was accepted. Input must now be canonical already and is used byte-for-byte as accepted.A.15— weekly self-healIt reports what it found rather than silently repairing. A self-heal that repairs quietly is indistinguishable from one that never runs — and a disagreement between the baseline and the delta-built cache is a signal about the delta path, not noise to absorb.
A.16— schema driftBranches on CVE
dataVersionexplicitly and makes an unknown version a loud degraded state carrying which fields weren't understood, setting S6'sparse_degraded. A parser that skips an unrecognised field and emits anyway produces a finding that looks whole and isn't.One decision escalated — ruled as §6.1 (G11)
A.14couldn't editinternal/ingest/bootstrap, whose decoders are unexported, so it re-derived CVE 5.x / OSV / KEV decoding. Two producers writing one table from one wire format.Worse than it looks: if they diverge,
A.15's weekly self-heal restores the same rows forever and nothing surfaces it. A self-healing system healing toward the wrong answer is quieter than one that breaks.A.14did the right thing available to it — a conformance test running both importers over the same fixtures, comparing every byte-derived column ofadvisory,affectedandcve_alias. But two implementations agreeing today is a smoke alarm, not a fix. Ruling: extract one shared decode package, owned byA.21, the first step permitted to edit both.Also recorded and not a defect:
SyncReconcilerefuses by default and should stay that way.A.8'sresolveArchiveURLpicks the largest archive asset — the 570 MB midnight baseline, not the ~17 MB end-of-day delta — so wiring reconcile to it would cost 570 MB/day and would silently be the re-downloadA.14's packet forbids.Testing
No new dependencies. No
t.Skipadded, sointernal/SKIPPED-CONTROLS.mdis unchanged.