@redue/cli 0.1.0-alpha.27 → 0.1.0-alpha.29
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.
- package/CHANGELOG.md +91 -0
- package/README.md +50 -22
- package/bin/redue.mjs +24 -12
- package/docs/user/agents.md +14 -7
- package/docs/user/getting-started.md +4 -3
- package/docs/user/json.md +2 -2
- package/docs/user/node-script.md +2 -2
- package/docs/user/sandbox.md +119 -42
- package/docs/user/shell.md +3 -3
- package/docs/user/supported-projects.md +10 -5
- package/package.json +1 -1
- package/src/claude-hook.mjs +49 -29
- package/src/cli.mjs +48 -17
- package/src/daemon.mjs +10 -31
- package/src/landlock.py +319 -20
- package/src/node-onboarding.mjs +3 -1
- package/src/presentation.mjs +4 -4
- package/src/python-onboarding.mjs +9 -7
- package/src/sandbox.mjs +168 -22
- package/src/setup.mjs +26 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,96 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.29 — 2026-11-03
|
|
4
|
+
|
|
5
|
+
Editing documentation no longer makes sandboxed checks STALE, a fast check
|
|
6
|
+
is skipped or recorded as it should be, and macOS test runs can no longer
|
|
7
|
+
reach a service started outside them.
|
|
8
|
+
|
|
9
|
+
- **Documentation is hidden too (`sandbox-v3`).** A sandboxed run now also
|
|
10
|
+
hides the project's documentation: files named `*.md`, `*.mdx`, `*.rst`,
|
|
11
|
+
`LICENSE*` or `CHANGELOG*` anywhere, and text, image and PDF files in the
|
|
12
|
+
root `docs/` folder (code there stays visible). The receipt names the
|
|
13
|
+
files the run hid, and a later edit to one of them leaves the result
|
|
14
|
+
CURRENT: editing the README no longer makes the tests STALE. A new
|
|
15
|
+
documentation file still counts. `init` tries this first, then the
|
|
16
|
+
git-ignored files only (`sandbox-v2`), then the whole checkout. A check
|
|
17
|
+
that reads documentation (a doctest over the README) keeps `sandbox-v2`.
|
|
18
|
+
Run `redue init` again to move an existing check to `sandbox-v3`.
|
|
19
|
+
- **A fast check is no longer missed by the hook.** The hook waited half of
|
|
20
|
+
the check's last run for `redue status`, so a 0.2 s mypy got 0.1 s, the
|
|
21
|
+
status answer came too late, and the agent's run went unrecorded. The
|
|
22
|
+
wait is now at least 2 s. `REDUE_HOOK_LOG` (an absolute file path) makes
|
|
23
|
+
the hook write one line per Bash command with its decision and why.
|
|
24
|
+
- **macOS test runs get no network.** Seatbelt cannot refuse only the ports
|
|
25
|
+
that were already listening (a per-port rule under a loopback allow is not
|
|
26
|
+
applied; an allow list of every other port does not compile), so a macOS
|
|
27
|
+
test run now gets Unix sockets in its own temporary directory only, as a
|
|
28
|
+
typecheck or lint does. A suite that needs loopback fails `init`'s trial
|
|
29
|
+
and runs without REDUE. Test checks set up by alpha.28 on macOS are not
|
|
30
|
+
CURRENT after the upgrade.
|
|
31
|
+
- **`redue status` reconciles by default.** `redue status --cached` gives the
|
|
32
|
+
observer's last published answer; `--sync` is still accepted. Like
|
|
33
|
+
`--sync`, plain `status` exits 2 when it cannot reach the observer (after
|
|
34
|
+
`redue stop`, for example); `--cached` exits 0.
|
|
35
|
+
- **`redue setup` says more and prints less.** It lists the checks it left
|
|
36
|
+
out and why (a kind it found nothing for, a proposed `tsc --noEmit` no
|
|
37
|
+
recipe takes), says what recording only means, and prints one line per
|
|
38
|
+
recorded run instead of the check's whole output.
|
|
39
|
+
- `redue remove-state` waits for the observer to exit before removing its
|
|
40
|
+
state (a Windows ENOTEMPTY).
|
|
41
|
+
- **Measured.** In the product census, repositories whose qualifying
|
|
42
|
+
sandboxed check keeps the documentation hidden, out of those with one:
|
|
43
|
+
TypeScript on Linux, typecheck 17 of 19, lint 20 of 20, tests 7 of 8; on
|
|
44
|
+
macOS 12 of 13, 14 of 16, 6 of 9. Python on Linux 15 of 15, 22 of 22, 12
|
|
45
|
+
of 17; on macOS 11 of 13, 17 of 17, 8 of 11. No network for macOS test
|
|
46
|
+
runs cost 1 TypeScript suite (it starts a server on 127.0.0.1) and 2
|
|
47
|
+
Python suites: test suites qualifying on macOS went from 12 to 11
|
|
48
|
+
(TypeScript) and 14 to 11 (Python; one checkout no longer installs). The
|
|
49
|
+
strace cross-check found no read outside the recorded inputs. In a Claude
|
|
50
|
+
Code follow-up on starlette and nestjs-api-gateway, a README edit made no
|
|
51
|
+
check STALE; the hook skipped mypy, pytest and the lint, and each skipped
|
|
52
|
+
command passed when run plainly.
|
|
53
|
+
|
|
54
|
+
## 0.1.0-alpha.28 — 2026-11-02
|
|
55
|
+
|
|
56
|
+
Test suites can run in the sandbox, so REDUE can reuse a passing test run
|
|
57
|
+
for more projects, Python included.
|
|
58
|
+
|
|
59
|
+
- **Sandboxed tests.** A root test script that the direct test recipe does
|
|
60
|
+
not take, and pytest in a Python project's `.venv`, can now use the
|
|
61
|
+
sandboxed recipe. `init` keeps it only when the run in the sandbox gives the
|
|
62
|
+
same exit status and test counts as a plain run. On top of what a
|
|
63
|
+
typecheck or lint gets, a test run gets:
|
|
64
|
+
- **Loopback only.** It may listen on any address and connect to the local
|
|
65
|
+
host and Unix sockets, never to a remote host. On macOS, Seatbelt
|
|
66
|
+
enforces that. On Linux, a seccomp filter refuses UDP and other non-TCP
|
|
67
|
+
sockets, TCP Fast Open and `io_uring`, and hands each `connect` to
|
|
68
|
+
REDUE's launcher. The launcher allows a local port nothing listens on or
|
|
69
|
+
one the run's own processes listen on, and refuses everything else,
|
|
70
|
+
including a service you started outside the run (a local database). If
|
|
71
|
+
the launcher cannot start or stops early, the result is UNVERIFIED.
|
|
72
|
+
- **A private `/tmp`.** Its `TMPDIR` is new for each run, and it may not
|
|
73
|
+
write elsewhere in `/tmp` or `/var/tmp`.
|
|
74
|
+
- **A fresh home** for each run, as on CI.
|
|
75
|
+
- **Ignored files hidden** when the trial agrees, else the whole checkout.
|
|
76
|
+
- **Measured.** In the product census, test suites with a qualifying check
|
|
77
|
+
(alpha.27 in brackets): TypeScript on Linux 11 (2), on macOS 12 (2);
|
|
78
|
+
Python on Linux 17 (0), on macOS 14 (0). The strace cross-check found no
|
|
79
|
+
read outside the recorded inputs. The Linux connect supervisor added at
|
|
80
|
+
most 1.5% over the sandbox without it on suites with worker processes
|
|
81
|
+
(pytest-xdist, Jest, Vitest): apprise 233 s to 237 s, run-to-run spread
|
|
82
|
+
218-243 s. In a pilot with Claude Code on two pytest projects, the hook
|
|
83
|
+
skipped both "do the tests still pass?" reruns (19 s to 6 s and 28 s to
|
|
84
|
+
7 s), and plain reruns agreed with both skips.
|
|
85
|
+
- **macOS hides contents only.** On macOS, a hidden file's contents and
|
|
86
|
+
execution are refused, but its metadata is not, as on Linux. A test that
|
|
87
|
+
writes into an ignored folder works.
|
|
88
|
+
- Known limits: a suite that reads back ignored files it writes in the same
|
|
89
|
+
run (a coverage database, a build it starts) gets the whole checkout, and
|
|
90
|
+
one that writes tracked files or coverage data during the run ends
|
|
91
|
+
UNVERIFIED. On Linux, a test that needs a service started outside the run
|
|
92
|
+
fails in the sandbox, so `init` leaves it to a plain run.
|
|
93
|
+
|
|
3
94
|
## 0.1.0-alpha.27 — 2026-11-01
|
|
4
95
|
|
|
5
96
|
A build or test run no longer makes a sandboxed typecheck or lint STALE,
|
package/README.md
CHANGED
|
@@ -26,13 +26,17 @@ redue setup claude
|
|
|
26
26
|
stops before writing anything if the dependencies are not installed. It:
|
|
27
27
|
|
|
28
28
|
- finds your typecheck, lint and test scripts and writes `redue.config.json`
|
|
29
|
-
(it never changes your scripts, source or dependencies).
|
|
30
|
-
|
|
29
|
+
(it never changes your scripts, source or dependencies). In a Python
|
|
30
|
+
project it finds these tools in `.venv/bin`, with the arguments your CI
|
|
31
|
+
workflows, Makefile, justfile, tox, nox or scripts give them: mypy,
|
|
32
|
+
pyright and ty (typecheck), ruff (lint) and pytest (test). It says which
|
|
33
|
+
checks it left out and why, and which it can only record, not reuse;
|
|
31
34
|
- connects Claude Code: a short section in `CLAUDE.md`, a skill, and two hooks
|
|
32
35
|
in `.claude/settings.local.json`;
|
|
33
36
|
- starts REDUE's file observer, waits while it indexes the project (a minute
|
|
34
37
|
or more in a large one), then runs each check that can qualify once, so
|
|
35
|
-
there is a first result to reuse
|
|
38
|
+
there is a first result to reuse, and prints one line per run (the run's
|
|
39
|
+
own output only when it fails);
|
|
36
40
|
- prints every file it writes. Running it again changes nothing.
|
|
37
41
|
|
|
38
42
|
Then start a new Claude Code session in the project and work as usual. When
|
|
@@ -43,7 +47,7 @@ through REDUE so the new result is recorded.
|
|
|
43
47
|
To look yourself:
|
|
44
48
|
|
|
45
49
|
```text
|
|
46
|
-
$ redue status
|
|
50
|
+
$ redue status
|
|
47
51
|
✓ typecheck CURRENT / PASS
|
|
48
52
|
✓ lint CURRENT / PASS
|
|
49
53
|
|
|
@@ -54,10 +58,10 @@ src/main.ts changed
|
|
|
54
58
|
Due again when verification is needed: redue run "typecheck"
|
|
55
59
|
```
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
CURRENT.
|
|
61
|
+
`redue status` checks the files and your environment before it answers, as
|
|
62
|
+
the agent's hook does. `redue status --cached` gives the last answer the
|
|
63
|
+
observer published without checking; it can say UNVERIFIED ("cannot confirm
|
|
64
|
+
your execution context") where `redue status` would say CURRENT.
|
|
61
65
|
|
|
62
66
|
`redue stats` shows, per check, the reruns the hook skipped, the time saved,
|
|
63
67
|
the recorded runs and the top reasons a check was UNVERIFIED, from a log kept
|
|
@@ -65,28 +69,52 @@ on your machine.
|
|
|
65
69
|
|
|
66
70
|
To undo everything setup did: `redue remove`.
|
|
67
71
|
|
|
72
|
+
## Still CURRENT after your build and tests
|
|
73
|
+
|
|
74
|
+
Running the project's build or tests writes files (`dist/`, `.next/`,
|
|
75
|
+
coverage). REDUE leaves those out when the files are git-ignored, so a
|
|
76
|
+
typecheck or lint that passed stays CURRENT. In the census, after each
|
|
77
|
+
project's own build, tests, typecheck and lint ran, the share of sandboxed
|
|
78
|
+
checks still CURRENT:
|
|
79
|
+
|
|
80
|
+
| | alpha.26 | alpha.27 |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| TypeScript, Linux | 5 of 43 | 31 of 37 |
|
|
83
|
+
| TypeScript, macOS | 5 of 38 | 24 of 34 |
|
|
84
|
+
| Python, Linux | 24 of 37 | 31 of 38 |
|
|
85
|
+
| Python, macOS | 20 of 33 | 28 of 34 |
|
|
86
|
+
|
|
87
|
+
The rest changed for real: the build rewrote a tracked file (a sitemap, a
|
|
88
|
+
feed, a generated source), added a file git doesn't ignore, or installed
|
|
89
|
+
packages.
|
|
90
|
+
|
|
68
91
|
## What it covers today
|
|
69
92
|
|
|
70
93
|
In a census of 120 active public TypeScript repositories (November 2026, REDUE
|
|
71
|
-
at commit
|
|
72
|
-
with dependencies' install scripts on,
|
|
94
|
+
at commit 8cb32d6, before 0.1.0-alpha.29), each installed as its users would,
|
|
95
|
+
with dependencies' install scripts on, 89 installed cleanly on a macOS runner.
|
|
73
96
|
A check counts when two recorded runs on the same code both came out CURRENT
|
|
74
|
-
on the same inputs.
|
|
97
|
+
on the same inputs. 34 repositories had at least one such check:
|
|
75
98
|
|
|
76
99
|
| Check | Repositories | How |
|
|
77
100
|
|---|---|---|
|
|
78
|
-
| Typecheck |
|
|
79
|
-
| Lint |
|
|
80
|
-
| Tests |
|
|
101
|
+
| Typecheck | 20 | 13 by running the project's script in an OS sandbox ([sandboxed recipe](docs/user/sandbox.md)), 12 of them with the files git ignores and the documentation hidden; 5 by running the local `tsc` directly ([direct recipe](docs/user/direct-typescript.md)); 2 by running the script with every Node process recorded ([general recipe](docs/user/node-script.md)) |
|
|
102
|
+
| Lint | 20 | 16 sandboxed recipe (14 with ignored files and documentation hidden, 1 with ignored files hidden), 4 general recipe |
|
|
103
|
+
| Tests | 11 | 9 sandboxed recipe with no network (6 with ignored files and documentation hidden, 3 with ignored files hidden); 2 Vitest run directly ([direct test recipe](docs/user/tests-direct.md)) |
|
|
104
|
+
|
|
105
|
+
alpha.28 found 38 repositories on macOS. Test runs there no longer get
|
|
106
|
+
loopback, which cost 1 test suite (it starts a server on 127.0.0.1). Of the
|
|
107
|
+
other 3, one lint check was lost to a documentation-hiding bug fixed before
|
|
108
|
+
this release, one checkout now installs with pnpm in a way REDUE does not
|
|
109
|
+
take, and one `init` did not answer within the census's 10 minutes. On
|
|
110
|
+
Linux, where tests keep loopback, 10 test suites qualified.
|
|
81
111
|
|
|
82
112
|
In a census of 120 Python repositories, 74 installed on a Linux runner
|
|
83
|
-
and
|
|
84
|
-
22, all through the sandboxed recipe
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
with strace: each file it read in the checkout was a recorded input, a cache
|
|
89
|
-
or its own output.
|
|
113
|
+
and 31 had a qualifying check: typecheck (mypy, pyright, ty) 15, lint (ruff)
|
|
114
|
+
22, tests (pytest) 17, all through the sandboxed recipe (15, 22 and 12 of
|
|
115
|
+
them with ignored files and documentation hidden). On Linux, every sandboxed
|
|
116
|
+
run was traced with strace: each file it read in the checkout was a recorded
|
|
117
|
+
input, a cache or its own output.
|
|
90
118
|
|
|
91
119
|
Everything else stays UNVERIFIED, with a reason you can read in
|
|
92
120
|
`redue explain`. The usual ones: a script starts a program REDUE cannot see
|
|
@@ -142,7 +170,7 @@ harness, tasks and raw results are in [`eval/`](eval/README.md).
|
|
|
142
170
|
redue init # write redue.config.json from your scripts
|
|
143
171
|
redue start # start the file observer
|
|
144
172
|
redue run typecheck # run a check through REDUE and record the result
|
|
145
|
-
redue status
|
|
173
|
+
redue status # what still holds
|
|
146
174
|
```
|
|
147
175
|
|
|
148
176
|
Codex is supported too (`redue agent setup codex --apply`); see
|
package/bin/redue.mjs
CHANGED
|
@@ -7,7 +7,7 @@ import {directCompiler,directQualification,directEnvironment,directInstalledInpu
|
|
|
7
7
|
import {runArgv,tscRun} from '../src/typescript-invocation.mjs';
|
|
8
8
|
import {testEnvironment,testRunnerAt,testsInterpretation,testsQualification} from '../src/test-direct.mjs';
|
|
9
9
|
import {scriptEnvironment,scriptInterpretation,scriptKinds,scriptQualification} from '../src/node-script.mjs';
|
|
10
|
-
import {hiddenListIssue,hiddenQualification,isSandboxed,pythonTool,sandboxEnvironment,sandboxInterpretationOf,sandboxKinds,sandboxQualification} from '../src/sandbox.mjs';
|
|
10
|
+
import {docsQualification,hiddenDocsIssue,hiddenListIssue,hiddenQualification,isSandboxed,pythonTool,sandboxEnvironment,sandboxInterpretationOf,sandboxKinds,sandboxQualification} from '../src/sandbox.mjs';
|
|
11
11
|
import os from 'node:os';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import {createHash} from 'node:crypto';
|
|
@@ -40,8 +40,8 @@ Commands:
|
|
|
40
40
|
Connect project instructions; explicit setup only
|
|
41
41
|
start Start the local observer
|
|
42
42
|
stop Stop this observer
|
|
43
|
-
status [--json
|
|
44
|
-
status --
|
|
43
|
+
status [--json] Reconcile inputs and caller context, then answer (--sync is the same)
|
|
44
|
+
status --cached [--json] | --short The last answer the observer published, without reconciling
|
|
45
45
|
explain [CHECK] [--json] Explain applicability (synchronized; may cost more than a rerun)
|
|
46
46
|
detail [CHECK] [--json] Alias for explain
|
|
47
47
|
run CHECK [--if-due] Execute one configured check and record its outcome; with --if-due,
|
|
@@ -102,9 +102,12 @@ const json=args.includes('--json');
|
|
|
102
102
|
// run --if-due: run the check only when its last pass does not apply now.
|
|
103
103
|
const ifDue=command==='run'&&args.includes('--if-due');
|
|
104
104
|
if(ifDue)args.splice(args.indexOf('--if-due'),1);
|
|
105
|
-
|
|
105
|
+
// status syncs by default (warm, it costs well under 100 ms more than the
|
|
106
|
+
// cached answer); --cached and --short read the cached answer.
|
|
107
|
+
const cachedOnly=args.includes('--cached');
|
|
106
108
|
const dryRun=args.includes('--dry-run');
|
|
107
109
|
const short=args.includes('--short');
|
|
110
|
+
const sync=args.includes('--sync')||command==='status'&&!cachedOnly&&!short;
|
|
108
111
|
let workspaceSelection=null,checkSelection=null,recipeSelection=null;
|
|
109
112
|
if(command==='init')for(let i=0;i<args.length;){
|
|
110
113
|
if(args[i]==='--workspace'||args[i]==='--check'||args[i]==='--recipe'){
|
|
@@ -115,9 +118,10 @@ if(command==='init')for(let i=0;i<args.length;){
|
|
|
115
118
|
}
|
|
116
119
|
const check=['run','detail','explain'].includes(command)&&args[0]&&!args[0].startsWith('--')?args.shift():null;
|
|
117
120
|
if(command==='run'&&!check)error('run requires a check name');
|
|
118
|
-
if(args.some(value=>!['--json','--sync','--dry-run','--short'].includes(value)))error('unknown option; use --help');
|
|
121
|
+
if(args.some(value=>!['--json','--sync','--cached','--dry-run','--short'].includes(value)))error('unknown option; use --help');
|
|
119
122
|
if(sync&&!['status','detail','explain'].includes(command))error('--sync is for status or detail');
|
|
120
|
-
if(
|
|
123
|
+
if(cachedOnly&&(command!=='status'||args.includes('--sync')))error('--cached is a status option; do not combine it with --sync');
|
|
124
|
+
if(short&&(command!=='status'||json||args.includes('--sync')))error('--short is a cached status option; do not combine it with --sync or --json');
|
|
121
125
|
if(dryRun&&command!=='init')error('--dry-run is for init');
|
|
122
126
|
|
|
123
127
|
if(command==='init'){
|
|
@@ -153,6 +157,7 @@ if(command==='init'){
|
|
|
153
157
|
}
|
|
154
158
|
// A sandboxed check is proposed only when it gives the same exit status and
|
|
155
159
|
// output inside the sandbox as outside it: first with the checkout's
|
|
160
|
+
// git-ignored files and documentation hidden (sandbox-v3), then with the
|
|
156
161
|
// git-ignored files hidden (sandbox-v2), else with the whole checkout
|
|
157
162
|
// (sandbox-v1), reusing the plain run. Otherwise init writes what it would
|
|
158
163
|
// have without the sandboxed recipe, with the reason.
|
|
@@ -165,11 +170,18 @@ if(command==='init'){
|
|
|
165
170
|
for(const [at,row] of [...discovery.checks.entries()]){
|
|
166
171
|
if(row.qualification!==sandboxQualification||row.level!=='ready')continue;
|
|
167
172
|
if(!json)console.error(`Running ${row.config.name} inside and outside the sandbox to confirm it...`);
|
|
168
|
-
const
|
|
169
|
-
if(!
|
|
173
|
+
const docs=await sandboxTrial(discovery.root,row.config,{hide:'docs'});
|
|
174
|
+
if(!docs.issue)trials++;
|
|
175
|
+
if(!docs.issue&&docs.same){
|
|
176
|
+
row.config={...row.config,qualification:docsQualification};row.qualification=docsQualification;
|
|
177
|
+
row.reason=row.reason.replace('the whole checkout is its input',"the checkout's files git doesn't ignore, except its documentation, are its input")+`; ran both ways at init, exit ${docs.plain}`;continue;}
|
|
178
|
+
// Without a list of the documentation, the git-ignored files are still tried.
|
|
179
|
+
const hidden=docs.plainRun?await sandboxTrial(discovery.root,row.config,{hide:true,plain:docs.plainRun}):
|
|
180
|
+
docs.issue===hiddenDocsIssue?await sandboxTrial(discovery.root,row.config,{hide:true}):docs;
|
|
170
181
|
if(!hidden.issue&&hidden.same){
|
|
171
182
|
row.config={...row.config,qualification:hiddenQualification};row.qualification=hiddenQualification;
|
|
172
|
-
row.reason=row.reason.replace('the whole checkout is its input',"the checkout's files git doesn't ignore are its input")
|
|
183
|
+
row.reason=row.reason.replace('the whole checkout is its input',"the checkout's files git doesn't ignore are its input")+
|
|
184
|
+
`; ran both ways at init, exit ${hidden.plain}; it needs the documentation (${trialIssue(docs)})`;continue;}
|
|
173
185
|
// Without git, nothing is hidden: the whole checkout is tried on its own.
|
|
174
186
|
const trial=hidden.plainRun?await sandboxTrial(discovery.root,row.config,{plain:hidden.plainRun}):
|
|
175
187
|
hidden.issue===hiddenListIssue?await sandboxTrial(discovery.root,row.config):hidden;
|
|
@@ -222,7 +234,7 @@ if(command==='init'){
|
|
|
222
234
|
console.log('Ready to qualify means the supported contract will be validated; it is not verification evidence.');
|
|
223
235
|
console.log(`Next: review the config, then redue start and redue run ${JSON.stringify(discovery.checks[0].config.name)}.`);
|
|
224
236
|
console.log('Connect your agent: redue agent setup codex (or claude). Preview with --dry-run.');
|
|
225
|
-
console.log('
|
|
237
|
+
console.log('Then redue status says whether each result applies to the files as they are now.');}
|
|
226
238
|
} else if(unsupported&&!json)console.log(`No config written: ${summary.issue||'unsupported discovery boundary'}.`);
|
|
227
239
|
if(json)console.log(JSON.stringify({...summary,proposed_config:discovery.config}));
|
|
228
240
|
await exit(0);
|
|
@@ -381,7 +393,7 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
381
393
|
const windowsContext=process.platform==='win32'?
|
|
382
394
|
['ComSpec','COMSPEC','PATHEXT','USERPROFILE']:[];
|
|
383
395
|
const autoEnvironment=npmDirect?directEnvironment:testsDirect?testEnvironment:generalScript?scriptEnvironment(manager):
|
|
384
|
-
sandboxed?sandboxEnvironment(Boolean(pythonTool(check)),check.script?manager:null):npmAutomatic?{variables:['NODE_OPTIONS','NODE_PATH','CI','HOME',
|
|
396
|
+
sandboxed?sandboxEnvironment(Boolean(pythonTool(check)),check.script?manager:null,check.kind):npmAutomatic?{variables:['NODE_OPTIONS','NODE_PATH','CI','HOME',
|
|
385
397
|
'NODE_ENV','BASH_ENV','ENV',...windowsContext],prefixes:['npm_config_','DYLD_','TSGO_'],
|
|
386
398
|
pathExecutables:['node','npm'],executableIdentity:true}:yarnWorkspace?
|
|
387
399
|
{variables:['NODE_OPTIONS','NODE_PATH','CI','HOME','NODE_ENV','BASH_ENV','ENV',...windowsContext],
|
|
@@ -408,7 +420,7 @@ const checks=publicConfig.checks.map(check=>{
|
|
|
408
420
|
...(npmDirect?{qualificationInterpretation:directInterpretation}:{}),
|
|
409
421
|
...(testsDirect?{qualificationInterpretation:testsInterpretation}:{}),
|
|
410
422
|
...(generalScript?{qualificationInterpretation:scriptInterpretation}:{}),
|
|
411
|
-
...(sandboxed?{qualificationInterpretation:sandboxInterpretationOf(check.qualification)}:{}),
|
|
423
|
+
...(sandboxed?{qualificationInterpretation:sandboxInterpretationOf(check.qualification,check.kind)}:{}),
|
|
412
424
|
workspace:check.workspace||null,kind:check.kind||null};
|
|
413
425
|
});
|
|
414
426
|
const runtime={schema:1,provider:'declared-project@1',root,state,checks};
|
package/docs/user/agents.md
CHANGED
|
@@ -12,8 +12,8 @@ generic agent's instructions with the project owner's permission.
|
|
|
12
12
|
`redue --config CONFIG status --sync --json`. Do not infer a recipe from its name.
|
|
13
13
|
Cached status cannot establish its caller context. In mixed configurations,
|
|
14
14
|
assess each check's contract separately; this is not a global sync-first policy.
|
|
15
|
-
3. For other/unknown recipes,
|
|
16
|
-
|
|
15
|
+
3. For other/unknown recipes, `status --json` synchronizes too (`--sync` is the
|
|
16
|
+
same); `status --cached --json` and `--short` remain cheap inspection.
|
|
17
17
|
4. Reuse only schema 1, healthy observation and the selected check's CURRENT/PASS
|
|
18
18
|
with `reuse_eligible: true`. Missing eligibility means false. A historical FAIL
|
|
19
19
|
is never green evidence. Do not rerun eligible evidence merely for freshness.
|
|
@@ -167,11 +167,18 @@ commands never start the hook. The installed skill lists these exact commands,
|
|
|
167
167
|
so the agent uses forms the hook recognizes.
|
|
168
168
|
|
|
169
169
|
On a match, the hook runs `redue status --sync --json`. It waits up to half the
|
|
170
|
-
duration of the check's last completed run, at
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
duration of the check's last completed run, at least 2 seconds and at most 10
|
|
171
|
+
seconds. A status call usually takes 70-250 ms, so a fast check (mypy in 0.2 s)
|
|
172
|
+
is still skipped when CURRENT, or run through `redue run` and recorded when not.
|
|
173
|
+
When the observer is slow or stuck, a matching command can wait the full 2
|
|
174
|
+
seconds before it runs as written. Claude Code's own limit for the hook is 15
|
|
175
|
+
seconds.
|
|
176
|
+
|
|
177
|
+
To see why the hook decided what it did, set `REDUE_HOOK_LOG` to an absolute
|
|
178
|
+
file path in the environment Claude Code starts with. The hook appends one JSON
|
|
179
|
+
line per Bash command: the command, the decision (`deny`, `rewrite` or
|
|
180
|
+
`allow`) and why, such as `no check matches the command` or `status did not
|
|
181
|
+
answer within 2000 ms`.
|
|
175
182
|
It blocks the command only when the check is CURRENT, PASS and
|
|
176
183
|
`reuse_eligible: true`. The message names the check and the receipt's run ID. Every
|
|
177
184
|
other result allows the command: STALE, UNVERIFIED, FAILED, a timeout, an error, or
|
|
@@ -117,9 +117,10 @@ compiler invocation it selects, and for npm the
|
|
|
117
117
|
run through the package manager. No existing configuration is silently migrated.
|
|
118
118
|
Only use configs and probes from trusted repositories: these execute local code.
|
|
119
119
|
|
|
120
|
-
`status`
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
`status` and `explain` revalidate caller context and may execute read-only probes
|
|
121
|
+
(`status --sync` is the same as `status`). `status --cached` is the cached,
|
|
122
|
+
conservative answer, without revalidating. Synchronization can be slower than a
|
|
123
|
+
cheap check, especially on Windows. A timeout is UNVERIFIED, not green.
|
|
123
124
|
|
|
124
125
|
## Lifecycle and removal
|
|
125
126
|
|
package/docs/user/json.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Alpha JSON interface (schema 1)
|
|
2
2
|
|
|
3
|
-
Use `status --json`
|
|
4
|
-
|
|
3
|
+
Use `status --json` (or `status --sync --json`) for caller-aware applicability,
|
|
4
|
+
`status --cached --json` for cached state, and `explain CHECK --json` for selected synchronized detail.
|
|
5
5
|
`detail` remains an alias. Successful reads emit one JSON object, no human prose.
|
|
6
6
|
|
|
7
7
|
```json
|
package/docs/user/node-script.md
CHANGED
|
@@ -8,8 +8,8 @@ pnpm and Yarn TypeScript recipes, the [direct test recipe](tests-direct.md)
|
|
|
8
8
|
and, on macOS and Linux, the [sandboxed recipe](sandbox.md) first. It uses
|
|
9
9
|
the general recipe for a root typecheck or lint script none of them takes,
|
|
10
10
|
and where the sandbox cannot run (at init, or at run time inside Claude
|
|
11
|
-
Code's macOS sandbox). Test scripts use the direct test recipe
|
|
12
|
-
not propose the general recipe for them.
|
|
11
|
+
Code's macOS sandbox). Test scripts use the direct test recipe or the
|
|
12
|
+
sandboxed recipe; `init` does not propose the general recipe for them.
|
|
13
13
|
|
|
14
14
|
It supports a project installed by npm, pnpm 9-12 (isolated or hoisted
|
|
15
15
|
`node_modules`) or Yarn with the `node-modules` linker, with one lockfile kind
|