create-cmp-cli 0.24.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 +10 -11
- 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 +271 -37
- package/packages/harness/src/lib/a11y.mjs +1 -1
- package/packages/harness/src/lib/affected-tests.mjs +99 -44
- 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 +34 -2
- 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 +434 -53
- 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 +175 -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} +229 -27
- 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/preview-gallery.mjs +1 -1
- package/packages/harness/src/receipt-check.mjs +80 -24
- 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 +175 -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 +3 -0
- package/template/qa/approve.mjs +10 -11
- package/template/qa/comments.json +1 -1
- package/template/qa/evidence/schema.json +258 -34
- package/template/qa/framework-check.mjs +271 -37
- 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/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 +34 -2
- 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 +434 -53
- 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 +175 -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} +229 -27
- 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/preview-gallery.mjs +1 -1
- package/template/qa/receipt-check.mjs +80 -24
- 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
|
@@ -22,6 +22,10 @@ import { fileURLToPath } from "node:url";
|
|
|
22
22
|
import { computeInputsHash } from "./lib/inputs-hash.mjs";
|
|
23
23
|
import { evaluateReceipt, readReceipt } from "./lib/receipt-validate.mjs";
|
|
24
24
|
import { readHold, assessHold, describeHold, holdExplains } from "./lib/agent-hold.mjs";
|
|
25
|
+
import { LANE_MARKER_STALE_MS, laneMarkerPath } from "./lib/lane-markers.mjs";
|
|
26
|
+
import { resolveHarnessManifest } from "./lib/harness-manifest.mjs";
|
|
27
|
+
import { loadProfileSync } from "./lib/profile-loader.mjs";
|
|
28
|
+
import { evidenceLadderFor } from "./lib/evidence-ladder.mjs";
|
|
25
29
|
|
|
26
30
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
27
31
|
|
|
@@ -30,9 +34,8 @@ const asHook = args.includes("--hook");
|
|
|
30
34
|
const asJson = args.includes("--json");
|
|
31
35
|
|
|
32
36
|
// The lane's own in-flight marker (verify.mjs stamps it, rewriting it at each
|
|
33
|
-
// step start with the step name and index)
|
|
34
|
-
|
|
35
|
-
const LANE_MARKER_STALE_MS = 30 * 60 * 1000;
|
|
37
|
+
// step start with the step name and index) — qa/lib/lane-markers.mjs, one
|
|
38
|
+
// path and one bound for every reader.
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* The full check running RIGHT NOW, or null. The gate must still refuse — a
|
|
@@ -44,7 +47,7 @@ const LANE_MARKER_STALE_MS = 30 * 60 * 1000;
|
|
|
44
47
|
*/
|
|
45
48
|
function laneInFlight() {
|
|
46
49
|
try {
|
|
47
|
-
const p =
|
|
50
|
+
const p = laneMarkerPath(ROOT);
|
|
48
51
|
const st = fs.statSync(p);
|
|
49
52
|
if (Date.now() - st.mtimeMs >= LANE_MARKER_STALE_MS) return null;
|
|
50
53
|
// Content is a bonus, never a requirement: legacy markers hold "pid iso".
|
|
@@ -73,7 +76,7 @@ function readStdinJson() {
|
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
// The predicate itself lives in qa/lib/receipt-validate.mjs (vendored from the
|
|
76
|
-
//
|
|
79
|
+
// prooflane-receipts package — one definition everywhere a receipt is judged); this
|
|
77
80
|
// CLI only reads the receipt and frames the exit codes.
|
|
78
81
|
function evaluate() {
|
|
79
82
|
const receipt = readReceipt(ROOT);
|
|
@@ -109,28 +112,61 @@ function evaluate() {
|
|
|
109
112
|
profile: receipt.profile,
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
115
|
+
// A step that COULD have run and did not is a gap a human can close, and a
|
|
116
|
+
// change is not done while it stands (2026-09-03). The lane boots its own
|
|
117
|
+
// device, so an environment SKIP — no device, a tool not installed, a lease
|
|
118
|
+
// held elsewhere — is never the project's honest structure; a `structure`
|
|
119
|
+
// SKIP (this project has no such tier at all) is, and is allowed.
|
|
120
|
+
//
|
|
121
|
+
// Stage 0 PR 6c: this used to test `["e2eSmoke", "androidChecks"]` by name
|
|
122
|
+
// and match Android reason text. On any other stack it therefore checked
|
|
123
|
+
// nothing — the one gate that refuses "done" over a tier that never ran,
|
|
124
|
+
// silently inert. `skipKind` is the stack-free signal and every current
|
|
125
|
+
// receipt carries it, so the rule is now: ANY step that skipped for an
|
|
126
|
+
// environmental reason blocks done, whatever it is called.
|
|
127
|
+
//
|
|
128
|
+
// Receipts predating `skipKind` (0.19.0 and earlier) are still read by their
|
|
129
|
+
// reason text, and THAT list is the profile's — read from the ladder's
|
|
130
|
+
// execution tier when the profile is loadable, and simply not applied when
|
|
131
|
+
// it is not. A legacy fallback that guessed would be worse than none.
|
|
132
|
+
//
|
|
133
|
+
// THE LADDER IS RESOLVED, NOT SPELLED. This used to read `profile.ladder`
|
|
134
|
+
// directly, which is one of the two places a profile may declare its rungs;
|
|
135
|
+
// qa/verify.mjs read the other, and nothing reconciled them. It goes
|
|
136
|
+
// through qa/lib/evidence-ladder.mjs now, so this reader and the lane can
|
|
137
|
+
// never be looking at different declarations of the same thing.
|
|
138
|
+
//
|
|
139
|
+
// NO PACK IS PASSED, AND THAT IS THE POINT. Getting the pack's spelling means
|
|
140
|
+
// calling `steps(ctx)`, and this file is the Stop hook: a done-gate that
|
|
141
|
+
// starts a lane to answer a legacy-compat question is not a gate, it is a
|
|
142
|
+
// second lane. So a profile that declares its ladder ONLY on the pack gives
|
|
143
|
+
// this reader nothing and gets no legacy fallback — the same honest silence
|
|
144
|
+
// as a profile with no ladder at all, and the reason the top-level spelling
|
|
145
|
+
// is the one `harness init` seeds and the one the resolver prefers.
|
|
146
|
+
const legacyPatterns = /no Android device|maestro CLI not installed|is held by|devices attached|CMP_DEVICE=none/;
|
|
147
|
+
let legacyNames = null;
|
|
148
|
+
try {
|
|
149
|
+
const manifest = resolveHarnessManifest(ROOT);
|
|
150
|
+
if (manifest.ok) {
|
|
151
|
+
const loaded = loadProfileSync(ROOT, manifest.manifest.profile);
|
|
152
|
+
const resolved = evidenceLadderFor(loaded.ok ? loaded.profile : null);
|
|
153
|
+
const ladder = resolved.ok ? resolved.ladder : null;
|
|
154
|
+
if (ladder && Array.isArray(ladder.deviceExecution)) legacyNames = ladder.deviceExecution;
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
// The Stop hook never crashes over a legacy-compat lookup.
|
|
158
|
+
}
|
|
159
|
+
const envSkipped = (Array.isArray(receipt.steps) ? receipt.steps : []).filter((s) => {
|
|
160
|
+
if (!s || s.verdict !== "SKIP") return false;
|
|
161
|
+
if (s.skipKind) return s.skipKind === "environment";
|
|
162
|
+
return Boolean(legacyNames && legacyNames.includes(s.name) && legacyPatterns.test(String(s.reason ?? "")));
|
|
163
|
+
});
|
|
128
164
|
if (envSkipped.length) {
|
|
129
165
|
return {
|
|
130
166
|
valid: false,
|
|
131
167
|
reason:
|
|
132
|
-
`
|
|
133
|
-
"
|
|
168
|
+
`a tier did not run — ${envSkipped.map((s) => `${s.name}: ${String(s.reason ?? "").split("\n")[0]}`).join("; ")}. ` +
|
|
169
|
+
"Those steps skipped for an environmental reason, not because this project lacks them; fix the cause and run `node qa/verify.mjs` again before finishing",
|
|
134
170
|
profile: receipt.profile,
|
|
135
171
|
};
|
|
136
172
|
}
|
|
@@ -152,9 +188,21 @@ function evaluate() {
|
|
|
152
188
|
// Surface the receipt's evidence rung (the ladder — qa/lib/evidence-level.mjs)
|
|
153
189
|
// alongside the verdict: the rung is the receipt's own derived field, read
|
|
154
190
|
// verbatim, never recomputed here. Older receipts without it stay valid.
|
|
191
|
+
//
|
|
192
|
+
// AND THE PACK WITH IT, ALWAYS — the two are set in the same breath because
|
|
193
|
+
// NORTH-STAR.md §6.5 requires that every surface showing a rung shows the
|
|
194
|
+
// pack, and §8.9 is why: one pack's L2 and another pack's L2 are different
|
|
195
|
+
// claims. This CLI printed the rung alone, which left every reader of the
|
|
196
|
+
// done-gate exactly where that rule says they must not be. Only the pack's
|
|
197
|
+
// ID is carried: it is the part that carries the meaning, and `pack.version`
|
|
198
|
+
// on a receipt is today the harness lock's number rather than the profile's
|
|
199
|
+
// (docs/adr/0008), so a surface that leaned on it would be repeating a
|
|
200
|
+
// borrowed fact.
|
|
155
201
|
const level = receipt.evidenceLevel;
|
|
156
202
|
if (level && typeof level === "object" && typeof level.rung === "string") {
|
|
157
203
|
result.evidenceLevel = level;
|
|
204
|
+
const id = receipt.pack && typeof receipt.pack.id === "string" ? receipt.pack.id.trim() : "";
|
|
205
|
+
result.packId = id || null;
|
|
158
206
|
}
|
|
159
207
|
return result;
|
|
160
208
|
}
|
|
@@ -202,7 +250,15 @@ if (asHook) {
|
|
|
202
250
|
process.exit(0);
|
|
203
251
|
}
|
|
204
252
|
|
|
205
|
-
|
|
253
|
+
// The rung NEVER travels alone (see evaluate() above): a rung is a claim in one
|
|
254
|
+
// pack's vocabulary, so the pack is named beside it, and a receipt that names no
|
|
255
|
+
// pack is SAID to name none rather than being rendered as if the omission did
|
|
256
|
+
// not matter. A rung whose pack is unknown is comparable to nothing, and that is
|
|
257
|
+
// a fact about the evidence, not a formatting detail to be tidied away.
|
|
258
|
+
const rungSuffix = result.evidenceLevel
|
|
259
|
+
? ` — evidence ${result.evidenceLevel.rung} · ${result.evidenceLevel.name}` +
|
|
260
|
+
(result.packId ? ` · pack ${result.packId}` : " · pack UNNAMED (this rung is comparable to nothing)")
|
|
261
|
+
: "";
|
|
206
262
|
|
|
207
263
|
if (asJson) {
|
|
208
264
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -29,7 +29,7 @@ const USAGE = `node qa/record-audit.mjs <subsystem> [--by <who-or-what>]
|
|
|
29
29
|
Appends one audit record (subsystem, HEAD sha, ISO timestamp, recorder) to
|
|
30
30
|
${AUDITS_REL_PATH}. The verify lane's release profile reports which
|
|
31
31
|
subsystems changed since their last record. Subsystems are derived from the
|
|
32
|
-
tree: the immediate package directories under the
|
|
32
|
+
tree: the immediate package directories under the platform source set
|
|
33
33
|
root ("${ROOT_SUBSYSTEM}" for files directly at the package root).
|
|
34
34
|
|
|
35
35
|
--list print the derived subsystems and their audit status
|
|
@@ -181,10 +181,18 @@ try {
|
|
|
181
181
|
} catch {
|
|
182
182
|
approvalsLib = null;
|
|
183
183
|
}
|
|
184
|
+
// The exemplar's shape and names are the cmp profile's (Stage 0 PR 5 —
|
|
185
|
+
// qa/lib/profiles/cmp/artifacts.mjs); this stamper is that profile's tool.
|
|
186
|
+
let profileLib = null;
|
|
187
|
+
try {
|
|
188
|
+
profileLib = await import("./lib/profiles/cmp/artifacts.mjs");
|
|
189
|
+
} catch {
|
|
190
|
+
profileLib = null;
|
|
191
|
+
}
|
|
184
192
|
|
|
185
193
|
function resolveSourceNames() {
|
|
186
|
-
if (
|
|
187
|
-
return
|
|
194
|
+
if (profileLib && typeof profileLib.resolveExemplarNames === "function") {
|
|
195
|
+
return profileLib.resolveExemplarNames(ROOT);
|
|
188
196
|
}
|
|
189
197
|
return { f: "home", F: "Home", F_UPPER: "HOME", E: "Item" };
|
|
190
198
|
}
|