Update documentation with 9 changed files (#4522) - #4528
Conversation
…ble unit-test gate (#4522) Resolve the self-deploy hang where a red canary crash left the relaunch gates stuck with no actionable signal. The unit-test gate now threads up to 16 KiB of `cargo test` output through `GateResult.detail` (superseding the prior 200-byte cap) and parses failing test markers so operators can see *why* a canary reddened. Because `detail` can now carry large, credential-bearing content (e.g. a token-embedded remote URL) into logs, telemetry, and operator stderr, add `GateResult::redacted_display()` (SEC-D2): credential-scrubbed and bounded to 512 bytes with char-boundary-safe truncation. Route every emitting sink through it — `handoff.rs` (RpcCallFailed.reason) and `operator_cli` stderr — while raw `Display` remains the un-redacted in-process debug path. Also unify `truncate_output` on the O(1) `is_char_boundary` back-off used by `truncate_output_tail`, replacing the O(max_len) char scan. - Adds diagnostics docs for the canary unit-test gate - Adds tests: credential redaction, 512-byte bound, multibyte-boundary safety Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
rysweet
left a comment
There was a problem hiding this comment.
Step 17b — Comprehensive Code Review (#4528 / issue #4522)
Verdict: ✅ Approve. The change correctly addresses both root causes of the stuck red-canary crash-loop (test-scope mismatch + undiagnosable failure) with security properly threaded. All 53 self_relaunch lib tests pass locally (cargo test --lib self_relaunch:: → 53 passed; 0 failed), including the 3 new test modules. No blocking issues.
Checklist
- Code quality & standards — clean, idiomatic Rust; thorough doc-comments tying each change to the #4522 root cause;
tracing/OTel only, zeroprint!/println!/eprint!on the gate path. - Test coverage adequate — new
diagnosability_tests,scope_tests, andredacted_displaytests cover the scope fix, both-stream capture, char-boundary safety, DoS bounding, and credential redaction.scope_testsis a real hermetic cargo spawn (fails loudly if toolchain absent — no silent skip). - No TODOs, stubs, or swallowed exceptions — none. The
Err(e)spawn arm is now a loudtracing::error!+ fail-closed RED. - No unimplemented functions — all helpers (
parse_unit_test_failure,truncate_output_tail,bound_gate_detail,redacted_display) are complete and tested. - Logic correctness —
--libmatches the proven-green unit scope;parse_unit_test_failurecaptures stdout+stderr (fixing the prior stdout-discard), surfaces failing-test names +test result:summary + a tail for the no-summary abort shape. Bounds: 20 markers / 8 KiB tail / 16 KiB detail / 512 B emission. - Edge case handling — multi-byte UTF-8 boundary safety in both
truncate_outputandtruncate_output_tail; empty streams; abort-with-no-summary; oversized flood (DoS guard). - Security —
redacted_display()+bound_gate_detail()scrub URL-embedded credentials (SEC-D2) at every emitting sink; deny-by-default env scrub withis_hijack_class_env(SEC-D3) is preserved and never weakened;LD_PRELOAD-class never allow-listed.
Non-blocking observations
- Redaction is URL-scheme-scoped.
redact_credentialsonly redactsscheme://userinfo@hostauthorities. A token appearing outside that shape — an scp-likeuser@host:pathremote, or aGITHUB_TOKEN=ghs_…echoed in a panic/compile message — would pass through intodetail. This is acceptable here (the env scrub prevents most token exposure and D4 explicitly scoped out secret-scanning regex), but it's a residual risk worth a code comment so a future reader doesn't assumedetailis unconditionally secret-safe. - Minor DRY:
bound_gate_detail(gates.rs) andredacted_display(types.rs) both implement "redact_credentials + bound to 512 B" with near-identical char-boundary back-off. Consider extracting one shared helper to keep the bound/redaction policy in a single place. TMPDIRomitted from the BASE env floor (the D2 design listed it). cargo/rustc fall back to/tmpon Linux so this is fine today; flagging for portability if the gate ever runs where/tmpisn't the default temp dir.scope_testsspawns a real cargo build inside the unit suite. Fast in practice (ran in 0.17 s, cached) and correctly fails loudly rather than skipping — just noting it adds a toolchain dependency tocargo test --lib.- PR title nit: "Update documentation with 9 changed files (#4522)" undersells the change — the substantive work is the canary unit-test gate scope + diagnosability fix, not docs. Consider retitling to match the commit (
fix(self-deploy): …diagnosable unit-test gate (#4522)).
None of the above block merge. The fix is correct, well-bounded, fail-closed, and well-tested.
Step 17c — Security Review (#4528 / issue #4522)Verdict: ✅ No high-confidence, exploitable security vulnerabilities found. SEC-D2 (secret redaction) and SEC-D3 (deny-by-default env scrub) are correctly implemented and consistently applied at every emitting sink. Verified by tracing all gate-detail sinks, redaction/truncation ordering, the env-scrub allowlist and its call sites, and the handoff file write. Checklist
Controls verifiedSEC-D2 — redaction applied at every sink, in safe order (redact → truncate):
SEC-D3 — env scrub is genuinely deny-by-default:
Command/argument injection — none. Gates spawn via Truncation — panic-safe & leak-safe. Handoff file — no secret exposure. Residual gaps (assessed — NOT high-confidence exploitable)
Non-blocking hardening suggestions
Neither is required to merge — current wiring is fail-closed and safe. |
Step 17d — Philosophy Guardian Review (#4528 / issue #4522)Verdict: ✅ Compliant. The change adheres to the project's ruthless-simplicity and bricks-&-studs philosophy. No blocking issues. Compliance checklist
Non-blocking philosophy notes (advisory, do not block merge)
The implementation is simple, fail-closed, well-bounded, and modular. Philosophy-compliant — merge-ready. |
… TMPDIR floor S3: Extract the char-boundary back-off duplicated across truncate_output, truncate_output_tail (gates.rs) and redacted_display (types.rs) into a single super::types::next_char_boundary helper (DRY, reviewer-endorsed). S5: Restore TMPDIR to scrub_gate_env's universal env floor so the unit-test gate's cargo/rustc toolchain keeps its temp dir, matching design intent and preventing a false RED on a healthy candidate. All 53 self_relaunch lib tests pass; clippy clean. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…agnostics The reference table cited `truncate_output_tail_keeps_summary` but the actual test is `truncate_output_tail_keeps_trailing_summary`. Align the doc with the source so the cited test name resolves (#4522 quality audit). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Ready for Final ReviewWorkflow steps completed: requirements, design, implementation, tests, code review, philosophy compliance, cleanup, and quality audit. Ready for merge approval. |
📊 Coverage Summary
Coverage data from CI run. Test files matching |
Summary
Concise workflow-generated PR for documentation.
Issue
Closes #4522
Changed files
Diff stat
Behavior
Implemented behavior through these branch commits:
587f7d2 fix(self-deploy): converge stuck red-canary self-deploy with diagnosable unit-test gate (#4522)
Validation
ℹ NODE_OPTIONS=--max-old-space-size=32768 (saved preference). To change: /home/azureuser/.amplihack/config
I'll start by inspecting the current state of the work — branch, changed files, and the specific changes to gates.rs.
● Get branch, diff stat, status (shell)
│ cd /home/azureuser/src/Simard-deploy-4049/worktrees/feat/issue-4522-simard-self-deploy-is-stuck-
│ in-a-red-canary-crash && git branch --show-current && echo "---DIFF STAT---" && git --no-pager
│ diff main --stat 2>/dev/null | tail -30 && echo "---STATUS---" && git --no-pager status --short
└ 27 lines…
Working tree is clean. Let me examine the key changed file (gates.rs) and understand the changes relevant to this task.
/ Search (grep)
Risk
No high-risk subsystem pattern detected from changed paths.
Checklist
This PR was created as a draft for review before merging.
Step 16b: Outside-In Testing Results
Tested from the PR branch (
feat/issue-4522-simard-self-deploy-is-stuck-in-a-red-canary-crash) as a consumer of the self-deploy canary gate would exercise it.Detected toolchains: Rust / Cargo workspace (
Cargo.tomlat root,simardcrate, edition 2024,cargo 1.95.0). No Node/Python/Go manifests. Per the qa-team skill's repo-type detection, Rust CLI repos substitutecargo testfor the gadugi harness.Chosen strategy: Native
cargo testagainst the changed modulesrc/self_relaunch/gates.rs. The outside-in boundary is the canary gate's observable verdict (pass/fail + diagnosable detail) — tests spawn real fake candidate binaries and a real fixture crate viacargo, never asserting on private internals.cargo test --lib self_relaunch::gates --no-runFinished test profilein ~64s, executable builttest result: FAILEDsummary; deny-by-default env scrub; hijack-class vars never re-injected; allow-list re-injection)cargo test --lib self_relaunch::gates:: -- --nocaptureunit_test_failure_surfaces_failing_test_name_and_summary ... ok--libscope — realcargospawn of a fixture crate whosetests/target would redden the scrubbed all-targets scopescope_tests)unit_test_gate_passes_for_healthy_candidate_under_lib_scope ... okAggregate:
test result: ok. 28 passed; 0 failed; 0 ignored.This directly validates both #4522 defects the PR fixes: (1) a real failure is now diagnosable (failing test name + summary surfaced from stdout, which the prior
truncate_output(&stderr, 200)discarded); (2) the scope/env mismatch is eliminated — the gate is aligned to the proven-green--libunit scope so a genuinely-healthy candidate goes GREEN, while the LD_PRELOAD/hijack-class deny-by-default defense is proven intact (hijack_class_name_in_canary_env_is_never_reinjected ... ok).Fix count during outside-in testing: 0 — all scenarios passed on the first run; no code changes required.