You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
LIM-1457: Secondary usefulness experiment (independent replication of #70) - #96
This adds a secondary usefulness experiment for #70 under experiments/usefulness-secondary/. The primary experiment, merged in #94, stays at experiments/usefulness/. This one is an independent replication with its own fixture, workload, drivers, and summarizer, kept as corroborating evidence as the maintainer requested. It shares no files with the primary experiment, which this PR leaves unchanged.
The branch is rebuilt on current main (Rust 1.99.0, neuromod 0.7.0), and all commits are by blocksorg[bot]:
0f15197 Pre-registration. It lands before any measurement. It adds:
PROTOCOL.md with checks C1–C12, T1–T3, and P1–P2, the decision rules, and an explicitly pre-registered I1 run-inventory gate;
a versioned synthetic, untrained 16 LIF × 8 channel fixture and its manifest;
the workload and the live, simulation, and transport configs. The transport config uses loopback ports 15565–15567, which differ from the primary's.
0233773 Tooling. This is the measured commit.
examples/usefulness_secondary_inprocess.rs runs daemon vs direct-engine baseline, with live, simulation, and mask variants.
examples/usefulness_secondary_transport.rs runs the real binary over ZeroMQ with /readyz and SIGTERM.
The examples are [[example]] entries gated on corpus-ipc, and add no new dependencies.
It also adds run.sh and summarize.py. The summarizer is stdlib only and fails both unevaluated checks and an incomplete run inventory.
08a2587 Results. Raw results under results/0233773…/ (gzipped, 1.9 MB), summary.md/.json, REPORT.md, and README and CHANGELOG entries placed next to the primary's.
No file in src/, CI, the qualification scripts, or experiments/usefulness/ changed.
Result
The decision rule fixed in advance gives supports further development (as runtime plumbing), the same decision as the primary experiment. All 18 gates pass.
Daemon output equals the direct engine exactly. 10,000 ticks × 5 separate processes match the baseline, and every repetition is identical.
Expected-behavior checks hold. The mask equivalence and sensitivity, drive, selectivity, quiet-period, and accounting checks pass. The simulation control emits 0 spikes.
In-process speed. Daemon p99 is 3.6–5.3 µs per tick against the 1 ms budget, at 265k–338k ticks/s. Derived overhead versus the baseline is about 1.4–2.0 µs at p50.
Real binary over ZeroMQ at 1 kHz.
600/600 probes got the correct response, with no stray output.
Probe latency was p99 1.69 ms.
SIGTERM gave exit 0 in under 5 ms.
Daemon RSS was about 6.6 MB.
neuromod 0.7.0 behaves like 0.6.0 here. All 25 rasters are identical, file by file, to the earlier 0.6.0 / Rust 1.98.1 runs.
Findings (details in REPORT.md):
tempo suppresses input drive. Nominal tempo (1.0) maps to norepinephrine 1.0 and cuts input drive to 10 %; driven firing fell from 10/10 to 2/10 ticks.
Trained-model usefulness is still unproven. The fixture is synthetic, and the published sidecar carries output_weights, which live startup rejects.
No end-to-end tracing or acknowledgment. Outputs carry no input correlation, and PUB sends are not acknowledged.
bandit, pydocstyle (D212 and D213), and pycodestyle report nothing.
experiments/usefulness-secondary/run.sh ran on a clean tree at 0233773, with every step exiting 0 and identity.json status complete.
To check reproducibility, I decompressed the committed raw files and re-ran summarize.py. It reproduced summary.json; the only difference is measured_commit.
Note: Cargo.toml on main does not exclude experiments/ from the published package, so both experiments' result files currently ship in the crate. I left this alone so it doesn't collide with the in-flight package-exclusions branch (LIM-1514).
🟡 Dirty-tree guard conflicts with untracked results directory — run.sh:18
🟡 Missing runs make pre-registered checks vacuously pass — summarize.py:236
🧹 Nitpicks (3) — 🟢 3 low
🟢 Quadratic probe-to-receive matching in transport summary (summarize.py:308) — For every probe send, summarize_transport rescans the entire filtered receive list to build the matching window (window = [j for j, r in enumerate(nonempty) if p["send_ns"] <= r["recv_ns"] < end]) at experiments/usefulness/summarize.py:309.
🟢 Workload load never validates valid_mask width (mod.rs:71) — Workload::load checks only each segment's values width against channels (examples/usefulness_common/mod.rs:72), never valid_mask lengths.
🟢 SECONDS-based step timing has 1-second granularity (run.sh:120) — step() computes duration as $((SECONDS - start)) (experiments/usefulness/run.sh:120, recorded at line 124), and SECONDS is integer seconds.
This PR introduces an experiment package under experiments/usefulness/ plus two Rust example drivers (examples/usefulness_inprocess.rs, examples/usefulness_transport.rs) and shared driver code in examples/usefulness_common/mod.rs, wired into Cargo.toml. The runner captures provenance in identity.json, executes baseline and daemon runs in live/simulation modes with zeroed/unmasked variants, and the summarizer evaluates the pre-registered C1-C12/P1-P2 checks against raw JSONL artifacts, emitting summary.md and per-run summaries.
Files
Change
Cargo.toml, examples/*
Add example drivers implementing workload decode, ingress feeding, trace capture, and summary arithmetic for in-process and ZeroMQ transport modes.
experiments/usefulness/run.sh
Add the orchestrated runner with dirty-tree guard, per-step timing, and identity/provenance capture.
experiments/usefulness/summarize.py
Add the stdlib-only summarizer implementing percentile stats, variant re-expansion, raster comparisons, and transport latency matching.
experiments/usefulness/fixtures/*
Add SNN model, workload, and manifest fixtures with SHA-256 hashes and channel-neuron mapping.
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer TIP This summary will be updated as you push new changes.
We reviewed changes in 35090db...08a2587 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
The reason will be displayed to describe this comment to others. Learn more.
The argument(s) to `DaemonConfig::load` contain the unit-type
This issue is raised when a unit value is passed to a function call without
using the unit literal: (). If this is intentional, extract the argument into
a separate expression and replace it with (). Otherwise, consider revisiting
this function call.
The reason will be displayed to describe this comment to others. Learn more.
`fn main` has a cyclomatic complexity of 45 with "very-high" risk
A function with high cyclomatic complexity can be hard to understand and
maintain. Cyclomatic complexity is a software metric that measures the number of
independent paths through a function. A higher cyclomatic complexity indicates
that the function has more decision points and is more complex.
The reason will be displayed to describe this comment to others. Learn more.
The argument(s) to `DaemonConfig::load` contain the unit-type
This issue is raised when a unit value is passed to a function call without
using the unit literal: (). If this is intentional, extract the argument into
a separate expression and replace it with (). Otherwise, consider revisiting
this function call.
The reason will be displayed to describe this comment to others. Learn more.
Expand to see previous review
Adds a pre-registered usefulness experiment harness: Rust in-process/transport example drivers, a Python summarizer, a shell runner, fixtures, and protocol/report docs.
Key findings
🟡 Dirty-tree guard conflicts with untracked results directory — run.sh:18
🟡 Missing runs make pre-registered checks vacuously pass — summarize.py:236
Reviewed the latest changes; 2 prior findings remain open.
💬 Inline comments (1)
🟡 Bound the http_get body read and total request time — mod.rs:254
The delta reworks the usefulness-experiment harness and drivers rather than the daemon core. Validation is now fail-closed: Segment::check_width enforces both values and valid_mask widths, and the summarizer gains an inventory gate (I1) plus "not evaluated" failures so missing run families can no longer pass vacuously. Timing moves from integer SECONDS to millisecond date +%s%N deltas, and transport probe matching uses bisect bucketing instead of pairwise scans. The dirty-tree guard now excludes experiments/usefulness/results, and both drivers validate frames before encoding.
The reason will be displayed to describe this comment to others. Learn more.
The argument(s) to `DaemonConfig::load` contain the unit-type
This issue is raised when a unit value is passed to a function call without
using the unit literal: (). If this is intentional, extract the argument into
a separate expression and replace it with (). Otherwise, consider revisiting
this function call.
The reason will be displayed to describe this comment to others. Learn more.
Expand to see previous review
This delta reworks the usefulness experiment harness with fail-closed validation, ms step timing, an inventory gate, and bisect-based transport matching, leaving one medium finding on the unbounded http_get body read.
Key findings
🟡 Bound the http_get body read and total request time — mod.rs:254
The reason will be displayed to describe this comment to others. Learn more.
🟡 bug · medium
Bound the http_get body read and total request time
http_get reads the response with unbounded read_to_string and only a per-read timeout (examples/usefulness_common/mod.rs:254); the 500ms set_read_timeout applies per syscall, not to the whole transfer. A control surface that trickles bytes or an unbounded /metrics producer keeps the read alive indefinitely while the String grows without limit. This stalls wait_ready polling, evidence capture, and SIGTERM shutdown of the daemon, and can balloon driver memory.
📋 Prompt for AI Agents
In examples/usefulness_common/mod.rs around lines 242-256, replace the unbounded stream.read_to_string(&mut response) with a size-limited read (e.g. stream.take(MAX_BODY_BYTES).read_to_end into a Vec<u8>, then validate UTF-8) and add an overall request deadline so a trickling or oversized control-surface response cannot stall the driver or grow memory without bound.
Review complete. No new findings in these changes.
Reviewed the latest changes; 3 prior findings remain open.
This incremental delta applies minor cleanup to the usefulness experiment tooling and refreshes the reported results, with no new defects verified across any lens or domain scan.
Files
Change
examples/usefulness_transport.rs
Extracts a config_path() helper for the static config path; behavior unchanged.
experiments/usefulness/summarize.py
Lint-style refactor (naming/imports/constants); no behavioral drift found.
experiments/usefulness/REPORT.md
Regenerated summary document reflecting the refactor and current results.
Independent replication of the #70 experiment, kept alongside the
primary experiments/usefulness/ (#94): protocol with the I1 run-inventory
gate, synthetic versioned LIF fixture and manifest, deterministic
workload, and mode configs under experiments/usefulness-secondary/,
committed before any measurement. Refs #70.
In-process daemon/baseline and ZeroMQ transport drivers as
corpus-ipc-gated examples (usefulness_secondary_*) using public APIs
only, a locked runner with provenance capture, and a stdlib summarizer
that fails unevaluated checks and an incomplete run inventory.
Refs #70.
Raw results for measured commit 0233773 (parent of this commit) on
Rust 1.99.0 / neuromod 0.7.0, summary, and REPORT.md. Link the
secondary experiment from README and CHANGELOG. Refs #70.
Reviewed the latest changes; 5 prior findings resolved.
🧹 Nitpicks (3) — 🟢 3 low
🟢 Receiver thread leaks on early error return (usefulness_secondary_transport.rs:439) — measure spawns the receiver thread at examples/usefulness_secondary_transport.rs:442 and only sets shared.stop and joins it at lines 453-454.
🟢 Committed identity.json leaks absolute local path (identity.json:8) — The committed identity.json records active_toolchain with an absolute workstation path (experiments/usefulness-secondary/results/023377370f393121f23aa24355a038737ce5a163/identity.json:8), and run.sh's write_identity emits the same machine-specific value on future runs.
🟢 Transport driver skips channels consistency check (usefulness_secondary_transport.rs:391) — start_session in examples/usefulness_secondary_transport.rs:391 validates only tick_rate_hz against the workload, while the sibling in-process driver examples/usefulness_secondary_inprocess.rs:412 also bails when cfg.channels != workload.channels.
The delta ports the usefulness experiment harness to a secondary variant under experiments/usefulness-secondary/ with new example drivers in examples/, plus Cargo.toml, README.md, and CHANGELOG.md updates. The new summarizer emits pre-registered checks fail-closed, the runner excludes results from the dirty-tree guard and times steps in nanoseconds, probe matching uses bisect, and the workload loader validates mask widths — resolving the prior review's findings. Remaining issues are minor: an error-path thread leak and a missing channels check in the transport driver, an unbounded http_get read carried over from the prior version, and an absolute local path committed inside the recorded identity.json.
blocksorgBot
changed the title
LIM-1457: Reproducible usefulness experiment with recorded results
LIM-1457: Secondary usefulness experiment (independent replication of #70)
Oct 25, 2026
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
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This adds a secondary usefulness experiment for #70 under
experiments/usefulness-secondary/. The primary experiment, merged in #94, stays atexperiments/usefulness/. This one is an independent replication with its own fixture, workload, drivers, and summarizer, kept as corroborating evidence as the maintainer requested. It shares no files with the primary experiment, which this PR leaves unchanged.The branch is rebuilt on current
main(Rust 1.99.0,neuromod0.7.0), and all commits are byblocksorg[bot]:0f15197Pre-registration. It lands before any measurement. It adds:PROTOCOL.mdwith checks C1–C12, T1–T3, and P1–P2, the decision rules, and an explicitly pre-registered I1 run-inventory gate;0233773Tooling. This is the measured commit.examples/usefulness_secondary_inprocess.rsruns daemon vs direct-engine baseline, with live, simulation, and mask variants.examples/usefulness_secondary_transport.rsruns the real binary over ZeroMQ with/readyzand SIGTERM.examples/usefulness_secondary_common/holds shared helpers.[[example]]entries gated oncorpus-ipc, and add no new dependencies.run.shandsummarize.py. The summarizer is stdlib only and fails both unevaluated checks and an incomplete run inventory.08a2587Results. Raw results underresults/0233773…/(gzipped, 1.9 MB),summary.md/.json,REPORT.md, and README and CHANGELOG entries placed next to the primary's.No file in
src/, CI, the qualification scripts, orexperiments/usefulness/changed.Result
The decision rule fixed in advance gives supports further development (as runtime plumbing), the same decision as the primary experiment. All 18 gates pass.
neuromod0.7.0 behaves like 0.6.0 here. All 25 rasters are identical, file by file, to the earlier 0.6.0 / Rust 1.98.1 runs.Findings (details in
REPORT.md):temposuppresses input drive. Nominaltempo(1.0) maps to norepinephrine 1.0 and cuts input drive to 10 %; driven firing fell from 10/10 to 2/10 ticks.output_weights, which live startup rejects.Verification
Run on Rust 1.99.0, Debian 13 VM, 8 vCPU:
cargo fmt --checkpassed.cargo clippy --locked --all-targets -- -D warningspassed.cargo test --lockedpassed: 160 tests.cargo clippy --locked --all-targets --all-features -- -D warningspassed. This covers the primary and secondary examples.cargo test --locked --all-featurespassed: 193 tests.experiments/usefulness-secondary/run.shran on a clean tree at0233773, with every step exiting 0 andidentity.jsonstatuscomplete.summarize.py. It reproducedsummary.json; the only difference ismeasured_commit.Note:
Cargo.tomlonmaindoes not excludeexperiments/from the published package, so both experiments' result files currently ship in the crate. I left this alone so it doesn't collide with the in-flight package-exclusions branch (LIM-1514).Refs #70 (resolved by #94)
Refs LIM-1457
🤖 Generated with Claude Code
Need help on this PR? Tag
@codesmith-botwith what you need.