Skip to content

Rename action from 'SpecSync Check' to 'SpecSync'#9

Merged
corvid-agent merged 1 commit into
mainfrom
0xLeif-patch-1
Mar 18, 2026
Merged

Rename action from 'SpecSync Check' to 'SpecSync'#9
corvid-agent merged 1 commit into
mainfrom
0xLeif-patch-1

Conversation

@0xLeif

@0xLeif 0xLeif commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

No description provided.

@corvid-agent corvid-agent merged commit a65d765 into main Mar 18, 2026
7 checks passed
@corvid-agent corvid-agent deleted the 0xLeif-patch-1 branch March 18, 2026 23:07
0xLeif added a commit that referenced this pull request Jul 3, 2026
…in const) + Rust comment tokenization (#311)

* Fix: export-parser language-idiom gaps + Rust comment/string tokenization

Four export-scanner bugs where a correctly-documented public symbol was reported as
"no matching export found" (failing check --strict) and an undocumented one was
silently dropped from coverage.

#4 Swift: `public final class Foo` never matched — the decl regex had slots for
`static`/`class` modifiers but not `final`. Replaced with `(?:(?:final|static|class)\s+)*`
so any combination between the access keyword and the declaration keyword is allowed.

#5 Go: items in grouped `const (...)` / `var (...)` / `type (...)` blocks (on their
own lines, no keyword prefix) were missed by the line-anchored regex. Added a
stateful pass that captures each grouped item's leading exported (uppercase)
identifier, tracking brace depth so struct/interface fields inside a grouped `type`
are not mistaken for top-level exports.

#9 Kotlin: top-level `const val NAME` was missed — `const` wasn't in the modifier
chain. Added `(?:const\s+)?` before the declaration keyword.

Rust (new, discovered via the dogfood self-check): the scanner stripped string
literals BEFORE comments with a DOTALL regex, so a `"` inside a `//`/`///` doc
comment was read as a string opener and swallowed code up to the next real `"` —
deleting `pub fn` declarations in between (any doc comment with an odd number of `"`
silently hid exports). Replaced the regex string+comment stripping with a single
linear pass that recognizes `//` and `/* */` (nesting) before `"`, and keeps a `//`
inside a string as content. Raw strings and double-quote char literals are still
blanked up front by the existing (proven) regexes.

Tests: Swift final/static/class modifiers; Kotlin const val (incl. internal
exclusion); Go grouped const/var/type with brace-depth (struct fields not captured);
Rust quotes-in-comments don't hide exports + pub-fn-in-string not extracted. All
existing parser tests (incl. the ai.rs raw-string case) pass. 712 unit + 168
integration, self-check 100% (36990 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: address review regressions in the export-parser cluster

Round-2 adversarial review (per-language lenses) found three regressions in the
first cut. Fixed with tests reproducing each:

Rust — byte/C raw strings not blanked: the RAW_STR regexes required `\b` before
`r`, which fails when `r` is preceded by `b`, so `br#"..."#` (and `cr#"..."#`) were
never blanked. With an odd number of interior quotes the linear scanner then read a
trailing quote as a string opener and swallowed every following `pub` declaration to
EOF (a real regression vs main). Widened the prefix to `(?:b|c)?r`.

Go — brace/paren counting ignored string literals: a `{`/`}`/`(`/`)` inside a
grouped item's value (`OpenBrace = "{"`, a struct tag `` `json:"a}"` ``, or a
multi-line `X = f(\n...\n)` value) corrupted brace/paren depth, dropping later items
and — because the group never returned to depth 0 — swallowing every subsequent
grouped block in the file. Now depth is tracked on a copy with string/rune literals
blanked (`blank_go_strings`), and a value's own parens are tracked separately so a
value-closing `)` is not mistaken for the group's closer.

Swift — declarations inside string literals leaked: extract_exports stripped
comments but not strings, so a `public final class X` inside a code-gen template
string was extracted as a phantom export (widening the modifier group to include
`final` incrementally worsened a pre-existing weakness). Replaced the comment-only
regex stripping with a single linear pass that blanks `"..."`, `"""..."""`, `//`,
and nesting `/* */` together — also repairing Swift's previously-broken multi-line
`//` comment handling.

Tests: Rust byte raw string doesn't hide exports; Go grouped delimiters-in-strings
(brace string, multi-line paren value, later block still seen) + struct-tag brace;
Swift declaration inside single-line and multi-line strings not extracted. 717 unit
+ 168 integration, fmt / clippy (bin) / self-check 100% (37264 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: multi-line tokenization regressions in export parsers (round 3)

Round-3 adversarial review found two more regressions, both from constructs that
span lines — which the earlier fixed-count regex (Rust) and per-line blanking (Go)
could not see. Fixed with tests reproducing each reviewer input:

Rust — raw strings with 4+ hashes: the RAW_STR regexes only covered 0..3 hashes, so
`r####"..."####` was never blanked and an interior quote leaked, opening an
unterminated string that swallowed every later pub decl to EOF (regression vs main).
Replaced the fixed 0..3-hash regexes AND the char-literal regex with a single
self-contained linear scanner that counts hashes, so raw strings of ANY hash count
(and the `br`/`cr` byte/C prefixes, guarded by an identifier boundary) are handled,
along with `'"'`/`'\"'` char literals, regular/byte/C strings, and nesting comments.

Go — multi-line backtick raw strings: blank_go_strings ran per line, so a
`schema = ` + "`...multi line SQL...`" template inside a grouped const/var/type block
was treated as code — leaking an uppercase body word (e.g. `CREATE`) as a phantom
export, and letting a `)`/`{`/`(` in the body close the group early or leave it
stuck open (dropping later items and subsequent groups). blank_go_strings now blanks
literal contents across the WHOLE file (backtick strings span lines, newlines
preserved), and every pass — GO_DECL, GO_METHOD, and the grouped scan — runs on the
blanked copy, so no string body is ever read as code or as a delimiter.

Not addressed here (pre-existing, confirmed identical on main by the review, NOT
regressions — deferred to a focused follow-up): Swift `public private(set) var` /
`weak` / `lazy` / `mutating` / `indirect`, and Kotlin `@JvmField const val` /
`lateinit`, are still missed by the modifier chains.

Tests: Rust raw strings with 4/5-hash + interior quotes don't hide later exports; Go
grouped block with a multi-line backtick SQL template (body not captured, later
items + later group still seen) and with an unbalanced `)`/`{` in the body. 720 unit
+ 168 integration, fmt / clippy (bin) / self-check 100% (37382 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: unify Go comment+string tokenization; revert Swift string gold-plating (round 4)

Round-4 review found that round 3's "run every pass on the blanked copy" change
exposed the pre-existing broken comment stripping, and that the Swift string scanner
(added in round 2, beyond the actual finding) desynced on raw strings.

Go — replace the regex comment stripping + string-only blanker with ONE linear pass
(strip_go_strings_and_comments) that recognizes `//` and `/* */` BEFORE `"`/`` ` ``/`'`.
The old `//.*$` regex lacked the multiline flag, so it stripped only the file's LAST
comment; every other comment survived into the string blanker, where an apostrophe
in a contraction ("can't") or a stray `"` was read as a rune/string opener and
swallowed following declarations across newlines — dropping documented exports on
ordinary Go files (a real regression). Unbalanced `(`/`{`/`}` in a surviving comment
likewise corrupted the grouped pass's depth (skipping later items, leaking struct
fields, or missing a block whose `(` had a trailing comment). Recognizing comments
first, then blanking string/rune literals (incl. multi-line backtick raw strings),
fixes all of these; every pass now reads the same correct copy.

Swift — revert the string/comment scanner added in round 2. It was gold-plating
beyond finding #4 (which only asked for `public final class`), and it introduced a
raw-string (`#"..."#`) desync that swallowed later declarations. Swift is back to the
original comment-regex stripping plus ONLY the `final`/`static`/`class` modifier
widening the finding required. Declarations embedded inside string literals are once
again a pre-existing, low-severity false-positive — identical to main, not a
regression — and are left for a separate, consistent all-parser follow-up.

Not addressed (pre-existing, confirmed identical on main, NOT regressions): Rust
`pub fn r#raw_ident` captures `r` (PUB_DECL `\w+`); Swift `override` / `indirect` /
`private(set)`; Kotlin `@JvmField`/`@JvmStatic const val`.

Tests: Go comment with apostrophe/quote doesn't drop exports; Go grouped-pass depth
not corrupted by `(`/`{`/`}` in comments (item line, group-open line, struct field).
720 unit + 168 integration, fmt / clippy (bin) / self-check 100% (37347 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: anchor Go grouped blocks to column 0; broaden Swift modifier set (round 5)

Round-5 review found one blocking regression plus a completeness gap worth closing.

Go (blocking regression) — the grouped-declaration pass matched the group-open on the
trimmed line, so a function-LOCAL (indented) `const (` / `var (` / `type (` block was
scanned and its uppercase items emitted as package exports (e.g. a local
`const ( MaxAttempts = 5 )` inside `func Run()` leaked `MaxAttempts`). Main's
GO_DECL/GO_METHOD are `(?m)^`-anchored to column 0 and never captured indented decls,
so this fabricated non-exports vs main. The group-open now matches on the un-trimmed
line (`line.trim_end()`), firing only at column 0 — where gofmt always places
package-level blocks — so every legitimate top-level block is still captured and
function-local ones are ignored.

Swift (completeness, in the spirit of finding #4) — the modifier group allowed only
`final`/`static`/`class`, but `public override func`, `public mutating func`,
`public lazy var`, and `public weak var` are all at least as common and were still
dropped. Broadened the group to also allow override/mutating/nonmutating/lazy/weak/
unowned/dynamic/nonisolated, so those public members are extracted too.

Still deferred (pre-existing, not regressions): Go multi-name grouped item lines
(`Foo, Bar = ...` keeps only `Foo`) and inline single-line groups (`const ( Foo = 1 )`);
Swift `private(set)` / `borrowing` / `consuming` and declaration-shaped tokens inside
string literals (a low, pre-existing false-positive as on main); Kotlin `@JvmField`/
`@JvmStatic const val`; Rust `pub fn r#raw_ident` capturing `r`.

Tests: Go function-local grouped block not exported (column-0 block still is); Swift
override/mutating/lazy/weak/dynamic/nonisolated/final-override members extracted. 722
unit + 168 integration, fmt / clippy (bin) / self-check 100% (37423 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: don't capture multi-line value continuations as grouped exports (round 6)

Round-6 review confirmed 3 of 4 fixes clean (Rust tokenizer byte-identical to main
across all 78 real source files; Swift a strict regex superset; Kotlin const val) and
found one remaining Go false-positive regression.

A package-level grouped `const`/`var` item whose value spans lines via a trailing `.`
(builder chain) or a trailing binary operator leaves brace/paren depth at 0, so the
continuation line's leading identifier was captured as a phantom export — e.g.
`Client = New().\n    WithTimeout(5)` leaked `WithTimeout`, and `Path = Base +\n
Suffix` leaked `Suffix`. main never captured these (no grouped pass), so it was a
regression that fails `check --strict` with spurious undocumented-export findings on
idiomatic Go.

Fix uses Go's automatic-semicolon-insertion rule (go_line_continues): a new grouped
item is only recognized when the previous in-group line ended a statement (last token
an identifier/literal/`)`/`]`/`}`/`++`/`--`). A line ending in an operator, `.`, `,`,
`(`, `{`, `[`, or `=` continues the value, so its next line is skipped for item
matching. To keep a string-only value (`X = "y"`) reading as a completed statement,
strip_go_strings_and_comments now emits a single `_` placeholder per string/rune/
backtick literal (instead of nothing) — the placeholder is never at line start, so it
is never captured, but it prevents `X = ` from looking unfinished.

Tests: builder-chain and operator-continued grouped values don't leak the
continuation identifier (item after them is still captured); a string/backtick-valued
item doesn't swallow the following item. 724 unit + 168 integration, fmt / clippy
(bin) / self-check 100% (37521 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

* Fix: constrain Go receiver regex to one line (round 7 nit)

Round-7 review verdict: READY_WITH_NITS, no blocking items, all four fixes correct
and regression-free. Folded in the reviewer's top recommended nit — a pre-existing
GO_DECL false positive that lands in the exact grouped-`type` struct-field area this
PR adds and tests, so worth closing here to make the "fields aren't exports"
guarantee complete.

GO_DECL's optional receiver group used `[^)]*`, which matches newlines; on a grouped
`type (` opener line the regex could leap across the block body to the first interior
`)` (e.g. a func-typed struct field) and capture the following uppercase field as a
phantom top-level export (`type ( Config struct { fn func(x int); Secret string } )`
leaked `Secret`). Constraining the receiver to `[^)\n]*` stops it spanning newlines;
real method receivers are always single-line, so legitimate `func (r *R) Name`
capture is unchanged.

Test: grouped `type` struct with a func-typed field followed by an exported field
extracts the types (Config, Helper) but not the field (Secret). 725 unit + 168
integration, fmt / clippy (bin) / self-check 100% (37549 LOC).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KDJxU4R8hUEuq1Y5jzft5m

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

2 participants