create-cmp-cli 0.23.0 → 0.25.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/bin/create-cmp.mjs +33 -3
- package/package.json +10 -4
- package/packages/harness/package.json +18 -8
- package/packages/harness/src/approve.mjs +46 -18
- package/packages/harness/src/console/console-data.mjs +117 -0
- package/packages/harness/src/console/console-evidence.mjs +122 -0
- package/packages/harness/src/console/console-overview.mjs +642 -0
- package/packages/harness/src/console/console-shell.mjs +1139 -0
- package/packages/harness/src/console/console-tabs.mjs +2828 -0
- package/packages/harness/src/console/contrast.mjs +74 -0
- package/packages/harness/src/console/preview-service.mjs +1384 -0
- package/packages/harness/src/framework-check.mjs +747 -0
- package/packages/harness/src/lib/a11y.mjs +1 -1
- package/packages/harness/src/lib/affected-tests.mjs +99 -44
- package/packages/harness/src/lib/agent-hold.mjs +234 -0
- package/packages/harness/src/lib/approvals.mjs +177 -350
- package/packages/harness/src/lib/audit-cadence.mjs +1 -1
- package/packages/harness/src/lib/comments.mjs +18 -4
- package/packages/harness/src/lib/determinism.mjs +275 -14
- package/packages/harness/src/lib/evidence-badge.mjs +38 -6
- package/packages/harness/src/lib/evidence-ladder.mjs +202 -0
- package/packages/harness/src/lib/evidence-level.mjs +144 -59
- package/packages/harness/src/lib/feature-brief.mjs +53 -16
- package/packages/harness/src/lib/flight-recorder.mjs +72 -8
- package/packages/harness/src/lib/framework-check.mjs +778 -0
- package/packages/harness/src/lib/harness-lock.mjs +87 -19
- package/packages/harness/src/lib/harness-manifest.mjs +208 -0
- package/packages/harness/src/lib/harness-region.mjs +106 -2
- package/packages/harness/src/lib/harness-source.mjs +142 -0
- package/packages/harness/src/lib/inputs-hash.mjs +180 -22
- package/packages/harness/src/lib/lane-markers.mjs +78 -0
- package/packages/harness/src/lib/lane-narrator.mjs +4 -2
- package/packages/harness/src/lib/lane-runner.mjs +72 -6
- package/packages/harness/src/lib/plan.mjs +44 -11
- package/packages/harness/src/lib/plant-calibration.mjs +135 -0
- package/packages/harness/src/lib/profile-loader.mjs +323 -0
- package/packages/harness/src/lib/profiles/cmp/affected.mjs +70 -0
- package/packages/harness/src/lib/profiles/cmp/android-checks.mjs +56 -0
- package/packages/harness/src/lib/profiles/cmp/artifacts.mjs +381 -0
- package/packages/harness/src/lib/{component-stories.mjs → profiles/cmp/component-stories.mjs} +5 -2
- package/packages/harness/src/lib/profiles/cmp/console-copy.mjs +29 -0
- package/packages/harness/src/lib/profiles/cmp/declarations.mjs +181 -0
- package/{template/qa/lib → packages/harness/src/lib/profiles/cmp}/e2e-coverage.mjs +13 -3
- package/packages/harness/src/lib/profiles/cmp/index.mjs +47 -0
- package/packages/harness/src/lib/profiles/cmp/ladder.mjs +67 -0
- package/packages/harness/src/lib/profiles/cmp/maestro.mjs +113 -0
- package/packages/harness/src/lib/profiles/cmp/plants.mjs +95 -0
- package/{template/qa/lib → packages/harness/src/lib/profiles/cmp}/reachability.mjs +1 -1
- package/packages/harness/src/lib/{steps-cmp.mjs → profiles/cmp/steps-cmp.mjs} +230 -28
- package/packages/harness/src/lib/receipt-validate.mjs +84 -16
- package/packages/harness/src/lib/spec-coverage.mjs +179 -92
- package/packages/harness/src/lib/spec-model.mjs +262 -0
- package/packages/harness/src/lib/step-cache.mjs +11 -2
- package/packages/harness/src/lib/step-outcomes.mjs +84 -151
- package/packages/harness/src/lib/walk.mjs +1 -1
- package/packages/harness/src/plan.mjs +30 -1
- package/packages/harness/src/preview-gallery.mjs +1 -1
- package/packages/harness/src/receipt-check.mjs +95 -26
- package/packages/harness/src/record-audit.mjs +1 -1
- package/packages/harness/src/scaffold-feature.mjs +10 -2
- package/packages/harness/src/verify.mjs +222 -142
- package/packages/harness/src/watch.mjs +146 -33
- package/packages/receipts/package.json +1 -1
- package/packages/receipts/src/index.mjs +2 -2
- package/packages/receipts/src/inputs-hash.mjs +180 -22
- package/packages/receipts/src/receipt-validate.mjs +84 -16
- package/src/commands/attach.mjs +128 -4
- package/src/commands/harden.mjs +7 -1
- package/src/commands/upgrade.mjs +7 -0
- package/src/lib/minimal.mjs +23 -1
- package/src/scaffold.mjs +8 -0
- package/template/.claude/settings.json +2 -2
- package/template/.claude/skills/add-feature/SKILL.md +7 -3
- package/template/.claude/skills/add-repository/SKILL.md +2 -2
- package/template/.claude/skills/add-screen/SKILL.md +8 -4
- package/template/.githooks/pre-push +9 -0
- package/template/CLAUDE.md +55 -186
- package/template/docs/TESTING.md +1 -1
- package/template/gitignore +4 -0
- package/template/qa/approve.mjs +46 -18
- package/template/qa/comments.json +1 -1
- package/template/qa/evidence/schema.json +258 -34
- package/template/qa/framework-check.mjs +747 -0
- package/template/qa/harness-manifest.json +10 -0
- package/template/qa/lib/a11y.mjs +1 -1
- package/template/qa/lib/affected-tests.mjs +99 -44
- package/template/qa/lib/agent-hold.mjs +234 -0
- package/template/qa/lib/approvals.mjs +177 -350
- package/template/qa/lib/audit-cadence.mjs +1 -1
- package/template/qa/lib/comments.mjs +18 -4
- package/template/qa/lib/determinism.mjs +275 -14
- package/template/qa/lib/evidence-badge.mjs +38 -6
- package/template/qa/lib/evidence-ladder.mjs +202 -0
- package/template/qa/lib/evidence-level.mjs +144 -59
- package/template/qa/lib/feature-brief.mjs +53 -16
- package/template/qa/lib/flight-recorder.mjs +72 -8
- package/template/qa/lib/framework-check.mjs +778 -0
- package/template/qa/lib/harness-lock.mjs +87 -19
- package/template/qa/lib/harness-manifest.mjs +208 -0
- package/template/qa/lib/harness-region.mjs +106 -2
- package/template/qa/lib/harness-source.mjs +142 -0
- package/template/qa/lib/inputs-hash.mjs +180 -22
- package/template/qa/lib/lane-markers.mjs +78 -0
- package/template/qa/lib/lane-narrator.mjs +4 -2
- package/template/qa/lib/lane-runner.mjs +72 -6
- package/template/qa/lib/plan.mjs +44 -11
- package/template/qa/lib/plant-calibration.mjs +135 -0
- package/template/qa/lib/profile-loader.mjs +323 -0
- package/template/qa/lib/profiles/cmp/affected.mjs +70 -0
- package/template/qa/lib/profiles/cmp/android-checks.mjs +56 -0
- package/template/qa/lib/profiles/cmp/artifacts.mjs +381 -0
- package/template/qa/lib/{component-stories.mjs → profiles/cmp/component-stories.mjs} +5 -2
- package/template/qa/lib/profiles/cmp/console-copy.mjs +29 -0
- package/template/qa/lib/profiles/cmp/declarations.mjs +181 -0
- package/{packages/harness/src/lib → template/qa/lib/profiles/cmp}/e2e-coverage.mjs +13 -3
- package/template/qa/lib/profiles/cmp/index.mjs +47 -0
- package/template/qa/lib/profiles/cmp/ladder.mjs +67 -0
- package/template/qa/lib/profiles/cmp/maestro.mjs +113 -0
- package/template/qa/lib/profiles/cmp/plants.mjs +95 -0
- package/{packages/harness/src/lib → template/qa/lib/profiles/cmp}/reachability.mjs +1 -1
- package/template/qa/lib/{steps-cmp.mjs → profiles/cmp/steps-cmp.mjs} +230 -28
- package/template/qa/lib/receipt-validate.mjs +84 -16
- package/template/qa/lib/spec-coverage.mjs +179 -92
- package/template/qa/lib/spec-model.mjs +262 -0
- package/template/qa/lib/step-cache.mjs +11 -2
- package/template/qa/lib/step-outcomes.mjs +84 -151
- package/template/qa/lib/walk.mjs +1 -1
- package/template/qa/plan.mjs +30 -1
- package/template/qa/preview-gallery.mjs +1 -1
- package/template/qa/receipt-check.mjs +95 -26
- package/template/qa/record-audit.mjs +1 -1
- package/template/qa/scaffold-feature.mjs +10 -2
- package/template/qa/verified-surface.json +17 -0
- package/template/qa/verify.mjs +222 -142
- package/template/qa/watch.mjs +146 -33
- /package/packages/harness/src/lib/{device-lease.mjs → profiles/cmp/device-lease.mjs} +0 -0
- /package/packages/harness/src/lib/{device-provider.mjs → profiles/cmp/device-provider.mjs} +0 -0
- /package/packages/harness/src/lib/{render.mjs → profiles/cmp/render.mjs} +0 -0
- /package/packages/harness/src/lib/{token-drift.mjs → profiles/cmp/token-drift.mjs} +0 -0
- /package/packages/harness/src/lib/{tree.mjs → profiles/cmp/tree.mjs} +0 -0
- /package/template/qa/lib/{device-lease.mjs → profiles/cmp/device-lease.mjs} +0 -0
- /package/template/qa/lib/{device-provider.mjs → profiles/cmp/device-provider.mjs} +0 -0
- /package/template/qa/lib/{render.mjs → profiles/cmp/render.mjs} +0 -0
- /package/template/qa/lib/{token-drift.mjs → profiles/cmp/token-drift.mjs} +0 -0
- /package/template/qa/lib/{tree.mjs → profiles/cmp/tree.mjs} +0 -0
package/template/CLAUDE.md
CHANGED
|
@@ -4,19 +4,20 @@
|
|
|
4
4
|
Generated by [create-cmp](https://github.com/kvdm-co-pilot/create-cmp) with a verification
|
|
5
5
|
harness. Every AI session in this repo works under this contract.
|
|
6
6
|
|
|
7
|
-
**Principles**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
principle wins and the rule is the bug.
|
|
7
|
+
**Principles** — derived, never claimed · prove the instrument before you read it · the layer you
|
|
8
|
+
changed cannot certify itself · proof costs what the change costs · never wait on nothing · a
|
|
9
|
+
signature binds content, a decision is closed · one record, read first. The full form with each
|
|
10
|
+
episode is create-cmp's `docs/PRINCIPLES.md` (upstream, not shipped); when a rule below and a
|
|
11
|
+
principle disagree, the principle wins and the rule is the bug.
|
|
13
12
|
|
|
14
13
|
## Definition of done
|
|
15
14
|
|
|
16
15
|
Done means `node qa/verify.mjs` reports PASS and the receipt it writes
|
|
17
|
-
(`qa/evidence/latest.json`)
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
(`qa/evidence/latest.json`) attests this tree — the hook checks the working tree by hash; commit
|
|
17
|
+
it with your change so CI and reviewers hold the same proof. Claiming completion without a PASS receipt is
|
|
18
|
+
a failure. SKIPped steps are recorded in the receipt, and a step that SKIPped for an environmental reason (no
|
|
19
|
+
device, no bootable AVD) makes the receipt unusable as done-evidence — `qa/receipt-check.mjs` refuses
|
|
20
|
+
it. Never present green-with-gaps as fully verified.
|
|
20
21
|
|
|
21
22
|
**Verify in two tiers — the full lane is a checkpoint, not an inner loop.** It builds,
|
|
22
23
|
tests, and gates the whole tree to produce the receipt, so it is slow by design; running it
|
|
@@ -29,15 +30,16 @@ once — when you believe the change is done.
|
|
|
29
30
|
did-I-break-anything signal is free the way an IDE's errors-on-save are free; and
|
|
30
31
|
`./gradlew :composeApp:desktopTest` for the unit tests your change touches. This is where
|
|
31
32
|
you catch your own mistakes.
|
|
32
|
-
- **Checkpoint — run once, at done:** `node qa/verify.mjs`. It writes the receipt
|
|
33
|
+
- **Checkpoint — run once, at done:** `node qa/verify.mjs`. It writes the receipt (a `--fast` run writes
|
|
34
|
+
`qa/evidence/latest-fast.json` instead, so the watcher can keep running); commit
|
|
33
35
|
the receipt with your change. The Stop hook (`qa/receipt-check.mjs`) then confirms — with a
|
|
34
|
-
cheap hash check, not another lane run — that a valid receipt attests
|
|
36
|
+
cheap hash check, not another lane run — that a valid receipt attests this tree, and CI
|
|
35
37
|
re-runs the full lane on push. After a green checkpoint, do not re-run the lane unless you
|
|
36
38
|
change the tree again.
|
|
37
39
|
|
|
38
40
|
Humans get the same gate at push time: run `node qa/setup-hooks.mjs` once (after `git init`)
|
|
39
|
-
to enable the shipped pre-push hook. It blocks a push whose
|
|
40
|
-
|
|
41
|
+
to enable the shipped pre-push hook. It blocks a push whose receipt doesn't attest
|
|
42
|
+
the tree — the same cheap check, before code leaves the machine (`git push --no-verify` bypasses
|
|
41
43
|
it; CI still enforces it).
|
|
42
44
|
|
|
43
45
|
## Specifications — behavior starts here
|
|
@@ -50,7 +52,7 @@ then implement. Durable tests cite their clause (`// SPEC: HOME-02`).
|
|
|
50
52
|
prove that test could ever *observe* the promise. Add `[tier: device]` (or `[tier: e2e]`)
|
|
51
53
|
after the id when the claim is about OS facts a host JVM cannot see — lifecycle, alarms,
|
|
52
54
|
notifications, permissions, real navigation. `specCoverage` then requires a citation from
|
|
53
|
-
`androidInstrumentedTest` or `qa/e2e` and FAILS without one, rather than accepting a
|
|
55
|
+
`androidInstrumentedTest` or `qa/e2e` for `device`, from `qa/e2e` alone for `e2e`, and FAILS without one, rather than accepting a
|
|
54
56
|
desktop test that is structurally blind to the claim.
|
|
55
57
|
[`specs/app-base.spec.md`](./specs/app-base.spec.md) states the architecture and shell
|
|
56
58
|
invariants the conformance gates enforce.
|
|
@@ -120,8 +122,9 @@ timezone), `DozeControl` (forced idle), `PermissionControl`, `ProcessControl`,
|
|
|
120
122
|
the exemplar proves an `allowWhileIdle` alarm delivers from inside forced deep idle by
|
|
121
123
|
nesting a clock warp in a Doze bracket. Exemplars: `PlatformBehaviorSeamTest`,
|
|
122
124
|
`RuntimeStateSeamTest`. Each organ's header states what it does NOT reproduce; read it
|
|
123
|
-
before claiming more than it proves. The lane's `androidChecks` step runs them
|
|
124
|
-
|
|
125
|
+
before claiming more than it proves. The lane's `androidChecks` step runs them — the lane provisions its own device: an attached one, or a
|
|
126
|
+
headless AVD it boots itself. `CMP_DEVICE=none` opts out, and that SKIP is environmental, so the receipt is
|
|
127
|
+
then not done-evidence. See `docs/TESTING.md`.
|
|
125
128
|
|
|
126
129
|
## Evidence
|
|
127
130
|
|
|
@@ -136,9 +139,12 @@ one verified, attributed state — so committing each receipt is what builds the
|
|
|
136
139
|
|
|
137
140
|
## The lane is not yours to edit
|
|
138
141
|
|
|
139
|
-
Every `.mjs` file directly under `qa/` and `qa/lib
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
Every `.mjs` file directly under `qa/` and `qa/lib/`, every `.mjs` under
|
|
143
|
+
`qa/lib/profiles/**` (this app's stack profile — its steps, tiers, ladder and device
|
|
144
|
+
glue), and the declarations the lane reads (`qa/harness-manifest.json`,
|
|
145
|
+
`qa/verified-surface.json`) are **machine-owned**: harness code that is byte-identical in
|
|
146
|
+
every create-cmp app and carries no app content at all. It
|
|
147
|
+
belongs to `prooflane-harness` (the name in your `qa/harness.lock.json`), versioned independently of the engine that stamped this
|
|
142
148
|
app's shape, and `qa/harness.lock.json` records a sha256 of every one of those files.
|
|
143
149
|
|
|
144
150
|
`node qa/verify.mjs` checks that lock first, on every run. Editing lane code fails the
|
|
@@ -147,7 +153,9 @@ honestly vouch for itself. Without that check the receipt was unfalsifiable in o
|
|
|
147
153
|
specific way: force every step to PASS in `qa/verify.mjs` and the receipt still validated,
|
|
148
154
|
since the edited file was simply part of the hashed input surface.
|
|
149
155
|
|
|
150
|
-
**So: do not edit `qa/*.mjs
|
|
156
|
+
**So: do not edit `qa/*.mjs`, `qa/lib/*.mjs`, `qa/lib/profiles/cmp/**`, `qa/verified-surface.json`, or
|
|
157
|
+
`qa/harness-manifest.json`.** (A profile you wrote yourself under `qa/lib/profiles/<id>/` is yours; after
|
|
158
|
+
editing it, `npx create-cmp-cli harness relock` re-takes the lock.) If the lane is wrong, the fix is
|
|
151
159
|
upstream in the engine, not here. If you genuinely must fork it, know that
|
|
152
160
|
`npx create-cmp-cli upgrade --harness` will replace the region and preserve your edits as
|
|
153
161
|
`qa/harness-local.patch` for you to re-apply or upstream — nothing is lost, but the fork
|
|
@@ -161,7 +169,7 @@ Upgrading the lane is safe to do unattended — it touches no app content and no
|
|
|
161
169
|
artifact:
|
|
162
170
|
|
|
163
171
|
```bash
|
|
164
|
-
npx create-cmp-cli upgrade --harness
|
|
172
|
+
npx create-cmp-cli upgrade --harness --yes # without --yes it dry-runs and exits 0 — a silent no-op from a tool
|
|
165
173
|
```
|
|
166
174
|
|
|
167
175
|
## Approvals — governed artifacts need a human's sign-off
|
|
@@ -170,43 +178,17 @@ Some artifacts are **governed**: a human approves them, and the approval is boun
|
|
|
170
178
|
artifact's content by hash (`qa/approvals.json`) — the evidence-receipt idea, applied to a
|
|
171
179
|
human decision. The ordered walk is a **definition order**, not just an approval order:
|
|
172
180
|
each artifact is the vocabulary the next is written in, so on a fresh app each step is a
|
|
173
|
-
conversation that ends in an approval — the genesis walk
|
|
174
|
-
|
|
175
|
-
The order
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
in the exact form you want published.
|
|
185
|
-
0.5. **First feature brief** — `docs/features/<exemplar>.md`, drafted from the interview's
|
|
186
|
-
"first screens" answer the moment intent is signed: the first feature's decisions and
|
|
187
|
-
their why, signed BEFORE its spec is written. Genesis runs the same decide → contract →
|
|
188
|
-
build loop as every later change; the app's first feature gets the same decide step
|
|
189
|
-
every later feature gets. (Express lane: skipped honestly, never fabricated;
|
|
190
|
-
placeholders earn a brief when they become real.)
|
|
191
|
-
1. **Architecture + structure** — `specs/app-base.spec.md` **and**
|
|
192
|
-
[`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md) (`cmp:generated` sections stripped
|
|
193
|
-
before hashing, so a mechanical regeneration never invalidates the approval — only an
|
|
194
|
-
authored-prose edit does).
|
|
195
|
-
2. **Exemplar spec** — `specs/<exemplar>.spec.md`. Confirmed BEFORE the slice is built:
|
|
196
|
-
propose the clauses, get the human's yes, then implement to satisfy them (the same
|
|
197
|
-
discipline `add-feature` already enforces post-genesis).
|
|
198
|
-
3. **Exemplar feature** — the **configured** exemplar's file set the `add-feature`
|
|
199
|
-
stamper clones from (see "Configurable exemplar"), built to the confirmed spec.
|
|
200
|
-
4. **Design system** — `presentation/theme/Theme.kt`, `presentation/theme/Tokens.kt`.
|
|
201
|
-
Locked on the REAL exemplar: candidates render on real screens, never stubs. If the
|
|
202
|
-
lock changes the exemplar's look, reopen → re-approve it — that loop is the design,
|
|
203
|
-
not a failure.
|
|
204
|
-
5. **Components** — every `presentation/components/*.kt` (a dynamic, sorted glob),
|
|
205
|
-
distilled from the screens per the inclusion rubric (`docs/ARCHITECTURE.md` §7). Once
|
|
206
|
-
approved, the registry is law: adding or changing a common component invalidates the
|
|
207
|
-
approval until a human re-approves.
|
|
208
|
-
6. **Per-feature spec** — `specs/<feature>.spec.md`, one governed artifact per feature,
|
|
209
|
-
added as features land.
|
|
181
|
+
conversation that ends in an approval — the genesis walk:
|
|
182
|
+
|
|
183
|
+
The genesis order — intent → first feature brief → architecture + structure → exemplar spec →
|
|
184
|
+
exemplar feature → design system → components → per-feature specs — is driven by the `cmp-new`
|
|
185
|
+
interview and is over before this file is your working contract; `node qa/approve.mjs --status`
|
|
186
|
+
prints every artifact and its state. Two disciplines survive it: **behavior is spec-first** (clauses
|
|
187
|
+
confirmed before the slice is built) and **visuals are UI-first** (the design system and component
|
|
188
|
+
vocabulary are distilled from real screens, so they lock after the exemplar — a provisional palette
|
|
189
|
+
carries the build until then; if the lock changes the exemplar's look, reopen → re-approve it — that
|
|
190
|
+
loop is the design, not a failure). Once approved, the component registry is law: adding or changing
|
|
191
|
+
a common component invalidates the approval until a human re-approves.
|
|
210
192
|
|
|
211
193
|
### After genesis — every change is the same loop
|
|
212
194
|
|
|
@@ -219,114 +201,11 @@ understood the change to be, which lane it takes, and why, before any tool runs.
|
|
|
219
201
|
can overrule the lane in a word; a silent route is a routing error even when the lane was
|
|
220
202
|
right.
|
|
221
203
|
|
|
222
|
-
**Grill before the brief** (the `grill-me` plugin skill; the rule holds without the plugin):
|
|
223
|
-
on the brief lane, after the triage restatement and before a word of the brief
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
decisions
|
|
227
|
-
recommended answer — and WAIT for the answers before anything else. Stop when no remaining
|
|
228
|
-
question would change the work; three rounds is the ceiling (more means the request needs
|
|
229
|
-
splitting). Answers land in the brief — settled calls become **Decisions** with their why,
|
|
230
|
-
the human's own calls the **Open decisions** section; the brief's signature closes them. The
|
|
231
|
-
direct lane is not grilled (one inline question at most, only when the restatement cannot
|
|
232
|
-
be made unambiguous); a bug fix or an emergency fix, never. While the grill is open, the
|
|
233
|
-
chain's first step reads `settle the open questions` (declare it before the first round;
|
|
234
|
-
re-declare when the answers reshape the steps).
|
|
235
|
-
|
|
236
|
-
**Brief lane** — when the change carries **decisions a future contributor could plausibly
|
|
237
|
-
"simplify" away** ("the day boundary is configurable, default 04:00 — not midnight") OR
|
|
238
|
-
**blast radius into other governed artifacts**. After naming the lane:
|
|
239
|
-
|
|
240
|
-
| Step | What |
|
|
241
|
-
|---|---|
|
|
242
|
-
| 1 | **Feature brief** — `docs/features/<name>.md`: the decisions with their why, research, rejected options, an **Open decisions** section until the human closes each. Signed BEFORE code. |
|
|
243
|
-
| 2 | **Design** — iff the feature has a UI surface (`"screens": true`, or screen files exist): draft the screens on STUB data, register them in the PreviewRegistry, render, and STOP. The human judges the rendered screens and signs `feature-design:<name>`. Never ask a human to approve a described UI. |
|
|
244
|
-
| 3 | **Contract** — reopen any signed spec the brief amends (`--reopen feature-spec:<surface> --reason "…"`); write the clauses where the behavior lives — about the form that now exists; the human signs |
|
|
245
|
-
| 4 | Build the slice (`add-feature` / preview loop). Declared blast lands "as declared"; re-approve touched visual artifacts on rendered output (wiring the signed screens from stub to real state drifts `feature-design:<name>` — its re-approval is that pass) |
|
|
246
|
-
| 5 | Prove — nothing to do: the lane's gates + receipt ARE the proof |
|
|
247
|
-
| 6 | The human's `--accept` — enabled only at provenDone AND a signed design |
|
|
248
|
-
|
|
249
|
-
**Direct lane** — everything else (bug fix, copy edit, tweak): confirm in chat, reopen →
|
|
250
|
-
amend clause → re-approve if a signed contract is touched, build, lane once at done.
|
|
251
|
-
Legacy features never get retro-briefs; spikes are ungoverned until they become real.
|
|
252
|
-
|
|
253
|
-
**A brief's LOCATION is the governance opt-in**: every `docs/features/*.md` is a governed
|
|
254
|
-
`feature-brief:<name>` artifact (hash-bound at signing; `<name>` pairs with
|
|
255
|
-
`specs/<name>.spec.md`). `docs/proposals/` stays ungoverned. The brief carries at most one
|
|
256
|
-
machine-read block, and it **declares — it never gates**:
|
|
257
|
-
|
|
258
|
-
```json cmp:feature
|
|
259
|
-
{ "touches": ["components", "design-system"], "screens": true }
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
`touches` is the declared blast radius — the artifact hashes already enforce; declaring
|
|
263
|
-
lets the console show "components re-approval, as planned" instead of an unexplained
|
|
264
|
-
failure, and surface **undeclared blast** when something drifted that no open brief
|
|
265
|
-
accounted for. `screens: true` declares a UI surface: it holds the design gate
|
|
266
|
-
(`feature-design:<name>` — the feature's own `presentation/<name>/*Screen.kt`, signed on
|
|
267
|
-
rendered output) before any screen file exists; once files exist, disk is ground truth
|
|
268
|
-
regardless. Both declare — neither gates.
|
|
269
|
-
|
|
270
|
-
**Doneness is DERIVED, never claimed.** There is no `--deliver` and no checks block —
|
|
271
|
-
deliberately (they existed and were removed as a weaker parallel truth). A feature is
|
|
272
|
-
`provenDone` when, mechanically: its spec has live clauses **and** every one is cited by a
|
|
273
|
-
test **and** the latest receipt is PASS **and** the receipt's `inputs.hash` attests the
|
|
274
|
-
tree as it stands. `--status` and the console print the same one-line `doneReason` either
|
|
275
|
-
way. What remains for humans is judgment: signing the brief (before code) and accepting
|
|
276
|
-
the feature (after proof — "the proven thing is what I wanted"); `--accept` is refused
|
|
277
|
-
until the derivation holds. Acceptance lives on the ledger row, never in the doc (the
|
|
278
|
-
signed bytes must not move when the human accepts).
|
|
279
|
-
|
|
280
|
-
| Command | What |
|
|
281
|
-
|---|---|
|
|
282
|
-
| `node qa/approve.mjs feature-brief:<name>` | the human signs the brief (before code) |
|
|
283
|
-
| `node qa/approve.mjs --accept <name>` | the human's bookend; refused until provenDone |
|
|
284
|
-
|
|
285
|
-
Editing a feature is the same brief **reopened** — `--reopen-feature <name> --reason "…"`
|
|
286
|
-
reopens the brief + its spec + its design + the set it declared, as ONE recorded change;
|
|
287
|
-
re-approval is one walk back. Every reopen **requires a `--reason`** (it walks back a
|
|
288
|
-
signature; the signer reads why from the ledger itself) and is journaled with `via` — the
|
|
289
|
-
append-only journal (`qa/approvals.log.jsonl`, printed by `--log`, shown in the console
|
|
290
|
-
strip's History) is how "what happened while I was away" stays answerable.
|
|
291
|
-
|
|
292
|
-
| Command | What |
|
|
293
|
-
|---|---|
|
|
294
|
-
| `node qa/approve.mjs --status` | Every governed artifact with live state (`unreviewed` / `approved` / `changed-since-approval` / `reopened`), short hash, mode badge |
|
|
295
|
-
| `node qa/approve.mjs <artifact>` | Record approval — hashes the artifact's files now, stamps the time; also clears a `defaults-accepted` mode |
|
|
296
|
-
| `node qa/approve.mjs --accept-defaults` | **Express lane**: approve every currently-resolvable artifact in one visible act, each stamped `"mode": "defaults-accepted"` — build now, walk the definition later. Unresolvable artifacts are skipped with the standard refusal printed. The ledger never pretends the defaults were designed. |
|
|
297
|
-
| `node qa/approve.mjs --reopen <artifact> --reason "…"` | Move an *approved* artifact (shaped or defaults-accepted) back to `reopened` for deliberate redesign. `--reason` is REQUIRED and recorded (`reopenedAt`, `via`, `reason` — on the row and in the journal). Refuses unknown ids and anything not currently approved. |
|
|
298
|
-
| `node qa/approve.mjs --reopen-feature <name> --reason "…"` | ONE recorded change: reopens the brief + `feature-spec:<name>` + `feature-design:<name>` + every declared `touches` artifact (each only if currently approved), all under one reason. |
|
|
299
|
-
| `node qa/approve.mjs --log` | The governance journal — every approve/reopen/accept with when, which surface (`via`), and why. |
|
|
300
|
-
|
|
301
|
-
With the create-cmp plugin, the same decisions can be made from the preview console
|
|
302
|
-
(`preview {projectDir}`'s URL) — it calls the same library, so the CLI and the console
|
|
303
|
-
never disagree. The console's rail is the human's work queue (colour = something waits
|
|
304
|
-
on them), and a drifted artifact shows, in its own section, exactly **what changed
|
|
305
|
-
against the signed bytes and which files are still exactly as signed**, with re-approval
|
|
306
|
-
in place. An agent blocks on a pending decision with
|
|
307
|
-
`approval_status {waitForDecision:true}`.
|
|
308
|
-
|
|
309
|
-
The verify lane's `approvals` gate (a step like any other, in every profile) resolves each
|
|
310
|
-
artifact's live status against `qa/approvals.json`:
|
|
311
|
-
|
|
312
|
-
- **`unreviewed`** → SKIP with a warning line. Nothing fails until a human opts in by
|
|
313
|
-
approving.
|
|
314
|
-
- **`reopened`** → SKIP with a warning line, exactly like `unreviewed`. Sanctioned redesign
|
|
315
|
-
never trips the gate; edits made while reopened are never drift. Re-approve when the
|
|
316
|
-
redesign lands.
|
|
317
|
-
- **`approved`, hash still matches** → PASS.
|
|
318
|
-
- **`approved`, hash no longer matches** → FAIL, naming the artifact and the re-approval
|
|
319
|
-
command. The artifact changed after sign-off — re-approve it or revert the change.
|
|
320
|
-
Invalidation is mechanical, like golden-tree drift, not a judgment call.
|
|
321
|
-
|
|
322
|
-
That asymmetry is the point: **redesign is a decision; drift is an accident** — the ledger
|
|
323
|
-
records which was which. A run with one reopened artifact and one drifted artifact FAILs
|
|
324
|
-
naming only the drifted one.
|
|
325
|
-
|
|
326
|
-
A gate FAIL fails the lane verdict, which fails `qa/receipt-check.mjs` (the Stop hook) by
|
|
327
|
-
the same mechanism as any other FAIL — no separate enforcement to maintain. `add-feature`
|
|
328
|
-
seeds each new feature's spec as `unreviewed` and prints the approval reminder; it never
|
|
329
|
-
refuses to stamp over this.
|
|
204
|
+
**Grill before the brief** (the `grill-me` plugin skill carries the procedure; the rule holds without the plugin):
|
|
205
|
+
on the brief lane, after the triage restatement and before a word of the brief, settle
|
|
206
|
+
the load-bearing questions — a signed brief or spec is a CLOSED decision: cite it, never re-ask it;
|
|
207
|
+
ask the unsettled ones as a numbered list with a recommended answer each, and WAIT. Answers land in the brief's **Decisions** and
|
|
208
|
+
**Open decisions**; the signature closes them. The direct lane is not grilled.
|
|
330
209
|
|
|
331
210
|
### Configurable exemplar — the DNA features are cloned from
|
|
332
211
|
|
|
@@ -381,19 +260,12 @@ which is worse than no chain. While the full check runs, the chain's observed li
|
|
|
381
260
|
narrates the lane's own position (step, elapsed, usual cost) — quote THAT, never an
|
|
382
261
|
estimate. The chain gates nothing; the walk stays the truth for doneness.
|
|
383
262
|
|
|
384
|
-
**
|
|
385
|
-
line
|
|
386
|
-
|
|
387
|
-
that
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
**While working — the header, then quiet:** open EVERY reply with the walk's one-line
|
|
391
|
-
header — the exact `[chat header]` line the per-prompt inject delivers. Paste it
|
|
392
|
-
verbatim, never compose it: it is the derivation's own string, so it cannot drift, and
|
|
393
|
-
it persists in the transcript, which the statusline beneath the input box never does.
|
|
394
|
-
After the header: one line per stage transition, nothing per-file. Stages carry their
|
|
395
|
-
plain-words gloss on first mention ("Contract — agreeing what it promises"); quote the
|
|
396
|
-
lane's cost only from the measured figure in the injected card, never an estimate.
|
|
263
|
+
**While a walk is open, the per-prompt inject carries the running protocol** — the `[studio: …]`
|
|
264
|
+
line (restore it if DOWN: the cmp-inspector `preview { projectDir }` tool, or tell the human once),
|
|
265
|
+
the `[chat header]` to open every reply with, verbatim, and the `▲ ARRIVED, UNPLANNED` line for work
|
|
266
|
+
that belongs to no open walk (default: after the current walk lands; one walk at a time). Render the
|
|
267
|
+
injected lines, never your memory of them; with no walk open it delivers nothing and you write none
|
|
268
|
+
of it. After the header: one line per stage transition, nothing per-file.
|
|
397
269
|
|
|
398
270
|
**At every human gate — loud:** a full stop card, never a bare question — and the
|
|
399
271
|
easiest act leads:
|
|
@@ -404,12 +276,6 @@ easiest act leads:
|
|
|
404
276
|
→ CLI fallback: <the command> (or "reply approve" when no console is up)
|
|
405
277
|
After this: <the remaining stages, and which ones stop for the human>
|
|
406
278
|
|
|
407
|
-
**Arrivals:** work that belongs to no open walk (undeclared drift, a harness
|
|
408
|
-
upgrade's rule-change reopens) is NEVER silently interleaved. Render
|
|
409
|
-
`▲ ARRIVED, UNPLANNED — <what> · now, or after <current walk> lands?` and default to
|
|
410
|
-
after. One walk at a time unless the human chooses otherwise — three unframed
|
|
411
|
-
interleaved walks is precisely the session this rule exists to prevent.
|
|
412
|
-
|
|
413
279
|
## Comments — review feedback flows back through the agent
|
|
414
280
|
|
|
415
281
|
Approvals are binding (they gate the verify lane); **comments are advisory** — a human's
|
|
@@ -523,14 +389,17 @@ conventions) · [`CONTRIBUTING.md`](./CONTRIBUTING.md) (workflow, Conventional C
|
|
|
523
389
|
| `node qa/setup-hooks.mjs` | Enable the pre-push receipt gate (one-time, after `git init`) |
|
|
524
390
|
| `./gradlew :composeApp:assembleDebug` | Android debug build |
|
|
525
391
|
| `./gradlew :composeApp:assembleRelease` | Android release build — R8 + `lintVital`, the variant the lane's `releaseBuild` step proves. Produces an **unsigned** APK; signing needs a keystore, which is yours to create and keep out of the repo. |
|
|
526
|
-
| `./gradlew :composeApp:hotRunDesktop --auto` | Desktop dev-client with hot reload |
|
|
527
392
|
| `./gradlew :composeApp:connectedDebugAndroidTest` | Instrumented behavior tests on the attached device (the lane's `androidChecks` step) |
|
|
393
|
+
| `node qa/framework-check.mjs` | GATE-RULES Rule 0: proves the lane returns both ways — PASS on the clean tree, FAIL by name on planted violations — in seconds, restoring every byte. Run it first in a repo whose harness is new or freshly upgraded. |
|
|
528
394
|
| `node qa/verify.mjs --profile smoke` | The smallest end-to-end lane: every pure-Node gate, no Gradle, no device — seconds. Proves the framework *returns*, never the change (its receipt is refused as done-evidence). Run it first in any repo whose harness is new or freshly upgraded |
|
|
529
395
|
| `node qa/verify.mjs --profile nightly` | Scheduled stage: everything `ci` proves with the determinism probe forced on. Proves the harness, never a change — its receipt (`stage: "nightly"`) is refused as done-evidence, exactly like `--fast`. Schedule it; never wait on it |
|
|
530
396
|
| `node qa/verify.mjs --profile release` | Ship-time lane: everything `ci` proves plus the audit-cadence report (`auditCadence` — which androidMain subsystems changed since their last recorded `cmp-audit`; a nudge, never a gate) and the release-APK Maestro smoke (`releaseSmoke`) |
|
|
531
397
|
| `node qa/verify.mjs --determinism` | Timezone determinism probe, alone: runs the JVM test tier twice under UTC-12 and UTC+14 and FAILs naming any test whose outcome differs — the dynamic net behind ARCH-13's static one. Opt-in inside a lane via `--profile ci --determinism`; never with `--fast`; writes no receipt on its own |
|
|
532
398
|
| `node qa/record-audit.mjs <subsystem>` | Record that a `cmp-audit` of an androidMain subsystem happened (appends subsystem + HEAD sha + timestamp to `qa/audits.jsonl`; refuses dirty/unknown targets). `--list` shows every derived subsystem and its audit status |
|
|
533
399
|
| `node qa/retrospective.mjs` | How this project actually uses its harness, from `qa/flight-recorder.jsonl` (appended by every lane run): fast vs full ratio, verbatim SKIP reasons grouped, whether the device tier is ever reached, longest stretch with no full lane. States only what the journal recorded |
|
|
400
|
+
<!-- >>> cmp:feature dev-client -->
|
|
401
|
+
| `./gradlew :composeApp:hotRunDesktop --auto` | Desktop dev-client with hot reload |
|
|
402
|
+
<!-- <<< cmp:feature dev-client -->
|
|
534
403
|
<!-- <<< cmp:feature harness -->
|
|
535
404
|
<!-- >>> cmp:feature !harness -->
|
|
536
405
|
# __APP_NAME__ — working guide
|
package/template/docs/TESTING.md
CHANGED
|
@@ -198,7 +198,7 @@ verbatim — the errors-on-save loop an IDE gives a human, for the agent. What i
|
|
|
198
198
|
evidence. It runs the fast tier only, so nothing it produces can satisfy the done-gate,
|
|
199
199
|
and every run says so in a standing footer; the checkpoint remains one deliberate full
|
|
200
200
|
`node qa/verify.mjs` run. Coordination: it never launches while a verify lane
|
|
201
|
-
(
|
|
201
|
+
(`qa/.lane-in-progress`) or a preview-daemon render (`composeApp/build/.cmp-render-in-progress`) holds the
|
|
202
202
|
project — it waits and coalesces, so watch mode, the preview daemon, and the lane can all
|
|
203
203
|
be resident on one project without two Gradle invocations ever colliding. `--once` runs a
|
|
204
204
|
single coordinated pass for scripting; `--json` emits one JSON object per run for
|
package/template/gitignore
CHANGED
|
@@ -16,6 +16,8 @@ build/
|
|
|
16
16
|
|
|
17
17
|
# Verify-lane binary evidence (screenshots, reports, page sources). The RECEIPT
|
|
18
18
|
# (qa/evidence/latest.json) IS committed — these artifacts are hashed into it instead.
|
|
19
|
+
# A --fast run writes latest-fast.json: inner-loop feedback, never evidence, never committed.
|
|
20
|
+
qa/evidence/latest-fast.json
|
|
19
21
|
qa-artifacts/
|
|
20
22
|
|
|
21
23
|
# iOS / CocoaPods / XcodeGen
|
|
@@ -44,6 +46,8 @@ qa/.plan.json
|
|
|
44
46
|
# The closed-chain trail (drive-narration N5): local because it carries raw
|
|
45
47
|
# human prompts — the committed journal for lane runs stays qa/flight-recorder.jsonl.
|
|
46
48
|
qa/.plan-history.jsonl
|
|
49
|
+
qa/.agent-hold.json
|
|
50
|
+
qa/.lane-in-progress
|
|
47
51
|
|
|
48
52
|
# Android signing. The keystore IS the app's identity on Android — it cannot be reissued —
|
|
49
53
|
# and keystore.properties holds its passwords. Never committed, never in a sidecar diff.
|
package/template/qa/approve.mjs
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// The approvals CLI — thin shell over qa/lib/approvals.mjs.
|
|
3
3
|
//
|
|
4
|
-
// node qa/approve.mjs <artifact>
|
|
5
|
-
//
|
|
4
|
+
// node qa/approve.mjs <artifact> [<artifact> …] --as "Name <email>"
|
|
5
|
+
// records approval (recomputes each artifact's
|
|
6
|
+
// hash now, stamps the time, writes qa/approvals.json).
|
|
7
|
+
// Sign every pending artifact in ONE command: each
|
|
8
|
+
// signature moves the receipt's input hash, so N
|
|
9
|
+
// separate signings cost N lane re-runs.
|
|
6
10
|
// node qa/approve.mjs --status lists every governed artifact + live state
|
|
7
11
|
// (unreviewed / approved / changed-since-approval /
|
|
8
12
|
// reopened, + mode when set) + short hash
|
|
@@ -44,7 +48,7 @@ import {
|
|
|
44
48
|
approveArtifact,
|
|
45
49
|
getApprovalStatuses,
|
|
46
50
|
getFeatureBoard,
|
|
47
|
-
|
|
51
|
+
isProjectGovernable,
|
|
48
52
|
listGovernedArtifacts,
|
|
49
53
|
readJournal,
|
|
50
54
|
reopenArtifact,
|
|
@@ -118,17 +122,16 @@ if (args.includes("--status")) {
|
|
|
118
122
|
process.exit(0);
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
// Write guard: refuse to RECORD approvals in a tree
|
|
122
|
-
//
|
|
123
|
-
//
|
|
125
|
+
// Write guard: refuse to RECORD approvals in a tree that cannot be governed —
|
|
126
|
+
// no manifest, no loadable profile, or a tree the profile itself refuses (the
|
|
127
|
+
// raw template, whose package is still a placeholder). Approvals belong to a
|
|
128
|
+
// real project; writing qa/approvals.json into the template pollutes the
|
|
124
129
|
// template itself. Read-only --status (above) stays available anywhere. Applies
|
|
125
130
|
// to every write operation below (single approve, express lane, reopen).
|
|
126
131
|
function refuseIfUnresolvable() {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"this looks like the raw template or a pre-stamp tree. Approvals are recorded in a generated project; refusing to write qa/approvals.json here.",
|
|
131
|
-
);
|
|
132
|
+
const v = isProjectGovernable(ROOT);
|
|
133
|
+
if (v.ok) return;
|
|
134
|
+
console.error(`error: ${v.reason}`);
|
|
132
135
|
process.exit(1);
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -243,7 +246,7 @@ if (acceptFlagIdx !== -1) {
|
|
|
243
246
|
if (args.length === 0) {
|
|
244
247
|
const ids = listGovernedArtifacts(ROOT).map((a) => a.id);
|
|
245
248
|
console.error(
|
|
246
|
-
'usage: node qa/approve.mjs <artifact> | --status | --log | --accept-defaults | --reopen <artifact> --reason "…" | --reopen-feature <name> --reason "…" | --accept <name>\n' +
|
|
249
|
+
'usage: node qa/approve.mjs <artifact> --as "Name <email>" | --status | --log | --accept-defaults | --reopen <artifact> --reason "…" | --reopen-feature <name> --reason "…" | --accept <name>\n' +
|
|
247
250
|
` valid artifacts: ${ids.length > 0 ? ids.join(", ") : "(none resolved in this project)"}`,
|
|
248
251
|
);
|
|
249
252
|
process.exit(1);
|
|
@@ -251,7 +254,27 @@ if (args.length === 0) {
|
|
|
251
254
|
|
|
252
255
|
refuseIfUnresolvable();
|
|
253
256
|
|
|
254
|
-
|
|
257
|
+
// EVERY artifact named in one command — one write, one invalidation.
|
|
258
|
+
//
|
|
259
|
+
// A signature changes qa/approvals.json's `status`, which the approvals gate
|
|
260
|
+
// reads, so it legitimately moves the receipt's input hash. Signed one at a
|
|
261
|
+
// time AFTER a green lane, that means N signatures invalidate the receipt N
|
|
262
|
+
// times and cost N lane re-runs and N bookkeeping commits. Measured in
|
|
263
|
+
// payment-blueprint's log on 2026-09-04: of 21 commits in one session, 8 were
|
|
264
|
+
// "sign the approval" / "bind the receipt" with no product content, and every
|
|
265
|
+
// code change cost two to three commits. It compounds as a repo accumulates
|
|
266
|
+
// governed artifacts, which is what makes a session start fast and grind later.
|
|
267
|
+
//
|
|
268
|
+
// Two halves to the fix: sign them together (here), and sign BEFORE the final
|
|
269
|
+
// lane run so the receipt you keep is already the one that covers the
|
|
270
|
+
// signatures. `--reopen-feature` established the idiom — one recorded change,
|
|
271
|
+
// not N commands.
|
|
272
|
+
const artifactIds = args.filter((a, i) => !a.startsWith("--") && (i === 0 || !args[i - 1].startsWith("--")));
|
|
273
|
+
// An unrecognised flag must still be REFUSED by name, never silently skipped:
|
|
274
|
+
// filtering flags out is how `node qa/approve.mjs --delivr meal` would quietly
|
|
275
|
+
// approve nothing and exit 0. Falling back to the first argument reproduces the
|
|
276
|
+
// exact refusal an unknown verb has always produced.
|
|
277
|
+
if (artifactIds.length === 0) artifactIds.push(args[0]);
|
|
255
278
|
// The signer is REQUIRED, not optional: see approveArtifact's refusal. Parsed
|
|
256
279
|
// here rather than defaulted from git config on purpose — `git config user.name`
|
|
257
280
|
// is whatever the machine says, and an agent running on a developer's laptop
|
|
@@ -267,9 +290,14 @@ if (!approvedBy || approvedBy.startsWith("--")) {
|
|
|
267
290
|
);
|
|
268
291
|
process.exit(1);
|
|
269
292
|
}
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
293
|
+
const results = artifactIds.map((id) => ({ id, result: approveArtifact(ROOT, id, { via: "cli", approvedBy }) }));
|
|
294
|
+
const failed = results.filter((r) => !r.result.ok);
|
|
295
|
+
for (const { id, result } of results) {
|
|
296
|
+
if (result.ok) console.log(`✓ approved ${result.artifact} — hash ${shortHash(result.hash)}, at ${result.approvedAt}`);
|
|
297
|
+
else console.error(`error: ${id}: ${result.reason}`);
|
|
298
|
+
}
|
|
299
|
+
if (results.length > 1) {
|
|
300
|
+
const signed = results.length - failed.length;
|
|
301
|
+
console.log(`\n${signed} signature${signed === 1 ? "" : "s"} in one write. Run the lane AFTER signing, not before — a signature moves the receipt's input hash, so signing after a green run costs you that run.`);
|
|
274
302
|
}
|
|
275
|
-
|
|
303
|
+
if (failed.length) process.exit(1);
|