LadybugDB ≤0.18.2 mis-evaluated `r.type IN [...]` on relationship table
groups: the boolean-filter fallback skipped writing selection buffers for
single-row unflat chunks, dropping/duplicating callers in context() and
impact() (upstream LadybugDB#692, fixed by LadybugDB#699, shipped in
0.18.3). Floor the dependency at ^0.18.3 and lock core + all five platform
packages.
Resurrect the caller-identity regression test from PR #2553 (closed as
superseded by the upstream fix): it pins context()/impact() to exact
caller IDs across CodeRelation sub-table pairs so any future predicate
regression fails loudly. Note: with CREATE-seeded data the test also
passes on 0.18.2 (the upstream repro needs COPY-written chunk layouts) —
it is a behavioural pin, not a bug reproduction.
Fixes#2508
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* chore(deps)(deps): bump js-yaml from 4.3.0 to 5.0.0 in /gitnexus
Bumps [js-yaml](https://github.com/nodeca/js-yaml) from 4.3.0 to 5.0.0.
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/4.3.0...5.0.0)
---
updated-dependencies:
- dependency-name: js-yaml
dependency-version: 5.0.0
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix(spring-config): migrate YAML parsing to js-yaml 5 event API
js-yaml 5 removed the loadAll `listener` callback, the EventType/State
types, and DEFAULT_SCHEMA that spring-config relied on, breaking the build.
Rebuild the per-key line tree from parseEvents()/constructFromEvents()
(positions are source offsets → mapped to lines), apply the `<<` merge tag
via CORE_SCHEMA.withTags(mergeTag) (CORE alone leaves merge keys unexpanded),
and resolve aliases by anchor name, which lets the object-identity WeakMap go.
Behavior preserved: 9 unit + 8 integration spring-config tests pass, including
merged-key declaration-line, cyclic-alias termination, and the depth budget.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(spring-config): restore v4 tag coverage, cover the v5 rewrite with tests
Review follow-up for #2618.
CORE_SCHEMA.withTags(mergeTag) was a narrowing, not a port: js-yaml 5
throws "unknown tag" on !!timestamp/!!binary/!!set/!!omap/!!pairs, and an
unknown tag aborts the whole parse, which readConfigKeys swallows — so an
application.yml using any of them would have gone from its full key set to
zero keys, silently. Carry the rest of what DEFAULT_SCHEMA was; none of
these tags can execute code.
Add tests for every path the review flagged as uncovered: multi-document
files, empty/comment-only/bare-`---`/bare-scalar documents, sequence-form
merge keys, and explicitly tagged values (which fail against the one-tag
schema, so they target the changed line).
Clear the anchor map per document. It cannot change output today —
constructFromEvents rejects a cross-document alias before the event tree is
built, now asserted — but it keeps both layers on YAML's scoping rule.
Drop the stale @types/js-yaml devDependency; js-yaml 5 ships its own types
and tsc --noEmit is clean without it. Lockfile hand-edited because npm
uninstall also strips every libc field.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(autofix): apply prettier + eslint fixes via /autofix command
* fix(spring-config): flatten !!set members, walk YAML iteratively
Review follow-up for #2618.
js-yaml 5 constructs `!!set` as a native Set; v4 built a plain
`{member: null}` object. Object.entries of a Set is empty, so a tagged set
collapsed to a bare leaf key and lost every member. Enumerate the Set
instead. Sets arrive as mapping events with key/value scalar pairs, so
member lines resolve through the usual lookup. !!binary and !!timestamp are
unaffected — both are scalar events and take the leaf path, which is why a
Uint8Array never explodes into one key per byte.
Convert findYamlMappingLocation and flattenYamlValue from recursion to an
explicit stack. Children are pushed in reverse so pops happen in
declaration order, preserving "first match" and `out` insertion order;
`leave` frames release the cycle guard where the old `finally` did. The
depth budget still throws at the same boundary with the same message.
Cover the gaps the review named: !!pairs (both duplicate entries survive),
anchor-name reuse resolving to the nearest preceding declaration, and
marker-only leading documents staying index-aligned across the two streams.
buildYamlEventTree keeps no node budget by design — one node per event over
an already-materialized array, bounded by MAX_CONFIG_FILE_BYTES. The
docstring now says so rather than implying MAX_YAML_TRAVERSAL_NODES covers
it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Babel 8 (devDep for the bench mutation oracle, pulled in by dependabot
previous floor (>=22.0.0), so every dev install on Node <22.18 emitted
nine EBADENGINE warnings. Rather than pin Babel back to 7, adopt Node
22.18+ as the supported minimum: set engines to ^22.18.0 || >=24.11.0,
matching Babel 8 exactly so the warnings resolve honestly with no
dependabot ignore needed.
@types/uuid@11 is a deprecated stub - uuid@14 ships its own types and no
tsconfig references it. Lockfile edited by hand (engines + @types/uuid
entry) to preserve the libc platform metadata a newer npm wrote;
verified consistent via npm ci (exit 0).
BREAKING CHANGE: the gitnexus package now requires Node ^22.18.0 || >=24.11.0
(previously >=22.0.0). Node 22.0-22.17 are no longer supported.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(deps): bump @ladybugdb/core to 0.18.0
Pins the release containing LadybugDB/ladybug#605 (TransactionManager
lock-order-inversion deadlock fix). Checked for known post-release
regressions specific to 0.18.0 via the Ladybug issue tracker — none found.
* fix(lbug): re-validate version-coupled comments and regexes for 0.18.0
Extends the LADYBUGDB-CONTRACT re-validation to two spots the marker
convention doesn't catch (bridge-db.ts's LBUG_OPEN_RETRY_PATTERNS,
conn-lock.ts's serialization rationale). Confirms via upstream source
diff (v0.16.1..v0.18.0) that every matched error-text string is
unchanged; conn-lock.ts's rationale is unaffected by #612/#623 since
neither addresses concurrent queries on one connection. Adds a
stemmer-sweep test proving the bundled 0.18.0 FTS extension accepts
every entry in SUPPORTED_FTS_STEMMERS, not just the default porter.
A live-trigger test for isMissingShadowSidecarError was attempted but
abandoned after empirical probing showed it isn't reliably
reproducible (even a SIGKILL-simulated crash didn't reproduce the
error on reopen) — documented as inspection-verified instead of
overclaiming test coverage that doesn't exist.
* test(lbug): add concurrent multi-connection deadlock stress test (#2338)
Directly validates LadybugDB/ladybug#605 — the TransactionManager
lock-order-inversion deadlock between a commit()-triggered checkpoint
and a concurrent beginAutoTransaction() — under a shape close to
GitNexus's real concurrent-writer load, independent of conn-lock.ts's
app-level serialization.
Comparison run against 0.17.1 (pre-fix): 1 of 4 runs hung for the full
60s timeout, a direct reproduction of the deadlock. 9 consecutive runs
against 0.18.0 (post-fix) all passed cleanly. Production is unchanged —
conn-lock.ts still serializes every write; this test validates the
engine-level fix without shipping multi-writer as a default.
* fix(test): address code review findings in multiwriter deadlock test
- Reuse lbug-config.ts's createLbugDatabase (via GITNEXUS_WAL_CHECKPOINT_THRESHOLD)
instead of a hand-duplicated 9-arg raw constructor call whose stated
justification (needing to bypass createLbugDatabase for the threshold
override) was incorrect — the env var already provides it.
- Close every QueryResult via the existing closeQueryResults helper
(write loop, read loop, verify query, setup query) instead of leaking
native cursors, matching lbug-adapter.ts's established pattern.
- Move all cleanup (timers, connections, db close, env var restore) into
the outer finally block so it runs on every exit path, not just the
happy path — a timeout or a writer exhausting its retry budget no
longer leaves dangling timers/connections/abandoned query loops.
Verified: 8 consecutive runs after the refactor, all passing cleanly.
Found via 8-angle parallel code review (medium effort); the two other
findings (isDbBusyError not recognizing LadybugDB's 'Only one write
transaction' message, and shadow-file poll timing sensitivity) are
noted in the PR description as residual — the first is a production-code
change beyond this validation test's scope, the second is inherent to
observing a transient native sidecar file and not cleanly fixable
without overengineering.
* fix(test): apply ce-code-review autofix findings
Fixes from an 8-persona parallel review round (correctness/testing/
maintainability/project-standards/reliability/adversarial/agent-native/
learnings):
- Extract the duplicated skipUnlessFtsAvailable/FTS_UNAVAILABLE_NOTE
helper (previously copy-pasted between lbug-core-adapter.test.ts and
fts-stemmer-sweep.test.ts) into a shared test/helpers/fts-availability.ts.
- Fix a native connection leak: verifyConn in the deadlock test's final
verification block is now pushed into the readers array the outer
finally already closes, so it's cleaned up even if the count query
throws.
- Fix a latent TypeScript type error (tsconfig.test.json catches it,
tsconfig.json doesn't): conn.query() types as QueryResult |
QueryResult[]; narrow to the single-result case before calling
.getAll() rather than assuming the array branch never happens.
- Replace repeated inline InstanceType<typeof import(...)> expressions
with local LbugDatabase/LbugConnection type aliases.
Verified: 12 consecutive runs of the deadlock test all pass, full
lbug-db project (336 tests) green.
Cross-reviewer-confirmed but left as residual (design judgment calls,
not mechanical fixes) for the PR description: isDbBusyError doesn't
recognize LadybugDB's 'Only one write transaction' message (pre-existing
production gap, confirmed independently by 3 reviewers); the deadlock
test's timeout path doesn't cancel in-flight writer/reader loops before
closing connections; the reader loop has no bounded retry for transient
errors during the race window; pinning @ladybugdb/core with a caret
range trades automatic patch updates for less re-validation certainty.
* docs: trim task-referencing JSDoc artifacts, add operator notes
The U2 re-validation pass left verbose 'Re-validated on the
0.17.0->0.18.0 bump (#2338): ...' paragraphs stacked onto 5 production
files' docstrings, alongside the already-updated version numbers. That
narrative (SIGKILL-probe methodology, diff commands run, issue
cross-references) belongs in the PR description, not in code comments
that will accumulate a new paragraph on every future bump and confuse
readers who just want the current fact. Trimmed each to state only the
durable, current-state fact:
- lbug-config.ts, sidecar-recovery.ts, lbug-adapter.ts, bridge-db.ts:
dropped the bump-narrative paragraphs; kept only genuinely durable
notes (e.g., which matchers are inspection-verified vs live-tested,
what upstream wording changed).
- conn-lock.ts: compressed a 12-line, 3-issue-number enumeration into
2 lines stating the current conclusion (no upstream 0.18.0 fix
addresses the same-connection-concurrent-query risk this lock
guards against).
Also added operator-facing notes to GUARDRAILS.md and RUNBOOK.md's
existing 'LadybugDB lock' sections: an isDbBusyError gap found during
this validation (LadybugDB's 'Only one write transaction...' message
isn't recognized by our busy/lock retry matcher) means that specific
error can surface unretried. Documented so it's recognized as the same
single-writer conflict, not a new failure mode.
* refactor(test): use gitnexus-shared's withRetry in multiwriter deadlock test
Replaces the hand-rolled writeWithRetry/sleep loop with the existing
gitnexus-shared retry helper (already used by embeddings/hf-env.ts)
instead of duplicating the pattern.
* fix(test): guarantee non-zero retry delay in deadlock test's writer loop
withRetry's isRetryable previously returned {retry: bool} with no afterMs,
so computeBackoffMs's exponential-jitter formula gave a deterministic
zero-delay on the first retry (floor(random()*1) is always 0 at attempt=0).
This contradicted the file's own documented tuning, which specifically
needs a non-zero 1-3ms delay to avoid tripping a different native guard.
Return an explicit afterMs override on the retryable branch instead.
* docs(test): remove dangling doc references from deadlock test JSDoc
The JSDoc pointed to a local-session-only docs/plans/2026-07-01-001-...
path (docs/ is repo-gitignored, so this never existed for anyone but the
implementing session) and to "the PR description" as a source of truth
that stops being current once the PR merges. Replace both with
self-contained prose and durable references (issue/PR numbers, commit
SHAs, GUARDRAILS.md/RUNBOOK.md) that stay resolvable after merge.
* fix(search): harden SUPPORTED_FTS_STEMMERS against external mutation
Type as ReadonlySet<string> to match this codebase's established
convention for exported validation allowlists (EVAL_SERVER_TOOLS,
STRUCTURAL_LABELS). Type-only change — no behavior change; both the
internal .has() check and the sweep test's spread-iterate pattern
continue to work unchanged.
* docs(guardrails): fold Known-gap note into the LadybugDB Sign's Why label
GUARDRAILS.md's own convention is strictly Trigger/Do/Why per Sign
entry (stated in the file's header, followed by all 5 other entries).
The new isDbBusyError gap note introduced a 4th label; fold it into
Why instead, which is what it's actually explaining.
* fix(test): run the multi-writer deadlock test on Windows too
itLbugMultiwriter mirrored lbug-core-adapter.test.ts's win32 skip, but
that pattern exists for a close-then-reopen-same-path lock lingering
bug (kuzudb/kuzu#3872). This test never reopens the database — it
holds connections open for the whole run — so the skip excluded the
one test validating issue #2338's deadlock fix from the platform
conn-lock.ts actually ships native bindings for.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Bump gitnexus, plugin.json, and marketplace.json to 1.6.8 and add the
CHANGELOG section. Headline: opt-in PDG-backed impact analysis plus the
full PDG/taint substrate (CFG → reaching-defs → intra/inter-procedural
taint → control dependence) across the language matrix, multi-branch
indexing, private GitHub PAT + Azure DevOps support, and MCP trace/HTTP.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(impact-pdg): run mutation oracle's analyze child from built dist, not tsx-over-src
The nightly Impact PDG Mutation Report workflow failed at the first fixture with
ERR_MODULE_NOT_FOUND for src/cli/lazy-action.js. The harness shelled the real CLI
out as `node --import tsx src/cli/index.ts analyze …`; on the CI runner's Node
22.22.3, native TypeScript type-stripping is enabled by default and handles the
.ts entry instead of tsx, and native stripping does NOT remap the `./lazy-action.js`
import specifier to lazy-action.ts the way tsx does — so CLI startup crashes
before analyze even runs.
The workflow already builds dist/ (build: 'true'). Prefer the shipped
dist/cli/index.js (plain compiled JS — no tsx, no strip-types, and the parse
workers it spawns also resolve from dist/) for the analyze child, falling back to
tsx's own CLI over src only for build-free local runs. Production-faithful and
version-agnostic across the engines range (node >=22.0).
Verified on a real Node 22.22.3: the dist child starts cleanly with no
lazy-action resolution error; the full `--mutation --only=inter-dispatcher-thin`
run scores realized recall 1.0 and gate-mutation-recall passes. Workers are
independently confirmed green on 22.22.3 in CI (run 27874383902).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(impact-pdg): declare the mutation oracle's @babel/* deps
`bench/impact-pdg/mutation-oracle.mjs` imports @babel/parser, @babel/traverse,
@babel/generator and @babel/types to instrument + value-diff the fixture AST,
but none were declared in package.json. @babel/parser and @babel/types happen to
be hoisted into gitnexus/node_modules transitively, but @babel/traverse and
@babel/generator are only present at the monorepo root — so a fresh `npm ci` in
gitnexus/ (CI) can't resolve them and the oracle dies at module load with
`Cannot find package '@babel/traverse'` right after analyze succeeds.
Declare all four as devDependencies (they're already lazily imported only on the
--mutation path, so they stay out of the unit-test module graph). Verified the
oracle resolves them from gitnexus/node_modules and scores recall 1.0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(impact-pdg): gate only recall-gated mutation checks (honor recallGated)
The recall gate filtered checks by `typeof c.recall === 'number'`, which includes
the UPSTREAM fixtures. The mutation oracle is a FORWARD value-diff: it mutates the
criterion line and observes which downstream lines' values change, so its
behavioral AIS can never intersect a reverse (upstream) PDG slice — recall is 0
by construction. measure.mjs already marks these `recallGated: false` (alongside
id-discrimination corroboration cases) and excludes them from its own internal
gate; the standalone gate just didn't honor that flag, so `intra-control-loop`
(direction: upstream, recall 0) tripped the floor even though the oracle ran the
full suite cleanly (mean recall 0.923).
Filter on `c.recallGated === true` so the floor applies only to the downstream
cases the forward oracle can fairly validate. Verified locally: an
upstream+downstream report now scores 1 of 2 and the gate passes.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(impact-pdg): fail the mutation gate when it has no recall signal + fix README drift
Tri-review hardening of this PR's own changes:
- gate-mutation-recall.mjs: the floor check passed vacuously when `scored` was
empty (`min === null` short-circuits `min !== null && min < floor`). Narrowing
the filter to `recallGated === true` made an empty `scored` set reachable in
more inputs (a degenerate corpus, or a harvest that silently emptied every
behavioral AIS). Now fail loudly when checks exist but none are recall-gated,
so a hollow gate is red rather than a green "scored cases: 0 of N". A genuinely
empty report (0 checks) still passes — it's not a degenerate-corpus signal.
- README.md: the harness substrate section still documented the old
`node --import tsx src/cli/index.ts …` child invocation this PR replaced;
update it to the dist-preferred form to match `cliChildArgs`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>