@mafianacho/nacholens 0.0.0-stage → 0.3.0
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/LICENSE +202 -0
- package/NOTICE +13 -0
- package/README.de.md +46 -0
- package/README.fr.md +46 -0
- package/README.ja.md +46 -0
- package/README.ko.md +46 -0
- package/README.md +45 -2
- package/README.zh.md +46 -0
- package/demo/shop/v1/README.md +7 -0
- package/demo/shop/v1/api.js +78 -0
- package/demo/shop/v1/db.js +38 -0
- package/demo/shop/v1/gateway.js +50 -0
- package/demo/shop/v1/mock-external.js +17 -0
- package/demo/shop/v1/public/app.js +32 -0
- package/demo/shop/v1/public/format.js +2 -0
- package/demo/shop/v1/public/index.html +13 -0
- package/demo/shop/v1/public/styles.css +4 -0
- package/demo/shop/v2/README.md +7 -0
- package/demo/shop/v2/api.js +88 -0
- package/demo/shop/v2/db.js +38 -0
- package/demo/shop/v2/gateway.js +50 -0
- package/demo/shop/v2/mock-external.js +17 -0
- package/demo/shop/v2/public/app.js +33 -0
- package/demo/shop/v2/public/format.js +3 -0
- package/demo/shop/v2/public/index.html +13 -0
- package/demo/shop/v2/public/styles.css +5 -0
- package/docs/guide.ko.md +97 -0
- package/docs/guide.md +142 -0
- package/docs/model-schema.md +137 -0
- package/docs/review.md +78 -0
- package/examples/demo-shop.artifact.html +1535 -0
- package/examples/demo-shop.artifact.json +7 -0
- package/examples/demo-shop.html +1535 -0
- package/examples/external/README.md +12 -0
- package/examples/external/full-stack-fastapi-template/LICENSE +21 -0
- package/examples/external/full-stack-fastapi-template/changes.json +56 -0
- package/examples/external/full-stack-fastapi-template/full-stack-fastapi-template-backend.html +1535 -0
- package/models/demo-shop/changes.auto.json +121 -0
- package/models/demo-shop/changes.example.json +16 -0
- package/models/demo-shop/intent.example.json +75 -0
- package/models/demo-shop/model.json +3151 -0
- package/package.json +60 -4
- package/skills/nacholens/SKILL.md +235 -0
- package/src/cli.js +377 -0
- package/src/demo.js +42 -0
- package/src/diff.js +254 -0
- package/src/gitio.js +67 -0
- package/src/install.js +94 -0
- package/src/model.js +434 -0
- package/src/page.js +87 -0
- package/src/player/fonts/FONTLOG.md +30 -0
- package/src/player/fonts/OFL-Anton.txt +93 -0
- package/src/player/fonts/OFL-IBMPlex.txt +94 -0
- package/src/player/fonts/anton-400.woff2 +0 -0
- package/src/player/fonts/nacho-mono-500.woff2 +0 -0
- package/src/player/fonts/nacho-mono-600.woff2 +0 -0
- package/src/player/fonts/nacho-sans-400.woff2 +0 -0
- package/src/player/fonts/nacho-sans-500.woff2 +0 -0
- package/src/player/fonts/nacho-sans-600.woff2 +0 -0
- package/src/player/fonts/nacho-sans-700.woff2 +0 -0
- package/src/player/player.css +318 -0
- package/src/player/player.js +872 -0
- package/src/player/review.css +49 -0
- package/src/player/review.js +230 -0
- package/src/player/template.html +58 -0
- package/src/review.js +321 -0
- package/src/scan.js +255 -0
- package/src/template.js +36 -0
- package/src/verify.js +90 -0
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mafianacho/nacholens",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "See what an AI changed in a backend without reading code: use-case flow animation with a change overlay derived from git diff. Zero dependencies.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"nacholens": "./src/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=20"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"skills",
|
|
16
|
+
"demo",
|
|
17
|
+
"models/demo-shop/model.json",
|
|
18
|
+
"models/demo-shop/changes.example.json",
|
|
19
|
+
"models/demo-shop/changes.auto.json",
|
|
20
|
+
"models/demo-shop/intent.example.json",
|
|
21
|
+
"examples",
|
|
22
|
+
"docs/guide.md",
|
|
23
|
+
"docs/guide.ko.md",
|
|
24
|
+
"docs/model-schema.md",
|
|
25
|
+
"docs/review.md",
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"NOTICE",
|
|
28
|
+
"README.md",
|
|
29
|
+
"README.ja.md",
|
|
30
|
+
"README.de.md",
|
|
31
|
+
"README.fr.md",
|
|
32
|
+
"README.zh.md",
|
|
33
|
+
"README.ko.md"
|
|
34
|
+
],
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/mafianacho/nacholens.git"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://github.com/mafianacho/nacholens#readme",
|
|
40
|
+
"bugs": {
|
|
41
|
+
"url": "https://github.com/mafianacho/nacholens/issues"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"architecture",
|
|
45
|
+
"visualization",
|
|
46
|
+
"code-review",
|
|
47
|
+
"ai",
|
|
48
|
+
"agent-skill",
|
|
49
|
+
"git-diff",
|
|
50
|
+
"backend"
|
|
51
|
+
],
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"scripts": {
|
|
56
|
+
"test": "node --test test/*.test.js",
|
|
57
|
+
"validate": "node src/cli.js validate models/demo-shop/model.json --changes models/demo-shop/changes.auto.json",
|
|
58
|
+
"demo": "node src/cli.js demo --out nacholens-demo",
|
|
59
|
+
"build:examples": "node src/cli.js build models/demo-shop/model.json --changes models/demo-shop/changes.auto.json --intent models/demo-shop/intent.example.json --format both --out examples",
|
|
60
|
+
"prepublishOnly": "node -e \"if(process.env.NACHOLENS_ALLOW_PUBLISH!=='1'){console.error('Publishing is blocked: set NACHOLENS_ALLOW_PUBLISH=1 only after the owner approved the release.');process.exit(1)}\" && npm test"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nacholens
|
|
3
|
+
description: Use when a user wants to SEE what an AI changed in a backend/infra repo without reading code. Builds a use-case flow animation (Claude -> artifact, others -> single HTML) with the changed parts highlighted from a git diff.
|
|
4
|
+
version: 1.3.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# nacholens — show what an AI changed in a backend, without reading code
|
|
8
|
+
|
|
9
|
+
Use this skill when someone says "show me what changed", "I can't read this diff", "draw how this service works".
|
|
10
|
+
The result is **one page**: pick a use case, and a request moves through client → gateway → services → database / third parties → back, step by step.
|
|
11
|
+
Whatever the AI changed in this work is highlighted in colour.
|
|
12
|
+
|
|
13
|
+
You (the agent) write a small **model** of the repo (components, use cases, steps, each backed by `file:lines` evidence). The CLI checks your evidence against the real source, compares a git diff with it, and renders the page. Nothing in the target repo is executed or modified.
|
|
14
|
+
|
|
15
|
+
## 0. Before you start
|
|
16
|
+
|
|
17
|
+
- Needs **Node.js ≥ 20** and **git**. No packages to install.
|
|
18
|
+
- `SV` below means the CLI. Find it in this order: `./cli/src/cli.js` next to this file (installed copy) → `$NACHOLENS_HOME/src/cli.js` → the `src/cli.js` of the nacholens repository. Run it as `node <path>/cli.js <command>`. Without any copy, `npx -y @mafianacho/nacholens@latest <command>` runs the published package (and `… install --to <dir>` puts this file and a CLI copy into `<dir>/nacholens/`).
|
|
19
|
+
- **Try it in 30 seconds**: `SV demo` builds the bundled demo shop (gateway → API → SQLite + 3 third parties, with a real change) into `./nacholens-demo/`.
|
|
20
|
+
- The **target repo is read-only**. Keep the model file outside it (`--model`) unless the user agrees to the default `<repo>/.nacholens/model.json`.
|
|
21
|
+
- Output format: **if you are Claude pass `--agent claude`** (artifact: paste it into the conversation as an HTML artifact); any other agent omits it and gets a single offline HTML file.
|
|
22
|
+
- Quote refs that contain `^` or `~` (`--base 'abc123^'`); `report.json` lists changed steps as `useCase/step` ids: map them to titles with your model when you write the explanation.
|
|
23
|
+
- Command forms: `SV validate [model.json]` and `SV verify --repo R [--model M]` both take the model as a positional argument or `--model`.
|
|
24
|
+
- CLI messages are Korean (validation hints, `TODO` template). File paths, JSON paths and `file:line` are language-neutral; follow them literally. The page's own words are English by default; add `--lang ko` to `build`/`demo` for Korean when the user writes in Korean. The model text (labels, narration) is yours: write it in the user's language.
|
|
25
|
+
|
|
26
|
+
## 1. Shortest path — a model already exists
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
SV build --repo <target repo> --base <ref before the change> [--head <ref>] [--model <model.json>] [--out <dir>] [--agent claude]
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- `--base` is the point before the change (branch, tag or SHA; for a PR, the target branch). `--head` defaults to `HEAD`.
|
|
33
|
+
- It validates the model, verifies every evidence range against `model.source.commit`, turns `git diff base..head` into the **change overlay**, and writes the page.
|
|
34
|
+
- Output in `--out` (default `./nacholens-out`): `<product>.html` or `<product>.artifact.html` (+ `.json`), `changes.json`, `report.json`.
|
|
35
|
+
|
|
36
|
+
## 2. No model yet (new repo)
|
|
37
|
+
|
|
38
|
+
`build` can be your first command: with no model it writes an empty template, lists every blank as a JSON path, and exits with code 1. Then:
|
|
39
|
+
|
|
40
|
+
1. `SV init --repo <repo> [--model <file>]` — template (every blank starts with `TODO`).
|
|
41
|
+
2. `SV scan --repo <repo>` — **leads**, not facts: stack, routes, gateways, data drivers, outbound hosts, env-injected addresses, HTTP calls. Use `--limit N` on big repos.
|
|
42
|
+
3. **Read the code** (start from the leads) and fill the model — rules in §3.
|
|
43
|
+
4. `SV validate --model <file>` until it exits 0 (structure, remaining `TODO`).
|
|
44
|
+
5. `SV verify --repo <repo> --model <file>` until it exits 0 (every evidence range exists in the source at `model.source.commit`).
|
|
45
|
+
6. Run `build` (§1). Read the result to the user (§4).
|
|
46
|
+
|
|
47
|
+
Budget: a first model for a small service is 15–40 steps. Do not try to cover everything; two good use cases beat ten shallow ones.
|
|
48
|
+
|
|
49
|
+
## 3. Writing the model — the part that decides quality
|
|
50
|
+
|
|
51
|
+
Hard rule: **no guessing.** Every component, link and step needs `evidence` you actually read. If you could not confirm something, leave it out and tell the user.
|
|
52
|
+
|
|
53
|
+
### 3.0 The smallest complete model (copy the shape)
|
|
54
|
+
|
|
55
|
+
No gateway, client outside the repo, one use case with every step kind. `SV init` writes a bigger template; this is the shape to aim for.
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"schemaVersion": 1,
|
|
60
|
+
"product": { "id": "notes-api", "name": "Notes API", "description": "Small REST API that stores notes in SQLite." },
|
|
61
|
+
"source": { "repo": "owner/notes-api", "commit": "<git rev-parse HEAD>" },
|
|
62
|
+
"components": [
|
|
63
|
+
{ "id": "client", "kind": "client", "label": "API client\nbrowser or app", "detail": "Whoever calls the API; it lives outside this repository.",
|
|
64
|
+
"evidence": [{ "file": "main.go", "lines": [20, 20], "contains": "mux.HandleFunc(\"POST /api/notes\"" }] },
|
|
65
|
+
{ "id": "api", "kind": "service", "label": "Notes API\nGo net/http", "detail": "Handles /api/notes.",
|
|
66
|
+
"evidence": [{ "file": "main.go", "lines": [30, 34], "contains": "http.Server{" }] },
|
|
67
|
+
{ "id": "db", "kind": "datastore", "label": "SQLite\nnotes.db", "detail": "Stores notes.",
|
|
68
|
+
"evidence": [{ "file": "storage/sqlite.go", "lines": [14, 16], "contains": "sql.Open(" }] }
|
|
69
|
+
],
|
|
70
|
+
"links": [
|
|
71
|
+
{ "a": "client", "b": "api", "label": "HTTP /api/notes", "evidence": [{ "file": "main.go", "lines": [20, 20], "contains": "/api/notes" }] },
|
|
72
|
+
{ "a": "api", "b": "db", "label": "SQL", "evidence": [{ "file": "storage/sqlite.go", "lines": [14, 16], "contains": "sql.Open(" }] }
|
|
73
|
+
],
|
|
74
|
+
"useCases": [{
|
|
75
|
+
"id": "add-note", "title": "Add a note", "summary": "A client stores a new note.", "trigger": "POST /api/notes",
|
|
76
|
+
"steps": [
|
|
77
|
+
{ "id": "req", "kind": "request", "from": "client", "to": "api", "label": "Send the note", "narration": "The client sends the note text.",
|
|
78
|
+
"request": "POST /api/notes {text}", "evidence": [{ "file": "main.go", "lines": [20, 20], "contains": "POST /api/notes" }] },
|
|
79
|
+
{ "id": "validate", "kind": "internal", "from": "api", "to": "api", "label": "Reject empty text", "narration": "An empty note is refused.",
|
|
80
|
+
"evidence": [{ "file": "handlers/note.go", "lines": [31, 34], "contains": "text == \"\"" }] },
|
|
81
|
+
{ "id": "insert", "kind": "call", "from": "api", "to": "db", "label": "Save the note", "narration": "The note is written to the database.",
|
|
82
|
+
"request": "INSERT note", "response": "new id", "evidence": [{ "file": "storage/sqlite.go", "lines": [42, 44], "contains": "INSERT INTO notes" }] },
|
|
83
|
+
{ "id": "done", "kind": "response", "from": "api", "to": "client", "label": "Return the id", "narration": "The client gets the id of the new note.",
|
|
84
|
+
"response": "201 {id}", "evidence": [{ "file": "handlers/note.go", "lines": [40, 41], "contains": "StatusCreated" }] }
|
|
85
|
+
]
|
|
86
|
+
}]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Rules this example shows: `internal` has `from == to` and no `via`; `call` is `from` caller `to` datastore/third party and needs both `request` and `response` text; `request`/`response` steps need their own one-line summary; a gateway is simply absent (no component, no links to it, no `via`); a client that is not in the repo cites **the route it calls** (that line is "where the connection is made").
|
|
91
|
+
|
|
92
|
+
**If a gateway really exists** (nginx, Traefik, Caddy, Next rewrites, a Worker): add it as `kind: gateway`, link client↔gateway↔service, and put `"via": ["gateway"]` on the steps that cross it. Removing a template gateway means deleting the component, both links and every `via`.
|
|
93
|
+
|
|
94
|
+
### 3.1 Pick use cases
|
|
95
|
+
|
|
96
|
+
- 2–5 **things a user or caller does**: sign in, list X, create Y, run a report, receive a webhook. `trigger` is the entry request (`GET /api/orders`).
|
|
97
|
+
- Cover variety: one read, one write, one that needs login/session, one that calls a third party. Skip startup, health checks and cron unless the user asked.
|
|
98
|
+
- Order them by how often a typical change touches them.
|
|
99
|
+
|
|
100
|
+
### 3.2 Components (4–9 in total)
|
|
101
|
+
|
|
102
|
+
- One component per **runtime or system**, never per file or class: browser/app, gateway/proxy, each service, each datastore, each third party.
|
|
103
|
+
- `kind`: `client` · `gateway` (reverse proxy, edge, ingress, API gateway, Next rewrites) · `service` · `datastore` (DB, cache, queue, object store) · `external` (third party you do not run).
|
|
104
|
+
- **A gateway is optional.** If requests go straight from client to service, the step is `from: client, to: service` with no `via`. Do not invent a gateway; do not hide a real one (scan's "gateway leads" show nginx/Traefik/Caddy/Ingress/rewrites/forwarding fetches).
|
|
105
|
+
- External services are often addressed through env vars (`PAYMENTS_URL`). Name the service by what it is after you read the call and its config, not by the variable.
|
|
106
|
+
- `lane`/`row` (0–9) are layout hints only; leave them out unless two nodes overlap.
|
|
107
|
+
|
|
108
|
+
### 3.3 Steps (5–10 per use case)
|
|
109
|
+
|
|
110
|
+
- One step per **hop or decision a reviewer might care about**: the request, an auth/session check, each validation or permission gate, each database access, each third-party call, building the response, the response.
|
|
111
|
+
- `request` (one way in), `response` (one way out), `call` (round trip to a datastore or third party; needs `request` and `response` summaries), `internal` (a decision inside one component).
|
|
112
|
+
- Merge trivia (logging, small mapping) into the neighbouring step unless that is what changed. Split things that can fail differently (auth vs validation vs permission).
|
|
113
|
+
- One step per external call. Never one step per function.
|
|
114
|
+
- `via` lists the gateway(s) between `from` and `to`; every hop needs a declared `link`.
|
|
115
|
+
- `narration`: one or two plain sentences, written for someone who does not read code. `request`/`response`: one line, never real secrets, tokens or personal data.
|
|
116
|
+
|
|
117
|
+
### 3.4 Choosing evidence (this is what the diff is matched against)
|
|
118
|
+
|
|
119
|
+
Evidence is `{file, lines:[start,end], contains}`. After a change, the CLI marks a step as changed when the diff overlaps its evidence, so evidence must be **tight and honest**:
|
|
120
|
+
|
|
121
|
+
- Cite the **smallest range that proves the claim** — the route registration, the call site, the SQL statement, the config line. 1–3 ranges per item, usually ≤ 15 lines each.
|
|
122
|
+
- Always give `contains`: a distinctive token on a line inside the range (not `async`, not `return`). `verify` fails if the token is not there, which is how it notices lines that drifted; without `contains` it only checks that the range exists (and warns).
|
|
123
|
+
- Prefer the code future changes will touch (handlers, queries, calls, config), not imports or type declarations.
|
|
124
|
+
- **Do not cite a whole route function or file** for a step: every edit nearby would light the step up. If the step is "ask the shipping tracker", cite the tracker call, not the 50-line handler.
|
|
125
|
+
- A step can be reported as **`added`** only when *all* its evidence is brand-new code. Keep evidence to code that implements that one step (not a shared helper or an unchanged neighbour).
|
|
126
|
+
- Do not cite the same lines for two steps unless they genuinely share them; both would light up.
|
|
127
|
+
- Component evidence = where it is started/configured/initialised. Link evidence = where the connection is made (the URL, the driver connect, the proxy rule).
|
|
128
|
+
- `model.source.commit` is the commit whose files you read. **Read the tree of `--head` for the range you will explain** (`git show <head>:path | cat -n`) or, if the checkout is at a different commit, say so in the model (`source.commit` = what you read). Line numbers are translated through the diff automatically, but only between the commit you read and `--head`, so the nearer the better.
|
|
129
|
+
- **Cite the statement, not the whole function.** When the change is one line inside a larger statement (a `select: {...}` list, a multi-line call), cite that statement (≤ ~12 lines) with `contains` = its first line.
|
|
130
|
+
- **Reformatting and moves.** A commit that re-indents or moves code lights up every step it touches even if behaviour is unchanged. Say so in your explanation: "these steps are marked because their code was moved/reformatted; behaviour looks the same" (only claim "looks the same" if you read both sides).
|
|
131
|
+
- **Shared helpers** (token creation, auth guard): cite the helper in the step that is about it. If several use cases genuinely share it, citing it in each is fine, and a later change will correctly light all of them.
|
|
132
|
+
|
|
133
|
+
### 3.4.1 Optional drill-down detail (parts, wires, tables, endpoints, payload, transforms)
|
|
134
|
+
|
|
135
|
+
Add this only after the flow model passes `verify`, and only what you read in code. A node without detail stays a plain node; that is fine. Full field list: `docs/model-schema.md` → "Optional drill-down detail".
|
|
136
|
+
|
|
137
|
+
- **Parts** of a service = what a reviewer would name: each route/socket handler, the auth extractor or middleware, each service, each adapter (DB pool, HTTP client). 6–16 parts, not one per function. Cite the declaration line (`pub struct OrderService {`, `app.get("/x"`, `impl Storage for PgStore`). Set `connects` on adapters to the datastore/external they reach.
|
|
138
|
+
- **Wires** = a call you saw from one part to another. Cite the call site (`self.orders.list(`), not the callee. No wire without a call site.
|
|
139
|
+
- **`trace`** on a step = the parts this step passes, in order. Consecutive parts need a wire; if the validator says one is missing, either you skipped a part or the wire does not exist — read again, do not add a wire to silence it.
|
|
140
|
+
- **Tables** from migrations/schema (`CREATE TABLE`, later `ALTER TABLE … ADD COLUMN`, renames). List the columns a reviewer needs (keys, the columns your steps touch), not every column. Cite the `CREATE TABLE` range plus each `ALTER` you rely on.
|
|
141
|
+
- **`data`** on a step = the actual query: `op` from the SQL verb (`INSERT … ON CONFLICT DO UPDATE` = `upsert`), `columns` from the statement, evidence = the statement lines in the repo code. An ORM call counts if you read which columns it writes.
|
|
142
|
+
- **Endpoints** of a third party: method + path from the call site, request/response key fields from the body you saw built and the struct it is parsed into. `auth` is a name only (`"Bearer API key (env TRACKER_API_KEY)"`); never a value — the validator rejects key/token-like strings.
|
|
143
|
+
- If you cannot find the call site for a wire or the statement for a column, leave it out and say so.
|
|
144
|
+
- **`payload`** on a moving step = the concrete data the user sees when clicking the dot: the HTTP line and JSON body you saw built (`json!({...})`, `JSON.stringify({...})`, the request struct), the SQL text with its bind parameters, or the row shape from the table definition. Values are placeholders (`<uuid>`, `<text>`, `<int>`), never real data or keys. Copy field names from the code; write `…` for what you did not read.
|
|
145
|
+
- **`transforms`** on a step = where the data changes shape on its `trace`: input type → part → output type, with a short field map (`text → masked (redact)`, `ttlMs → expires_at (Date.now() + ttlMs)`). Cite the conversion lines (the struct literal, the `.bind(` list, the mapping call). One transform per part that actually converts; a part that only passes data on needs none.
|
|
146
|
+
|
|
147
|
+
### 3.5 Good and bad (from the bundled demo shop)
|
|
148
|
+
|
|
149
|
+
| Item | Bad | Good |
|
|
150
|
+
|---|---|---|
|
|
151
|
+
| Step "Ask the tracker for each status" | evidence `api.js:36-85` (whole route) | `api.js` lines of `async function trackShipping`, with `contains: "async function trackShipping"` |
|
|
152
|
+
| `contains` | `"async"` | `"DELETE FROM oauth_state WHERE state = ? RETURNING state"` |
|
|
153
|
+
| Components | `api.js`, `db.js`, `helpers.js` | `Order API`, `SQLite`, `Shipping tracker` |
|
|
154
|
+
| Login use case | 31 steps, one per helper function | 10 steps: start, make state, save state, redirect, consent, callback, consume state, exchange code, save session, done |
|
|
155
|
+
| Step label | `Call trackShipping()` | `Ask the tracker for each status` |
|
|
156
|
+
| Narration | `Invokes fetch with encodeURIComponent on o.tracking and maps the Promise.all` | `Orders that have a tracking code are looked up in parallel. A failed lookup becomes "unknown" instead of failing the list.` |
|
|
157
|
+
| Unknown behaviour | "probably retries on 500" | omit; tell the user it was not verified |
|
|
158
|
+
|
|
159
|
+
### 3.6 Reading a repo fast
|
|
160
|
+
|
|
161
|
+
- Start from `scan` leads: routes → pick use cases; data/clients/env addresses → components and `call` steps; gateways/configs → the gateway and how requests reach the service.
|
|
162
|
+
- Node: routers (`app.get`, Hono, Fastify, Nest decorators, Next `pages/api` and `app/**/route.ts`), middleware order for auth/CSRF. Python: FastAPI/Flask decorators, dependencies (auth), ORM sessions, `httpx`/`requests` calls, Django `urls.py`. Go: `HandleFunc("GET /x")`, chi/gin routers, middleware chain, `database/sql` calls, `http.Get`/`Do`.
|
|
163
|
+
- Rust: `scan` has no framework rules for it yet and may list nothing. Read it with `grep -rn '\.route(\|\.nest(' crates/` (axum: `routes/mod.rs` is the route table; `extract.rs` holds auth extractors; `ws/` holds sockets), `sqlx::query` for SQL, `reqwest` / `self.post(` for outbound calls, and the service layer (`services/*.rs`) for the steps in between. In hexagonal layouts the real step lives in `application/services`; the adapter files are the datastore/external side.
|
|
164
|
+
- Pure domain functions that implement a step (a ranking, a classifier, a state transition) belong to that step: cite the one signature line (`pub fn rank_hits(`) next to the service call, so a change to the algorithm lights the step.
|
|
165
|
+
- Inline unit-test files (`tests.rs`, `tests_*.rs`, `*_test.rs`) are not recognised as tests; they will show under "not in the model". Say so in one line to the user; do not cite them.
|
|
166
|
+
- Generated files (OpenAPI json, lock-like dumps, codegen output) will also show as unmapped. Leave them; mention them once.
|
|
167
|
+
- A merge commit is a range too: `git log -1 --format=%P <merge>` prints its parents; use `--base <first parent> --head <merge>` to explain the whole branch that was merged. You may read the model from a later commit than `--head` (`source.commit`); the CLI translates lines and says so in `changes.json`.
|
|
168
|
+
- Monorepo: model one product at a time (one `model.json` each).
|
|
169
|
+
- For compose/k8s/nginx/Traefik repos, the gateway and ports are in those files, not in app code.
|
|
170
|
+
|
|
171
|
+
## 4. Reading the result to the user
|
|
172
|
+
|
|
173
|
+
Use plain words, not file names first. Tell three things from `build` output and `report.json`:
|
|
174
|
+
|
|
175
|
+
1. **What changed** — `changedSteps` / `changedComponents` as use-case names and step titles (e.g. "In *List my orders*, the new step *Ask the tracker for each status*").
|
|
176
|
+
2. **Changes the model does not cover** — `unmapped`, with file names. Meaning: a new path exists, or the model is out of date. Offer to add evidence and rebuild.
|
|
177
|
+
3. **How to open it** — the output path (Claude: paste the artifact). Pick a flow on the left and press Play; click a box to watch the same step inside it; the dock under the stage lists what is left to check (planner / QA / developer). The top-right switch shows/hides the change highlight.
|
|
178
|
+
|
|
179
|
+
If you could not open the page in a browser, say so; do not claim it "looks right".
|
|
180
|
+
|
|
181
|
+
### 4.0 Commits that are not features (dependency bumps, CI, formatting)
|
|
182
|
+
|
|
183
|
+
If the diff is mostly lock files, `pyproject.toml`, `package.json`, `go.mod`, CI workflows: the CLI ignores lock files, lists manifests and CI files as **config** (soft grey line, not an error) and the flow usually barely changes. Do not invent a step to give the change a home. Report it as it is: "this is a dependency/tooling change; N flow steps are touched, the rest is maintenance". Cite a dependency line as component evidence only if the claim is real ("the API is built on FastAPI") and you want future version changes to light that component.
|
|
184
|
+
|
|
185
|
+
Mixed dev/prod infrastructure (a Mailpit container only in `compose.override.yml`): model what runs in production, mention the dev-only container in the `detail` text, and cite the production config.
|
|
186
|
+
|
|
187
|
+
### 4.1 Triage `unmapped` — never try to zero it in one go
|
|
188
|
+
|
|
189
|
+
`unmapped` is "changes the model did not see", not an error list.
|
|
190
|
+
|
|
191
|
+
| Kind | Examples | What to do |
|
|
192
|
+
|---|---|---|
|
|
193
|
+
| Belongs to a request flow | routes, handlers, queries, config, third-party calls, auth | Add the lines to the right step's `evidence` (a new behaviour = a new step), then `verify` → `build` |
|
|
194
|
+
| Not part of any flow | page markup, formatting helpers, type declarations, migrations | Leave it; tell the user in one line that it is UI/helper code and does not appear in the flow |
|
|
195
|
+
| Stylesheets, dependency manifests, CI files | `.css`, `pyproject.toml`, `requirements*.txt`, `go.mod`, `package.json`, `.github/workflows/*` | Reported separately (`category: "style"` / `"config"`), soft line only, ignored by `--strict` |
|
|
196
|
+
|
|
197
|
+
A first model on a new repo will show warnings. Handle new server-side files first; the rest only if the user wants. **Never attach evidence to a step just to silence a warning.**
|
|
198
|
+
|
|
199
|
+
## 5. Exit codes (for CI later; this skill wires up no CI)
|
|
200
|
+
|
|
201
|
+
| Code | Meaning | Action |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| 0 | Success | — |
|
|
204
|
+
| 1 | Model/evidence problem (template unfilled, structure error, evidence differs from the source) | Fix the JSON path / `file:line` in the message |
|
|
205
|
+
| 2 | Usage, I/O or git error (unknown ref, wrong path) | Check arguments |
|
|
206
|
+
| 3 | `--strict` found changed **code** the model does not cover (style-only files do not count; output is still written) | Add evidence or accept |
|
|
207
|
+
| 4 | `--strict-intent`: at least one planner expectation is unmet (output is still written) | Fix the code, or tell the planner the expectation is not met |
|
|
208
|
+
|
|
209
|
+
In CI, upload `--out` as an artifact and read `report.json` (`ok`, `unmapped`).
|
|
210
|
+
|
|
211
|
+
## 6. Quality bar before you report
|
|
212
|
+
|
|
213
|
+
- `validate` and `verify` exit 0; `build` exit 0 (or 3 only if the user asked for `--strict`).
|
|
214
|
+
- 2–4 use cases, each 3–10 steps (3 is fine for a tiny service; do not pad), every step with evidence you read; no item cites more than ~3 ranges.
|
|
215
|
+
- No step is a bare restatement of a function name; narration reads fine to a non-developer.
|
|
216
|
+
- You did not touch the target repo (check `git status` there).
|
|
217
|
+
- You can name what you did **not** model.
|
|
218
|
+
- If you added drill-down detail: every part, wire, table, column list and endpoint field came from code you opened, and `verify` covers them (its count includes them).
|
|
219
|
+
|
|
220
|
+
## 7. Common mistakes
|
|
221
|
+
|
|
222
|
+
- Typing line numbers from memory → `verify` catches it; open the file and count.
|
|
223
|
+
- Skipping the gateway in a step without a direct `link` → validation fails (declare the link or route `via` the gateway).
|
|
224
|
+
- Expecting a database or third-party change to mark that component as changed → by design only client/gateway/service code in this repo marks components; datastores/externals show only when their own evidence changes.
|
|
225
|
+
- Tests, docs and lock files appear under `ignored` with a reason; they are not silently dropped.
|
|
226
|
+
- Citing a huge range to "be safe" → every nearby edit becomes a false "changed step". Narrow it.
|
|
227
|
+
|
|
228
|
+
## 8. Review dock: before/after, intent check, regression map
|
|
229
|
+
|
|
230
|
+
Full reference: `docs/review.md` in the repository. Use these when the user asks "what was it before?", "is this what we asked for?" or "what else can this break?".
|
|
231
|
+
|
|
232
|
+
- **Before/after** is on by default whenever there is a change overlay. For a real flow comparison, write (or reuse) a second model against the **base** commit and pass `--before-model`; without it a modified step shows only its changed evidence, never a guessed old text. Do not invent a before-model: read the base tree.
|
|
233
|
+
- **Intent check**: turn the planner's words into `intent.json` (`expectations[]`: `id`, `text` in the planner's own words, `useCase`, optional `check`). Give an expectation a `check` only when a model step can prove it (`step` / `absent` / `order`, matchers `kind|from|to|with|table|op|text`). If you cannot name the step that would prove it, **leave `check` out**: it is reported `unknown` and a human checks it. Never write a check just to get a green result. Run `intent` (or `build --intent`), then tell the user: met / unmet / unknown counts, which met expectations were touched by this work, and the *uncovered* changes (possible scope drift; the user decides).
|
|
234
|
+
- **Regression map** needs `--repo --base`. Report the use cases to re-check and why (shared table / endpoint / part / wire), and the ones with `none-found` tests. `none-found` means "no test file mentions the route", not "untested".
|
|
235
|
+
- Keep `verify` green first: all three views read the model, so a wrong model gives a wrong verdict.
|