Skip to content

ci(release): qualify Cargo archives, rustdoc and fresh installs - #79

Open
rmems wants to merge 2 commits into
mainfrom
ci/69-cargo-release-qualification
Open

rmems wants to merge 2 commits into
mainfrom
ci/69-cargo-release-qualification

Conversation

@rmems

@rmems rmems commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

User description

Closes #69

Summary

  • Add a Linux packaged-release CI job on Rust 1.98.1. Qualify the exact PR head SHA rather than a synthetic merge commit, and never reuse checkout build artifacts.
  • Check required Cargo archive contents, extract the .crate outside the checkout, and build/test default and corpus-ipc modes independently.
  • Run warning-denying rustdoc, install release binaries into isolated roots, check help/version/readiness/SIGINT/SIGTERM and missing/corrupt live-checkpoint rejection, and run tiny external library consumers in both modes.
  • Run cargo +1.98.1 publish --dry-run --locked without bypass flags. No actual crates.io upload is performed.
  • Always retain the archive, SHA/checksum, exact commands/exit codes, logs, consumer source/lockfiles, and smoke configurations as a SHA-specific CI artifact for 90 days. Document local reproduction and release-record retention.
  • Fix the redundant intra-doc link uncovered by the strict rustdoc gate; no runtime behavior changes.

Acceptance evidence

Local qualification passed on candidate 198e195, using Rust/Cargo 1.98.1 on Linux:

  • cargo +1.98.1 package --locked --list: 40 files; required manifests/lockfile, licenses, README, toolchain, sources and test fixtures present.
  • cargo +1.98.1 package --locked: package verification passed; embedded Git SHA matches the clean candidate checkout.
  • Extracted archive default build/tests: 151 passed, 0 failed, 1 intentionally ignored signal test.
  • Extracted archive Linux corpus-ipc build/tests: 173 unit tests and 9 integration tests passed, 0 failed, 1 intentionally ignored signal test.
  • Strict rustdoc: RUSTDOCFLAGS="-D warnings" cargo +1.98.1 doc --locked --no-deps, separately default and --features corpus-ipc.
  • Both fresh release installs: --help, --version = brainstem-daemon 0.3.0, simulation /readyz = 200, SIGINT and SIGTERM graceful exit = 0. Missing/corrupt live checkpoints each exit 1 with checkpoint-specific errors.
  • Both external consumers compile/run with their own resolved lockfiles: simulation restores successfully; missing live checkpoint rejected through the public library API.
  • cargo +1.98.1 publish --dry-run --locked: exit 0, warning: aborting upload due to dry run.
  • Repository quality gate: cargo fmt --check, locked build/test and strict all-target Clippy, default and all-features; all passed. Workflow actionlint and Python syntax checks passed.
  • Negative harness probes rejected premature successful startup exit, ignored shutdown, an unexpected successful invalid-live exit, and hanging commands. These temporary probes were removed.

Retained local archive SHA-256: 4a20191be0f6620f743f4e24b3b1fe15c420d4c012b049927d7725284d267eea.

The PR CI run will independently retain its evidence under package-qualification-198e195b33aff2042069240372c471df308bb2b4. See the run's downloadable artifact and identity.json; only status: passed means qualification completed. Preserve that artifact in the release record before its 90-day expiry, and rerun qualification for any changed candidate SHA. Local results do not assert that pending hosted CI checks are green.

Scope and delivery

Part of 02 — v0.3.0 release qualification. All #69 acceptance checks have passed locally; hosted CI confirmation follows this PR. This does not qualify other release gates, publish a crate, merge this PR, or create/update any Linear issue.


Devin Review


Summary by cubic

Adds a Linux release-qualification CI job that tests the actual Cargo package archive built from the PR head SHA instead of the checkout or a synthetic merge commit.

The job verifies required archive contents, extracts and builds/tests the .crate in both default and corpus-ipc modes, runs warning-denying rustdoc, installs release binaries into isolated roots with CLI and signal smoke tests, runs external library consumers, and performs cargo publish --dry-run --locked (no upload). Evidence is retained as a SHA-specific artifact for 90 days, including identity.json with the SHA, archive checksum, and qualification status. Local docs document reproduction.

Also fixes a redundant intra-doc link that the strict rustdoc gate flagged; no runtime behavior changes.

Written for commit 198e195. Summary will update on new commits.

View guided diff Turn on auto-fix


CodeAnt-AI Description

Verify Cargo release archives and fresh installs in CI

What Changed

  • CI checks the exact candidate’s Cargo archive outside the checkout, including required files, builds, and tests with default and corpus-ipc features.
  • Freshly installed binaries are checked for readiness, graceful shutdown, and clear rejection of missing or corrupt live checkpoints; external library consumers and warning-free documentation are also verified.
  • CI runs a publish dry run without uploading, and retains the archive and qualification evidence for 90 days, including when checks fail.
  • Documents how to reproduce the release checks locally.

Impact

✅ Catch missing release files before publication
✅ Catch broken fresh installs before release
✅ Keep SHA-specific evidence for failed release checks

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

ampagent and others added 2 commits October 9, 2026 00:06
Add Linux packaged-crate qualification for both backends, strict rustdoc,
isolated install and external-consumer smoke checks, and publish dry-run.
Retain SHA-specific archive and command evidence even when qualification fails.

Amp-Thread-ID: https://ampcode.com/threads/T-01a11df5-5390-73cf-a980-c28826029bd9
Co-authored-by: Raul Cardenas Montoya <montoyaraul34@gmail.com>
The packaged release gate found a redundant explicit intra-doc target.
Keep the same destination without suppressing the rustdoc warning.

Amp-Thread-ID: https://ampcode.com/threads/T-01a11df5-5390-73cf-a980-c28826029bd9
Co-authored-by: Raul Cardenas Montoya <montoyaraul34@gmail.com>
@rmems rmems added ci Continuous Integration testing labels Oct 9, 2026
@rmems rmems self-assigned this Oct 9, 2026
@codeant-ai

codeant-ai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR 198e195 Oct 09, 2026 · 00:17 00:21

@codeant-ai

codeant-ai Bot commented Oct 9, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Oct 9, 2026
@coderabbitai

coderabbitai Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 19 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 5 included reviews currently available. Your 7 included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Essentials
  • Run ID: 52747200-d835-44a2-a4e3-c2a67b13c94c
📥 Commits

Reviewing files that changed from the base of the PR and between 59e8cc3 and 198e195.

📒 Files selected for processing (4)
  • .github/scripts/qualify-package.py
  • .github/workflows/ci.yml
  • docs/ci.md
  • src/ingress/mod.rs
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@amazon-q-developer amazon-q-developer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This PR adds comprehensive Cargo archive release qualification to CI. The implementation is solid with proper error handling, timeout management, and evidence retention. The new packaged release job correctly qualifies the exact PR head SHA, runs extensive tests on extracted archives in both modes, and validates the publish path. Documentation is thorough and the doc link fix is appropriate. No blocking issues identified.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

@codescene-access codescene-access Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Gates Failed
New code is healthy (1 new file with code health below 10.00)
Enforce critical code health rules (1 file with Bumpy Road Ahead)
Enforce advisory code health rules (1 file with Complex Method, Large Method, Excess Number of Function Arguments)

Our agent can fix these. Install it.

Gates Passed
3 Quality Gates Passed

Reason for failure
New code is healthy Violations Code Health Impact
qualify-package.py 4 rules 8.48 Suppress
Enforce critical code health rules Violations Code Health Impact
qualify-package.py 1 critical rule 8.48 Suppress
Enforce advisory code health rules Violations Code Health Impact
qualify-package.py 3 advisory rules 8.48 Suppress

See analysis details in CodeScene

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

Comment on lines +51 to +127
def smoke(binary, mode, version, work, evidence, env):
help_text = run(f"{mode}-help", [binary, "--help"], work, evidence, env, timeout=10)
if "--config" not in help_text or "--version" not in help_text:
raise RuntimeError("installed CLI is missing expected options")
actual_version = run(f"{mode}-version", [binary, "--version"], work, evidence, env, timeout=10)
if actual_version.strip() != f"brainstem-daemon {version}":
raise RuntimeError("installed version does not match the package")

# Allocate distinct ports; no external publisher is needed for simulation.
ports = set()
while len(ports) < 3:
ports.add(free_port())
sub_port, pub_port, control_port = sorted(ports)
config_text = f'''lif_count = 4
izh_count = 0
channels = 4
tick_rate_hz = 100
log_level = "info"
spine_sub_port = {sub_port}
spine_pub_port = {pub_port}
control_bind = "127.0.0.1:{control_port}"
model_path = "missing-checkpoint.json"
'''
simulation = work / f"{mode}-simulation.toml"
simulation.write_text('runtime_mode = "simulation"\n' + config_text)
shutil.copy2(simulation, evidence / simulation.name)
# Observe readiness, not merely survival after an arbitrary sleep. Exercise
# both Unix shutdown signals with bounded startup and teardown.
for stop_signal in (signal.SIGINT, signal.SIGTERM):
label = f"{mode}-simulation-{stop_signal.name}"
with (evidence / f"{label}.log").open("w") as log:
process = subprocess.Popen(
[binary, "--config", simulation], cwd=work, env=env,
stdout=log, stderr=subprocess.STDOUT,
)
try:
deadline = time.monotonic() + 10
while True:
if process.poll() is not None:
raise RuntimeError(f"{label}: exited before readiness ({process.returncode})")
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{control_port}/readyz", timeout=0.5,
) as response:
if response.status == 200 and response.read() == b"ready\n":
break
except (urllib.error.URLError, TimeoutError):
pass
if time.monotonic() >= deadline:
raise RuntimeError(f"{label}: never became ready")
time.sleep(0.05)
process.send_signal(stop_signal)
if process.wait(timeout=10) != 0:
raise RuntimeError(f"{label}: did not shut down successfully")
finally:
if process.poll() is None:
process.kill()
process.wait()
log.write(f"\nQUALIFIED: /readyz=200; {stop_signal.name}; graceful exit=0\n")
print(f"[{label}] /readyz=200; graceful exit=0", flush=True)

# Live is the default. Both missing and corrupt checkpoints must fail before
# runtime startup; a successful exit or a hang must not pass this check.
for invalid in ("missing", "corrupt"):
checkpoint = work / "missing-checkpoint.json"
if invalid == "corrupt":
checkpoint.write_text("not a checkpoint")
live = work / f"{mode}-live-{invalid}.toml"
live.write_text(config_text)
shutil.copy2(live, evidence / live.name)
text = run(
f"{mode}-live-{invalid}", [binary, "--config", live], work,
evidence, env, expected=1, timeout=10,
)
if "invalid or missing Spikenaut checkpoint" not in text:
raise RuntimeError("live rejection did not report checkpoint failure")
checkpoint.unlink()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Complex Method
smoke has a cyclomatic complexity of 23, threshold = 9

Suppress

Comment on lines +130 to +232
def qualify(evidence):
repo = Path(__file__).resolve().parents[2]
env = os.environ.copy()
# Do not inherit target/Rust flags that could mask a packaged build failure.
for key in ("CARGO_TARGET_DIR", "RUSTFLAGS", "CARGO_ENCODED_RUSTFLAGS",
"RUSTDOCFLAGS", "CARGO_ENCODED_RUSTDOCFLAGS", "RUST_LOG"):
env.pop(key, None)
env.update(CC="gcc", CXX="g++", CARGO_TERM_COLOR="never")
cargo = ["cargo", "+1.98.1"]
sha = run("git-sha", ["git", "rev-parse", "HEAD"], repo, evidence, env).strip()
dirty = run("git-status", ["git", "status", "--porcelain", "--untracked-files=no"], repo, evidence, env)
if dirty.strip():
raise RuntimeError("qualification requires a clean committed checkout")
run("rustc", ["rustc", "+1.98.1", "--version", "--verbose"], repo, evidence, env)
run("cargo", cargo + ["--version"], repo, evidence, env)
manifest = tomllib.loads((repo / "Cargo.toml").read_text())
package_name = f"{manifest['package']['name']}-{manifest['package']['version']}"
identity = {"sha": sha, "package": package_name, "status": "running"}
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")

with tempfile.TemporaryDirectory(prefix="brainstem-package-") as temporary:
work = Path(temporary)
env["CARGO_TARGET_DIR"] = str(work / "target")
contents = run("package-list", cargo + ["package", "--locked", "--list"], repo, evidence, env)
tracked = run("git-sources", ["git", "ls-files", "src", "tests"], repo, evidence, env)
required = set(tracked.splitlines()) | {
"Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "README.md",
"LICENSE-MIT", "LICENSE-APACHE-2.0", "rust-toolchain.toml", ".cargo_vcs_info.json",
}
listed = set(contents.splitlines())
missing = required - listed
if missing:
raise RuntimeError(f"package is missing required files: {sorted(missing)}")
if any(path.startswith((".github/", ".devin/", "docs/", "target/")) for path in listed):
raise RuntimeError("package includes repository-only or generated files")
run("package", cargo + ["package", "--locked"], repo, evidence, env)
archive = work / "target" / "package" / f"{package_name}.crate"
shutil.copy2(archive, evidence / archive.name)
identity["archive_sha256"] = hashlib.sha256(archive.read_bytes()).hexdigest()
extracted = work / "extracted"
extracted.mkdir()
with tarfile.open(archive) as tar:
tar.extractall(extracted, filter="data")
package = extracted / package_name
vcs = json.loads((package / ".cargo_vcs_info.json").read_text())
if vcs["git"]["sha1"] != sha or vcs["git"].get("dirty", False):
raise RuntimeError("archive VCS identity does not match the clean candidate SHA")
if not all((package / path).is_file() for path in required):
raise RuntimeError("extracted archive is missing required files")
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")

for mode, features in (("stub", []), ("corpus-ipc", ["--features", "corpus-ipc"])):
# Never reuse checkout artifacts, or artifacts from the other backend.
env["CARGO_TARGET_DIR"] = str(work / f"target-{mode}")
for command in ("build", "test"):
run(f"{mode}-{command}", cargo + [command, "--locked"] + features, package, evidence, env)
docs_env = dict(env, RUSTDOCFLAGS="-D warnings")
run(f"{mode}-rustdoc", cargo + ["doc", "--locked", "--no-deps"] + features, package, evidence, docs_env)
root = work / f"install-{mode}"
run(
f"{mode}-install", cargo + ["install", "--locked", "--path", str(package), "--root", str(root)] + features,
work, evidence, env,
)
smoke(root / "bin" / "brainstem-daemon", mode, manifest["package"]["version"], work, evidence, env)
consumer = work / f"consumer-{mode}"
(consumer / "src").mkdir(parents=True)
(consumer / "Cargo.toml").write_text(f'''[package]
name = "package-consumer"
version = "0.0.0"
edition = "2024"

[dependencies]
brainstem-daemon = {{ path = {json.dumps(str(package))}, features = {json.dumps(features[1:] if features else [])} }}
''')
(consumer / "src" / "main.rs").write_text('''use brainstem_daemon::{BrainstemDaemon, DaemonConfig, RuntimeMode};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args().nth(1).expect("config argument");
let mut config = DaemonConfig::load(std::path::Path::new(&path))?;
assert_eq!(config.runtime_mode, RuntimeMode::Simulation);
let daemon = BrainstemDaemon::try_new(config.clone())?;
assert!(daemon.restore_network().is_ok());
config.runtime_mode = RuntimeMode::Live;
assert!(BrainstemDaemon::try_new(config)?.restore_network().is_err());
println!("external consumer: simulation restore OK; missing live checkpoint rejected");
Ok(())
}
''')
shutil.copytree(consumer, evidence / f"consumer-{mode}")
run(f"{mode}-consumer-lock", cargo + ["generate-lockfile"], consumer, evidence, env)
shutil.copy2(consumer / "Cargo.lock", evidence / f"consumer-{mode}" / "Cargo.lock")
run(
f"{mode}-consumer", cargo + ["run", "--locked", "--", str(work / f"{mode}-simulation.toml")],
consumer, evidence, env,
)

# Use the clean source checkout for Cargo's publish path, which itself
# packages and verifies it. Nothing is uploaded; no bypass flags are used.
env["CARGO_TARGET_DIR"] = str(work / "publish-target")
run("publish-dry-run", cargo + ["publish", "--dry-run", "--locked"], repo, evidence, env)
identity["status"] = "passed"
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")
print(f"PASS: packaged release qualification for {sha}", flush=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Complex Method
qualify has a cyclomatic complexity of 17, threshold = 9

Suppress

Comment on lines +130 to +232
def qualify(evidence):
repo = Path(__file__).resolve().parents[2]
env = os.environ.copy()
# Do not inherit target/Rust flags that could mask a packaged build failure.
for key in ("CARGO_TARGET_DIR", "RUSTFLAGS", "CARGO_ENCODED_RUSTFLAGS",
"RUSTDOCFLAGS", "CARGO_ENCODED_RUSTDOCFLAGS", "RUST_LOG"):
env.pop(key, None)
env.update(CC="gcc", CXX="g++", CARGO_TERM_COLOR="never")
cargo = ["cargo", "+1.98.1"]
sha = run("git-sha", ["git", "rev-parse", "HEAD"], repo, evidence, env).strip()
dirty = run("git-status", ["git", "status", "--porcelain", "--untracked-files=no"], repo, evidence, env)
if dirty.strip():
raise RuntimeError("qualification requires a clean committed checkout")
run("rustc", ["rustc", "+1.98.1", "--version", "--verbose"], repo, evidence, env)
run("cargo", cargo + ["--version"], repo, evidence, env)
manifest = tomllib.loads((repo / "Cargo.toml").read_text())
package_name = f"{manifest['package']['name']}-{manifest['package']['version']}"
identity = {"sha": sha, "package": package_name, "status": "running"}
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")

with tempfile.TemporaryDirectory(prefix="brainstem-package-") as temporary:
work = Path(temporary)
env["CARGO_TARGET_DIR"] = str(work / "target")
contents = run("package-list", cargo + ["package", "--locked", "--list"], repo, evidence, env)
tracked = run("git-sources", ["git", "ls-files", "src", "tests"], repo, evidence, env)
required = set(tracked.splitlines()) | {
"Cargo.toml", "Cargo.toml.orig", "Cargo.lock", "README.md",
"LICENSE-MIT", "LICENSE-APACHE-2.0", "rust-toolchain.toml", ".cargo_vcs_info.json",
}
listed = set(contents.splitlines())
missing = required - listed
if missing:
raise RuntimeError(f"package is missing required files: {sorted(missing)}")
if any(path.startswith((".github/", ".devin/", "docs/", "target/")) for path in listed):
raise RuntimeError("package includes repository-only or generated files")
run("package", cargo + ["package", "--locked"], repo, evidence, env)
archive = work / "target" / "package" / f"{package_name}.crate"
shutil.copy2(archive, evidence / archive.name)
identity["archive_sha256"] = hashlib.sha256(archive.read_bytes()).hexdigest()
extracted = work / "extracted"
extracted.mkdir()
with tarfile.open(archive) as tar:
tar.extractall(extracted, filter="data")
package = extracted / package_name
vcs = json.loads((package / ".cargo_vcs_info.json").read_text())
if vcs["git"]["sha1"] != sha or vcs["git"].get("dirty", False):
raise RuntimeError("archive VCS identity does not match the clean candidate SHA")
if not all((package / path).is_file() for path in required):
raise RuntimeError("extracted archive is missing required files")
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")

for mode, features in (("stub", []), ("corpus-ipc", ["--features", "corpus-ipc"])):
# Never reuse checkout artifacts, or artifacts from the other backend.
env["CARGO_TARGET_DIR"] = str(work / f"target-{mode}")
for command in ("build", "test"):
run(f"{mode}-{command}", cargo + [command, "--locked"] + features, package, evidence, env)
docs_env = dict(env, RUSTDOCFLAGS="-D warnings")
run(f"{mode}-rustdoc", cargo + ["doc", "--locked", "--no-deps"] + features, package, evidence, docs_env)
root = work / f"install-{mode}"
run(
f"{mode}-install", cargo + ["install", "--locked", "--path", str(package), "--root", str(root)] + features,
work, evidence, env,
)
smoke(root / "bin" / "brainstem-daemon", mode, manifest["package"]["version"], work, evidence, env)
consumer = work / f"consumer-{mode}"
(consumer / "src").mkdir(parents=True)
(consumer / "Cargo.toml").write_text(f'''[package]
name = "package-consumer"
version = "0.0.0"
edition = "2024"

[dependencies]
brainstem-daemon = {{ path = {json.dumps(str(package))}, features = {json.dumps(features[1:] if features else [])} }}
''')
(consumer / "src" / "main.rs").write_text('''use brainstem_daemon::{BrainstemDaemon, DaemonConfig, RuntimeMode};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = std::env::args().nth(1).expect("config argument");
let mut config = DaemonConfig::load(std::path::Path::new(&path))?;
assert_eq!(config.runtime_mode, RuntimeMode::Simulation);
let daemon = BrainstemDaemon::try_new(config.clone())?;
assert!(daemon.restore_network().is_ok());
config.runtime_mode = RuntimeMode::Live;
assert!(BrainstemDaemon::try_new(config)?.restore_network().is_err());
println!("external consumer: simulation restore OK; missing live checkpoint rejected");
Ok(())
}
''')
shutil.copytree(consumer, evidence / f"consumer-{mode}")
run(f"{mode}-consumer-lock", cargo + ["generate-lockfile"], consumer, evidence, env)
shutil.copy2(consumer / "Cargo.lock", evidence / f"consumer-{mode}" / "Cargo.lock")
run(
f"{mode}-consumer", cargo + ["run", "--locked", "--", str(work / f"{mode}-simulation.toml")],
consumer, evidence, env,
)

# Use the clean source checkout for Cargo's publish path, which itself
# packages and verifies it. Nothing is uploaded; no bypass flags are used.
env["CARGO_TARGET_DIR"] = str(work / "publish-target")
run("publish-dry-run", cargo + ["publish", "--dry-run", "--locked"], repo, evidence, env)
identity["status"] = "passed"
(evidence / "identity.json").write_text(json.dumps(identity, indent=2) + "\n")
print(f"PASS: packaged release qualification for {sha}", flush=True)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Large Method
qualify has 94 lines, threshold = 70

Suppress

Comment on lines +51 to +127
def smoke(binary, mode, version, work, evidence, env):
help_text = run(f"{mode}-help", [binary, "--help"], work, evidence, env, timeout=10)
if "--config" not in help_text or "--version" not in help_text:
raise RuntimeError("installed CLI is missing expected options")
actual_version = run(f"{mode}-version", [binary, "--version"], work, evidence, env, timeout=10)
if actual_version.strip() != f"brainstem-daemon {version}":
raise RuntimeError("installed version does not match the package")

# Allocate distinct ports; no external publisher is needed for simulation.
ports = set()
while len(ports) < 3:
ports.add(free_port())
sub_port, pub_port, control_port = sorted(ports)
config_text = f'''lif_count = 4
izh_count = 0
channels = 4
tick_rate_hz = 100
log_level = "info"
spine_sub_port = {sub_port}
spine_pub_port = {pub_port}
control_bind = "127.0.0.1:{control_port}"
model_path = "missing-checkpoint.json"
'''
simulation = work / f"{mode}-simulation.toml"
simulation.write_text('runtime_mode = "simulation"\n' + config_text)
shutil.copy2(simulation, evidence / simulation.name)
# Observe readiness, not merely survival after an arbitrary sleep. Exercise
# both Unix shutdown signals with bounded startup and teardown.
for stop_signal in (signal.SIGINT, signal.SIGTERM):
label = f"{mode}-simulation-{stop_signal.name}"
with (evidence / f"{label}.log").open("w") as log:
process = subprocess.Popen(
[binary, "--config", simulation], cwd=work, env=env,
stdout=log, stderr=subprocess.STDOUT,
)
try:
deadline = time.monotonic() + 10
while True:
if process.poll() is not None:
raise RuntimeError(f"{label}: exited before readiness ({process.returncode})")
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{control_port}/readyz", timeout=0.5,
) as response:
if response.status == 200 and response.read() == b"ready\n":
break
except (urllib.error.URLError, TimeoutError):
pass
if time.monotonic() >= deadline:
raise RuntimeError(f"{label}: never became ready")
time.sleep(0.05)
process.send_signal(stop_signal)
if process.wait(timeout=10) != 0:
raise RuntimeError(f"{label}: did not shut down successfully")
finally:
if process.poll() is None:
process.kill()
process.wait()
log.write(f"\nQUALIFIED: /readyz=200; {stop_signal.name}; graceful exit=0\n")
print(f"[{label}] /readyz=200; graceful exit=0", flush=True)

# Live is the default. Both missing and corrupt checkpoints must fail before
# runtime startup; a successful exit or a hang must not pass this check.
for invalid in ("missing", "corrupt"):
checkpoint = work / "missing-checkpoint.json"
if invalid == "corrupt":
checkpoint.write_text("not a checkpoint")
live = work / f"{mode}-live-{invalid}.toml"
live.write_text(config_text)
shutil.copy2(live, evidence / live.name)
text = run(
f"{mode}-live-{invalid}", [binary, "--config", live], work,
evidence, env, expected=1, timeout=10,
)
if "invalid or missing Spikenaut checkpoint" not in text:
raise RuntimeError("live rejection did not report checkpoint failure")
checkpoint.unlink()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Large Method
smoke has 70 lines, threshold = 70

Suppress

Comment on lines +51 to +127
def smoke(binary, mode, version, work, evidence, env):
help_text = run(f"{mode}-help", [binary, "--help"], work, evidence, env, timeout=10)
if "--config" not in help_text or "--version" not in help_text:
raise RuntimeError("installed CLI is missing expected options")
actual_version = run(f"{mode}-version", [binary, "--version"], work, evidence, env, timeout=10)
if actual_version.strip() != f"brainstem-daemon {version}":
raise RuntimeError("installed version does not match the package")

# Allocate distinct ports; no external publisher is needed for simulation.
ports = set()
while len(ports) < 3:
ports.add(free_port())
sub_port, pub_port, control_port = sorted(ports)
config_text = f'''lif_count = 4
izh_count = 0
channels = 4
tick_rate_hz = 100
log_level = "info"
spine_sub_port = {sub_port}
spine_pub_port = {pub_port}
control_bind = "127.0.0.1:{control_port}"
model_path = "missing-checkpoint.json"
'''
simulation = work / f"{mode}-simulation.toml"
simulation.write_text('runtime_mode = "simulation"\n' + config_text)
shutil.copy2(simulation, evidence / simulation.name)
# Observe readiness, not merely survival after an arbitrary sleep. Exercise
# both Unix shutdown signals with bounded startup and teardown.
for stop_signal in (signal.SIGINT, signal.SIGTERM):
label = f"{mode}-simulation-{stop_signal.name}"
with (evidence / f"{label}.log").open("w") as log:
process = subprocess.Popen(
[binary, "--config", simulation], cwd=work, env=env,
stdout=log, stderr=subprocess.STDOUT,
)
try:
deadline = time.monotonic() + 10
while True:
if process.poll() is not None:
raise RuntimeError(f"{label}: exited before readiness ({process.returncode})")
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{control_port}/readyz", timeout=0.5,
) as response:
if response.status == 200 and response.read() == b"ready\n":
break
except (urllib.error.URLError, TimeoutError):
pass
if time.monotonic() >= deadline:
raise RuntimeError(f"{label}: never became ready")
time.sleep(0.05)
process.send_signal(stop_signal)
if process.wait(timeout=10) != 0:
raise RuntimeError(f"{label}: did not shut down successfully")
finally:
if process.poll() is None:
process.kill()
process.wait()
log.write(f"\nQUALIFIED: /readyz=200; {stop_signal.name}; graceful exit=0\n")
print(f"[{label}] /readyz=200; graceful exit=0", flush=True)

# Live is the default. Both missing and corrupt checkpoints must fail before
# runtime startup; a successful exit or a hang must not pass this check.
for invalid in ("missing", "corrupt"):
checkpoint = work / "missing-checkpoint.json"
if invalid == "corrupt":
checkpoint.write_text("not a checkpoint")
live = work / f"{mode}-live-{invalid}.toml"
live.write_text(config_text)
shutil.copy2(live, evidence / live.name)
text = run(
f"{mode}-live-{invalid}", [binary, "--config", live], work,
evidence, env, expected=1, timeout=10,
)
if "invalid or missing Spikenaut checkpoint" not in text:
raise RuntimeError("live rejection did not report checkpoint failure")
checkpoint.unlink()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Bumpy Road Ahead
smoke has 2 blocks with nested conditional logic. Any nesting of 2 or deeper is considered. Threshold is 2 blocks per function

Suppress

Comment on lines +22 to +42
def run(label, args, cwd, evidence, env, expected=0, timeout=1800):
log = evidence / f"{label}.log"
record = {"label": label, "command": list(map(str, args)), "cwd": str(cwd),
"expected_exit": expected, "timeout_seconds": timeout,
"rustdocflags": env.get("RUSTDOCFLAGS", "")}
with (evidence / "commands.jsonl").open("a") as commands:
commands.write(json.dumps(record) + "\n")
print(f"[{label}] cwd={cwd} $ {shlex.join(map(str, args))}", flush=True)
with log.open("w") as output:
result = subprocess.run(
args, cwd=cwd, env=env, stdout=output, stderr=subprocess.STDOUT,
timeout=timeout, check=False,
)
text = log.read_text()
with (evidence / "commands.jsonl").open("a") as commands:
commands.write(json.dumps({"label": label, "exit": result.returncode}) + "\n")
print(text[-4000:], flush=True)
print(f"[{label}] exit={result.returncode}", flush=True)
if result.returncode != expected:
raise RuntimeError(f"{label}: expected exit {expected}, got {result.returncode}")
return text

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Excess Number of Function Arguments
run has 7 arguments, max arguments = 4

Suppress

Comment on lines +51 to +127
def smoke(binary, mode, version, work, evidence, env):
help_text = run(f"{mode}-help", [binary, "--help"], work, evidence, env, timeout=10)
if "--config" not in help_text or "--version" not in help_text:
raise RuntimeError("installed CLI is missing expected options")
actual_version = run(f"{mode}-version", [binary, "--version"], work, evidence, env, timeout=10)
if actual_version.strip() != f"brainstem-daemon {version}":
raise RuntimeError("installed version does not match the package")

# Allocate distinct ports; no external publisher is needed for simulation.
ports = set()
while len(ports) < 3:
ports.add(free_port())
sub_port, pub_port, control_port = sorted(ports)
config_text = f'''lif_count = 4
izh_count = 0
channels = 4
tick_rate_hz = 100
log_level = "info"
spine_sub_port = {sub_port}
spine_pub_port = {pub_port}
control_bind = "127.0.0.1:{control_port}"
model_path = "missing-checkpoint.json"
'''
simulation = work / f"{mode}-simulation.toml"
simulation.write_text('runtime_mode = "simulation"\n' + config_text)
shutil.copy2(simulation, evidence / simulation.name)
# Observe readiness, not merely survival after an arbitrary sleep. Exercise
# both Unix shutdown signals with bounded startup and teardown.
for stop_signal in (signal.SIGINT, signal.SIGTERM):
label = f"{mode}-simulation-{stop_signal.name}"
with (evidence / f"{label}.log").open("w") as log:
process = subprocess.Popen(
[binary, "--config", simulation], cwd=work, env=env,
stdout=log, stderr=subprocess.STDOUT,
)
try:
deadline = time.monotonic() + 10
while True:
if process.poll() is not None:
raise RuntimeError(f"{label}: exited before readiness ({process.returncode})")
try:
with urllib.request.urlopen(
f"http://127.0.0.1:{control_port}/readyz", timeout=0.5,
) as response:
if response.status == 200 and response.read() == b"ready\n":
break
except (urllib.error.URLError, TimeoutError):
pass
if time.monotonic() >= deadline:
raise RuntimeError(f"{label}: never became ready")
time.sleep(0.05)
process.send_signal(stop_signal)
if process.wait(timeout=10) != 0:
raise RuntimeError(f"{label}: did not shut down successfully")
finally:
if process.poll() is None:
process.kill()
process.wait()
log.write(f"\nQUALIFIED: /readyz=200; {stop_signal.name}; graceful exit=0\n")
print(f"[{label}] /readyz=200; graceful exit=0", flush=True)

# Live is the default. Both missing and corrupt checkpoints must fail before
# runtime startup; a successful exit or a hang must not pass this check.
for invalid in ("missing", "corrupt"):
checkpoint = work / "missing-checkpoint.json"
if invalid == "corrupt":
checkpoint.write_text("not a checkpoint")
live = work / f"{mode}-live-{invalid}.toml"
live.write_text(config_text)
shutil.copy2(live, evidence / live.name)
text = run(
f"{mode}-live-{invalid}", [binary, "--config", live], work,
evidence, env, expected=1, timeout=10,
)
if "invalid or missing Spikenaut checkpoint" not in text:
raise RuntimeError("live rejection did not report checkpoint failure")
checkpoint.unlink()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

❌ New issue: Excess Number of Function Arguments
smoke has 6 arguments, max arguments = 4

Suppress

@codacy-production

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 high · 5 medium · 1 minor

Alerts:
⚠ 7 issues (≤ 0 issues of at least minor severity)

Results:
7 new issues

Category Results
BestPractice 1 medium
ErrorProne 1 high
CodeStyle 1 minor
Complexity 4 medium

View in Codacy

🟢 Metrics 34 complexity · 0 duplication

Metric Results
Complexity 34
Duplication 0

View in Codacy

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.

@deepsource-io

deepsource-io Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 59e8cc3...198e195 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Rust Oct 9, 2026 12:17a.m. Review ↗
Secrets Oct 9, 2026 12:17a.m. Review ↗

Important

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.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@codeant-ai

codeant-ai Bot commented Oct 9, 2026

Copy link
Copy Markdown

CodeAnt Nitpicks

1 code suggestion

1. Popen bypasses run(), so the signal-smoke command and working directory are never recorded; the retained evidence cannot reproduce these checks exactly.

Incomplete implementation · .github/scripts/qualify-package.py:82-85

@codeant-ai

codeant-ai Bot commented Oct 9, 2026

Copy link
Copy Markdown

CodeAnt PR Risk: Low Risk

  • The PR appears safe to merge; the new Linux gate checks extracted archives, fresh installs, and external consumers in both feature modes.
  • The ingress change simplifies a documentation link without changing runtime behavior.

Assessed commit: 198e195b33af

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

2 issues found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/ci.yml">

<violation number="1" location=".github/workflows/ci.yml:156">
P3: `if-no-files-found: error` makes this always() retention step fail as a second error whenever an earlier step (checkout, apt-get, toolchain install) fails before target/package-evidence/ is created. The job already reports the original failure, so this only adds misleading noise to the run summary. Use `warn` so the retention step under `if: always()` can never fail the run by itself.</violation>
</file>

<file name=".github/scripts/qualify-package.py">

<violation number="1" location=".github/scripts/qualify-package.py:45">
P3: `free_port` returns an ephemeral port by binding and immediately closing the socket, but `smoke` only binds those ports later, inside a freshly spawned daemon process (and in `corpus-ipc` mode after ZMQ context/socket setup). Between the close and the daemon's bind, any other local process can claim the port, which makes this release gate flaky: the daemon fails to bind (or the readiness probe hits a different service) and the whole qualification run fails spuriously. Pass `0` would be the robust alternative if the daemon could report its assigned ports; with this design, lease each port by double-checking it is still free right before spawning, or retry the whole smoke run with a fresh port set on bind failure.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread .github/workflows/ci.yml
with:
name: package-qualification-${{ github.event.pull_request.head.sha || github.sha }}
path: target/package-evidence/
if-no-files-found: error

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: if-no-files-found: error makes this always() retention step fail as a second error whenever an earlier step (checkout, apt-get, toolchain install) fails before target/package-evidence/ is created. The job already reports the original failure, so this only adds misleading noise to the run summary. Use warn so the retention step under if: always() can never fail the run by itself.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .github/workflows/ci.yml, line 156:

<comment>`if-no-files-found: error` makes this always() retention step fail as a second error whenever an earlier step (checkout, apt-get, toolchain install) fails before target/package-evidence/ is created. The job already reports the original failure, so this only adds misleading noise to the run summary. Use `warn` so the retention step under `if: always()` can never fail the run by itself.</comment>

<file context>
@@ -122,3 +122,36 @@ jobs:
+        with:
+          name: package-qualification-${{ github.event.pull_request.head.sha || github.sha }}
+          path: target/package-evidence/
+          if-no-files-found: error
+          retention-days: 90
</file context>
Suggested change
if-no-files-found: error
if-no-files-found: warn

return text


def free_port():

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: free_port returns an ephemeral port by binding and immediately closing the socket, but smoke only binds those ports later, inside a freshly spawned daemon process (and in corpus-ipc mode after ZMQ context/socket setup). Between the close and the daemon's bind, any other local process can claim the port, which makes this release gate flaky: the daemon fails to bind (or the readiness probe hits a different service) and the whole qualification run fails spuriously. Pass 0 would be the robust alternative if the daemon could report its assigned ports; with this design, lease each port by double-checking it is still free right before spawning, or retry the whole smoke run with a fresh port set on bind failure.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .github/scripts/qualify-package.py, line 45:

<comment>`free_port` returns an ephemeral port by binding and immediately closing the socket, but `smoke` only binds those ports later, inside a freshly spawned daemon process (and in `corpus-ipc` mode after ZMQ context/socket setup). Between the close and the daemon's bind, any other local process can claim the port, which makes this release gate flaky: the daemon fails to bind (or the readiness probe hits a different service) and the whole qualification run fails spuriously. Pass `0` would be the robust alternative if the daemon could report its assigned ports; with this design, lease each port by double-checking it is still free right before spawning, or retry the whole smoke run with a fresh port set on bind failure.</comment>

<file context>
@@ -0,0 +1,247 @@
+    return text
+
+
+def free_port():
+    with socket.socket() as sock:
+        sock.bind(("127.0.0.1", 0))
</file context>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Continuous Integration size:L This PR changes 100-499 lines, ignoring generated files testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci(release): verify the Cargo archive, rustdoc and fresh installs for v0.3.0

2 participants