Skip to content

fix(execpolicy): a redirect is not a command separator - #9

Open
SparkofSpike wants to merge 322 commits into
mainfrom
fix/safety-gate-ampersand-classification
Open

SparkofSpike wants to merge 322 commits into
mainfrom
fix/safety-gate-ampersand-classification

Conversation

@SparkofSpike

Copy link
Copy Markdown
Owner

Summary

Every shell command containing a 2>&1 redirect was refused on Windows with:

Built-in safety gate requires approval: Windows command input cannot be
classified safely enough to exclude termination of Codewhale npm launchers;
use a direct PID- or port-specific command.

The refusal reason names npm launchers, but no launcher check ever ran. The
command was simply unclassifiable, and the reason text is a canned
fail-closed string. Two lines in the shared classifier made a plain redirect
unreadable:

crates/execpolicy/src/command_safety.rs, split_command_segments, kept a bare
& inside the word ('&' => current.push(ch)), and command_invocations
re-parsed any word containing &. Since the segmenter never separates a bare
&, that re-parse returned the identical word, recursed again, and hit
MAX_WRAPPER_DEPTH. command_invocations then returned None — "fail closed"
— and windows_session_runtime_risk turned that into
UnclassifiedWindowsInvocation, which the runtime floor converts into a
non-bypassable block (auto_review.rs:1364).

So: the safety gate was not too strict about launchers. It could not read the
command at all.

Reproduction (before this change)

In a Full Access session with the sandbox disabled, on Windows:

  • refused: cargo fmt --all -- --check 2>&1 | Select-Object -First 30
  • refused: git fetch origin 2>&1; git log --oneline -1 origin/main
  • refused: & $GH auth status (PowerShell's call operator — a bare &)
  • refused: git grep -n "2>&1" -- crates/ (the literal text inside quotes)
  • allowed: any of the above with the & removed

The last two lines are the tell: the classifier never gets to decide what the
command means, because a bare & makes it stop reading.

Changes

  • split_command_segments: a & that is a command separator now splits
    the segment. POSIX job control and cmd.exe both treat it that way. The & of
    a redirection stays inside the word: >& copies a file descriptor (2>&1,
    >&2) and &> is bash's combined redirect.
  • command_invocations: re-parse a word only when re-parsing can yield a
    different token stream — whitespace, ;, or |. A & that survived
    segmenting is part of a redirect; recursing on it could only reproduce the
    same word.

The second change alone would have been a weakening: a bare & glued to
its neighbours (echo x&rm -rf /etc) names two commands, and left unsplit it
corrupts the command word of the second one. Splitting it is the fix; dropping
the recursion is what makes the redirect readable.

Evidence

redirects_stay_classifiable_and_a_lone_ampersand_still_separates (new, in
crates/execpolicy/src/command_safety.rs) asserts both directions:

  • git log 2>&1, cargo test 2>&1 | tail -5, make > build.log 2>&1,
    tool &> out.txt stay classifiable.
  • echo x & rm -rf /etc, echo x&rm -rf /etc,
    curl https://example.com & rm -rf /etc still yield an rm invocation, so
    the second stage is reached rather than blanket-refused.

Run against the previous code, that test fails with exactly the reported
symptom:

a redirect must stay readable instead of failing closed: git log 2>&1
test result: FAILED. 0 passed; 1 failed

With the change: cargo test -p codewhale-execpolicy command_safety →
87 passed; 0 failed.

TUI side: cargo test -p codewhale-tui auto_review → 72 passed; 0 failed.
test_cases_09.rs used to assert the Windows floor as the expected outcome
for cargo test & curl https://example.com; that assertion is removed because
the command is classifiable and belongs on the reviewer path like every other
approval-shaped command. The remaining windows_* tests in that module pass
unchanged.

cargo fmt --all -- --check is clean.

Note on a flaky-looking neighbour

core::engine::tests::auto_review_asks_the_user_and_returns_the_answer
overflowed its stack on this machine. It is unrelated to this change (the diff
only removes recursion) and it is a stack-size limit, not a logic failure:
with RUST_MIN_STACK=33554432 it passes, and it passes as part of the 72. Not
fixed here — out of scope, but worth a separate look.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

Checklist

  • Updated docs or comments as needed
  • Added or updated tests where relevant

Related Issues

No-Issue: observed in an operator session on 0.10.1 (Windows/PowerShell); no
upstream issue filed for it.

Attribution

🤖 Generated by SpikeBot 000(CodeWhale-LOCAL)

@SparkofSpike

Copy link
Copy Markdown
Owner Author

Adversarial review — PR #9: "a redirect is not a command separator"

Verdict

The fix holds. The root-cause analysis is accurate, both edits are needed and
complementary, and I could not construct a single command where the new code
loses a discovered invocation or lets a real destructive stage go unread. I ran
the crate on this box (isolated toolchain, cargo test -p codewhale-execpolicy)
and confirmed the reported symptom and its resolution empirically. I found one
confirmed, low-severity regression (quoted & is split inside quotes) that
over-reads, never under-reads — worth a follow-up, not a block.


Findings (most severe first)

F1 — CONFIRMED: & inside quoted text is split, inventing phantom commands and misclassifying benign strings

crates/execpolicy/src/command_safety.rs:2033-2035 — split_command_segments
is a raw character scan with no quote awareness. The new &-splits fire even
when the & is inside a shell quote, producing segments that shell_words
would never have produced from the original line.

Ran on this box (temporary probe, since reverted):

command previous code this PR
echo "a & b" readable, single argv invocations [["echo","a"],...,["b"]] — phantom b
echo "a&b" None (fail-closed) invocations include phantom ["b"]
echo 'x & rm -rf /etc' level=Dangerous via None invocations include ["rm","-rf","/etc"] (a destructor the shell never runs), level=Dangerous

The quoted-&-with-rm case is the sharpest: command_invocations("echo 'x & rm -rf /etc'") now yields an rm -rf /etc argv, which reaches
argv_is_destroyer at crates/tui/src/tui/auto_review.rs:1565-1570. The two
command_invocations consumers there are both hold-only (block), never
allow gates, so a phantom rm can only over-hold — it cannot let a real
destroyer through. Consequence: benign commands quoting shell metacharacters
(git commit -m "fix & feature", checks that grep for rm -rf text) get
split into phantom invocations and may be refused/held spuriously. This is a
availability / classification regression, not a security opening. Old code
failed closed on the same input (returned None), which is where the bug the
PR fixes
came from; but the fix could equally skip splitting when the & is
inside quotes, at once preserving readability and not inventing commands.

I could not turn this into a false negative: an innocent & is split too,
never merging two real commands, so no real separator is hidden.

F2. CONFIRMED: analyze_command levels are byte-for-byte unchanged across the adversarial battery — blast radius is well bounded

I captured SafetyLevel for 50 commands (redirects, glued separators, quoting,
eval, curl|sh, a&b… shapes) under both the previous and new source. That
list was identical in order and level. The only behavioral delta the PR creates
is in command_invocations() — every & command went from None(fail-closed)
to a readable Vec. So the functional effect is confined to the three
consumers at auto_review.rs:1364, 1376, 1569 (the Windows floor and the
destroyer hold). The Windowsnpm-launcher hard-blow the PR targets
(command_invocations == None ⇒ UnclassifiedWindowsInvocation) is exactly what
changes. That is the correct, contained surface. TUI-side I did not build
(codewhale-tui takes tens of minutes on this box); the 72 passed CI claim
was not verified by me.

F3. CONFIRMED: no discovered invocation is lost — provably a superset

Every & that is not preceded by > and not followed by >
(command_safety.rs:2033) splits; the only survivor inside a segment is the
redirect form. &-free commands are byte-identical between versions. Therefore
command_invocations and split_command_segments can only ever enumerate a
strict superset of the old (readable) set. I tested the shapes the task named —
a>&b, a&>b, 2>&1&rm -rf /etc, cmd &>file & rm -rf /etc, x>&y&z — plus
echo >& rm -rf /etc, cmd > file >& rm -rf /etc, sort < in > out & mv, and
the second rm stage reappears in every case the shell would actually run it
and in none where the & was a redirect. The author's argument that "edit 2
alone would be a weakening" is correct: with only the & removed from the
re-parse predicate and no split, echo x&rm -rf /etc would read words
["echo","x&rm",…] and the rm never discovered. Both edits must land
together — they do.

F4. Confirmed (correct behavior, not a defect): redirect spellings are all preserved

I exercised the operator list for the guard table:

spelling survives split? evidence
2>&1 yes git log 2>&1 readable
>&2 yes >&2 echo hi → [">&2","echo","hi"]
1>&- yes single token, readable
&>file yes tool &> out.txt readable
&>>file yes >>&file single token
2>&1& (trailing) yes→bare & splits after 2>&1&rm -rf /etc → ["2>&1"],["rm…"]
N>&M yes exec 3>&1, 2>file 3>&1

The trailing-& sub-case (2>&1&rm) is itself nearly the acid test for F3 and
works. The >&/&> guard correctly discerns redirect from backgrounding.

5 (asked) — test_vacuity

The new test redirects_stay_classifiable_and_a_lone_ampersand_still_separates
is not vacuous: run against the previous code it fails on the first assert
(git log 2>&1 → None), and its second half reproduces the F1 phantom for the
spaced echo x & rm -rf /etc (which is genuinely a real separator and must be
rm). The is_some assert is the one the old code cannot pass. Credible.

6 — test_cases_09.rs assertion change is correct, and the Windows floor isn't dead

The deleted branch asserted Block(UnclassifiedWindowsInvocation) for cargo test & curl … under the buggy behavior; with the fix that command is
classifiable, so the correct expectation for it on all platforms is
ConsultReviewer (still refuses auto-approval). Real coverage of the floor is
retained elsewhere: auto_review.rs:2017 asserts deep sh -c nesting still
yields UnclassifiedWindowsInvocation via the depth cap, and
windows_session_runtime_risk/.._risk_scoped still fire on genuine
unclassifiable nesting. Not a coverage hole.

7 — Comments/doc accuracy

  • The comment at command_safety.rs:2030-2032 is accurate for POSIX and for
    cmd.exe bare-&.
  • The depth accounting is unchanged and, now that re-parse no longer recurses on
    redirects, the previously-infinite redirect loop is the one real depth bug
    the PR removes; MAX_WRAPPER_DEPTH bounds the genuine nested sh -c case
    still covered at auto_review.rs:2017. No miscount found.

Bottom line

No adversary succeeded against it. The core reclassification (client
&-separator vs. redirect) is sound, the two edits are mutually necessary, the
effect is precisely scoped to two hold-only consumers, and no real destructive
invocation was demonstrated lost. Confirm (i.e. the bug is fixed and the
fix does not open a destructive-path dimpling). Highest-value follow-up:
teach split_command_segments about quotes so & inside quotes doesn't
manufacture phantom commands (F1), which currently flags more than it should
and would also trip a user grepping for 2>&1 in their own helper text.


References (lines in commit d37431a):

  • crates/execpolicy/src/command_safety.rs:2033-2035 — guard + bare-& split
  • crates/execpolicy/src/command_safety.rs:2096-2097 — re-parse predicate
  • crates/tui/src/tui/auto_review.rs:1364 — None → Unclassified (the bug)
  • crates/tui/src/tui/auto_review.rs:1565-1570 — hold-only use of command_invocations
  • crates/tui/src/core/engine/tests/test_cases_09.rs:474-486 — changed windows-floor assertion

Attribution

🤖 Generated by SpikeBot 003(ClaudeCode-JP)

The redirect fix made `&` a separator but left the scanner quote-blind, so an
operator inside quotes was split anyway. The adversarial review of this branch
caught the result: `git commit -m "fix & feature"` read as two commands, and
`echo 'x & rm -rf /etc'` produced an `rm` invocation the shell never runs.
Both reach the destroyer hold, which can only refuse a command that was fine —
and a spurious refusal is the bug class this branch exists to remove.

Two parts:

- `split_command_segments` tracks quote state, so an operator inside quotes
  stays in its word. The read-only grammar already promised this
  (`rg 'a && b; c' src`); the shared scanner now agrees.
- `command_invocations` re-parses a word only when it is a shell's script
  argument (`sh -c`, `bash -lc`, ...). An ordinary argument that happens to
  contain whitespace or an operator is not a nested command line; re-parsing it
  invented commands, and once a `&` made a word reproduce itself it also failed
  readable commands closed.

Tests: `a_quoted_operator_stays_data` covers the four shapes from the review
(`git commit -m "fix & feature"`, `echo "a & b"`, `echo 'x & rm -rf /etc'`,
`printf 'a;b | c'`) and asserts one command stays one command. The nested
`sh -c` detection the previous change relies on is still covered by
`command_invocations_reach_past_wrapper_options_and_into_shell_payloads`,
including its depth-cap assertion. `cargo test -p codewhale-execpolicy
command_safety` -> 88 passed; 0 failed.
@SparkofSpike

Copy link
Copy Markdown
Owner Author

Adversarial review — follow-up commit ea9bc1b ("a quoted operator is data, not a separator")

Verdict

The follow-up holds — no regression. F1's phantom invocations are gone
from command_invocations for all four named shapes; the narrowed recursion
does not lose a real command in any case I could construct; quote state closes
and stays closed correctly for every separator-shaped input except one
pre-existing backslash-escape residual; and the Windows floor
(None ⇒ UnclassifiedWindowsInvocation) plus the PR's original
git log 2>&1 fix still hold. The commit fixes exactly what F1 named. My
strongest adversarial attempt — a non-literal destructive payload in a -e
interpreter — is a pre-existing gap this PR does not widen.

Evidence: cargo test -p codewhale-execpolicy command_safety on this box
→ 88 passed; 0 failed. Plus a temporary probe (reverted afterwards) running
command_invocations/analyze_command under both this commit and the
pre-follow-up d37431a, side-by-side.


Findings (most severe first)

1. CONFIRMED — F1 fixed: no phantom invocation survives the four named cases.

crates/execpolicy/src/command_safety.rs:2017 split_command_segments now
tracks quote state; :2122 command_invocations re-parses a word only via
:2106 is_shell_script_argument. Side-by-side over d37431a:

command d37431a argv (the F1 phantom) ea9bc1b argv result
echo "a & b" ["echo","a"],["a"],["b"] ["echo","a & b"] phantom gone
echo "a&b" ["echo","a"],["a"],["b"] ["echo","a&b"] phantom gone
echo 'x & rm -rf /etc' ["rm","-rf","/etc"] present no rm argv phantom rm gone
git commit -m "fix & feature" ["git","commit","-m","fix"], phantom feature ["git","commit","-m","fix & feature"] one command

No phantom rm survives in command_invocations, and no stage the shell
actually runs is missing from the argv list.

Nuance (not a defect): the user-visible analyze_command verdict for
echo 'x & rm -rf /etc' is still dangerous — command_safety.rs:1821
DANGEROUS_PATTERNS (rm -rf /) is quote-blind and fires on the literal
text. That is pre-existing (unchanged from main) and intentional
hardening (the "keep that protection" comment at :1814); it over-reads,
never under-reads. The F1 issue — the invented rm argv at the destroyer
hold — is gone even though the substring fallback still classifies the
quoted string dangerous. If the goal were "a benign string quoting rm -rf /etc is never refused", that remains unmet, but it is not this edit's
omission.

2. CONFIRMED — narrowed recursion loses no real command; every dropped re-parse is a phantom.

Ran the battery under both d37431a and ea9bc1b. For the -e interpreters
(perl -e 'system("rm -rf /etc")', ruby -e 'system "rm", "-rf", "/etc"',
node -e '...execSync("rm -rf /etc")', awk 'BEGIN { system("rm -rf /etc") }')
and python3 -c "import os; os.system('rm -rf /etc')", the old code re-parsed
the payload and produced an rm argv whose argv[0] was "system(rm", "rm,",
"require(...)execsync(rm", or "os.system('rm" — none equal "rm", so
none ever matched auto_review.rs:1580 argv_is_destroyer. Those were phantom
non-holds already; the new code drops them, and the real destructive text is
still caught by the substring fallback → Dangerous in both old and new.
No destructive stage is lost.

The genuinely-running shell payloads — sh -c 'rm -rf /home',
bash -lc 'cd /; rm -rf /tmp', bash -- -c 'rm -rf /boot',
/bin/sh -c 'rm -rf /var', find | xargs [ -I{} ] sh -c … — are still
re-parsed
(previous word -c / -lc / -ic passes
is_shell_script_argument), yield the exact ["rm","-rf",…] argv, and are
dangerous. Verified argv-for-argv.

sh -c 'true' -- 'rm -rf /etc' and sh --command 'rm -rf /etc': old code
re-parsed and raised a phantom; new drops it. That is correct — in
sh -c 'true' -- 'rm -rf /etc' the rm word is $1 of the true script,
never executed, and --command is not a real option. Both were over-reads.
xargs -I{} sh -c 'rm -rf {}' still surfaces ["rm","-rf","{}"]. The
command_word-folded forms (/bin/sh, \rm, /usr/bin/rm) and the
find | xargs … sh -c '/etc' chain behave identically old vs new. This
category yielded no false negative.

3. CONFIRMED — one pre-existing residual: the scanner does not honor backslash escapes, so \" inside double quotes can still invent a phantom rm.

echo "a \" & rm -rf /etc": the char scanner at 2017 treats the \" as
closing the double quote (no escape awareness), so & rm -rf /etc lands
outside a quote and the & splits → a phantom ["rm","-rf","/etc"] argv the
shell never runs. This is identical in d37431a and ea9bc1b and redundant
with the Dangerous already raised; it is not introduced or reopened by
this commit, but the fix's quote-tracking still is not a shell lexer. The
opposite failure — quote state closing early and thereby hiding a real
separator — did not surface: echo "a&b" & rm -rf /etc (quoted op followed by
real) keeps the quoted & as data and still splits the real one, finding the
rm; echo "&" ; rm behaves the same; consecutive single quotes
(echo 'it'\''s & x') stay data with no phantom; and an unterminated double
quote (echo "x & rm -rf /etc) keeps & inside the never-closed quote,
matching the shell's non-execution of that rm. Effect: exactly one,
pre-existing, over-read-only finding in this category.

4. CONFIRMED — Windows floor & the PR's original fix still hold.

command_invocations returning None still arms UnclassifiedWindowsInvocation
(auto_review.rs:1359). git log 2>&1 and make >build.log 2>&1 | tail -3
both stay Some (classifiable) here. The depth-cap path
command_invocations_reach_past_wrapper_options_and_into_shell_payloads still
asserts a too-deep nested sh -c returns None, and the suite passes it.
The PR's own redirects_stay_classifiable_and_a_lone_ampersand_still_separates
still covers echo x&rm -rf /etc (un-spaced real &) and tool &> out.txt.
Nothing new in this category.

5. CONFIRMED — wrapper-option reach-in (sudo -u, command_word folding, --, xargs) is preserved.

sudo -u me rm -rf /etc, \rm -rf /etc, /usr/bin/rm -rf /etc, and
find | xargs [ -I{} ] sh -c/bash -c 'rm -rf /etc' all still yield the
rm argv and are dangerous. This is the original PR's core guarantee and
none of it regresses.


Bottom line: the follow-up resolves F1 in exactly the surface it touched
and I could not find a genuine command it now misses that the old code caught —
the dropped re-parses are phantom rms, substring-fallback redundancy, or
system()-payload gaps identical before and after the change. Merge-able.
Categories 2 and 4 produced no confirmed defect.

Attribution

🤖 Generated by SpikeBot 003(ClaudeCode-JP)

SparkofSpike and others added 27 commits October 9, 2026 03:13
A goal continues until it is complete or blocked. Operate tells the model to
create one for every substantive request, so a request with natural stages has
no way to stop between them: the intra-turn continuation re-dispatches on any
answer that leaves the goal Active, and a completed turn re-arms another one.
That leaves "verified complete" and "blocked" as the only model-side exits —
and a blocked goal stays blocked until an explicit `/goal resume`, so reporting
a milestone that way strands the work instead of continuing it.

`update_goal` gains `status: "yield"`: the model finished a stage and the next
step is the user's call. The goal stops being Active — which is exactly what
both continuation dispatchers test before re-arming — while staying unfinished,
and the user's next message resumes it. That is the same shape as a runtime
stop, which is already resumable by writing to it. A blocker the model reported
and a pause the user asked for still need an explicit resume.

The continuation prompt and the tool description name the new exit, so the
model knows it may hand back at a milestone rather than grinding to a terminal
status.

Tests: `a_yield_stops_continuation_and_the_users_message_resumes_it`,
`a_user_pause_is_not_resumed_by_a_yield_resume`, and
`update_goal_yield_pauses_without_completing_or_blocking`. The existing
`update_goal_rejects_model_resume` keeps proving that the model cannot resume
itself. `cargo test -p codewhale-tui --lib goal` -> 143 passed; 0 failed.
The adversarial review blocked the first version; the independent review found
the same host-side gap. Three fixes:

- `mark_yielded` refuses any goal that is not active. Without that guard the
  tool could overwrite a NoProgress stall, a BudgetLimit pause, a user pause, or
  a blocker into a user-resumable hand-back — widening the resume contract this
  state exists to keep narrow. The guard is what makes "two states, two
  meanings" true at the tool boundary rather than only at resume time.

- `GoalPauseReason::Unrecognized` with `#[serde(other)]`. The durable host
  stores the reason as JSON (`crates/state/src/lib.rs`), so a new variant with
  no fallback made a record written by this build fail an older build's goal
  load. Nothing constructs the variant; the resume path reads it as "not a
  hand-back".

- `thread_goal_status_projection` takes the record's own pause reason, and
  `sync_from_host_status_with_reason` carries it through. Mapping every Paused
  status to `GoalPauseReason::User` erased a hand-back on any host sync between
  the yield and the user's reply, leaving the goal silently paused.

The tests the review called unpinned are now pinned at the gate the loop
consults: `a_yielded_goal_is_not_rearmed_by_the_continuation_gate`, with an
active-goal control proving the gate is not dead under test conditions.

Known and recorded in the PR body rather than closed here: a model can yield
repeatedly without advancing the goal, and the stall counter only moves on
`record_not_achieved`, so the NoProgress net does not catch that pattern.

`cargo test -p codewhale-tui --lib goal` -> 147 passed; 0 failed.
Persist /pet on|off through Settings and the existing ordered background
writer. Restore the main pet backdrop without the launch overlay. The
Appearance setting reports saved and effective values separately; session
only overrides and rejected values leave the startup choice alone.

The inspector's c key and painted footer target invoke /copy, retaining its
completed-answer receipt, raw Markdown, recovery backup and queued-vs-accepted
clipboard wording. A bounded command receipt remains visible over Done.
Hidden, resized and truncated footer targets are cleared before input.
All three new labels are translated in the fifteen complete locale packs.

Validation, final source:
- Pet: 60 passed, 0 failed (restart/order/shutdown, malformed settings, drafts,
  real keyboard/pointer routes, copy authority and dark/light renderer).
- Ordered writer: 9 passed; shell routing: 17 passed; /copy: 14 passed;
  toast lifecycle: 6 passed; localized settings schema: 1 passed. No failures.
- Config schema: 4 passed; localization: 54 passed. Total: 165 passed, 0 failed.
- npm test: 1,317 passed, 7 skipped, 0 failed (545 Node + 772 Vitest).
- npm run check:web passed. Changed Rust formatting: 17 files passed.
- Production TUI Clippy passed with -D warnings and the CI's existing
  uninlined_format_args, too_many_arguments and unnecessary_map_or allowances.

Earlier local receipts retained: a test import used a retired private command
path (E0433), then the first compiled run passed 58 and failed 1 because its
unknown-key fixture assumed preservation beyond the existing serializer's
contract. Tests now use the public command entry and supported preference
annotations; malformed TOML remains byte-preserved. Final checks above pass.

Stage only 35 declared paths. Preserve unrelated shared documentation/web/
avatar changes, including the two avatar guards in pet_watch/mod.rs. The
shared library remains the merged codewhale-hq#25 component; no new execution authority.
This source slice updates codewhale-hq#6920. Actual 0.10.2 candidate codewhale-hq#6907 integration,
local installed binary and live terminal/provider acceptance remain separate.

Refs SHA-6533
The fold added in the previous commit hardened `default` and `deny` but took
the document's `allow`, `proxy`, `proxy_fake_ip_cidrs`, and `audit` wholesale.
`decide` checks `allow` *before* falling back to `default`, so under a
deny-by-default authority — the canonical managed / Fleet stance — a document
listing `allow = ["api.github.com"]` still produced `Allow`. The fold was
escapable in the one direction it exists to protect, and the review that found
it also caught that the case's own test asserted the leak: it checked
`folded.default` stayed `Deny` and that the document's host was in `allow`,
never what `decide` returned.

The fold is now asymmetric by the authority's own fallback:

- `deny`: union. Either layer saying no is a no.
- `default`: the stricter of the two, as before.
- Authority `default = Deny`: the lower layer contributes nothing that can
  allow. `allow`, `proxy`, and the fake-IP CIDRs stay the authority's own — no
  host lifted, no SSRF exception granted.
- Authority `default = Prompt`: the lower layer may name hosts, which is what
  `/network allow <host>` writes. Those **merge** with the authority's own
  instead of replacing them, so a refresh cannot erase an administrator's
  allowances either.
- `audit`: either layer asking for the log keeps it. A document cannot silence
  audit under an authority.

`folding_a_document_never_widens_an_authority` now asserts `decide()` verdicts
for both fallbacks rather than field shape: the document's host is `Deny` under
a denial and `Allow` under a prompt, the authority's own allowance survives, an
unlisted host keeps the authority's fallback, and no fake-IP exception or audit
silencing crosses the fold.

`cargo test -p codewhale-tui network_policy` -> 40 passed (lib) + 28 passed
(integration); `cargo test -p codewhale-tui tool_context` -> 24 passed.
The review noted the authority-Allow fallback branch was correct but unpinned:
a managed overlay may legitimately resolve `default = "allow"`, and its fold
takes the union branch. The test asserts the document's own `default = deny`
narrows it and a named host stays allowed -- nothing there is more permissive
than the authority's own policy, because that policy already allowed
everything.

`cargo test -p codewhale-tui network_policy` -> 41 passed (lib) + 29 passed
(integration).
Land the four 0.10.2 release repairs from the frozen review batch. The
source reviews passed. This commit adds local compile and focused-namespace
evidence, not hosted CI. Hosted CI on this head is the full proof.

Repairs:

- Undo (commands/contract/debug_operations.rs,
  commands/debug_mutation_host_tests.rs): refuse ambiguous, repeated,
  truncated, multiline, missing or multipart request labels instead of
  matching display snippets. Exactly one held match is required, and force
  cannot bypass identity checks. Addresses the P1 review on
  debug_operations.rs:245-251.
- Child approvals (core/engine/handle.rs, child_host.rs, tools/subagent/*):
  keep ApprovalDecider through the child Approved and Denied outcomes, so
  receipts attribute the actual decider. A timeout stays a denial,
  attributed to Host. Consumers migrated. Managed Computer Use consent
  authority is unchanged.
- Plan handoff (tui/plan_handoff.rs, tui/app.rs, tui/app/init.rs,
  tui/ui/*, docs/GUIDE.md, docs/MODES.md, docs/zh_hans/GUIDE.md):
  successful nonempty Plan prose offers handoff and queues its exact text
  once. Failed preparation rolls back only an unchanged owned seed.
  Refs codewhale-hq#6902. Addresses the P2 review on plan_handoff.rs:48.
- OCR and CI (tools/image_ocr/host_tests.rs, .config/nextest.toml,
  .github/workflows/ci.yml): the cancellation fixture waits on an explicit
  descendant latch and observes cleanup, instead of racing a one-second
  write. OCR and PDF host tests join the max-threads-1 group. macOS test
  compilation sets CARGO_PROFILE_TEST_DEBUG=0 before cache setup.

Local evidence (macOS arm64, debug, library tests; not CI):

- cargo test -p codewhale-tui --lib --locked --no-run -j 2: exit 0,
  0 errors, one linker note.
- With RUST_MIN_STACK=8388608 (the workflow value in ci.yml):
  plan_handoff 16 passed, 0 failed; core::engine::tests::child_host
  19 passed, 0 failed.
- With the 2 MiB default test-thread stack: plan_handoff 15 of 16 and
  child_host 10 of 19 pass. The rest overflow the stack in debug builds.
  Measured stack need is 4 to 8 MiB. All nine child_host tests exist at
  HEAD and passed HEAD's CI on Linux and macOS.
- debug_mutation_host_tests 38 of 38; the approval routing test
  an_agent_approval_answer_reaches_the_agent_while_the_engine_is_busy
  1 of 1; image_ocr::host_tests 8 of 8, including
  real_host_ocr_cancellation_bounds_parser_tree_and_never_replays.
- cargo fmt -p codewhale-tui -- --check and git diff --check: clean.

Not run here: the full 19k-test suite, nextest, Windows, doctests, and
hosted macOS timing. The previous macOS job exceeded 2h45m.

Limits, as reviewed: checkpoint admission is queued acceptance, not disk
durability, and an accepted queued checkpoint cannot be recalled. Pending
Plan ownership is ephemeral. No restart recovery or permanent custody is
claimed.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Merge origin/feat/tui-native-pet-mode (fdae4dd) into the 0.10.2
candidate (1bddf50) on integrate/0.10.2-pet. The two changelogs
conflicted: the pet note now sits under 0.10.2 Added, cites the shared
components as codewhale-ratatui codewhale-hq#23 and codewhale-hq#25, and the 0.10.2 intro names
the pet view. Everything else auto-merged.

The vendored ratatui kit already carries upstream 6f0ab44 (codewhale-hq#25) for the
pet path: lib.rs, whale_motion/ and assets/whale-motion/ are byte-identical.
It differs from upstream in three deliberate ways: the 0.10.2 Terminal
panel added by 1c82c93 (components/workbar.rs, gallery/workbar.rs,
gallery/showcase.rs); upstream's gallery avatar showcase, which the TUI pet
path does not use; and a doc line plus module order in avatar_sprite.rs and
components/mod.rs. The vendor manifest (UPSTREAM.json) still records
2ad795b3, so the kit owner should re-sync it or record these differences.

Not yet compiled, tested or installed. Build, focused tests, the npm gate
and installed-binary checks follow on this commit.

Refs codewhale-hq#6920 codewhale-hq#6907 SHA-6533 SHA-6645

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ld-approval assertion)

Hosted run 37865763270 on 1bddf50 failed for exactly two reasons, read from
the raw job logs:

- Lint (job 113612126883): the only error is clippy::type_complexity at
  crates/tui/src/commands/contract/debug_operations.rs:460, the
  `&dyn Fn(&str) -> Result<Option<bool>, Box<DebugUndoOutcome>>` parameter of
  plan_undo_step. Name the shared result type once (RequestPositionResult) and
  use it for that parameter and for request_is_last's return type. No behavior
  change.
- Test (ubuntu, windows, macos): on macOS (job 113612126935) the summary is
  "19122 tests run: 19121 passed (2 slow), 1 failed, 33 skipped". The single
  failure is tools::subagent::tests::child_permission_gate::
  ask_with_a_prompting_host_raises_the_prompt_and_honours_the_answer at
  tests.rs:24510. The case already matched ToolError::PermissionDenied, then
  asserted that err.to_string() starts with "Tool 'bash' denied by user — ".
  ToolError's Display prefixes "Failed to authorize tool execution: "
  (crates/tools/src/lib.rs:78), so the string never matched. Assert on the
  typed message instead, as the sibling case at tests.rs:25375 does. The
  approval ownership, outcome, duplicate-resolution and durable-receipt
  assertions are unchanged, and production permission behavior is unchanged.

Evidence level: hosted-log diagnosis only. These two paths were not compiled or
run locally (the machine is under memory pressure), so the hosted run on this
exact head is the qualifying evidence. Refs codewhale-hq#6907.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ntainer skills

Founder direction, 2026-10-08: agents do not write new unit or integration
tests unless explicitly instructed, and do not add new code comments.
AGENTS.md now says so in "Code, migrations, and evidence", replacing "add
or adjust tests to cover what was built" and "add tests when they cheaply
protect"; existing tests still gate and are updated or deleted when a
change makes them wrong. The two rules that required new code text follow:
a new layer names its predecessor in the commit message instead of a
module doc, and a moved contract moves its existing guard test.

Maintainer skills (docs/skills):
- cw-slice, cw-gates: locale guidance moved to
  crates/localization/locales (the crates/tui/locales path no longer
  exists); test/comment wording matches AGENTS.md; cw-gates points to
  AGENTS.md "Claiming a test passed" instead of restating it.
- cw-land: landing shapes match AGENTS.md working rules (existing
  checkout first, PR for three-OS CI, worktree only for conflicting lanes,
  integration branch for unpushable contributor conflicts); removed the
  reference to scripts/worktree-gc.sh, which has never existed on main.
- cw-orient: do not create a worktree just because another agent shares
  the checkout.
- README: lists the six-stage maintainer loop.

Evidence: every repository path the skills reference was checked against
origin/main with git cat-file; the two stale ones above were the only
misses. Docs only; no code or tests changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Founder, 2026-10-08: more PRs means more CI and slower work. Verified
maintainer slices land directly on main and push; contributor PRs still
merge as PRs. cw-land drops the maintainer-PR landing shape to match.
Docs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- OAuth: stop sending `originator` on the ChatGPT authorize URL. The recorded
  initial probes (chatgpt-authorize-probes.json) returned 302 to login for the
  documented dynamic-client parameters and 400 invalid_authorize_request when
  originator=codewhale was added. Remove the unused constant. The stored entry
  field stays Option<String>, so existing credential files still parse. The
  URL test now asserts the documented parameters and PKCE state.
- Pet owner: receive viewer activity through the existing worker channel,
  advance and checkpoint the pet world only while a viewer, producer or audio
  lease is live, and wait to the 30 fps frame deadline instead of polling every
  2 ms. Idle CPU on this candidate is not yet measured.
- Child permission test: match the typed ToolError::PermissionDenied message.
  The CI failure was an expectation mismatch with the Display prefix; approval
  behaviour is unchanged.
- Debug operations: name the request-position result alias (type_complexity).
- Config-panel goldens at 40, 80 and 120 columns, and the pet feature summary,
  shortened to fit the 120-character registry limit.
- Changelogs record the pet and OAuth behaviour.

Tested tree: 80e1d2a plus diff sha256 7de16933daa2b39bf0ab93239535bd1a9c9541ec15d3c8e39de0c64617cdd6a6.
  scripts/dev-cargo.sh test -p codewhale-tui --lib --locked -j 1 -- <focused filters> --test-threads=1
  test result: ok. 286 passed; 0 failed; 15159 filtered out (exit 0)
  Includes tui::views::tests::config_panel_golden_at_eighty_and_one_twenty,
  tools::subagent::tests::child_permission_gate::ask_with_a_prompting_host_raises_the_prompt_and_honours_the_answer
  and the oauth authorize-URL tests.

After that run, the two comment lines the continuation had added to
subagent/tests.rs were deleted (the no-new-comments rule), so the committed
diff sha256 is 127ad37e0b658fa26d6db12dcc97e0c22c20009e940c3257525db10016cdf2b6.
That removes comments only. rustfmt --edition 2024 --check passes on the four
touched Rust files (exit 0).

Not yet run on this commit: feature_registry, npm test, npm run check:web,
release build, installed-binary checks.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
origin/main adds only maintainer docs and skills (AGENTS.md, docs/skills/*).
No code changes, no conflicts with the candidate.
…After

Google's quota errors state the wait in the body (RetryInfo.retryDelay, or
"Please retry in 33.2s"), not in a Retry-After header. The client read only
the header, so a per-minute window was retried after the default 1-4 s
backoff and the turn failed. The body hint is now used when the header is
absent, capped at 120 s so a daily-quota reset is not slept through.

Also: the non-streaming translation path treats a `stop` whose reported
output equals the requested ceiling as a length stop, so it fails loudly
instead of returning a cut-off answer. The turn-loop path already had this
(WIP commit 3ae9013).

Refs codewhale-hq#6923 (SHA-6877), Refs codewhale-hq#6889 (SHA-6853, item 3).

Evidence (tree 222848b plus these two files):
- rustfmt --edition 2024 --check on both files: exit 0
- cargo test -p codewhale-tui --lib -- llm_client retry_after rate_limit
  translate output_ceiling finish_reason --test-threads=1:
  test result: ok. 105 passed; 0 failed; 15340 filtered out (exit 0)
- cargo test -p codewhale-models: test result: ok. 40 passed; 0 failed
- cargo test -p codewhale-tui --test integration exec_turn_usage:
  test result: ok. 11 passed; 0 failed (exit 0), on the debug CLI built from
  this tree
- Local mock journey on the debug binary, no provider spend:
  hint retryDelay 2s: client waited 2.00 s; mock requests 2.006 s apart;
  exit 0; stdout "ok".
  no-hint control: waited 0.92 s.
  600 s hint: not slept through; waited 0.99 s (cap applies).
- feature_registry: test result: ok. 6 passed (run on the pre-edit tree;
  these two files changed during that run and do not touch the registry).

No new tests added.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…t in the cwd

Contributor @Lstarsky0; commit authorship is preserved. Approved on GitHub
by Hmbown. Merged into local main only; not pushed.

Refs codewhale-hq#6513.
…acks up to date

Contributor @Lstarsky0; commit authorship is preserved. Approved on GitHub
by Hmbown. Merged into local main only; not pushed.
…#6553 and codewhale-hq#6554 audit fixes on the 0.10.2 line

- Workflow: the terminal receipt goes through send_terminal_event instead of a
  silent try_send, so a full completion inbox no longer drops it (codewhale-hq#6560 D02-06).
- Heavy-command classifier: `cargo +toolchain build` is heavy, because the
  toolchain override is skipped when the subcommand is found. scripts/dev-test.sh
  and dev-cargo.sh are heavy too, also when run through a wrapper (codewhale-hq#6560 D01-11).
- Parent mail: each agent's queue is capped at 64 messages. A send past the cap
  returns an error instead of growing the queue without bound (codewhale-hq#6560 D03).
- Subagent state: the store is published through utils::write_atomic (fsync of
  the file and its directory, then rename) instead of fs::write plus rename.
  The in-process publish-sequence guard is kept (codewhale-hq#6555 D02-01).
- Image search: the non-adapter seed and result reads go through the capped
  body reader instead of unbounded .text() (codewhale-hq#6554 D03-07).

Evidence (tree 222848b plus the merged PRs and this change):
- rustfmt --edition 2024 --check on the four files: exit 0
- cargo test -p codewhale-tui --lib --locked -j 1 -- resource_admission
  agent_message_queues persist_and_reload test_persist_and_reload
  headless_worker_records_persist workflow --test-threads=1:
  test result: ok. 259 passed; 0 failed; 15186 filtered out (exit 0)
- No existing test covers the parent-mail cap or the image-search read caps.
  Both compile and are exercised only indirectly.

Refs codewhale-hq#6560, codewhale-hq#6555, codewhale-hq#6553, codewhale-hq#6554 (SHA-6776, SHA-6771, SHA-6769, SHA-6770).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…e is missing

- check-versions.sh: a missing providers-export golden or config-policy status
  fixture is an error, not a silent pass (codewhale-hq#6559 T04).
- preflight.sh: with no base to compare against, the feature release-note
  receipt step is recorded as a failure, not "skipped" (codewhale-hq#6559).

Evidence:
- bash -n on both scripts: ok.
- scripts/release/check-versions.sh on this tree: exit 1 before and after, with
  identical output. The failure predates this change. Feature commit
  5e2e59e (codewhale-hq#6907) has no release-note receipt in CHANGELOG.md, and that is
  not fixed here.
- Both fixtures exist in this tree, so the new branches are not reached here.
  No existing test covers the missing-fixture case.

Refs codewhale-hq#6559 (SHA-6775).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…low lands without a restart

Contributor commits by @SparkofSpike are kept with their authorship. Approved on
GitHub by Hmbown. Merged into local main; verification of the merged tree is
pending.
…tone

Contributor commits by @SparkofSpike are kept with their authorship. Approved on
GitHub by Hmbown. The one conflict was two test blocks appended at the same place
in core/engine/tests/test_cases_12.rs; both are kept, and the file is rustfmt-clean.
Merged into local main; verification of the merged tree is pending.
Use one canonical main checkout and one frozen release candidate. Focused local receipts precede exact-SHA hosted qualification and explicit publication approval. Preserve contributor PR integration and recovery boundaries, align registry guard guidance, and repair missing SparkofSpike credit and pet feature receipt that caused main Version drift failure.

Validation: npm test 1312 passed, 0 failed, 7 skipped; npm run check:web passed. Existing changelog tests 6 passed; source/install docs 24 topics passed; version pins and 6 linked feature receipts passed. Workflow contracts passed 154 consumed paths, 11 event cases x 12 steps, 13 hermetic checks and 7-target/34-asset candidate contract. Registry guard bash syntax and scoped whitespace passed. Native Claude/catalog work remains unstaged and unqualified; this commit contains only 9 owned release/guidance/credit paths.
Production requests failed with HTTP500 after an otherwise successful
OpenNext upload because Next16.4 loads preview-props.json at startup.
Update the adapter to1.20.9/aws4.1.8 and apply the exact reviewed upstream
glob correction before builds, guarded by original and patched SHA256.
Preserve real draft-mode keys; unknown adapter bytes fail before mutation.
Patch source: opennextjs/opennextjs-cloudflare PR1356 at
346d5fdd1611ff2e601de3627486397e75906730.

Verification: npm test1312pass/0fail/7skip; npm run check:web passed
(772web tests in74files,894static pages,0lint errors/1existing warning).
Full Cloudflare bundle build passed. Local generated Worker /api/facts
and /en both return200 and the updated homepage title is present.
Patch rerun is idempotent; independent source review and diff check pass.
Prior deployment37894483203 failed public verification; exact new
hosted deployment/public readback remains pending.
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.6.1 to 26.6.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Bumps [react](https://github.com/react/react/tree/HEAD/packages/react) and [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react). These dependencies needed to be updated together.

Updates `react` from 19.2.8 to 19.3.0
- [Release notes](https://github.com/react/react/releases)
- [Changelog](https://github.com/react/react/blob/main/CHANGELOG.md)
- [Commits](https://github.com/react/react/commits/v19.3.0/packages/react)

Updates `@types/react` from 19.2.14 to 19.3.0
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-version: 19.3.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
- dependency-name: react
  dependency-version: 19.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
0.10.2: Terminal dock, shell wait controls, recovery and contributor fixes
Focus Fleet and Providers on original captured panel rows with full-terminal disclosure. Preserve all six source capture hashes and cell data. Maintain readable 13px minimum terminal text with contained, focusable horizontal scrolling on phones. Use shared English/Chinese captions and install notes; add Cargo and pin the Windows package identifier.

Validation: npm test 1312 passed, 0 failed, 7 skipped; npm run check:web passed with 894/894 pages, zero lint errors and one existing unused-spawnSync warning. Existing mechanical design detector returned zero findings. Independent eight-path/capture/screenshot review found no material blocker. Desktop, 390px mobile and Chinese preview inspected; Cargo copy exactly matched the full command; provider region keyboard scrolling moved 40px with focus retained. No new tests. Public deployment is a separate receipt.

Refs codewhale-hq#6831
Add selected Claude subscription mode through the existing owned PKCE/grant lifecycle, with fixed HTTPS origin, generation fencing, bounded callback, redacted failure output and no API-key billing fallback. Preserve bearer headers per request. Apply shared models.dev completion at all three provider-lake seams; remove duplicate gateway enrichment and retain source/provenance markers. Pin protocol serde_json preserve_order for the existing standalone serialization golden.

Validation: one serialized hermetic warm native batch with Cargo timings passed CLI28, config148, protocol1 and TUI1340: total1517 passed,0 failed,5 ignored. Exact stable serialized-byte test passed. Compilation24m46s; total1605.03s; maximum RSS6829604864 bytes. CLI/TUI linker emitted an oversized __eh_frame warning. Rust formatting and whitespace pass. npm test1312 passed,0 failed,7 skipped; check:web passed894/894 pages on these source inputs. Independent bounded auth/catalog reviews performed; no new tests or code comments.

This is the verified source checkpoint before crate extraction. Full fourteen-item models.dev repair remains open, including rich pricing, endpoint-preserving row identity and remaining admission/receipt consumers. Actual Codewhale Claude login and provider inference, installed acceptance, hosted release CI and publication remain unqualified. The Haiku CLI worker receipt is separate.

Refs codewhale-hq#6932 and codewhale-hq#6396
Refresh the vendored Computer Use bundle to codewhale-cu-plugin@ef0cd8a1d69e
(PR codewhale-hq#15): keystrokes and pointer events follow the window that receives them
and fail closed without an owner; Linux gains AT-SPI focus/get_value/
select_text, popup-open key refusal, settle before Return, app_ref.name
windows and captures, server-owned ffmpeg x11grab recording, kill_app and
list_sessions, and the refusal/doc/skill updates. Adds the input-owner and
linux-desktop suites to the vendored tree; the three deliberate Core variants
(package.json, README.md, tests/manifest.test.mjs) are unchanged.

Evidence: node --test tests/*.test.mjs 470 tests (450 pass, 0 fail, 20
platform-gated skips); check-cu-sync reports marketplace == upstream
@ ef0cd8a1d69e and the Core tree matching the marketplace runtime set. Only
tests/ files are new and no runtime file name moved, so the builtin
embed-list test and Rust sources are unaffected. No exhaustive Rust suite,
installed app or release qualification claim.
Hmbown and others added 30 commits October 11, 2026 20:28
…/nixpkgs-7a0f122

build(deps): bump nixpkgs from `6774f7b` to `7a0f122`
…/fenix-e659899

build(deps): bump fenix from `48b35ac` to `e659899`
…_and_yarn/web/types/node-26.6.3

chore(deps-dev): bump @types/node from 26.6.1 to 26.6.4 in /web
…_and_yarn/web/gt-2.22.4

build(deps-dev): bump gt from 2.17.2 to 2.22.4 in /web
React 19.3 requires its matching renderer; the PR still pinned react-dom
19.2.8 and its build rejected the mixed versions. Apply the exact cloud
handoff patch: react-dom 19.3.0 and scheduler 0.28.0, with the reviewed
registry integrity values. No unrelated dependency changes.

Founder authorized this existing Dependabot branch update. The canonical
checkout remains on main; a separate index applies only the two web
manifest files against the PR head.

Verification: git apply --cached --check passed at 082b326; inspected
10 insertions/10 deletions in exactly two files. Reused cloud patch receipt:
74 test files, 772 passed; 0 failed, lint, TypeScript and build passed.
Those are prior cloud results, not a fresh local run of this branch. The
unchanged local-main JavaScript/web gate separately passed 1312 tests;
0 failed; 7 skipped. Hosted CI on this new commit remains required.

Refs codewhale-hq#6811

Signed-off-by: Hunter Bown <hmbown@gmail.com>
Resolve the adjacent dependency manifest/lockfile changes semantically:
keep main types/node 26.6.4 and gt 2.22.4, and retain the PR React,
react-dom and types/react 19.3 updates with scheduler 0.28.0.

The preceding f72b63a web workflow passed 7 dependency checks and
74 files / 772 Vitest tests, 0 failed; facts/docs, TypeScript and build
passed. ESLint reported 0 errors and one existing warning. This merge
requires a fresh exact-head web receipt; no Rust source changed.

Refs codewhale-hq#6811

Signed-off-by: Hunter Bown <hmbown@gmail.com>
A turn at or above a catalog row's first context tier now reports pricing
tier unavailable rather than silently using its base rate. Unknown tier
kinds also fail closed. Both catalog consumers reuse context_tiers() and
the existing localized UnrepresentedTier reason; no new prompt bytes or
session events are introduced.

Continues verified cloud slice 2cb33c4 from PR codewhale-hq#6965. This is S8 only;
the founder kept the remaining codewhale-hq#6396 scope in v0.10.2.

Verification on canonical main 506cdf3 plus the recovered source:
- macOS focused pricing/cost_status nextest:
  Summary: 130 tests run: 130 passed, 15203 skipped (0 failed).
- npm test: 1312 passed; 0 failed; 7 skipped.
- npm run check:web: exit 0 (facts, docs, lint, types and build).
- cargo fmt --all -- --check and changelog synchronization: exit 0.
- No new tests. The bundled catalog has no tiered row; the new boundary
  remains source-reviewed rather than directly exercised by a fixture.

Refs codewhale-hq#6396
Refs CW-6787

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj
Direct callers now honor CODEWHALE_BUILD_LOCK_HELD and the same six
falsey CODEWHALE_BUILD_LOCK values as dev-cache.sh. This prevents nested
calls from waiting forever on their parent's lock and preserves child
exit statuses. The locked path is unchanged.

Continues cloud slice e43d86e from PR codewhale-hq#6965.

Local disposable-lock probes: 8 passed; 0 failed (six falsey bypasses,
held-lock bypass, and a real nested invocation). Exit 7 is preserved.
The normal machine lock serialized the macOS tests and Windows checks.

Shared unchanged-source gate: npm test 1312 passed; 0 failed; 7 skipped;
npm run check:web exited 0. No new tests or comments.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj
Windows now uses the existing lease-first owner admission/recovery path.
A captured owner publication is replaced only after matching the selected
principal, store, scope, config and endpoint and proving its recorded
process is dead. Access-denied or uncertain liveness refuses recovery;
creation time rejects a reused PID. Exact-handle POSIX deletion retires
the old receipt while readers retain their handles.

Preserve the Unix selection rule exactly. Windows blocking mutations and
receipt cleanup retain the actual Runtime manager across cancellation.
The founder-requested crash/reentry test kills owner A, preserves its
publication, and proves owner B serves A's saved thread while a CLI
attaches to B. Keep Windows system variables and fixture temp paths,
drain stdout after readiness, and reuse the shared TLS client builder.
Register the test in nextest's spawns-binaries group. Update both Runtime
API docs and synchronize the 0.10.2 changelog.

Completes and consolidates cloud WIP ec242fe from PR codewhale-hq#6965 with the
lease-custody and harness fixes above. Source was frozen throughout the
checks; retained SHA-256 inventory shows no source changes.

Local macOS receipts (exit 0):
- owner_crash_reentry: Summary ... 1 tests run: 1 passed, 0 skipped.
- daemon_socket owner_bootstrap: 6 passed; 0 failed; 9 skipped.
- existing cancellation helper test: 1 passed; 0 failed; 172 skipped.
- TUI owner_bootstrap: 7 passed; 0 failed; 15326 skipped.
- First harness run failed (0 passed; 1 failed) because it bypassed the
  shared TLS provider initializer; corrected run above passed.
- Native linking emitted one existing oversized __eh_frame warning;
  these are passing tests, not a zero-warning native build claim.

Windows GNU cross-checks with RUSTFLAGS=-Dwarnings, all features:
- config + app-server check and clippy -- -D warnings: exit 0.
- TUI + CLI check --all-targets: exit 0, 8m 04s.
- brew mingw-w64 could not install because of a pre-existing invalid
  llvm@21 keg. Used installed Zig0.16 wrappers. The first TUI/CLI check
  failed in dependency SIMD C++; the passing retry disabled only its
  ICELAKE implementation through target-specific CXXFLAGS. No dependency
  or Engine source was modified for that tooling workaround.

Shared unchanged-source gate: npm test 1312 passed; 0 failed; 7 skipped;
npm run check:web exit 0. fmt, blocking-call budget (538 sites), dead-code
budget (221 suppressions) and changelog synchronization passed.

The hosted Test (windows-latest) / Run tests PASS for
codewhale-cli::owner_crash_reentry on the candidate SHA is still required
before tagging. Linux execution and native Windows app qualification
remain separate receipts. No tag, release or deployment is authorized.

Refs codewhale-hq#6915
Refs CW-7079

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BsXVvReYca2UxzimQtj7Mj
Put owner_crash_reentry in its own override of the same spawns-binaries
group. This is equivalent to the union filter for process limits, while
preserving the existing release-workflow assertion for binary(integration).

The first hosted Version drift check on 35498ef failed with
"nextest must bound the integration binary". This fixes that guard failure
without changing a test or weakening the group limit.

Verification:
- release-workflows.test.js: 13 hermetic checks passed; 11 wrapper event
  cases x 12 steps; workflow contracts passed.
- check-versions.sh --range-audit-advisory: exit 0, including
  "test result: 10 passed; 0 failed" for dev-test wrapper checks.
- The compiled macOS crash test remains unchanged (1 passed; 0 failed);
  hosted Windows execution on the final candidate still gates the tag.

Refs codewhale-hq#6915
Refs CW-7079
Document Ctrl+] as the portable pet view chord, distinguish the kitty
keyboard-protocol Tab chords, and explain Terminal.app's tab switching.
The key reference now describes pet view cycling as well as the work dock.
Keep the reviewed cove fallback correction in both languages.

Checked against the current main view routing and capability fallback;
owned Markdown whitespace check passed. Documentation-only slice reuses
the unchanged-source npm gate (1312 passed; 0 failed; 7 skipped and
check:web exit 0); no new tests or comments.

Verification clarification for 25b7fcc: check-versions.sh reports
version/release-note consistency; it does not include wrapper tests. The
workflow guard script separately passed 13 hermetic checks and 11 event
cases x 12 steps. with-hermetic-test-home.test.sh was then run separately
and produced the real line "test result: 10 passed; 0 failed". Logs are
retained with the continuation receipts. The previous commit's wording
that the version checker included that test was incorrect.

Refs codewhale-hq#6960
Refs codewhale-hq#6915
Inspector F8/F9 now go through the same command/revive path as the main
view. All macOS F-key hints retain fn+ in ASCII-safe mode. Keep the
terminal dot alpha so studio brightness and doze dimming reach the
existing accessible ink blend; mirror upstream ratatui 13ee6c7 exactly.

Normal companion startup and short frame stalls no longer tell the user
to start a service or select a source. Only a real failure or another
selected session produces that notice. Resolve and decode the current
avatar sample before painting its notice, reusing the same sprite path
as the actual paint and deleting the previous-frame fallback flag.

Use typed MessageIds and 10 translated fragments in all 15 complete locale
packs. Correct pixel opt-in/cell-size/fallback documentation, macOS help
hint scope, and synchronized release notes. The portable-chord and work-
dock documentation already landed in dde71a1.

Evidence on frozen dde71a1 + 32 owned-file hashes:
- localization: Summary ... 54 tests run: 54 passed, 0 skipped.
- focused pet/key routing/installer/marketplace/bootstrap: 129 passed;
  0 failed; 15204 skipped.
- real owner_crash_reentry: 1 passed; 0 failed.
- clippy TUI/CLI all-features/all-targets with exact existing CI flags:
  exit 0, 8m47. The first plain invocation failed on 21 lib / 23 lib-test
  unchanged baseline diagnostics; CI already permits uninlined_format_args,
  too_many_arguments and unnecessary_map_or. No new lint allowance.
- Native linking reported one oversized __eh_frame warning, retained.
- fmt, blocking budget 538 sites, dead-code budget 221 suppressions,
  changelog sync and owned whitespace checks passed.
- Shared unchanged JS gate: npm test 1312 passed; 0 failed; 7 skipped;
  check:web exit 0. No new tests or code comments.
- Upstream ratatui 13ee6c7 hosted CI is green on 3 OSes, including gallery,
  package, MSRV and clippy. No crate publication.

The frozen batch included an adjacent Runtime Skills bootstrap hook.
Its fresh/custom runtime inspections passed, but source review found
that legacy directory fallback is not writable-owned. Those three paths
are deliberately unlanded pending the ownership correction; this commit
contains only the verified pet partition and does not qualify that hook.

The four peer pet paths were reviewed and integrated from the coordinated
App launch chat. No Engine/prompt/store/execution authority is added.

Refs codewhale-hq#6960
Use the existing bundled installer after the Runtime owner opens its store, and admit writes only to the owned default profile. Both interactive and headless startup preserve explicit skills_dir settings and legacy ~/.deepseek/skills content. Read discovery still uses the existing resolver. Pin the first-party catalogue to the green marketplace e3e61273f2ae31ec205e3c7f21b34dd73954d6b2.

Verified source: 6f0df69 plus these four files; frozen hashes matched after the build. Focused macOS checks: 46 passed; 0 failed. Actual nextest receipts:
  Summary [   5.063s] 45 tests run: 45 passed, 15288 skipped
  Summary [   8.059s] 1 test run: 1 passed, 0 skipped
Both commands exited 0. TUI production clippy passed with the existing CI warning policy (-D warnings and its three existing allowances), fmt and diff checks passed. The existing oversized __eh_frame linker warning remains.

Real private Runtime API inspections: 4 passed; 0 failed. Fresh explicit/default owned profiles expose all 47 bundled skills. Custom and legacy directories retain their original bytes and expose no newly seeded bundles. Corrected executable SHA-256: 0f576488941453c9e96d4799d3ba7c6025c0dbe95d118ad72a83793a0b780633.

Unchanged JavaScript gate reused: npm test 1312 passed; 0 failed; 7 skipped, npm run check:web exit 0. Marketplace generator and --check passed. No provider or plugin execution, packaged/native App acceptance, or concurrent plugin mutation qualification is claimed.

Refs CW-7177; CW-7072
…_and_yarn/web/multi-7f19880bf6

web: pair React and React DOM 19.3
Accept and type-check the optional bash description without forwarding it to execution. Keep the command header unchanged and render a localized, escaped, bounded note before every compact-card return. Remove the display-only field from exact shell approval hashes and Auto-Review input; credential masking is measured before removal. The original model value remains in append-only ToolUse history and exports.

New sessions pin one additional schema property: exactly112 serialized bytes and28 estimated tokens on each of six active/full Plan/Act/Operate catalogs. Tool identities, counts and system prompt are unchanged; all55 contract metrics match the re-locked budget. Existing prompt goldens change only that property and their sizes/hashes. Existing ceiling expectations and ExecCell literals were updated; no new tests or code comments.

Native macOS checks:423 passed;0 failed. Actual receipts:
  Summary [   8.507s] 420 tests run: 420 passed, 14897 skipped
  test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 15316 filtered out; finished in 3.68s
  Summary [   0.016s] 2 tests run: 2 passed, 0 skipped
Clippy passed for all TUI targets with the existing CI warning policy; fmt/diff and blocking/dead-code budgets passed. The existing oversized __eh_frame native linker warning remains.

Actual native PTY render/export inspections:2 passed;0 failed at80 and40 columns. Both show run done · pwd and note: show working dir together; both Markdown exports retain the original command and description JSON. Inspections use only a scripted loopback provider; no real provider, account sign-in, physical Terminal.app or native App acceptance is claimed. The inspection launcher was corrected to acquire its own controlling terminal, separate bracketed paste from Enter, and close its PTY master before waiting for exit; these are artifact-only harness repairs, not product changes.

Verified native inputs:2c550fa8c595a747f343133e73dc2f729504dcde plus the frozen owned source, reconciled with the web-only green React merge05ede26b5a unchanged. Executable SHA256ca0a1ec31218c533539e40c008643f01723627e7bed836a8ed2d08952f672c8a. Shared gate rerun on the reconciled tree: npm test1312 passed;0 failed;7 skipped, npm run check:web exit0 (one existing lint warning). Separate web-only data preparation passed this same gate and remains unstaged for its own slice.

Refs CW-7386; codewhale-hq/codewhale-app#3193
/constitution opens the manager after the existing asynchronous stored-account check and reports when the local document is standby. GET /v1/constitution and preview return the same renderer body, with provenance kept in source/revision. Doctor labels disabled local guidance inactive and explicitly leaves the account unprobed. The context inspector reads the recorded personal constitution as a session message outside the stable system prefix. Eight typed labels cover all15 complete locale packs.

Move command metadata and collision admission into the new command_catalog leaf. The commands owner implements its snapshot port; Runtime and tui_help no longer import commands. Preserve canonical-name-before-alias shadowing, hidden/extension filtering, explicit missing-catalog failures and startup installation. UI dispatch stays with its existing owner. Runtime catalogue and tool help remain byte compatible in actual CLI execution.

Frozen source: base9ebbb063da plus45 owned native/doc paths, q13-x2-frozen-source.json. Existing focused TUI run: "Summary [  94.180s] 231 tests run: 230 passed (1 slow), 1 failed, 15085 skipped". The failure was the extracted HTTP fixture omitting profile; its existing stub was repaired and rerun: "Summary [   0.152s] 1 test run: 1 passed, 15315 skipped". Reuse the unchanged230 passing cases. Config: "Summary [   0.026s] 20 tests run: 20 passed, 780 skipped". Localisation: "Summary [   2.336s] 54 tests run: 54 passed, 0 skipped". Combined305 passed;0 outstanding failed. Initial E0308 doctor borrow compile failure retained and fixed before tests.

CLI build passed10m57; config/TUI all-targets clippy passed8m28 with the existing three CI allowances and no new ones. Pre-existing linker __eh_frame size warning persists. fmt, diff and blocking/dead-code/command-boundary/module-graph checks pass: production edges11->0; test edges32->30; 118 closure modules; largest SCC98->82. No model prefix or tool schema changes; existing prefix golden check passed in the focused run.

Actual private Runtime inspection:111 catalogue rows including3 user rows byte equal before/after, with canonical and alias collision receipts; GET/preview local guidance377bytes equal; bundled-choice doctor reports inactive/account_profile not_probed. Existing headless --auto tool_search -> tui_help execution before/after returned identical help (2 passed;0 failed). Native macOS PTY manager/inspector:2 rendered surfaces passed;0 failed. The first fixture explicitly deferred onboarding; the corrected checkpoint fixture captured active law in the provider request and its session-message inspector row. The final selector was evaluated against the actual MessageId text in the preserved frame. Actual account sign-in was not run. Immutable CLI SHA25662367c5a82d97fa4167f8592e8a27a759cbf42fe595bfe9c2097d173c1b360f9.

Root npm gate on the same native/doc inputs plus the separately owned website batch:1312 passed;0 failed;7 skipped, and check:web passed. No new tests or code comments; existing metadata assertions moved/adapted. Known limits: manager checks a stored session, not a successful profile fetch; doctor does not probe the account; the separate /context report can still label local guidance for signed-in users. Local tests and scripted-provider inspection do not establish real account, native App, physical terminal or final-candidate hosted Windows acceptance.

Refs CW-7387
Refs CW-7162
Refs codewhale-hq#6940
Make the 72 registry capabilities findable in the catalogue and docs search. Derive status, surface badges and documentation links through the existing facts pipeline. Add /features.json, 26 raw Markdown topic routes and /llms-full.txt; align navigation, sitemap and explicit crawler policy. Release JSON-LD uses only stable rows with a declared since version at or before the published install version.

Put the coding-agent category in home metadata and identity across the actual 18 routed locale packs. Correct session receipts, default Auto routing, ACP and read-only MCP server claims; mark Computer Use preview. Document request preview, LSP, execution policy, skills and the reviewed Claude Code import subset. Ratatui component indexing becomes English-only; its hub retains existing locale coverage.

Root gate on base9ebbb063da plus 137 frozen web paths and the separately frozen Q13/X2 native/doc inputs: 1,312 passed; 0 failed; 7 skipped (wrapper101, SDK19, extension host413, security7, Vitest772). Actual receipt lines:
"# pass 413"
"# fail 0"
"# skipped 7"
"Tests  772 passed (772)"

npm ci, facts/latest-release/docs/local GT catalogs, lint, tsc and production build passed. One existing lint warning. Final LLMS-only copy correction: "Tests  3 passed (3)"; fresh production build passed. The existing buildFacts comment was moved back to its owner without an executable change.

Local browser inspection: 36 pages at 375/768/1280 in en/zh/ja/ar, each captured full-page and viewport. All72 feature queries and26 raw Markdown routes passed; every raw topic appears in the full export; zero page errors and no horizontal overflow. The harness disables speculative link prefetch and answers local telemetry with204. This does not establish deployed-site, crawler indexing, WAF or Search Console acceptance. No new tests or code comments; obsolete existing assertions updated. No deployment.
Move the existing Fleet editor key/pin/save assertions into the UI owner's existing save test. The runtime test seeds the same persisted ReviewerRoute/fixture-review-model/off/non-shortlist member before ordinary reload and type-only admission. Keep all prior assertions and add no test functions or code comments. Correct the planned test-support owner to the existing Runtime feature.

Before on the frozen Q13/X2 test binary: "test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 15315 filtered out; finished in 6.36s" and "test result: ok. 16 passed; 0 failed; 0 ignored; 0 measured; 15300 filtered out; finished in 17.12s". After on frozen4path batch at dd38999: "Summary [   0.941s] 17 tests run: 17 passed, 15299 skipped". Initial unused load_fleet_in_scope import compile failure retained and removed before tests. No assertions weakened.

Runtime boundary production0/test30/uilib1 -> production0/test25/uilib0; late1/doc0 unchanged. fmt/diff/blocking/dead-code/module-graph checks pass. Reuse unchanged root npm1312 passed;0 failed;7 skipped and check:web evidence from the immediately preceding source slice. Hosted Q12 schema/event expectations and asset prose need their separate follow-up; this test-only relocation changes no production or model-facing bytes.

Refs CW-7160
Refs codewhale-hq#6938
Hosted main CI38221033360 found the existing read-only Bash schema assertion missing description and all6 event goldens retaining the previous schema/prefix hashes. Add the optional field to that existing assertion and re-record the existing event family. Review every changed JSON path: only the Bash description property, +112 serialized payload bytes and the derived prefix/catalog hashes changed. Turn outcomes, tool execution and approval semantics are unchanged.

Correct the current changelog marketplace pin to e3e61273f2ae31ec205e3c7f21b34dd73954d6b2 and the actual19 catalogue entries, including codewhale-skills; sync the TUI changelog. The previous claim still named9b5f9d6 after the separate asset sync.

Existing focused native gate: "Summary [   0.312s] 2 tests run: 2 passed, 15314 skipped"; events contain6 cases. Bundled-plugin claim checker passes. Source9-path hashes unchanged; fmt/diff pass. Fresh required root npm gate1312 passed;0 failed;7 skipped; check:web including facts/latest release/docs/locale assets/lint/types/production build passed (one existing lint warning). No new tests or code comments. Complete failed hosted log and exact golden-diff review retained under artifacts/0102-handoff-20261012. Hosted acceptance of the corrected SHA remains pending.

Refs CW-7386
Keep the Workflow tool test's spawn-minted requested/effective route receipt and durable JSONL reload assertions. Move its existing panel/history assertions into the existing row-receipt round-trip test, seeded with the same typed wire fields. Remove only duplicate coordination pager assertions already covered by the UI's durable-resolution test. No new test functions or code comments; correct the existing tool comment that formerly claimed UI rendering.

Before on unchanged compiled test inputs: workflow1, panel31 and coordination4 passed;0 failed. After on95d4ce460a plus five frozen paths: "Summary [   0.477s] 37 tests run: 37 passed, 15279 skipped", including the original durable claim eviction/resolution case. Source hashes unchanged. Runtime boundary production0/test25/uilib0 -> production0/test18/uilib0, late1/doc0 unchanged. fmt/diff pass; blocking538 sites/165files and dead-code221 suppressions remain within unchanged budgets.

Production and model-facing bytes are unchanged. Reuse immediately preceding root npm1312 passed;0 failed;7 skipped and check:web evidence. Full hosted acceptance remains separate.

Refs CW-7160
Refs codewhale-hq#6938
Move the two existing session save/reopen rendering tests into history tests, retaining the API replay and rich plan assertions. Keep the engine MCP guidance assertion at the shared runtime decoder and move its System-cell render assertion into the existing history snapshot test. Remove the imports left unused by the move. No new test functions or code comments.

Before, the two moved persistence cases passed1/0 each and MCP cases passed2/0. After on a395e38 plus six frozen paths: "Summary [   6.777s] 194 tests run: 194 passed, 15122 skipped". Source hashes stayed unchanged; no outstanding test failures. The first compile failed on the unused StepStatus import, now removed. An extra-flags dependency rebuild was interrupted and the warmed default flags restored; neither interrupted build is claimed as evidence. fmt/diff and runtime boundary checks pass at production0/test13/late1/uilib0/doc0. Blocking538 sites/165files and dead-code221 suppressions remain within their budgets.

Production and model-facing bytes are unchanged. Reuse preceding root npm1312 passed;0 failed;7 skipped and check:web evidence. Hosted qualification remains separate.

Refs CW-7160
Refs codewhale-hq#6938
The failed Windows artifact on PR6965 contained all24 Unicode lines exactly once and one submission. Its appended machine turn_meta named a random workspace cw-clipboard-03eIS5, so the unchanged substring assertion counted that path as a duplicate of line3. The passing6f0 artifact has the identical input-body hash. Rename the temporary fixture prefix to cw-terminal-fixture- and retain every delivery, no-submit and single-turn assertion. Add progress messages to the existing desktop stdout log:95d separately stalled before composer readiness, with no paste/request capture, and that artifact cannot identify the blocked desktop call.

Two captured-artifact replays passed;0 failed. Node syntax and diff checks pass. Fresh required root gate:101+19+413+7+772=1312 passed;0 failed;7 skipped; check:web completed. Source hashes unchanged. No new test cases or code comments. Actual Windows execution of this harness repair is still pending hosted CI; this does not claim a clipboard or final-candidate pass.

Refs CW-7079
Refs codewhale-hq#6915
Move the UI-free half of crates/tui/src/test_support.rs and test_env_lock.rs into the existing codewhale-runtime test-support feature. Keep TUI resolver fences, sandbox fixture placement, JWT/trust helpers and App fixtures in their existing owner for this packet. TUI and the included shell integration harness now use the same barrier. Migrate the shell and two client private environment locks to that barrier; retain stream_entry's existing shared lock and remove only its extra private lock. Shell fixtures reuse EnvVarGuard so failures restore the variables. No new test functions, crate or code comments.

Visibility review: only the opt-in test-support feature (or Runtime unit tests) exposes these helpers. Previously pub(crate) helper APIs become pub so the TUI shim and included harness can share one owner; environment scope internals, ticket generation fields, seal fields and guarded-key bookkeeping remain private. EnvVarGuard set/remove retain the existing safe-function contract and debug assertions requiring the process barrier before unsafe environment mutation; restore/drop order and stale-generation refusal are unchanged. The public constructor intentionally has no implicit Default that would acquire a non-reentrant process lock. Production builds do not enable the feature by default. The moved barrier body is identical apart from visibility and the corrected existing module header.

Baseline existing guards: "test result: ok. 12 passed; 0 failed" plus2barrier/3private-mutex cases passed0failed. After source inventory15316TUI cases ->15311, with five moved to Runtime. Runtime: "Summary [   0.013s] 5 tests run: 5 passed, 188 skipped". Full TUI: "Summary [ 199.151s] 15286 tests run: 15283 passed (1 slow), 3 failed, 25 skipped". Those three nested-Cargo fixtures inherited the outer build directory outside their workspace sandbox; removing that outer test-launch setting and reusing the unchanged compiled source gave "test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 15308 filtered out; finished in 1.94s". Original failures retained; no outstanding failures among the15286TUI cases. Shell denial integration: "Summary [   2.881s] 3 tests run: 3 passed, 217 skipped". Initial missing OnceLock import compile retained and fixed; unchanged Runtime evidence reused.

Strict Runtime/TUI all-targets/all-features clippy passes8m25 with the existing CI -Dwarnings and three allowances; all18 frozen hashes unchanged. Feature-only check passes, package list includes both Runtime feature files and excludes TUI test helpers; Cargo.lock unchanged. Blocking checker34passed0failed; blocking538sites/165files unchanged; dead-code221 ->220 (139allow/81expect) after removing the old module mount suppression. Boundary production0/test13/late1/uilib0/doc0 unchanged. Required npm/web gate1312passed0failed7skipped and check:webcompleted. Shared-process qualification and final hosted release acceptance remain separate.

Refs CW-7160
Refs codewhale-hq#6938
Move test_tui_options and test_app_with_options to tui/test_support, migrate every114 current consumer and remove both constructors from the Runtime-facing shim. The two complete definitions and their existing comments match the old definitions; rustfmt only removes the trailing blank line at the new file EOF. Move the existing default-options doc onto its actual constructor. No new test functions, code comments or compatibility re-export.

Source44f7f6bbca plus119 frozen paths: strict all-targets/all-features TUI clippy passes40.55s using the existing CI three allowances; "Summary [   1.403s] 238 tests run: 238 passed, 15073 skipped". Baseline all238 cases already passed in the preceding full library run. Source hashes unchanged; no outstanding failures. fmt/diff and boundary gates pass: production0/test13/late1/uilib0/doc0 -> production0/test6/late1/uilib0/doc0. No former constructor paths remain.

Production and model-facing bytes unchanged. Reuse the unchanged required npm1312passed0failed7skipped and check:web gate from the immediately preceding clipboard/helper packets. Final hosted qualification remains separate.

Refs CW-7160
Refs codewhale-hq#6938
Move the existing redaction confirmation decision and receipt write onto Config, retaining the same config fields and codewhale-config authority/persistence helpers. Move workspace trust next to its existing config owner with the same legacy marker rule. Move the unchanged0.9.4 constitution checkpoint and existing documentation to prompts. Migrate every caller and remove old public function/constant paths without re-exports. Reconcile the roadmap with the actual0.10.2 source graph; the later engine/exec-agent test remains separate. No new tests or code comments.

On35e3204870 plus16 frozen paths: "Summary [   2.643s] 86 tests run: 86 passed, 15225 skipped" covering redaction decisions/refusal, workspace trust and profile/local constitution requests. The same cases were passing in the prior full TUI run. Strict all-targets/all-features TUI clippy passes38.63s with the existing CI three allowances; first unused onboarding import diagnostic retained, removed before final check. All16 hashes unchanged. Source ratchet PASS at production0/test0/late1/uilib0/doc0 over118closuremodules; no old decision paths remain. Cargo crate-boundary, fmt/diff gates pass. Blocking538sites/165files and dead-code220 remain within budget. Fresh required npm1312passed0failed7skipped and check:webcompleted.

Visibility/authority: the Config methods expose only the same existing decisions and persistence contract; they add no confirmation authority or alternate store. Workspace trust remains crate-private. The checkpoint value and model-facing bytes are unchanged. Full hosted/shared-process release qualification remains separate.

Refs CW-7160
Refs codewhale-hq#6938
Feed accepted HTTP Runtime turn activity into the existing companion's
metadata-only producer. Attach only to a running owner, perform conditional
idle selection inside the owner's world, and refuse a live producer even
when it arrives after the inspected frame. Manual selection stays explicit.
Bound queues/items/waits, clear uncertain observations after a gap, and stop
heartbeats twelve seconds after completion or lost coverage. Completed turns
alone grant Done; prompts, reasoning, tool names and arguments never cross.

Keep HTTP client construction and disposal off Tokio workers. Document the
current default whale and shared projection, refresh the existing feature
facts, and leave native-terminal/live-audio pet qualification unclaimed.
Founder F6 moves codewhale-hq#6155 items 1-2 to next-milestone qualification; the issue
remains open. No ACP/background-TUI projection or model/KV-cache additions.

Verification on frozen fifteen-path batch, base 3126fe3:
- Existing native pet checks: "Summary [1.304s] 35 tests run: 35 passed,
  15276 skipped". Two unchanged feature-registry checks also passed in the
  original "37 tests run: 37 passed" batch.
- Exact hosted CI clippy flags, all targets/features: passed, 41.55s.
- CLI build passed, 42.86s; existing macOS oversized __eh_frame linker
  warning retained, no Rust warnings.
- Private loopback Runtime/companion inspection: 7 passed; 0 failed.
  Checks completion, interruption, delayed release, no companion spawning,
  active simulated TUI preservation and atomic idle-selection race refusal.
- Existing shared-owner black-box contract: 11 passed; 0 failed.
- Required JavaScript gate: original web run 769 passed; 3 timed out.
  Focused unchanged installer rerun: "Tests 25 passed (25)". Other suites
  101+19+413+7 passed; repaired aggregate 1312 passed; 0 outstanding failed;
  7 skipped. Original timeout outputs retained. check:web passed.
- Generated facts/docs checks passed; fmt/whitespace passed. Existing budgets:
  "blocking-call budget: 538 sites across 165 files, within budget";
  "PASS: 220 suppressions (allow=139, expect=81), exactly at budget".

Receipts: CW/artifacts/0102-handoff-20261012/idle-handoff-*.log,
idle-handoff-frozen-source.json, idle-handoff-binary.json and
runtime-pet-receipt.json. Hosted Windows/native-app and real terminal/audio
qualification remain separate. No release, deploy or provider spend.

Refs codewhale-hq#6155, codewhale-hq#6139, CW-6587
Custom DeepSeek Chat Completions requests silently dropped configured effort. Reuse the existing DeepSeek mapper so configured low/high/max and the thinking toggle reach streaming and ordinary HTTP requests. Extend the existing request capture check across direct/custom routes and both transports.

Validation: 12 focused Rust checks passed, including 24 HTTP capture cases. The rebuilt CLI passed 6 loopback cases (11 HTTP requests), including max effort, enabled thinking, 65,536/384,000-token output limits and reasoning replay after a tool result. External network access was blocked during CLI captures; no provider calls were made.

npm test: 1,312 passed, 7 skipped, 0 failed. npm run check:web passed after regenerating the existing generated assets. Rust and CLI evidence came from the shared checkout with concurrent catalog work retained; unrelated test-only formatting changed during compilation. No pristine-release or provider-semantic claim is made.
… nested-shell detection

Resolve command_safety conflicts against origin/main 008d21d without
rewriting SparkofSpike's commits. Preserve quoted ampersands as data,
recognize redirections and separators, reuse the existing shell payload
parser for option-bearing shell wrappers, and retain cmd /C and PowerShell
-Command payload inspection. Remove the obsolete descriptor-only helper.
Keep the contributor's regression tests and main's redirect coverage;
update the existing expectations for the newly classifiable syntax.

Verification on macOS, native shared build lock and hermetic test home:
- scripts/dev-test.sh execpolicy command_safety:
  89 tests run: 89 passed, 147 skipped (filter exclusions).
- scripts/dev-test.sh tui auto_review:
  79 tests run: 79 passed, 15232 skipped (filter exclusions).
- npm test: 1312 passed; 0 failed; 7 skipped.
- npm run check:web: passed, including latest-release check and build.
- cargo fmt --all -- --check and git diff --check: passed.
- clippy -p codewhale-execpolicy --all-targets --locked -D warnings: passed.
Windows execution and hosted Windows CI on this merge are still pending.
No release, tag, or main push is part of this commit.

Refs codewhale-hq#6929, codewhale-hq#6963
Review of the contributor conflict resolution found that a pair of
backslashes before a closing double quote consumed that quote as escaped.
Consume the escaped backslash as a pair, so the following closing quote
ends its string and a glued command separator remains visible.
Extended the contributor's existing ampersand regression test; no new test
function or code comment was added.

macOS verification: 89 tests run: 89 passed, 147 skipped (filter exclusions).
Focused all-targets execpolicy Clippy, formatting and diff whitespace pass.
Unchanged web/npm source retains the immediately preceding eb817db gate:
npm test 1312 passed; 0 failed; 7 skipped, and npm run check:web passed.
Windows execution remains pending hosted CI; no Windows proof claimed.

Refs codewhale-hq#6929
Refs codewhale-hq#6963
cmd.exe treats apostrophes as literal characters, so its /C or /K payload
can execute an ampersand-separated command inside apostrophes. The shared
POSIX-oriented scanner previously grouped that text and missed the later
invocation. Normalize literal apostrophes only in the explicit cmd payload
before the existing bounded nested scan. Keep ordinary quoted arguments and
other shell payloads on their existing path. Extend the existing wrapper case
table; no new test function or code comment.

Refs codewhale-hq#6963. Follow-up review on contributor PR codewhale-hq#6929, retained as its own PR.

macOS: CODEWHALE_DEV_NEXTEST=0 CARGO_BUILD_JOBS=1 scripts/dev-test.sh execpolicy command_safety
  test result: ok. 89 passed; 0 failed; 0 ignored; 0 measured; 147 filtered out; finished in 0.01s
Focused all-target Clippy, fmt and git diff --check passed. The first fmt check
requested only match-arm formatting, applied before commit. Reused unchanged
web/npm gate: 1312 passed; 0 failed; 7 skipped; check:web passed.

Windows 11 ARM64: a harmless cmd /D /S /C echo probe emitted the second-stage
marker between literal apostrophes. This proves shell semantics only; the new
Rust classifier has not yet run on Windows. Hosted Windows CI is required.
The prior PR head's Windows job passed 18299 tests, including owner_crash_reentry;
that receipt does not qualify this changed head. No native-app, package or
Windows 10 1607 acceptance is claimed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants