Repository navigation
Conversation
fed0c1b to
2eeb15f
Compare
2eeb15f to
5b4ea78
Compare
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
DeusData
left a comment
There was a problem hiding this comment.
Thank you for this — both defects are real, and you characterised them the way I wish every report did: valid input, the native tool accepts it, our parser reports an error region, and parse_partial_count goes up for a file that is fine. A false "partially parsed" is worse than it sounds, because it teaches people to ignore the one signal that tells them the graph is missing something. The tests are good too: sqlite_malformed_still_flagged is exactly the control that stops "fixed" from meaning "stopped reporting errors".
What I checked, so you know where this stands. A regenerated LR parser cannot be reviewed by reading 959,393 lines, so I ran the exhaustive checks we use for vendored grammars on both new blobs, and the same checks on the blobs they replace:
| sql (old → new) | tsx (old → new) | |
|---|---|---|
| bytes > 0x7F / control bytes other than LF | 0 / 0 → 0 / 0 | 0 / 0 → 0 / 0 |
| string literals containing a space | none → none | unique symbol → unique symbol |
| comments | 1 → 1 (the generator banner) | 0 → 0 |
SYMBOL_COUNT |
770 → 773 | 393 → 394 |
STATE_COUNT |
30,622 → 31,010 | 5,986 → 5,986 |
ABI / EXTERNAL_TOKEN_COUNT |
15 / 3 → 15 / 3 | 14 / 10 → 14 / 10 |
Zero non-ASCII bytes means hidden-character tricks are arithmetically impossible rather than merely unobserved; no spaced literals means no prose; and the symbol deltas (+3, +1) are the size your description predicts. scanner.c is untouched in both grammars and the licences are unchanged. I have no safety concern with these files.
What I cannot do yet is the other half — prove the bytes are upstream plus your declared change and nothing else. That is the substance of what I would like changed:
1. The grammar change itself needs to be in the repository, not only its output. MANIFEST.md describes the SQLite additions and the jsx_text_ampersand token in prose, but the grammar.js edits that produced them are not in the diff. Without them nobody — including you, six months from now — can regenerate these parsers, re-apply the change on the next upstream refresh, or verify the result by hash. Every local divergence we carry today is a few lines in a scanner.c that a reader can see in the MANIFEST's "Local source patches" table; a grammar-level change is a different size of thing and needs its source. Concretely: the patch against the pinned upstream commit for each grammar (a plain git diff of grammar.js is ideal), plus the exact regenerate commands, and a row for each in that table. With that, upstream @ pinned sha + your patch → generate → same sha256 as scripts/vendored-checksums.txt becomes a check anyone can run, and I will run it.
2. DCO sign-off. The commit has no Signed-off-by: trailer, which is why dco is red. git commit --amend -s and a force-push fixes it — force-pushing your own PR branch is completely fine here. A pseudonymous or noreply identity is acceptable, as long as it matches the commit author.
3. Two PRs rather than one, please. SQLite migrations and JSX ampersands are unrelated defects in unrelated grammars. Split, each can be verified, merged, bisected and — if it ever comes to it — reverted on its own, and a problem in one does not hold the other hostage. The TSX change is the smaller and more self-contained of the two and could land first.
4. Have you offered these upstream? A raw & in JSX text being rejected is a long-standing gap in tree-sitter-javascript, and SQLite's PRAGMA / AUTOINCREMENT / trigger bodies would help every user of tree-sitter-sql, not only us. If upstream takes them, our local patch row disappears at the next refresh, which is the best outcome for everyone. Not a condition for merging — but if you open those PRs, link them in the MANIFEST row.
Two things that are on our side, not yours, so you do not spend time on them:
- The red
changescheck is our CI's fault. That job asks the GitHub API for the PR's diff, and the API answersHTTP 422: this diff is taking too long to generatefor a parser this size. It would fail identically for any grammar refresh. I have recorded it and it will not be held against this PR. - Carrying a locally modified grammar — as opposed to a pinned upstream one — is a maintenance commitment we have so far only made for grammars we wrote ourselves (
tools/tree-sitter-*). I think the case here is good, because the alternative is telling SQLite users their valid migrations are broken, but it is a decision I want to make deliberately rather than by merging. Item 1 is what makes it a decision I can say yes to.
One small note on the validation section: the tests/test_cli.c:11201 failure you saw in the full suite is not yours — line 11201 is the VS Code agent-install lifecycle test, which nothing in this PR touches. I have not reproduced it here, so I am not going to call it a known flake; if it fails for you on an unmodified main as well, a separate issue with the VS Code durable diag … line it prints would be very welcome.
Thank you again. This is careful work on a genuinely annoying problem, and I would like to see it land.
…422s
The `changes` job decides whether a PR touches product code, and ci-ok
requires it. It asks GitHub for the PR's file list. An earlier fix moved
it off the .diff endpoint, which rejects PRs over 20k lines, onto the
paginated pulls/files endpoint -- but that endpoint renders the diff
server-side as well, and for a PR that regenerates a vendored parser it
answers
HTTP 422: Sorry, this diff is taking too long to generate.
Observed on DeusData#2246 (a 42 MB sql/parser.c): `changes` red, therefore ci-ok
red, for a reason that has nothing to do with the contribution. The
compare endpoint fails the same way, so there is no API route to the
file list for such a PR. Every grammar refresh would hit this.
When the API call fails, the job now takes the list from git instead.
The PR merge ref's first parent is the base, so a name-only diff across
that one merge commit is exactly the PR's changed files. The fetch is
depth 2 with --filter=blob:none into a bare repository under RUNNER_TEMP:
commits and trees only, no blobs, nothing checked out and nothing from
the PR executed. No new action, no new permission, and the API path and
the classification regex are untouched, so ordinary PRs behave as before.
A ::notice:: line records when the fallback was used. If the fetch fails
too, the step exits non-zero and writes no output -- the gate fails
closed, as it did before.
Proof, by executing the workflow's own run block under
`bash -eo pipefail` with a stub gh:
API path, docs-only list rc 0 product=false
fallback, real DeusData#2246 rc 0 product=true, the same five
files `git diff --numstat`
reports for the PR; 0.85 s,
188 KB fetched
fallback, nonexistent merge ref rc 128 no output written
Contract tests that read pr.yml -- security_gate_fail_closed,
smoke_fixture_contract, windows_bundle_contract, venue_parity_contract --
pass.
Not addressed here, noted for a follow-up: the classification pipes the
list into `grep -q` under pipefail, which can report a match as a
failure once the list outgrows the pipe buffer.
Signed-off-by: Martin Vogel <[email protected]>
Found while proving the previous commit, in the same step, and the worse
of the two defects because it fails OPEN. The classification was
if printf '%s\n' "$FILES" | grep -qE '^(src/|internal/|...)'; then
under the runner's `bash -eo pipefail`. grep -q exits on its first match;
once the list is larger than the pipe buffer printf is still writing,
dies of SIGPIPE, and pipefail turns the MATCH into a failed pipeline. The
else branch then writes product=false, and pr-smoke and memwaste are
skipped -- on exactly the PRs that change the most files. The previous
commit's message flagged this as a follow-up; it is fixed here instead.
The list now reaches grep through a here-string, so there is no pipe to
break. The regex is unchanged.
Proof, by executing the workflow's own run block under
`bash -eo pipefail` with a stub gh, ten runs each:
849 KB list, first line src/a.c before: product=false 10/10
after: product=true 10/10
849 KB list, docs only product=false before and after
small docs-only list product=false
422 fallback on the real DeusData#2246 product=true
fallback, nonexistent merge ref rc 128, no output written
It takes roughly 1,500 changed files to cross the buffer, which is why
ordinary PRs never showed it.
Signed-off-by: Martin Vogel <[email protected]>
|
Thanks again for the SQLite and TSX reproductions. The outstanding review request remains reproducible grammar-source patches and generation commands, signed-off commits, and separate PRs for the two grammars. The local-grammar maintenance decision also remains with the maintainer; this sweep does not resolve it. |
|
Thanks again for this, Kerf, and here is an update that should save you some work.
Whether we carry a patched SQL grammar at all is still a decision on our side. The shape above is what makes it a decision we can say yes to. If you would rather not take this further, just say so. Should we go ahead with a patched SQL grammar ourselves, we would build on your tests and credit you as co-author. |
|
Thank you for the SQLite and TSX reproductions. The October 20 correction stands: TSX is already fixed, so no separate TSX PR is needed. The remaining proposal is the reproducible SQLite grammar patch and corpus with a sign-off; carrying that fork remains our decision. Sorry for the delay on that decision—I’ll follow up soon. |
|
Thanks for your patience, Kerf. Here is the follow-up I promised on October 25. The decision is yes: we will carry a patched SQL grammar so that valid SQLite migrations stop being reported as partially parsed. Your report and your tests made that an easy call. The valid-migration test pins exactly the forms people hit in real SQLite schemas, and Since this branch has been quiet for a while, we will do the remaining work ourselves rather than ask more of you:
The TSX half needs nothing further: it is fixed on main by 1b79558 (#2327), which has its own regression test. We will link the new PR here and close this one with credit once it lands. None of this is a judgment on your work. A regenerated parser can only be accepted together with the grammar source that reproduces it, and the branch also needs a DCO sign-off that only you can add, so finishing it on our side is simply the quickest way to get the fix to SQLite users. If you would like to look over the patch when it is up, you are very welcome to. |
|
As promised, the follow-up is up: #2957. It carries the SQL grammar fork with the SQLite patch and its corpus test, your two SQLite tests exactly as you wrote them, and you as co-author on the commit. On real migrations, vaultwarden's SQLite set goes from 3 partially parsed files to 0 and atuin's valid SQLite files are all clean now. If you would like to look over the patch, you are very welcome to. We will close this PR with credit once #2957 lands. Thank you again, Kerf. |
|
The SQLite fix is now on On real migrations, vaultwarden's SQLite set went from 3 partially parsed files to 0, and every valid SQLite file in atuin now parses cleanly; the only files still flagged there are the genuinely invalid down migration and the MySQL and Postgres files. The SQL grammar is now a self-maintained fork with the SQLite additions as a reviewable patch, so the parser can be regenerated and checked by anyone. The TSX half of your report was already fixed by #2327. Closing this one in favour of #2957. Thanks again for the careful report and for the tests that made the fix easy to prove. |
Valid SQLite migration files were reported parse_partial because the
vendored SQL grammar (DerekStride/tree-sitter-sql @ 851e9cb257ba) rejects
SQLite syntax they use: PRAGMA, AUTOINCREMENT, a trigger's BEGIN ... END
body, a column named `key` after the first column, WITHOUT ROWID, an
unparenthesized trigger WHEN, INSERT OR IGNORE, and CREATE VIRTUAL TABLE
... USING fts5(...). Each became an ERROR node; the trigger's error region
in atuin's ai_session schema also swallowed the CREATE TABLE after it.
The `key` case is lexical: after a comma inside a column list the grammar
also accepts MySQL's `KEY name (cols)` index, so `key` lexes as that
keyword there and `key TEXT NOT NULL` became a malformed index.
Fix: sql becomes a self-maintained fork of the pin we already vendored,
following the pascal model. tools/tree-sitter-sql/ holds upstream's
grammar.js + grammar/ (blob-identical to the pin and to upstream main @
3c99273f9b32, where its corpus lives) and one patch,
patches/sqlite-dialect.patch, touching grammar/ only:
- PRAGMA [schema.]name [= value | (value)] (new pragma_statement)
- AUTOINCREMENT as keyword_auto_increment, upstream's own change
(c042cde1e2a6, 2026-11-08, after our pin)
- `key` accepted as a column name; the type keywords that take no
arguments are accepted as a MySQL index name so `KEY name (name)` stays
an index
- WITHOUT ROWID
- a trigger body as the existing block node, after an optional
unparenthesized WHEN (precedence keeps Postgres `WHEN (...) EXECUTE
FUNCTION` trees unchanged)
- INSERT OR {ABORT | FAIL | IGNORE | REPLACE | ROLLBACK}
- CREATE VIRTUAL TABLE [IF NOT EXISTS] name USING module(args), aliased
to create_table (new module_argument node), so the table is extracted
as a Table like any other
Upstream node types keep their shape: upstream's 511 corpus cases pass
unchanged; test/corpus/sqlite.txt adds 11 (9 fail on the unpatched
grammar, 2 controls pass on both). The vendored scanner.c is kept as is
(it carries our start_tag leak fix 0e61867, now listed under the
MANIFEST's "Local source patches"; upstream fixed the same leak after our
pin in ee2f84f9b857).
Provenance: without the patch, tree-sitter-cli 0.25.10 regenerates
upstream's shipped parser.c byte-for-byte except line 1 (upstream's
generator stamp has no version), and grammar.json, node-types.json and
tree_sitter/*.h byte-identical. Patched: three generations are
byte-identical (parser.c SHA-256 3c64e5c4...7887); ABI 15; states 30,622
-> 31,081, symbols 770 -> 782; 0 non-ASCII bytes, no string literal with
a space, one comment, the same call-position identifiers as before. Node
types only gain entries (pragma_statement, module_argument, four
keywords; create_table gains a `module` field).
Proof, indexed from scratch with isolated caches (main 6d34ca1 vs this
change): dani-garcia/vaultwarden @ 58a980f8 migrations/sqlite (116
files) parse_partial 3 -> 0, graph identical (303 nodes, 446 edges);
atuinsh/atuin @ 2535161e *.sql (69 files) 19 -> 7: all 12 valid SQLite
files clean; the 7 left are the invalid `ALTER TABLE ... DROP INDEX`
down migration and the 2 MySQL + 4 Postgres files, same ranges as
before; graph +2 Table nodes (the fts5 table and the table the trigger
error swallowed), +2 DEFINES, +3 USAGE lineage edges. No newly flagged
file. Control: helm @ 9b88efd6 indexes to an identical graph (12,251
nodes, 39,561 edges).
Registered like pascal: MANIFEST.md fork rows + notes (ABI tally
recounted, unchanged), the audit FORKS map, THIRD_PARTY.md, vendored
checksums. LICENSE stays upstream's MIT byte-identical.
Tests: parse_coverage carries the two SQLite tests from DeusData#2246 as written
(sqlite_valid_migration_not_flagged, red before this change, and the
sqlite_malformed_still_flagged control) and adds `key` columns, WITHOUT
ROWID, a WHEN trigger with INSERT OR IGNORE plus an fts5 table (asserted
as a Table), PRAGMA forms, and two controls: MySQL's KEY index still
clean, and ALTER TABLE ... DROP INDEX still flagged.
Distilled from DeusData#2246.
Co-authored-by: Kerf <[email protected]>
Signed-off-by: Martin Vogel <[email protected]>
Summary
PRAGMA,AUTOINCREMENT, SQLiteCREATE TRIGGER ... BEGIN ... END, and SQLiteIF NOT EXISTSmigration forms&in JSX text is accepted without changing HTML entity parsingMotivation
codebase-memory-mcp 0.11.0reports valid files as partially parsed:PRAGMA,AUTOINCREMENT, and SQLite trigger bodiesIdentity & contactBoth inputs pass their native validation, but the vendored parsers emit error regions. This causes
parse_partial_countto report false positives and can omit graph constructs.Validation
build/c/test-runner parse_coverage: 40/40 passedbuild/c/test-runner grammar_regression: passedscripts/security-vendored.sh: passedparse_partial_count=0,parse_unusable_count=0,nodes=1507,edges=3973tests/test_cli.c:11201), 10 skippedThe vendored parsers remain pinned to the commits recorded in
internal/cbm/vendored/grammars/MANIFEST.md; the manifest records generator versions and local grammar additions, and the checksum manifest is refreshed for the generated artifacts.