Skip to content

Commit 2faaf0e

Browse files
Merge pull request #279 from StepenkoAnatoli/arena/9aac6bbb-research-kit
docs(break-test): pass 8 report - network-off and root probes clean, one Low finding in handoff's subdirectory message
2 parents 1c1759f + c442d66 commit 2faaf0e

2 files changed

Lines changed: 205 additions & 0 deletions

File tree

‎docs/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ in this repository.
2727
| read the 2026-10-04 break-test (pass 4) | [`build-report-2026-10-04-break-test.md`](build-report-2026-10-04-break-test.md) | thirteen green runs, five findings, four fixed as their own commits, one recorded as a decided trade-off |
2828
| review the 2026-10-06 handoff audit | [`review-2026-10-06-handoff-audit.md`](review-2026-10-06-handoff-audit.md) | truthful closure labels, chronological audit snapshots, gate wording and their verification limits |
2929
| read the 2026-10-07 break-test (pass 7) | [`build-report-2026-10-07-break-test.md`](build-report-2026-10-07-break-test.md) | historical Arena probes, POSIX read-only deployment repair, the Windows CI reproduction, reporting limits and a separate follow-up verification record |
30+
| read the 2026-10-11 break-test (pass 8) | [`build-report-2026-10-11-break-test.md`](build-report-2026-10-11-break-test.md) | the no-network and root gaps closed, 70 repeated runs with no flake, one Low finding: `handoff`'s message when run from inside `research/` |
3031

3132
Two habits of this repository that a newcomer should know before reading further:
3233

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
# Break-test pass 8 - 2026-10-11
2+
3+
The break-test skill, run in a session pinned to the branch `arena/9aac6bbb-research-kit`
4+
at `195b2ca`. The pinned-branch rule applied: every probe ran in a throwaway clone under
5+
`/tmp/bt/`, never in the checkout. No code fix was applied (see F1), so the only commit is
6+
this report.
7+
8+
## Overview
9+
10+
- **Stack and gate.** Node ESM, no dependencies and no `package.json`; kit 0.9.5. The gate,
11+
read from `.github/workflows/offline-suite.yml` (authoritative over the README):
12+
`selftest.mjs`, `preflight.mjs`, `examples/release-evidence/run-example.mjs`, the three
13+
Node<->Python conformance runners, `timeline.mjs` and `export-warc.mjs` regenerating the
14+
committed bytes, and an empty `git status --porcelain`. CI's seven jobs are
15+
`platform (ubuntu-latest, ubuntu-26.04, windows-latest)`, `node (24, 26)` and
16+
`archive tree`, aggregated into the required `suite` check.
17+
- **Environment.** Linux container, 2 CPUs, uid 1001 (non-root, `sudo` group), Node
18+
v22.22.3, git 2.39.5, Python 3.11.2, no Chromium or Chrome, only the locales `C`,
19+
`C.utf8` and `POSIX`, fd cap 1024. Differently from pass 7: `unshare -n` is still refused,
20+
but **`unshare -rn` works** (user namespace + network namespace), which makes the
21+
network-off probe pass 7 could not run possible here. Inside that namespace the loopback
22+
interface must be brought up (`ip link set lo up`) or every test that uses a local
23+
127.0.0.1 server fails.
24+
- **Isolation.** Throwaway clones under `/tmp/bt/`: `fresh` (probe target), `p1`/`p2`
25+
(parallel runs), `deliv` (delivery diagnostics), `crlf` (autocrlf), a Hebrew-and-space
26+
path, a 272-character path, plus `/tmp/bt/browser` and `/tmp/bt/al2023libs` for an
27+
abandoned browser probe. The checkout itself was never modified, and
28+
`git status --porcelain` was empty before and after every run.
29+
- **Baseline.** Three unchanged full-suite runs in the real checkout: **1782 passed, 0
30+
failed, failure set empty, 4 unsupported, exit 1** (an incomplete run is not a pass,
31+
ADR-0108), at 166.1 s / 169.5 s / 176.9 s, 87 test files, Node 22.22.3. The 4 UNSUP are
32+
the `browser-guard`/`browser-transport` LIVE tests, which need Chromium; this host has
33+
none. No test failed in any baseline run, and no flake appeared.
34+
- **Root gate, same head.** `preflight.mjs` PASS (0 blocking, 1 warning
35+
`hygiene/brief-unstamped`, 28 passing, `evidencePolicy=pluralist`); `handoff.mjs` OK (33
36+
ledger entries, 27 evidence rows, chain verifies); `doctor.mjs` exit 1 with its one
37+
by-design blocker (`firecrawl-cli` not on PATH and no explicit keyless route declared;
38+
doctor prints the one-line fix); all four CI extra steps pass - release example 6/6, the
39+
three conformance pairs agree per vector, `TIMELINE.md (60 events)` and
40+
`55 records (27 captures)` regenerate, and the tree stays clean.
41+
42+
## Findings
43+
44+
> **[F1] `handoff` run from inside a project's `research/` folder says nothing has been
45+
> collected and tells the reader to collect it there** (Low, Possible)
46+
47+
Repro (any gated project with a collected corpus, e.g. a fresh clone):
48+
49+
```
50+
cd <project>/research
51+
node "$HOME/.agents/research-kit/bin/handoff.mjs" # exit 1
52+
```
53+
54+
Observed:
55+
56+
```
57+
handoff: nothing to hand off yet - nothing has been collected in this project.
58+
59+
Collect it here, then commit research/ including research/raw/ and its dotfiles:
60+
.../research-kit/bin/research.mjs
61+
```
62+
63+
The parent directory is the project root and holds a complete corpus - `handoff.mjs` run
64+
there prints OK (33 ledger entries, 27 evidence rows, chain verifies).
65+
66+
- **Root cause.** `bin/handoff.mjs` scopes everything to the cwd, which is the kit's
67+
contract ("the project is the current working directory"). From a subdirectory the four
68+
artifacts are absent, that is indistinguishable from an uncollected project, and the
69+
`nothingCollected` branch prints the collect-it-here remedy without checking whether the
70+
cwd is one of the project's own folders.
71+
- **Impact.** A misleading diagnosis whose advice points at collection, which spends
72+
credits (ADR-0129). The harm is bounded: the suggested command refuses in that directory
73+
- `research.mjs --dry-run` from `research/` prints `research/plan.json does not exist -
74+
nothing to collect` and exits 2 - so nothing is spent. `preflight.mjs` from the same
75+
folder prints `not gated - this project holds none of the four gate markers, so there is
76+
nothing to judge.` and exits 0 (the ungated scope notice, ADR-0153): the same
77+
subdirectory-scoped answer, without the wrong advice.
78+
- **Action: Not fixed, needs your decision.** The kit is feature-frozen (ADR-0117), and
79+
detecting "this cwd looks like a project's `research/` folder" is new behaviour, not a
80+
wording repair; it also changes user-facing text that tests cover. Recommended minimal
81+
change for the owner: when the cwd contains `raw/` and `EVIDENCE.md`, or
82+
`../research/raw/.fetches.jsonl` exists, print "this looks like the project's `research/`
83+
folder - run this from the project root" instead of the collect remedy.
84+
- **Verification.** The repro above, in a clone; the root-directory invocation still prints
85+
OK and is unchanged.
86+
87+
## Applied fixes
88+
89+
None. F1 is a frozen-surface diagnostic change and is reported for a decision instead.
90+
91+
## Rejected fixes
92+
93+
None attempted - no apply-and-revert cycle ran, because F1 is the only finding and its fix
94+
is not a break-test decision to take.
95+
96+
## Remaining risks
97+
98+
1. **The browser LIVE tests remain unverified here** (Medium, Unknown likelihood). They are
99+
the one place a real flake has been recorded: CI on 2026-10-11, the `node (24)` job's
100+
LIVE `browser-guard` meta-refresh test failed once - Chromium never made a request in
101+
45 s - and passed on re-run and in every sibling job. Attempted and abandoned: an
102+
npm-sourced `@sparticuz/chromium` build was installed to `/tmp` (73 MB, its own
103+
`al2023` libraries extracted to satisfy `libnss3`/`libnspr4`), but its Lambda-target
104+
binary never dumped a DOM in this container (`--dump-dom about:blank` timed out at 45 s
105+
with a GPU-process crash loop), and it is not the Chrome CI uses in any case.
106+
Recommendation: run the two LIVE files about 50 times on a host with the CI browser and
107+
see whether the meta-refresh flake reproduces; whether that environment is worth
108+
providing is the owner's call.
109+
2. **A fresh collector without the Firecrawl CLI is not READY** (Low, Certain by design).
110+
`doctor.mjs` exits 1 and names the fix (install the CLI, or declare
111+
`transport: http-keyless` in the machine config). No action needed; recorded so a
112+
"1 blocker" reading is not mistaken for breakage.
113+
3. **Reports unindexed in `docs/README.md`** (Low, Certain). Pass 7 recorded this; this
114+
report's row is added in the same commit, and the older unindexed reports remain for the
115+
owner to index or leave.
116+
117+
## Probe defects
118+
119+
- The first network-off run was executed without `ip link set lo up`, so the tests that use
120+
local loopback servers failed (9 `FAIL` lines in `browser-guard`, `large-response` and
121+
`panel`) while a browser attempt also competed for the CPU. That run was discarded, and
122+
the probe was re-run in a namespace whose loopback was up: 1782 passed, 0 failed. The
123+
discarded run is not evidence about the kit.
124+
- One invocation was wrong by the operator, not the kit: `preflight.mjs` was invoked as
125+
`node research-kit/bin/preflight.mjs` from a project folder that does not contain the kit
126+
(the kit lives at its install path), which produced a `MODULE_NOT_FOUND` from Node.
127+
Re-run with the kit's absolute path, the verdict was the designed FAIL of an uncollected
128+
project (2 blocking) - the skill's step-1 reminder that the project is the cwd, not the
129+
kit's location.
130+
131+
## Probes run
132+
133+
Commands are from the root of a clone unless noted. Full-suite invocations used
134+
`RESEARCH_KIT_RESULT_FILE`; the unsupported count is reported rather than waived, because
135+
ADR-0108 treats an incomplete run as not-a-pass. The 27 rows below are an activity
136+
inventory, not 27 independent full-suite passes.
137+
138+
| # | Probe | Command | Result |
139+
|---|---|---|---|
140+
| 1 | Baseline x3 | `node research-kit/bin/selftest.mjs` (real checkout) | 1782/0/4, exit 1, 166.1-176.9 s; failure set empty |
141+
| 2 | Root-in-userns control | `unshare -r node research-kit/bin/selftest.mjs` | 1782/0/4, 164.4 s - root changes nothing |
142+
| 3 | **Network off** | `unshare -rn sh -c 'ip link set lo up; node research-kit/bin/selftest.mjs'` | 1782/0/4, 162.5 s - the "runs offline" claim holds with no DNS and no sockets (pass 7's unreachable probe) |
143+
| 4 | Timezone | `TZ=Pacific/Kiritimati` full suite | 1782/0/4, 170.2 s |
144+
| 5 | Two suites at once | two clones, both full suites concurrently, 2 CPUs | both 1782/0/4, 175.9 s / 174.7 s |
145+
| 6 | Flakiness programme | 10x each: `concurrency`, `http-linear`, `proxy`, `large-response`, `panel`, `stdout-flush`, `browser-guard` | 0 FAIL in 70 runs; `browser-guard` exits 1 only because of its 4 UNSUP LIVE tests |
146+
| 7 | Clean checkout | fresh `git clone`, then `preflight.mjs` and `handoff.mjs` | exit 0 / exit 0 - nothing depends on untracked files |
147+
| 8 | CRLF / `core.autocrlf=true` | clone with `core.autocrlf true`, `git checkout-index -a -f`, then handoff/preflight | all files still LF; handoff OK; preflight PASS - the `.gitattributes` promise holds |
148+
| 9 | Missing capture | remove one `research/raw/*.md` in a copy, `handoff.mjs` | `handoff-capture-missing` names the evidence row and file; `handoff-chain-broken raw-missing`; collector remedy printed |
149+
| 10 | CRLF-smudged corpus | rewrite `research/raw/*.md` to CRLF, `handoff.mjs` | `handoff-chain-broken body-unmodified/line-endings ... differs only by CRLF line endings` - the ADR-0020 diagnosis, not a false "did not travel" |
150+
| 11 | HOME unset | `env -u HOME` doctor / preflight / handoff | exit 1 (the by-design blocker) / 0 / 0; no stack traces |
151+
| 12 | Minimal env | `env -i PATH=...` preflight / doctor | exit 0 / exit 1 (same blocker); no stack traces |
152+
| 13 | No git on PATH | `env PATH=<empty dir> node <abs>/doctor.mjs` | `fail git - git is not on PATH - the commit gate cannot install`, exit 1, fix printed; preflight still PASS |
153+
| 14 | Read-only project | `chmod -R a-w research/`, `preflight.mjs` | PASS - reads only |
154+
| 15 | Leaked git context | `GIT_DIR=<other repo>/.git` with preflight/handoff | correct verdicts (PASS / OK) - the CLIs are not redirected |
155+
| 16 | `RESEARCH_KIT_HOME` is a file | real `install.mjs` | exit 2, `could not write ... EEXIST`, target file untouched |
156+
| 17 | Read-only HOME | real `install.mjs`, `HOME` 0500 | exit 2, `could not write ... EACCES`; nothing left behind |
157+
| 18 | Hebrew + space project | `new-project.mjs "/tmp/bt/<hebrew> <space>" --topic "<hebrew>"`, `decompose.mjs`, `preflight.mjs` (kit by absolute path) | scaffold OK, decompose OK (MAP.md carries the Hebrew topic), preflight FAIL by design - 2 blocking on an uncollected project |
158+
| 19 | Long path | suite subset + handoff in a 272-character path | 10 passed / 0 failed; handoff OK |
159+
| 20 | fd cap | `ulimit -n 64`, suite subset | 2 passed / 0 failed |
160+
| 21 | Unicode TMPDIR | `TMPDIR=/tmp/bt/tmp-<hebrew>`, suite subset | 10 passed / 0 failed |
161+
| 22 | Panel port taken | listener on the port, then `panel.mjs --port <same>` | exit 2, `could not listen ... EADDRINUSE` - named, no stack |
162+
| 23 | Panel bad project | `panel.mjs --project /nonexistent` | exit 2, `<path> is not a folder` |
163+
| 24 | Subdirectory diagnostics | `preflight.mjs` / `handoff.mjs` from `research/` | F1 above: handoff's message is wrong for that cwd; preflight's "not gated" is the documented ungated scope |
164+
| 25 | CI extras | release example, three conformance pairs, timeline + export-warc, `git status` | 6/6 examples PASS; 3/3 pairs agree; 60 events and 55 records regenerate; tree clean |
165+
| 26 | Doctor / preflight / handoff at the head | the root gate | 1 blocker (by design), PASS 0 blocking, handoff OK |
166+
| 27 | Leftovers | `git status --porcelain` after every stage | empty - the suite and the probes wrote nothing into the checkout |
167+
168+
## Not probed
169+
170+
- **Windows and macOS.** No such host. CI covers Windows on every commit; macOS remains
171+
best-effort and untested in CI, as the workflow says.
172+
- **Real uid 0 outside a user namespace.** `unshare -r` maps the user to root *inside* a
173+
namespace; true container-root permission semantics were not covered, and the probe in
174+
row 2 showed no difference from the non-root baseline anyway.
175+
- **Locales other than `C`, `C.utf8` and `POSIX`.** None are installed.
176+
- **The four browser LIVE tests.** No Chromium or Chrome on this host; the npm-sourced
177+
build that was tried (see risk 1) never produced a DOM and is not the CI browser.
178+
- **The metered Firecrawl transport.** No key and no credits, and the skill forbids
179+
reaching paid services without consent; the collector ran keyless throughout.
180+
- **A real machine-wide install** (global `core.hooksPath`, `~/.claude/settings.json`).
181+
The skill forbids modifying the machine, so `install.mjs` ran with `HOME` or
182+
`RESEARCH_KIT_HOME` pointed at scratch paths, and the pre-commit hook was not registered.
183+
- **Node lines 24 and 26.** Only 22.22.3 is available locally; CI runs the other lines on
184+
every commit.
185+
- **Flakiness beyond the recorded programme.** 70 repeated subset runs and 7 full-suite
186+
passes found no flake; a multi-hour soak was not run.
187+
- **Git versions other than 2.39.5**, and a shallow clone's effect on the bundle/archive
188+
checks (the sandbox's own checkout is history-truncated, and `doctor`'s bundle row and
189+
the archive-tree job both passed anyway).
190+
191+
## Summary
192+
193+
The build is resilient at this head. Seven full-suite passes - three baselines, the
194+
root-in-userns control, the no-network run, a UTC+14 run and two simultaneous runs - all
195+
returned 1782 passed, 0 failed and an empty failure set, and 70 repeated runs of the
196+
timing-sensitive files produced no flake. Every deliberate break tried (no HOME, no git,
197+
read-only HOME and `research/`, a file where a folder belongs, a taken port, a missing
198+
capture, a CRLF-smudged corpus, Hebrew and 272-character paths, an fd cap of 64, a unicode
199+
TMPDIR) produced a named diagnostic instead of a crash; the two large gaps pass 7 had left
200+
open - no-network isolation and root semantics - are now closed. The single finding is a
201+
misleading `handoff` message when it is run from inside `research/` (Low; not fixed,
202+
because the freeze makes the fix a feature). The most important open risk is the browser
203+
LIVE tests, unverifiable here and the only place a flake has ever been recorded. Nothing on
204+
the branch changes kit code: review this one report commit, and the branch stays green.

0 commit comments

Comments
 (0)