create-cmp-cli 0.14.1 → 0.16.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/README.md +54 -353
- package/bin/create-cmp.mjs +19 -3
- package/llms.txt +3 -3
- package/options.schema.json +4 -0
- package/package.json +2 -2
- package/packages/harness/package.json +9 -1
- package/packages/harness/src/lib/harness-lock.mjs +2 -2
- package/packages/harness/src/lib/inputs-hash.mjs +1 -1
- package/packages/harness/src/lib/receipt-validate.mjs +1 -1
- package/packages/harness/src/receipt-check.mjs +1 -1
- package/packages/receipts/package.json +11 -3
- package/packages/receipts/src/index.mjs +1 -1
- package/packages/receipts/src/inputs-hash.mjs +1 -1
- package/packages/receipts/src/receipt-validate.mjs +1 -1
- package/src/commands/attach.mjs +250 -0
- package/src/commands/create.mjs +13 -2
- package/src/commands/harden.mjs +263 -0
- package/src/lib/adr-seed.mjs +27 -0
- package/src/lib/harness-upgrade.mjs +4 -0
- package/src/lib/hooks.mjs +140 -0
- package/src/lib/minimal.mjs +130 -0
- package/src/lib/toggle.mjs +4 -1
- package/src/lib/verify.mjs +6 -1
- package/src/scaffold.mjs +18 -1
- package/template/.github/workflows/verify.yml +15 -0
- package/template/.gradle/8.11.1/checksums/checksums.lock +0 -0
- package/template/.gradle/8.11.1/fileChanges/last-build.bin +0 -0
- package/template/.gradle/8.11.1/fileHashes/fileHashes.lock +0 -0
- package/template/.gradle/8.11.1/gc.properties +0 -0
- package/template/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/template/.gradle/buildOutputCleanup/cache.properties +2 -0
- package/template/.gradle/vcs-1/gc.properties +0 -0
- package/template/AGENTS.md +57 -8
- package/template/CLAUDE.md +49 -0
- package/template/CONTRIBUTING.md +13 -0
- package/template/README.md +33 -0
- package/template/docs/ARCHITECTURE.md +18 -1
- package/template/docs/TESTING.md +10 -0
- package/template/manifest.json +13 -0
- package/template/qa/lib/harness-lock.mjs +2 -2
- package/template/qa/lib/inputs-hash.mjs +1 -1
- package/template/qa/lib/receipt-validate.mjs +1 -1
- package/template/qa/receipt-check.mjs +1 -1
package/template/README.md
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# __APP_NAME__
|
|
2
2
|
|
|
3
|
+
<!-- >>> cmp:feature harness -->
|
|
3
4
|
<!-- cmp:generated evidence -->
|
|
4
5
|
[](https://github.com/kvdm-co-pilot/create-cmp) — no verify receipt yet. Run `node qa/verify.mjs`.
|
|
5
6
|
<!-- /cmp:generated -->
|
|
@@ -9,6 +10,14 @@ A Kotlin / Compose Multiplatform app, generated by
|
|
|
9
10
|
the architecture, testing conventions, and definition of done are enforced mechanically, not
|
|
10
11
|
by convention. Start with [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md); AI collaborators
|
|
11
12
|
follow the contract in [`CLAUDE.md`](./CLAUDE.md).
|
|
13
|
+
<!-- <<< cmp:feature harness -->
|
|
14
|
+
<!-- >>> cmp:feature !harness -->
|
|
15
|
+
A Kotlin / Compose Multiplatform app, generated by
|
|
16
|
+
[create-cmp](https://github.com/kvdm-co-pilot/create-cmp) as a **minimal scaffold**: the
|
|
17
|
+
full app architecture with its tests, without the verification harness (one command adds
|
|
18
|
+
it — see "Full mode" below). Start with [`docs/ARCHITECTURE.md`](./docs/ARCHITECTURE.md);
|
|
19
|
+
AI collaborators follow the guide in [`CLAUDE.md`](./CLAUDE.md).
|
|
20
|
+
<!-- <<< cmp:feature !harness -->
|
|
12
21
|
|
|
13
22
|
## Quick start
|
|
14
23
|
|
|
@@ -29,8 +38,10 @@ follow the contract in [`CLAUDE.md`](./CLAUDE.md).
|
|
|
29
38
|
# Unit tests (fast inner loop)
|
|
30
39
|
./gradlew :composeApp:desktopTest
|
|
31
40
|
|
|
41
|
+
<!-- >>> cmp:feature harness -->
|
|
32
42
|
# The verify lane — build + tests + every shipped gate, with an evidence receipt
|
|
33
43
|
node qa/verify.mjs
|
|
44
|
+
<!-- <<< cmp:feature harness -->
|
|
34
45
|
```
|
|
35
46
|
|
|
36
47
|
<!-- >>> cmp:feature inspector -->
|
|
@@ -55,7 +66,9 @@ before shipping — from Claude Code, the `cmp-firebase-connect` skill drives it
|
|
|
55
66
|
## Project structure
|
|
56
67
|
|
|
57
68
|
```
|
|
69
|
+
<!-- >>> cmp:feature harness -->
|
|
58
70
|
specs/ behavior specifications — new behavior starts here
|
|
71
|
+
<!-- <<< cmp:feature harness -->
|
|
59
72
|
composeApp/src/commonMain/ shared UI + logic (presentation / domain / data / di)
|
|
60
73
|
composeApp/src/commonTest/ unit tests — exemplar pattern in presentation/home
|
|
61
74
|
composeApp/src/desktopTest/ conformance gates + Compose UI tests + golden trees (JVM tier)
|
|
@@ -66,8 +79,10 @@ composeApp/src/iosMain/ iOS actuals · iosApp/ is the Xcode shell
|
|
|
66
79
|
<!-- >>> cmp:feature dev-client -->
|
|
67
80
|
composeApp/src/desktopMain/ desktop dev-client (see docs/dev-client.md)
|
|
68
81
|
<!-- <<< cmp:feature dev-client -->
|
|
82
|
+
<!-- >>> cmp:feature harness -->
|
|
69
83
|
qa/verify.mjs the verify lane — this repo's definition of done
|
|
70
84
|
qa/evidence/latest.json the committed evidence receipt (see CONTRIBUTING.md)
|
|
85
|
+
<!-- <<< cmp:feature harness -->
|
|
71
86
|
<!-- >>> cmp:feature e2e -->
|
|
72
87
|
qa/e2e/ Maestro E2E flows (device smoke)
|
|
73
88
|
<!-- <<< cmp:feature e2e -->
|
|
@@ -82,9 +97,26 @@ docs/ architecture, testing, ADRs
|
|
|
82
97
|
| [`docs/TESTING.md`](./docs/TESTING.md) | The test pyramid, conventions, how to run everything |
|
|
83
98
|
| [`docs/adr/`](./docs/adr/) | Architecture decision records |
|
|
84
99
|
| [`CONTRIBUTING.md`](./CONTRIBUTING.md) | Workflow, definition of done, commit style |
|
|
100
|
+
<!-- >>> cmp:feature harness -->
|
|
85
101
|
| [`CLAUDE.md`](./CLAUDE.md) | The AI delivery contract |
|
|
102
|
+
<!-- <<< cmp:feature harness -->
|
|
103
|
+
<!-- >>> cmp:feature !harness -->
|
|
104
|
+
| [`CLAUDE.md`](./CLAUDE.md) | The AI working guide |
|
|
105
|
+
<!-- <<< cmp:feature !harness -->
|
|
86
106
|
| [`CHANGELOG.md`](./CHANGELOG.md) | Notable changes (Keep a Changelog) |
|
|
87
107
|
|
|
108
|
+
<!-- >>> cmp:feature !harness -->
|
|
109
|
+
## Full mode
|
|
110
|
+
|
|
111
|
+
This scaffold is deliberately light: no verify lane, evidence receipts, behavior specs,
|
|
112
|
+
approval gates, or generators. The full harness — a machine-checked definition of done,
|
|
113
|
+
with an audit trail — installs in place, idempotently, with one command:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx create-cmp-cli harden
|
|
117
|
+
```
|
|
118
|
+
<!-- <<< cmp:feature !harness -->
|
|
119
|
+
<!-- >>> cmp:feature harness -->
|
|
88
120
|
## Verification
|
|
89
121
|
|
|
90
122
|
Every change must pass the verify lane (`node qa/verify.mjs`) and commit its updated receipt
|
|
@@ -115,6 +147,7 @@ in [`.claude/settings.json`](./.claude/settings.json) — nothing else depends o
|
|
|
115
147
|
CI independently enforces the same "receipt attests HEAD" check on every push
|
|
116
148
|
(`.github/workflows/verify.yml`), so disabling the local hook only trades an immediate
|
|
117
149
|
signal for a later one.
|
|
150
|
+
<!-- <<< cmp:feature harness -->
|
|
118
151
|
|
|
119
152
|
---
|
|
120
153
|
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
+
<!-- >>> cmp:feature harness -->
|
|
3
4
|
> **Reading this document.** Every normative sentence below carries a tier tag.
|
|
4
5
|
> `[enforced: CLAUSE-ID]`: a named gate in `node qa/verify.mjs` fails the lane on violation.
|
|
5
6
|
> `[governed]`: the sentence lives inside a hash-bound human approval (`qa/approvals.json`);
|
|
6
7
|
> changing it without re-approval fails the `approvals` gate. `[advisory]`: a documented
|
|
7
8
|
> convention with no mechanical check yet. Every sentence is law, signed intent, or advice —
|
|
8
9
|
> and says which.
|
|
10
|
+
<!-- <<< cmp:feature harness -->
|
|
11
|
+
<!-- >>> cmp:feature !harness -->
|
|
12
|
+
> **Reading this document.** Every normative sentence below carries a tier tag.
|
|
13
|
+
> `[enforced: CLAUSE-ID]`: a named source-scanning gate in the desktopTest conformance
|
|
14
|
+
> suite fails the tests on violation. `[governed]` / `[advisory]`: conventions this
|
|
15
|
+
> minimal scaffold checks by review, not by machine — the full harness
|
|
16
|
+
> (`npx create-cmp-cli harden`) adds the mechanical gates.
|
|
17
|
+
<!-- <<< cmp:feature !harness -->
|
|
9
18
|
|
|
10
19
|
## 1. Purpose & quality goals
|
|
11
20
|
|
|
21
|
+
<!-- >>> cmp:feature harness -->
|
|
12
22
|
This app's purpose, audience, and shape are recorded in [`specs/intent.md`](../specs/intent.md)
|
|
13
23
|
— the root brief this document, the component registry, and the exemplar feature all trace
|
|
14
|
-
back to.
|
|
24
|
+
back to.
|
|
25
|
+
<!-- <<< cmp:feature harness -->
|
|
26
|
+
The table below is the default quality-goal set a fresh scaffold ships with. The
|
|
15
27
|
genesis walk's architecture conversation is where a human promotes, demotes, or replaces
|
|
16
28
|
them for this app's actual priorities ("offline matters more than a11y for a field-work
|
|
17
29
|
app").
|
|
@@ -423,6 +435,11 @@ demotes to a regular feature. To add a feature, mirror the exemplar exactly:
|
|
|
423
435
|
empty/content split) (+ test using a fake from `testing/fakes/`).
|
|
424
436
|
4. DI: register in `di/AppModule.kt`.
|
|
425
437
|
5. Navigation: add the route in `presentation/navigation/`.
|
|
438
|
+
<!-- >>> cmp:feature harness -->
|
|
426
439
|
6. Run `node qa/verify.mjs` — done means PASS + committed receipt.
|
|
440
|
+
<!-- <<< cmp:feature harness -->
|
|
441
|
+
<!-- >>> cmp:feature !harness -->
|
|
442
|
+
6. Run `./gradlew :composeApp:desktopTest` — green is the bar this scaffold can check.
|
|
443
|
+
<!-- <<< cmp:feature !harness -->
|
|
427
444
|
|
|
428
445
|
Significant decisions get an ADR in [`docs/adr/`](./adr/) — see the template there.
|
package/template/docs/TESTING.md
CHANGED
|
@@ -13,12 +13,20 @@ copy their shape.
|
|
|
13
13
|
<!-- >>> cmp:feature e2e -->
|
|
14
14
|
| E2E smoke (few) | `qa/e2e/*.yaml` (Maestro) | `maestro test qa/e2e/smoke.yaml` |
|
|
15
15
|
<!-- <<< cmp:feature e2e -->
|
|
16
|
+
<!-- >>> cmp:feature harness -->
|
|
16
17
|
| The lane (all of it) | `qa/verify.mjs` | `node qa/verify.mjs` |
|
|
17
18
|
|
|
18
19
|
Every durable test cites the spec clause it verifies (`// SPEC: HOME-02` — see
|
|
19
20
|
[`specs/`](../specs/README.md)); **new behavior begins as a spec clause.** The lane's
|
|
20
21
|
`specCoverage` step enforces this: it fails on orphan clauses (no citing test) and orphan tags
|
|
21
22
|
(no matching clause, or one citing a withdrawn clause).
|
|
23
|
+
<!-- <<< cmp:feature harness -->
|
|
24
|
+
<!-- >>> cmp:feature !harness -->
|
|
25
|
+
|
|
26
|
+
Durable tests may cite a spec clause id in a comment (`// SPEC: HOME-02`) — the shipped
|
|
27
|
+
tests do. This minimal scaffold carries no `specs/` directory or coverage gate; both
|
|
28
|
+
arrive with `npx create-cmp-cli harden`.
|
|
29
|
+
<!-- <<< cmp:feature !harness -->
|
|
22
30
|
|
|
23
31
|
## Unit conventions
|
|
24
32
|
|
|
@@ -140,6 +148,7 @@ one: a search assert that passed standalone failed in-lane behind a 33s type gap
|
|
|
140
148
|
asserts are for static post-navigation elements only.
|
|
141
149
|
<!-- <<< cmp:feature e2e -->
|
|
142
150
|
|
|
151
|
+
<!-- >>> cmp:feature harness -->
|
|
143
152
|
## The verify lane
|
|
144
153
|
|
|
145
154
|
`node qa/verify.mjs` is the definition of done: spec coverage → build → unit tests →
|
|
@@ -235,3 +244,4 @@ rung is the coarse grade; the per-step list stays the fine print. A FAILed lane
|
|
|
235
244
|
| **L1 desktop** | Full static + JVM evidence: build, unit tests, conformance, golden trees, a11y, release COMPILE, and the pure-Node gates. | That the app runs on a device at all — no APK was installed or driven; platform behavior (alarms, notifications) is invisible from this rung. |
|
|
236
245
|
| **L2 device** | L1 plus executed on-device evidence: the debug APK installed and driven (`e2eSmoke`), instrumented platform assertions (`androidChecks`), and/or live token drift. | That the release variant behaves (R8 differs from debug — that is L3's job), nor that alarms/notifications actually land unless an instrumented behavior test asserts them. |
|
|
237
246
|
| **L3 release** | L2 plus `releaseSmoke` PASSed: the signed release APK installed and driven on a device. | Real-backend behavior (the emulator/dev backend is a documented tier boundary — see the instrumented-tier section) and store-review compliance. |
|
|
247
|
+
<!-- <<< cmp:feature harness -->
|
package/template/manifest.json
CHANGED
|
@@ -57,6 +57,18 @@
|
|
|
57
57
|
"whenDisabled": "Remove the marker lines AND the body between them."
|
|
58
58
|
},
|
|
59
59
|
"features": {
|
|
60
|
+
"harness": {
|
|
61
|
+
"enabledByDefault": true,
|
|
62
|
+
"paths": [
|
|
63
|
+
"specs",
|
|
64
|
+
".claude/skills",
|
|
65
|
+
".githooks",
|
|
66
|
+
"qa/approvals.json",
|
|
67
|
+
"qa/comments.json",
|
|
68
|
+
"qa/evidence"
|
|
69
|
+
],
|
|
70
|
+
"notes": "The verification harness — the mode split (`--minimal` disables it; `create-cmp harden` installs it back). Three mechanisms share the subtraction, each already owned by the engine: (1) the paths above delete the app-owned governance surfaces (specs/, approvals+comments ledgers, evidence, generator skills, the pre-push receipt hook); (2) `cmp:feature harness`/`!harness` marker blocks give CLAUDE.md, AGENTS.md, README.md, CONTRIBUTING.md, docs/TESTING.md and .github/workflows/verify.yml their two honest renderings from one file; (3) the machine-owned lane region is subtracted by the ENGINE (src/lib/minimal.mjs), which keeps qa/preview-gallery.mjs plus its transitive qa/lib imports — derived from import statements, never listed here, so it cannot rot. .claude/settings.json is rewritten to the advisory-only hook set via the src/lib/hooks.mjs classifier (Stop hook and qa/-referencing nudges are full-mode only). qa/golden/ stays in BOTH modes — the desktopTest golden-tree tests read it and minimal keeps the full JVM test tier. qa/e2e is its own feature and stays orthogonal (Maestro flows run standalone)."
|
|
71
|
+
},
|
|
60
72
|
"ios": {
|
|
61
73
|
"enabledByDefault": true,
|
|
62
74
|
"paths": [
|
|
@@ -113,6 +125,7 @@
|
|
|
113
125
|
},
|
|
114
126
|
"verify": {
|
|
115
127
|
"android": "node qa/verify.mjs --profile scaffold",
|
|
128
|
+
"androidMinimal": "./gradlew :composeApp:desktopTest :composeApp:assembleDebug",
|
|
116
129
|
"androidBuildOnly": "./gradlew :composeApp:assembleDebug",
|
|
117
130
|
"androidSmoke": "./gradlew :composeApp:installDebug && maestro test qa/e2e/smoke.yaml",
|
|
118
131
|
"iosLink": "./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// Answered LOCALLY, offline, on every lane run. Needs nothing
|
|
9
9
|
// but the tree and this file.
|
|
10
10
|
//
|
|
11
|
-
// AUTHENTICITY "is my lane the real published create-cmp
|
|
11
|
+
// AUTHENTICITY "is my lane the real published @create-cmp/harness@X?"
|
|
12
12
|
// Answered REMOTELY, on request, by comparing this file's
|
|
13
13
|
// `sha256` against the published version's — `create-cmp
|
|
14
14
|
// upgrade --harness` does it, and so can any third party
|
|
@@ -61,7 +61,7 @@ export function readHarnessLock(root) {
|
|
|
61
61
|
* @param {{name?: string, version: string}} harness identity to record
|
|
62
62
|
* @returns {{sha256: string, fileCount: number}}
|
|
63
63
|
*/
|
|
64
|
-
export function writeHarnessLock(root, { name = "create-cmp
|
|
64
|
+
export function writeHarnessLock(root, { name = "@create-cmp/harness", version }) {
|
|
65
65
|
if (typeof version !== "string" || version.length === 0) {
|
|
66
66
|
throw new Error("writeHarnessLock: a harness version is required");
|
|
67
67
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// there is exactly one definition of the surface and the algorithm.
|
|
6
6
|
//
|
|
7
7
|
// SINGLE SOURCE OF TRUTH: packages/receipts/src/inputs-hash.mjs in the
|
|
8
|
-
// create-cmp repo (the
|
|
8
|
+
// create-cmp repo (the `@create-cmp/receipts` package). The copy in a generated
|
|
9
9
|
// project's qa/lib/ is vendored byte-identical at scaffold time and pinned by
|
|
10
10
|
// test/receipts-parity.test.mjs — edit the package source, then run
|
|
11
11
|
// `node scripts/sync-harness.mjs`.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// tarball rather than the working tree.
|
|
7
7
|
//
|
|
8
8
|
// SINGLE SOURCE OF TRUTH: packages/receipts/src/receipt-validate.mjs in the
|
|
9
|
-
// create-cmp repo (the
|
|
9
|
+
// create-cmp repo (the `@create-cmp/receipts` package). The copy in a generated
|
|
10
10
|
// project's qa/lib/ is vendored byte-identical at scaffold time and pinned by
|
|
11
11
|
// test/receipts-parity.test.mjs — edit the package source, then run
|
|
12
12
|
// `node scripts/sync-harness.mjs`.
|
|
@@ -39,7 +39,7 @@ function readStdinJson() {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// The predicate itself lives in qa/lib/receipt-validate.mjs (vendored from the
|
|
42
|
-
// cmp
|
|
42
|
+
// @create-cmp/receipts package — one definition everywhere a receipt is judged); this
|
|
43
43
|
// CLI only reads the receipt and frames the exit codes.
|
|
44
44
|
function evaluate() {
|
|
45
45
|
const receipt = readReceipt(ROOT);
|