Skip to content

docs: confine every review pass to its worktree as CA-32, assigned to WP-4 - #28

Merged
constantin-jais merged 3 commits into
migrate/recover-codefrom
work/docs-ca-32-pass-read-confined
Oct 26, 2026
Merged

constantin-jais merged 3 commits into
migrate/recover-codefrom
work/docs-ca-32-pass-read-confined

Conversation

@constantin-jais

@constantin-jais constantin-jais commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

Why

The two second-family reviews of WP-3 (#27, Kimi K3 and GLM 5.3) rated as major a channel that the letter of INV-a does not cover:

  • The pi read tool resolves absolute and ~ paths outside the directory of the pass. Source: pi 1.4.0, dist/core/tools/path-utils.js:38-44, "Handles ~ expansion and absolute paths". The grep, find and ls tools do the same.
  • The verdict, free text included, is published under docs/reviews/ of this public repository.

A reviewed commit can therefore get a host file copied into detail and published. No deterministic control over the verdict can tell a copied secret from a comment, so only a boundary enforced outside pi closes the channel. The default driver has no activation table, so the channel is open on it today.

Owner decisions of 2026-10-24, verbatim:

  • R-13 (the mechanism that enforces CA-22): « Linux netns + nftables (Recommandé) »
  • K1 (how this channel binds activation): « 6e condition, deux pilotes (Recommandé) »

What changes

docs/cahier-des-charges.md:

  • CA-32, new. The pass reads nothing outside its detached worktree.
    • The boundary is enforced outside pi, by the R-13 mechanism.
    • No provider secret is readable from inside the pass: neither a mounted file nor an environment variable exposed by a process pseudo-file.
    • Until it holds, both drivers refuse real passes; --dry-run stays available.
    • Proved by a red vector, pass-read-confined, with a neutralisation (mounting the sentinel directory must let the read through).
  • CA-26:
    • The profile also carries --no-extensions and --no-approve, with their measured reason: an extension can register a tool under a built-in name and replace it (dist/core/agent-session.js:2893-2894; the shipped examples/extensions/ssh.ts:128-129 routes read over ssh).
    • The validation grep now targets fanout-core.ts, because WP-2 moved the pi spawn out of fanout.ts.
    • The "value at the commit read" names 7644bfd and stays red there.
  • R-13 records the mechanism and the options rejected the same day. R-16 records the channel and the decision.
  • Threat table: one row for the channel.
  • Range mentions (CA-18 à CA-32, R-11 à R-16) and provenance are updated.

docs/prd.md:

  • The WP-4 row now covers CA-32. Its scope gains:
    • tools/review/confinement/**, a new directory for the R-13 mechanism;
    • tools/review/*.ts, because both drivers must spawn their passes under that mechanism.
  • Three preamble sentences and the provenance say so.
  • The maturity line is unchanged.

After this merges, issue #20 (WP-4) will be updated to cite the merge commit as its PRD-ref. The work-package gate judges a package at the commit its issue cites.

Ordering

This PR goes before WP-3 (#27). The second round of #27, not pushed when this was written, will add the sixth condition pass-read-confined to the activation table, with satisfied: false and source CA-32; #27 is not merged without it. CA-32 therefore exists before any code cites it. R-16 is worded to hold on both sides of that merge: no true pass-read-confined condition exists, whether it is absent or false.

Verification

  • Norm reader (bun prd-read.ts <repo-root>, governance tools/quality/prd-model.ts): RESULT: pass. It warns that tools/review/confinement/** matches no tracked file; the PRD declares that directory as to be created by WP-4.
  • pi source lines cited above were re-read by the orchestrator on the installed pi 1.4.0 release.

Exempt: docs-only

🤖 Generated with Claude Code

… WP-4

Owner decisions of 2026-10-24, verbatim:
- R-13: "Linux netns + nftables (Recommandé)".
- K1: "6e condition, deux pilotes (Recommandé)".

The second-family reviews of WP-3 (Kimi K3, GLM 5.3, on #27) both rated
as major a channel that the letter of INV-a does not cover. The pi `read`
tool resolves absolute and `~` paths outside the pass directory (pi 1.4.0,
dist/core/tools/path-utils.js:38-44), and the verdict, free text included,
is published under docs/reviews/. A reviewed commit can therefore get a
host file copied into a public artefact. No deterministic control over the
verdict can tell a copied secret from a comment, so only a boundary
enforced outside pi closes the channel.

Requirements specification:
- CA-32: the pass reads nothing outside its detached worktree, enforced by
  the R-13 mechanism, with no provider secret readable from inside the pass
  (neither a mounted file nor an environment variable that a process
  pseudo-file would expose). Both drivers refuse real passes until it
  holds. Red vector `pass-read-confined`, with a neutralisation.
- R-16 records the channel and the decision. R-13 records the mechanism
  and the options rejected that day.
- CA-26: the profile also carries --no-extensions and --no-approve, with
  their measured reason (an extension can replace a built-in tool by name,
  dist/core/agent-session.js:2893-2894). The validation command points at
  fanout-core.ts, because WP-2 moved the pi spawn out of fanout.ts. The
  "value at the commit read" now names 7644bfd and stays red there.
- Threat table: one row for the channel.

PRD: the WP-4 row now covers CA-32. Its scope gains
tools/review/confinement/** (the R-13 mechanism) and tools/review/*.ts
(both drivers must run their passes under it). The WP-4 issue will cite
the commit of this change.

Norm reader: RESULT: pass.

Exempt: docs-only
Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com>
…acle

Second-family review of #28 (GLM 5.3, ACCEPTABLE on 0f306b2). Each finding
was checked against its source.

- Major, applied. R-16 named a `pass-read-confined` condition at
  `satisfied: false` that no branch carries yet: the second round of #27 is
  not pushed. The detection column now reads "no true condition", which
  holds both before and after #27 merges.
- Minor, applied. Network namespaces and nftables do not confine a file
  read. CA-32 now says that the container mounts confine the read, that the
  network namespace and its rules confine egress, and that the second does
  not replace the first.
- Minor, applied. The vector now requires that the error returned for the
  sentinel is byte-identical to the error for a missing path inside the
  worktree, so a refusal cannot tell whether a host file exists.
- Minor, rejected. The suggestion was to note in issue #20 that acceptance
  also runs check:review-tools. A work-package issue body must be exactly
  the form output (WORK-PACKAGES.md, "Nothing else is allowed in the
  body"). The full validation chain stays in the requirements
  specification.

Exempt: docs-only
Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com>
Second round of the second-family review of #28 (GLM 5.3, ACCEPTABLE on
0a8a870). The reviewer accepted the four dispositions of round one and
raised one new minor finding, checked here at its source.

The round-one wording asked for a byte-identical error between the
sentinel and a missing path inside the worktree. That cannot be met. pi
returns error.message unchanged (pi 1.4.0, dist/core/tools/read.js:77,
pi-agent-core dist/agent-loop.js:542-546), and the Node message embeds the
path it read. Reproduced: accessSync on two missing paths gives two
different strings, "ENOENT: no such file or directory, access '<path>'".
An author who compared the full message could never turn the vector
green, so the sixth activation condition could never be met.

The requirement now compares the same path in two host states: a host file
present at that path, and no file there. Equality there is exactly the
"no existence oracle" property.

Exempt: docs-only
Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com>
@constantin-jais
constantin-jais merged commit ef14a3e into migrate/recover-code Oct 26, 2026
1 check passed
constantin-jais added a commit that referenced this pull request Oct 26, 2026
… by both drivers

Review round 1 on 28dfe9c, finding F10: the owner's decision of 2026-10-24
on K1 (read is not confined to the worktree), verbatim "6e condition, deux
pilotes (Recommandé)", written as CA-32 by #28.

The activation table gains its sixth condition: id pass-read-confined,
requirement "a review pass cannot read outside its detached worktree,
enforced outside pi", source citing the owner decision of 2026-10-24 and
CA-32, work package WP-4, satisfied false, evidence null. The LangGraph
path applies all six: "3 of 6 activation condition(s) unmet".

The default driver now refuses real passes itself, in runCampaignDirect and
not only on the command line, while the conditions that bind it are unmet:
pass-read-confined alone, since it loads no package (defaultDriverRefusal).
The refusal comes before anything is printed or opened, names the
condition, and leaves --dry-run available; a dry run, an empty run and a
refused overwrite run no pass and need no activation. Both drivers throw
ActivationRefusedError, which fanout.ts prints with exit 2. The canonical
table check moves from fanout-graph.ts to fanout-activation.ts
(assertCanonicalTable) and serves both drivers. The LangGraph refusal no
longer suggests omitting --runtime, since the default path refuses too.

Tests that run real passes with the stand-in pi hand both drivers the
table reserved for tests (ALL_CONDITIONS_MET_FOR_TESTS), as the graph
driver's already did. fanout-cli.test.ts: the command line now pins the
refusal, that no flag or variable lifts it, and every path that runs no
pass (dry run, recorded verdict skipped, forced overwrite refused, missing
commit); what a real campaign records, a failing pass and a dirty tree are
pinned in process through the default driver and printed as the command
line prints. The CA-29 test with the package absent now shows a real run
reaching the driver's own refusal, identical to the run with the package
present and with no module resolution error. authority-boundary.test.ts
covers the default driver: it takes its refusal from fanout-activation.ts
and fanout.ts hands neither driver a table.

Gate-integrity questions for the added and changed tests:
1. What makes them red (env -i PATH HOME, full review suite):
   - `if (false) throw new ActivationRefusedError(refusal ?? "");` in
     runCampaignDirect: 4 red (driver refusal, CLI refusal, no flag lifts
     it, CA-29 with the package absent);
   - DEFAULT_DRIVER_CONDITION_IDS emptied to `[]`: 5 red (the same 4 and
     "the default path is refused by the sixth condition alone").
2. Would an equivalent refactor redden them: no for the behaviour cases,
   which read refusals, messages and what ran. The authority-boundary case
   pins two call shapes on purpose (the driver reads its table from the
   activation module; the command line passes none), as its neighbours do.

Work-package: #19 WP-3
Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.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