Skip to content

test: spec corpus hygiene and a generated affected map (T-10) - #393

Merged
jsam merged 5 commits into
mainfrom
fm/il-344-spec-hygiene
Oct 6, 2026
Merged

jsam merged 5 commits into
mainfrom
fm/il-344-spec-hygiene

Conversation

@jsam

@jsam jsam commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Intent

Do GitHub issue #344 in inputlayer/inputlayer, part of the test restructure in TESTING.md: "Testing: Spec corpus hygiene (T-10)".

Part of the test strategy (captain, 2026-10-05: scenarios that test the changes together; cleaner, faster suites; TESTING.md). Epic #305.

Scope. Remove the 4 exact duplicates (keep the lowest-numbered file), fold 39_meta_complete into 33_meta, move 42_consistency_pack, 43_prompt_integrity, 50_landing_page under examples/iql/90_product_fixtures (still run, named for what they are) and correct 50_landing_page/02_incremental.iql's comments per D1; generate scripts/affected-map.toml from each category's statement types (a script that lists which src/ modules a category exercises using --debug plan output) so test-affected.sh stops using a hand-written case; --debug flag for the runner.

Acceptance. 1,153 specs; test-affected.sh picks the categories for a change in src/provenance (today: none); runner works against debug binaries.

Deliver it as a small PR; done means merged with CI green.

D1 is the docs-truth change (#306, merged as #372): persistent rules are stored definitions re-derived from the base facts on every read, so no doc or example comment may claim materialization or incremental maintenance.

What Changed

  • Spec corpus cleanup in examples/iql/: removed four duplicate specs and their snapshots (09_recursion/86_descendant, 11_types/34_bool_ops, 12_errors/55_empty_batch_insert, 22_set_operations/36_exclusive_or), folded 39_meta_complete into 33_meta (renumbered 33_–39_), and moved 42_consistency_pack, 43_prompt_integrity and 50_landing_page under 90_product_fixtures/. The comment in 50_landing_page/02_incremental.iql no longer claims the query avoids a full recompute. The differential oracle corpus path, the verified-completions docs and the TESTING.md category table follow the new paths.
  • Generated affected map: new scripts/gen-affected-map.py writes scripts/affected-map.toml, and run_snapshot_tests.sh --affected now reads leaf-module mappings from it instead of a hand-written case. src/provenance now maps to 36_explain_trace and 90_product_fixtures, and the vector and temporal modules pick up more categories than the old hand-written list. Unmapped src/ changes still run the whole corpus, as do changes to the map or its generator.
  • Map is text-derived, not plan-derived: the generator matches statement types in spec text, .loaded files and recorded transcripts against a fixed statement-type → module table, rather than using --debug plan output; its docstring records why (plans name no src/ module, and .why/.why_not produce no plan). No --debug flag is added to the runner or the generator in this branch. The map and generator are added to the CI rust path filter and to the make pre-pr rust inputs.

🤖 Generated with Claude Code

Risk Assessment

⚠️ Medium: The code is well-bounded and I found no wrong-result defect (map lookup, nested-category filtering, oracle path and duplicate removals all trace correctly), but the result contradicts the literal "1,153 specs" criterion and the generator's regex-table approach and new stale-map CI gate need the author's confirmation.

Testing

Built the debug server and client through the runner (test-affected.sh HEAD --debug) and drove everything on an isolated port and data directory. A probe edit in src/provenance selected 36_explain_trace and 90_product_fixtures and those specs passed on target/debug. Further probe edits covered docs-only, vector, temporal, mixed, prefix-collision and map-file changes; each selected the expected categories or widened to the whole corpus. The folded 33_meta specs, the moved fixtures and the four kept twins passed one by one. The whole corpus on debug binaries failed once on a 60 s client timeout for the consistency-pack fixture while host load was above 40 on 32 cores; the identical re-run passed with 1,148 passed and 7 skipped, and the fixture alone takes about 8 s. The seven landing-page scripts are skipped by the runner because they have no recorded transcript, as at the base, so I ran 02_incremental.iql directly against a disposable debug server. The oracle corpus test, example_verification, generator regeneration and make pre-pr routing also passed. No UI surface is involved, so there are no screenshots. All probe edits were reverted and build output removed; the worktree is clean.

  • Live validation: ✅ go - 15 of 16 scenarios driven live against the product
Scenario Result Live Evidence
Edit a file in src/provenance and run test-affected.sh --debug: it picks 36_explain_trace and 90_product_fixtures and their specs pass on debug binaries ✅ pass live 01-provenance-affected-debug.txt: selection lists both categories, 18 passed, 0 failed, 7 skipped (landing-page scripts without transcripts), exit 0; only target/debug existed
Runner works against debug binaries across the whole corpus (run_snapshot_tests.sh --debug --skip-build) ✅ pass live 06-full-corpus-debug-rerun.txt: 1,148 passed, 0 failed, 7 skipped, exit 0. First attempt (05-full-corpus-debug.txt) had one 60 s client timeout under host load; see finding
Without --debug and with only debug binaries present, the runner refuses rather than silently using them ✅ pass live 03-affected-selection-cases.txt: 'Binaries not found after build!' naming target/release, exit 1
Adversarial: a file that only shares the name prefix (src/provenance_probe.rs) does not get the narrow provenance mapping and widens to every category ✅ pass live 03-affected-selection-cases.txt: 'Changes since HEAD affect every category'
Adversarial: a provenance edit combined with a non-leaf engine file (src/lib.rs) widens to every category ✅ pass live 03-affected-selection-cases.txt: 'Changes since HEAD affect every category'
A provenance edit plus an edited spec selects the union of the mapped categories and the spec's own category ✅ pass live 03-affected-selection-cases.txt: 29_temporal, 36_explain_trace, 90_product_fixtures
Vector and temporal leaf modules still select their categories from the generated map; temporal ones run and pass on debug binaries ✅ pass live 03-affected-selection-cases.txt: src/vector_ops.rs selects 7 categories; src/temporal_ops.rs selects 18_advanced_patterns and 29_temporal, 125 passed
A docs-only change selects no specs and exits 0; editing the map itself runs every category ✅ pass live 03-affected-selection-cases.txt: 'No runnable specs affected', exit 0; map edit prints 'affect every category'
Regenerating the map with gen-affected-map.py reproduces the committed scripts/affected-map.toml, and the generator's removed --debug option is rejected ✅ pass live python3 scripts/gen-affected-map.py left no git diff; --debug printed 'Unknown option: --debug', exit 1
The seven specs folded from 39_meta_complete run and pass as 33_meta/33..39 ✅ pass live 04-folded-moved-specs-sequential.txt: PASS [33_meta] 33_status through 39_help_comprehensive
Moved product fixtures still run under examples/iql/90_product_fixtures: consistency pack and prompt integrity pass; landing-page scripts are picked up and skipped for lack of a transcript, as at the… ✅ pass live 04-folded-moved-specs-sequential.txt: PASS [42_consistency_pack], PASS [43_prompt_integrity], 7 SKIP [50_landing_page] 'no .out file'
The four duplicates are gone, their lower-numbered twins remain and pass, and the corpus holds 1,149 recorded specs (1,153 at base minus 4) ✅ pass live 02-corpus-shape.txt (counts 1153 -> 1149, four files absent, four twins present); 04-folded-moved-specs-sequential.txt (twins PASS)
The corrected landing-page 02_incremental.iql behaves as its comments now say: after adding one edge, the next query includes diana ✅ pass live 09-landing-incremental-live.txt: second ?authority("alice", Who) returns 3 rows including diana; comments make no materialization or incremental-maintenance claim
The differential oracle corpus test finds the consistency pack at its new path and still agrees with spec and reference ✅ pass live 07-oracle-and-example-verification.txt: iql_corpus_agrees_with_spec_and_reference ok; example_verification 13 passed
make pre-pr treats a change to affected-map.toml or gen-affected-map.py as a Rust input and runs lint, unit tests and test-affected.sh ✅ pass live 08-pre-pr-routing.txt: both map files start clippy, test and test-affected.sh; the control script starts only fmt, python3 and perf-gate.sh (real Makefile, stubbed tools)
A PR that changes only affected-map.toml or gen-affected-map.py triggers the Rust CI job through the paths filter in ci.yml ⏸️ untested no The filter is evaluated by the dorny/paths-filter action inside GitHub Actions; there is no local runner for it in this workspace, and pushing or triggering CI belongs to the outer executor's later ph…
Evidence: Provenance edit selects its categories and runs them on debug binaries

$ git status --short M src/provenance/why_not.rs $ INPUTLAYER_TEST_PORT=18744 scripts/test-affected.sh HEAD --debug Changes since HEAD affect these categories: - 36_explain_trace - 90_product_fixtures Building project... Starting server... Server started (PID 1746769) Running 25 tests in parallel (32 workers), 0 sequentially... Passed: 18 Failed: 0 Skipped: 7 All tests passed! exit=0

$ git status --short
 M src/provenance/why_not.rs
$ INPUTLAYER_TEST_PORT=18744 scripts/test-affected.sh HEAD --debug
Changes since HEAD affect these categories:
  - 36_explain_trace
  - 90_product_fixtures

Building project...
Starting server...
Server log: /tmp/tmp.NIE8O9xgPU/server.log
Server started (PID 1746769)
Pre-cleaning stale knowledge graphs...

========================================
  InputLayer Snapshot Tests (parallel: 32 jobs)
========================================

Running 25 tests in parallel (32 workers), 0 sequentially...


========================================
  Summary
========================================
Passed:  18
Failed:  0
Skipped: 7

All tests passed!
exit=0
Evidence: Corpus shape: counts, removed duplicates, folded and moved categories
$ find examples/iql -name "*.iql.out" | wc -l   (HEAD)
1149
$ git ls-tree -r --name-only efa32cec examples/iql | grep -c "\.iql\.out$"   (base)
1153
$ runnable specs (runner find rule)
1155
$ removed duplicates / kept twins
absent   09_recursion/86_descendant
absent   12_errors/55_empty_batch_insert
absent   11_types/34_bool_ops
absent   22_set_operations/36_exclusive_or
present  09_recursion/76_tree_traversal
present  12_errors/41_empty_insert_batch
present  11_types/27_bool_logic
present  08_negation/72_exclusive_or
$ ls -d examples/iql/39_meta_complete examples/iql/4[23]_* examples/iql/50_*
ls: cannot access 'examples/iql/39_meta_complete': No such file or directory
ls: cannot access 'examples/iql/4[23]_*': No such file or directory
ls: cannot access 'examples/iql/50_*': No such file or directory
$ ls examples/iql/33_meta | grep -E "^3[3-9]_"
33_status.iql
33_status.iql.out
34_load_basic.iql
34_load_basic.iql.out
35_rule_management.iql
35_rule_management.iql.out
36_rel_operations.iql
36_rel_operations.iql.out
37_session_complete.iql
37_session_complete.iql.out
38_kg_comprehensive.iql
38_kg_comprehensive.iql.out
39_help_comprehensive.iql
39_help_comprehensive.iql.out
$ find examples/iql/90_product_fixtures -name "*.iql" | sort
examples/iql/90_product_fixtures/42_consistency_pack/01_consistency_core_fixture.iql
examples/iql/90_product_fixtures/43_prompt_integrity/01_prompt_integrity_fixture.iql
examples/iql/90_product_fixtures/50_landing_page/01_retraction.iql
examples/iql/90_product_fixtures/50_landing_page/02_incremental.iql
examples/iql/90_product_fixtures/50_landing_page/03_provenance.iql
examples/iql/90_product_fixtures/50_landing_page/04_rules_vectors.iql
examples/iql/90_product_fixtures/50_landing_page/05_manufacturing.iql
examples/iql/90_product_fixtures/50_landing_page/06_supply_chain.iql
examples/iql/90_product_fixtures/50_landing_page/07_agent_memory.iql
Evidence: Affected-selection cases, including adversarial prefix and widening cases

\### A change outside the engine (docs only) selects nothing
$ git status --short
 M docs/README.md
$ scripts/test-affected.sh HEAD --debug --skip-build
No runnable specs affected by the changes since HEAD.
exit=0

\### provenance + an unrelated spec: union of mapped categories and the spec's category (no release binaries, so the run stops after selection)
$ git status --short
 M examples/iql/29_temporal/01_time_now.iql
 M src/provenance/proof_tree.rs
$ scripts/test-affected.sh HEAD --skip-build
Changes since HEAD affect these categories:
  - 29_temporal
  - 36_explain_trace
  - 90_product_fixtures

Skipping build (--skip-build)...
Binaries not found after build!
  Client: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-client
  Server: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-server
exit=1

\### ADVERSARIAL: src/provenance_probe.rs only shares a name prefix with src/provenance; must not get the narrow mapping
$ git status --short
 A src/provenance_probe.rs
$ scripts/test-affected.sh HEAD --skip-build
Changes since HEAD affect every category - running all snapshot tests.

Skipping build (--skip-build)...
Binaries not found after build!
  Client: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-client
  Server: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-server
exit=1

\### ADVERSARIAL: provenance plus a non-leaf engine file must widen to every category
$ git status --short
 M src/lib.rs
 M src/provenance/why_not.rs
$ scripts/test-affected.sh HEAD --skip-build
Changes since HEAD affect every category - running all snapshot tests.

Skipping build (--skip-build)...
Binaries not found after build!
  Client: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-client
  Server: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-server
exit=1

\### Editing the map itself affects every category
$ git status --short
 M scripts/affected-map.toml
$ scripts/test-affected.sh HEAD --skip-build
Changes since HEAD affect every category - running all snapshot tests.

Skipping build (--skip-build)...
Binaries not found after build!
  Client: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-client
  Server: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-server
exit=1

\### src/vector_ops.rs selects the vector categories
$ git status --short
 M src/vector_ops.rs
$ scripts/test-affected.sh HEAD --skip-build
Changes since HEAD affect these categories:
  - 12_errors
  - 16_vectors
  - 28_docs_coverage
  - 30_quantization
  - 31_lsh
  - 33_meta
  - 90_product_fixtures

Skipping build (--skip-build)...
Binaries not found after build!
  Client: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-client
  Server: ~/.no-mistakes/worktrees/9eaf1dfa27af/01M477FZNSSH7NBTS0Q2QPX4VK/target/release/inputlayer-server
exit=1

\### src/temporal_ops.rs selects temporal categories and runs them on debug binaries
$ git status --short
 M src/temporal_ops.rs
$ scripts/test-affected.sh HEAD --debug --skip-build
Changes since HEAD affect these categories:
  - 18_advanced_patterns
  - 29_temporal

Skipping build (--skip-build)...
Starting server...
Server log: /tmp/tmp.7oxe3oQZqL/server.log
Server started (PID 1804888)
Pre-cleaning stale knowledge graphs...

========================================
  InputLayer Snapshot Tests (parallel: 32 jobs)
========================================

Running 125 tests in parallel (32 workers), 0 sequentially...


========================================
  Summary
========================================
Passed:  125
Failed:  0
Skipped: 0

All tests passed!
exit=0
Evidence: Folded 33_meta specs, moved fixtures and kept twins, one by one on debug binaries

PASS [08_negation] 72_exclusive_or.iql PASS [09_recursion] 76_tree_traversal.iql PASS [11_types] 27_bool_logic.iql PASS [12_errors] 41_empty_insert_batch.iql PASS [33_meta] 33_status.iql PASS [33_meta] 34_load_basic.iql PASS [33_meta] 35_rule_management.iql PASS [33_meta] 36_rel_operations.iql PASS [33_meta] 37_session_complete.iql PASS [33_meta] 38_kg_comprehensive.iql PASS [33_meta] 39_help_comprehensive.iql PASS [42_consistency_pack] 01_consistency_core_fixture.iql PASS [43_prompt_integrity] 01_prompt_integrity_fixture.iql SKIP [50_landing_page] 01_retraction.iql - no .out file (6 more landing-page SKIP lines) Passed: 13 Failed: 0 Skipped: 7

$ scripts/run_snapshot_tests.sh --debug --skip-build -j 1 -f "/\(33_meta/3[3-9]_\|90_product_fixtures\|76_tree_traversal\|41_empty_insert_batch\|27_bool_logic\|08_negation/72_exclusive_or\)"
Skipping build (--skip-build)...
Starting server...
Server log: /tmp/tmp.V1N9mSeUsz/server.log
Server started (PID 1859014)
Pre-cleaning stale knowledge graphs...

========================================
  InputLayer Snapshot Tests (sequential)
========================================

PASS [08_negation] 72_exclusive_or.iql
PASS [09_recursion] 76_tree_traversal.iql
PASS [11_types] 27_bool_logic.iql
PASS [12_errors] 41_empty_insert_batch.iql
PASS [33_meta] 33_status.iql
PASS [33_meta] 34_load_basic.iql
PASS [33_meta] 35_rule_management.iql
PASS [33_meta] 36_rel_operations.iql
PASS [33_meta] 37_session_complete.iql
PASS [33_meta] 38_kg_comprehensive.iql
PASS [33_meta] 39_help_comprehensive.iql
PASS [42_consistency_pack] 01_consistency_core_fixture.iql
PASS [43_prompt_integrity] 01_prompt_integrity_fixture.iql
SKIP [50_landing_page] 01_retraction.iql - no .out file
SKIP [50_landing_page] 02_incremental.iql - no .out file
SKIP [50_landing_page] 03_provenance.iql - no .out file
SKIP [50_landing_page] 04_rules_vectors.iql - no .out file
SKIP [50_landing_page] 05_manufacturing.iql - no .out file
SKIP [50_landing_page] 06_supply_chain.iql - no .out file
SKIP [50_landing_page] 07_agent_memory.iql - no .out file

========================================
  Summary
========================================
Passed:  13
Failed:  0
Skipped: 7

All tests passed!
exit=0
Evidence: Full corpus on debug binaries, first run (one timeout under host load)
$ scripts/run_snapshot_tests.sh --debug --skip-build
Skipping build (--skip-build)...
Starting server...
Server log: /tmp/tmp.j8qwhonZwD/server.log
Server started (PID 1865641)
Pre-cleaning stale knowledge graphs...

========================================
  InputLayer Snapshot Tests (parallel: 32 jobs)
========================================

Running 1151 tests in parallel (32 workers), 4 sequentially...

environment: line 14: 1907438 Alarm clock             perl -e 'alarm shift; exec @ARGV' "$TEST_TIMEOUT" "$CLIENT_BIN" --server "$CLIENT_SERVER_URL" --script "$test_file" > "$output_file" 2> "$stderr_file"
FAIL [42_consistency_pack] 01_consistency_core_fixture.iql
  --- Diff (expected vs actual) ---
404d403
< Knowledge graph 'test_consistency_n42t01' dropped.


Running 4 sequential tests (global state)...
Restarting server (clean data)...
Server log: /tmp/tmp.j8qwhonZwD/server.log
Server started (PID 2083615)
PASS [01_knowledge_graph] 02_list_kg.iql
PASS [33_meta] 03_compact.iql
PASS [33_meta] 12_compact_empty.iql
PASS [33_meta] 38_kg_comprehensive.iql

========================================
  Summary
========================================
Passed:  1147
Failed:  1
Skipped: 7

Some tests failed!
Run with -v for detailed output

=== Server Log (last 50 lines) ===
�[2m2026-10-06T00:12:08.672012Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_parse_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mstatements�[0m�[2m=�[0m1 �[3mparse_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.672034Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_start �[3mkg�[0m�[2m=�[0mtest_kg_alpha_n39t06
�[2m2026-10-06T00:12:08.684791Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m3
�[2m2026-10-06T00:12:08.684839Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_prepare_complete �[3mkg�[0m�[2m=�[0mtest_kg_alpha_n39t06 �[3melapsed_ms�[0m�[2m=�[0m12
�[2m2026-10-06T00:12:08.684854Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_prepare_ok �[3mkg�[0m�[2m=�[0mtest_kg_alpha_n39t06
�[2m2026-10-06T00:12:08.698645Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m2
�[2m2026-10-06T00:12:08.704974Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_finish_complete �[3mkg�[0m�[2m=�[0mtest_kg_alpha_n39t06 �[3melapsed_ms�[0m�[2m=�[0m20
�[2m2026-10-06T00:12:08.706046Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_finish_ok �[3mkg�[0m�[2m=�[0mtest_kg_alpha_n39t06
�[2m2026-10-06T00:12:08.706135Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_statement_exec_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mstmt_exec_ms�[0m�[2m=�[0m34 �[3msession_facts�[0m�[2m=�[0m0 �[3msession_rules�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.706154Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_complete_messages �[3mprogram_len�[0m�[2m=�[0m29 �[3mtotal_ms�[0m�[2m=�[0m34
�[2m2026-10-06T00:12:08.706383Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mcompute_ms�[0m�[2m=�[0m34 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m19 �[3mmsg_bytes�[0m�[2m=�[0m70�[0m
�[2m2026-10-06T00:12:08.706702Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_end �[3mprogram_len�[0m�[2m=�[0m29 �[3melapsed_ms�[0m�[2m=�[0m34 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m19 �[3mmsg_bytes�[0m�[2m=�[0m70�[0m
�[2m2026-10-06T00:12:08.709468Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_start �[3mprogram_len�[0m�[2m=�[0m28 �[3mparam_count�[0m�[2m=�[0m0 �[3mprogram_preview�[0m�[2m=�[0m.kg drop test_kg_beta_n39t06 �[2m�[3mrequest_id�[0m�[2m=�[0m20 �[3mmsg_bytes�[0m�[2m=�[0m69�[0m
�[2m2026-10-06T00:12:08.709709Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_start �[3mprogram_len�[0m�[2m=�[0m28
�[2m2026-10-06T00:12:08.709738Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_ensure_exists �[3mkg�[0m�[2m=�[0mdefault �[3melapsed_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.709763Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_parse_complete �[3mprogram_len�[0m�[2m=�[0m28 �[3mstatements�[0m�[2m=�[0m1 �[3mparse_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.709785Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_start �[3mkg�[0m�[2m=�[0mtest_kg_beta_n39t06
�[2m2026-10-06T00:12:08.724345Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m6
�[2m2026-10-06T00:12:08.724398Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_prepare_complete �[3mkg�[0m�[2m=�[0mtest_kg_beta_n39t06 �[3melapsed_ms�[0m�[2m=�[0m14
�[2m2026-10-06T00:12:08.724415Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_prepare_ok �[3mkg�[0m�[2m=�[0mtest_kg_beta_n39t06
�[2m2026-10-06T00:12:08.745924Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m6
�[2m2026-10-06T00:12:08.749156Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_finish_complete �[3mkg�[0m�[2m=�[0mtest_kg_beta_n39t06 �[3melapsed_ms�[0m�[2m=�[0m24
�[2m2026-10-06T00:12:08.749186Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_finish_ok �[3mkg�[0m�[2m=�[0mtest_kg_beta_n39t06
�[2m2026-10-06T00:12:08.749261Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_statement_exec_complete �[3mprogram_len�[0m�[2m=�[0m28 �[3mstmt_exec_ms�[0m�[2m=�[0m39 �[3msession_facts�[0m�[2m=�[0m0 �[3msession_rules�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.749279Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_complete_messages �[3mprogram_len�[0m�[2m=�[0m28 �[3mtotal_ms�[0m�[2m=�[0m39
�[2m2026-10-06T00:12:08.749872Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_complete �[3mprogram_len�[0m�[2m=�[0m28 �[3mcompute_ms�[0m�[2m=�[0m40 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m20 �[3mmsg_bytes�[0m�[2m=�[0m69�[0m
�[2m2026-10-06T00:12:08.750238Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_end �[3mprogram_len�[0m�[2m=�[0m28 �[3melapsed_ms�[0m�[2m=�[0m40 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m20 �[3mmsg_bytes�[0m�[2m=�[0m69�[0m
�[2m2026-10-06T00:12:08.751430Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_start �[3mprogram_len�[0m�[2m=�[0m29 �[3mparam_count�[0m�[2m=�[0m0 �[3mprogram_preview�[0m�[2m=�[0m.kg drop test_kg_gamma_n39t06 �[2m�[3mrequest_id�[0m�[2m=�[0m21 �[3mmsg_bytes�[0m�[2m=�[0m70�[0m
�[2m2026-10-06T00:12:08.751711Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_start �[3mprogram_len�[0m�[2m=�[0m29
�[2m2026-10-06T00:12:08.751746Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_ensure_exists �[3mkg�[0m�[2m=�[0mdefault �[3melapsed_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.751783Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_parse_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mstatements�[0m�[2m=�[0m1 �[3mparse_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.751812Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_start �[3mkg�[0m�[2m=�[0mtest_kg_gamma_n39t06
�[2m2026-10-06T00:12:08.761631Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m2
�[2m2026-10-06T00:12:08.761681Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_prepare_complete �[3mkg�[0m�[2m=�[0mtest_kg_gamma_n39t06 �[3melapsed_ms�[0m�[2m=�[0m9
�[2m2026-10-06T00:12:08.761697Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_prepare_ok �[3mkg�[0m�[2m=�[0mtest_kg_gamma_n39t06
�[2m2026-10-06T00:12:08.770933Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m save_metadata_complete �[3melapsed_ms�[0m�[2m=�[0m5
�[2m2026-10-06T00:12:08.774283Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_drop_finish_complete �[3mkg�[0m�[2m=�[0mtest_kg_gamma_n39t06 �[3melapsed_ms�[0m�[2m=�[0m12
�[2m2026-10-06T00:12:08.774315Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m meta_kg_drop_finish_ok �[3mkg�[0m�[2m=�[0mtest_kg_gamma_n39t06
�[2m2026-10-06T00:12:08.774392Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_statement_exec_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mstmt_exec_ms�[0m�[2m=�[0m22 �[3msession_facts�[0m�[2m=�[0m0 �[3msession_rules�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.774417Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_complete_messages �[3mprogram_len�[0m�[2m=�[0m29 �[3mtotal_ms�[0m�[2m=�[0m22
�[2m2026-10-06T00:12:08.774646Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_complete �[3mprogram_len�[0m�[2m=�[0m29 �[3mcompute_ms�[0m�[2m=�[0m23 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m21 �[3mmsg_bytes�[0m�[2m=�[0m70�[0m
�[2m2026-10-06T00:12:08.775016Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_end �[3mprogram_len�[0m�[2m=�[0m29 �[3melapsed_ms�[0m�[2m=�[0m23 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m21 �[3mmsg_bytes�[0m�[2m=�[0m70�[0m
�[2m2026-10-06T00:12:08.776302Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_start �[3mprogram_len�[0m�[2m=�[0m8 �[3mparam_count�[0m�[2m=�[0m0 �[3mprogram_preview�[0m�[2m=�[0m.kg list �[2m�[3mrequest_id�[0m�[2m=�[0m22 �[3mmsg_bytes�[0m�[2m=�[0m49�[0m
�[2m2026-10-06T00:12:08.779541Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_start �[3mprogram_len�[0m�[2m=�[0m8
�[2m2026-10-06T00:12:08.779598Z�[0m �[32m INFO�[0m �[2minputlayer::storage_engine�[0m�[2m:�[0m kg_ensure_exists �[3mkg�[0m�[2m=�[0mdefault �[3melapsed_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.779650Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_parse_complete �[3mprogram_len�[0m�[2m=�[0m8 �[3mstatements�[0m�[2m=�[0m1 �[3mparse_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.779771Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_job_complete_messages �[3mprogram_len�[0m�[2m=�[0m8 �[3mtotal_ms�[0m�[2m=�[0m0
�[2m2026-10-06T00:12:08.780008Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::handler�[0m�[2m:�[0m query_complete �[3mprogram_len�[0m�[2m=�[0m8 �[3mcompute_ms�[0m�[2m=�[0m3 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m22 �[3mmsg_bytes�[0m�[2m=�[0m49�[0m
�[2m2026-10-06T00:12:08.780074Z�[0m �[32m INFO�[0m �[1mws_request�[0m: �[2minputlayer::protocol::rest::handlers::ws::execute�[0m�[2m:�[0m ws_execute_end �[3mprogram_len�[0m�[2m=�[0m8 �[3melapsed_ms�[0m�[2m=�[0m3 �[3mok�[0m�[2m=�[0mtrue �[2m�[3mrequest_id�[0m�[2m=�[0m22 �[3mmsg_bytes�[0m�[2m=�[0m49�[0m
�[2m2026-10-06T00:12:08.785524Z�[0m �[32m INFO�[0m �[2minputlayer::protocol::rest::handlers::ws�[0m�[2m:�[0m ws_session_disconnecting �[3mactive_sessions�[0m�[2m=�[0m1
=== End Server Log ===
exit=1
Evidence: Full corpus on debug binaries, re-run (all pass)

$ scripts/run_snapshot_tests.sh --debug --skip-build (second run) Running 1151 tests in parallel (32 workers), 4 sequentially... Running 4 sequential tests (global state)... PASS [01_knowledge_graph] 02_list_kg.iql PASS [33_meta] 03_compact.iql PASS [33_meta] 12_compact_empty.iql PASS [33_meta] 38_kg_comprehensive.iql Passed: 1148 Failed: 0 Skipped: 7 All tests passed! exit=0

$ scripts/run_snapshot_tests.sh --debug --skip-build   (second run)
Skipping build (--skip-build)...
Starting server...
Server log: /tmp/tmp.FiC6rgrjZD/server.log
Server started (PID 2104552)
Pre-cleaning stale knowledge graphs...

========================================
  InputLayer Snapshot Tests (parallel: 32 jobs)
========================================

Running 1151 tests in parallel (32 workers), 4 sequentially...


Running 4 sequential tests (global state)...
Restarting server (clean data)...
Server log: /tmp/tmp.FiC6rgrjZD/server.log
Server started (PID 2271799)
PASS [01_knowledge_graph] 02_list_kg.iql
PASS [33_meta] 03_compact.iql
PASS [33_meta] 12_compact_empty.iql
PASS [33_meta] 38_kg_comprehensive.iql

========================================
  Summary
========================================
Passed:  1148
Failed:  0
Skipped: 7

All tests passed!
exit=0
Evidence: Oracle corpus test and example verification
$ cargo test --test differential_oracle iql_corpus_agrees_with_spec_and_reference -- --nocapture
    "reference stopped at `.rule remove reachable 1`: statement meta RuleRe": 1,
    "reference stopped at `.rule remove reachable 2`: statement meta RuleRe": 1,
    "reference stopped at `.rule remove simple 1`: statement meta RuleRemov": 1,
    "rule 'items' over a relation with base facts": 1,
    "statement meta RuleDropPrefix(\"mem_\")": 1,
    "statement meta RuleDropPrefix(\"test_\")": 1,
    "statement meta RuleDropPrefix(\"zzz_\")": 1,
    "statement meta RuleEdit { name: \"reachable\", index: 1, rule_text: \"+re": 1,
    "statement meta RuleRemove { name: \"best\", index: 0 }": 1,
    "statement meta RuleRemove { name: \"multi\", index: 1 }": 1,
    "statement meta RuleRemove { name: \"nonexistent\", index: 0 }": 1,
    "statement meta RuleRemove { name: \"output\", index: 1 }": 1,
    "statement meta RuleRemove { name: \"reach\", index: 0 }": 1,
    "statement meta RuleRemove { name: \"reach\", index: 1 }": 1,
    "statement meta RuleRemove { name: \"reachable\", index: 0 }": 1,
    "statement meta RuleRemove { name: \"reachable\", index: 1 }": 1,
    "statement meta RuleRemove { name: \"simple\", index: 0 }": 1,
    "statement meta RuleRemove { name: \"simple\", index: 4 }": 1,
    "sum over \"a\"": 1,
    "term Arithmetic(Binary { op: Add, left: Variable(\"C1\"), right: Variabl": 1,
    "term Arithmetic(Binary { op: Add, left: Variable(\"D\"), right: Constant": 1,
    "term Arithmetic(Binary { op: Add, left: Variable(\"W1\"), right: Variabl": 1,
    "term Arithmetic(Binary { op: Sub, left: Variable(\"Id\"), right: Constan": 1,
    "term Arithmetic(Binary { op: Sub, left: Variable(\"T\"), right: Constant": 2,
    "variable D not bound by a positive atom": 4,
    "variable D2 not bound by a positive atom": 1,
    "variable Depth not bound by a positive atom": 1,
    "variable L not bound by a positive atom": 1,
    "variable N not bound by a positive atom": 1,
    "variable NewPrice not bound by a positive atom": 1,
    "variable Sq not bound by a positive atom": 1,
    "variable Total not bound by a positive atom": 1,
    "variable TotalCost not bound by a positive atom": 1,
    "variable TotalQty not bound by a positive atom": 1,
    "variable Val not bound by a positive atom": 2,
    "variable X not bound by a positive atom": 1,
    "variable Y not bound by a positive atom": 6,
}
test iql_corpus_agrees_with_spec_and_reference ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 8 filtered out; finished in 13.55s
exit=0
$ cargo test --test example_verification
test test_negation_syntax_valid ... ok
test test_knowledge_graph_tests ... ok
test test_joins_tests ... ok
test test_recursion_syntax_valid ... ok
test test_relations_tests ... ok
test test_negation_tests ... ok
test test_recursion_tests ... ok
test test_all_iql_examples_present ... ok
test test_example_statistics ... ok
test test_all_test_files_have_output_snapshots ... ok
test test_examples_not_empty ... ok
test test_output_files_not_empty ... ok

test result: ok. 13 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

exit=0
Evidence: make pre-pr routing for the map files

scripts/affected-map.toml: exit=0 checks=['clippy', 'fmt', 'perf-gate.sh', 'python3', 'test', 'test-affected.sh'] scripts/gen-affected-map.py: exit=0 checks=['clippy', 'fmt', 'perf-gate.sh', 'python3', 'test', 'test-affected.sh'] scripts/change.sh: exit=0 checks=['fmt', 'perf-gate.sh', 'python3']

make pre-pr (real Makefile, stubbed tools) - which checks start for a change to one file
scripts/affected-map.toml: exit=0 checks=['clippy', 'fmt', 'perf-gate.sh', 'python3', 'test', 'test-affected.sh']
scripts/gen-affected-map.py: exit=0 checks=['clippy', 'fmt', 'perf-gate.sh', 'python3', 'test', 'test-affected.sh']
scripts/change.sh: exit=0 checks=['fmt', 'perf-gate.sh', 'python3']
Evidence: Landing-page 02_incremental.iql run live against a debug server
$ cat examples/iql/90_product_fixtures/50_landing_page/02_incremental.iql
// Landing page example: a new fact reaches derived results
// Authority chain: the next query after a new edge includes the new paths
// (rules are recomputed from the current facts on every query).

.kg create landing_incremental
.kg use landing_incremental

// Facts: management chain
+manages[("alice", "bob"), ("bob", "charlie")]

// Rules: transitive authority
+authority(X, Y) <- manages(X, Y)
+authority(X, Z) <- authority(X, Y), manages(Y, Z)

// Query: who does alice have authority over?
?authority("alice", Who)

// Add one new edge
+manages("charlie", "diana")

// Query again: diana appears (the rules are recomputed from the current facts)
?authority("alice", Who)

// Cleanup
.kg use default
.kg drop landing_incremental

$ inputlayer-client --server http://127.0.0.1:18745 --script examples/iql/90_product_fixtures/50_landing_page/02_incremental.iql   (target/debug binaries, disposable server)
Connecting to server at http://127.0.0.1:18745...
Connected!

Server status: healthy
Authenticated as: admin
Current knowledge graph: default

> .kg create landing_incremental
Knowledge graph 'landing_incremental' created.
Switched to knowledge graph: landing_incremental
> .kg use landing_incremental
Switched to knowledge graph: landing_incremental
> +manages[("alice", "bob"), ("bob", "charlie")]
Inserted 2 fact(s) into 'manages'.
> +authority(X, Y) <- manages(X, Y)
Rule 'authority' registered.
> +authority(X, Z) <- authority(X, Y), manages(Y, Z)
Rule 'authority' registered.
> ?authority("alice", Who)
┌─────────┬───────────┐
│ alice   │ Who       │
├─────────┼───────────┤
│ "alice" │ "bob"     │
│ "alice" │ "charlie" │
└─────────┴───────────┘
2 rows
> +manages("charlie", "diana")
Inserted 1 fact(s) into 'manages'.
> ?authority("alice", Who)
┌─────────┬───────────┐
│ alice   │ Who       │
├─────────┼───────────┤
│ "alice" │ "bob"     │
│ "alice" │ "charlie" │
│ "alice" │ "diana"   │
└─────────┴───────────┘
3 rows
> .kg use default
Switched to knowledge graph: default
> .kg drop landing_incremental
Knowledge graph 'landing_incremental' dropped.
exit=0
- Outcome: ⚠️ 1 info across 1 run (13m8s)

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 3 issues (1 warning, 2 infos)
  • ⚠️ examples/iql/09_recursion/86_descendant.iql:1 - Intent conformance: the acceptance criterion says "1,153 specs", but the change leaves 1,149 recorded transcripts (1,148 runnable by the snapshot runner). The base commit already has exactly 1,153 .iql.out files (1,160 .iql), so the other required criterion, "Remove the 4 exact duplicates", necessarily takes it to 1,149; the commit message states "1,153 -> 1,149" itself. The two criteria cannot both hold at this base (the issue's number looks like it was written against a 1,157-spec corpus). Needs a human decision: accept 1,149 as the intended end state, or identify which removals should not happen.
  • ⚠️ scripts/gen-affected-map.py:35 - Intent conformance: the intent asks for "a script that lists which src/ modules a category exercises using --debug plan output". The generator does not run anything with --debug or derive modules from plans: STATEMENT_TYPES (lines 35-42) is a hand-written table of three regexes over spec/transcript text, each mapped to a hard-coded module list, so the statement-type to module mapping is still hand-maintained (moved from the bash case into Python). Recorded .debug transcripts are only incidentally included in the text the regexes scan. The --debug option the change adds (lines 15, 77-78, 97, 100) belongs to the generator and only prints each category's matched statement types; the runner's --debug flag already existed at the base (from ci: PR gate runs scenarios, affected specs and the oracle from one debug build; add main.yml on push to main #370) and is untouched. The category sets it produces are sound for the three types (I checked the regexes against the builtin function table and the .why/.why_not/.why full parser, and src/provenance is only reached from the handler's why/why_not paths), so this is a question of whether the regex-over-text approach satisfies the criterion, not a wrong-result defect.
  • ⚠️ scripts/run_snapshot_tests.sh:527 - Simplification: the stale-map refusal (gen-affected-map.py --check before every --affected run, lines 527-530) is a component no intent requirement asks for. It is a new hard gate: CI runs run_snapshot_tests.sh --debug --skip-build --affected HEAD^1 (.github/workflows/ci.yml:253), so any PR that adds or changes a spec so that a category gains or loses a .why/vector/temporal statement now fails CI until the map is regenerated and committed, and every --affected run now needs python3. The intent only requires that the map be generated and that test-affected.sh read it. Remedy if not wanted: remove the check (and the --check mode at gen-affected-map.py:101-107); otherwise confirm the gate is intended.

🔧 Fix applied.
3 warnings still open:

  • ⚠️ examples/iql/09_recursion/86_descendant.iql:1 - Intent conformance: the acceptance criterion says "1,153 specs", but the change leaves 1,149 recorded transcripts (1,148 runnable by the snapshot runner). The base commit already has exactly 1,153 .iql.out files (1,160 .iql), so the other required criterion, "Remove the 4 exact duplicates", necessarily takes it to 1,149; the commit message states "1,153 -> 1,149" itself. The two criteria cannot both hold at this base (the issue's number looks like it was written against a 1,157-spec corpus). Needs a human decision: accept 1,149 as the intended end state, or identify which removals should not happen.
  • ⚠️ scripts/gen-affected-map.py:35 - Intent conformance: the intent asks for "a script that lists which src/ modules a category exercises using --debug plan output". The generator does not run anything with --debug or derive modules from plans: STATEMENT_TYPES (lines 35-42) is a hand-written table of three regexes over spec/transcript text, each mapped to a hard-coded module list, so the statement-type to module mapping is still hand-maintained (moved from the bash case into Python). Recorded .debug transcripts are only incidentally included in the text the regexes scan. The --debug option the change adds (lines 15, 77-78, 97, 100) belongs to the generator and only prints each category's matched statement types; the runner's --debug flag already existed at the base (from ci: PR gate runs scenarios, affected specs and the oracle from one debug build; add main.yml on push to main #370) and is untouched. The category sets it produces are sound for the three types (I checked the regexes against the builtin function table and the .why/.why_not/.why full parser, and src/provenance is only reached from the handler's why/why_not paths), so this is a question of whether the regex-over-text approach satisfies the criterion, not a wrong-result defect.
  • ⚠️ scripts/gen-affected-map.py:34 - Recorded human decision not met (round 1, map-not-from-debug-plan: "Have the generator derive each category's src/ modules from the recorded --debug plan output of its specs rather than from regexes over spec text ... If the --debug plan output cannot support this at all, stop and report what it does expose instead of approximating"). The round 1 fix commit (20a42fe, titled "plan-derived affected map blocked, unresolved") only removed the stale-map gate; STATEMENT_TYPES (lines 34-41) is unchanged: three hand-written regexes over spec/transcript text, each mapped to a hard-coded module list, and scripts/affected-map.toml is still produced from them. The fixer stopped as the decision instructed, so this is the report of what the plan output exposes, verified in the source: (1) recorded plan output exists for only 7 specs, all in 33_meta (16_debug, 17_debug_join, 18_debug_recursive, 19_debug_with_filter, 20_debug_negation, 21_debug_aggregation, 26_debug_complex); the other 35 categories record none. (2) That output is a PIPELINE TRACE of parsed rules and IR operators (Scan, Join, Map, Union, Filter, Antijoin, Aggregate); it names no src/ module. (3) .why/.why_not are meta commands that never produce a plan, so src/provenance cannot be derived from plan output at all; the acceptance "a change in src/provenance selects the categories that exercise it" currently holds only through the why regex. (4) The runner's --debug flag (run_snapshot_tests.sh:488) selects target/debug binaries and emits no plan output. Meeting the decision as written would need new machinery (the generator running every spec's queries through .debug against a live server, plus a separate non-plan source for provenance), which extends the change; that remedy, not the defect, needs authorization. Decision needed: accept the regex-over-text generator as the end state, or authorize the larger plan-running generator. Also inside this component and not required by the intent: the generator's own --debug option (lines 14, 76-77, 95, 98), which only prints each category's matched types.

🔧 Fix applied.
3 issues (2 warnings, 1 info) still open:

  • ⚠️ examples/iql/09_recursion/86_descendant.iql:1 - Intent conformance: the acceptance criterion says "1,153 specs", but the change leaves 1,149 recorded transcripts (1,148 runnable by the snapshot runner). The base commit already has exactly 1,153 .iql.out files (1,160 .iql), so the other required criterion, "Remove the 4 exact duplicates", necessarily takes it to 1,149; the commit message states "1,153 -> 1,149" itself. The two criteria cannot both hold at this base (the issue's number looks like it was written against a 1,157-spec corpus). Needs a human decision: accept 1,149 as the intended end state, or identify which removals should not happen.
  • ⚠️ scripts/run_snapshot_tests.sh:469 - The change declares scripts/affected-map.toml and scripts/gen-affected-map.py as inputs that affect every spec in two places (run_snapshot_tests.sh:469 sets AFFECTED_ALL=1 for them; Makefile:644 adds them to PRE_PR_RUST_INPUTS), but the third owner of the same input list, the rust paths filter in .github/workflows/ci.yml:45-64, still lists only scripts/run_snapshot_tests.sh and scripts/test-timings.sh. Concrete sequence: a PR edits only the generator's STATEMENT_TYPES table and commits the regenerated map (for example dropping a category from src/provenance); no path in the rust filter matches, so the whole Rust job, including run_snapshot_tests.sh --debug --skip-build --affected HEAD^1 at ci.yml:253, is skipped and the changed selection is never exercised in CI, although the runner itself would have run the full corpus. Fix: add scripts/affected-map.toml and scripts/gen-affected-map.py to the rust filter in ci.yml next to scripts/run_snapshot_tests.sh.
  • ℹ️ examples/iql/33_meta/34_load_basic.iql:1 - The folded spec's header still reads "Basic operations in meta_complete category", a category this change removes. It is a comment only (comments are not in the recorded transcript, so the snapshot is unaffected); the files were moved unchanged on purpose, so this is noted rather than prescribed.

🔧 Fix applied.
3 issues (1 warning, 2 infos) still open:

  • ⚠️ examples/iql/09_recursion/86_descendant.iql:1 - Intent conformance: the acceptance criterion says "1,153 specs", but the change leaves 1,149 recorded transcripts (1,148 runnable by the snapshot runner). The base commit already has exactly 1,153 .iql.out files (1,160 .iql), so the other required criterion, "Remove the 4 exact duplicates", necessarily takes it to 1,149; the commit message states "1,153 -> 1,149" itself. The two criteria cannot both hold at this base (the issue's number looks like it was written against a 1,157-spec corpus). Needs a human decision: accept 1,149 as the intended end state, or identify which removals should not happen.
  • ℹ️ examples/iql/33_meta/34_load_basic.iql:1 - The folded spec's header still reads "Basic operations in meta_complete category", a category this change removes. It is a comment only (comments are not in the recorded transcript, so the snapshot is unaffected); the files were moved unchanged on purpose, so this is noted rather than prescribed.
  • ℹ️ examples/iql/09_recursion/86_descendant.iql:1 - For awareness only: the four removed specs are not byte-identical to the specs kept, and no byte-identical spec pair exists at the base. Each repeats a lower-numbered spec statement for statement and records the same results, differing only in comments, the knowledge graph name and, for two, the derived relation name: 09_recursion/86_descendant = 76_tree_traversal; 12_errors/55_empty_batch_insert = 41_empty_insert_batch; 11_types/34_bool_ops = 27_bool_logic (relations true_flags/false_flags vs enabled/disabled); 22_set_operations/36_exclusive_or = 08_negation/72_exclusive_or (xor_ab vs xor_result, kept twin is in another category). I diffed all four pairs at the base, sources and transcripts; the lowest-numbered file is kept in each. This is the only reading of "the 4 exact duplicates" the corpus supports, and the recorded decision already accepts the 1,149 end state.
⚠️ **Test** - 1 info
  • ℹ️ examples/iql/90_product_fixtures/42_consistency_pack/01_consistency_core_fixture.iql:1 - In the first full-corpus run on debug binaries (32 parallel jobs, host load average above 40 on 32 cores from other work on the machine), this spec hit the runner's 60 s client timeout just before its final .kg drop line and was reported as FAIL. The identical re-run passed (1,148 passed, 0 failed), and the spec alone finishes in about 8 s. The engine sources and the spec are unchanged by this change (the file was only moved), so I read this as a load-dependent flake rather than a regression; the move does place it at the end of the parallel schedule instead of the middle. Evidence: 05-full-corpus-debug.txt and 06-full-corpus-debug-rerun.txt.
  • Live validation: ✅ go - 15 of 16 scenarios driven live against the product
Scenario Result Live Evidence
Edit a file in src/provenance and run test-affected.sh --debug: it picks 36_explain_trace and 90_product_fixtures and their specs pass on debug binaries ✅ pass live 01-provenance-affected-debug.txt: selection lists both categories, 18 passed, 0 failed, 7 skipped (landing-page scripts without transcripts), exit 0; only target/debug existed
Runner works against debug binaries across the whole corpus (run_snapshot_tests.sh --debug --skip-build) ✅ pass live 06-full-corpus-debug-rerun.txt: 1,148 passed, 0 failed, 7 skipped, exit 0. First attempt (05-full-corpus-debug.txt) had one 60 s client timeout under host load; see finding
Without --debug and with only debug binaries present, the runner refuses rather than silently using them ✅ pass live 03-affected-selection-cases.txt: 'Binaries not found after build!' naming target/release, exit 1
Adversarial: a file that only shares the name prefix (src/provenance_probe.rs) does not get the narrow provenance mapping and widens to every category ✅ pass live 03-affected-selection-cases.txt: 'Changes since HEAD affect every category'
Adversarial: a provenance edit combined with a non-leaf engine file (src/lib.rs) widens to every category ✅ pass live 03-affected-selection-cases.txt: 'Changes since HEAD affect every category'
A provenance edit plus an edited spec selects the union of the mapped categories and the spec's own category ✅ pass live 03-affected-selection-cases.txt: 29_temporal, 36_explain_trace, 90_product_fixtures
Vector and temporal leaf modules still select their categories from the generated map; temporal ones run and pass on debug binaries ✅ pass live 03-affected-selection-cases.txt: src/vector_ops.rs selects 7 categories; src/temporal_ops.rs selects 18_advanced_patterns and 29_temporal, 125 passed
A docs-only change selects no specs and exits 0; editing the map itself runs every category ✅ pass live 03-affected-selection-cases.txt: 'No runnable specs affected', exit 0; map edit prints 'affect every category'
Regenerating the map with gen-affected-map.py reproduces the committed scripts/affected-map.toml, and the generator's removed --debug option is rejected ✅ pass live python3 scripts/gen-affected-map.py left no git diff; --debug printed 'Unknown option: --debug', exit 1
The seven specs folded from 39_meta_complete run and pass as 33_meta/33..39 ✅ pass live 04-folded-moved-specs-sequential.txt: PASS [33_meta] 33_status through 39_help_comprehensive
Moved product fixtures still run under examples/iql/90_product_fixtures: consistency pack and prompt integrity pass; landing-page scripts are picked up and skipped for lack of a transcript, as at the… ✅ pass live 04-folded-moved-specs-sequential.txt: PASS [42_consistency_pack], PASS [43_prompt_integrity], 7 SKIP [50_landing_page] 'no .out file'
The four duplicates are gone, their lower-numbered twins remain and pass, and the corpus holds 1,149 recorded specs (1,153 at base minus 4) ✅ pass live 02-corpus-shape.txt (counts 1153 -> 1149, four files absent, four twins present); 04-folded-moved-specs-sequential.txt (twins PASS)
The corrected landing-page 02_incremental.iql behaves as its comments now say: after adding one edge, the next query includes diana ✅ pass live 09-landing-incremental-live.txt: second ?authority("alice", Who) returns 3 rows including diana; comments make no materialization or incremental-maintenance claim
The differential oracle corpus test finds the consistency pack at its new path and still agrees with spec and reference ✅ pass live 07-oracle-and-example-verification.txt: iql_corpus_agrees_with_spec_and_reference ok; example_verification 13 passed
make pre-pr treats a change to affected-map.toml or gen-affected-map.py as a Rust input and runs lint, unit tests and test-affected.sh ✅ pass live 08-pre-pr-routing.txt: both map files start clippy, test and test-affected.sh; the control script starts only fmt, python3 and perf-gate.sh (real Makefile, stubbed tools)
A PR that changes only affected-map.toml or gen-affected-map.py triggers the Rust CI job through the paths filter in ci.yml ⏸️ untested no The filter is evaluated by the dorny/paths-filter action inside GitHub Actions; there is no local runner for it in this workspace, and pushing or triggering CI belongs to the outer executor's later ph…
  • INPUTLAYER_TEST_PORT=18744 scripts/test-affected.sh HEAD --debug with a probe edit in src/provenance/why_not.rs (builds debug binaries, runs the selected categories)
  • scripts/test-affected.sh HEAD --debug --skip-build with a docs-only edit (selects nothing, exit 0)
  • scripts/test-affected.sh HEAD --skip-build with probe edits: provenance plus a 29_temporal spec, new file src/provenance_probe.rs, provenance plus src/lib.rs, scripts/affected-map.toml, src/vector_ops.rs (selection printed; stops at missing release binaries)
  • scripts/test-affected.sh HEAD --debug --skip-build with a probe edit in src/temporal_ops.rs (125 specs pass)
  • scripts/run_snapshot_tests.sh --debug --skip-build -j 1 -f '/\(33_meta/3[3-9]_\|90_product_fixtures\|76_tree_traversal\|41_empty_insert_batch\|27_bool_logic\|08_negation/72_exclusive_or\)'
  • scripts/run_snapshot_tests.sh --debug --skip-build twice (first run: one timeout; second run: 1,148 passed, 7 skipped)
  • scripts/run_snapshot_tests.sh --debug --skip-build -j 1 -f 42_consistency_pack (passes in about 8 s)
  • python3 scripts/gen-affected-map.py then git diff --stat -- scripts/affected-map.toml (no difference); python3 scripts/gen-affected-map.py --debug (rejected, exit 1)
  • cargo test --test differential_oracle iql_corpus_agrees_with_spec_and_reference
  • cargo test --test example_verification
  • make pre-pr in a disposable repo with stubbed tools via scripts/test_pre_pr.py's run_gate, for changes to scripts/affected-map.toml, scripts/gen-affected-map.py and a control script
  • target/debug/inputlayer-client --script examples/iql/90_product_fixtures/50_landing_page/02_incremental.iql against a disposable debug server on port 18745
  • Corpus shape: counts of .iql.out at base and HEAD, presence of removed duplicates and kept twins, old category directories gone
⚠️ **Document** - 1 info
  • ℹ️ TESTING.md:476 - The category table in TESTING.md has no rows for three categories that exist in examples/iql: 34_type_conversion, 36_explain_trace and 41_timing_breakdown. They were already missing at the base commit, so I left them alone; the change's own rows (33_meta, 90_product_fixtures, removal of 39_meta_complete) are correct. Possible follow-up: add the three rows, or generate the table from the directory listing.
⚠️ **Lint** - 1 info
  • ℹ️ tests/differential_oracle/corpus.rs:36 - make lint (cargo clippy over the whole workspace) was not run in this pass. The only Rust change is one string literal in this file. Checks that did run and pass: cargo fmt --all -- --check, bash -n scripts/run_snapshot_tests.sh, python3 -m py_compile scripts/gen-affected-map.py, and scripts/affected-map.toml matches the generator's output. ruff and shellcheck are not on PATH and the repository configures neither.
✅ **Push** - passed

✅ No issues found.

jsam added 5 commits October 5, 2026 23:48
…344)

Spec corpus (examples/iql), snapshot transcripts 1,153 -> 1,149 (1,152 ->
1,148 specs run by the snapshot runner; the remaining transcript belongs to
the 40_load_command helper):

- Remove four specs that repeat a lower-numbered spec statement for
  statement, differing only in the knowledge graph name and, for two of
  them, the name of the derived relation:
    09_recursion/86_descendant       = 09_recursion/76_tree_traversal
    12_errors/55_empty_batch_insert  = 12_errors/41_empty_insert_batch
    11_types/34_bool_ops             = 11_types/27_bool_logic
    22_set_operations/36_exclusive_or = 08_negation/72_exclusive_or
- Fold 39_meta_complete into 33_meta: its seven specs move unchanged to
  33_meta/33_status .. 39_help_comprehensive.
- Move 42_consistency_pack, 43_prompt_integrity and 50_landing_page under
  90_product_fixtures; they run as before (the seven landing page demo
  scripts still have no snapshots and are skipped by the runner).
- 50_landing_page/02_incremental.iql no longer claims the second query
  avoids a full recompute.

Affected-only runs: run_snapshot_tests.sh --affected reads the leaf-module
map from scripts/affected-map.toml instead of a hand-written case. The map
is generated by scripts/gen-affected-map.py from each category's statement
types (specs, the files they .load, and recorded transcripts including
.debug plan output); --affected refuses a stale map. A change under
src/provenance now runs the categories that use .why/.why_not instead of
the whole corpus.
@jsam
jsam merged commit 0056538 into main Oct 6, 2026
24 checks passed
@jsam
jsam deleted the fm/il-344-spec-hygiene branch October 6, 2026 00:27
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.

1 participant