Skip to content

Commit dce565f

Browse files
Collect the delivery-architecture evidence into a committed nested project
The recommendation in docs/preliminary-2026-09-21-delivery-architecture.md had no provenance under it: nine pages were collected into a scratch project outside the repository, read, and then deleted as routine cleanup, taking the captures, the hash-chained ledger and every claim's traceability with them. This is that research done again, through the gate, in a place it cannot be lost from. Nine pages, metered route, nine credits, all graded full, none failed. preflight PASS 0 blocking, 0 warning(s), 12 passing handoff OK - 9 ledger entries, every cited capture on disk, chain verifies doctor READY Run with docs/decisions/2026-09-21-delivery-architecture/ as the working directory. The repository's own preflight judges the repository's corpus and says nothing about this one; citing it here would repeat the error that produced the preliminary note. THE EVIDENCE OVERTURNED THE HEADLINE FINDING IT WAS COLLECTED TO SUPPORT. The preliminary note recorded that POST /actions/workflows/{id}/dispatches returns 204 No Content, that a caller therefore cannot learn which run it started, and that a client_ref correlation token had to be designed into the first release. The collected documentation says 200, with workflow_run_id, run_url and html_url. Both are true, and neither page explains the other - so it was settled by running both against this repository: 2022-11-28 (the default) 204 No Content, empty body 2026-03-10 200 OK, {"workflow_run_id":35548135379,...} X-Github-Api-Version-Selected names the version in each response, and run 35548135379 resolves to a real queued workflow_dispatch run. The Deprecation header on the 204 is the same signal from the other side: the version returning nothing is the deprecated one, and it is the default. So the correlation race is a property of an unpinned client, not of the endpoint. The requirement that replaces client_ref is smaller and firmer: pin X-GitHub-Api-Version. A second finding worth the collection on its own. Environments give required reviewers and wait timers - but on a GitHub Free plan those rules exist only for public repositories, and a Free repository converted to private has its protection rules and environment secrets IGNORED, not refused. The privacy argument for going private would silently remove both the approval gate on the paid run and the secret holding the credential. Sensitive research needs a paid plan, not a visibility toggle. The three human steps were done here rather than claimed: every MAP row judged COVERED or DISMISSED with a reason, every auto-extracted Finding rewritten into a claim, and the brief's Contradictions and Decision sections authored. The extractor's draft for E-01 was a single table row; the claim it became is the finding above. Committed deliberately: research/raw/.fetches.jsonl. The nested .gitignore excludes the byproducts (.usage.jsonl and friends) with patterns relative to this project, so the repository's own rules never had to learn the path exists. Two gaps are recorded rather than papered over: the runner-side `npm install -g firecrawl@<version>` path has still never executed, and whether artifact download suits the intended operator is a usability question no vendor page answers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent feb7a3d commit dce565f

24 files changed

Lines changed: 7486 additions & 0 deletions
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# The corpus's line endings are integrity, not style.
2+
#
3+
# A capture's body hash is taken over the LF bytes the collector fetched. git's smudge
4+
# filter rewrites every text file on a default Windows checkout (core.autocrlf=true), so
5+
# without this file the whole corpus fails `body-unmodified` on arrival with nothing
6+
# actually wrong with it. This file outranks a reader's core.autocrlf. See ADR-0020.
7+
8+
research/raw/* text eol=lf
9+
*.jsonl text eol=lf
10+
research/*.md text eol=lf
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
node_modules/
2+
.DS_Store
3+
4+
# Credentials never belong in a research project (AGENTS.md Rule 6). Note that
5+
# `firecrawl env` writes the key into .env, which is why the rule says never to run it
6+
# inside a repository.
7+
.env
8+
.env.*
9+
*.pem
10+
*.key
11+
12+
# The Firecrawl CLI saves scrape output into .firecrawl/ in whatever directory it runs
13+
# from. That is a vendor cache, not evidence - the kit's own captures live in
14+
# research/raw/ with a ledger entry behind each one.
15+
.firecrawl/
16+
17+
# The kit's local review logs are not evidence.
18+
research/raw/.usage.jsonl
19+
research/raw/.failures.jsonl
20+
research/raw/.diagnostics.jsonl
21+
research/raw/.fetches.lock
22+
23+
# The chain IS evidence and must travel. The explicit negation is here so a broader
24+
# dotfile rule added later cannot silently exclude it - this repository lost its ledger
25+
# exactly that way once.
26+
!research/raw/.fetches.jsonl
27+
!research/overrides.log
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Research-first project bootstrap
2+
3+
Agent instructions for this project. Read this before planning, designing, or building
4+
anything here.
5+
6+
## This is a DECISION PROJECT, and the project root is this directory
7+
8+
`docs/decisions/2026-09-21-delivery-architecture/` is a self-contained research project
9+
nested inside a larger repository. Two rules follow, and both have been broken already:
10+
11+
1. **Every kit command runs with THIS directory as the current working directory.** The kit
12+
takes no project argument. Run from the repository root, `preflight` judges the
13+
repository's corpus - a different contract, a different set of unknowns - and reports a
14+
verdict that says nothing about this project. Quoting the parent's PASS beside this
15+
project's claims is the error that produced
16+
`../../preliminary-2026-09-21-delivery-architecture.md`.
17+
2. **This project's corpus is committed, ledger included.** Research supporting a
18+
repository-level architectural decision lives at `docs/decisions/<date>-<name>/`.
19+
Temporary scratch projects may be used for experiments, but their findings cannot
20+
support a committed decision unless the complete evidence corpus and ledger are
21+
preserved. The first attempt at this research was collected into a temporary directory
22+
and deleted as cleanup, taking nine captures, the hash chain and the provenance for
23+
every claim with it.
24+
25+
Do not collect this project's topic into the repository's own `research/`, and do not
26+
collect the repository's topic into this one. One project, one contract, one corpus.
27+
28+
## How this project works: two phases, two roles
29+
30+
**Phase 1 - research (this kit).** Close every blocking unknown with fetched primary
31+
sources, then pass the gate. The output is a briefing, not code: `research/DISCOVERY.md`
32+
(the contract), `research/EVIDENCE.md` (claims with cached pages behind them), and
33+
`research/BRIEF.md` (the handoff). Phase 1 never writes product code.
34+
35+
**Phase 2 - build (someone else's job).** A builder - another agent, a different model, or
36+
a human - takes `research/BRIEF.md` plus `research/` and implements. They should not need
37+
to re-research anything: if they do, phase 1 was incomplete, and the fix is to collect the
38+
missing fact rather than to let the builder guess it.
39+
40+
The gate is the handoff point. Before it passes, phase 2 does not start.
41+
42+
## Two machines, two roles
43+
44+
The role is machine config - `role: "collector" | "builder"` in
45+
`~/.agents/research-kit.config.json`, default `collector`; declare it with
46+
`node ../../../research-kit/bin/install-hooks.mjs --role builder`.
47+
48+
| | **collector** (the operator's PC) | **builder** (a sandbox, a CI box, a laptop) |
49+
|---|---|---|
50+
| holds | the Firecrawl key | no key, no Firecrawl egress |
51+
| runs | `decompose.mjs`, `research.mjs` | `handoff.mjs`, `preflight.mjs`, the build |
52+
| a missing key is | a **FAIL** | **informational** |
53+
| must | push `research/raw/` including its dotfiles | **not collect** - the collection CLIs refuse (exit 2) |
54+
55+
**If you are on a builder machine and there is no brief: you do not collect.** A page
56+
fetched by hand is not evidence in this kit, and a builder that "re-collects" a missing
57+
page forges a corpus instead of reporting a gap. Name the missing fact instead.
58+
59+
A builder's first command is:
60+
61+
```
62+
node ../../../research-kit/bin/handoff.mjs
63+
```
64+
65+
The remedy depends on the cause, and the command says which: something that did not
66+
travel is the collector's to push (`git add -f research/raw/`, dotfiles included);
67+
a corpus that travelled whole and was rewritten on checkout here is fixed **here**, with
68+
`.gitattributes`, and costs no credits.
69+
70+
## Starting in the wrong place: ask which project, do not hunt for it
71+
72+
The project is the **current working directory**. The kit takes no project argument and
73+
never will. If the cwd is not the project the operator means - the kit's own repo, a
74+
parent folder, an unrelated checkout - **ask which project**, and wait. Do not search the
75+
filesystem, do not infer it from a name, do not look it up on a remote. Searching a
76+
person's disk to guess his intent reads directories nobody authorised, and the question
77+
it replaces takes one line.
78+
79+
Signals: no `AGENTS.md`, no `research/` directory, or a `research/` corpus plainly about
80+
something else. An existing corpus about another topic is the signal doing its job.
81+
82+
## Resuming after an interruption: resume, do not restart
83+
84+
The gate is a pure function of repository state, so orient from disk before touching
85+
anything. `doctor.mjs` and preflight answer *where this project is*; the last commit
86+
report and `docs/ARCHITECTURE.md` say *what was happening and why*; `git status` answers
87+
what is unfinished, and its uncommitted changes are the interrupted task - partial and
88+
unverified, work in progress, never state to trust. The corpus is already collected and
89+
spent credits are not spent again.
90+
91+
## Rule 1 - research first, then build
92+
93+
The sequence is decompose -> contract -> collect -> gate -> brief.
94+
95+
0. **Decompose** (collector machine): `node ../../../research-kit/bin/decompose.mjs --topic "<topic>"`
96+
drafts `research/MAP.md` seeded with the universal checklist. The tool contains no
97+
judgment. Mark every row COVERED (citing U-## rows), DISMISSED (reason required), or
98+
GAP.
99+
1. **Write the intent** in `research/DISCOVERY.md` under `## Build intent`, and enumerate
100+
the blocking unknowns FROM the map.
101+
2. **Collect**: `node ../../../research-kit/bin/research.mjs --plan research/plan.json`. Prefer the page
102+
that *owns* the fact over any write-up about it.
103+
3. **Rewrite** each auto-extracted `Finding` cell into a real claim, keeping the `Raw`
104+
cell pointing at the cached page.
105+
4. **Gate**: `node ../../../research-kit/bin/preflight.mjs`. **Do not start building until it prints
106+
PASS.** Evidence must be *fetched*, not typed, and by a named transport.
107+
5. **Hand off**: write `research/BRIEF.md`.
108+
109+
There are exactly three overrides - `git commit --no-verify`, a deliberate
110+
`research/GATE_OFF`, and a repository-local `core.hooksPath` - and all three are recorded
111+
in `research/overrides.log`. If you take one, say so in your reply.
112+
113+
## Rule 2 - questions are for intent, never for facts
114+
115+
At most **3** questions, once, up front, and only about things no document can answer. If
116+
a question's answer is in public documentation, it is a research task. **Never answer
117+
"insufficient info" and stop.**
118+
119+
## Rule 3 - known unknowns are allowed, silence is not
120+
121+
Mark a genuinely unreachable fact `KNOWN-UNKNOWN` and write the day-one verification step
122+
in the `Evidence` cell. An unlabeled gap is the failure mode this project exists to
123+
prevent.
124+
125+
## Rule 4 - source quality
126+
127+
`P` primary/official carries the design. `S` secondary is context. `L` lead-only is a
128+
hint, never proof. Never invent a citation. Flag contradictions instead of averaging them.
129+
130+
## Rule 5 - cost discipline
131+
132+
Every scrape spends credits. Plan the queries in `research/plan.json` first, reuse the
133+
cache (`--refresh-days`), and check the budget with `node ../../../research-kit/bin/research.mjs --status`.
134+
135+
## Rule 6 - secrets
136+
137+
The key lives in the CLI config or the environment. Never write it into this repository.
138+
139+
## The standing protocol
140+
141+
Five rules for how work here is committed and recorded. Documentation, not enforcement:
142+
no check judges prose, because a gate that judges prose is a gate that will be wrong.
143+
144+
1. **Architecture map current in the same commit.** A commit touching a declared code
145+
path (`research/kit.json`) stages `docs/ARCHITECTURE.md` with it. Enforced - and a
146+
prompt, not a proof: the gate checks the map was *staged*, not that it is *current*.
147+
2. **Commit report: five named parts** - what changed / why / what it touched / what you
148+
verified / what you got wrong and fixed. The got-wrong line is not optional; if
149+
nothing was gotten wrong it is written as "nothing to report".
150+
3. **An ADR for any choice with a rejected alternative**, dated, with the reason a future
151+
explorer would need to avoid re-suggesting it.
152+
4. **One commit per task - the revert test.** `git revert <sha>` undoes it alone.
153+
5. **A red suite stops work**, reported immediately and alone, **with the cwd recorded**.
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Start here
2+
3+
This is a **decision project**: a self-contained research project whose corpus supports one
4+
repository-level architectural decision. It is not the repository's own research project -
5+
that one lives at the repository root and answers a different set of unknowns.
6+
7+
The question here: **how should Research-Kit be delivered to a non-technical user?**
8+
9+
Read `research/BRIEF.md`. Everything else is what it rests on.
10+
11+
---
12+
13+
## The convention this project establishes
14+
15+
> Research supporting a repository-level architectural decision lives in a self-contained
16+
> project at `docs/decisions/<date>-<decision-name>/`.
17+
>
18+
> Temporary scratch projects may be used for experiments, but **their findings cannot
19+
> support a committed decision unless the complete evidence corpus and ledger are
20+
> preserved.**
21+
22+
The second sentence exists because of a specific failure. The first attempt at this
23+
research collected nine pages into a temporary directory outside the repository, read the
24+
findings, and then deleted the directory as routine cleanup - taking the captures, the
25+
hash-chained ledger and the provenance for every claim. What survived was a recommendation
26+
with nothing under it. See `../../preliminary-2026-09-21-delivery-architecture.md`, which
27+
is that note, kept and labelled rather than quietly promoted.
28+
29+
Why nested rather than a second repository, or a multi-topic contract: `readCorpus(root)`
30+
reads one `research/` per project root and a project has exactly one discovery contract, so
31+
a self-contained directory is the shape the kit already supports. Every command works by
32+
changing into this directory first. No protocol change, no second repository per decision,
33+
and the reasoning sits beside the thing it reasons about.
34+
35+
---
36+
37+
## Verifying this project
38+
39+
**From this directory.** The parent repository's preflight judges the parent corpus and
40+
says nothing about this one; quoting it here would be the same category error that produced
41+
the preliminary note.
42+
43+
```
44+
cd docs/decisions/2026-09-21-delivery-architecture
45+
node ..\..\..\research-kit\bin\doctor.mjs
46+
node ..\..\..\research-kit\bin\preflight.mjs
47+
node ..\..\..\research-kit\bin\handoff.mjs
48+
```
49+
50+
As of 2026-09-21 that is `READY`, `PASS 0 blocking, 0 warning(s), 12 passing`, and
51+
`handoff OK - 9 ledger entries, every cited capture on disk, chain verifies`.
52+
53+
---
54+
55+
## What is in here
56+
57+
| File | What it is |
58+
|---|---|
59+
| `research/BRIEF.md` | The reviewed handoff. Read this first |
60+
| `research/DISCOVERY.md` | The contract: eight unknowns, all CLOSED |
61+
| `research/EVIDENCE.md` | Nine rows, each pointing at a cached page |
62+
| `research/MAP.md` | The topic decomposition, every row judged, plus two stated gaps |
63+
| `research/raw/` | The nine captures **and** `.fetches.jsonl`, the hash chain |
64+
| `research/plan.json` | What was collected and why |
65+
66+
`research/raw/.fetches.jsonl` is committed deliberately. A decision project whose ledger
67+
does not travel proves nothing, which is the whole lesson above.
68+
69+
---
70+
71+
## Starting another decision project
72+
73+
```
74+
node ..\..\..\research-kit\bin\new-project.mjs docs/decisions/<date>-<name> --topic "<question>"
75+
```
76+
77+
Then fill `research/DISCOVERY.md` with unknowns that would change the design if guessed
78+
wrong, collect, do the three human steps, and commit the corpus with the ledger.
79+
80+
---
81+
82+
## Budget
83+
84+
This project's collection cost **9 credits** - nine pages, no searches, nothing cached,
85+
nothing failed. `research/raw/.usage.jsonl` records the run and is not committed; it is
86+
machine-local state, not evidence.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ARCHITECTURE - not applicable to this project
2+
3+
The standard scaffold writes this file because most projects it creates go on to hold
4+
code, and `research/kit.json` declares the code paths (`src`, `lib`, `bin`, `scripts`,
5+
`app`) whose commits the gate requires this file to accompany.
6+
7+
**This project holds no code and is not expected to.** It is a decision project: its whole
8+
output is `research/BRIEF.md` and the corpus under `research/` that the brief rests on.
9+
None of the declared code paths exists here, so the rule this file exists to serve never
10+
fires.
11+
12+
The file is kept rather than deleted for one reason: `research/kit.json` and the scaffold's
13+
`LAYOUT` both expect it, and a project missing a shape entry reports a finding in `doctor`
14+
that a reader then has to investigate and dismiss. A file that says "not applicable, and
15+
here is why" costs less than a recurring warning nobody can close.
16+
17+
If a future decision project under `docs/decisions/` does ship code - a spike, a prototype,
18+
a reference implementation - replace this file with a real map of it and the gate resumes
19+
doing its job with no configuration change.
20+
21+
The map of the **kit's** code is `../../../ARCHITECTURE.md`. The map of this project's
22+
**topic** is `../research/MAP.md` - a different artifact with a different job, judged by a
23+
different check.

0 commit comments

Comments
 (0)