Command-Line Interface (sc-neurocore)

May 18, 2026 · View on GitHub

Module: sc_neurocore.cli Entry point: sc-neurocore (declared in pyproject.toml [project.scripts]) Source: src/sc_neurocore/cli.pyargparse-based, single-main() dispatch Status (v3.14.0): deployment, serving, hub bundle generation, compilation, formal verification, and synthesis-evidence collection have focused tests.


1. Installation & Entry Point

The CLI ships with the PyPI package sc-neurocore. Installing the package registers the console script sc-neurocore:

pip install sc-neurocore
sc-neurocore --version
# sc-neurocore 3.14.0

Without installation (development checkout):

PYTHONPATH=src python3 -m sc_neurocore.cli --version

The entry point is wired through pyproject.toml:

[project.scripts]
sc-neurocore = "sc_neurocore.cli:main"

main() returns an integer exit code; the module entrypoint calls sys.exit(main()).


2. Command Reference

The CLI accepts a single positional command token chosen from:

{info, benchmark, preflight, deploy, serve, map-nir, hub-init, compile, compile-nir, scnir, formal, studio, collect-synthesis}

with an optional positional model argument (file path or ODE string, depending on the command). All other parameters are keyword flags; running sc-neurocore -h prints the full argparse help.

CommandPurposeRequired positionalReturns
infoPrint version, Python, Rust engine status, optional deps0
benchmarkRun pytest benchmarks/benchmark_suite.py --benchmark-onlypytest exit code
preflightRun tools/preflight.pypreflight exit code
compileEquation string → SystemVerilog RTL (+ optional TB + Yosys)ODE string0 on success, 1 on missing model
deployNIR/PyTorch model → SC-NeuroCore HDL project for FPGA, or static web scaffold with --target webmodel file path0 on success, 1 on bad format
serveStart streaming spike inference server (SpikeServer).nir file path0 while running
map-nirGenerate deterministic silicon-mapping reports for neuromorphic targets.nir file path0 on success, 1 on bad input
hub-initGenerate an offline-first self-hosted Docker Compose hub bundle0 on success, 1 on invalid config
compile-nirCompile NIR/ONNX network files to FPGA artefacts.nir or .onnx path0 on success, 1 on bad input
scnirValidate, upgrade, or export SC-aware NIR metadata documentsvalidate model.scnir.json, upgrade model.scnir.json --output upgraded.scnir.json, or export model.nir --output model.scnir.json0 on success, 1 on invalid input
formalGenerate network formal-verification artefacts and reportsverify-network0 on valid artefacts/proofs, 1 on invalid input, replay violation, or failed proof
studioLaunch Visual SNN Design Studio (FastAPI + Uvicorn)0 on clean exit, 1 if FastAPI missing
collect-synthesisConvert real utilisation, timing, and power reports into optimiser evidence JSON0 on success, 1 on missing or invalid input

2.1 info

Prints package version, Python interpreter version, Rust engine status (version + SIMD tier from sc_neurocore_engine.simd_tier(), or not available if the engine wheel isn't installed), and versions of the optional numpy and jax imports if importable.

Verified output on this workstation (no Rust engine wheel installed):

sc-neurocore 3.14.0
Python 3.12.3 (main, Mar  3 2026, 12:15:18) [GCC 13.3.0]
Rust engine: not available
NumPy: 2.2.6

The Rust-engine status line additionally reports a version mismatch when the engine wheel reports a different __version__ than the Python package (handled by _format_engine_status).

2.2 compile-nir

Compiles .nir or .onnx network files through NIR import, NeuronGraph lowering, fixed-point quantisation, SC-NIR metadata export, and FPGA RTL generation:

sc-neurocore compile-nir model.nir \
  --module-name my_snn \
  --T 1024 \
  --source-kind sobol \
  --base-seed 66 \
  -o build/

The output directory contains the top module, per-neuron modules, combined weight ROM, one SC-NIR stochastic source module per stream, and scnir_document.json plus scnir_source_manifest.json. scnir_document.json is the full validated SC-NIR metadata document for the compiled network; scnir_source_manifest.json maps those streams to emitted source modules and records compile evidence including interconnect, q_format, total_neurons, total_synapses, scnir_stream_count, and scnir_signal_kinds. Nested-graph exports also include scnir_hierarchy_instance_count and scnir_hierarchy_port_count, derived from the generated SC-NIR document. Mixed analogue/spiking exports also include scnir_signal_routes, which records whether each present stream role is routed through direct fixed-point MAC, weighted AER event routing, or stochastic source-module generation. Downstream tools do not need to infer those summaries from RTL comments. --source-kind currently accepts lfsr and sobol; both map to source modules with the threshold[15:0]/bit_out interface. --base-seed controls deterministic stream seed allocation.

2.3 scnir

Validates SC-NIR JSON metadata with the fail-closed validator in sc_neurocore.ir.scnir_schema:

sc-neurocore scnir validate model.scnir.json

The validator rejects unknown fields, duplicate stream identifiers, invalid bitstream lengths, unsupported encodings, invalid fixed-point precision, under-specified random sources, and correlation constraints that reference missing streams. The reference schema is schemas/scnir/scnir.schema.json.

Canonicalises a supported SC-NIR document through the versioned migration surface:

sc-neurocore scnir upgrade model.scnir.json --output upgraded.scnir.json

The current schema is sc-neurocore.scnir.v0.6. The upgrade command migrates sc-neurocore.scnir.v0.1 documents by adding explicit delay_steps=0 to legacy streams, migrates every pre-v0.3 document by adding explicit signal_kind metadata, and migrates every pre-v0.4 document by adding explicit stream transforms=[] metadata before running the typed validator and deterministic writer. Version v0.5 also accepts exact source-width delay vectors for heterogeneous NIR Delay streams while preserving scalar delay metadata. Version v0.6 adds explicit hierarchy instance and port metadata, and legacy documents upgrade with hierarchy=[]. Unsupported schema versions are rejected instead of being guessed.

Exports deterministic SC-NIR metadata from a NIR graph by using the existing NIR import path and NeuronGraph lowering:

sc-neurocore scnir export model.nir --output model.scnir.json --T 1024

The export records spiking population streams, analogue-state population streams, weight streams, fixed-point precision, explicit recurrent delay steps, unique source seeds, and max-correlation constraints. It fails closed on invalid bitstream length or fixed-point precision configuration.

Validates the executable SC-NIR primitive compatibility matrix and every declared audit-evidence file path:

sc-neurocore scnir compatibility .
sc-neurocore scnir compatibility . --output scnir_compatibility.json
sc-neurocore scnir closure-audit . --output scnir_closure_audit.json

When the optional repository root argument is omitted, the command uses the current working directory. Supplying --output writes the deterministic machine-readable compatibility matrix only after validation succeeds. closure-audit writes a versioned audit bundle containing the validated matrix, support-level counts, the unique evidence files that were checked, file sizes, per-evidence SHA-256 digests, and a canonical matrix SHA-256 digest. Missing parser rows, stale rows, unsupported HDL-support claims without stream metadata, empty audit evidence, or evidence paths that do not resolve to files fail closed.

2.4 benchmark

Delegates to the project's pytest-benchmark suite via subprocess.run:

subprocess.run(
    [sys.executable, "-m", "pytest",
     "benchmarks/benchmark_suite.py", "--benchmark-only"]
).returncode

The CLI itself is not benchmarked (see Section 7). The exit code is the pytest exit code; CI consumers should treat any non-zero value as failure.

2.5 preflight

Delegates to tools/preflight.py. Used by the pre-push policy (see feedback_preflight_no_block memory: never let the pre-push hook run the full suite — preflight.py is the gated subset).

2.5 hub-init

Writes a deterministic local hub bundle containing:

  • docker-compose.yml
  • .env.example
  • hub_manifest.json
  • model_zoo_index.json
  • benchmark_plan.json
  • README.md
  • local cache/, models/, and benchmarks/results/ directories

Default generation is offline-first and loopback-bound:

sc-neurocore hub-init --output build/hub --port 8001
docker compose -f build/hub/docker-compose.yml up studio

Operational flags:

FlagDefaultMeaning
--bind-host127.0.0.1Host address used for Studio port publishing
--port8001Studio service port
--hub-imagesc-neurocore-hub:localCompose image tag
--onlineunsetClears generated offline environment flags

The generated Compose services use the checked-in deploy/Dockerfile, run with a read-only root filesystem, mount only cache/model/result directories, set no-new-privileges, and include a Studio readiness check against /api/health. The benchmark runner is opt-in via the benchmark Compose profile; it is not started with the Studio service.

2.6 compile

Compiles a free-form ODE description into synthesisable SystemVerilog using sc_neurocore.compiler.equation_compiler.equation_to_fpga. Optionally emits a testbench and runs Yosys (open-source FPGA targets only).

Verified four-step output on dv/dt = -(v-E_L)/tau_m + I/C:

PYTHONPATH=src python3 -m sc_neurocore.cli compile \
    "dv/dt = -(v-E_L)/tau_m + I/C" \
    --threshold "v > -50" \
    --reset "v = -65" \
    --params "E_L=-65,tau_m=10,C=1" \
    --init "v=-65" \
    --dt 1.0 \
    --output build/lif \
    --module-name lif_demo
# [1/4] Parsing ODE: dv/dt = -(v-E_L)/tau_m + I/C
#   State variables: ['v']
#   Parameters: ['E_L', 'tau_m', 'C']
# [2/4] Verilog written: build/lif/lif_demo.v
# [3/4] Testbench skipped (use --testbench to generate)
# [4/4] Synthesis skipped (use --synthesize to run Yosys)

The generated lif_demo.v is 45 lines (Q8.8 fixed-point, 16-bit signed parameters) and synthesises with Yosys for ICE40/ECP5 targets when --synthesize is passed.

Default changed to --dt 1.0. See Section 9 for the history. The compiler now rejects values that quantise to 0 in Q8.8 with an actionable ValueError.

2.7 deploy

FPGA targets run a five-step pipeline (six with auto-synthesis):

  1. Load model.nir via nir_lib.read + from_nir, or .pt/.pth via torch.load(weights_only=True) with automatic per-Linear ReLU stitching for ANN-to-SNN conversion.
  2. QuantiseQ88 configuration (8 integer + 8 fraction bits = 16-bit signed).
  3. Generate Verilog — calls equation_to_fpga for the canonical LIF dv/dt = (-v + I)/tau with tau=20.0.
  4. Copy HDL library — recursively copies hdl/ into the output directory, excluding testbench (tb_*) and formal/ files.
  5. Generate project filesMakefile for Yosys targets (ice40, ecp5) or project.tcl for Vivado targets (artix7, zynq).
  6. Auto-synthesise (optional) — runs Yosys + nextpnr + bitstream packing when the open-source toolchain is on $PATH.

_TARGET_CONFIGS:

TargetFamilyDevicePackageTool
ice40ice40hx8kct256Yosys
ecp5ecp585kCABGA381Yosys
artix7xc7axc7a100tcsg324Vivado
zynqxc7zxc7z020clg400Vivado

--target web generates a static browser bundle instead of an FPGA project:

sc-neurocore deploy model.nir --target web --output build/web --dt 1.0 --T 256

Generated files:

  • manifest.json — deterministic model/runtime contract.
  • index.html — browser entry point.
  • runtime/sc_neurocore_web.js — manifest loader and WebGPU capability check.
  • runtime/sc_neurocore_webgpu.wgsl — minimal SC probability shader scaffold.
  • model/<name> — copied source model artefact.

The web target accepts .nir, .pt, .pth, and .json inputs. It does not invoke PyTorch, NIR import, Node.js, or a native WASM build during generation, so packaging can be tested in CI without browser drivers or hardware accelerators.

2.8 serve

Loads a .nir graph and starts sc_neurocore.serve.SpikeServer in blocking mode on the configured port. Other formats are rejected with exit code 1.

sc-neurocore serve model.nir --port 8001 --dt 1.0

2.9 collect-synthesis

Collects FPGA synthesis reports into the strict optimiser observation format. The command requires explicit compiler-design metadata and measured model accuracy so it cannot invent missing benchmark evidence.

Required flags:

  • --design — JSON compiler-design metadata for the synthesised model.
  • --utilisation / --utilization — Vivado utilisation or Quartus fitter report.
  • --power — Vivado or Quartus power report.
  • --accuracy-score — measured model accuracy or parity score for this design.

Optional flags:

  • --timing — timing report when latency appears outside the utilisation report.
  • --latency-cycles — explicit latency when vendor reports do not carry it.
  • --clock-mhz and --inferences-per-run — both required together for workload-normalised energy calculation.
  • --out — output JSON path; without it, JSON is written to stdout.

Example:

sc-neurocore collect-synthesis \
    --design build/network_design.json \
    --utilisation build/vivado_utilisation.rpt \
    --power build/vivado_power.rpt \
    --timing build/vivado_timing.rpt \
    --accuracy-score 0.991 \
    --clock-mhz 100 \
    --inferences-per-run 1 \
    --out build/synthesis_observations.json

The output is accepted by sc_neurocore.optimizer.load_observations() and by tools/optimise_sc_design.py --evidence.

2.10 formal verify-network

Generates a deterministic dense LIF fixture, rate-bound SystemVerilog assertions, optional refractory assertions, a combined formal bundle, a SymbiYosys job, and a validated JSON report:

sc-neurocore formal verify-network \
    --module-name dense_lif_frontier_fixture \
    --input-width 3 \
    --output-width 2 \
    --output-index 0 \
    --window-cycles 8 \
    --max-spikes 4 \
    --refractory-cycles 2 \
    --population-inactivity 3 \
    --output build/formal

By default the report is written to build/formal/formal_rate_bound_report.json. --run-symbiyosys executes the generated .sby job when SymbiYosys is available; otherwise the report records tool_unavailable and keeps the generated artefacts for external execution. See Network Formal Verification for the full report contract and Python validation API.

2.11 studio

Launches the Visual SNN Design Studio (FastAPI + Uvicorn) and opens http://127.0.0.1:{port} in the default browser. Requires the studio extra:

pip install "sc-neurocore[studio]"
sc-neurocore studio --port 8001

If FastAPI/Uvicorn is missing, the command exits with code 1 and prints the install hint.


3. Architecture

                ┌──────────────────────┐
                │ sc-neurocore (entry) │
                └──────────┬───────────┘
                           │ argparse
        ┌──────────────┬──────────────┬──────────────┐
        │              │              │              │
        ▼              ▼              ▼              ▼
   _cmd_info      _cmd_compile   _cmd_deploy  _cmd_collect_synthesis
        │              │              │              │
        ▼              ▼              ▼              ▼
 sc_neurocore   equation_      nir_bridge     optimiser
 _engine        compiler       conversion     synthesis_evidence
 (Rust status)  (Python)       (Python)       (Python)
                       │              │              │
                       ▼              ▼              ▼
                   Verilog        hdl/ +       evidence JSON
                   RTL            Makefile/.tcl

main() is a single linear dispatcher — no subparsers, no command classes, no plugin registry. Each _cmd_* helper performs its own imports lazily so that the CLI cold-start cost is bounded by the dispatcher itself, not by every command's transitive dependency tree.

Private helpers

SymbolPurpose
_cmd_infoPrint runtime/engine status
_cmd_compileODE → Verilog dispatcher
_cmd_serveStreaming server launcher
_cmd_benchmarkpytest-benchmark delegate
_cmd_preflightpreflight.py delegate
_cmd_deployNIR/PyTorch → FPGA project
_cmd_collect_synthesisReport files → optimiser evidence JSON
_cmd_formalNetwork formal artefact and report generator
_cmd_studioFastAPI Studio launcher
_auto_synthesizeYosys + nextpnr + packing
_generate_projectMakefile or project.tcl emitter
_format_engine_statusRust engine status line
_safe_simd_tierDefensive SIMD-tier accessor
_print_optional_dependency_versionnumpy/jax version line
_TARGET_CONFIGSFPGA target metadata table

4. Rust Engine Integration

The CLI does not call the Rust engine for compute — cli.py is dispatch only. It does query the engine for status reporting in _cmd_info:

import sc_neurocore_engine as engine
version = getattr(engine, "__version__", "unknown")
simd_tier = engine.simd_tier()  # "scalar" | "sse2" | "avx2" | "avx512" | …

If the engine wheel is missing, _format_engine_status returns "Rust engine: not available" rather than raising. This makes the CLI usable in pure-Python environments (no maturin build, no Rust toolchain).

If the engine wheel reports a __version__ different from the Python package's __version__, the status line includes the mismatch — useful when debugging mixed-installation issues (e.g. pip install -e . against an older wheel still on the sys.path).

There is no Rust path for the CLI itself; the dispatcher is intentionally pure Python so it imports in <300 ms with no Rust toolchain present.


5. FPGA Targets

Two backend tools are supported:

  • Yosys + nextpnr (open-source) — ice40, ecp5. Auto-synthesised by --synthesize and wrapped in a Makefile for repeat builds. Bitstream packing uses icepack (ice40) or ecppack (ecp5).
  • Vivado (proprietary) — artix7, zynq. Not auto-run; project.tcl is emitted for the user to invoke vivado -mode batch -source project.tcl.

The _auto_synthesize helper is a best-effort path: it returns False silently if yosys is not on $PATH, and it never raises on synthesis/PnR failure — instead it prints the last 5 lines of stderr. This is a deliberate degradation strategy: the user gets the Verilog output even when the toolchain is broken or absent.


6. Examples

All examples below were executed on this workstation (Linux, Python 3.12.3, sc-neurocore 3.14.0) before being committed. Each block is reproducible.

6.1 Print version & engine status

sc-neurocore info
# sc-neurocore 3.14.0
# Python 3.12.3 (main, Mar  3 2026, 12:15:18) [GCC 13.3.0]
# Rust engine: not available
# NumPy: 2.2.6

6.2 Compile a custom LIF to Verilog

sc-neurocore compile \
    "dv/dt = -(v-E_L)/tau_m + I/C" \
    --threshold "v > -50" \
    --reset "v = -65" \
    --params "E_L=-65,tau_m=10,C=1" \
    --init "v=-65" \
    --dt 1.0 \
    --output build/lif \
    --module-name lif_demo \
    --testbench

Generates:

  • build/lif/lif_demo.v — 45-line synthesisable RTL
  • build/lif/tb_lif_demo.v — Icarus-runnable testbench

Then simulate:

iverilog -o sim build/lif/lif_demo.v build/lif/tb_lif_demo.v && vvp sim

6.3 Compile with synthesis (open-source toolchain)

sc-neurocore compile "dv/dt = -(v-E_L)/tau_m + I/C" \
    --threshold "v > -50" --reset "v = -65" \
    --params "E_L=-65,tau_m=10,C=1" --init "v=-65" --dt 1.0 \
    --target ice40 --synthesize

If yosys is on $PATH the synthesis runs in-process and prints cell/wire counts plus the path to the JSON netlist. If nextpnr-ice40 is also present, place-and-route runs and emits *.asc; if icepack is present, the bitstream *.bin is produced and its size logged.

6.4 Deploy a NIR graph to ICE40

sc-neurocore deploy model.nir --target ice40 --output build/deploy
cd build/deploy && make synth

6.5 Collect synthesis evidence

After an external FPGA tool has produced utilisation, timing, and power reports:

sc-neurocore collect-synthesis \
    --design build/network_design.json \
    --utilisation build/vivado_utilisation.rpt \
    --power build/vivado_power.rpt \
    --timing build/vivado_timing.rpt \
    --accuracy-score 0.991 \
    --clock-mhz 100 \
    --inferences-per-run 1 \
    --out build/synthesis_observations.json

This produces evidence JSON with one observations record plus optional workload-normalised energy fields. It does not run Vivado, Quartus, Yosys, or nextpnr.

6.6 Launch the Studio

pip install "sc-neurocore[studio]"
sc-neurocore studio --port 8001
# SC-NeuroCore Studio starting at http://127.0.0.1:8001

7. Performance

7.1 Cold-start (this workstation, 2026-04-17)

Measured with time.perf_counter() around from sc_neurocore.cli import main, five fresh interpreter starts, hot disk cache:

RunImport timeMax RSS
1195.2 ms28.4 MB
2298.6 ms28.3 MB
3182.7 ms28.3 MB
4215.7 ms28.5 MB
5202.9 ms28.5 MB
Median~203 ms28.4 MB

Hardware: Intel i5-11600K, 32 GB DDR4, root ext4 SSD. Python 3.12.3 (system). Run from /media/anulum/.../SC-NEUROCORE/ with PYTHONPATH=src.

The cold-start cost is dominated by argparse and the lazy bootstrap of sc_neurocore's top-level __init__.py. None of the _cmd_* helpers' imports are paid until that command is dispatched.

7.2 Per-command latency

_cmd_info walks the optional numpy / jax / sc_neurocore_engine imports — each adds 50–200 ms when present. _cmd_compile invokes the equation compiler (single ODE: <30 ms). _cmd_deploy performs disk I/O for the entire hdl/ tree copy (~200 KB) plus optional Yosys (multi-second).

7.3 Rust path

N/A — the CLI is intentionally pure-Python dispatch. The Rust engine is only queried for status. Compute hot paths are reached only via downstream modules (compiler, serve, nir_bridge, etc.), which carry their own Rust paths.

7.4 Benchmarks

sc-neurocore benchmark delegates to the project's pytest-benchmark suite under benchmarks/benchmark_suite.py. The CLI itself has no dedicated benchmark suite — cold-start is documented above instead.


8. Pipeline Wiring

SurfaceHow it's wiredVerifier
Console scriptpyproject.toml [project.scripts] sc-neurocore = "sc_neurocore.cli:main"pip install registers it
Package mainpython -m sc_neurocore.cli works via the module entrypointManual invocation
Compile path_cmd_compilesc_neurocore.compiler.equation_compiler.equation_to_fpga + generate_testbenchtests/test_equation_compiler.py
Deploy NIR path_cmd_deploysc_neurocore.nir_bridge.from_nirtests/test_nir_bridge*.py
Deploy PyTorch path_cmd_deploysc_neurocore.conversion.converttests/test_conversion*.py
Serve path_cmd_servesc_neurocore.serve.SpikeServertests/test_serve_server.py
Synthesis evidence path_cmd_collect_synthesissc_neurocore.optimizer.build_payload_from_reportstests/test_optimizer/test_synthesis_evidence_cli.py
Studio path_cmd_studiosc_neurocore.studio.app.create_apptests/test_cli.py::test_studio_*
Status path_cmd_infosc_neurocore_engine.simd_tiertests/test_cli.py::test_info_*

Every dispatched command terminates in either a registered subprocess or a public symbol of another sc-neurocore subpackage. There are no orphan helpers.


9. Known Issues

9.1 --dt 0.001 (was: silent dead Verilog, now: fail-fast)

Discovered: 2026-04-17 while writing this doc. Severity: HIGH — silent correctness bug in v3.14.0. Status: fixed by task #7. The compiler now raises ValueError on Q8.8 dt underflow and the CLI default has been changed from --dt 0.001 to --dt 1.0.

Original behaviour (v3.14.0): --dt 0.001 (1 ms) was encoded into Q8.8 fixed-point as 0.001 * 256 = 0.256, which truncated to 0. The generated Verilog multiplied the dv update by zero on every cycle, so the membrane voltage never changed. The bug was silent — no warning, no error.

Current behaviour:

  • The CLI default is --dt 1.0 (one timestep per Q8.8 LSB → 16'sd256 in the generated multiplier).
  • Any dt that quantises to 0 in the chosen fixed-point format raises ValueError from compile_to_verilog with an actionable message:
    ValueError: dt=0.001 underflows in Q8.8: smallest representable
    non-zero value is 0.00390625 (neuron.dt * 2**8 = 0.256 → 0).
    Use dt >= 0.00390625 (e.g. dt=1.0 for 1-step intervals), or pass
    a wider fraction (e.g. Q4.12 via fraction=12) to the compiler.
    
  • dt=0.0 is still accepted (degenerate but legal — produces a non-advancing model, useful for certain test patterns).
  • The fraction argument to compile_to_verilog lets callers widen the fixed-point format (e.g. fraction=12 for Q4.12 accepts dt=0.001).

Reproduce the new behaviour:

# default dt=1.0 succeeds
sc-neurocore compile "dv/dt=-(v-E_L)/tau_m" \
    --threshold "v>-50" --reset "v=-65" \
    --params "tau_m=10,E_L=-65" --init "v=-65"
grep _dt_mul_v build/sc_equation_neuron.v
# wire signed [31:0] _dt_mul_v = (...) * 16'sd256;   ← non-zero

# explicit dt=0.001 raises with actionable message
sc-neurocore compile "dv/dt=-v/tau" \
    --threshold "v>-50" --reset "v=-65" \
    --params "tau=10" --init "v=-65" --dt 0.001
# ValueError: dt=0.001 underflows in Q8.8: ...

Regression tests: tests/test_equation_compiler.py::TestDtUnderflowGuard (7 cases covering raise, message content, boundary at 1/256, dt=0.0 legality, wider-fraction acceptance, CLI default success, CLI explicit-dt raise).

9.2 compile / deploy / serve test coverage (closed: task #8)

tests/test_cli.py now covers all three commands:

  • deploy (TestDeployCommand, 5 tests): missing-arg exit code, unsupported extension exit code, full PyTorch happy path (writes sc_deploy_lif.sv + Makefile), Vivado target emits project.tcl, end-to-end via main(). The 3 PyTorch-using tests skip cleanly when torch is not installed (CI has torch).
  • serve (TestServeCommand, 4 tests): missing-arg exit code, non-.nir rejection, full happy path with mocked nir.read + from_nir + SpikeServer, dispatch via main().
  • compile end-to-end coverage was already in tests/test_equation_compiler.py::TestCompileCLI; the new TestDtUnderflowGuard adds 2 more CLI cases (default-dt success, explicit-dt-0.001 raise).

9.3 collect-synthesis does not run vendor tools

collect-synthesis intentionally parses reports that already exist. It does not invoke Vivado, Quartus, Yosys, nextpnr, or board programming utilities. Use deploy to scaffold the FPGA project, run the external implementation flow, then pass the generated reports into collect-synthesis.


10. Tests & Coverage

tests/test_cli.py and the focused optimiser CLI tests cover the public dispatcher surface:

test_version_flag                              PASS
test_info_command                              PASS
test_no_command_prints_help                    PASS
test_info_without_rust_engine                  PASS
test_info_reports_engine_version_mismatch      PASS
test_info_ignores_broken_optional_jax_import   PASS
test_info_ignores_broken_optional_numpy_import PASS
test_format_engine_status_without_simd_tier    PASS
test_format_engine_status_with_broken_simd_tier PASS
test_benchmark_delegates_to_subprocess         PASS
test_preflight_delegates_to_subprocess         PASS
test_studio_launches_uvicorn                   PASS
test_studio_missing_fastapi                    PASS
test_studio_command_via_main                   PASS
test_collect_synthesis_command_writes_optimizer_evidence PASS
test_collect_synthesis_command_reports_missing_required_args PASS

Run locally:

PYTHONPATH=src python3 -m pytest \
    tests/test_cli.py \
    tests/test_optimizer/test_synthesis_evidence_cli.py -q

Multi-angle dimensions covered:

  • happy paths (info, version)
  • absence of optional deps (no numpy / no jax / no Rust engine / no FastAPI)
  • broken optional deps (__version__-less modules, simd_tier raising, simd_tier not callable)
  • subprocess delegation (benchmark, preflight)
  • end-to-end command dispatch via main() (studio)
  • report evidence success and missing-required-argument errors

11. Audit Status (7-point checklist)

#DimensionStatusDetail
1Pipeline wiring✅ PASSConsole script registered; every _cmd_* reaches a downstream public symbol
2Multi-angle tests✅ PASSCLI tests cover info, benchmark, preflight, studio, deploy, serve, compile dispatch cases, and synthesis-evidence collection.
3Rust pathN/ADispatch-only; engine is queried for status only
4BenchmarksN/ACLI cold-start measured (Section 7); no pytest-benchmark suite for the dispatcher itself
5Performance docs✅ PASSSection 7 (this page) with measured numbers
6Documentation page✅ PASSThis page
7Rules followed✅ PASSSPDX header present; no undocumented type-check suppressions remain in cli.py.

Net status: 0 WARN, 0 FAIL. Tasks #7 and #8 are both closed, and collect-synthesis is wired, documented, and tested.


12. References

Internal:


13. Auto-rendered API

::: sc_neurocore.cli options: show_root_heading: true show_source: true members: - main