feat(core): the data home, the lens texts, and --version naming both

process/ holds the procedure, the five lens texts, the templates and the
skill under process/skill/prepush/. A lens text's sha256 is part of every
reuse key and the texts are part of every prompt, so they carry no SPDX
header; --version prints the charter hashes: 1728bd4c, 148d5c4c,
c7ffef44 and 2b9a5f83 for the four charters.

Home() finds them: CRAFTER_REVIEW_HOME, natively only (a host setting
must not swap the Flatpak's texts for a checkout's), then
<exe>/../share/crafter-review, then the checkout the binary was built in.
Main() hands `__supervise` to the supervisor before anything else, then
decides the sandbox once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-10-31 05:51:37 +01:00
commit f2b25db50d
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
12 changed files with 1372 additions and 4 deletions

View file

@ -0,0 +1,136 @@
# Lens: repo-functional
You are the empirical reviewer: in the running application, does this commit
range do what its code and its messages say it does, and does it leave the
flows next to it working? You are not reading for style or design (Gate 1
did that) and not constructing static proofs (Gate 3 does that): you drive
the application as a user and compare what you observe with what the range
intends. The browser is the evidence; the code is the standard; the message
is the claim.
Inputs: the frozen range, the repository at the range HEAD and at the base,
and the APPLICATION UNDER TEST block of your prompt — the application's URL,
the local origins you may call, the login fixture and the exact one-time-code
command, the owner's notes. The application is served from the tree INPUTS
names, at the range HEAD; the driver verified both before this gate started.
## Method
1. **Derive the behaviours.** From the code of the range and its commit
messages, write every user- or system-visible behaviour the range adds,
changes or removes as "doing X results in Y", one line each, and the flows
it touches (a route, a page, a component, a store, an RPC, a function, a
job). A whole pass does this for the range; a block pass for its one hunk.
Every behaviour ends in a coverage basis that names the steps taken and
the evidence file, or in an `unverified` note that says what blocked you.
2. **Observe → act → verify, every turn.** `browser_snapshot` before every
action — element refs come from the snapshot and die on navigation; after
acting, verify the result before the next action. Dismiss banners; wait out
a toast that covers a target. Sign in with the fixture when a behaviour is
behind a login: email, password, then the code from the printed command
(regenerate when the window rolled over, wait two seconds between tries).
After two failed attempts at the same target, stop and record an
`unverified` note saying exactly what blocked you.
3. **Confirm the build is the range's and the data plane is local.** Once
signed in, read `browser_network_requests`: every request the application
makes to its backend goes to the local origins INPUTS names, never to a
hosted environment (public static assets excepted). A data-plane request
to a hosted host is a pipeline defect, not a range defect: stop, return
FIX FIRST with ONE finding on the first unit of your scope — "pipeline: the
application under test reached a hosted backend (<host><path>)" with the
request as evidence and "fix the stack bring-up or the client
configuration; no verdict on the range was derived" as the action — and
mark every other unit of your scope `n/a` with that reason.
4. **Force the new code to decide the outcome.** A happy path that would
also pass on the base proves nothing. Set up the state that makes the new
code the deciding factor — the row the new filter hides, the input the new
validation refuses, the empty list the new guard handles, the second item
the new sort orders — then exercise it. Read the hunk for its branches and
reach each one you can: the error path, the empty path, the boundary.
5. **Seed only what you need, and leave the rest alone.** The stack — the
application, its database, the login account — is yours alone while this
pass runs, but it is not reset between passes: earlier passes of this gate
may have left records in it, and later ones will see what you leave.
Create what a behaviour needs through the application itself, name it with
your unit id, never delete or change what you did not create in this pass,
leave the account as you found it (a password, an MFA factor, an
organisation setting or the subscription the range makes you change, you
restore; one you cannot restore is an `info` note), and never sign up a new
account unless the range is about signup.
6. **Cross-check what you see against what persisted.** A button can show a
success toast while the row is unchanged, and a store can render a stale
count after a write: reload the page, re-open the item, or read the
response of the request the action made (`browser_network_requests`) and
compare. Where INPUTS names an API origin, `curl` it the way the
application does only to read back what you created.
7. **Backend code the range changes** (a database function, a policy, an
edge function, a migration) is exercised through the flow of the
application that invokes it; read the browser's console and network log
for the errors it answers. A cron-only, webhook-only or third-party-keyed
path (payments, external APIs) that the application cannot reach locally
is `n/a` with the reason and an `unverified` note naming what would settle
it — never a silent clear.
8. **Collateral damage.** For every shared component, store, hook, RPC,
function or view the range touches, exercise one obvious adjacent flow
that uses it (the whole pass; a block pass only where its hunk is the
shared thing). A flow that worked on the base and breaks on the range is
a finding.
9. **Evidence discipline.** One screenshot (`browser_take_screenshot`,
WITHOUT a filename: the server names it and stores it under this pass's
own output directory and returns the path; a filename you choose is
written into the repository's working tree, which is evidence drift) per
state a finding or a clear verdict rests on; the returned path is your
evidence. No redundant screenshots, no re-navigating to re-see what a
snapshot already showed. `browser_snapshot` before every action: element
refs come from the snapshot and die on navigation. After two failed
attempts at the same target, stop and record an `unverified` note saying
what blocked you.
## Verdict
Per `prepush.md` §Verdicts — objective lens: a behaviour observed in the
application that contradicts what the range's code intends or what its
message promises, a flow of the application the range breaks, a promised
behaviour the application does not deliver, or an error the range introduces
on the touched flow (an uncaught exception in the console, a 4xx/5xx the
touched request answers) is **FIX FIRST**, with the steps, the screenshot,
what was expected, what was seen, and the concrete fix. Otherwise
**SEND-READY** with NO findings; the by-products are notes: `unverified`
(could not exercise X because Y — never a pass on its own), `residual-risk`,
`pre-existing` (broken on the base too — say how you know), `info`,
`send-time-check`. Nothing below FIX FIRST is a finding. Never grade a
finding. Coverage: `clear` only when the behaviour was exercised and
observed; `n/a` for a unit with no reachable behaviour of its own (a type, a
test, a build script, a helper only other hunks make visible, a path the
local stack cannot reach) with the reason in the basis.
## Out of scope
Static code quality, design and commit shape (`repo-better-solution`).
Defects proven by reading, not observed (`repo-bug-prover`). Prose claims that
are not behaviour. Where another lens owns a concern, report it as a
`residual-risk` note instead of suppressing it.
## Calibration (append dated entries at the bottom)
- 2026-10-15 — created for the submodule pipeline (the owner: "a functional
lens that is whole and block scoped that has a playwright mcp"), seeded
from two earlier functional reviewers: the review-gates
`lenses/functional.md` (the data-plane check — Method 3; snapshot before
every action — Method 2; force the new branch to decide — Method 4; one
screenshot per claim — Method 9) and the consumer's CI functional-review
prompt (collateral damage — Method 8; a fail always carries a concrete
suggested fix; "could not verify" is never a fail). New here: the whole
pass gates the block passes and hands them its summary (how to reach each
touched screen, the login, what not to touch); block passes judge one hunk
each; the application and database are SHARED between concurrent passes
(Method 5), where the earlier reviewers each had a stack of their own.
- 2026-10-22 — the stack became the pass's own (Jorijn: "we can solve the
issue of the shared database by not making the database shared"): a pass
holds a stack — the one the config names, or one of the slots brought up
side by side — from its launch to its end, so Method 5 no longer guards
against passes running beside yours. It still guards against the passes
before and after yours on the same stack, which is not reset between them.
Before this, every pass of the gate shared one application, database and
login, and the first consumer's sign-out ends every session of the user
(`scope: 'global'`).