Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ Each release of Research-Kit, newest first. The version is `KIT_VERSION` in

## Unreleased

- `decompose --limit` consistently requires a whole number of at least 1 before
transport selection. `--max-scrapes 0` remains supported (#320).

- ZIP archives declare UTF-8 names in both headers, so ordinary extractors preserve non-ASCII paths declared by the package manifest (#345).

- Machine config saves preserve unpatched raw role and posture values, including typos
Expand Down
2 changes: 1 addition & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ build approval. The shared verdict library and all gated formatting stay unchang
(→ evaluate; `--staged-stdin` reads the piped path list; `--posture` answers 0 allow /
1 fail-closed / 2 unreadable-and-closed, so no shell has to parse JSON), `doctor.mjs` (`--fix-arity`), `install.mjs` (deploy + `--into` per-project
binding, role-aware next steps; collector keyless instructions name the selected machine config rather than a per-run research flag, builder instructions require complete/current review, and an unresolved role prints diagnostics before role-specific advice (2026-10-08, ADR-0150); `tolerateClosedStdout` before its first write, so a reader that leaves early does not turn a finished deploy into exit 1, 2026-09-30), `install-hooks.mjs` (`--fail-closed`, `--role`; a `CONFIG-UNREADABLE` save refusal is named with exit 2 before the requested config patch is written, #364),
`new-project.mjs` (its first next step omits `--topic` once the project has one, ADR-0056), `decompose.mjs` (phase 0 CLI, refused on a builder by role),
`new-project.mjs` (its first next step omits `--topic` once the project has one, ADR-0056), `decompose.mjs` (phase 0 CLI, refused on a builder by role; `--limit` requires at least 1 at the CLI boundary, matching the library's positive search-result limit, while `--max-scrapes` still allows 0, #320),
`timeline.mjs`, `audit.mjs` (lists and resolves versions through the manifest's
reader), `brief.mjs` (drafts the handoff with all evaluated warning reasons, remedies and
row/unknown targets, labeled as whole-corpus observations; it reports the brief's state; it,
Expand Down
2 changes: 1 addition & 1 deletion research-kit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ node research-kit/bin/selftest.mjs # all of it
node research-kit/bin/selftest.mjs gate hook # just these files
```

1845 tests, offline, no key and no network. The runner **awaits** every test, so `ok` means
1846 tests, offline, no key and no network. The runner **awaits** every test, so `ok` means
the assertions settled (ADR-0021), and each test is raced against a watchdog
(`RESEARCH_KIT_TEST_TIMEOUT`, default 60s).

Expand Down
2 changes: 1 addition & 1 deletion research-kit/bin/decompose.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ refuseUnknownFlags(flags, ['help', 'topic', 'dry-run', 'force', 'limit', 'max-sc
// made 1, so `--max-scrapes` with no value quietly paid for one page.
checkFlagValues(flags, {
topic: 'value', recipe: 'value', transport: 'value', 'search-transport': 'value',
limit: { int: true, min: 0 }, 'max-scrapes': { int: true, min: 0 },
limit: { int: true, min: 1 }, 'max-scrapes': { int: true, min: 0 },
});
const root = process.cwd();

Expand Down
11 changes: 10 additions & 1 deletion research-kit/test/cli.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1032,7 +1032,9 @@ test('a flag given a bad value, or none, is refused before anything runs', () =>
// Number() makes 1 - so a real run paid for one page nobody asked for.
['decompose.mjs', ['--dry-run', '--max-scrapes'], /--max-scrapes needs a value/],
['decompose.mjs', ['--dry-run', '--limit'], /--limit needs a value/],
['decompose.mjs', ['--dry-run', '--limit', 'abc'], /--limit must be a whole number of at least 0, not "abc"/],
['decompose.mjs', ['--dry-run', '--limit', 'abc'], /--limit must be a whole number of at least 1, not "abc"/],
['decompose.mjs', ['--dry-run', '--limit', '0'], /--limit must be a whole number of at least 1, not "0"/],
['decompose.mjs', ['--dry-run', '--limit', '-1'], /--limit must be a whole number of at least 1, not "-1"/],
['preflight.mjs', ['--check'], /--check needs a value/],
['gate.mjs', ['--gate', 'push'], /--gate must be one of/],
['prior.mjs', ['--file'], /--file needs a value/],
Expand All @@ -1047,6 +1049,13 @@ test('a flag given a bad value, or none, is refused before anything runs', () =>
assert.equal(only.status, 2, `preflight --only is accepted and never read:\n${only.all.slice(0, 300)}`);
});

test('decompose accepts a search limit of one while max-scrapes still accepts zero', () => {
const r = run('decompose.mjs', ['--dry-run', '--limit', '1', '--max-scrapes', '0', '--transport', 'http-keyless'], {
root: planned('budget-floor'),
});
assert.equal(r.status, 0, r.all);
});

// Found 2026-09-27: new-project over an existing project with a different --topic reported
// "wrote 0, kept 13" and printed a fresh project's next steps. The topic asked for was not
// applied, and nothing said so.
Expand Down
Loading