Open
Conversation
Adds the full tsanalyzer package — complexity, sizes, deps, mutation (generate/apply/annotate), testrunner, eval harness fixtures — and brings in go-tree-sitter via go.mod/go.sum as the parsing engine. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the EVAL_ENV variable and eval-ts target to the Makefile, and wires up Node 22 setup + npm cache + the Eval TypeScript (EVAL-3) step in ci.yml. Rust/cargo and eval-rust/eval-mixed steps land in PR #3. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a blank import for tsanalyzer so its init() runs and registers the TypeScript language implementation with the lang registry on startup. rustanalyzer import lands in PR #3. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
TestRunner_Timeout was failing on Linux CI, taking the full 5s sleep duration instead of honoring the 200ms timeout. On Linux, /bin/sh forks sleep as a child rather than exec-optimizing to it, so exec.CommandContext's default Cancel (SIGKILL to the leader) kills only the shell. The orphaned sleep keeps the inherited stdout/stderr pipes open, blocking cmd.Wait() on io.Copy until sleep naturally exits. Put the subprocess in its own process group via Setpgid and signal the whole group on cancel. This also matters in production where npx vitest / jest fork worker processes that would otherwise be leaked on timeout. WaitDelay gives a cross-platform upper bound on pipe-close wait as a safety net. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The diffguard dogfooding job on this PR flagged six complexity/size
violations against the new tsanalyzer package plus one surviving
math_operator mutant in replaceRange. All show up only because the
entire package is net-new in the diff, so every helper counts as
changed code subject to the thresholds.
Refactors applied:
- complexity.go: split walkComplexity's 62-line switch into per-
construct helpers (ifComplexity, loopComplexity, callComplexity,
walkAllChildren, isFunctionLikeNode). Semantics unchanged.
- mutation_generate.go: hoist binaryFlips / strictEqualityFlips to
package scope, collapse MutantSite construction into newMutantSite,
and split hasOptionalChainToken into a grammar fast path plus
optionalChainTokenOffset (now shared with the applier).
- mutation_apply.go: reuse optionalChainTokenOffset in
applyOptionalChainRemoval; swap replaceRange's hand-rolled
capacity math for slices.Concat, which removes the surviving
math_operator mutant on the capacity expression (the `+`/`-`
flips produced bitwise-identical output).
- tsanalyzer.go: replace hasTSFile's 11-way `||` chain with a
package-level detectionSkipDirs map.
- testrunner.go: extract runCommand so RunTest stays under the
50-line threshold after the process-group plumbing landed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
cffls
reviewed
Apr 24, 2026
Collaborator
cffls
left a comment
There was a problem hiding this comment.
Thanks! Since the lang-foundation PR has been merged, the base branch could be changed to main.
Contributor
Author
Ahhh, this repo does not have "Automatically delete head branches after merge" turned on. I just deleted that branch from that closed PR. That forces GitHub to automatically update the stacked PR's. If you turn that on, it will do it automatically :) |
Addresses PR review feedback (@cffls): README was Go-only despite the tsanalyzer being registered. Adds a Languages section covering auto-detection, TS prerequisites (node/npm), test-file exclusions, and runner precedence (vitest > jest > npm test), plus CLI examples for running diffguard against a TypeScript repo and the extra GitHub Actions steps needed so mutation runs can invoke npx vitest / npx jest. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Resolves README.md conflict by keeping both additions: origin/main's --skip-generated mode description stays under the Modes subsection, and this branch's Languages section sits immediately below it. No code-level conflicts — go build, go vet, and go test ./... all clean on the merged tree. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Temp-copy test runners (TypeScript) swap mutant bytes over the original file during RunTest, which races with other workers' ApplyMutation reads of the same path. When a worker's ApplyMutation ran while another worker's RunTest had the file mutated on disk, it either mutated against already-mutated source or failed to find its target node and returned nil — silently classifying the mutant as SURVIVED. This surfaced reliably on Ubuntu CI (slower I/O + Linux scheduler) where TestEval_Mutation_TSOp_Positive saw 2 of 5 Tier-1 mutants survive; on macOS the race window closed faster than any concurrent read could land, hiding the bug. Split runMutantsParallel into two phases: prepareMutants applies and writes every mutant to its temp file upfront (file on disk still pristine, parallel-safe), then runPreparedMutant fires RunTest calls in parallel. Go's overlay-based runner is unaffected; the fix is only load-bearing for temp-copy runners. Added TestRunMutantsParallel_ApplyPrecedesTest as a regression guard — fake applier timestamps reads, fake runner swaps the file briefly, and the test asserts no ApplyMutation observed post-swap state. Confirmed the test fails with the old single-phase orchestrator and passes with the two-phase one. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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
Stacked on #2. Adds a TypeScript implementation of the
lang.Languageinterface, registered via blank import. Self-contained: tsanalyzer unit tests + 10 evaldata fixtures run through the sharedevalharnessintroduced in PR #2 .Retarget to `main` once PR #2 merges.
What ships
internal/lang/tsanalyzer/— 69 files: analyzer code, testdata, 10 evaldata fixtures (complexity/sizes/deps_cycle/mutation_kill/mutation_tsop in negative + positive variants).go.mod/go.sum— addgithub.com/smacker/go-tree-sitter+ transitive deps.cmd/diffguard/main.go— second blank import (goanalyzer + tsanalyzer).Makefile—EVAL_ENVvariable +eval-tstarget + `.PHONY` update..github/workflows/ci.yml— Node 22 setup + npm cache + `Eval — TypeScript (EVAL-3)` step.Verification
go build ./... && go vet ./... && go test ./... -count=1— green.make eval-tspasses locally (requires Node 22+).eval-tson every push (see the updated workflow).Reviewer notes