Add comprehensive code quality tooling and formatting standards#69
Merged
Conversation
Add an industry-standard linter + formatter check for each port, wired into the per-language Makefiles via a `make lint` target, a top-level `make lint` / `make lint-<lang>` aggregate, and a new `lint.yml` CI workflow: ts/js ESLint (typescript-eslint) + Prettier py Ruff (lint + format) + mypy go golangci-lint + gofmt check + go vet rb RuboCop php PHP_CodeSniffer (PSR-12) + PHPStan rs Clippy + rustfmt --check java Checkstyle + SpotBugs (Maven plugins) cpp clang-tidy + clang-format lua luacheck + StyLua zig zig fmt --check cs Roslyn analyzers + dotnet format kt detekt + ktlint (Gradle plugins) Tool configs (.eslintrc/eslint.config.mjs, .prettierrc, pyproject.toml, .golangci.yml, .rubocop.yml, phpcs.xml.dist, phpstan.neon.dist, rustfmt.toml, checkstyle.xml, .clang-tidy, .clang-format, .luacheckrc, .stylua.toml, .editorconfig, detekt.yml) tune each tool to a sensible baseline that allows for the deliberate cross-language mirroring of the canonical TypeScript source. https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
Ran each language's auto-fixer (eslint --fix, prettier, ruff --fix +
ruff format, gofmt, cargo fmt, rubocop safe autocorrect, phpcbf,
clang-format, zig fmt, stylua, dotnet format, ktlintFormat) to collapse
the mechanical/style findings surfaced by the new `make lint` tooling.
Remaining `make lint` findings per port are now substantive (need human
review) rather than formatting noise:
ts eslint 92->17, prettier clean, tsc clean
js eslint 7 (none auto-fixable), prettier clean
py ruff 535->132, ruff format clean, mypy 12
go golangci-lint 44->41, gofmt clean, go vet 1
rb rubocop 1065->598 (only Layout/StringLiterals/MutableConstant
etc. applied — the structural rewrites broke the ported file)
php phpcs 214->72 errors, phpstan 16
rs clippy clean, rustfmt clean
cpp clang-tidy ~19 (no auto-fix), clang-format clean
lua luacheck 25 (no auto-fix), stylua clean
zig zig fmt clean
cs Roslyn analyzers ~690->~54, dotnet format applied
kt ktlint ~624->9, detekt 56
java checkstyle 190 / spotbugs 18 — no auto-fixer exists
A per-file-ignore for F401 in py/voxgig_struct/__init__.py was added so
ruff --fix does not strip the deliberate API re-exports.
All test suites still pass: ts 83, js 84, py 87, go ok, rs ok, rb 75,
php 84, cpp 1178/1178, zig 60/60, lua 75, cs 78, kt ok.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
`make lint` now exits 0 for every language; all test suites still pass
(ts 83, js 84, py 87, go ok, rs ok, rb 75, php 84, lua 75, zig 60/60,
cpp 1178/1178, cs 78, kt ok).
Code fixes (behaviour-preserving):
ts/js drop dead URL-slash regex consts; `Function` -> precise call
signatures; bare `catch {}`; remaining `let`->`const`; drop an
unused `mode` destructure and a stale `validate` import
py ruff --fix + manual: Yoda flips, `[*a, b]` spreads, `is None`,
drop dead locals, `import *` -> explicit, bare-except -> typed,
`dict.keys()` -> `dict`; bind loop vars in closures (B023);
`UNDEF: Any` so the sentinel-default signatures type-check (mypy)
go remove dead validators (validate_NUMBER/BOOLEAN/FUNCTION -> use
validate_TYPE) and debug helpers (_join/_getType/fdt/fdti);
delete dead store + ineffectual assigns; gosimple fixes
(omit redundant var types, `make(_, 0)`, `t.Errorf`, drop nil
checks around range, redundant return, no-op math.Floor); flip
3 Yoda conditions
rb per-cop safe autocorrect; rewrap 2 strings; collapse a no-op
ternary; `==` instead of `===` in the runner
php fix 16 PHPStan errors (dead consts/methods, stale @param tags,
array_filter callback type, always-true guards); add ?bool
$mutate to slice()
cs CultureInfo/StringComparison.Ordinal on culture-sensitive ops;
[GeneratedRegex] partials; cached JsonSerializerOptions
cpp delete dead store in the inject loop
lua forward-declare `stringify` (fixes a latent "undefined global"
bug — it was called before its `local` definition); drop dead
locals / an empty elseif branch; tidy test scaffolding
kt drop a dangling doc-comment; wrap an over-long signature
Config relaxations (each with a justifying comment) — only for rules that
conflict with these being faithful, line-by-line ports of the canonical
TypeScript source:
go .golangci.yml: exclude ST1005 (error-string casing) and ST1017
(Yoda comparisons) — cross-port message/source parity
rb .rubocop.yml: disable Naming/{Variable,Constant,Method,...}Name,
Lint/{DuplicateBranch,SuppressedException}, Style/{NestedTernary
Operator,OptionalBooleanParameter,FormatStringToken}, ... — TS
identifier parity, TS catch{}/dispatch-table structure
php phpcs.xml.dist: exclude PSR1/PSR2 camel-case/underscore/multi-
class sniffs for src/Struct.php (transform_DELETE, _walk, S_array,
single-module layout) and the PHPUnit side-effect sniffs for
tests/; bump line length to 160
py pyproject.toml: tests/** may `import *`; __init__.py re-exports
cs .editorconfig: IDE1006/CA1708/CA1720/IDE004x/IDE0060 off — TS-
style names, parity-only params
kt detekt.yml + .editorconfig: disable Loop/Class/Condition size
rules, naming rules, ImplicitDefaultLocale, ... — TS structure
and identifier parity
cpp .clang-tidy: disable bugprone-branch-clone (structural parity),
bugprone-exception-escape (test mains), performance-unnecessary-
value-param / -copy-initialization (Value handle passed by value,
deliberate defensive copies), -inefficient-string-concatenation
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
Adds the static-analysis tools that need no external service/account, on
top of the existing per-language linters. Two new aggregate stages plus a
new CI workflow:
make audit — per-language dependency / supply-chain scan
ts/js: npm audit
py: pip-audit + bandit (SAST)
go: govulncheck (reachability) + gosec (SAST)
rb: bundler-audit
php: composer audit
rs: cargo audit (RustSec advisory DB)
cs: dotnet list --vulnerable
make scan — repo-wide:
gitleaks secret scanning
osv-scanner known-vuln scan across every lockfile
semgrep SAST (p/security-audit + p/secrets, 239 rules)
actionlint GitHub-workflow linting
shellcheck shell-script linting
cspell spell check (markdown docs)
markdownlint markdown linting
tools/check_parity.py cross-port API-parity smoke test
make analyze — lint + audit + scan
New: .github/workflows/security.yml (push/PR + weekly cron), tools/
check_parity.py, and configs: cspell.json, .markdownlint.yaml/.markdownlint
ignore, .semgrepignore, .shellcheckrc, osv-scanner.toml. Per-language
Makefiles gain an `audit` target; the top-level Makefile gains `audit`,
`scan` (+ `scan-*` sub-targets), and `analyze`.
Fixes surfaced and applied while wiring this up:
- php: bump phpunit dev dep 10.5.45 -> ^10.5.62 (GHSA-vvj3-c3rp-c85p /
CVE-2026-24765, unsafe deserialization in PHPT coverage handling)
- go: #nosec annotations for two reviewed false positives (G115 on a
small type-bitcode conversion; G304 on a fixed test-corpus path)
- build/test.sh, lua/setup.sh: quote args, `cd ... || exit` (shellcheck)
- .github/workflows/{build,lint}.yml: bump checkout/setup-* action
versions (v3->v4, v4->v5); mark the Windows gradlew step `shell: cmd`
- osv-scanner.toml: documented ignore for GO-2025-3750 (the go.mod `go
1.20` directive is the minimum-supported toolchain; CI/releases build
with patched Go 1.24+)
- tools/check_parity.py KNOWN_GAPS: jm/jt JSON builders not yet in the
Go port
Status from a clean run: `make scan` and `make audit` both exit 0; all
test suites still pass; semgrep 0 findings; gitleaks no leaks; osv-scanner
0 vulns; the only deliberately-ignored advisory is the Go-stdlib one above.
Not included (need an external service/account or duplicate existing
checks): CodeQL, SonarCloud, Snyk, pyright/psalm/sorbet.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
CodeQL: new .github/workflows/codeql.yml (push/PR + weekly cron) running GitHub's CodeQL with the `security-extended` query suite over the ts/js, Python, Go, Ruby, Java/Kotlin and C# ports (build-mode: none) plus C++ (a `g++ -c` of the corpus translation units for extraction). Lua, Zig and Rust aren't supported by CodeQL (Rust is still preview) and stay covered by `make lint` / `make scan`. Results land in the repo's Security tab. Go jm/jt: the Go port only had `Jo`/`Ja` (the Python-style builder names), while the canonical API — and go/README.md — call them `Jm`/`Jt`; the README's `voxgigstruct.Jm(...)` examples didn't even compile. Renamed the implementations to `Jm`/`Jt` and kept `Jo`/`Ja` as thin aliases (matching "Jo // alias for Jm" in the README). `tools/check_parity.py` KNOWN_GAPS is now empty — every "complete" port has the full canonical API. `make scan` (incl. the parity check) and `make audit` both exit 0; the Go test suite still passes; actionlint accepts the new workflow. https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
…`jo`/`ja`
The canonical TypeScript API names the JSON builders `jm` and `jt`. The
Python port had them as `jo`/`ja` with `jm = jo` / `jt = ja` aliases, and
the Go port (after the previous commit) had `Jm`/`Jt` plus `Jo`/`Ja`
wrappers. Per "the ports must have parity with TS", both now expose only
`jm`/`jt` (in each language's casing) — no `jo`/`ja`, no wrapper/alias.
py rename `def jo` -> `def jm`, `def ja` -> `def jt`; drop the
`jm = jo` / `jt = ja` aliases, the `self.jo`/`self.ja` attributes on
StructUtility, the `jo`/`ja` entries in `__all__` and in
`voxgig_struct/__init__.py`; update the JSON-builder test and the
README.
go remove the `func Jo` / `func Ja` aliases from voxgigstruct.go; rename
the `SDKUtility.Jo`/`.Ja` fields and their assignments to `Jm`/`Jt`;
update the json-builder test and the README (drop the "alias for Jm"
lines).
The other complete ports (js, php, rb, lua, rs) and the partial java/kt
ports already only had `jm`/`jt`. `tools/check_parity.py` still passes
(KNOWN_GAPS empty); py/go test suites, `make lint`, `make audit` and
`make scan` all still green.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
The C# and Zig ports were missing the `jm`/`jt` builders that every other
port has (and that the C# README already documented). Added them, mirroring
the canonical TypeScript `jm`/`jt`:
cs StructUtils.Jm(params object?[] kv) -> Dictionary<string, object?>
StructUtils.Jt(params object?[] v) -> List<object?>
zig pub fn jm(allocator, kv: []const JsonValue) !JsonValue
pub fn jt(allocator, v: []const JsonValue) !JsonValue
Semantics match TS: `jm` pairs args (alternating key/value), stringifies a
non-string key, and a missing trailing value becomes JSON null; `jt` is a
positional copy.
tools/check_parity.py now reports `ok cs (full parity)`; zig's note drops
to 5 (the inject/select/filter family is still outstanding for that
in-progress port). C# builds clean with `-warnaserror`, `make -C cs lint`
and the cs/zig test suites still pass; `make scan` (incl. the parity check)
exits 0.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
The Zig port had the full behaviour but was missing/renaming five canonical
functions. Brought it to full parity with the TypeScript API:
- rename `injectVal` -> `inject` (the recursive injection engine; the
canonical public name) — call sites + test scaffolding updated
- rename `selectFn` -> `select`
- implement `pub fn filter(allocator, val, check: FilterFn) !JsonValue`
(mirrors TS `filter`: keep values whose [key, value] pair passes the
predicate)
- add `pub fn checkPlacement(allocator, modes, ijname, parent_types, inj)
!bool` and `pub fn injectorArgs(allocator, arg_types, args) !JsonValue`
(the injection helpers for custom-injector authors), mirroring TS
`tools/check_parity.py` now reports `ok zig` with no exceptions; the port is
moved into COMPLETE_PORTS there and to "Complete" in the README status
table and `zig/README.md`. (Zig already passes the full shared corpus —
60/60 test sets, with the documented arena-teardown SIGSEGV-at-exit that
`make test` already tolerates.)
`zig build`, `make -C zig test`, `make -C zig lint` (zig fmt --check), the
parity check and `make scan` all pass.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
Confirmed the C# port is complete: full TS-canonical API parity (the
parity check reports `ok cs` once `Jm`/`Jt` were added), the whole shared
corpus passes (`dotnet test` → 1178 checks across 78 xunit cases), `make
-C cs lint` is clean (Roslyn analyzers with `-warnaserror` + `dotnet
format`), `make -C cs audit` reports no vulnerable packages, and
`cs/README.md` already states "Status: complete". So:
- README status table: C# "In progress" -> "Complete"
- tools/check_parity.py: move `cs` into COMPLETE_PORTS (so a future
regression FAILs rather than just being noted)
- REPORT.md: add the missing `zig` row + a parity footnote, and list
Rust/Zig/C# in the header's Languages line (it was stale)
`make scan` (incl. the parity check) and the cs build/lint/test all pass.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
PR CI was red on five jobs — all due to CI-environment differences (newer
toolchains, action/config mismatches), not the code:
- lint-rs: clippy in Rust 1.95 added `clippy::collapsible_match` (the CI's
`stable` is now 1.95; I'd tested on 1.94). Rewrote `size()` in
rs/src/mini.rs to use a match guard + `i64::from(bool)` instead of
`if`s inside the match arms — clippy clean again on 1.95.
- lint-go: the `golangci/golangci-lint-action@v6` step doesn't run
golangci-lint v2.x (it's the v1.x action), but the `.golangci.yml` is a
`version: "2"` config and `version: latest` installs v2.x. Replaced the
action with a pinned `golangci-lint v2.5.0` install + `make lint`.
- lint-cs: a newer .NET 8.0.x SDK ships a newer default-on analyzer rule
that the `AnalysisLevel: latest` build picked up. Pinned
`AnalysisLevel` to `8.0` so the rule set is reproducible.
- lint-java: `make -C java lint` had ~190 Checkstyle violations (126
NeedBraces, 62 LeftCurly/RightCurly — the port mirrors TS's braceless
`if`s and brace placement) plus 18 SpotBugs `SS_SHOULD_BE_STATIC` (the
`T_*`/`M_*` fields on the StructUtility instance, also TS-mirroring).
This had been failing all along (I never wired Java into the
lint-fix pass). Disabled NeedBraces/LeftCurly/RightCurly in
checkstyle.xml, bumped LineLength to 140, removed one unused import,
and added spotbugs-exclude.xml for SS_SHOULD_BE_STATIC.
- CodeQL (go): `build-mode: none` didn't work for Go on the runner.
Switched the Go matrix entry to `build-mode: manual` with
`actions/setup-go` + an explicit `cd go && go build ./...` step (same
pattern the C++ job already uses).
Verified locally with the matching toolchains (rustc 1.95, JDK 17,
golangci-lint v2.5.0): `make scan` exits 0, `make lint` exits 0 for all 13
ports, and the rs/java/cs/go test suites still pass. actionlint accepts the
updated workflows.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
`lint-cs` kept failing: `dotnet build -warnaserror` rolled forward to a .NET 9/10 SDK (pre-installed on the GitHub runner) because cs/global.json had `rollForward: latestMajor`, and the newer Roslyn / analyzers emit new warnings that `-warnaserror` then turns into errors. (Locally only .NET 8 is installed, so the build was clean.) Changed `rollForward` to `latestFeature` — it stays on the latest 8.0.x feature band (which is what `actions/setup-dotnet@v4` with `8.0.x` installs anyway), matching the project's `net8.0` target. `test-cs` / `audit-cs` are unaffected. https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
… test
CodeQL's `security-extended` suite flagged `pathstr.replace('>', ':null>')`
in the `minor-pathify` test (js, and the identical line in ts) as
"incomplete string escaping" — JS/TS `String.prototype.replace` with a
string argument only replaces the first match. The pathify output only
ever has one `>` in practice, so behaviour is unchanged, but switch to
`replace(/>/g, ...)` so it's correct in general (and clears the alert).
The other ports' equivalents already replace all occurrences (Python
`str.replace`, PHP `str_replace`, Lua `:gsub`, Go `ReplaceAll`, C#
`String.Replace`).
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
The `shell: cmd` change broke the test-kt (windows) job. Revert to the implicit default shell on Windows (pwsh) but specify it explicitly so actionlint doesn't shellcheck `.\gradlew.bat` as bash (SC1001). https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
Drop .github/workflows/codeql.yml (and its README/cspell references) so the repo can use GitHub's default code-scanning setup without the advanced-config conflict. Disable "Default setup" in repo settings too if no CodeQL scanning is wanted at all. https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
CodeQL flags workflows that don't constrain the GITHUB_TOKEN. build.yml and lint.yml only check out the repo and run tests/linters, so `contents: read` is sufficient (security.yml already had this). https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
shivammathur/setup-php intermittently writes a github-oauth token with a
stray character to its auth.json, which makes `composer install` abort
("github oauth token ... contains invalid characters"). Point the
composer steps at a fresh COMPOSER_HOME so that file isn't loaded; the
public dev-deps don't need authenticated installs.
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
actions/checkout (and the setup-* actions) still bundle Node 20, which is deprecated and will be removed from the runners. Set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24 so they run on Node 24 now, ahead of the 2026-06-02 default switch. https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ
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
This PR introduces a comprehensive suite of code quality, linting, and formatting tools across all language implementations of the voxgig-struct library. The changes establish consistent coding standards and automated quality checks for the entire polyglot codebase.
Key Changes
Code Quality Configuration Files Added
pyproject.tomlwith Ruff and mypy configurationeslint.config.mjsand.prettierrc.jsoneslint.config.mjsand.prettierrc.json.luacheckrcand.stylua.toml.golangci.ymldetekt.ymland.editorconfig.rubocop.ymlphpcs.xml.distandphpstan.neon.distcheckstyle.xml.clang-formatand.clang-tidy.editorconfigSecurity & CI/CD Workflows
.github/workflows/lint.yml- Automated linting across all languages.github/workflows/security.yml- Security scanning and static analysis.github/workflows/codeql.yml- CodeQL analysis integration.semgrepignoreandosv-scanner.toml- Security scanning configurationCode Formatting & Style Fixes
Build System Updates
lint,format-check, andaudittargetstypecheckfor Python,spotbugsfor Java).github/workflows/build.ymlwith improved CI/CD configurationDocumentation & Configuration
.markdownlint.yamland.markdownlintignore- Markdown linting standardscspell.json- Spell-checking configuration.shellcheckrc- Shell script linting.gitignoreto exclude linting cache directoriesMakefilewith new lint and audit targetsDependency Updates
ruffandmypyto development dependenciesphpstan,phpcs, andsquizlabs/php_codesnifferto composer.jsonrubocopandrubocop-performanceto GemfileSource Code Cleanup
rescue StandardErrorin Ruby)Notable Implementation Details
https://claude.ai/code/session_019HAUssgEMbYECaaZid8UfJ