Skip to content

Checkpoint: development → main (PR #95 follow-ups, PR95 increment, Wave 3 Steps 9a.1–9b.2, NI, CLI MVP priority reset) - #96

Merged
cemililik merged 97 commits into
mainfrom
development
Nov 4, 2026
Merged

cemililik merged 97 commits into
mainfrom
development

Conversation

@cemililik

@cemililik cemililik commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

A scoped checkpoint of development into main, approved by the maintainer on 2026-11-04 (decision D61). It
brings main level with development before the CLI MVP work starts. It is not the Wave 3 PR: Wave 3 is
paused after Step 9b.2, and its PR still follows Step 19 when the wave resumes.

88 commits since the PR #95 merge (6d2a7372): 168 files, +31,863 / −3,176.

What it brings to main

  • PR fix(repo): harden engine and cli lifetimes (wave 3 steps 1-8) #95 post-merge follow-ups: the four-round independent review of a3e164b5 and its corrections.
  • The PR95 increment: the scrubber residuals, including the two security residuals (api-key:Bearer
    token erasure; raw ?/# URL userinfo). It closed under the maintainer's 2026-10-21 masker bar and round cap.
  • Wave 3 Steps 9a.1, 9a.2a, 9a.2b, 9b.1 and 9b.2 (feat(cli): 2.E — ink streaming TUI (live node status + token stream + cost) #46/ADR-0111), each independently accepted on
    development. They add:
    • the candidate build and finalizer;
    • the strict source-map decoder, the private manifest and packed-frame symbolication;
    • the product map controls and the package_test receipt;
    • the private container, the isolated GnuPG runner and encrypt-once (synthetic recipients only);
    • the release binding receipt v1, verified reuse and the absent production recipient loader.
  • The interim release guard (Step 9a.1). It is currently on development only. After this merge, main's
    release.yml also refuses every npm publication. That is intended: the guard stays in force until the
    superseding pre-1.0 release ADR (D59) is Accepted and its guard change passes a security sitting.
  • NI, native file identity: every product file and directory identity check compares exact bigints.
    Accepted at fc5af701; its Windows clause closed after three green advisory Windows runs (the last at
    5a328bca).
  • The CLI MVP priority reset (64fb8772, docs only):
    • current.md is reduced to live status and the new order: Gate 0, MVP-1…MVP-10, then v0.2.1.
    • D58–D72 are recorded.
    • The unique history moves verbatim into the phase registers.
    • M3.5 (CLI MVP, v0.2.0) is added to the milestone spine.

Review and evidence

Each increment's review rounds, security sittings and CI evidence are recorded in the
Phase 2.5.5 register. The pushed head's push CI is
on the branch:

  • 5a328bca passed all 18 jobs, including the four installed-CLI consumers and the advisory Windows job;
  • 64fb8772 is docs only and its CI is running.

This PR adds no new review claim beyond those records.

Checklist

  • Conventional Commits; per-increment acceptance recorded in the register
  • No new runtime dependency; the GnuPG prerequisite was approved with Step 9 (local pnpm run ci and the CI Ubuntu runners)
  • Docs updated in their canonical homes
  • Maintainer merge (merge commit, as with PR fix(repo): harden engine and cli lifetimes (wave 3 steps 1-8) #95)

🤖 Generated with Claude Code


Play the walkthrough    Follow each request

cemililik and others added 30 commits October 19, 2026 13:56
PR #95 merged to main as 6d2a737 at 22:44 UTC on 2026-10-18. Record the
merge checkpoint in the Phase 2.5.5 register, point current.md and the agent
guides at Step 9, and keep Wave 3 open: Steps 9-19 are not started.

- phase register: merge checkpoint (completed scope, review attribution, the
  independent review's dispositions, publication state) and the
  maintainer-approved 9a-9d split of Step 9 as rows 9a-9d of the increment table
- deferred-tasks: the independent review's unactioned findings, and the
  SonarCloud main-branch gate recorded as done
- limits stated rather than implied: a3e164b has author verification only;
  CI for the merged head and the merge commit finished green after the merge;
  the SonarCloud dispositions were applied on main on 2026-10-19

Refs: ADR-0111

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An independent security sitting on a3e164b found that a scheme glued to a
word character (xhttps://...), split by LF/TAB and carrying a raw @ in the
credential, left a partial mask with part of the credential visible. The
split-credential scanner still skipped any start preceded by a word
character, although the URL rule no longer has a word boundary.

The URL rule's boundary now blocks no start, and the word-character shortcut
applies only when a rule's boundary rejects word characters. The anchored
scanner gate and the URL pattern line are unchanged, so no causal-control
anchor moves.

- tests: two regression cases (reverting the gate fails exactly those), plus
  an underscore glue, a split inside the scheme, a multi-URL case and three
  characterisation cases for the fail-closed trade-offs the review found
  (prose before a URL and a later @ redacts the whole diagnostic; a longer
  scheme name such as sftp matches by suffix). A test comment wrongly said
  ws/wss/ftp/ftps were masked before the PR; they are the PR's additions
- comments: the stale boundary and slice(2) contract text is corrected
- pins: the scrub-secrets file set 103 -> 111, in mutations.mjs and
  provenance.test.mjs; the secrets-controls README counts follow (278, 111,
  180, 1,343), measured from the control run

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The new gated-reservation case pinned the join of the owner's whenClosed only
for the failed-spawn publication. A mutant that kept the join on that branch
alone and dropped it for natural exit, abort and timeout passed every test in
tool-host/process*.test.ts.

- process-close.test.ts: one table over four branches. Removing the join
  fails all four; keeping it only on the failed-spawn path fails natural
  exit, abort and timeout; dropping it only there fails failed-spawn. The
  closed-child wait now carries an explicit bound
- native-forwarding.test-support.ts: the longer waitFor bound applies only to
  the one wait that observes a real child booting (3 s, leaving 2 s of the 5 s
  default case timeout). Three chained 4 s waits could exceed the case timeout
  and lose the named assertion, and the other waits only observe registry
  reactions, so they return to the default. The docblock now says so

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ound

Independent review of the follow-up found three weaknesses in the join table
and one change that did not do what it claimed.

- process-close.test.ts: the negative assertion waited one macrotask, so an
  ungated publication that was merely late (20 ms, 60 ms) slipped past; it
  now holds the gate for 100 ms and catches both in all four cases. A case
  that failed before the gate opened leaked an immortal node child (the abort
  mutant reproduced it); the case now reaps the child and releases the gate
  in a finally
- native-forwarding.test-support.ts: restore the original default waits. The
  3 s bound on the real-child boot wait could not help its only caller, whose
  outer waits in drive-home.test.ts use the 1 s default and enclose the boot,
  so a slow boot failed exactly as before, only later. No flake was ever
  reproduced (0 failures under heavy load), and the standard is wary of
  lengthening waits to hide one
- mcp-orphan.e2e.test.ts: the skip comment said every case fails at the first
  execFileSync, but only one case reads the process table. It now says the
  whole suite is skipped on Windows out of caution because it has only run on
  POSIX

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment-only: no behaviour, anchor or pinned count changes.

- the URL rule docblock and the characterisation-test comment said the
  consequences "never reveal" a credential; a credential split by a plain
  space that also holds a raw @ stays the documented opaque-fragments-across-
  spaces limit, as it was for the pre-PR matcher
- the wordBlocksStart comment now states that 'a' stands for every ASCII word
  character, which holds because each current boundary class rejects all of
  them or none
- secrets-controls README: the R1 recovery mutant now only affects the key
  rules, since the URL and header rules supply their own joined patterns

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment-only: no behaviour, anchor or pinned count changes.

- the header docblock no longer attributes the whole-diagnostic redaction of
  prose next to a URL to the missing word boundary: the pre-PR matcher did
  the same for http(s), and dropping the boundary only extends it to prose
  that whitespace stripping glues to the scheme
- the joinedPattern contract says the SyntaxError occurs on LF/TAB input that
  no earlier rule has already redacted, not on every such input
- secrets-controls README: the R2 recovery mutant also selects the URL rule
  now that it has its own joined pattern, with no effect on its result

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
a3e164b (corrections from the third independent review of PR #95, including
a secret-scrubbing rule) merged with author verification only. Record the
independent review that followed and the open findings it left.

- phase register: the merge checkpoint no longer claims the commit has no
  independent acceptance, and a new section records four review rounds and a
  security sitting (per-area results, the findings and their dispositions,
  what the reviewers did and did not cover, the follow-up commits)
- deferred-tasks: the residuals the review found and did not fix, two of them
  security items that predate the follow-up (an api-key:Bearer token leak,
  and userinfo with a raw ? or # before the first colon left unmasked), plus
  the examined-and-unchanged waitFor bound and the review's nits
- current.md, CLAUDE.md, AGENTS.md: the merge limits are stated as resolved or
  pointed at the open findings instead of "author verification only"

Refs: ADR-0111

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A pre-implementation audit read the canonical documents for Wave 3 Steps 9-19
and the open PR #95 findings against the current source (sixteen auditors,
fifteen adversarial verifiers and a completeness critic). The maintainer
approved every recommendation on 2026-10-19. No new ADR is required.

- Record the rulings, the PR95 plan amendment (scrubber residuals before
  9a) and the recorded splits for Steps 9-19 in the phase register's
  increment table (29 increments, ID coverage unchanged apart from PR95).
- Give every open PR #95 item an owner in deferred-tasks.md; LOW-1 and
  LOW-2 become approved deferrals with their triggers.
- Reconcile stale status: the phase banner, the a3e164b review wording in
  current.md, where the post-merge follow-ups live (development, pushed,
  push CI 39275223233 green), README and CLAUDE/AGENTS status lines.
- Add forward pointers from the preflight tables to the approval, a dated
  #45 note for ADR-0110's fixed infrastructure diagnostic, G30's pull
  forward to Step 13 in both phase files, and the Outcomes bullet's
  reconciliation with exit criterion 4.
- Mark accepted Steps 1-8 task bullets DONE or certified.
- AGENTS.md: describe the development-to-main PR branch model in use.

Refs: ADR-0110, ADR-0111, ADR-0112, ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR #95 independent review left two security residuals in the
diagnostic secret backstop and several scanner gaps. The maintainer ruled
the policy on 2026-10-19 (Wave 3 PR95 increment); this implements it in
llm, in core's journal projection twin, in the secrets causal controls
(repo tooling) and in the canonical docs.

- An api-key, x-api-key or x-goog-api-key value that begins with a
  Bearer/Basic label (also LF/TAB-split) is masked together with its
  token, so the header rule no longer erases the later label rules' anchor
  and leaks the token. Like those rules, a label that ends a line takes the
  next token with it, failing closed.
- Raw ?/# userinfo is public only after a host-shaped prefix that an =
  assignment follows, in scrub-secrets and in core's bounding.ts twin:
  https://us?er:TOKEN@h, http://#TOKEN@h and https://my.user:1234?TOKEN@h
  are masked, and a host fragment without an assignment fails closed.
  Core keeps its documented ']' and short vanity-handle differences.
- The finite URL scheme list gains sixteen connection-string schemes
  (postgres ... smtps). They mask colon-bearing userinfo only, so
  ssh://git@host stays visible; an unlisted suffix scheme still matches.
- The split-credential scanner compiles its probes once. A package-
  internal splitProbe checks its two invariants at module load with a
  typed error, and a :// window skips URL starts that cannot match: an
  8 MiB letter-heavy diagnostic no longer costs a RegExp attempt per
  character.
- New witnesses, a parity test between the two maskers and nine causal
  controls pin each mechanism (66 controls, 416 named witnesses). The
  eight-MiB cases now have a 15 s case deadline, inside the causal
  CAUSAL_TIMEOUT_BUDGET, instead of a 5 s wall-clock assertion.
- The CLI supported-leaf guard admits the leaf's own dependency-free
  probe module; the leaf still reaches nothing else but terminal-safe.
- error-handling.md gains the canonical diagnostic secret backstop
  policy. built-in-tools.md and the effect journal contract record the
  shared ?/# predicate and its v5 consequence: a resumed effect whose
  args carried such userinfo now refuses instead of replaying.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 1 of the PR95 review was NONCLEAN in all five lenses; every verified
finding is fixed here, in llm, core's twin predicate, the CLI tests, the
secrets causal controls (repo tooling) and the docs.

- The host-shaped ?/# predicate ran an unbounded dotted run through V8's
  RegExp backtrack stack: an admitted-size prefix made scrubSecrets,
  makeLlmError and core's redactSecretShapedText/boundForModel throw
  RangeError. Both maskers now refuse a prefix longer than 260 characters
  (a DNS name plus a port) before the RegExp, which fails closed.
- A header value with stacked or quoted labels (Bearer Bearer <token>,
  Bearer "<token>") leaked the token. Up to eight labels and an optional
  quote now precede the token, bounded so no label run can exhaust the
  stack, and the standalone Bearer/Basic rules accept the same stacked
  labels.
- Already-masked userinfo is a fixed point (an exactly masked password
  under a connection-string scheme, or every part masked or empty under
  any scheme), so a templated postgres://app:{{secrets.password}}@db no
  longer raises the authored-content notice on every run.
- New tests pin the host-shaped anchors and port bound, multi-character
  label whitespace, stacking, the length bound at admitted size and the
  fixed point. Six new causal controls (72 in all, 430 named witnesses)
  and the snapshot detector's 11-case workload follow.
- error-handling.md now states which rules probe LF/TAB joins, how the
  ?/# rule combines with the scheme rule, stacked labels, fixed points and
  the unlisted-scheme and whitespace-delimited limits; built-in-tools.md
  lists every core difference; commands.md, current.md and the rulings
  record (per-increment security sittings) are corrected.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The second review round found that quote characters beside a
Bearer/Basic label let the api-key header rule take the label as its
token and leak the real one, that the eight-label bound failed open
past its limit, and that several bounds and documented behaviours were
unpinned or misdescribed.

- The header rule accepts (escaped) quotes before every label and
  before the token, and its token is never another label or an
  already-masked [REDACTED]. A ninth label therefore fails the rule
  instead of leaking the token; the label rules and the
  whitespace-stripped residual probe then blank the whole diagnostic.
- The standalone Bearer/Basic rules no longer stack labels: a label
  never takes another label (or a masked marker) as its token, so any
  number of stacked labels leaves the last one to mask the token, with
  no repetition to bound. Masked header, Bearer and Basic tokens are
  fixed points, so a templated `x-api-key: Bearer {{secrets.key}}`
  stays quiet in the authored-content notice.
- Tests pin the eight-label bound and its fail-closed side,
  admitted-size label runs, the 260-character host bound (260 kept,
  261 masked) and case-insensitivity in both maskers, and core's
  documented authority-delimiter difference from the LLM backstop.
- Seven new secrets causal controls (79 controls, 441 witnesses), with
  updated provenance pins and README counts.
- error-handling.md, built-in-tools.md and the bounding.ts docblock
  describe the label shapes, fixed points and core's authority
  delimiters exactly. The phase register records ruling C2's
  production-key constraint, the #308 DONE marker and the split-row
  IDs; deferred-tasks.md schedules the key constraint for Wave 4b.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The third review round found that sequential rule application let one
rule erase the context another needed: an api-key header token could
swallow a Bearer/Basic label glued behind punctuation, and the round-2
masked-marker guard kept a tail glued to a mask (including the
scrubber's own sk-/AIza masks) visible. A quote-closed label, quote
runs, JSON-in-JSON headers and quoted or escaped placeholders were also
handled incompletely, and a labels-only header value blanked the
diagnostic depending on context.

- Every non-query rule now finds its matches in the original text and
  the masked ranges of all rules are merged before anything is
  replaced, so no rule can erase another's context. A header or label
  rule masks only its named token group: header names, separators and
  labels stay visible, an LF/TAB-split label keeps its spelling, and
  masking an exact [REDACTED] is the identity, so the three
  masked-marker lookaheads are gone.
- The header rule uses a zero-width boundary, accepts escaped quotes
  before its colon, skips up to eight punctuation-glued or quote-closed
  labels, refuses a run of up to eight concatenated labels or a nested
  header name as its token, and ends the token before a nested header
  name or at escapes before a delimiter.
- The label rules accept a label closed by its own quote, refuse a
  glued or concatenated label as their token, and end the token before
  a label glued behind a non-word character. No token can start inside
  a quote run and every quantified label group is bounded, so
  admitted-size runs stay linear and stack-safe.
- Tests pin each class, the quoted and escaped fixed points, the
  context-free labels-only value, core's ", ' and } delimiters and
  admitted-size escape and glued-label runs. 23 header and label
  controls replace ten (92 controls, 462 witnesses); provenance pins and
  README counts follow.
- error-handling.md describes the merged masking, the shapes, the
  trade-offs and the " limit; commands.md notes quoted header
  placeholders; deferred-tasks.md and the rulings row record ruling C2
  as approved.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 4 of the PR95 increment's review (at 6fb7e9d) confirmed 19
findings across the scrubber, tests, docs, security, property and critic
lenses. All are fixed or, for one, documented as a deliberate limit.

LLM diagnostic backstop (packages/llm/src/scrub-secrets.ts):
- The literal query names are one rule of the merged masking pass:
  their value ranges come from the original text like every other rule,
  so a header token cut short at `;` no longer leaves a query value's
  tail visible (SCRUB-R4-3).
- An ambiguous authority (userinfo holding a raw ?/# that is not a
  public contact) is masked through its end unless a host-shaped name
  follows its last @, so `https://svc?password=P@ssw0rd123` no longer
  leaves `ssw0rd123` visible while `https://u:pa?ss@h.example` keeps its
  host (SEC-R4-1). The host test is length-guarded like the contact
  prefix.
- A run of up to eight concatenated labels (`BearerBearer`) is one label
  in the header value and in the standalone rule, and ends a token glued
  behind punctuation; nine concatenated labels blank the diagnostic
  before any rule runs (SCRUB-R4-1, SEC-R4-2, GEN-R4-1, DOC-R4-1).
- The Bearer and Basic rules are one rule whose first label is either
  label, so neither copy can drift from the other (CRIT-R4-1).
- scrubSecrets repeats its merged pass until the output stops changing,
  at most three passes, and fails closed otherwise, so a second scrub
  changes nothing (SCRUB-R4-2, GEN-R4-2).
- A word glued before `sftp://` still reads as sftp: reading it as the
  colonless ftp would make every whitespace-joined probe, the CLI's
  display residual included, blank `connect to sftp://user@host`. It is
  recorded as a limit and pinned (GEN-R4-4).

Core projection (packages/core/src/tools/bounding.ts):
- The same ambiguous-authority extent and a bounded fixed point.
- A scheme label never takes a URL scheme as its token, so the split
  pass no longer hides a password behind `token postgres://` (GEN-R4-5).
- An LF/TAB right after `://` or before the userinfo's @ belongs to the
  split userinfo (SEC-R4-3).

Tests and controls:
- 14 scrubber and 6 core cases cover every finding, including the header
  `'`/`}` stops, quoted standalone tokens, a range containing a later
  one, Basic copies of the round-3 cases and admitted-size runs that a
  quadratic regression would push past the case deadline (T4-1..T4-5).
  The merge-extent witnesses put a key inside an ambiguous authority's
  rest: the bounded repeat re-masks a truncated range wherever its rule
  matches again, so only a tail with no @ left discriminates the extent.
- The secrets family has 109 controls (481 targets and witnesses): 19
  new, the Bearer/Basic not-a-label pair merged, and the equivalent
  label concatenation-bound control retired. Edges that only change how
  much is masked are pinned by tests without a control, keeping the
  family within the 2,000-file evidence bound. The secrets CI job's
  allowance becomes 60 minutes, like the filesystem job's.

Docs: error-handling.md's backstop policy (masking, URL userinfo,
trade-offs, limits; DOC-R4-2 and GEN-R4-3 wording), built-in-tools.md,
effect-journal.md's digest note and the controls README.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 5 of the PR95 increment's review (at b2c50e8) confirmed 20
findings. Two were high: core's round-4 authority extent erased the
context its own later rules read. All are fixed.

Core projection (packages/core/src/tools/bounding.ts):
- Core's rules run in sequence, so masking through an ambiguous
  authority's end swallowed the next credential's key name, label,
  scheme or PEM marker, and in the LF/TAB-joined view it ran across a
  real line break (SCRUB-R5-1, PR95R5-GEN-1). The extent is gone. An
  ambiguous userinfo is now masked together with exactly the query
  values core's opaque-query rule (24+ characters after ?/&) and secret
  key=value rule would mask whole, ending where those rules end them;
  the joined view masks the userinfo only. This also closes the tails
  ruling (2) left after a single-label host (PR95R5-GEN-2) and the
  nested URL the old resume point skipped (TST-R5-1).
- The URL-scheme lookahead after a scheme label is removed: it refused
  any token whose run reached '://', so tokens glued to a URL stayed
  visible (SCRUB-R5-2, CONS-R5-1, SEC-R5-1). The split view now masks
  URL userinfo before scheme labels, as the main pass always did.
- A separator inside a URL's 'scheme://' now splits its userinfo, as
  one at the userinfo's edges does: core had left
  'https:/\n/user:pass@host' unmasked since before the increment.
- The bounded fixed point moves to a tested helper (TST-R5-6).

LLM backstop (packages/llm/src/scrub-secrets.ts):
- The split scan retries the next start after a kept candidate whose
  first letter ends a line, so 'files\nftp://tok\nen@h' (read as
  sftp) blanks again while a real 'files\nsftp://user@h' stays
  (SCRUB-R5-3).
- The fixed point uses the same tested helper; the CLI's leaf
  reachability test admits it.

Tests and controls:
- Discriminating tests for the label rule's trailing ';'/',', its
  embedded stop's quote run and bound, the nine-label bound's case and
  LF/TAB letters, an exactly masked password, the escaped joined header
  name, the split retry, the residual blank after another rule's mask,
  every core round-5 witness and both fixed-point helpers; each was
  checked against the mutation it kills (TST-R5-2..5).
- The secrets family stays at 110 controls (483 targets and witnesses),
  the most the unchanged 2,000-file export bound admits: three core
  controls are replaced for the new mechanisms, two are added (split
  scheme, split retry) and one pattern-piece control is retired. From
  this round on, pieces inside a mechanism's pattern are pinned by
  discriminating tests rather than new controls.

Docs: error-handling.md (exactly masked userinfo, core's extent, the
residual blank after a mask, the glued-sftp limit's scope),
built-in-tools.md, effect-journal.md, the controls README (counts,
round 5, the escape-run wording), a PR95 implementation checkpoint in
the phase register with status lines in current.md, CLAUDE.md and
AGENTS.md, row 9a's split marker, the Step 9 mechanics link and the
commit-and-pr skill's development-branch step (DOC-R5-1..4, TST-R5-7,
PR95R5-GEN-3, CRIT-R5-1).

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 6 of the PR95 increment's review (at 6b2e94d) confirmed 21
findings, nine high. Round 5's value mirror for an ambiguous userinfo
was quadratic, still masked through a later credential's key, label or
PEM marker, and was dropped by the split view. All are fixed.

Core projection (packages/core/src/tools/bounding.ts):
- Core now works like the scrubber: every rule reports its ranges on
  one text and overlapping ranges merge before anything is replaced,
  so no mask removes the key name, scheme label, PEM marker or URL
  scheme another rule reads (R6-SCRUB-1, R6-SCRUB-3, SEC-R6-2,
  SEC-R6-3, GEN-R6-1). The value mirror (laterQueryValueEnd) and the
  split view's sequential rewrite are gone, which removes the
  quadratic scan (R6-SCRUB-2, CONS-R6-1, SEC-R6-1, GEN-R6-2).
- A key or parameter in a URL userinfo's own text belongs to it: all
  of an unambiguous userinfo, and a user:password before a raw ?/#.
  The rest of an ambiguous userinfo is read by the key/value and
  opaque-query rules, so every value they mask whole is masked to its
  own end (R6-SCRUB-4), while a git remote such as
  https://x-access-token:<t>@github.com/o/r keeps its host and path.
- The key/value rule resumes its scan at each value, so a key inside
  a value is read too, and reads values through earlier masks, line
  breaks included; the scheme-label rule resumes after each label; a
  split header label, and a scheme label even where a key's value runs
  on over it, may start right after a credential. These keep the cases
  the old order masked by accident.
- PEM blocks are found with one shared END search instead of a
  bounded rescan after every BEGIN marker (CRIT-R6-1): an open-PEM
  tool result at admitted size takes under a second, not 22 s.

LLM backstop (packages/llm/src/scrub-secrets.ts):
- The nine-label bound also reads the raw text, which normalization
  can shorten by one label letter after ESC [ (CRIT-R6-2).
- The literal secret query names also follow #, as in an OAuth
  implicit-flow #access_token= (GEN-R6-3).

Tests and controls:
- Discriminating core cases for every round-6 witness, each prefix
  form of the round-5 witnesses, the userinfo's zones, nested keys and
  labels, values through masks, label boundaries, the PEM body bound
  and admitted-size linearity; LLM cases for the raw-text bound and
  fragments; and both maskers' fail-closed values, observed through a
  module mock of the fixed-point helper (TST-R6-1, TST-R6-2). Each was
  checked against the mutation it kills.
- The secrets family stays at 110 controls (491 targets and
  witnesses): six core controls pin the new mechanisms, the three
  round-5 core controls go with their mechanisms, six are re-anchored,
  and three single-character pieces of the header rule are retired to
  make room after their tests were checked to kill the same mutations.

Docs: built-in-tools.md (core's merged ranges and zones),
effect-journal.md (the v5 projection changes, the split-scheme rule
included), error-handling.md (core's sentence, fragments, the raw-text
bound and two more limits), the controls README, the phase register's
round table and witness count, and the status lines (DOC-R6-1..3,
TST-R6-3, CONS-R6-2, GEN-R6-4).

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 7 of the PR95 increment's review (at b89ac62) confirmed 17
findings, two high. A URL userinfo's owned zone still hid real keys,
the quoted-value pattern exhausted V8's RegExp stack at an admitted
8 MiB value, a projection that did not settle could give two calls
one journal digest, and the secrets family's evidence export no
longer fit its byte bound. All material findings are fixed.

Core projection (packages/core/src/tools/bounding.ts):
- Only a userinfo's username owns its keys: the text before its
  first ':' when no raw ?/# comes before that colon, nothing for a
  colonless userinfo, and in the split view never past the first
  LF/TAB. A key in the password or parameter position, or on the
  line after a split URL, now masks its value through the last '@'
  (jdbc:sqlserver://…;password=P@ss…, DATABASE_URL=https://h\n
  DB_PASSWORD=P@ss…), while a git remote such as
  https://x-access-token:<t>@github.com/o/r keeps its host
  (PR95R7-SCRUB-1, SEC-R7-1).
- Quoted values are found by a linear character scan instead of a
  repeated-group RegExp, so an exactly admitted 8 MiB value projects
  in about 50 ms instead of throwing RangeError after a write landed
  (PR95R7-SCRUB-2, SEC-R7-2).
- A credential glued to the end of a fixed-length one (AKIA…ASIA…)
  is masked in the same pass, and a split span glued to the previous
  one is exempt from the original-boundary check, so such runs
  settle (PR95R7-GEN-1).
- projectSecretShapedValue reports whether the journal projection
  settled (fixedPointOf), and makeSummary recognizes split shapes
  with the full fixed point (SEC-R7-3).
- joinSplitHeaderLabels returns early when the text holds no ':'
  (PR95R7-CRIT-2; output-neutral).

Registry (packages/core/src/tools/registry.ts):
- A tiered effect whose journal projection does not settle is
  refused with a loop-correctable ToolArgsInvalidError before its
  prepare, its argument names masked, so the fail-closed constant
  never becomes a journal identity (PR95R7-GEN-1).

Causal controls and CI:
- The coordinator gains --shard index/count (round-robin, recorded
  in the summary, never complete), and the secrets job runs as a
  two-leg matrix whose legs export separately: one export of the
  110-control family measured about 77 MiB against the unchanged
  64-MiB bound (PR95R7-CRIT-1). Both shards ran locally with the
  export enabled: 1,087 files and 45.50 MiB, and 1,087 files and
  45.48 MiB.
- Ten controls: core-userinfo-username-zone, core-split-zone-cap,
  core-quoted-value-scan, core-standalone-chain,
  core-glued-span-boundary, core-summary-fixed-point,
  core-journal-settled, core-unsettled-refusal, and the two halves
  of the nine-label bound (diagnostic-label-run-normalized/-raw).
  The family is 120 controls / 501 targets and witnesses.

Tests: discriminating cases for every witness above, the normalized
nine-label view, values read through every kind of earlier mask, the
merged run's replacement, the key rule's names and delimiters, and
the refusal's masked names, each checked against the mutation it
kills (TST-R7-1..5); the fixed-point mocks clear their history after
each case, which drops the core file's heap from 725 MB to 130 MB
(CONS-R7-1).

Docs: built-in-tools.md, effect-journal.md, tool-registry.md and
error-handling.md (DOC-R7-1 and the backstop's missing generic
key/value rule), both controls READMEs, the phase register's round
table (DOC-R7-2) and gates, and the status lines and freshness dates
(DOC-R7-3).

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 8 of the PR95 increment's review (at 082962e) confirmed 16
findings, one high. Round 7's glued-credential chain could start a
narrower family inside a wider credential or swallow a key name, and
the round found four older gaps. All material findings are fixed; the
password-position finding is documented as credential equality.

Core projection (packages/core/src/tools/bounding.ts):
- The round-7 chain and its glued-span exemption are removed; a glued
  credential is read at the earlier mask's boundary one pass later,
  as before, and a run that never settles is redacted and refused
  (PR95R8-SCRUB-1, TST-R8-3).
- Structured summaries mask what JSON escaping hid (PR95R8-PROP-1,
  high): a non-string result is summarized from its JSON once every
  member under a credential-named key and every argument after a
  credential flag is masked whole, and the text rules read that JSON
  as one text, a value one JSON string down included, so a PEM block
  or a quoted value that runs across members keeps its context. A
  summary masks a quoted value that stops at a line break through a
  later quote that could close it.
- The key rule reads one JSON string down: an escaped quote before
  the separator, a value opened by an escaped quote decoded one level,
  also behind escaped tabs and line breaks (where an apostrophe opens
  a value too). Only its escaped quote closes such a value.
- A quoted value closes on its line. One that does not is decided
  once the pass's other masks are known (PR95R8-PROP-4): when one of
  them takes its line break it closes as the next pass reads it and
  is masked whole; otherwise it is read as an unquoted run from after
  its quote, to its line's end, since its quote may close a string the
  key sits in.
- A header label at a line start inside a split credential's
  continuation is read through a second label view (PR95R8-PROP-2).
- Two member names of one object that project alike make the journal
  projection unsettled, so the registry refuses the tiered call.
- toText makes both texts from one media-redaction walk, and the key
  and scheme rules return early on text that cannot hold them.
- The userinfo zone stays round 7's. A password-position key's value
  runs to the key rule's delimiter, through a URL's host and path, so
  calls that differ only there share a projection, as recognized
  credentials do; documented (DOC-R8-1, PR95R8-SCRUB-2).
- A quoted value closes at the later of two readings: with the masks
  before and inside it filled, and with each mask keeping the
  backslashes it ends with. It applies to the key rule, the journal's
  closing view and the summary's closer search (SCRUB-PRE6-1, a
  regression against 9902e0a since round 6). The same rule masks the
  tail that an opaque value's mask unescaped behind a URL userinfo,
  which the author's oracles found to be a regression too.
- A structured summary's walk keeps a private-key marker whose
  partner lies outside a member it masks whole, so a block across
  members is still masked (SEC-R8-PEM-1); the journal and display
  walks keep their whole mask.
- Every summary also reads a view of the same length in which each
  escaped \n, \r or \t is that separator, for the split-credential,
  standalone and scheme-label rules, so a line-initial token in a
  run_command or MCP result is masked (PR95-GEN-R8P6-1, a gap
  9902e0a shares). The journal projection reads no escape.

Backstop (packages/llm/src/scrub-secrets.ts): the split probe also
reads the raw text when terminal normalization changed it. A
JSON-escaped separator before a quoted header value and terminal
sequences that take a shape's first letter are documented limits, and
a path-less hyperlink target the raw probe joins with a later @ a
documented fail-closed trade-off.

Tests and controls:
- Discriminating cases for every change, each checked against its
  mutation (122 mutants: 118 killed, 4 output-neutral): structured
  summaries across members, line breaks and credential members, unclosed
  values read as runs or closed through the pass's other masks and the
  calls they keep distinct, one JSON level down, colliding member names
  (also in arrays), continuation labels, two and three PEM blocks
  (TST-R8-1), the scrubber's TAB retry (TST-R8-2), the fill's bracket,
  the escaped CR/LF and the ' delimiter (TST-R8-4), the raw split probe,
  header tokens a JSON escape begins, credentials before a terminal
  sequence masked in place, the host cut at a fragment, a real, unmocked
  non-settling refusal (PR95R8-PROP-3), a quoted value's two readings,
  the private-key markers a structured summary keeps and the escaped
  separators every summary reads. Core's eight-MiB cases take a case
  deadline instead of wall-clock bounds (CRIT-R8-2).
- Twelve controls added, two removed with their mechanisms, six
  re-anchored and two witnesses added: 130 controls / 513 targets and
  witnesses in two shards of 65. Both shards ran locally with the
  export enabled: 1,177 files and 49.83 / 49.76 MiB.

Before this commit the reviewers' generative harness, exhaustive
identity and summary oracles, a distinctness oracle, the sixth
pre-review's own generators, a mutation run and the affected causal
controls ran against the uncommitted fix, and six internal multi-lens
pre-reviews reviewed it as unreferenced commits; their findings are
resolved here, and two low edge cases that 9902e0a shares are recorded
in deferred-tasks.md. Mechanisms an earlier candidate added beyond the
findings (a header escape guard, a terminal final-byte view, masking
unclosed values to the line end, a trimmed opaque mask, a per-string
structured summary) were withdrawn when a pre-review showed they leaked,
merged identities or lost context.

Docs: built-in-tools.md, effect-journal.md §11 (including the refusal's
slot behaviour, CRIT-R8-1), tool-registry.md, error-handling.md and the
scrubber's comments (PR95R8-PROP-5), commands.md's date (DOC-R8-2), the
secrets controls README, ci.yml, the phase register's round table,
ruling and gates, deferred-tasks.md's masker section, and the status
lines.

The maintainer's ruling of 2026-10-21 narrows this increment's
acceptance bar: a round with no high or medium finding and no
regression against 9902e0a accepts it, and each low finding is
recorded in deferred-tasks.md with its file, an example and where to
re-check it. The phase register's PR95 checkpoint records the ruling;
every other increment keeps two clean complete cumulative rounds.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 9 of the PR95 increment's review (at 6f7dd34) confirmed 18
findings: two high, eight medium, seven low and a nit. Under the
maintainer's 2026-10-21 acceptance bar every high and medium finding
is corrected here; three lows are recorded in deferred-tasks.md.

Core projection (packages/core/src/tools/bounding.ts):
- Round 8 compared whole views with === inside its loops and walked a
  shared run of backslashes once per mask, both quadratic at admitted
  size (SEC-R9-1, SEC-R9-2). Each view of a quoted value is now built
  once per pass and its presence decided once, and keepingEscapes
  remembers each run's start at every end it serves.
- A value whose line break a scheme token takes is reread with and
  without the key rule's own masks, so another key's value that takes
  its closing quote or ends at its opening one no longer keeps it open,
  and the key rule's first decision also reads views with the scheme
  labels' tokens filled, which only move a closing a plain reading
  reaches (R9-SCRUB-1, R9-GEN-1).
- A summary masks a value that one reading stops at a line break
  through its later closer as well as through a reading that closes
  early (R9-SCRUB-2).
- A structured summary reads the unwalked JSON once: each value the
  walk masks whole goes back where the walk left a private-use
  placeholder, as a whole JSON value or as the text of a --flag=value
  argument, and its mask is a range of that same pass. A quoted value
  or a private-key block that runs into or out of a credential member
  is masked across members again, and round 8's kept PEM markers are
  withdrawn (R9-SCRUB-3, R9-SCRUB-4, R9-GEN-2).
- A summary reads private-key markers that a line break, a tab or more
  than one space splits; the journal reads them as before (R9-SCRUB-5,
  R9-GEN-3).

Tests and controls:
- Witnesses for every finding and an admitted-size case deadline for
  the four quadratic shapes in the journal, display and summary paths;
  an opaque parameter in a URL username keeps its host (TST-R9-1).
  A mutation run of 144 mutants kills 139; five are output-neutral.
- core-summary-pem-markers, core-summary-whole-json and
  core-unclosed-closing-view are re-anchored, and
  core-key-value-through-masks now targets round 7's reading through
  earlier masks, since the unclosed reread also reads round 6's quoted
  witnesses; the family stays 130 controls / 513 targets and witnesses
  in two shards of 65, whose local exports held 1,177 files each and
  measured 50.19 MiB and 50.09 MiB.

Docs: built-in-tools.md (the readings, the unwalked summary, wrapped
markers, the 33-character glued-key window, and the terminal-sequence,
indented-value and private-key block limits), effect-journal.md §11,
deferred-tasks.md (TST-R9-2, R9-GEN-4, CRIT-R9-1, the freshness line,
and two author-found private-key block gaps, unchanged since base,
pending the maintainer's confirmation), the phase
register's PR95 rows, round table, ruling text and gates, the secrets
controls README and the status lines.

The maintainer's round cap of 2026-10-21 makes round 10 the last: after
it only high findings are corrected, and its other findings are
recorded in deferred-tasks.md before Step 9a.1 begins.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 10 of the PR95 increment's review (at 59c6449), the last under
the maintainer's 2026-10-21 round cap, reported eleven verified
findings: seven medium and four low as verified (three of the mediums
are one finding), no high and no blocker. On 2026-10-22 the maintainer
counted GEN-R10-1 as high and approved the two private-key block gaps
found after round 9 as deferrals; every other finding is recorded in
deferred-tasks.md, as the round cap provides.

Core projection (packages/core/src/tools/bounding.ts):
- In a structured result's JSON a query value at the end of a line ran
  on through the next line's key and took the backslash of its value's
  escaped quote, so a `cat .env` result's POSTGRES_PASSWORD="…" after
  `?sslmode=require` showed in the summary that agent:tool_result
  carries (GEN-R10-1, unchanged since base). A summary now opens a
  key's value whose escaped opening quote ends an earlier mask's
  backslashes where that mask keeps them, reading it only in the views
  that keep them. The journal keeps its reading, so v5 digests do not
  change; a test pins that.
- The docblocks that counted one summary difference, or said that no
  text rule reads a whole-masked member, are corrected (DOC-R10-2).

Tests and controls:
- A test with five witnesses (run_command, MCP text part, CRLF, Mongo
  and a tab) and a journal pin; 218 bounding cases. A mutation run of
  six mutants on the new code kills four; the two survivors are
  output-neutral, since a mask the kept view keeps ends before a
  delimiter, where a view that keeps the backslashes closes no later
  than the filled one.
- core-quoted-value-scan and core-escaped-quote are re-anchored on the
  changed lines; the family stays 130 controls / 513 targets and
  witnesses in two shards of 65, whose local exports held 1,177 files
  each and measured 50.24 MiB and 50.14 MiB.
- Differentials against 59c6449 and base: the journal and display text
  identical on 250,000 inputs; every summary that differs on 320,000
  inputs shows a subsequence of 59c6449's visible text; no
  planted-token regression in the generators or in an exhaustive run
  of 837,606 sequences.

Docs: built-in-tools.md (the kept opening and its journal limit; the
wrapped markers' whitespace bound, the U+E000 placeholder's fallback and
a PEM END marker glued to another match, named where the document
claimed more), effect-journal.md §11 (the closing readings of a value
that closes, INT-R10-2), deferred-tasks.md (round 10's medium and low
findings with examples and re-checks, the journal half of GEN-R10-1,
the two approved private-key block entries, the PR95 items of the
PR #95 review closed), the phase register (round 10, the rulings, the
closure and the round-9 records that DOC-R10-1 found stale), the
secrets controls README and the status lines.

The increment closes under the round cap. It is not independently
accepted in the sense of the approved bar: round 10 was NONCLEAN, and
this correction has author verification only.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's engine coverage floor job failed on 9eb3815: round 9's
admitted-size case 'keeps every projection linear where masks share a
run of backslashes or end in one' ran ten 8-MiB projections in one case.
It takes 3.7 s plain and 7.2 s under local V8 coverage, and on the
GitHub runner it passed the 15 s case deadline (bounding.test.ts:2964,
"Test timed out in 15000ms"). The case is now one case per shape (three
input shapes and the structured summary), with the same inputs,
assertions and deadline; each takes 1.1-2.8 s under local coverage. No
deadline is extended.

The core projection cases go from 218 to 221, so CORE_PROJECTION_CASES,
the provenance pin and the secrets controls README follow. The local
coverage gate passes: llm 97.79 % lines / 95.69 % branches, mcp 96.56 /
91.29, core (measured) 96.71 / 91.06.

Refs: ADR-0113
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI's filesystem causal controls failed on 2a1f735 at export with
"Raw phase manifest bound" (tools/causal-controls/retention.mjs:400),
and the lint · typecheck · test job's guard then refused its result.
Every phase retains an exact manifest of the whole source graph; once
Wave 3's PR95 increment grew that graph, a manifest measured 203,421
bytes and the 330 manifests of one export 67,128,930 bytes, 20,066 over
the unchanged 64-MiB retained-evidence bound. The family's capacity
arithmetic had counted files only (1,987 of 2,000).

As the secrets family did in PR95's round 7, the filesystem job now
runs two legs that each run and export a disjoint round-robin half
(`--shard k/2`, fail-fast off, one artifact per leg) under the
unchanged per-phase, case, output, 2,000-file and 64-MiB bounds. Both
shards ran locally with the export at 2a1f735's source: 55 controls
each, all passing; 165 manifests (33,564,465 bytes) and 997 files per
export, 25.65 MiB and 25.59 MiB.

require-w8-acceptance.test.mjs now checks both families' sharded jobs
(an unsharded filesystem command fails it), and the filesystem
controls README records the bound and the measurement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wave 3 Step 9a.1, the first increment of the CLI release map/custody
substrate (#46, ADR-0111): the candidate build and its prepack
finalizer. The receipt stays compiled_package_test; symbolication, the
private map manifest, custody and the release state controller remain
Steps 9a.2-9d.

- Owned candidate tree (tools/cli-package/candidate.mjs): the producer
  never builds in the developer's workspace. It records the checkout's
  commit and porcelain status, copies every file Git would ship (by the
  repository's ignore rules and the developer's global excludes file,
  which is the only global setting it reads) into a fresh owner-only
  tree with exact modes, refuses if the commit or status changed during
  the copy, installs the locked closure offline, frozen and without
  lifecycle scripts, and verifies that every workspace link resolves
  inside the tree.
- Producer (produce.mjs, run by pack.mjs): fresh uncached upstream tsc
  maps for the five engine packages, the sole prepack build, a build
  cache check before the seal, owner-only staging outside the artifact,
  and removal of the private root on every path; its composition is
  tested through injected ports.
- Prepack (prepack.mjs) runs tsup once; with a validated staging
  directory the candidate-mode build emits external maps without
  sourcesContent and the finalizer (finalize.mjs) accounts for every
  output, validates every upstream and final map, rewrites sources to
  repository-relative original TypeScript, removes only the exact map
  trailers before hashing and stages maps and metafile privately.
- The seal reads the candidate and its staging explicitly; the closure
  guard is map-aware and refuses any map or map link in an ordinary
  build; the PR #95 fold-ins remove the archive's dead TAR_FORMAT
  branches and replace the installed smoke's ancestor loop.
- release.yml's publish job runs a fail-closed guard first: its entry
  point exits 1 before it evaluates its rule, so no artifact and no way
  of invoking it can reach npm publish until Step 9d's custody exists
  (the Interim release guard ruling of 2026-10-19).

An internal pre-review of the prepared change found seventeen issues,
none high once verified; all are corrected here (the guard's entry
point, the tested producer, the global excludes, exact modes and the
test gaps). A mutation run of twenty mutants over the new code kills
all of them. The full local CI then caught a top-level await in
closure.test.mjs that ran after most tests were registered, so a slow
fixture build let the runner remove the file's scratch directory before
a later test; both fixtures are now built before the first test.

Docs: packaging.md (Status and the 9a.1 implementation boundary), the
release runbook, an ADR-0051 amendment, the tooling README, the phase
register (9a.1 row and checkpoint, and the PR95 closure's post-closure
CI corrections 2a1f735 and 4c3cafe), deferred-tasks.md (the PR #95
tooling nits) and the status lines.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 1 (at a72c7ec) reported nine verified findings, two medium; its
plan lens was clean.

- An interrupted producer (SIGINT, SIGTERM) left its private maps,
  metafile, hash record and candidate copy in the temporary directory
  while its detached pnpm pack ran on. runCommand now tracks every live
  child tree and stopLiveCommands() kills them synchronously; the
  producer handles SIGINT, SIGTERM and SIGHUP by stopping them, removing
  the private root and build home, emptying the artifact directory and
  exiting with the signal's code. A producer killed outright is a named
  limit.
- Private staging must be canonical and outside the candidate by real
  path, the repository's included.
- Killing tests for the global-excludes wiring (and check.mjs's producer
  environment, now producerEnvironment()), the placement check's
  artifact half, the candidate's file-count and byte bounds (now
  injectable limits) and three finalizer refusals.
- CLAUDE.md and AGENTS.md name 9a.1 as the candidate build and
  finalizer; the ci.yml pack step comment mirrors release.yml; the
  register's post-closure CI record now matches the runs, and the 9a.1
  checkpoint records round 1.

Gates: lint:tools, typecheck:tools and every pnpm run ci step outside
the three control families (whose captured sources are unchanged since
a72c7ec's full run), with the real producer in smoke:cli-package; a
mutation run of nineteen mutants over the corrections kills all of them.

Refs: ADR-0111
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 2 (at 8ddb7ec) reported twelve verified findings, four medium;
its plan and integration lenses were clean.

- The producer's finally removed its interrupt handlers before the
  final removal, so an interrupt during it ended the process with the
  private root half removed. The handlers now stay until everything
  private is gone; the removal is a port, and a test interrupts it.
- SIGHUP, the handlers' removal and a whole build tree (a grandchild in
  the build's process group) are tested.
- Path containment is decided by segments in one shared helper
  (paths.mjs); '..private' is inside.
- Private staging and the producer's placement compare the real paths
  and letter case the filesystem stores (realpathSync.native).
- pnpm smoke:cli-package forwards an interrupt to the producer and the
  consumer it started (forwardInterrupts) instead of ending first.
- The finalizer gives its maps directory an exact mode; staged and
  public modes are tested under umask 0277; further finalizer and
  staged-closure refusals gained killing tests.
- The ADR-0051 note links ADR-0111, and the 9a.1 checkpoint records
  round 2.

Gates: lint:tools, typecheck:tools and every pnpm run ci step outside
the three control families (their captured sources are unchanged), with
the real producer in smoke:cli-package; a mutation run of eleven
mutants over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 3 (at 184562b) reported eleven verified findings, two medium;
its security lens was clean.

- An interrupt that arrived after the build's last child exited, during
  the seal or the final removal, was dropped: Node delivers a caught
  signal only when the event loop next polls, and the handlers were
  removed first. Round 2's test asserted exit 0 and so pinned the
  dropped interrupt. exitOnInterrupt (process.mjs) now keeps the
  handlers until the loop has polled once more. That takes two turns
  when the work ran from an I/O callback; one turn was measured to drop
  the signal. The producer then empties the artifact directory and
  exits with the signal's code, tested from an I/O-callback tail.
- An interrupted consumer stops its children, the bootstrap witness's
  included (tracked in tools/cli-bootstrap), removes its installation
  and fixtures and exits with the signal's code; completeConsumer
  requires that abandon step.
- pnpm smoke:cli-package starts no later stage once an interrupt
  arrived, removes its root with retries, and is tested end to end
  from a copied tooling tree (check.test.mjs).
- The producer's isolated build environment, its native real path of
  the checkout and the prepack catch-all gained killing tests.
- Git reading the original checkout uses no home, global or system
  configuration (isolatedGitEnvironment); a skip-worktree or
  assume-unchanged file makes the source identity dirty.
- An upstream map may map nothing only for tsc's type-only
  `export {};` output.
- packaging.md, the README, current.md and the 9a.1 checkpoint record
  the corrections and round 3.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
filesystem and secrets families (their captured sources are unchanged),
with the real producer in smoke:cli-package, and the acceptance tools
under CI=true GITHUB_ACTIONS=true; a mutation run of thirty-one
mutants over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 4 (at aa5c994) reported ten verified findings, three medium;
its integration and critic lenses were clean.

- An interrupted consumer killed the installed smoke's worker before
  its own removal: the worker's directory stayed under the report
  directory and the native fixture target the installed CLI had
  started in its own process group ran on as an orphan. The fixture's
  processes now end once the smoke worker that owns their case is
  gone, and completeConsumer, given the report directory as a required
  supportRoot, abandons the bootstrap witness's children and fixtures
  and the smoke's worker roots before removing the installation.
  Interrupting the real consumer during the native cases leaves no
  process and no directory.
- The local check's ordinary failures, its summary and its hand-off of
  the store and excludes variables are tested.
- A dirty identity's path to the seal, the producer's removal of engine
  build state and the bounded children's process group are tested.
- "Owner-only" is qualified where POSIX modes are not observable; the
  candidate copy's umask wording and the checkpoint's scope list are
  corrected, and the register records round 4.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
three control families (their captured sources are unchanged), with the
real producer and consumer in smoke:cli-package, and the acceptance
tools under CI=true GITHUB_ACTIONS=true; a mutation run of fifteen
mutants over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 5 (at d51a243) reported seven verified findings, one medium;
its plan, correctness and integration lenses were clean.

- prepareCandidate's link verification is tested as composed: an
  install that resolves an engine package or the root dependency
  directory to the original checkout refuses after the install.
- The candidate copy refuses a listed path whose parent directory below
  the checkout is a link, so a tracked directory replaced by an ignored
  link cannot bring in bytes from outside the checkout.
- A core.excludesFile that is present but empty disables global
  excludes, as in Git, instead of falling back to the default file.
- Settled children leaving both live-child sets, the exact
  `export {};` rule, each field of a facade's empty map, an interrupt
  the consumer outlives and the smoke-root prefix limit are tested.
- packaging.md and the 9a.1 checkpoint record the corrections and
  round 5.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
three control families (their captured sources are unchanged), with the
real producer and consumer in smoke:cli-package, and the acceptance
tools under CI=true GITHUB_ACTIONS=true; a mutation run of ten mutants
over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 6 (at 3c6c5dc) reported six verified findings (four distinct),
one medium; its plan and correctness lenses were clean.

- A test pins that a leader-exit native descendant watches the smoke
  worker, not its exited parent, so the product's group reap stays
  what the installed control proves.
- The staging directory's ownership check has a killing test.
- Every wait for the local check's exit in check.test.mjs is bounded.
- Five live status statements that still said Wave 3 Steps 9–19 had
  not started now say that Step 9a.1 is under review; the register
  records round 6.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
three control families (their captured sources are unchanged), with the
real producer and consumer in smoke:cli-package, and the acceptance
tools under CI=true GITHUB_ACTIONS=true; a mutation run of three
mutants over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 7 (at 0e59f5f) reported three verified low findings; its plan,
security, correctness and integration lenses were clean.

- Git reading the original checkout ignores replace refs
  (GIT_NO_REPLACE_OBJECTS), so a replace ref on HEAD can no longer make
  a substituted tree look clean to the source identity; a
  real-repository test pins it.
- The linked-parent refusal is tested for a link above the listed
  file's own directory and below the root, for both ignore routes.
- The fixture owner tests bound every wait and clean up in an
  after-hook, so a regression fails within the test and leaves no
  process; packaging.md and the register record round 7.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
three control families (their captured sources are unchanged), with the
real producer and consumer in smoke:cli-package, and the acceptance
tools under CI=true GITHUB_ACTIONS=true; a mutation run of five mutants
over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 8 (at c691ef9) reported two verified low findings and two nits,
all from its tests lens; plan, security, correctness, integration and
the critic were clean.

- The finalizer's pre-write refusals for a non-flat runtime output and
  for a runtime output whose own map entry was swapped are tested.
- The excludes lookup's forwarded configuration-location variables are
  pinned, with real-Git cases for an excludes file set only in an XDG
  configuration directory or a GIT_CONFIG_GLOBAL file.
- Further refusals gained tests (upstream name type, metafile shape,
  metafile and map byte bounds, the pinned pnpm version), as did the
  producer's exact modes under a restrictive umask; the reference index
  row and the register record round 8.

Gates: lint:tools, typecheck:tools, every pnpm run ci step except the
three control families (their captured sources are unchanged), with the
real producer and consumer in smoke:cli-package, and the acceptance
tools under CI=true GITHUB_ACTIONS=true; a mutation run of thirteen
mutants over the corrections kills all of them.

Refs: ADR-0111, ADR-0051
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik and others added 4 commits October 26, 2026 03:43
Round 2 at 5cda645 reported two mediums:
- the import control resolved every package manifest against the
  repository root, but pnpm runs a package's scripts in its directory,
  so a nested script could reach custody.mjs unseen; the control now
  refuses any quoted reference, outside the gate, to a file named
  custody.mjs, the one tracked file of that name, with separators,
  queries, fragments, percent-escapes and case normalized;
- a trailing separator bypassed the gate's link refusal, because
  lstat('link/') follows the link; the destination and the private
  root must now be normalized absolute paths, and the destination is
  checked before it is probed for a receipt.

Lows corrected: the sealer reads its options inside its fixed-code
boundary; the validator reads the origin and inputs once; a dynamic
import in the receipt's closure must be one string literal; a recipient
root whose parent is a file is never absent; the 9d handoff widens all
three directory admissions together. One low is recorded: encryptOnce
called directly still follows a trailing-separator link.

Refs: #46, ADR-0111
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 3 at 463a047: the plan and integration lenses were clean and the
security sitting reported lows only. The tests lens reported four
mediums, all in tests or static controls, the code itself correct:
- a non-EEXIST link failure was untested; an injected EPERM now
  refuses UNEXPECTED, publishes nothing and keeps the record sealable;
- the gate's refusal of a linked tarball was untested;
- no test pinned the patterns' trailing anchors;
- a template literal hid a custody.mjs reference from the control;
  the control now refuses any mention of the name outside the gate,
  comments included, after JavaScript and percent escapes, separators
  and case are normalized.

Lows corrected: the closure scan reads every specifier after from or
import whatever the spacing; new tests for single reads, throwing
options, same-count renames, the toolchain bounds and the receipt bytes
reuse returns; the contract's diagnostics names every path's code. The
generated sweep gained throw, anchor, logical-operator, bound and
statement-deletion operators: 233 mutants, three more gaps fixed, ten
argued equivalent.

Refs: #46, ADR-0111
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Round 4 at 563402e, a new complete cumulative review of
819f8cb..563402e with every reviewer in its own worktree, reported no
blocker, high or medium finding and no regression; its separate
crypto/custody security sitting found lows only. The full pnpm run ci
passed plain and alone at that head (test:acceptance-tools 1024 of 1024,
all 25 product map controls, every causal-control family), and the
custody and release tests passed on Ubuntu 24.04 with GnuPG 2.4.4.

Two lows are corrected here (the contract's diagnostics and the
custody.mjs control's escape claim); the rest are recorded in
deferred-tasks.md with their re-checks. This commit changes no code.
Step 9c is next.

Refs: #46, ADR-0111
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer paused Wave 3 after Step 9b.2 on 2026-10-26 and put the CLI
MVP first; D58-D72 were ruled on 2026-11-04 from a multi-agent review of the
roadmap documents and the code at 5a328bc.

- current.md is reduced from 2,095 to 420 lines: live status, the MVP order
  (Gate 0, MVP-1...MVP-10, then v0.2.1), the after-MVP queue with Waves 3-7,
  the decision table with D58-D72, standing rules and history links. Every
  inbound anchor is kept.
- Unique records move verbatim: the 2026-07-26 execution plan with Waves 0-1
  and the PR #81 closing list (the #W15 definitions) to the Phase 2.5.5
  register, the 2.6.F and 2.6.C implementation records to Phase 2.6, and the
  W4 start-gate ruling to Phase 2.6.5. Duplicated history is deleted.
- Phase 2.5.5 records the priority reset: the pause, the evidence, the
  decisions, seven recorded Wave 3 splits with their order amendments, dated
  amendments to the Acceptance, Publication and interim release guard
  rulings, and NI's third green Windows run, which closes its clause.
- Phase 2.6 gains the CLI MVP additions (MVP-A1...A23) as the one home for
  new items, the D62 direction on the default grant, the ADR row 9/11 splits
  and the M2.6-1 note.
- The milestone spine gains M3.5 (CLI MVP, v0.2.0). deferred-tasks.md closes
  the NI entry and re-points CR-70, CR-71, #61 and the chat-resume entry.
- The CLAUDE.md and AGENTS.md status blocks are condensed to the current state.

Docs only; no source, test or workflow change.

Refs: ADR-0111, ADR-0057, ADR-0095

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coldtea-pr-lens

coldtea-pr-lens Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
  • Redraw · drawn at 64fb877, the branch has moved on

Architecture

Architecture diagram for HodeTech/Relavium at 64fb877

Play the walkthrough

Inside the changed components — 2 views

Component view — Packaging & Custody

The candidate tree, source map inspection, symbolication verification, and GnuPG archival components.

Architecture view of Component view — Packaging & Custody in HodeTech/Relavium

Component view — Host & Security Hardening

Exact 64-bit filesystem pinning, projection settling, and diagnostic secret scrubbing.

Architecture view of Component view — Host & Security Hardening in HodeTech/Relavium

Data flow

Data flow diagram for HodeTech/Relavium at 64fb877

Follow each request


Review

✨ Nothing flagged · reviewed 64fb877


Tip

Run npx @coldtea/pr-lens-cli analyze --base origin/main on a branch, then npx @coldtea/pr-lens-cli render .pr-lens/graph.json. Same lenses, your own model key, before the pull request exists

🪧 More tips
  • Run npx skills add coldteadotai/pr-lens, then tell your coding agent: "Diagram the change you just made with PR Lens and attach it to the pull request."
  • Click the link under each diagram to open it on a canvas you can zoom, pan and step through
  • The diagrams are links. Click one to open it on the canvas, then press W or click play to walk through the change
  • Open a diagram on the canvas, then press W or click play to walk through the change one step at a time
  • The CLI's render reads .github/pr-lens.yml and applies your renames, exclusions and lane pins at draw time
  • Set github.comment.collapsed: true in .github/pr-lens.yml to fold the comment behind one View architecture… row. Drawing still runs as before
  • Set github.draw: on-demand in .github/pr-lens.yml and PR Lens stops drawing pull requests on its own. Comment @pr-lens draw on a pull request when you want that one drawn
  • Add .github/workflows/pr-lens.yml with coldteadotai/pr-lens/packages/action@v0 and your model provider's key as its api-key to run PR Lens from your own CI. Any /chat/completions endpoint works
  • After a push, tick Redraw to draw the latest commit
  • Switch GitHub to dark mode and the diagrams follow. The moving dots are this pull request's data in motion
  • Set github.description: false in .github/pr-lens.yml to stop PR Lens adding buttons to your PR descriptions

Thanks for using PR Lens! It's free for OSS. If it helped, a star helps other teams find it.  Star PR Lens on GitHub

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @cemililik, this pull request is too large to review

It changes 35,039 lines. Sourcery can review pull requests with up to 20,000 changed lines.

@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 179 files, which is 29 over the limit of 150.

To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to Team to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 58aadeb1-00d5-44c6-af7f-5df632928196
Commits

Reviewing files that changed from the base of the PR and between 6d2a737 and f25fb83.

Files selected for processing: 179
  • .claude/skills/commit-and-pr/SKILL.md
  • .github/workflows/ci.yml
  • .github/workflows/models-catalog.yml
  • .github/workflows/release.yml
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • apps/cli/package.json
  • apps/cli/src/authoring/snapshot-notice.test.ts
  • apps/cli/src/commands/fs-invocation.test.ts
  • apps/cli/src/commands/native-forwarding.test-support.ts
  • apps/cli/src/engine/tool-host/fs-policy.test.ts
  • apps/cli/src/engine/tool-host/fs-policy.ts
  • apps/cli/src/engine/tool-host/native-identity.test.ts
  • apps/cli/src/engine/tool-host/process-close.test.ts
  • apps/cli/src/harness/mcp-orphan.e2e.test.ts
  • apps/cli/src/process/supported-leaf-exports.test.ts
  • apps/cli/src/render/sanitize.test.ts
  • apps/cli/src/render/sanitize.ts
  • apps/cli/src/render/tui/final-summary.test.ts
  • apps/cli/src/render/tui/final-summary.ts
  • apps/cli/src/workflows/catalog.test.ts
  • apps/cli/src/workflows/catalog.ts
  • apps/cli/tsconfig.config.json
  • apps/cli/tsup.config.ts
  • docs/decisions/0051-cli-distribution-thin-bundle-private-engine.md
  • docs/decisions/0105-filesystem-roots-are-invocation-grants-not-project-authority.md
  • docs/decisions/0106-media-publication-requires-bounded-verified-bytes.md
  • docs/reference/cli/README.md
  • docs/reference/cli/commands.md
  • docs/reference/cli/packaging.md
  • docs/reference/cli/regression-harness.md
  • docs/reference/shared-core/README.md
  • docs/reference/shared-core/built-in-tools.md
  • docs/reference/shared-core/diagnostic-secret-backstop.md
  • docs/reference/shared-core/effect-journal.md
  • docs/reference/shared-core/tool-registry.md
  • docs/roadmap/README.md
  • docs/roadmap/current.md
  • docs/roadmap/deferred-tasks.md
  • docs/roadmap/phases/phase-2-cli.md
  • docs/roadmap/phases/phase-2.5.5-hardening-and-remediation.md
  • docs/roadmap/phases/phase-2.6-conversational-authoring.md
  • docs/roadmap/phases/phase-2.6.5-core-reliability-remediation.md
  • docs/runbooks/local-dev-setup.md
  • docs/runbooks/release-a-surface.md
  • docs/standards/code-review.md
  • docs/standards/error-handling.md
  • package.json
  • packages/core/src/tools/bounding.test.ts
  • packages/core/src/tools/bounding.ts
  • packages/core/src/tools/fixed-point.test.ts
  • packages/core/src/tools/fixed-point.ts
  • packages/core/src/tools/registry.test.ts
  • packages/core/src/tools/registry.ts
  • packages/db/src/media-store-marker-acquisition.test.ts
  • packages/db/src/media-store-native-identity.test.ts
  • packages/db/src/media-store-paths.test.ts
  • packages/db/src/media-store-paths.ts
  • packages/db/src/media-store-read.ts
  • packages/db/src/media-store-temporaries.test.ts
  • packages/db/src/media-store-temporaries.ts
  • packages/db/src/media-store.ts
  • packages/llm/src/fixed-point.test.ts
  • packages/llm/src/fixed-point.ts
  • packages/llm/src/scrub-secrets.test.ts
  • packages/llm/src/scrub-secrets.ts
  • packages/llm/src/split-credential-probe.test.ts
  • packages/llm/src/split-credential-probe.ts
  • tools/budget-replay-compat/README.md
  • tools/budget-replay-compat/evidence-retention.mjs
  • tools/budget-replay-compat/lifecycle-smoke.mjs
  • tools/bundle-closure/check.mjs
  • tools/bundle-closure/closure.mjs
  • tools/bundle-closure/closure.test.mjs
  • tools/bundle-closure/inventory.mjs
  • tools/causal-controls/README.md
  • tools/causal-controls/provenance.mjs
  • tools/causal-controls/provenance.test.mjs
  • tools/causal-controls/run.mjs
  • tools/ci/require-packed-cli.test.mjs
  • tools/ci/require-w8-acceptance.test.mjs
  • tools/ci/workspace-scheduling.test.mjs
  • tools/cli-bootstrap/child.mjs
  • tools/cli-bootstrap/process.mjs
  • tools/cli-bootstrap/witness.test.mjs
  • tools/cli-package/README.md
  • tools/cli-package/archive.mjs
  • tools/cli-package/archive.test.mjs
  • tools/cli-package/binding.mjs
  • tools/cli-package/binding.test.mjs
  • tools/cli-package/candidate-fixture.mjs
  • tools/cli-package/candidate.mjs
  • tools/cli-package/candidate.test.mjs
  • tools/cli-package/check.mjs
  • tools/cli-package/check.test.mjs
  • tools/cli-package/child-exit.mjs
  • tools/cli-package/consume.mjs
  • tools/cli-package/consumer-lifetime.mjs
  • tools/cli-package/consumer-lifetime.test.mjs
  • tools/cli-package/container.mjs
  • tools/cli-package/container.test.mjs
  • tools/cli-package/custody-error.mjs
  • tools/cli-package/custody-fixtures/expired-primary.asc
  • tools/cli-package/custody-fixtures/expired-subkey.asc
  • tools/cli-package/custody-fixtures/generate.mjs
  • tools/cli-package/custody-fixtures/good-aead.asc
  • tools/cli-package/custody-fixtures/good.asc
  • tools/cli-package/custody-fixtures/other.asc
  • tools/cli-package/custody-fixtures/pins.json
  • tools/cli-package/custody-fixtures/revoked-primary.asc
  • tools/cli-package/custody-fixtures/revoked-subkey.asc
  • tools/cli-package/custody-fixtures/rsa2048.asc
  • tools/cli-package/custody-fixtures/rsa3072.asc
  • tools/cli-package/custody-fixtures/sign-only.asc
  • tools/cli-package/custody-fixtures/two-encryption-subkeys.asc
  • tools/cli-package/custody.mjs
  • tools/cli-package/custody.test.mjs
  • tools/cli-package/files.mjs
  • tools/cli-package/finalize.mjs
  • tools/cli-package/finalize.test.mjs
  • tools/cli-package/frame-capture-hooks.mjs
  • tools/cli-package/frame-capture-keyring.mjs
  • tools/cli-package/frame-capture.mjs
  • tools/cli-package/gnupg.mjs
  • tools/cli-package/gnupg.test.mjs
  • tools/cli-package/gpg-fixture.mjs
  • tools/cli-package/manifest.mjs
  • tools/cli-package/manifest.test.mjs
  • tools/cli-package/map-controls-core.mjs
  • tools/cli-package/map-controls.mjs
  • tools/cli-package/map-controls.test.mjs
  • tools/cli-package/pack.mjs
  • tools/cli-package/paths.mjs
  • tools/cli-package/paths.test.mjs
  • tools/cli-package/pipeline.mjs
  • tools/cli-package/pipeline.test.mjs
  • tools/cli-package/prepack.mjs
  • tools/cli-package/prepack.test.mjs
  • tools/cli-package/process.mjs
  • tools/cli-package/process.test.mjs
  • tools/cli-package/produce.mjs
  • tools/cli-package/produce.test.mjs
  • tools/cli-package/production-failure.mjs
  • tools/cli-package/release-binding.mjs
  • tools/cli-package/release-binding.test.mjs
  • tools/cli-package/release-custody.mjs
  • tools/cli-package/release-custody.test.mjs
  • tools/cli-package/release-fixture.mjs
  • tools/cli-package/release-guard-core.mjs
  • tools/cli-package/release-guard.mjs
  • tools/cli-package/release-guard.test.mjs
  • tools/cli-package/release-recipient.mjs
  • tools/cli-package/release-recipient.test.mjs
  • tools/cli-package/release-scope.test.mjs
  • tools/cli-package/seal.mjs
  • tools/cli-package/seal.test.mjs
  • tools/cli-package/smoke/README.md
  • tools/cli-package/smoke/index.mjs
  • tools/cli-package/smoke/installation.mjs
  • tools/cli-package/smoke/installation.test.mjs
  • tools/cli-package/smoke/native-descendant.cjs
  • tools/cli-package/smoke/native-process.mjs
  • tools/cli-package/smoke/native-process.test.mjs
  • tools/cli-package/smoke/native-status.cjs
  • tools/cli-package/smoke/worker.mjs
  • tools/cli-package/source-map.mjs
  • tools/cli-package/source-map.test.mjs
  • tools/cli-package/symbolicate.mjs
  • tools/cli-package/symbolicate.test.mjs
  • tools/cli-package/ustar-fixture.mjs
  • tools/cli-package/ustar.mjs
  • tools/cli-package/ustar.test.mjs
  • tools/fs-policy-controls/README.md
  • tools/fs-policy-controls/mutations.mjs
  • tools/overflow-capture/capture.mjs
  • tools/overflow-capture/check.mjs
  • tools/secrets-controls/README.md
  • tools/secrets-controls/mutations.mjs

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

cemililik and others added 9 commits November 4, 2026 13:56
Three verified PR #96 review findings in the journal and display projection:

- A flag whose last word names a credential (--auth-token, --access-token,
  --pwd, --x-api-key, --credentials) now masks its argument, as the
  object-key rule already masked those names as keys; curl's -u/--user
  user:password pair is masked in every spelling. A flag that only mentions
  a credential (--password-stdin, --token-file, --max-tokens) or starts with
  no- keeps the next argument's identity.
- A `,` or `/` in a URL password no longer ends the authority: the userinfo
  is read on to its `@`, unless a port, a public query contact, another
  scheme or a JSON/quote delimiter comes first. The leaking test pins are
  replaced; quotes, `}` and `]` stay documented differences.
- boundForModel counts the inline media the texts omit (data URIs, base64
  sources, binary buffers) against the model-facing ceiling. A 30 MiB data
  URI was measured as its marker, passed whole to the model and settled
  into history.db; the spill and preview stay media-free.

Also clears Sonar's reduce-without-initial-value bugs (S6959, S7727) and two
smells in the escape view. The secrets controls' pinned case counts follow
the new tests (234 bounding, 116 registry); their unnecessary String.raw
literals are plain strings with identical values. A manual mutation sweep
killed all 24 mutants. Affected v5 journal rows refuse matching prepare, as
the PR95 tightening documented.

Refs: ADR-0080, ADR-0089

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The final summary's error row collapsed separators with sanitizeInline but
did not mask, unlike every other sink for untrusted diagnostics. It now uses
sanitizeUntrustedInline, with a regression test (PR #96 review, low).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
tsup.config.ts decides whether a build carries source maps, yet no tsc
program covered it (PR #96 review, low). A second typecheck program now
does, and the one index-signature access it found uses bracket notation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR #96 review and SonarCloud findings in tools/:

- The packed-manifest runtime dependency check is an allowlist (exact,
  caret or tilde registry versions) shared by the archive inspector and
  the receipt binding, so npm:, github:, git and tarball URLs, `*` and open
  ranges are refused; negative tests on both paths.
- copyMatchesCommit asks Git for each copy's blob id (hash-object
  --no-filters, batched under the Windows command-line limit) instead of a
  plain SHA-1 of ours (S4790), keeps the ordinary-file proof that
  hash-object would follow a link past, and accepts the repository's object
  format; tests cover several batches, a linked copy and a short answer.
- Every flagged sort states its code-unit order with one comparator
  (S2871 x24); localeCompare would reorder manifests and digests.
- Fixtures resolve gpg, gpgconf and ps to absolute paths (S4036); the two
  maintainer/CI path arguments get an absolute-path check and a NOSONAR
  rationale (S8707).
- Mechanical smells: S1121, S2094, S3358, S4043, S4123, S6353, S6557,
  S6582, S7755, S7763, S7780, S7781, S7786.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d lanes

Maintainer rulings on the PR #96 review (2026-11-04):

- windows-concurrency drops its advisory label; it carries ADR-0106's
  Windows filesystem-policy scenarios, which name required CI. Branch
  protection adds it as a required check.
- publish runs in the `release` deployment environment, which is to hold
  NPM_TOKEN and its required reviewer; a repository secret is readable by
  any workflow a writer pushes. The guard is unchanged, and release.yml and
  packaging.md record that D59's release ADR supersedes its condition and
  must override ADR-0111's "missing custody cannot downgrade to test mode".
- The model-catalog nightly names each provider lane live or skipped, as a
  warning and in the job summary (only DEEPSEEK_API_KEY is configured).
- The coverage job's allowance rises 30 -> 45 minutes (15-27 measured) and
  the secrets causal-control legs' 60 -> 75 (up to 48 measured); their pins
  follow. The baseline-predecessor fetch merges a nested if (S1066).

Refs: ADR-0106, ADR-0111

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w's rulings

The D66 reset kept current.md's live status and moved its 2026-07-26 plan
verbatim, but a PR #96 review found its other unique records had left the
docs (983 of 1,844 non-empty lines). They are moved verbatim now, links
rewritten and headings lowered by one: the Wave 2/3 sections, the rows the
reset edited in place, and current.md's, CLAUDE.md's and AGENTS.md's status
blocks to Phase 2.5.5; the W7/W8 and Phase 2.6.5 records to Phase 2.6.5;
Phase 2's status to Phase 2; "Not started yet" to Phase 2.6. Five lines
remain by design (old metadata, two re-titled headings, one paraphrased
duplicate whose phase copy is canonical); both move notes say so.

Maintainer rulings (2026-11-04):
- code-review.md states the single-maintainer review practice.
- The LLM backstop's shape list moves to its reference home,
  docs/reference/shared-core/diagnostic-secret-backstop.md; error-handling.md
  keeps the rule. The module comment no longer claims an LF/TAB-split opaque
  Bearer value is joined, and the reference records that limit.
- ADR-0105/0106's 2026-10-25 corrections take the dated "Amended" form.
- 2.6.Q owns the stream usage-capture item; Step 9d owns a global
  encrypt-once record; the un-deferred item is "the CR-71 residual".

Also: README and the local-dev runbook describe what works (CLI MVP first;
a build-then-run CLI loop; desktop and extension not in the repo yet); the
Wave 3 count reads 32; MVP-3 and MVP-8 split the Home strip; the branch
rule matches AGENTS.md; and deferred-tasks.md records the review's verified
lows and the Sonar dispositions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR #96 ruling on NPM_TOKEN said the release prerequisites would carry
it: the publish credential is scoped to the release job before the guard
lifts, by npm trusted publishing or a protected `release` environment that
holds the token, with the repository-level secret deleted. The D59 release
ADR chooses between them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SonarCloud's re-analysis of PR #96 (gate OK) reported two cognitive
complexity increases from this correction: redactArgVector rose past the
bound (21) and visitUrlUserinfo from 18 to 21. The inline-credential and
comma/slash-password branches move into helpers with identical behavior,
and two cases now pin what the move must keep: a username key does not
swallow the host, and with no `@` to read on to the scan resumes inside the
authority. The keyring stub gains the real Entry's refusing methods instead
of a NOSONAR comment Prettier had moved off its line (S2094). Every mutant
of the helpers is killed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The PR #96 review fix that counts omitted inline media against the
model ceiling made the counter optional, and its three guards lifted
redactInlineMediaForText's cognitive complexity to 18 (SonarCloud
S3776, new on 2026-11-04). The observability projection now passes a
discarded counter instead, so the walk keeps its pre-review shape.
No behavior change: toText always passed a counter, and the event-field
projection ignores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

@cemililik
cemililik merged commit b3cf117 into main Nov 4, 2026
39 checks passed
cemililik added a commit that referenced this pull request Nov 4, 2026
… URL passwords masked

The D64 round 1 on the PR #96 review correction confirmed two regressions
from 0826f30 in the journal and display projections:

- A `-u`/`--user` whose value is no user:password pair consumed the next
  element, so a credential flag there (`unzip -u -P <pw>`,
  `--user --api-key <k>`) never masked its own value. Such a value is now
  processed as any element.
- The `,`/`/` password helper gave the username the text before the first
  `:` even after a raw `?`/`#`, so the key/value rules never saw a
  credential key there and its value's tail after the `@` stayed visible
  (`http://svc?token=a:b/c@d`). The username now owns that text only when
  no raw `?`/`#` comes first, as in the authority scan.

Each fix is pinned by new cases that fail when it is reverted. Comments
now state that a raw `?`/`#` after the delimiter keeps a password public
(a recorded limit) and that the seam refuses inline media in a tool
result, and the userinfo test no longer claims every spelling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
Lows from the D64 round 1 on the PR #96 review correction:

- The runtime-dependency allowlist follows semver's dot-separated
  prerelease identifiers, since npm reads `1.2.3-.` or `1.2.3-a..b` as a
  dist-tag; exact, caret, tilde and prerelease forms are now tested.
- A stranded JSDoc moves back to `assertPublicPackagePath`, and the custody
  fixture generator resolves GnuPG before it creates its temporary home.
- A test pins that the required coverage and Windows jobs keep their
  branch-protection names and carry no job-level `if` or
  `continue-on-error` (GitHub counts a skipped required check as passing).
- ci.yml no longer calls the Windows job advisory, and release.yml says
  the `release` environment is meant to hold NPM_TOKEN (MVP-A23), not that
  it does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
…review lows

- Mark the D61 development→main checkpoint done: PR #96 merged as b3cf117
  on 2026-11-04. current.md's status table, Gate 0, D61 row and history,
  the CLAUDE.md, AGENTS.md and README status lines, and a new D61
  checkpoint record in the phase 2.5.5 register with the merge facts,
  the evidence and the repository settings.
- Record the D64 round 1 on the review correction: five confirmed medium
  findings, all fixed (5a1006e, 3cb9712 and this commit). D64's bar
  still needs a clean round 2 over the corrected head.
- Correct the documents the round found false: the `release` environment
  does not hold NPM_TOKEN yet (packaging.md, current.md); a raw `?`/`#`
  after a URL password's `,`/`/` keeps it public; the seam refuses inline
  media in a tool result. The media bound now lives in built-in-tools.md,
  the argv rule states that it does not know the program, and
  effect-journal.md states the identity cost.
- Fix the round's documentation lows: a stale backstop pointer, the move
  notes (the five omitted lines named), six empty self-links in moved
  text, the Windows job's required-check basis, the admin bypass in
  code-review.md, and the 2.6.Q usage item narrowed to what W8 left.
- deferred-tasks.md: rewrite four PR95 DONE bodies to their closure state
  and annotate the entries the MVP pulled (the PR #96 review's R12b-03 and
  R12b-04); record the round's remaining lows and the review's 86
  unverified first-pass lows, triaged at f25fb83 (78 entries).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
The D64 round 2 on the PR #96 review correction confirmed a medium
regression from 0826f30: a boolean flag whose last word names a
credential (`--pass-credentials`, `--with-token`, `--ask-password`) masked
the element after it unconditionally, so a credential flag written there
was masked in its place and its own value showed in toolInput and the
journal digest (`helm ... --pass-credentials --password <pw>`). A
credential flag in value position is now processed as a flag, which
generalizes the round-1 fix for a boolean `-u`/`--user`.

With it, lows from the same round:
- the `--flag=value` spelling reads the flag name with the text rules, so a
  secret shape in a last-word flag's name stays masked;
- a comment restores that a `,` in a username still ends the authority;
- two surviving mutants are pinned (`-u=1000:1000`, a username key with a
  raw `?` in a `,`/`/` password).

Every new case fails when its fix is reverted. A differential oracle over
365,640 argument vectors finds no credential that 64fb877 masked and
this head shows, in either projection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
… check could skip

Lows from the D64 round 2 on the PR #96 review correction:

- A runtime-dependency requirement is at most 100 characters (npm reads a
  version over semver's 256 as a dist-tag), and a name follows npm's rules
  (no part begins with `.` or `_`, not `node_modules` or `favicon.ico`).
  Each guard of the shared allowlist now has a test that fails without it,
  including a non-string requirement.
- The required-check pin also asserts that the coverage and Windows jobs
  have no `needs`, that the Windows Build step and the steps before it and
  every coverage step carry no `if` or `continue-on-error`, and that `ci`
  keeps its context name; the reviewer's six surviving mutants now fail.
- ci.yml calls the Windows leg of #294 "then-advisory".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
The D64 round 3 on the PR #96 review correction found that each new
reading could still show text 64fb877 masked:

- Round 2's fix read a credential flag in value position as a flag, so a
  password spelled like one (`--password -MySecret`) showed (medium).
  Such an element is now masked as the previous flag's value and still
  masks the element after it, so neither reading leaks; the cost is the
  flag name in that position.
- The `,`/`/` password reading gave the username its text, which hid a
  credential-word username (`x-access-token:`) or a key after a raw
  `?`/`;`/`&` from the key rules, so a password's tail after a raw `@`
  showed. The username now owns no text there, so the key rules mask as
  they did before and the reading only adds a mask.

A superset oracle (markers in every position, flag-shaped values and
credential-word usernames included) finds no marker 64fb877 hid that
this head shows, over a million display, value and journal comparisons,
and finds 60,299 at 07f653e. New cases pin the password tail's five
stops and the glued `-u-` guard; every mutant of the sweep fails a test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
…ds and step shells

Lows from the D64 round 3 on the PR #96 review correction:

- The batch test asserts each hash-object command line stays within 16 KiB,
  and an answer with an extra line is no match. The blob id is exactly 40
  hex digits, as sourceIdentity admits only SHA-1 repositories.
- The allowlist's refusal is tested on optionalDependencies and
  peerDependencies too; its comment says leading zeros are admitted, as
  npm's and pnpm's loose reading accepts them.
- The required-check pin also refuses a job-level default shell and a step
  shell (only the headless smoke may name bash), and its comment states
  what a skip and a rename each do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
… masked flag names hidden

The D64 round 4 on the PR #96 review correction found the add-only
property still broken in two places (0826f30):

- When an authority ends in `scheme:` and the `/` delimiter begins a
  nested `//`, the `,`/`/` password reading masked to the nested URL's
  first `@` and moved the scheme scan past it, so that URL's password tail
  showed (`https://proxy:https://user:p@ss@target/`; medium). The reading
  now leaves a straddling `://`, and a backslash in its authority or tail
  (a JSON escape the key rules read), to the other rules.
- A last-word flag's `--flag=value` spelling showed a name the text rules
  mask together with its value (`--db-token=<v>`). The name now stays only
  where the text rules keep it; otherwise the element is masked whole. An
  anchored name (`--password=`) stays as before.

Two independent oracles, each first shown to catch 7a12ee6, find no
marker 64fb877 hid that this head shows: the round's reviewer's (about
2.2 million comparisons) and ours extended with flag-name, nested-URL and
escape shapes (one million). Each guard has a case that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
…the aggregate's guards

Lows from the D64 round 4 on the PR #96 review correction:

- copyMatchesCommit refuses a relative repository or candidate root
  (PACKED_CLI_CANDIDATE_ROOT): the ordinary-file proof read against this
  process while Git hashed against the checkout.
- The batch test pins that hash-object batches are packed, so a counter
  that is never reset (one git per path) fails it.
- The required-check pin also holds the `ci` aggregate's guard steps to
  exactly their keys and refuses a default shell on `ci`.
- ci.yml's floor-check comment names the three required checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
…rgv rules additive

The maintainer ruled on 2026-11-05 to stop the D64 rounds on the PR #96
correction after one careful pass. That pass found:

- The `,`/`/` URL password reading (added for the review's A4) cannot be
  shown to only add masks: the projection repeats until stable, and a
  reading's mask can change the text other rules read in a later pass.
  D64 rounds 1, 3 and 4 each found a credential it newly showed. It is
  removed: visitUrlUserinfo is again the base's, and the text projection
  equals 64fb877's on 200,000 generated texts. A4 is a recorded limit
  again; the test that pinned the reading now pins the base behavior.
- The argv rules provably only add masks: at the base an element was
  masked when the element before it was an anchored credential flag that
  was not itself masked; now whenever the element before it is any
  credential flag. A glued `-u` pair now keeps its `-u` only where the
  text rules keep it, like the `--flag=value` name (`-utoken:<v>`).

Both superset oracles (ours and the round-4 reviewer's, each validated on
a known-bad version) find no violation; core's 4,941 and the CLI's 5,828
tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cemililik added a commit that referenced this pull request Nov 4, 2026
…correction

- built-in-tools.md and effect-journal.md: the authority scan again ends
  at a `,` or `/` in a password (the review's A4, a recorded limit, and
  why the reading was removed); the argv rules state that they only add
  masks.
- deferred-tasks.md: A4 recorded with its trigger (a range-merging design
  validated by a superset oracle); the six entries about the removed
  reading closed as moot; the resolution noted after round 4.
- D61 record and current.md: the maintainer's 2026-11-05 ruling to close
  the correction with one careful pass instead of a fifth round, a
  recorded deviation from D64.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant