carveout 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/AGENTS.md +21 -3
  2. package/NOTICE.md +2 -1
  3. package/README.md +248 -473
  4. package/dist/src/account.d.ts +37 -0
  5. package/dist/src/account.js +275 -0
  6. package/dist/src/api.d.ts +148 -0
  7. package/dist/src/api.js +120 -0
  8. package/dist/src/carve.d.ts +788 -0
  9. package/dist/src/carve.js +1721 -125
  10. package/dist/src/cli.d.ts +10 -0
  11. package/dist/src/cli.js +674 -47
  12. package/dist/src/command.d.ts +10 -0
  13. package/dist/src/core.d.ts +11 -0
  14. package/dist/src/cut.d.ts +78 -0
  15. package/dist/src/cut.js +25 -7
  16. package/dist/src/extract/compiler-config.d.ts +49 -0
  17. package/dist/src/extract/fixture.d.ts +103 -0
  18. package/dist/src/extract/hash.d.ts +6 -0
  19. package/dist/src/extract/package-registry.d.ts +47 -0
  20. package/dist/src/extract/package-types.d.ts +52 -0
  21. package/dist/src/extract/surface.d.ts +44 -0
  22. package/dist/src/extract/typescript.d.ts +180 -0
  23. package/dist/src/gate.d.ts +55 -0
  24. package/dist/src/gate.js +1 -0
  25. package/dist/src/graph.d.ts +142 -0
  26. package/dist/src/graph.js +4 -3
  27. package/dist/src/impact.d.ts +113 -0
  28. package/dist/src/imports.d.ts +67 -0
  29. package/dist/src/mergeback.d.ts +182 -0
  30. package/dist/src/mergeback.js +580 -35
  31. package/dist/src/platform.d.ts +69 -0
  32. package/dist/src/platform.js +184 -0
  33. package/dist/src/policy.d.ts +181 -0
  34. package/dist/src/policy.js +716 -230
  35. package/dist/src/project/build.d.ts +23 -0
  36. package/dist/src/project/catalog.d.ts +25 -0
  37. package/dist/src/project/project.d.ts +287 -0
  38. package/dist/src/project/project.js +479 -52
  39. package/dist/src/project/provenance.d.ts +73 -0
  40. package/dist/src/project/provenance.js +12 -0
  41. package/dist/src/project/runner.d.ts +19 -0
  42. package/dist/src/project/secrets.d.ts +64 -0
  43. package/dist/src/project/vitest-config.d.ts +70 -0
  44. package/dist/src/project/vitest-config.js +291 -0
  45. package/dist/src/pseudonyms/aliases.d.ts +107 -0
  46. package/dist/src/pseudonyms/aliases.js +394 -0
  47. package/dist/src/pseudonyms/draft.d.ts +64 -0
  48. package/dist/src/pseudonyms/draft.js +284 -0
  49. package/dist/src/pseudonyms/folder.d.ts +172 -0
  50. package/dist/src/pseudonyms/folder.js +527 -0
  51. package/dist/src/pseudonyms/index.d.ts +11 -0
  52. package/dist/src/pseudonyms/index.js +11 -0
  53. package/dist/src/pseudonyms/vocabulary.d.ts +113 -0
  54. package/dist/src/pseudonyms/vocabulary.js +522 -0
  55. package/dist/src/readiness.d.ts +71 -0
  56. package/dist/src/readiness.js +131 -0
  57. package/dist/src/receipt.d.ts +154 -0
  58. package/dist/src/receipt.js +296 -0
  59. package/dist/src/sandbox.d.ts +53 -0
  60. package/dist/src/sandbox.js +154 -0
  61. package/dist/src/slice/analysis.d.ts +75 -0
  62. package/dist/src/slice/externals.d.ts +51 -0
  63. package/dist/src/slice/graph.d.ts +69 -0
  64. package/dist/src/slice/manifest.d.ts +262 -0
  65. package/dist/src/slice/manifest.js +4 -3
  66. package/dist/src/slice/reach.d.ts +204 -0
  67. package/dist/src/slice/reach.js +11 -5
  68. package/dist/src/slice/report.d.ts +49 -0
  69. package/dist/src/slice/report.js +5 -2
  70. package/dist/src/specifiers.d.ts +48 -0
  71. package/dist/src/status.d.ts +46 -0
  72. package/dist/src/status.js +3 -2
  73. package/dist/src/test-integrity.d.ts +54 -0
  74. package/dist/src/test-integrity.js +812 -0
  75. package/package.json +24 -5
  76. package/skills/carveout/SKILL.md +517 -0
  77. package/skills/carveout-gate/SKILL.md +98 -0
  78. package/SKILL.md +0 -423
package/README.md CHANGED
@@ -1,435 +1,241 @@
1
1
  <h1 align="center">carveout</h1>
2
2
 
3
3
  <p align="center">
4
- <strong>Zero-trust handoff for your codebase.</strong><br>
5
- They see one folder. None of their code runs until you've read it.
4
+ <strong>Open-source a feature, not your codebase.</strong><br>
5
+ Let a contractor, an outside contributor or an AI agent work on your closed repository.<br>
6
+ They get one folder that builds and tests on its own. Their work comes back as a patch that runs none of their code.
6
7
  </p>
7
8
 
8
9
  <p align="center">
9
10
  <a href="https://www.npmjs.com/package/carveout"><img src="https://img.shields.io/npm/v/carveout?style=flat-square&label=npm" alt="npm version"></a>
10
11
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="MIT license"></a>
11
- <a href="SKILL.md"><img src="https://img.shields.io/badge/Claude_Code-skill-D97757?style=flat-square" alt="Claude Code skill"></a>
12
+ <a href="skills/carveout/SKILL.md"><img src="https://img.shields.io/badge/Claude_Code-skill-D97757?style=flat-square" alt="Claude Code skill"></a>
13
+ <a href="https://skills.sh/lunox-work/carveout"><img src="https://img.shields.io/badge/skills.sh-npx_skills_add_lunox--work%2Fcarveout-111?style=flat-square" alt="skills.sh"></a>
12
14
  <a href="#limits"><img src="https://img.shields.io/badge/TypeScript-repositories-3178C6?style=flat-square" alt="TypeScript repositories"></a>
13
15
  </p>
14
16
 
15
17
  <p align="center">
16
- <img src="assets/hero.png" width="100%" alt="Zero-trust handoff for your codebase. A contractor sees 0.7% of the repository's source (median of 8 slices of a 245k-line TypeScript repository), and 0 lines of their code run before you read the patch: CI and hook files, manifests, lockfiles, deleted or weakened tests, new network or disk access and files outside the slice are kept out.">
18
+ <img src="assets/hero.png" width="100%" alt="Open-source a feature, not your codebase. What they see: 0.7% of a 245k-line repository, the median slice (11% with tests and pure code). What runs on your machine: 0 lines of their code; CI, hooks, lockfiles, weakened tests, code that spots a test run, new network or disk access and files outside the slice are held. What comes back: 52 of 52 real commits replayed through a slice come back as patches that apply.">
17
19
  </p>
18
20
 
19
21
  <table>
20
22
  <tr>
21
- <td align="center" width="25%"><h3>under 1%</h3>of the repository<br>is what they see<br><sub>median 0.7% of 245k lines</sub></td>
22
- <td align="center" width="25%"><h3>0 lines</h3>of their code run<br>to make the patch<br><sub>it is read, never executed</sub></td>
23
- <td align="center" width="25%"><h3>52 of 52</h3>returned patches<br>applied to the repository<br><sub>real commits, sliced and merged back</sub></td>
24
- <td align="center" width="25%"><h3>89%</h3>still apply after<br>the repository moved on<br><sub>every one typechecks</sub></td>
23
+ <td align="center" width="25%"><h3>0.7%</h3>of the repository<br>is what a slice shows<br><sub>median of 8 slices of 245k lines; 11% with tests and pure code</sub></td>
24
+ <td align="center" width="25%"><h3>0 lines</h3>of their code run<br>on your machine<br><sub>the patch is read, never run; checks run in a container with no network</sub></td>
25
+ <td align="center" width="25%"><h3>52 of 52</h3>replayed commits<br>come back and apply<br><sub>49 byte-identical; 89% with <code>--3way</code> after the repo moved</sub></td>
26
+ <td align="center" width="25%"><h3>6 of 6</h3>agents in a sealed folder<br>shipped their task<br><sub>each patch passed the owner's hidden tests</sub></td>
25
27
  </tr>
26
28
  </table>
27
29
 
28
30
  <p align="center">
29
- <a href="#handing-a-slice-to-a-contractor">Handoff</a> •
31
+ <a href="#before--after">Before/After</a> •
32
+ <a href="#install">Install</a> •
33
+ <a href="#how-it-works">How it works</a> •
34
+ <a href="#what-comes-back-and-what-is-held">What comes back</a> •
35
+ <a href="#proof">Proof</a> •
30
36
  <a href="#commands">Commands</a> •
31
- <a href="#also-checking-a-change-in-the-repository">Impact and gate</a> •
32
- <a href="#experiments">Experiments</a> •
33
37
  <a href="#limits">Limits</a> •
34
- <a href="experiments/RESULTS.md">RESULTS.md</a>
38
+ <a href="docs/REFERENCE.md">Reference</a>
35
39
  </p>
36
40
 
37
- Hand one part of a closed TypeScript repository to an outside contractor,
38
- and take their work back, without showing them the rest.
41
+ Named for the corporate carve-out: one unit split off from a company so it
42
+ can stand on its own. carveout does that for one feature of a repository,
43
+ and takes the work back when it is done.
39
44
 
40
- You name a file or a folder. carveout copies it, and what it imports up to
41
- a depth you choose, into a new folder at the same paths. Every module it
42
- imports from the rest of the repository becomes a `.d.ts` stub with a
43
- recording mock behind it, so the folder installs, builds and tests on its
44
- own: the contractor gets the slice's source and the declarations of what it
45
- uses, never the bodies behind them. When the folder comes back,
46
- `carveout patch` turns the edits into a patch for the repository, runs none
47
- of the contractor's code, and keeps out what they should not slip in: CI
48
- and hook files, manifests and lockfiles, deleted or weakened tests, new
49
- files in other packages.
45
+ ---
50
46
 
51
- ```sh
52
- # maintainer, at the repository's root
53
- npx -y carveout plan apps/api/src/billing --depth 1 --with-tests # what the folder would hold; writes nothing
54
- npx -y carveout cut apps/api/src/billing --depth 1 --with-tests --public --out ../billing-slice
55
- npx -y carveout check ../billing-slice # npm ci, build, test, as the contractor will
47
+ ## Before / After
56
48
 
57
- # contractor: npm ci, edit, npm test, send the folder back
49
+ <table>
50
+ <tr>
51
+ <td width="50%">
52
+
53
+ ### 🔓 Without carveout: they get the repo
58
54
 
59
- # maintainer, on the folder that came back
60
- npx -y carveout status ../billing-slice # does the repository still match the cut?
61
- npx -y carveout patch ../billing-slice --output billing.patch # runs none of its code
62
- git apply --3way billing.patch
63
- # or both, with a check: carveout merge ../billing-slice --into . --verify "npm test"
64
55
  ```
56
+ $ git clone git@github.com:acme/closed-repo
57
+ 709 files · 245,456 lines of TypeScript
58
+ every package, infra/, .github/workflows/
59
+ their branch runs CI with your secrets
60
+ ```
61
+
62
+ </td>
63
+ <td width="50%">
65
64
 
66
- Use it to:
65
+ ### 🔒 With carveout: they get one folder
67
66
 
68
- - let a contractor, an outside contributor or an agent you do not trust
69
- with the rest work on one part of a closed repository, and take the work
70
- back as a patch that is checked before anything runs it
71
- ([below](#handing-a-slice-to-a-contractor));
72
- - work on one package of a monorepo without installing or building the
73
- others: a slice installs in about a third of the time and an eighth of
74
- the disk;
75
- - see what a module really depends on, and what depends on it;
76
- - make a minimal reproduction of one module.
67
+ ```
68
+ $ carveout handoff \
69
+ packages/jira/src/selection-jql.ts
70
+ Verdict: ready: it installs, builds, and its
71
+ tests pass before any edit
72
+ Tests: 15 of 15 test(s) pass before any edit
73
+ Shows 15 files, 2,980 lines
74
+ Names: 3 rename(s) hide the repository's
75
+ ```
77
76
 
78
- The same import graph also checks a change made inside the repository,
79
- yours or a coding agent's: `carveout impact` names what it reaches and the
80
- tests to run, and `carveout gate` holds weakened tests and CI or config
81
- edits, also as a Claude Code Stop hook
82
- ([below](#also-checking-a-change-in-the-repository)).
77
+ </td>
78
+ </tr>
79
+ <tr>
80
+ <td width="50%">
81
+
82
+ ### ✅ The work comes back
83
+
84
+ ```
85
+ $ carveout patch ../returned --output change.patch
86
+ carveout: 2 changed, 0 new and 0 deleted files
87
+ $ git apply change.patch && npm test -w packages/jira
88
+ ℹ tests 165
89
+ ℹ pass 165
90
+ ℹ fail 0
91
+ ```
92
+
93
+ </td>
94
+ <td width="50%">
95
+
96
+ ### 🚫 A shortcut does not
97
+
98
+ ```
99
+ $ carveout patch ../returned
100
+ carveout: kept out:
101
+ packages/jira/src/selection-jql.ts,
102
+ could pass a test without the code
103
+ passing it (process.execArgv matched
104
+ against a test runner)
105
+ exit 6
106
+ ```
107
+
108
+ </td>
109
+ </tr>
110
+ </table>
83
111
 
84
- ## Handing a slice to a contractor
112
+ **Same task. 15 files out of 709. The shortcut held before anything ran
113
+ it.**
85
114
 
86
- For a contractor or contributor who must not see the rest of a closed
87
- repository. The maintainer cuts and later merges; the contractor only works
88
- in the folder.
115
+ A real hand-off on [sandbox-factory](https://github.com/lunox-work/sandbox-factory),
116
+ 2026-11-11. The 15 files are the entry point, its test, and 13 pure modules
117
+ copied so that test runs on real code. Everything else the slice touches is
118
+ a `.d.ts` stub with a recording mock: declarations, never bodies. Those 13
119
+ modules were pricing code. `plan` lists every copied file before anything is
120
+ written, and `--private` keeps a folder out: here that shows 245 lines, and 7
121
+ tests then fail on the mocks before any edit. You choose.
122
+
123
+ ## Install
89
124
 
90
125
  ```sh
91
- # maintainer, at the repository's root
92
- carveout plan packages/api/src/quote.ts --depth 1 --with-tests --whole-package --copy-pure
93
- carveout cut packages/api/src/quote.ts --depth 1 --with-tests --whole-package --copy-pure \
94
- --public --out ../quote-slice
95
- carveout check ../quote-slice # read what it holds before sending it
96
- # contractor: npm ci, edit, npm test, send the folder back
97
- # maintainer, on the folder that came back
98
- carveout patch ../returned --output quote.patch # runs none of its code
99
- less quote.patch # read all of it before anything runs it
100
- git switch -c contrib/quote && git apply --3way quote.patch
101
- carveout impact --base HEAD # the importers and tests the contractor never saw
102
- # then a PR: CI decides
126
+ npx skills add lunox-work/carveout -g
103
127
  ```
104
128
 
105
- What carveout holds to:
106
-
107
- - **The folder.** It holds the slice's source as it is, and stubs of what
108
- the slice imports from the rest: the declarations of the names it uses
109
- (a one-line barrel included), never their bodies or comments. With
110
- `--public` (or `"public": true` in the configuration), literal values are
111
- widened and the folder names neither the maintainer's checkout nor the
112
- files outside the slice that import it. `--private <path>` keeps a module
113
- out even when the walk reaches it, `--copy-pure` included. `--copy-pure`
114
- copies more of the repository (the pure code the slice runs) so the
115
- contributor's tests run against it rather than against mocks: leave it
116
- off, or mark that code private, where it must stay closed. `plan` and
117
- `cut` list the source copied from outside the entry points by folder,
118
- with its lines, tests not counted: each folder there is a `--private`
119
- decision to make before the folder is sent.
120
- - **The folder that comes back.** Nothing in it is trusted. `patch` and
121
- `apply` refuse a `carveout.json` that names a path outside the folder or
122
- a `.git` path, or lists as the slice's a file the repository does not
123
- hold at those bytes, and work against the checkout given with `--repo`
124
- (or the current directory), which must hold the cut's commit. `patch`
125
- runs none of the folder's code.
126
- - **The hand-off policy.** The patch carries edits to the slice's files and
127
- new files in the slice's packages. It leaves out, names, and exits 6 on:
128
- new files in other packages (a cut module's folder among them);
129
- protected paths (CI workflows, hooks, `.vscode`, `.devcontainer`,
130
- `package.json`, lockfiles, `.npmrc`, `.env*`, Dockerfiles, Makefiles,
131
- `*.sh`, `CODEOWNERS`, `carveout.config.json`); deleted tests; and edited
132
- tests that remove or rewrite an assertion, skip or focus (`.only`) a
133
- test, or leave an assertion unreachable or in a `catch` that swallows it
134
- (an expected value changed may be the change itself, or a test bent to
135
- pass: the maintainer reads it); and a script, new or edited, that gains
136
- a reach it did not have: Node's disk, network or process modules, a
137
- service SDK, `fetch`, `eval`, the environment, or a module or global
138
- named by a computed value (a returned test that reads the closed source
139
- and sends it out is one). `--allow <path|glob>` lets a reviewed one
140
- through. `merge` applies nothing while any is refused. The policy reads
141
- syntax, not intent: a new runner configuration (`vitest.config.ts`), or
142
- code that does harm with only the reach its file already had, passes it.
143
- - **Running what came back.** The contributor's tests, and every new file
144
- the patch carries, are code that runs beside the whole repository, the
145
- part the slice kept closed included. A branch pushed to the repository
146
- runs CI with the repository's secrets and network, which a fork's pull
147
- request does not: code there can read the source and send it out. Read
148
- the whole patch before anything runs it, or run its first CI with no
149
- secrets and no outbound network. `check --isolated` gives the folder a
150
- scratch HOME and npm cache and no install scripts, and `merge --verify`
151
- a scratch HOME and only the variables named with `--verify-env`:
152
- neither is a sandbox, and `merge --verify` runs in the full checkout.
153
-
154
- What it does not promise:
155
-
156
- - **What a pass in the folder covers is stated.** `plan`, `cut` and
157
- BOUNDARY.md name every test that runs the slice's code and stays in the
158
- repository, and why (not asked for, another package's, Jest, the file
159
- limit). When none is in the folder, a pass means the code builds and
160
- keeps its exports, nothing more.
161
- - **Passing in the slice is a pre-check, not the merge gate.** The slice's
162
- tests run against mocks of everything it cuts. A test that needs a cut
163
- module's real behaviour fails at the slice's baseline and is not counted,
164
- and a test that imports `carveout/` to make a mock answer stays in the
165
- folder: the change comes back without it, and `patch` says how many. In
166
- the experiments, verdicts in the slice and after the merge agreed in 74%
167
- of cases, nearly all disagreements in the safe direction (the slice
168
- failed, the repository passed). `--copy-pure` turns most such baselines
169
- green, and the surface check catches a changed export the importers in
170
- other packages rely on; neither runs those importers. Those figures come
171
- from earlier CLIs: the model-run benchmarks have not been run again since
172
- `--copy-pure`, the surface check and the barrel-chain rule. The
173
- repository's CI decides, once the patch has been read.
174
- - **The slice's own source is the contributor's to read.** Choose the
175
- entry points and `--private` paths with that in mind; `plan` lists every
176
- file before anything is written.
177
-
178
- ## What a cut holds
129
+ In Claude Code, the repository is also a plugin marketplace:
130
+ `/plugin marketplace add lunox-work/carveout`, then
131
+ `/plugin install carveout@carveout`. It holds two skills:
132
+ [`carveout`](skills/carveout/SKILL.md), the hand-off, and
133
+ [`carveout-gate`](skills/carveout-gate/SKILL.md), `impact` and `gate` for a
134
+ change made in the repository. The plugin also bundles the `lunox` MCP server
135
+ (`.mcp.json`), Lunox's hosted platform: Claude Code asks you to sign in on
136
+ first use (`/mcp`), and signed out the skills run locally as before.
137
+
138
+ Then ask your coding agent (Claude Code, Codex, Cursor, or any agent that
139
+ loads skills): _"hand `src/billing` to a contractor"_. It plans the slice,
140
+ shows you what goes out, and cuts it once you agree. Without a skill, use the
141
+ CLI:
179
142
 
143
+ ```sh
144
+ npx -y carveout handoff src/billing/quote.ts --task task.md --out ../quote-slice
180
145
  ```
181
- billing-slice/
182
- apps/api/src/billing/... the slice, copied at its repository paths
183
- packages/db/src/index.d.ts a stub of what the slice uses from a cut module
184
- packages/db/src/index.js its recording mock
185
- carveout/packages/stripe/ a mock package for a service SDK
186
- carveout/mock.js the mock runtime: recordedCalls(), mockImplementation()
187
- carveout/tests/ an interface test: every export outside code imports
188
- carveout/surface/ those exports' declarations at the cut, and the type
189
- check npm test holds them to
190
- BOUNDARY.md an index: the tests that run the slice's code (in the
191
- folder, and left behind with why), the modules it
192
- imports from outside, and the exports outside code
193
- takes from it
194
- carveout.json the cut: hashes, cuts, packages, rewrites, blockers
195
- package.json, tsconfig.json, package-lock.json, carveout.env
146
+
147
+ Needs Node 22.12+ and a TypeScript repository (npm, pnpm or Yarn; `node:test`
148
+ or Vitest). Code that came back runs only in Docker; without it, carveout
149
+ runs none of it unless you pass `--no-sandbox`.
150
+
151
+ ## Try it in 60 seconds
152
+
153
+ ```sh
154
+ # maintainer, at the repository's root
155
+ npx -y carveout plan apps/api/src/billing --depth 1 --with-tests # what the folder would hold; writes nothing
156
+ npx -y carveout handoff apps/api/src/billing --task task.md --out ../billing-slice
157
+
158
+ # contractor: npm ci, edit, npm test, send the folder back
159
+
160
+ # maintainer, on the folder that came back
161
+ npx -y carveout patch ../billing-slice --output billing.patch # runs none of its code
162
+ less billing.patch # read all of it
163
+ git switch -c contrib/billing && git apply --3way billing.patch # then a PR: CI decides
196
164
  ```
197
165
 
198
- - **Stubs** are emitted by the TypeScript compiler from the cut modules'
199
- own declarations, and narrowed to the names the slice uses.
200
- - **Workspace packages** are stubbed from their source rather than
201
- installed, unless the cut follows them (`--follow-workspaces`): then an
202
- import of one is an import of its source file, copied or cut like any
203
- module. Either way, a file in another package that imports the slice by
204
- the package's name is one of its importers.
205
- - **Installed packages** are those the repository pins from the public
206
- registry. Each comes with its `@types` and its required peers, at the
207
- repository's versions.
208
- - **Mock packages** stand in for service SDKs (payments, cloud, databases,
209
- queues, email, HTTP clients, LLM SDKs) and for private-registry packages.
210
- - **Import rewrites.** Path aliases, workspace imports and bundler-style
211
- extensionless imports become relative paths Node resolves. Each is
212
- recorded, and `patch` undoes it.
213
- - **Assets.** Stylesheets and images a module imports are copied, and load
214
- in Node as their path. `import.meta.glob` and `import.meta.env` work
215
- under Node.
216
- - **The repository's checks.** The compiler options that only reject
217
- code (`strict`, `noUnusedLocals` and the like) come from the
218
- repository's tsconfig. `noImplicitAny` too, unless the slice does not
219
- compile under it with its stubs and untyped packages, which the cut says. Node's types come at the repository's version.
220
- When the package's own test sets coverage thresholds, `npm test` holds
221
- the source files you add to them, and fails, as the package's script
222
- does, on a file you add that no test loads (`assert-all-covered`).
223
- - **The public surface.** Every export code outside the slice imports
224
- must stay exported (the interface test) and keep its type: `npm test`
225
- type-checks `carveout/surface/check.ts`, which holds each such value to
226
- its type at the cut and each such type to its shape both ways, and
227
- names the export whose type changed. Enums and classes with private
228
- members are not checked; a check that fails before any edit is not
229
- counted.
230
- - **Credentials.** A credentials file is never copied. A credential found
231
- in anything the cut would write, a `--copy` folder or the resolved
232
- lockfile included, stops the cut: nothing is written.
233
-
234
- The plan and the cut read the repository and never run its code. The cut
235
- fetches package types from the public registry with install scripts off,
236
- and resolves a lockfile; when the repository has an npm lockfile, the
237
- slice starts from its locked versions. `--offline` skips both.
238
-
239
- ## What the walk follows
240
-
241
- - **Names, not whole files.** Importing `a` from a barrel brings the barrel
242
- and the module that declares `a`, at the barrel's depth. The barrel's
243
- other re-exports are cut.
244
- - **Code, not types.** A module the slice takes only types from
245
- (`import type`, or an interface or type alias by name) is cut to its
246
- stub and never copied.
247
- - **Files read by path.** `new URL("./worker.ts", import.meta.url)` and
248
- `join(__dirname, "get.sql")` copy the file. A folder read by path, or such
249
- a file left outside the slice, is a `path_read` blocker.
250
- - **Modules named to a test runner** (`vi.mock`, `jest.requireActual`,
251
- `require.resolve`) are cut, not followed. Their specifiers are rewritten
252
- like imports.
253
- - **The file budget.** When it runs out, `plan` lists the files it left out.
254
- - **The whole package**, with `--whole-package`: every file of the entry
255
- points' package the imports reach comes along at any depth, so the cut
256
- falls at package and service boundaries, and a sibling module is copied
257
- rather than stubbed. A barrel's re-exports come only when they supply a
258
- name the slice asks of it. Imports into other packages still stop at the
259
- depth.
260
- - **Barrel chains.** A test that takes a slice name through barrels
261
- (`src/index.ts` → `pricing/index.ts` → `pricing/weight.ts`) is a test of
262
- the slice: `--with-tests` brings it and the whole chain, each barrel cut
263
- to what it forwards from the slice.
264
- - **A test's helpers.** A test brought along by `--with-tests` brings what
265
- it imports at runtime from its own package, or from a test folder
266
- anywhere (`test/`, `__mocks__/`, `fixtures/`): a fake copied is a test
267
- that passes, a fake stubbed is one that fails before any change.
268
- - **Private paths** (`--private <path>`, or `private` in the
269
- configuration) are never copied: wherever the walk reaches one it is
270
- cut and stubbed, with literal values widened.
271
- - **Pure code**, with `--copy-pure`: a module the slice runs from outside
272
- it is copied rather than mocked, past the depth and across packages,
273
- when nothing its runtime imports reach outside the slice imports a
274
- service SDK, Node's disk, network or process modules, reads the
275
- environment or a path, or is private. A mocked validator or error
276
- class answers with more mocks, and the tests that use it fail before
277
- any change; copied, they run as in the repository. Through a barrel,
278
- only the modules that supply the names come. The copied code is the
279
- contributor's to read: `plan` lists it.
280
-
281
- Only the files the walk needs are parsed. Files that may import the slice
282
- are found by name first, and the compiler confirms each one.
166
+ ## How it works
283
167
 
284
- ## Commands
168
+ <p align="center">
169
+ <img src="assets/flow.png" width="100%" alt="One folder out, one checked patch back. Out: carveout handoff on a 709-file repository sends 15 files; the rest are .d.ts stubs and mocks, names in pseudonyms, 15 of 15 tests pass before any edit. Their work: a contractor, contributor or agent runs npm ci, edits, and npm test passes 17 of 17. Back: carveout patch runs none of it; 2 files come back and the repository's tests pass 165 of 165; code that matched process.execArgv against a test runner is held.">
170
+ </p>
285
171
 
286
- | Command | Does |
287
- | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
288
- | `carveout plan <entry>...` | Lists the files, cuts, importers, packages, environment and blockers; the lines the cut shows, by why each file is there; every test that runs the code, in the folder or left behind and why. Writes nothing. |
289
- | `carveout cut <entry>... --out <dir>` | Writes the folder. Exits 2 if it was written with blockers; writes nothing if it would carry a credential. |
290
- | `carveout check <dir>` | Runs `npm ci`, `npm run build` and `npm test` in a scratch copy; records the tests that fail on the mocks. `--isolated` for a folder that came back. |
291
- | `carveout status <dir>` | Says whether a cut now would write the same folder, and names what changed. Exits 4 if stale. |
292
- | `carveout patch <dir>` | Prints the edits as a patch for the repository (`--output <file>`), with full blob ids for `git apply --3way`. Runs none of the folder's code; keeps out what the hand-off policy refuses (exit 6). |
293
- | `carveout merge <dir> --into <checkout>` | Patches, applies with `--3way` in the checkout you name, and runs `--verify <cmd>` there with a scratch HOME and only the `--verify-env` variables. Applies nothing on a refusal (exit 6). |
294
- | `carveout apply <dir> <patch>` | Brings a patch for the repository into the folder, imports rewritten; `--stubs` adds the stubs it needs. A module the patch adds past the cut's reach joins the folder as an entry, with its tests. |
295
-
296
- Options for `plan` and `cut`:
297
-
298
- - `--repo <root>` (default `.`)
299
- - `--depth N` (default 3)
300
- - `--max-files N` (default 40)
301
- - `--with-tests`, which also brings the tests outside the slice that import
302
- it (`*.test.*`, `*.spec.*`, `__tests__/`) and run on `node:test` or
303
- Vitest (imported, or its globals in a package that depends on Vitest and
304
- not Jest), directly or through barrels outside the slice that re-export
305
- from it; such a barrel comes along, its other re-exports cut, and so do
306
- the test's own helpers. `npm test` in the folder runs them. When the
307
- file budget cannot hold them all, the tests nearest the entry points
308
- come first: those that take names the entry points declare, whatever
309
- barrel they import through, ahead of those that take names declared
310
- further out. Without a
311
- lockfile Vitest is mocked like any package; cut with `--install vitest`.
312
- - `--whole-package`, `--follow-workspaces`, `--copy-pure` (which follows
313
- the workspaces too) and `--private <path>` (see above; `--private` takes
314
- a path, a folder or a glob, more than once).
315
- - `--json`
316
-
317
- A `carveout.config.json` at the repository's root sets the same for every
318
- cut, so the owner decides once where a slice may follow and what it keeps
319
- out: `{"wholePackage": true, "followWorkspaces": true, "private":
320
- ["packages/core/src/pricing/**"], "copy": ["packages/db/drizzle"],
321
- "widenLiterals": true, "public": true, "copyPure": true}`. A flag overrides a boolean; the
322
- lists are joined. With any `private` path, or `public`, `widenLiterals` is
323
- on unless the file says otherwise.
324
-
325
- Options for `cut` only:
326
-
327
- - `--mock <pkg>` and `--install <pkg>`, which take precedence over the
328
- defaults. A package whose class a carried test patches by prototype
329
- (`S3Client.prototype.send = ...`) is installed without being asked: a
330
- mock has no prototype to patch, and such a test never reaches the
331
- service. So is one whose class the code tests with `instanceof`
332
- (`error instanceof Anthropic.APIError`): a mock narrows nothing, and the
333
- folder would not build;
334
- - `--copy <dir>`, which copies a folder the code reads by a name the walk
335
- cannot follow (a migrations folder) beside the slice. Its files are the
336
- repository's: an edit to one is in the patch;
337
- - `--live <pkg>`, which keeps a service package real: installed and typed
338
- as the repository has it, with the environment its importers read left
339
- out of `carveout.env` for the shell to supply, so the code's own
340
- defaults apply otherwise. For tests that need the service itself, such
341
- as a database driver against a local server;
342
- - `--widen-literals`, which writes `string` in stubs in place of literal
343
- values. A constant the stub reads as a type (`type Kind = (typeof
344
- KINDS)[number]`) keeps its name-like strings (`"github"`, `"draft"`):
345
- they are that type's members, and widened, the slice would stop checking
346
- the code that relies on them. Its numbers, URLs and sentences widen;
347
- - `--public`, for a folder handed to someone outside: literals widened,
348
- and neither the checkout's path or folder name nor the files outside the
349
- slice that import it written (each importer is named by its package
350
- folder; compiler messages name files from the repository's root). The
351
- commands that need the repository then take `--repo`, defaulting to the
352
- current directory;
353
- - `--offline`.
354
-
355
- Options for `patch`, `status`, `apply` and `merge`: `--repo <checkout>`,
356
- the repository to work against (it must hold the cut's commit; `merge`
357
- uses `--into`). For `patch` and `merge`: `--allow <path|glob>`, which lets
358
- a path the hand-off policy refuses through. For `merge`: `--verify-env
359
- <name>`, an environment variable the verify command is given.
360
-
361
- Exit codes: 0 done, 1 refused or failed, 2 written with blockers (`cut`)
362
- or a step failed (`check`, `merge`, `impact --run`; `gate --hook` on a
363
- hold), 3 nothing to patch, 4 stale (`status`), 5 the edits reach past the
364
- slice, 6 the hand-off policy kept files out (`patch`, `merge`) or held a
365
- change (`gate`).
172
+ - **Out.** You name a file or a folder. carveout copies it, and what it
173
+ imports up to a depth you choose, at the same paths. Every module it
174
+ imports from the rest becomes a `.d.ts` stub with a recording mock, so the
175
+ folder installs, builds and tests on its own.
176
+ - **Nameless.** A hand-off is written in pseudonyms: the repository's
177
+ package names, scope, remote, authors and your Git identity are renamed
178
+ (`LunoxCart` becomes `AcmeCart`). Only your receipt maps them back, and it
179
+ never leaves your machine.
180
+ - **Ready before it ships.** `check` runs the folder as the contractor will.
181
+ It says the folder is ready (its tests pass before any edit) or names the
182
+ tests that fail on the mocks and what would run them on real code.
183
+ - **Back.** `carveout patch` turns the folder's edits into a patch in your
184
+ names. It trusts nothing in the folder: the receipt decides what a patch
185
+ may carry. It runs none of their code.
186
+
187
+ ## What comes back, and what is held
188
+
189
+ | Comes back | Held, named, exit 6 until you `--allow` it |
190
+ | ------------------------------------------------ | -------------------------------------------------------------------------- |
191
+ | Edits to the slice's files | CI workflows, hooks, Dockerfiles, Makefiles, `.env*`, manifests, lockfiles |
192
+ | New files in the slice's packages | Deleted tests, and tests that lose, move or rewrite an assertion |
193
+ | New tests, where the package's runner finds them | `.skip`, `.only`, and code that spots a test run (`NODE_ENV`, argv, stack) |
194
+ | A package you add, named with its pin | New reach: disk, network, process, environment, `eval`, a missing package |
195
+ | Your names, put back | New `@ts-ignore` or `eslint-disable`, and files in other packages |
196
+
197
+ The policy reads syntax, not intent: code that special-cases a test's own
198
+ inputs passes it. Read the whole patch before anything runs it, and run its
199
+ first CI with no secrets. The full policy is in the
200
+ [reference](docs/REFERENCE.md#handing-a-slice-to-a-contractor).
201
+
202
+ ## Proof
203
+
204
+ - **What a slice shows.** Over 8 slices of a 245k-line repository, a
205
+ default cut copies a median of 0.7% of the source (0.03 to 2.4%). With the
206
+ hand-off's tests and pure code, 11% (0.3 to 24%). Stub declarations come
207
+ on top and are not counted
208
+ ([hero-exposure.mjs](experiments/hero-exposure.mjs)).
209
+ - **The round trip.** 52 real commits replayed through a slice all come back
210
+ as patches that apply, 49 byte-identical. On a repository that moved on,
211
+ 27% apply plain and 89% with `git apply --3way`.
212
+ - **Agents in a folder.** Six agents, each given only a public hand-off
213
+ folder, delivered their tasks. Each patch applied in the repository's
214
+ names and passed the owner's hidden acceptance tests.
215
+ - **Folders that stand alone.** Ten of eleven slices from three
216
+ repositories install, build and test on their own with the hand-off's
217
+ flags. The eleventh fails on a narrowed inferred type ([Limits](#limits)).
218
+ - **What it does not win.** A slice does not make an agent cheaper or
219
+ better: it read 17 to 24% more input and passed hidden tests 5 to 10
220
+ points less often than the same agent in the whole repository. A pass in
221
+ the slice matched the verdict after the merge in 74% of replays, nearly
222
+ every miss the safe way (the slice failed, the repository passed). The
223
+ slice is a pre-check; your CI is the gate.
224
+
225
+ Pre-registered, with the raw data: [experiments/RESULTS.md](experiments/RESULTS.md).
366
226
 
367
227
  ## Also: checking a change in the repository
368
228
 
369
- The import graph a slice is cut from also answers two questions about a
370
- change made inside the repository, yours or a coding agent's, with no slice
371
- involved: what does it reach, and what should a reviewer read before it
372
- lands? `gate` applies the hand-off policy above to that change.
229
+ The same import graph checks a change made inside the repository, yours or
230
+ a coding agent's:
373
231
 
374
232
  ```sh
375
- npx -y carveout impact # what the uncommitted change reaches
376
- npx -y carveout impact --base main --run # since main, and run just those tests
377
- npx -y carveout gate --base main # weakened tests, CI or config edits, suppressions
233
+ npx -y carveout impact # what the change reaches, and the tests to run
234
+ npx -y carveout gate --base main # weakened tests, CI or config edits, suppressions
378
235
  ```
379
236
 
380
- | Command | Does |
381
- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
382
- | `carveout impact [<file>...]` | What a change in the repository reaches: importers, changed exports and who takes them, the tests to run and their commands. `--base`, `--depth`, `--run` (exit 2 on a failing test command). |
383
- | `carveout gate` | Holds a change's weakened or deleted tests, protected paths, check configuration and new suppressions (exit 6). `--base`, `--scope`, `--allow`, `--hook`. |
384
-
385
- `impact` and `gate` read the change from Git: the base (default `HEAD`)
386
- against the working tree, untracked files included. Neither runs the
387
- repository's code, but for `impact --run`.
388
-
389
- **`carveout impact`** reports:
390
-
391
- - **Importers**: every file that imports a changed module, directly or
392
- through others, found by name and confirmed by the compiler (aliases,
393
- workspace package names, barrels, `vi.mock`). It follows names, as the
394
- slice walk does: a barrel carries a change only through the names it
395
- forwards from the changed module, and a module only through its exports
396
- whose declarations reach a changed declaration. With a base, only the
397
- exports the diff reaches start the search; named files start with all of
398
- theirs.
399
- - **Exports**: each export of a changed module whose type changed or that
400
- is gone, as the TypeScript checker prints it at the base and now, one
401
- level of members deep, and the files that take it.
402
- - **Tests**: the tests among the importers, each with its runner
403
- (`node:test`, Vitest or Jest; otherwise the package's own `npm test`),
404
- and one command per package that runs just them. The commands run only
405
- installed binaries (`npx --no`).
406
- - **`--run`** runs those commands one at a time, in the repository, with
407
- your environment, and exits 2 when one fails. `--depth N` bounds the hops;
408
- `--json` gives it all as data.
409
-
410
- On sandbox-factory (1,288 files) it takes under 1.5 s. A change to one
411
- function of `packages/core/src/stages.ts` selects 21 of the repository's
412
- 241 tests, among them the API test that pins that function's message, two
413
- hops away; naming the whole file selects 156.
414
-
415
- **`carveout gate`** holds for review, and exits 6 on any:
416
-
417
- - protected paths (CI workflows, hooks, manifests, lockfiles, `.env*`,
418
- Dockerfiles; the hand-off policy's list above);
419
- - configuration a check reads, when edited: `tsconfig*.json`, ESLint,
420
- Biome, Vitest, Jest, coverage settings;
421
- - a deleted test, and a test whose assertions were removed or rewritten,
422
- skipped, focused with `.only`, made unreachable (an early `return`,
423
- `if (false)`), or wrapped in a `catch` that swallows their failure;
424
- - new suppressions in a script: `@ts-ignore`, `@ts-expect-error`,
425
- `eslint-disable`, coverage ignores;
426
- - with `--scope <path|glob>`, any file outside it.
427
-
428
- A hold is a question for a reviewer, not a verdict: a change asked to
429
- alter a behaviour rewrites the assertion that held it. `--allow
430
- <path|glob>` lets a reviewed file through. As a Claude Code Stop hook,
431
- `--hook` sends the holds back to the agent (exit 2) once per stop, asking
432
- it to undo what the task did not ask for and to name the rest in its reply:
237
+ `gate` also works as a Claude Code Stop hook. It sends its holds back to the
238
+ agent once per stop:
433
239
 
434
240
  ```json
435
241
  {
@@ -445,99 +251,46 @@ it to undo what the task did not ask for and to name the rest in its reply:
445
251
  }
446
252
  ```
447
253
 
448
- On a patch that came back from a slice, `git apply` it on a branch and run
449
- `carveout impact --base HEAD` (no `--run`) before anything runs it: it
450
- names the importers and tests the contractor never saw.
451
-
452
- ## Tested on
453
-
454
- Thirteen real slices from three TypeScript repositories, sandbox-factory
455
- among them. They range from 1 to 173 files, and cover Node services, React
456
- components and a whole Vite app's `src` at depth 1, with npm workspaces and
457
- path aliases. Every one installed, built and passed its interface test with
458
- `carveout check`.
459
-
460
- One needed a second cut. An auth module's installed package loads an
461
- optional peer at runtime, `drizzle-orm`. The first cut listed it among the
462
- optional peers and `check` named it; cutting again with
463
- `--install drizzle-orm` passed.
464
-
465
- Walking by name rather than by file made three of them smaller, with the
466
- rest unchanged: 4 files to 2, 3 to 1, and 19 to 11. The modules left out
467
- were ones they imported only types from, now stubs. All thirteen still pass
468
- `check`.
469
-
470
- The lazy graph walks as the whole graph does: identical results in 722
471
- walks over five repositories, cal.com and n8n among them. On n8n (24k
472
- files) the median walk takes 2.3 s against 13.7 s for the whole graph. A
473
- slice whose files have common names can read most of the repository, and
474
- then takes as long.
475
-
476
- On n8n (pnpm, catalogs, Vitest globals), a one-module slice installs,
477
- builds and passes `check`, where it once stopped at the install on a
478
- `catalog:` pin; with `--with-tests` it carries five Vitest tests, and its
479
- build then fails on limits named below (an `override` of a mocked base
480
- class, `never[]` under the relaxed `noImplicitAny`).
481
-
482
- ## Experiments
483
-
484
- `experiments/` holds the pre-registered experiments and their data
485
- ([RESULTS.md](experiments/RESULTS.md)).
486
-
487
- - **What a slice shows.** Over the eight sandbox-factory slices in
488
- `slices.json`, a default cut copies a median of 0.7% of the repository's
489
- 245k lines of TypeScript as source (0.03 to 2.4%); with
490
- `--whole-package --with-tests --copy-pure`, 11% (0.3 to 24%)
491
- ([hero-exposure.mjs](experiments/hero-exposure.mjs)).
492
- - **Agents in a slice (Phases 5 to 8).** An agent given a slice instead of
493
- the repository did not get cheaper or better: it read 17 to 24% more
494
- input, passed hidden tests 5 to 10 points less often, and a weaker model
495
- did as well in the repository as in a slice. A full-repository agent read
496
- a median of two files outside the slice. The slice's measured wins are
497
- the ones without a model: setup at 0.35× the time and 0.12× the disk, and
498
- a bounded view. That is why the slice is for someone who must not see
499
- the rest, not a way to make an agent cheaper, and why `impact` and
500
- `gate` work in the repository itself.
501
- - **`status` (E1).** Over 345 later commits it never said `current` when a
502
- fresh cut differed.
503
- - **Three-way patches (E2).** Applied to a repository that had moved, a
504
- patch applied plain in 27% of cases and with `git apply --3way` in 89%.
505
- Every three-way result typechecked.
506
- - **Co-change hints in `plan` (E3).** Precision was too low, so they were
507
- dropped.
508
- - **Graph time (E4).** The graph of a 24k-file monorepo takes 12 s, down
509
- from 7.5 minutes before a fix that leaves the graph identical. A cache
510
- across runs was not built: it could not reach a 3× speedup.
254
+ ## Commands
255
+
256
+ | Command | Does |
257
+ | ----------------------------- | ------------------------------------------------------------------------------ |
258
+ | `carveout handoff <entry>...` | Cut for someone outside, check it, and say whether it is ready to send |
259
+ | `carveout plan <entry>...` | List what a folder would hold, cut and show. Writes nothing |
260
+ | `carveout cut <entry>...` | Write the folder |
261
+ | `carveout check <dir>` | `npm ci`, build and test it as the contractor will; returned folders in Docker |
262
+ | `carveout patch <dir>` | Turn the folder's edits into a patch, holding what the policy refuses |
263
+ | `carveout merge <dir>` | Patch, apply, and run `--verify` in a container |
264
+ | `carveout status <dir>` | Say whether the repository still matches the cut |
265
+ | `carveout refresh <dir>` | Bring the repository's new commits into the folder, merged with the work |
266
+ | `carveout widen <dir>` | Cut again with more entry points, keeping the work |
267
+ | `carveout impact` | What a change reaches: importers, changed exports, tests to run |
268
+ | `carveout gate` | Hold weakened tests, CI and config edits, and new suppressions |
269
+ | `carveout login` | Optional: sign in to the Lunox platform; a token in `~/.lunox/credentials` |
270
+ | `carveout whoami` | Print the account, workspace and token expiry signed in with |
271
+ | `carveout logout` | Delete the saved platform token |
272
+
273
+ Every option: [reference](docs/REFERENCE.md#commands). Only `login`, `whoami`
274
+ and `logout` talk to the Lunox platform; every other command is local and needs
275
+ no account.
511
276
 
512
277
  ## Limits
513
278
 
514
- - **Languages.** Stubs are extracted from TypeScript only. A JavaScript
515
- source in a slice is reported as `language_unsupported`.
516
- - **Inferred types.** A stub can be narrower than the source when a type is
517
- inferred from a package carveout could not read. The cut reports this; it
518
- does not hide it.
519
- - **Lockfiles.** Peers and locked versions are read from an npm
520
- `package-lock.json`. pnpm and Yarn repositories fall back to the versions
521
- in their package.json files; a `catalog:` pin takes the catalog's
522
- version (`pnpm-workspace.yaml`, or `catalog(s)` in the root
523
- package.json). A pnpm lock entry with no tarball URL shows a package is
524
- public only when its integrity equals the public registry's for that
525
- version (checked online; offline it is mocked). Such folders resolve
526
- peers as pnpm and Yarn do (`legacy-peer-deps`).
527
- - **Test runners.** `node:test` and Vitest. A Jest test is named as left
528
- behind, never run.
529
- - **Folders named like build output.** Files under `build/`, `dist/`,
530
- `vendor/` and `third_party/` are not parsed: a source folder named
531
- `build` is copied when reached, but its own imports are not followed.
532
- - **What `impact` can miss.** It follows imports, not runtime: a module
533
- loaded by a computed name, a file read by path, or configuration a test
534
- reads is not traced, and a test it does not list can still fail. Inside a
535
- module it traces names by the identifiers each top-level statement uses,
536
- which errs toward more. A type is compared one level of members deep: a
537
- change inside a named type shows on that type's own module. Its
538
- `node:test` command for TypeScript uses Node's type stripping, which does
539
- not resolve path aliases; such a package's own test script is the one to
279
+ - **TypeScript only.** A JavaScript source in a slice is reported, not
280
+ stubbed.
281
+ - **`node:test` and Vitest.** A Jest test is named as left behind, never
540
282
  run.
283
+ - **Mocks only record calls.** A change that needs a cut module's real
284
+ behaviour needs `--copy-pure`, `--live <package>` or a wider cut
285
+ (`carveout widen`).
286
+ - **More green, more shown.** Copying pure code turns red baselines green
287
+ and shows more of the repository. `plan` lists every file first.
288
+ - **Syntax, not intent.** The policy is a reader's checklist, not a proof.
289
+ Read the patch; the repository's CI decides.
290
+ - **Docker** (or Podman) runs code that came back. Without it, nothing runs
291
+ but with `--no-sandbox`, which runs it as you.
292
+
293
+ All of them: [reference](docs/REFERENCE.md#limits).
541
294
 
542
295
  <!-- Keep this section and AGENTS.md the same. -->
543
296
 
@@ -562,8 +315,9 @@ the person:
562
315
 
563
316
  For a slice, run `npx -y carveout plan <entry> --depth 1` first. Show the
564
317
  person what the slice would hold and what it cuts, and agree the entry
565
- points and the depth with them. Then `cut` and `check`, and report the
566
- check's result as it is.
318
+ points and the depth with them. Then `cut` and `check` (or `handoff`, both
319
+ in one, with `--task` for the work and its acceptance), and report the
320
+ verdict as it is: ready, tests that fail on the mocks, or no tests.
567
321
 
568
322
  For a change in the repository, run `npx -y carveout impact` (add
569
323
  `--base <branch>` for committed work) and report the exports, importers and
@@ -597,14 +351,31 @@ What to tell them, accurately:
597
351
  - `--copy-pure` copies the pure code the slice runs instead of mocking
598
352
  it, so the folder's tests pass before any change; it shows the
599
353
  contributor more of the repository, which `plan` lists.
354
+ - `--public` also writes the folder in pseudonyms: the names the
355
+ repository goes by (packages, remote, authors, the maintainer's Git
356
+ identity, `--name`) are renamed under a table only the maintainer's
357
+ receipt holds, and `patch` renames the work back. A name left behind
358
+ blocks the cut; report it, and add it with `--name` or `pseudonyms` in
359
+ the configuration. Never send the receipt.
600
360
  - For someone outside the repository, cut with `--public`, take the folder
601
361
  back with `carveout patch` (it runs none of the folder's code and keeps
602
362
  out CI, hook, manifest and lockfile files, new files in other packages,
603
- deleted tests and code that newly reaches the disk, network or
363
+ deleted or bent tests, code that could pass a test without the code
364
+ under test, and code that newly reaches the disk, network or
604
365
  environment), read the whole patch before anything runs it (its
605
366
  tests and new files are code beside the closed repository, and a branch
606
367
  pushed there gets CI's secrets and network), then let the repository's
607
368
  CI judge the change: a pass in the slice is a pass against mocks.
369
+ - `carveout check` and `merge --verify` run a folder that came back only
370
+ after the hand-off policy has read it, and only in a container (Docker)
371
+ that mounts nothing of the machine but a copy, with no network but for an
372
+ install with scripts off. When the container cannot run, they run
373
+ nothing; `--no-sandbox` runs the folder's code as the person, with their
374
+ files and network: say so before passing it.
375
+ - For a job long enough that the repository moves, `carveout refresh`
376
+ brings its commits into the folder, merged into the work (an overlap is
377
+ named, never overwritten), and a patch after it carries the work alone;
378
+ `carveout widen` cuts the folder again, wider, with the work in it.
608
379
  - `carveout check` passing means the folder installs, builds, loads every
609
380
  export outside code uses and keeps their types. It tests the slice's
610
381
  behaviour only when the cut was made with `--with-tests`, and then only
@@ -616,3 +387,7 @@ What to tell them, accurately:
616
387
  can still fail. The repository's CI decides.
617
388
  - `gate` names what a reviewer should read; it does not judge. A rewritten
618
389
  assertion may be the change that was asked for.
390
+
391
+ ## License
392
+
393
+ MIT. See [LICENSE](LICENSE) and [NOTICE.md](NOTICE.md).