Skip to content

fix: propagate reasoner failures to the exit code + extended deep taxonomy benchmark - #1962

Open
jeswr wants to merge 3 commits into
mainfrom
fix/edt-perf-and-exit-code
Open

fix: propagate reasoner failures to the exit code + extended deep taxonomy benchmark#1962
jeswr wants to merge 3 commits into
mainfrom
fix/edt-perf-and-exit-code

Conversation

@jeswr

@jeswr jeswr commented Jul 4, 2026

Copy link
Copy Markdown
Member

🚧 Draft — agent-generated PR. Written by an AI agent working on the issue backlog; please review before considering it for merge.

Closes #337
Closes #338

Extended deep taxonomy benchmark (#337)

perf/bench.ts gains a Run extended deep taxonomy benchmark [1000] case in the canonical EDT shape (the one from #338 / eulersharp 12dtb): an individual at the bottom of a 1000-level rdfs:subClassOf chain with 3-way branching, the single backward rule {?X a ?D} <= {?C rdfs:subClassOf ?D. ?X a ?C}., and the target membership asked via --query. EYE proves it in linear time — locally (nice'd, shared 2-core box) a full run including module boot is 0.4–1.4 s and the derived :i1000 a :N1000. was asserted on every validation run, so the CI benchmark job stays bounded.

Why there is no [reasoning only] variant on a pre-loaded module (the pattern the DT[10/50/100] cases use): re-running main on the same module re-asserts the backward rule, and the duplicated rule clauses make the backward search explode — measured: first run 843 ms, second run did not terminate within 180 s. benchmark.js re-invokes the case body on the same state, so a pre-loaded-module variant would hang CI. The fresh-module-per-iteration n3reasoner case is immune; boot cost can be subtracted using the existing Initialise SWIPL with EYE image case. (This measurement is probably also relevant to any future warm-instance-pool work: module reuse needs state clearing, not just FS cleanup.)

Non-zero exit code on reasoner failure (#338)

The CLI used to exit 0 with empty output when the reasoner failed — exactly the silent failure in #338. Empirically, EYE reports failures (parse errors, resource errors, missing files) on stderr with an ** ERROR ** marker while the underlying Prolog goal still succeeds, and on a successful run without --quiet it also writes a benign banner/timing to stderr — so "any stderr" is not a usable failure signal, but the marker is.

  • lib/bin/main.ts: captures the module's stderr (still echoing it), checks for the ** ERROR ** marker and for uncaught Prolog exceptions, and sets process.exitCode = 1 on failure (normal output flushing still happens; no hard exit()).
  • lib/query.ts: qaQuery now returns the final call result ({ done, error?, message? } — the shape swipl-wasm produces), so callers can observe an uncaught exception. Backwards compatible (it previously resolved undefined).
  • __tests__/cli-test.ts: new cases assert exitCode === 1 + ** ERROR ** on stderr for an intentionally invalid input, and an untouched exitCode on success.

Note: this makes the CLI exit non-zero on inputs that previously "succeeded" silently with no output — intended, but technically observable behaviour change for scripts that relied on exit 0.

Validation

🤖 Generated with Claude Code

Two related changes for the extended-deep-taxonomy issues:

- perf/bench.ts: add an 'extended deep taxonomy benchmark [1000]' case in
  the canonical EDT shape (individual at the bottom of a 1000-level
  rdfs:subClassOf chain with 3-way branching, backward rule
  {?X a ?D} <= {?C rdfs:subClassOf ?D. ?X a ?C}, target membership via
  --query). EYE proves it in linear time (<0.5s warm locally), so the CI
  benchmark stays bounded. The case deliberately boots a fresh module per
  iteration: re-running main() on a pre-loaded module re-asserts the
  backward rule and the duplicated clauses make the backward search explode
  (documented in the case comment).

- lib/bin/main.ts: the CLI used to exit 0 with empty output when the
  reasoner failed (e.g. resource errors on large N3 files, parse errors).
  EYE reports failures on stderr with an '** ERROR **' marker while the
  underlying Prolog goal still succeeds, so mainFunc now captures stderr,
  checks for the marker (and for uncaught Prolog exceptions, which
  lib/query.ts qaQuery now surfaces by returning the final call result),
  and sets process.exitCode = 1 on failure.

- __tests__/cli-test.ts: regression tests asserting a non-zero exit code
  for an erroring input and an untouched exit code on success.

Closes #337
Closes #338

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jeswr
jeswr marked this pull request as ready for review July 4, 2026 23:34
Copilot AI review requested due to automatic review settings July 4, 2026 23:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'EYE JS Benchmark'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.25.

Benchmark suite Current: b387b51 Previous: d129aee Ratio
Run socrates query 14.77 ops/sec (±7.81%) 22.2 ops/sec (±2.39%) 1.50
Executing the socrates query 119 ops/sec (±22.29%) 3890 ops/sec (±1.67%) 32.69
Run deep taxonomy benchmark [10] 7.09 ops/sec (±2.94%) 18.83 ops/sec (±6.56%) 2.66
Run deep taxonomy benchmark [50] 0.27 ops/sec (±0.59%) 19.63 ops/sec (±7.26%) 72.70
Run deep taxonomy benchmark [100] 0.04 ops/sec (±1.05%) 18.67 ops/sec (±6.95%) 466.75
Run deep taxonomy benchmark [10] [reasoning only] 7.7 ops/sec (±16.76%) 4107 ops/sec (±1.65%) 533.38
Run deep taxonomy benchmark [50] [reasoning only] 0.63 ops/sec (±23.46%) 4147 ops/sec (±1.85%) 6582.54
Run deep taxonomy benchmark [100] [reasoning only] 0.17 ops/sec (±24.99%) 4136 ops/sec (±1.60%) 24329.41
Run timbl + foaf + rdfs rules 2.15 ops/sec (±2.18%) 17.23 ops/sec (±6.18%) 8.01
Run timbl + foaf + owl2rl rules 1.21 ops/sec (±1.90%) 17.03 ops/sec (±3.91%) 14.07
Run timbl + rdfs rules 4.75 ops/sec (±3.74%) 18.28 ops/sec (±6.60%) 3.85
Run timbl + owl2rl rules 6.04 ops/sec (±1.03%) 17.1 ops/sec (±6.85%) 2.83
Run timbl + foaf + rdfs rules [string] 2.22 ops/sec (±0.50%) 18.45 ops/sec (±7.08%) 8.31
Run timbl + foaf + owl2rl rules [string] 1.27 ops/sec (±0.19%) 18.54 ops/sec (±6.64%) 14.60
Run timbl + rdfs rules [string] 4.93 ops/sec (±0.43%) 18.77 ops/sec (±6.77%) 3.81
Run timbl + owl2rl rules [string] 6.24 ops/sec (±0.26%) 18.98 ops/sec (±6.47%) 3.04

This comment was automatically generated by workflow using github-action-benchmark.

CC: @jeswr

jeswr and others added 2 commits July 5, 2026 00:21
`proc` is dependency-injected into mainFunc so tests can observe the
exit code; assigning to it is the point of the change, so silence
no-param-reassign for that one line. Lint-only, no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Coverage thresholds were failing on the new exit-code logic:

- dist/bin/main.js 64-65: the qaQuery catch block was never executed
  (its istanbul-ignore hint was emitted by tsc as a trailing comment of
  the try block, so it was ignored). Rather than fixing the hint, drop
  it and exercise the path for real: two new CLI tests reject qaQuery
  with an Error and a non-Error, covering both sides of the
  `e instanceof Error` ternary and the failed exit-code branch.
- dist/transformers.js 86: the "Error while parsing query result" throw
  in parse() was uncovered; a new test injects a stub SWIPL build whose
  output is invalid N3 and asserts the rejection.

No library behavior changes and no threshold changes; local run is now
100% statements/lines/functions, 98.71% branches (the only remaining
miss is the tsc __importDefault helper branch, pre-existing on main).

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.

Investigate: Silent failure on large N3 files chore: add performance tests for extended deep taxonomy benchmark

2 participants