test(sidecar): cover the wire protocol and agent mapping - #409
test(sidecar): cover the wire protocol and agent mapping#409pawellisowski wants to merge 8 commits into
Conversation
`src/sidecar.rs` had 444 lines of code behind 44 lines of test: two `discover()` cases and two `serde` shape checks. Everything between a reply landing on stdout and a `GeneratedAgent` coming out was uncovered, and the only tests that touched it — `tests/sidecar_integration.rs` — skip themselves when the NativeAOT binary is absent, which is every CI run. Cover it with a `#!/bin/sh` stand-in for the sidecar (unix-gated; the code under test is platform-independent) driven through `AWARE_SIDECAR`, plus direct tests for the two pure functions: - `render_inputs_yaml`: the `required`/`default` lines are conditional, and all three fields go through `quote_yaml_scalar` because they come from plug-in metadata. - `to_local_agent`: the sidecar's `version` is the SDK target, not the agent version; `mode`, skills and the caller's `source_kind` must reach the manifest rather than being flattened. - The wire paths: request shape, `ok:false` carrying the sidecar's own diagnostic, `ok:true` with no `data`/`agent`, unparseable stdout quoting both streams, and an unstamped reply rejected end to end. - `coverage-validate` returning violations as a value rather than an error, and not accepting `coverage-generate`'s payload. - `reflect_csharp` spawning `AWARE_ROSLYN`, not `AWARE_SIDECAR`. Drops `no_inputs_render_to_an_empty_body`: it stayed green under three real breaks of `render_inputs_yaml`, and the empty case it claimed is already asserted on a realistic fixture by the command-mapping test.
|
@codex review Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d582da2be3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Both findings were real, and both were about the tests depending on the machine they run on rather than on the code under test. `fake_sidecar` interpolated `TMPDIR`-derived paths into a `/bin/sh` script unquoted. Where `TMPDIR` holds a space or a metacharacter, `sh` word-splits them and the script reads and writes the wrong files while still exiting 0 — so every live-path test would have seen empty output and failed for a reason that named nothing. Shell-quote them, and run all of those tests beneath a deliberately hostile temp dir (space, apostrophe, `$`) so the quoting is exercised by the suite instead of asserted in a comment: removing it now turns 10 tests red. `roslyn_discovery_does_not_fall_back_to_the_sidecar_variable` assumed `AWARE_ROSLYN` was unset instead of ensuring it. A developer who exports it either fails the test (an override pointing at a missing file errors as `AWARE_ROSLYN=… but file not found`, naming neither `aware-roslyn` nor the fallback) or — worse — passes it vacuously, because the fallback under test returns early and never runs. Fixing that needs two variables held at once, which the guard could not do; its own docs invited the extension. `EnvVarGuard::scope` takes the whole list under one acquisition of the non-reentrant lock, with `None` meaning "unset for the duration" and `Drop` restoring in reverse. `set` becomes a one-element `scope`, so every existing call site is unchanged; `replace` still refuses anything but a single-variable guard rather than guessing which one was meant.
Codex round 1 — both P2 findings accepted and fixedReviewed commit P2 — quote temporary paths in the fake-sidecar scriptCorrect, and it would have failed loudly rather than subtly: Fixed with a
P2 — isolate the fallback test from ambient
|
| Mutation | Tests that went red |
|---|---|
M22 — scope treats None as a no-op instead of unsetting |
scope_unsets_a_named_variable_and_puts_it_back |
M23 — Drop does not re-remove a variable that was absent |
scope_unsets_a_named_variable_and_puts_it_back, restores_absent_variable_on_drop, replace_swaps_the_value_without_disturbing_the_saved_original, restores_after_a_panic_inside_the_scope |
Regression check
The full M1–M21 table from the PR body was re-run against the fixed tree, not assumed: every mutation still turns its named tests red, and the pre-existing a_second_guard_cannot_be_acquired_while_the_first_is_alive still passes, so the scope refactor did not quietly make the lock reentrant.
Gates re-run on fcde55c1 from cli/ on the pinned 1.95.0 toolchain: cargo fmt --all -- --check pass, cargo clippy --all-targets -- -D warnings pass, cargo test pass (879 unit + every integration suite, 0 failed, 1 ignored).
@codex review
Generated by Claude Code
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fcde55c1d7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
`sh_quote` went through `to_string_lossy`, so a `TMPDIR` holding bytes that are not valid UTF-8 would put U+FFFD in the generated script. It would name files that do not exist, still exit 0, and leave every live-path test failing against empty output for a reason pointing nowhere near the temp dir. Read the path as bytes via `OsStrExt` and build the script body as `Vec<u8>`. Covering that by putting the fixtures under a non-UTF-8 temp dir does not work, and the reason is worth recording: `discover_named` reads its override with `std::env::var`, which rejects a non-UTF-8 value as `NotUnicode` and falls through as though the variable were unset — so the fake sidecar is never found and the test fails for an unrelated reason. `hostile_tempdir` therefore stays ASCII (space, apostrophe, `$`), which is what covers the word-splitting hazard, and the lossy-conversion hazard is covered directly by `sh_quote_preserves_non_utf8_path_bytes`.
Codex round 2 — the new P2 accepted and fixedReviewed commit P2 — preserve non-UTF-8 bytes when quoting fixture pathsCorrect, and it is the same class of defect as round 1's: the round-1 fix stopped
One thing the fix could not do, and whyThe natural way to cover this would be to run the fixtures under a temp dir whose own name carries such a byte. That was implemented first and it does not work — usefully so, because the reason is a real limit in the code under test rather than in the test:
With a non-UTF-8 temp dir, 11 tests failed with So
Noted, not fixed:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b83ef4b901
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The duplicate-key assertion fired partway through the apply loop, so entries before the duplicate had already been written to the process environment. `Self` is never constructed when that panics, so there is no `Drop` to undo them and the override leaked into every later test — a misuse would surface as unrelated failures elsewhere rather than as itself. Validate the whole list first, before the environment lock is even taken, so a rejected `scope` leaves the environment exactly as it found it.
Codex round 3 — finding fixed, and this PR now stops here for a humanNot merging. Leaving this open for review. The routine that opened this PR may re-request Codex at most twice; Codex has now returned a finding in three consecutive rounds, so the rule is to stop rather than keep iterating or merge on an approval that does not cover the head commit. Head is P2 — validate duplicate keys before applying scope overridesCorrect, and it is a defect in code this PR introduced last round. Fixed in
The new test asserts the environment is untouched after the panic, not merely that it panicked — which is the half that would otherwise go unnoticed. Where this leaves the PR
Every finding across all three rounds was accepted and fixed at root; none was argued down or triaged away. The full M1–M25 mutation set was re-run against the head tree and every mutation still turns its named tests red. A reviewer picking this up needs to look at Still open, unchanged from round 2
Generated by Claude Code |
|
@codex review Requesting a review of the current head All four P2 findings raised on this PR are addressed on the branch: the ambient No new work from me on this PR — I am the sweeper pass that carries the maintenance routines' PRs to a verdict, and this one only needed a review of its head. Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 07c2b0d2f8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
`a_duplicate_key_is_rejected_before_anything_is_applied` asserted that both variables were absent after the rejected `scope`. That is a claim about the runner's environment, not about `scope`: exporting either name failed the test even though nothing had been written. Reproduced by setting both and watching it go red. Snapshot both values before the call and compare after, which is what "leaves the environment exactly as it found it" actually means. The test keeps its teeth — moving the duplicate check back inside the apply loop still turns it red, with or without ambient values set.
Codex round 4 (unsolicited) — P3 fixed. Still not merging.Codex re-reviewed P3 — preserve ambient values in the duplicate-key testCorrect, and it is the same defect this PR was opened to remove — a test asserting something about the runner's environment rather than about the code. Verified by reproduction rather than accepted on inspection — running the pre-fix test with both names exported turns it red: Now snapshots both values before The important half is that the fix did not cost the test its teeth — checked in both environments:
Still not merging
Four review rounds, five findings, every one accepted and fixed at root — none argued down, none triaged away, including the three in code this PR itself introduced. But the merge condition is unchanged and unmet: a review must name the commit being merged, and no reviewer has seen For a human picking this up: the substance is Generated by Claude Code |
|
@codex review Requesting a review of I am the PR-sweeper pass, not the routine that opened this PR — the routine has spent its re-request budget and stood down, which is exactly the state this pass exists to pick up. I reached the same fix for the P3 independently while State: five findings across four rounds, all fixed at root, all threads resolved. Gates re-run here on The only thing missing is a review that names the commit being merged. Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a8f8b77ad
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ack what it borrowed Same defect as `7a8f8b77`, one test over. `scope_unsets_a_named_variable_and_puts_it_back` asserted `AWARE_TEST_ENV_GUARD_SCOPE_SET` was absent after the scope — but `scope` restores whatever was there, so a runner exporting that name failed the test for behaving correctly. Reproduced: exporting it turns the old test red. The cleanup had the mirror problem. The test overwrote any ambient `..._SCOPE_CLEARED` and then removed it unconditionally at the end, so it deleted a value it had not created. Both now snapshot before touching anything: the override is asserted to return to its snapshot, and the cleanup restores the snapshot rather than removing. Teeth checked rather than assumed — with `Drop` mutated to stop re-removing a variable that was absent, the test goes red both with and without ambient values set.
|
@codex review Fixed in P3 — preserve ambient values in the scope test. Correct, and it is the same defect as the last round exactly one test over. The cleanup had the mirror problem, which the finding also names: the test overwrote any ambient Both now snapshot before anything is touched. The override is asserted to return to its snapshot — absent if it was absent, its own value if it had one — and the cleanup restores the snapshot instead of removing. The half worth checking is that it did not lose its teeth in becoming correct:
Gates on Round 1 of 2 for this sweeper pass. If a further finding comes back after the next one, I stop and leave this for a human rather than keep cycling unattended. Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dcdd9edc89
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
`tempfile::tempdir()` roots at `std::env::temp_dir()`, i.e. at `TMPDIR`. Every fixture here is handed to `discover_named` through `AWARE_SIDECAR`, and that reads its override with `std::env::var`, which rejects a non-UTF-8 value as `NotUnicode` and falls through as though the variable were unset. So on a machine whose `TMPDIR` carries a non-UTF-8 byte the fake sidecar is never found. Measured, with such a directory really exported: 13 of the 44 sidecar tests fail, every one of them for a reason that has nothing to do with what it covers. After this change all 44 pass, under that `TMPDIR` and under a clean one alike. Both fixture sites now take their base from `readable_tempdir_base`, which falls back when `to_str` fails — the exact test `discover_named` applies later, so it rejects precisely what that would reject. The ASCII shell hazards `hostile_tempdir` exists for are in the prefix, which is ours; only the base had to change. `live_fixtures_land_on_a_readable_path_under_a_non_utf8_tmpdir` pins it, asserting the property rather than the fallback path so choosing a different UTF-8 base later is not a test change. It goes red against the inherited base. The underlying production limitation is unchanged and still flagged: a valid non-UTF-8 override is silently indistinguishable from unset. That is a behaviour change in `discover_named`, not a test fix.
|
@codex review Fixed in P2 — create live fixtures beneath a UTF-8 path. Correct, and worse than stated. I exported a real directory named with a
Every one of those 13 failed with Both fixture sites now take their base from a new One extra site beyond the ones you flagged:
To be explicit about what this does not do: the production limitation is unchanged and still flagged — Gates on This was round 2 of 2 for this sweeper pass. If anything further comes back, I stop and leave this open for a human rather than keep cycling unattended. Generated by Claude Code |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ad268bd241
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ests Two fixes, both about tests depending on the machine rather than on the code, which is the defect class this branch keeps rediscovering. `live_fixtures_land_on_a_readable_path_under_a_non_utf8_tmpdir` proved the right property by pointing the process's own `TMPDIR` at a non-UTF-8 path. `TMPDIR` is process-global and the many tests in this binary that call `tempfile::tempdir()` directly do not go through `EnvVarGuard`, so nothing orders them against that write: one overlapping it would try to create its directory under a path that does not exist and fail its own `unwrap()`, nondeterministically, in a test unrelated to any of this. Split the decision into `readable_base_from(candidate)` and drive it directly. Both directions are now covered — a non-UTF-8 candidate is replaced, and a good one is used as given, so hardcoding the fallback no longer passes. Second, the ambient-value sweep. `scope_unsets_...` was fixed one test at a time; exporting the `AWARE_TEST_ENV_GUARD_*` names showed four of the six failing while the guard behaved correctly. `Ambient` pins each variable's starting state and restores the runner's own value on drop. It forces the state rather than only recording it, and that is the point: snapshot-and-compare would stop the false failure, but a test named "restores an *absent* variable" would then quietly stop testing the absent case wherever the variable is set — a vacuous pass in place of a visible failure. Verified by exporting all nine names: every test still passes, and every mutation still turns the same tests red.
|
| Mutation | Test that went red |
|---|---|
| M26 — fallback removed, a non-UTF-8 candidate used as-is | a_non_utf8_tmpdir_is_replaced_by_a_readable_base |
M27 — base hardcoded to /tmp, ignoring a good TMPDIR |
a_readable_tmpdir_is_used_as_given |
M27 is the half worth calling out: without it, hardcoding the fallback passes, and the helper would silently stop using the runner's TMPDIR at all.
2. The ambient-value defect, swept module-wide instead of one test per round. Codex has now flagged this same class three times (AWARE_ROSLYN, then two AWARE_TEST_ENV_GUARD_* tests), each time on the next test along. Rather than wait for round six, I exported all nine names this module uses and measured: four of the six tests failed while the guard behaved perfectly. Only one of the four had been reported.
Ambient pins each variable's starting state and restores the runner's own value on drop.
It forces the state rather than only recording it, and that difference is the substance. Snapshot-and-compare — the literal suggestion — stops the false failure, but a test named restores_absent_variable_on_drop would then quietly stop testing the absent case on any machine where the variable is set: a vacuous pass traded for a visible failure, which is the exact thing this PR exists to remove. Forcing the precondition keeps each assertion meaning what its name says, everywhere.
Verified both ways — with all nine names exported, every test passes, and M22/M23/M25 still turn precisely the same tests red as in a clean environment.
State
| Head | 8f9e8ecb — unreviewed |
| Local gates | fmt --check pass · clippy -D warnings pass · cargo test 888 passed, 0 failed |
| Extra runs | full suite green under a non-UTF-8 TMPDIR, and under all nine AWARE_TEST_ENV_GUARD_* exported |
| Codex rounds | 6 · findings 8 · all fixed, all threads resolved |
Recommendation
Six review rounds have converged on one theme — tests that assert about the machine rather than the code — and each fix has been correct while occasionally introducing the next instance. That is a good argument for a human to read d582da2b..8f9e8ecb once, end to end, rather than for more automated rounds. The substance is still the first commit; the other five are review response.
Also still open and untouched: discover_named (cli/src/sidecar.rs:40) reads its override with std::env::var, so a valid non-UTF-8 AWARE_SIDECAR/AWARE_ROSLYN is silently indistinguishable from unset. Every TMPDIR finding above is a downstream symptom of it. Fixing it is a production behaviour change and remains the maintainer's call.
Generated by Claude Code
Sweeper pass: stopping at my cap. Not merging.I am the PR-sweeper routine — the pass that picks up routine PRs whose own routine has stood down. I was the second session in the race described above; that was my doing and I'll own it. When What I contributed: two rounds, both on Codex findings against the head at the time.
Codex's next finding was against Why I am stopping rather than merging. My cap is two fix-and-re-review rounds per pass, and I have spent both. The State I am leaving: head I have deliberately not re-requested Codex. Six rounds have converged on one theme, and I agree with the recommendation above: this wants a human reading Generated by Claude Code |
Area
cli/src/sidecar.rs— one file, in depth. It carried 444 lines of code behind 44 lines of test: twodiscover()cases and twoserdeshape checks onOkResponse. Everything between a reply landing on stdout and aGeneratedAgentcoming back out was uncovered.The one suite that did touch it,
cli/tests/sidecar_integration.rs, gates every test onsidecar_binary() -> Option<PathBuf>and returns early when the NativeAOT binary is absent — which is every CI run. So those paths were untested in practice, not merely thinly tested.Approach: a
#!/bin/shstand-in for the sidecar, pointed at throughAWARE_SIDECAR, that records the request on stdin and replays a fixed stdout/stderr. That drivesinvoke_raw_withfor real — spawn, stdin write, wait, parse — without the C# binary. It is#[cfg(unix)]; the code under test is platform-independent, so covering it on one platform covers it. The two pure functions (render_inputs_yaml,to_local_agent) are tested directly, with fixtures parsed off the wire viaserde_jsonrather than built field by field, so the#[serde(default)]fallbacks onSidecarCommand::mode/::inputsare exercised by the same fixtures.19 tests added, 1 deleted. Net +509 lines, all in
#[cfg(test)]. No production code changed.Gates
Run from
cli/on the pinned toolchain (rust-toolchain.toml→ 1.95.0), withclang libsecret-1-dev libdbus-1-dev pkg-configinstalled as CI does:cargo fmt --all -- --checkcargo clippy --all-targets -- -D warningscargo testMutation evidence — tests added
Every test below was proven red by breaking the code it covers and watching it fail, then restoring. Each row is one mutation applied in isolation to
src/sidecar.rs; the right-hand column lists every test that went red under it, so the overlap is visible rather than implied.render_inputs_yaml:if !i.optional→if i.optionalrequired_is_emitted_only_for_non_optional_inputs,defaults_are_emitted_only_when_the_input_declares_one,hostile_input_metadata_is_yaml_quoted_in_every_field,commands_are_keyed_by_name_and_carry_mode_and_rendered_inputsrender_inputs_yaml: thedefault:line never emitteddefaults_are_emitted_only_when_the_input_declares_one,hostile_input_metadata_is_yaml_quoted_in_every_fieldrender_inputs_yaml:quote_yaml_scalardropped from name + typehostile_input_metadata_is_yaml_quoted_in_every_fieldto_local_agent:versionandsdk_targetswappedsidecar_version_becomes_sdk_target_and_the_agent_starts_at_0_1_0,reply_is_mapped_into_a_generated_agentto_local_agent:stateful: s.stateful→stateful: falsesidecar_version_becomes_sdk_target_and_the_agent_starts_at_0_1_0to_local_agent:mode: c.mode→mode: Nonecommands_are_keyed_by_name_and_carry_mode_and_rendered_inputsto_local_agent: skillfilenameandbodyswappedskills_are_carried_through_verbatimto_local_agent:source_kindhardcoded to"dlls"provenance_records_the_source_kind_the_caller_passed,roslyn_reflection_uses_its_own_binary_and_source_kindinvoke_raw_with: trailing\nnever written to stdinrequest_is_one_newline_terminated_json_object_naming_the_opinvoke_raw_with:Envelope { op }hardcoded to"reflect"request_is_one_newline_terminated_json_object_naming_the_opinvoke_raw_with:if !parsed.ok→if falsesidecar_reported_failure_surfaces_its_message_and_the_op,failure_without_a_message_still_failsinvoke_raw_with:parsed.errordropped from the messagesidecar_reported_failure_surfaces_its_message_and_the_opinvoke_raw_with: missingdata→ emptyResponseDatainstead of an errorsuccess_without_a_data_payload_is_rejectedinvoke: missingagent→ a defaultSidecarAgentinstead of an errorsuccess_without_an_agent_is_rejectedinvoke_raw_with:stderrdropped from the not-JSON messageunparseable_stdout_reports_both_streamsOkResponse::versiongiven#[serde(default)]reply_without_a_version_stamp_is_rejected_on_the_live_path,reply_without_version_is_rejected(pre-existing)coverage_validate:ok == falseturned into anErrcoverage_validate_returns_violations_as_a_value_not_an_errorcoverage_validate: missing payload → a default result instead of an errorcoverage_verbs_do_not_accept_each_others_payloadsdiscover_roslynreadsAWARE_SIDECARroslyn_discovery_does_not_fall_back_to_the_sidecar_variable,roslyn_reflection_uses_its_own_binary_and_source_kindreflect_csharpstamps"dlls"instead of"csharp"roslyn_reflection_uses_its_own_binary_and_source_kindM16 also kills a pre-existing test, which is the intended overlap:
reply_without_version_is_rejectedchecks the guard againstOkResponsein isolation, the new test checks it end to end on the live spawn path.Mutation evidence — test deleted
no_inputs_render_to_an_empty_body—assert_eq!(render_inputs_yaml(&[]), "").Justification is the mirror image: it was run against M1, M2 and M3 — three genuine breaks of the very function it named — and stayed green through all three. It could only ever fail on a mutation invented to fail it (appending an unconditional trailing newline), which is not evidence of coverage. The behaviour it claimed is not lost:
commands_are_keyed_by_name_and_carry_mode_and_rendered_inputsassertslist.inputs_yaml == ""for a command that declares no inputs, on a fixture parsed off the wire.Notes on test shape
Deliberately avoided the three failure modes this kind of sweep invites:
unwrap()standing in for an assertion. Error-path tests destructure the concrete variant (let AwareError::Network(message) = err else { panic!(...) }) and assert on the message, so a test that fails does so for the reason it names.hostile_input_metadata_is_yaml_quoted_in_every_fieldgives each field a different quoting trigger (:in the name, leading*in the type,#in the default) so dropping the quoting on one shows up distinctly rather than being masked by the others, and then re-parses the output throughserde_yamlto confirm the values survive the quoting.roslyn_discovery_does_not_fall_back_to_the_sidecar_variableis written to tolerate a realaware-roslynon the runner's PATH — it asserts the resolved path is not the sidecar's rather than asserting discovery fails — so it cannot go red for an environmental reason.Review
Codex is primary per CLAUDE.md §"PR review — non-negotiable"; requested on this PR. Result will be recorded here and in the routine log (#342).
Generated by Claude Code