@penpair/web 0.3.7 → 0.4.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/docs/adapters.md CHANGED
@@ -27,7 +27,7 @@ Use it when your application uses Marwes UI. Follow the [adapter overview](marwe
27
27
  5. Run a project provider to open that state, read it back, capture the browser and compare the image.
28
28
  6. Review the screenshots and differences; record human approval separately.
29
29
 
30
- The adapter checks that the returned evidence identifies the requested case and expected state, references the correct unchanged baseline, and has matching PNG dimensions/scale and font evidence. A complete result within the configured difference threshold is technical evidence, not visual approval or an accessibility assessment.
30
+ The adapter checks that the returned evidence identifies the requested case and expected state, references the correct unchanged baseline, and has matching image dimensions/scale (PNG or static lossless WebP) and font evidence. A complete result within the configured difference threshold is technical evidence, not visual approval or an accessibility assessment.
31
31
 
32
32
  The provider is project code with an `async runCase(test)` export. It supplies actual route/theme/state/fixture setup, browser readback, screenshot capture and image comparison through the project's pinned tools. It can integrate the project's existing Reflection/browser checker. The adapter itself does not install a browser or execute a particular Reflection engine automatically.
33
33
 
package/docs/approvals.md CHANGED
@@ -8,7 +8,7 @@ penpair approve --page Dashboard --frame FRAME_ID --write \
8
8
  # Include --case CONFIGURED_BASELINE_ID for a rich theme/state/fixture case.
9
9
  ```
10
10
 
11
- Preview captures stable evidence and records nothing. Recording captures again and appends a schema-1 owner-words record to `design/approvals.json`, with file/page/frame IDs, revision, scale, actual theme/render context, optional declared case, PNG SHA-256, date, identity, exact note and record hash. The immutable PNG lives under `design/approvals/SHA256.png`. A local exclusive append lock prevents lost records; a stale lock requires inspection. The file is atomically replaced after image bytes exist. Prior records are retained.
11
+ Preview captures stable evidence and records nothing. Recording captures again and appends a schema-1 owner-words record to `design/approvals.json`, with file/page/frame IDs, revision, scale, actual theme/render context, optional declared case, encoded-image SHA-256, date, identity, exact note and record hash. The immutable image lives under `design/approvals/SHA256.webp` or the preserved PNG form `design/approvals/SHA256.png`. A local exclusive append lock prevents lost records; a stale lock requires inspection. The file is atomically replaced after image bytes exist. Prior records are retained.
12
12
 
13
13
  The command records supplied words; it cannot authenticate a person or establish consent. An agent needs the owner's actual approval for the exact reviewed artifact and must never invent words or run this as a routine automatic success step. No approvals were recorded by this implementation task. Content hashes detect changes, not identity. An empty note/identity, unstable capture, mismatched case, altered approved bytes or credential redaction refuses publication.
14
14
 
@@ -16,4 +16,35 @@ The command records supplied words; it cannot authenticate a person or establish
16
16
 
17
17
  An exceptional `--apply --update --force-reason 'EXPLICIT_REASON'` may save a reference without a matching owner record. Its provenance says `forced-reference`; the reason persists. Rich case coverage and the implementation-case adapter refuse to count that waiver as owner-approved evidence. A waived reference is useful for an experiment, but cannot silently satisfy the design acceptance gate. Empty reasons and reasons without apply/update refuse.
18
18
 
19
- The latest revision is a conservative invalidation boundary: an unrelated design edit may require fresh approval even if pixels stay equal. A PNG alone does not approve structure, behavior or accessibility. Approval records remain local/project artifacts until reviewed; no publish, client configuration or consumer-repository edit occurs automatically.
19
+ The latest revision is a conservative invalidation boundary: an unrelated design edit may require fresh approval even if pixels stay equal. An image alone does not approve structure, behavior or accessibility. Approval records remain local/project artifacts until reviewed; no publish, client configuration or consumer-repository edit occurs automatically.
20
+
21
+ ## Image format and migration
22
+
23
+ New image approvals default to static lossless WebP. `approve --format png` or
24
+ `approve --format webp` selects an exact encoding. For `approve --case CASE_ID`
25
+ without explicit `--format`, the baseline resolver preserves an existing
26
+ reference's actual format; otherwise it uses `baselines.format`, then WebP.
27
+ An approval without `--case` uses explicit `--format` or WebP. Review the actual
28
+ encoded candidate before supplying owner words; equal decoded pixels do not make
29
+ PNG and WebP byte approvals interchangeable.
30
+
31
+ Changing `baselines.format` or supplying `baselines --format webp` alone does not
32
+ convert an existing PNG reference. Preview migration explicitly:
33
+
34
+ ```sh
35
+ penpair baselines --format webp --migrate-format
36
+ penpair approve --page Dashboard --frame FRAME_ID --case CASE_ID --format webp
37
+ ```
38
+
39
+ After inspecting the new-format artifact and obtaining the owner's actual words,
40
+ record it using the same approval command with `--write --by OWNER --note
41
+ EXACT_OWNER_WORDS`. Then apply the migration:
42
+
43
+ ```sh
44
+ penpair baselines --format webp --migrate-format --apply --update
45
+ ```
46
+
47
+ The CLI requires `--format` with `--migrate-format`; applying requires `--update`.
48
+ Normal exact-image/case approval rules still apply. Prior PNG approval records
49
+ and generations stay intact; migration publishes a new immutable generation.
50
+ Public `export` and `shots` output remain PNG and are independent of this choice.
package/docs/artifacts.md CHANGED
@@ -1,13 +1,33 @@
1
1
  # Capture and approval provenance
2
2
 
3
- Baseline manifests use schema 2. `capturedRevision` identifies the coherent capture run, while each frame has separate `captured` and `approved` records. Those records identify the file, page and frame by immutable ID, revision, PNG hash, scale, active theme names, capture time, penpair version, reported Penpot API version and browser user agent when available. A missing environment value is recorded as unknown; the record does not establish that fonts loaded correctly or identify every rendering dependency.
3
+ Baseline manifests use schema 2. `capturedRevision` identifies the coherent capture run, while each frame has separate `captured` and `approved` records. Those records identify the file, page and frame by immutable ID, revision, encoded-image hash, scale, active theme names, capture time, penpair version, reported Penpot API version and browser user agent when available. A missing environment value is recorded as unknown; the record does not establish that fonts loaded correctly or identify every rendering dependency.
4
4
 
5
- An unchanged reference retains its original approval record. A changed image recaptured without `--update` retains its approved bytes, path, revision and hash, while `captured` describes the new candidate. Legacy images without trustworthy provenance are marked `legacy`; a current revision is never invented for them. Changing a baseline still requires the owner's `--apply --update`. Schema 2 does not claim a separate human review record; that is tracked in D2.
5
+ An unchanged reference retains its original approval record. A changed image recaptured without `--update` retains its approved bytes, path, revision and hash, while `captured` describes the new candidate. Legacy images without trustworthy provenance are marked `legacy`; a current revision is never invented for them. Changing a baseline still requires the owner's `--apply --update`. Exact owner words are retained separately in [approval records](approvals.md); manifest provenance alone cannot manufacture consent.
6
6
 
7
7
  All configured frames must be present and stable. File/page/revision evidence is checked inside exports and across consecutive exports; the complete frame listing is checked again after capture. Intervening changes, failures or cancellation do not publish a new complete manifest. There is no partial-publication mode.
8
8
 
9
- PNG captures live in immutable `captures/<capture-id>/<baseline-id>.png` generations instead of replacing `<baseline-id>.png`. Read approved image paths from manifest frame `file` fields. Generated Reflection cases already use those paths. `penpair-baselines.json` is the authoritative commit point and is published last with a same-directory atomic rename. Its `casesFile` points to the exact immutable case file for that generation. The root `reflection-cases.json` remains a convenience for existing config imports; when reading it together with a manifest, require matching `captureId`, or follow `casesFile` from the manifest. Independent root files cannot be replaced as one filesystem transaction.
9
+ Baseline captures live in immutable `captures/<capture-id>/<baseline-id>.webp` or `.png` generations. New references default to static lossless WebP; existing PNG references keep PNG during ordinary captures and updates. Historical `<baseline-id>.png` files are not renamed or converted implicitly. Read approved image paths from manifest frame `file` fields. Generated Reflection cases already use those paths. `penpair-baselines.json` is the authoritative commit point and is published last with a same-directory atomic rename. Its `casesFile` points to the exact immutable case file for that generation. The root `reflection-cases.json` remains a convenience for existing config imports; when reading it together with a manifest, require matching `captureId`, or follow `casesFile` from the manifest. Independent root files cannot be replaced as one filesystem transaction.
10
10
 
11
11
  A process interrupted during output may leave an unreferenced capture generation. Previous manifest references remain usable; inspect before cleaning generations. Staging directories are removed on handled errors. This is filesystem publication safety, not a power-loss-proof multi-file transaction or a remote backup.
12
12
 
13
13
  Standalone PNG export and full-file archive candidates also stage complete bytes before publication. Archives use exclusive creation and never replace an existing file. PNG exports atomically replace the requested output only after a stable result. The export report includes target provenance. Archive restore verification remains a separate scratch import/readback task.
14
+
15
+ ## Baseline encoding and explicit migration
16
+
17
+ Always follow the manifest's exact `file` path and encoded-byte hash; do not
18
+ construct a `.png` or `.webp` path from the baseline ID. PNG and WebP with identical
19
+ pixels have different approval identities. An existing reference keeps its
20
+ actual encoding even when `baselines.format` or `--format` selects another
21
+ format. New references choose explicit `--format`, then config, then WebP.
22
+
23
+ An explicit `--format webp --migrate-format` previews migration. Publishing it
24
+ also requires `--apply --update` and the normal matching exact new-format approval
25
+ (or explicit waiver rules). Earlier immutable generations and owner records
26
+ remain unchanged. See [approval migration](approvals.md#image-format-and-migration).
27
+
28
+ WebP conversion is lossless and independently verifies decoded RGBA bytes,
29
+ dimensions and ICC preservation. Animation, unsupported bit depth, excessive
30
+ size or pixel/metadata mismatch refuses conversion and preserves the previous
31
+ manifest. Public PNG exports, shot generations and pinned release-source PNG
32
+ renders retain their existing format; baseline defaults do not redefine those
33
+ artifacts.
package/docs/coverage.md CHANGED
@@ -20,9 +20,9 @@ A baseline frame may declare an explicit viewport, theme label, UI state, fixtur
20
20
 
21
21
  `theme`, `state` and `fixture` are declared design intent. A label alone does not prove actual theme activation, application state or data loading. Optional `activeThemeIds` asserts the complete exact active native theme-ID set at capture; omit it for a manually styled frame and retain that uncertainty. Baseline capture remains read-only toward the Penpot document: it never switches themes. Use the separate [mode trial](mode-verification.md) for temporary theme switching.
22
22
 
23
- Frame geometry must match the declared CSS viewport within less than 0.01 CSS px per axis, allowing floating-point noise from Penpot transforms and auto sizing. Baseline capture, case approval and offline coverage use the same tolerance. Exported PNG dimensions must still exactly equal the declared viewport multiplied by the observed export scale and rounded to whole pixels; the geometry tolerance never applies to PNG dimensions. A failed viewport/theme assertion marks `case-mismatch`, exits incomplete and preserves the previous generation. New provenance retains raw, unrounded design dimensions, active native theme IDs where available, API/tool version, renderer description and capture time. Font names are declarations; loaded font versions remain unobserved.
23
+ Frame geometry must match the declared CSS viewport within less than 0.01 CSS px per axis, allowing floating-point noise from Penpot transforms and auto sizing. Baseline capture, case approval and offline coverage use the same tolerance. Encoded image dimensions (PNG or static lossless WebP) must still exactly equal the declared viewport multiplied by the observed export scale and rounded to whole pixels; the geometry tolerance never applies to image dimensions. A failed viewport/theme assertion marks `case-mismatch`, exits incomplete and preserves the previous generation. New provenance retains raw, unrounded design dimensions, active native theme IDs where available, API/tool version, renderer description and capture time. Font names are declarations; loaded font versions remain unobserved.
24
24
 
25
- Case metadata is part of approval identity. Changing a fixture/state/theme/viewport declaration requires `--apply --update` even if the PNG bytes happen to match. Moving a matching image to a different file/page/frame identity also requires review. Recapture preserves the original approval until that explicit update. Metadata is pinned before asynchronous capture.
25
+ Case metadata is part of approval identity. Changing a fixture/state/theme/viewport declaration requires `--apply --update` even if the image bytes happen to match. Moving a matching image to a different file/page/frame identity also requires review. Recapture preserves the original approval until that explicit update. Metadata is pinned before asynchronous capture.
26
26
 
27
27
  ## Required matrix
28
28
 
@@ -30,7 +30,7 @@ Add `baselines.coverage.required` as an explicit list of route/viewport/theme/st
30
30
 
31
31
  See [an example matrix](../examples/coverage-matrix.json) spanning desktop/mobile, light/dark and an empty state. Only request combinations the product intends to support; no matrix is inferred from frame sizes or names.
32
32
 
33
- `penpair baselines coverage --json` runs offline. It loads the version-2 manifest, checks the configured file pin, validates project-bound artifact paths, independently hashes approved PNG bytes and reports:
33
+ `penpair baselines coverage --json` runs offline. It loads the version-2 manifest, checks the configured file pin, validates project-bound artifact paths, independently hashes approved PNG or static lossless WebP bytes and reports:
34
34
 
35
35
  - `missing-case`: no configured frame for the required tuple.
36
36
  - `missing-capture`: a frame is configured but has no approved capture.
@@ -41,7 +41,7 @@ See [an example matrix](../examples/coverage-matrix.json) spanning desktop/mobil
41
41
 
42
42
  `designCasesComplete` means all requested design slots have matching approved intent. `browserVerification: not-run` and `accessibility: not-assessed` remain separate, even when every design slot is complete. Unknown font loading, absent activation assertions and missing renderer metadata are listed. The offline report exits 3 for gaps; it never connects to Penpot or loads project extensions.
43
43
 
44
- Ordinary `baselines` can publish a coherent capture of its configured frames while the broader required matrix still has gaps; that command returns incomplete when configured requirements remain uncovered. The manifest distinguishes capture completeness from matrix coverage. An incomplete capture itself still publishes nothing. Manifest reads are bounded to 16 MiB and each approved PNG check to 64 MiB; exceeding those bounds cannot produce covered evidence.
44
+ Ordinary `baselines` can publish a coherent capture of its configured frames while the broader required matrix still has gaps; that command returns incomplete when configured requirements remain uncovered. The manifest distinguishes capture completeness from matrix coverage. An incomplete capture itself still publishes nothing. Manifest reads are bounded to 16 MiB and each approved image check to 64 MiB; exceeding those bounds cannot produce covered evidence.
45
45
 
46
46
  ## Browser integration
47
47
 
@@ -62,3 +62,19 @@ Baseline entries require `frame` or `frameId`, or both. `frameId` is a unique bo
62
62
  When both are supplied, a different current board name reports `renamed` and the entry is never captured under its stale name. Review the rename and update the expected name before retrying. ID-only entries follow the ID and report its current name. Page searches stop with an explicit error if the 10,000-shape budget truncates the listing.
63
63
 
64
64
  `approve --case CASE_ID --page PAGE --frame ID` compares `--frame` directly with a configured `frameId`; name-only cases retain the unique-name check. Nested frames use the same exact frame-ID capture identity for approval matching. The baseline manifest continues to record `frameId`; Reflection case format is unchanged.
65
+
66
+ ## Image encoding
67
+
68
+ New baseline references default to static lossless WebP. Existing PNG references
69
+ and approvals retain their exact original bytes, paths and hashes during normal
70
+ capture/update. Neither `baselines.format` nor `--format` alone migrates an
71
+ existing reference. Use explicit `--format webp --migrate-format` for a preview;
72
+ application also requires `--apply --update` and matching exact new-format image
73
+ approval. A PNG approval does not cover equivalent WebP pixels. See
74
+ [approval and migration](approvals.md#image-format-and-migration).
75
+
76
+ WebP conversion preserves exact dimensions, decoded RGBA pixels including RGB
77
+ under transparency, and the source ICC profile. Unsupported animated/16-bit
78
+ input or failed conversion verification leaves the prior generation intact.
79
+ The conversion budget is 64 megapixels; geometry tolerance never relaxes image
80
+ pixel-size or pixel-identity checks.
@@ -0,0 +1,162 @@
1
+ # Design releases
2
+
3
+ A design release freezes a reviewed design handoff for an explicit product SemVer,
4
+ independent of the Penpair package version. It joins
5
+ [journey maps](journey-maps.md), [integration maps](integration-maps.md), exact
6
+ design cases and immutable evidence so a later implementation agent can identify
7
+ what was reviewed. This is a design release, separate from publishing Penpair's
8
+ npm packages or accepting a frontend implementation.
9
+
10
+ ## Local commands
11
+
12
+ Create the plan from actual source inspection, using your own product SemVer.
13
+ These paths illustrate project-owned inputs. A release accepts at most 100
14
+ selected source screens. Use the local project command, such as
15
+ `pnpm exec penpair`, when installed as a dependency.
16
+
17
+ ```sh
18
+ # Save a planned release, then inspect the remote assembly preview.
19
+ penpair design-release plan --from design/releases/1.0.0/plan.json \
20
+ --out design/releases/1.0.0/release.json
21
+ penpair design-release assemble --from design/releases/1.0.0/release.json
22
+
23
+ # After reviewing the preview and authorizing the assembly write:
24
+ penpair design-release assemble --from design/releases/1.0.0/release.json --apply
25
+ ```
26
+
27
+ Next, perform the guided native copy and record actual source/target identities
28
+ in `receipts.json`. Verification reads the live frames and saves evidence;
29
+ finalization below is a preview and records no owner approval.
30
+
31
+ ```sh
32
+ penpair design-release verify --from design/releases/1.0.0/release.json \
33
+ --receipts design/releases/1.0.0/receipts.json
34
+ penpair design-release finalize --from design/releases/1.0.0/release.json
35
+ penpair design-release check --from design/releases/1.0.0/release.json --live
36
+ penpair design-release context --from design/releases/1.0.0/release.json --draft
37
+ ```
38
+
39
+ Only apply finalization after the owner supplies actual words for the exact
40
+ verified candidate, as described below. After finalization, omit `--draft` to
41
+ request approved context. Missing required proof keeps verification/finalization
42
+ incomplete; these commands do not bypass the native support limits.
43
+
44
+ ## Prepare and assemble
45
+
46
+ Use the `design-release` command family to plan, assemble, verify, finalize,
47
+ check and produce context. Use the installed command's help for current argument
48
+ names. Planning exposes required inputs and unresolved work. Assembly records
49
+ identities and hashes; it does not invent source observations or owner approval.
50
+ Each selected source needs its complete `inspection` reference (project-relative
51
+ `path` and exact `sha256`). Pin a stable source PNG through its `render` reference
52
+ before verification; a draft without this reference cannot pass fidelity. A
53
+ source using tokens also needs a complete pinned token-inspection `dependencies`
54
+ reference. Required dependency changes invalidate the pinned source evidence.
55
+
56
+ Include the parent and every linked detail-map contract in the release's
57
+ `contracts` list, each with its independent exact review. A reviewed parent link
58
+ is not approval of the child graph. See [linked detail maps](integration-maps.md#linked-detail-maps).
59
+
60
+ Keep the release page in the same pinned Penpot file, named `release/<version>`.
61
+ Assembly creates the owned overview/map sections and reports `awaiting-copy`.
62
+ Copy source design frames natively using a reviewed, guided copy/paste operation
63
+ by a person or an authorized AI using existing browser tools. Assembly does not
64
+ perform an unsupported cross-page clone.
65
+ Preserve visual fidelity, native components, token bindings, text and layout;
66
+ do not recreate frames from screenshots or normalize their dimensions. A source
67
+ using auto height must retain it. Floating-point geometry noise is not a reason
68
+ to change source sizing.
69
+
70
+ The live sequence is read-only inspection, pinned snapshot, one idempotent write,
71
+ independent readback and stable exports that are looked at. Snippets stay under
72
+ 90 KB. If a write response is uncertain, inspect what exists before retrying.
73
+ Keep unmanaged content intact; a page name alone cannot authorize replacement.
74
+ Do not copy via a lossy or structurally incomplete fallback when native fidelity
75
+ cannot be established: report the missing proof.
76
+
77
+ ## Supported native verification limits
78
+
79
+ Native verification is deliberately narrower than native copy/paste. Selecting a
80
+ screen for this workflow does not establish that its content can be verified.
81
+ The current verifier blocks these cases:
82
+
83
+ - **All component instances, copies, main instances and component roots.** The
84
+ inspection cannot establish complete override state. Retaining links during a
85
+ copy is required, but it does not prove override fidelity.
86
+ - **Groups without an explicit `maskedGroup` observation.** An absent mask flag
87
+ is unknown, not proof that the group is unmasked.
88
+ - **Unknown text range/style observations**, including unobserved mixed-range
89
+ state, and any other unknown or missing required inspection field.
90
+ - **Paths/vector shapes whose actual path content was not observed.** A path
91
+ type or matching raster export cannot establish the native path data.
92
+ - Unsupported shape content, incomplete hierarchy, unobservable interactions or
93
+ missing pinned source render/dependency evidence.
94
+
95
+ A selected screen containing one of these cases remains blocked with a reason;
96
+ it cannot be finalized by waiving the finding or accepting equal pixels. Retain
97
+ its native structure and source evidence, then obtain the missing observation or
98
+ extend and validate inspection support before retrying. Do not detach components,
99
+ flatten groups, remove styles or substitute raster images to bypass the gate.
100
+ Simple screens whose required fields are fully observed can be assessed, but
101
+ that is not a claim of complete support for every native Penpot feature.
102
+
103
+ ## Verify the package
104
+
105
+ Supply a project-owned receipt list with explicit `sourceId` and
106
+ `targetFrameId` values to `verify --receipts FILE`. Check release frame identities
107
+ against the selected sources and retain immutable inspection/export evidence
108
+ with exact paths and hashes. Compare source and release frames independently,
109
+ including component/token identity and sizing
110
+ behavior, and inspect stable exports. A current rendering cannot rewrite the
111
+ bytes of an already recorded artifact. Native release objects remain mutable,
112
+ including changes inherited through shared components and tokens. A later change
113
+ is drift and needs a new reviewed generation, not a silent refresh of finalized
114
+ evidence.
115
+
116
+ New baseline evidence uses lossless WebP. Existing PNG baselines and image
117
+ approvals remain valid in their original format. Conversion must be a deliberate
118
+ migration with new evidence and review; never change approved bytes in place.
119
+ Penpot can supply PNG during export, so conversion must retain exact dimensions
120
+ and pixels. Quality 100 alone does not establish lossless encoding.
121
+
122
+ The release needs matching exact-artifact review records and no unresolved
123
+ blocking findings. Preserve the distinction between:
124
+
125
+ - Map review: the owner's supplied words bind an exact contract hash.
126
+ - Design image approval: the existing exact case/image approval protocol.
127
+ - Technical verification: independently observed structure and image evidence.
128
+ - Implementation acceptance: actual application behavior, browser evidence and
129
+ owner review, assessed separately after implementation.
130
+
131
+ ## Finalize and continue
132
+
133
+ The release record progresses from `planned` through `awaiting-copy` and
134
+ `verified` to `final`. Finalization records the reviewed release identity and
135
+ immutable evidence. It must not promote missing evidence, waivers or inferred owner approval into a
136
+ successful design gate. Finalized means the design package is ready to guide
137
+ implementation; it does not mean the application has been built or checked.
138
+
139
+ Finalization previews by default. Only use `finalize --apply --by OWNER --note
140
+ EXACT_WORDS` after the owner has supplied those words for the exact candidate.
141
+ Keep journal/run identity when reconciling interrupted remote work.
142
+
143
+ Run checks again before using the package later. Missing or modified artifacts,
144
+ changed map/source hashes and changed release evidence must remain visible as
145
+ drift or incomplete evidence. A saved-source check does not contact Penpot;
146
+ live freshness requires an explicit fresh observation. Context must identify
147
+ its verification scope and any truncation. Release context includes actual frozen
148
+ `mapContexts`, exact release-page `mapTargets`, independently pending implementation
149
+ status and next actions. Linked detail navigation targets the release's own child
150
+ boards; each child contract retains its separate review requirement.
151
+ Its `proposedBaselines` is a configuration fragment with the release page, WebP
152
+ format, baseline root and explicit target `frameId` entries. Review and merge it
153
+ into the existing `baselines` block; context does not rewrite project config or
154
+ approve the new target image identities.
155
+
156
+ Use `penpair-handoff` with the approved integration context, then `penpair-verify`
157
+ for the actual frontend. Keep those later implementation results separately;
158
+ never rewrite the finalized design to match an implementation failure.
159
+
160
+ The [reset-password example](../examples/design-release-maps/README.md) is an
161
+ unapproved fixture showing decisions and gaps. It is not a finalized release or
162
+ proof of a live Penpot copy.
package/docs/guide.md CHANGED
@@ -10,6 +10,7 @@ consumer evidence.
10
10
  | Path | Documents |
11
11
  | --- | --- |
12
12
  | First run / shared file | [Choose an installation route](installation.md), [manual installation](getting-started.md), [team](team.md), [validation](validation.md) |
13
+ | Intent / release | [Journey maps](journey-maps.md), [integration maps](integration-maps.md), [design releases](design-releases.md) |
13
14
  | Build / handoff | [Cookbook](cookbook.md), [workflow](design-workflow.md), [kit](build-kit.md), [file scripts](run-file.md), [handoff](handoff.md) |
14
15
  | Evidence | [Inspection](inspection.md), [advisory audits](audits.md), [snapshots](snapshots.md), [approvals](approvals.md), [coverage](coverage.md), [artifacts](artifacts.md) |
15
16
  | Integration | [Companion adapters](adapters.md), [MCP](mcp.md), [formats](formats.md), [extension trust](extensions-trust.md), [credentials](credentials.md), [token adapter](marwes.md) |
@@ -95,6 +96,7 @@ For registered writes, supply an explicit `verify({ inspect, apply, readback })`
95
96
  "baselines": {
96
97
  "page": "Candidate",
97
98
  "root": "tests/baselines",
99
+ "format": "webp",
98
100
  "threshold": 0.05,
99
101
  "frames": [
100
102
  { "frame": "F / candidates", "id": "candidates-desktop", "route": "candidates" },
@@ -107,8 +109,23 @@ For registered writes, supply an explicit `verify({ inspect, apply, readback })`
107
109
  - Each entry requires `frame` or `frameId` (or both). `frameId` is a board UUID, unique across entries, resolved anywhere on the configured page. Frames inside boards, such as release frames inside section boards, need `frameId`. Name-only entries still match top-level frames only.
108
110
  - With both fields, `frame` is an expected name: a changed name reports `renamed` and captures nothing for that entry. Review and update the expected name before retrying. ID-only entries use the board's current name in reports. A truncated page search (10,000 shapes) stops the capture instead of using a partial listing.
109
111
  - For example: `{ "frameId": "10216397-acdf-805e-8008-d69b71f841d4", "frame": "Login / Empty", "id": "login-empty", "route": "login" }`.
110
- - `id` names the image (`<id>.png`) and the check. `route` is the route in your app this frame is the design of; a frame without a route is exported but gets no check.
111
- - Without `--apply` the command only looks and reports each frame as `new`, `same`, `changed`, `unstable` (the export never settled) or `missing`/`renamed`. `--apply` writes new images, `penpair-baselines.json` (file, revision, sizes, hashes) and `reflection-cases.json`. A `changed` image is replaced only with `--apply --update`: that is the act of approving the new design, and the owner's call. The exit code is 2 when a design differs from its image, 3 when a frame is missing, renamed or unstable.
112
+ - `id` names the image (`<id>.webp` for a new default reference; existing PNG references retain `.png`) and the check. `route` is the route in your app this frame is the design of; a frame without a route is exported but gets no check.
113
+ - Without `--apply` the command only looks and reports each frame as `new`, `same`, `changed`, `unstable` (the export never settled) or `missing`/`renamed`. `--apply` writes new images, `penpair-baselines.json` (file, revision, sizes, hashes) and `reflection-cases.json`. A `changed` image is replaced only with `--apply --update` and matching exact-artifact owner approval (or an explicitly recorded waiver); see [approval records](approvals.md). The exit code is 2 when a design differs from its image, 3 when a frame is missing, renamed or unstable.
114
+
115
+ New references default to static lossless WebP. `baselines.format` accepts
116
+ `"webp"` or `"png"`; a command's `--format` overrides that choice for new
117
+ references. Existing approved images keep their original encoding during ordinary
118
+ captures and updates, even when config or `--format` selects another format.
119
+ Approved byte hashes are never silently converted.
120
+
121
+ To preview an explicit migration, run `penpair baselines --format webp
122
+ --migrate-format`. After reviewing and recording matching new-format approval,
123
+ apply it with `penpair baselines --format webp --migrate-format --apply --update`.
124
+ `--migrate-format` requires explicit `--format`, and applying it requires
125
+ `--update`. Use `approve --format webp` (plus the intended `--case`, when present)
126
+ to review/record the same encoding; PNG approval cannot approve WebP bytes.
127
+ See [approval and migration details](approvals.md#image-format-and-migration).
128
+ Public `export` and `shots` remain PNG.
112
129
 
113
130
  The generated `reflection-cases.json` holds, per route, the viewports (the image's exact size) and the `visualSmoke` cases (review-only, `strict: false`). A Reflection config uses it like this; only the route's own expectations are written by hand:
114
131
 
@@ -169,6 +186,6 @@ Use `inspect <shape-id> --page <name> --all` for bounded structured evidence wit
169
186
 
170
187
  ## State, theme and responsive coverage
171
188
 
172
- Add explicit `frames[].case` metadata and a required matrix under `baselines.coverage`. Case geometry allows less than 0.01 CSS px of Penpot floating-point noise per axis; exported PNG dimensions remain exact at the capture scale, and raw geometry stays in provenance. `baselines coverage --json` validates approved images offline and exposes missing/unsupported slots. Rich cases remain pending browser setup mappings; see [coverage semantics and approval](coverage.md).
189
+ Add explicit `frames[].case` metadata and a required matrix under `baselines.coverage`. Case geometry allows less than 0.01 CSS px of Penpot floating-point noise per axis; encoded image dimensions (PNG or lossless WebP) remain exact at the capture scale, and raw geometry stays in provenance. `baselines coverage --json` validates approved images offline and exposes missing/unsupported slots. Rich cases remain pending browser setup mappings; see [coverage semantics and approval](coverage.md).
173
190
 
174
191
  For project-specific agent tools, use the [explicit-root stdio server and client examples](mcp.md). It exposes typed guarded services and defaults to omitting design-write tools.
package/docs/handoff.md CHANGED
@@ -29,6 +29,6 @@ An optional schema-1 component map names exact source identities:
29
29
 
30
30
  Names and equal pixels do not establish a mapping. There are no guessed library IDs in shipped maps: the adapter's native component catalogue awaits C1 proof. Static scalar props are owner-declared hints; text props resolve only explicit descendants of that instance. Missing text IDs stay unknown. No inferred click handlers, state transitions, keyboard behavior, accessibility roles, focus order or responsive layout claims are emitted. The current recorded state does not describe every breakpoint or theme.
31
31
 
32
- `spec check` regenerates from the supplied structured capture and map, detecting manual edits, source/revision changes and mapping changes (exit 2). It does not contact Penpot or establish current live freshness. A PNG export manifest alone cannot prove structural/text freshness; use a new structured inspection rather than treating an unchanged image as proof. Live human handoff/adoption acceptance remains pending under I12; native mapping generation remains gated under C1. JSON output uses the shared command envelope.
32
+ `spec check` regenerates from the supplied structured capture and map, detecting manual edits, source/revision changes and mapping changes (exit 2). It does not contact Penpot or establish current live freshness. An image export manifest alone cannot prove structural/text freshness; use a new structured inspection rather than treating an unchanged image as proof. Live human handoff/adoption acceptance remains pending under I12; native mapping generation remains gated under C1. JSON output uses the shared command envelope.
33
33
 
34
34
  Owner review can be retained as an [exact-artifact approval record](approvals.md). Changed baseline references require matching supplied owner words or an explicitly labeled waiver; a comparison metric does not supply approval.
@@ -0,0 +1,171 @@
1
+ # Integration maps
2
+
3
+ An integration map links a reviewed user journey to the screens, routes,
4
+ components, state, data and verification work needed to implement it. It gives
5
+ agents a shared project record of what the solution must present and what the
6
+ current implementation actually presents.
7
+
8
+ Start with the [journey map](journey-maps.md) and complete [design evidence](handoff.md).
9
+ Inspect existing code before proposing new modules. Reuse project architecture;
10
+ a visual frame does not justify inventing a backend or component framework.
11
+
12
+ ## Local commands
13
+
14
+ ```sh
15
+ penpair integration-map check --from design/integrations/reset-password/map.json
16
+ penpair integration-map render --from design/integrations/reset-password/map.json --out design/integrations/reset-password/rendered
17
+ penpair integration-map context --from design/integrations/reset-password/map.json --out design/integrations/reset-password/context.json
18
+ ```
19
+
20
+ Use the local project command when installed as a dependency. Add `--live` to a
21
+ check only when an explicit fresh Penpot observation is needed; ordinary saved
22
+ input validation does not establish current live freshness.
23
+
24
+ ## Record the gaps before building
25
+
26
+ Use stable requirements and connect each to relevant design cases, proposed or
27
+ existing code responsibilities, data contracts and verification evidence. Mark
28
+ claims `observed`, `proposed`, `owner-decision` or `unknown`. Distinguish visible
29
+ screen state from inferred behavior, and application intent from observed code.
30
+ Keep unresolved component identity, API behavior and accessibility semantics
31
+ explicit.
32
+
33
+ | Requirement | Design observation | Implementation observation | Finding and next action |
34
+ | --- | --- | --- | --- |
35
+ | `reset-error-retry` | Illustrative design has a retryable error with the address retained | Hypothetical implementation clears the address after a failed request | Blocking: the person must retype their address. Preserve the value and verify the failed-request/retry path; this example is not observed project evidence. |
36
+
37
+ Compare both directions: intended requirements without implementation, and
38
+ implemented behavior without an agreed rationale. Link concrete source/evidence
39
+ rather than reporting that a diagram or passing build proves completeness.
40
+
41
+ ## Contract, drawings and edits
42
+
43
+ A schema-1 `integration-map` contract uses the same sources, nodes, edges,
44
+ requirements and findings as a journey contract. Its `journey` reference binds the
45
+ project-relative path and exact SHA-256 of its journey map. Component mapping
46
+ still requires exact source identities as described in [handoff](handoff.md);
47
+ familiar labels are insufficient.
48
+
49
+ The `integration-map` command family checks a contract, renders derived diagrams,
50
+ publishes managed native objects, imports native edits as a reviewable proposal,
51
+ accepts a proposal, records supplied owner review and emits bounded context.
52
+ Read installed help for exact arguments. Do not equate `accept` of an edit
53
+ proposal with owner `approve`, image approval or implementation acceptance.
54
+
55
+ Canonical JSON is authoritative. Mermaid, drawing plans and native Penpot boards
56
+ are derived views tied to its hash. A managed Penpot edit must become a proposal
57
+ against that hash; inspect the changes before accepting them. Unmanaged shapes
58
+ and ambiguous identities are not automatic contract edits. Publishing and
59
+ updating native drawings follow the full Penpot write protocol.
60
+
61
+ Semantic native import writes both the canonical proposal and a bound adjacent
62
+ `<proposal>.native.json` observation. Preserve both. Native acceptance rechecks
63
+ the live observation read-only before changing the canonical contract and saved
64
+ baseline; afterward republish explicitly to update native contract metadata.
65
+ A purely local semantic proposal without native observation can be accepted
66
+ offline. If canonical acceptance succeeded but saving the projection failed,
67
+ reconcile with the same proposal rather than blindly publishing again.
68
+
69
+ Geometry-only edits use a separate `map-presentation-proposal`. Acceptance checks
70
+ the exact canonical hash, saved baseline and current observed projection, captures
71
+ fresh stable evidence and adopts presentation without changing canonical bytes.
72
+ Renew native visual review: old review does not automatically cover new render
73
+ evidence. Future publication preserves accepted node positions and sizes.
74
+ Unsupported style/ownership changes and unmanaged objects remain conflicts.
75
+
76
+ Use clear typography, short labels and a neutral diagram theme. The diagram's
77
+ font defaults to Inter; an explicit presentation override belongs to the map.
78
+ Diagram presentation must not modify the application's tokens or theme.
79
+
80
+ ## Readable titles and descriptions
81
+
82
+ Existing contracts remain supported. Optional map `summary` and `description`
83
+ add a short introduction and detailed context. A node's existing `label` remains
84
+ its title; optional `summary`, `description` and `icon` supply a one-line purpose,
85
+ longer explanation and semantic icon. Keep labels specific and concise instead
86
+ of repeating “proposed” or “unknown”; the origin badge supplies that distinction.
87
+ Descriptions still need to explain which evidence supports a claim and which
88
+ questions remain unresolved.
89
+
90
+ Native cards use compact titles, summaries, icons and connector ports. Full
91
+ original text and long descriptions remain in managed notes below the flow and
92
+ in generated Markdown/context; they must not disappear when card text is shortened.
93
+ Descriptions and icons belong to canonical intent: edits need proposal review and
94
+ renewed approval. Use one fixed icon key: `screen`, `action`, `decision`, `data`,
95
+ `service`, `code`, `check` or `link`. Icons use native vector paths, with no external
96
+ image URLs or asset downloads.
97
+
98
+ ## Linked detail maps
99
+
100
+ Decompose oversized maps explicitly; Penpair does not automatically partition a
101
+ contract. Give each detail map its own canonical contract, native publication
102
+ and owner review. In the parent, a `kind: "detail-link"` node references exactly
103
+ one `sourceId`: that source must have `kind: "contract"`, a project-relative
104
+ `path`, the raw file-byte `sha256`, and exact `fileId`, `pageId` and `frameId` of
105
+ the published detail board in the same pinned file. This source byte hash is
106
+ distinct from the canonical hash used by a journey reference.
107
+
108
+ The linked child must have the same map kind as the parent. The link covers at
109
+ least one parent requirement through `requirementIds`, and the child retains
110
+ all those requirement IDs. Generated Markdown links to the contract; the native
111
+ card uses a `NavigateTo` interaction to its declared board. Missing/stale source
112
+ bytes or incompatible child requirements prevent readiness.
113
+
114
+ Parent context identifies the child handoff and its independent review rather
115
+ than inlining the child graph. Later agents must read that child contract too.
116
+ Include every parent and linked detail contract in the design release with its
117
+ own exact review. Publishing or approving the parent does not publish or approve
118
+ its detail maps.
119
+
120
+ ## Implementation context
121
+
122
+ A context result should identify the exact map and journey, review/freshness,
123
+ open findings, requirements and linked source evidence. Bounded or truncated
124
+ context must say so; missing evidence remains pending. Freshness against supplied
125
+ saved files does not assert live Penpot freshness or functional correctness.
126
+
127
+ Later agents should:
128
+
129
+ 1. Read the current map context and linked sources before implementing.
130
+ 2. Surface stale inputs and blocking findings before relying on approval.
131
+ 3. Implement within the reviewed scope; propose changes to intent separately.
132
+ 4. Attach actual route/state, browser and behavior evidence to the project record.
133
+ 5. Reconcile findings and seek implementation acceptance independently.
134
+
135
+ Use [Reflection Check](adapters.md#check-the-frontend-against-the-design) for actual
136
+ browser comparisons. It supplies visual evidence; behavior and accessibility
137
+ need their own checks. A low pixel difference does not approve architecture or
138
+ prove a working recovery flow.
139
+
140
+ ## Design readiness and implementation readiness
141
+
142
+ A [design release](design-releases.md) can finalize a reviewed, internally
143
+ consistent design package while implementation is still `not-assessed`. That
144
+ separation is intentional: agents need stable approved design context before
145
+ they build. Never mark implementation accepted because a map was approved.
146
+
147
+ See the [worked reset-password example](../examples/design-release-maps/README.md)
148
+ for a proposed journey and integration map with visible unknowns.
149
+
150
+ Every local requirement must appear in a node or edge's `requirementIds`, and an
151
+ integration map retains all requirement IDs from its linked journey. Omitted
152
+ coverage prevents readiness and design finalization; drafts can still be checked
153
+ and rendered. Drawing plans explicitly become incomplete above a 16,384 px axis
154
+ or 64 KiB serialized plan budget. Split the map instead of publishing clipped
155
+ content.
156
+
157
+ ## Publication recovery
158
+
159
+ After an uncertain write, inspect the actual target and retained run first. Resume
160
+ the original journal explicitly, keeping the original contract, sources and
161
+ target flags (including whether `--board-id` was omitted):
162
+
163
+ ```sh
164
+ penpair integration-map publish --from design/integrations/reset-password/map.json \
165
+ --page-id PAGE_ID --apply --resume RUN_ID
166
+ ```
167
+
168
+ Use the original `--board-id` and `--projection` options if they were supplied.
169
+ Resume independently reconciles readback and exports; it does not blindly repeat
170
+ the write. Changed input or code signatures must refuse reuse. Inspect again and
171
+ start a new explicitly authorized run when needed; do not bypass those guards.
@@ -0,0 +1,139 @@
1
+ # Journey maps
2
+
3
+ A journey map records what a person is trying to accomplish, which states they
4
+ encounter, and the decisions still needed before implementation. It is a
5
+ project-owned contract that later agents can read without recovering earlier
6
+ chats. Use an [integration map](integration-maps.md) to connect the journey to
7
+ application structure, and a [design release](design-releases.md) to freeze a
8
+ reviewed design handoff.
9
+
10
+ ## Local commands
11
+
12
+ ```sh
13
+ penpair journey-map check --from design/journeys/reset-password/map.json
14
+ penpair journey-map render --from design/journeys/reset-password/map.json --out design/journeys/reset-password/rendered
15
+ penpair journey-map context --from design/journeys/reset-password/map.json --out design/journeys/reset-password/context.json
16
+ ```
17
+
18
+ Use your project's local command, such as `pnpm exec penpair`, when installed as
19
+ an application dependency. Rendering with `--out` writes local derived artifacts;
20
+ it does not publish into Penpot. Context is intended for reviewed inputs; drafts
21
+ must be explicit and cannot imply readiness.
22
+
23
+ ## Start from observed intent
24
+
25
+ Inspect the actual frames and existing application before drafting. Record the
26
+ user goal, entry points, successful outcome, alternate paths and recovery. Use
27
+ stable requirement and node IDs so a finding can refer to the same requirement
28
+ through review and implementation. A frame establishes visible design facts;
29
+ it does not establish what a button does, how an API behaves or how keyboard
30
+ focus moves.
31
+
32
+ Every node, edge and requirement declares its origin:
33
+
34
+ | Origin | Meaning |
35
+ | --- | --- |
36
+ | `observed` | Directly supported by the referenced design, code or evidence |
37
+ | `proposed` | An AI or designer recommendation awaiting a decision |
38
+ | `owner-decision` | A decision actually supplied by the owner |
39
+ | `unknown` | Missing information; retain it as a question |
40
+
41
+ An origin is provenance, not approval. In particular, an `owner-decision` label
42
+ cannot replace a review record binding the owner's actual words to the exact
43
+ contract hash. Do not turn guesses into observations or invent approval notes.
44
+
45
+ ## Keep the flow readable
46
+
47
+ Use short, specific node titles (`label`), optional one-line `summary` and longer
48
+ `description` text for evidence, decisions and unknowns. The map itself also
49
+ accepts optional `summary` and `description`. Origin badges distinguish observed
50
+ facts from proposals without repeating that status in every title. Optional
51
+ semantic `icon` values use fixed native vectors. Full notes remain available
52
+ below the flow and in Markdown/context; legacy contracts need no new fields.
53
+ See [titles and descriptions](integration-maps.md#readable-titles-and-descriptions).
54
+ These optional fields are part of reviewed canonical intent, so changing them
55
+ needs renewed approval.
56
+
57
+ ## Contract and review
58
+
59
+ The schema-1 JSON contract contains `kind: "journey-map"`, `id`, `title`, `goal`,
60
+ `sources`, `nodes`, `edges`, `requirements` and `findings`. Sources retain their
61
+ SHA-256 and available path/file/page/frame/revision identity. Nodes and edges
62
+ carry readable labels and stable IDs; requirement links connect them to the
63
+ intended outcomes. Open blocking findings keep readiness pending. Resolving a
64
+ finding requires an explicit resolution, rather than deleting its history.
65
+
66
+ The `journey-map` command family supports checking, rendering, publishing,
67
+ importing managed edits as proposals, accepting proposals, recording supplied
68
+ approval, and producing bounded context. Consult the installed command's help
69
+ for arguments. Checking and rendering locally do not approve or publish a map.
70
+ Native publication is a Penpot write and needs the normal inspection, pinned
71
+ snapshot, one idempotent write, independent readback and inspected stable export.
72
+ Check the exported typeface against the canvas: a font available to the editor
73
+ can still be unavailable to its exporter. Correct font metadata and identical
74
+ consecutive exports do not prove font fidelity. Visible font substitution needs
75
+ correction before visual approval.
76
+
77
+ The contract is authoritative; Mermaid and native Penpot objects are derived
78
+ views. Managed native edits become a proposal against the exact prior contract
79
+ hash. Review the diff and accept it explicitly before regenerating dependent
80
+ views. A changed label or connector must not silently rewrite accepted intent.
81
+ An old proposal cannot overwrite a newer contract.
82
+
83
+ Owner review is a separate `artifact-review` record containing the artifact
84
+ identity, exact hash, supplied words, identity, date and record hash. Review is
85
+ not authenticated identity. Changed contracts or source hashes need fresh review;
86
+ matching saved inputs do not prove the live file is unchanged.
87
+
88
+ ## Review gates
89
+
90
+ 1. **Intent ready:** the goal, success/recovery paths, evidence and unknowns are
91
+ explicit. Blocking questions are answered or remain visibly blocking.
92
+ 2. **Contract reviewed:** the owner reviewed the exact map and supporting
93
+ decisions. Technical checks and a neat diagram do not supply acceptance.
94
+ 3. **Handoff ready:** the integration map references this exact journey hash,
95
+ required design cases exist, and outstanding implementation work is explicit.
96
+
97
+ ## Reset-password example
98
+
99
+ The [worked example](../examples/design-release-maps/README.md) describes an
100
+ illustrative reset flow. An observed confirmation frame is distinct from a
101
+ proposed submission transition and an unknown expiry policy. The example is
102
+ unapproved and contains no fabricated inspection or owner evidence.
103
+
104
+ Keep loading, empty, error and success states proportionate to the feature.
105
+ Capture keyboard entry, focus movement, error announcements and responsive
106
+ behavior as explicit requirements or unknowns. The map does not establish that
107
+ any of these behaviors has been implemented or tested.
108
+
109
+ Contracts are bounded to 16 MiB, 1,000 nodes and 2,000 edges. Context has a
110
+ 128 KiB limit and reports omissions explicitly. Source paths remain inside the
111
+ project. Labels and descriptions are escaped literal data in generated views.
112
+
113
+ Every local requirement must be connected through node/edge `requirementIds`.
114
+ A readable graph with unmapped requirements remains incomplete for readiness.
115
+ Drawing plans also refuse complete native publication above a 16,384 px axis or
116
+ 64 KiB serialized plan size; split large maps into reviewable parts. Semantic
117
+ native imports retain a bound adjacent `.native.json` observation and require
118
+ fresh live readback during acceptance. Pure geometry changes instead produce a
119
+ presentation proposal, preserve canonical intent and require fresh visual review.
120
+
121
+ For explicit decomposition, use [linked detail maps](integration-maps.md#linked-detail-maps).
122
+ Journey details remain journey contracts, with independent publication and review;
123
+ parent approval does not cover a child map automatically.
124
+
125
+ ## Publication recovery
126
+
127
+ After an uncertain write, inspect the actual target and retained run first. Resume
128
+ the original journal explicitly, keeping the original contract, sources and
129
+ target flags (including whether `--board-id` was omitted):
130
+
131
+ ```sh
132
+ penpair journey-map publish --from design/journeys/reset-password/map.json \
133
+ --page-id PAGE_ID --apply --resume RUN_ID
134
+ ```
135
+
136
+ Use the original `--board-id` and `--projection` options if they were supplied.
137
+ Resume independently reconciles readback and exports; it does not blindly repeat
138
+ the write. Changed input or code signatures must refuse reuse. Inspect again and
139
+ start a new explicitly authorized run when needed; do not bypass those guards.
@@ -0,0 +1,72 @@
1
+ # Reviewed-map workflow example
2
+
3
+ These contracts describe a **synthetic, unapproved** reset-password feature.
4
+ They are valid draft inputs, not a real Penpot inspection, code audit, owner
5
+ review or finalized design release. `sample-source.md` is their only observed
6
+ source. Proposed application architecture and unknown policy stay labeled.
7
+
8
+ From the repository root:
9
+
10
+ ```sh
11
+ pnpm build
12
+ node packages/web/dist/cli.js journey-map render --from examples/design-release-maps/journey.json
13
+ node packages/web/dist/cli.js integration-map render --from examples/design-release-maps/integration.json
14
+ ```
15
+
16
+ Use the installed CLI entrypoint when working from a packaged installation.
17
+ Rendering is offline and establishes neither owner approval nor live fidelity.
18
+ Checks/context should retain the open blockers and missing reviews; this fixture
19
+ is intentionally not ready for implementation or release finalization.
20
+
21
+ ## Read the map
22
+
23
+ ```mermaid
24
+ flowchart LR
25
+ Entry["Enter email"]
26
+ Confirm["Check your email"]
27
+ Error["Retry request"]
28
+ Entry -->|"Success"| Confirm
29
+ Entry -->|"Failure"| Error
30
+ Error -->|"Retry"| Entry
31
+ ```
32
+
33
+ Each JSON node uses a concise `label` as its title, a one-line `summary`, a
34
+ longer `description` for evidence and decisions, and a fixed semantic `icon`.
35
+ Native cards show the compact title/summary with origin badges; detailed notes
36
+ retain the full explanation below the flow. The short labels above do not
37
+ change provenance: entry/confirmation are observations of the synthetic text,
38
+ while success, failure and retry behavior remain proposals. Policy and actual
39
+ code/browser evidence remain unknown.
40
+
41
+ This short illustration explains the intended flow; the JSON contract remains
42
+ authoritative. Generate full Mermaid/native drawing plans from it rather than
43
+ editing this illustration as if it updated the contract. A native drawing is
44
+ editable, but managed semantic changes must be imported as proposals, reviewed,
45
+ accepted against their base hash and approved again.
46
+
47
+ | Requirement | Intended result | Hypothetical mismatch | Next action |
48
+ | --- | --- | --- | --- |
49
+ | `reset-error-retry` | Keep the address after a failed request and allow retry | An implementation clears it and makes the person retype it | Confirm the proposed behavior, then test a real failed request and retry. This row is not observed project evidence. |
50
+
51
+ ## Three review gates
52
+
53
+ 1. **Intent ready:** inspect real source frames; decide request, disclosure,
54
+ expiry, recovery, loading and focus behavior. Do not convert unknown policy
55
+ into an inferred security rule.
56
+ 2. **Design package reviewed:** replace synthetic sources with actual inspection
57
+ paths/hashes, review the exact journey/integration maps, assemble target
58
+ native frames in `release/<product-version>`, verify fidelity, then obtain
59
+ the actual owner words for the exact release candidate. Source image
60
+ approval does not approve new target frame identities.
61
+ 3. **Implementation accepted:** implement against that stable context, observe
62
+ actual route/state/fixture behavior, compare browser images, test recovery
63
+ and accessibility, and review the results. Finalized design alone cannot
64
+ satisfy this gate.
65
+
66
+ No release plan with invented file IDs, frame IDs, transfer receipts or owner
67
+ words is included. Create a plan only after inspecting the actual pinned file
68
+ and selecting sources. The [release guide](../../docs/design-releases.md)
69
+ describes the native copy, immutable evidence and later drift checks.
70
+
71
+ New baselines default to static lossless WebP. Preserve existing approved PNG
72
+ bytes; format migration is explicit and requires normal exact-byte review.
@@ -0,0 +1,181 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "integration-map",
4
+ "id": "reset-password-integration",
5
+ "title": "Reset implementation",
6
+ "goal": "Connect the proposed reset flow to implementation responsibilities without inventing existing code.",
7
+ "sources": [
8
+ {
9
+ "id": "sample-brief",
10
+ "kind": "other",
11
+ "path": "examples/design-release-maps/sample-source.md",
12
+ "sha256": "ab97f0a30f066e9a9f2689d1624608ea91287751cbc6a613542c0e97de187a96"
13
+ }
14
+ ],
15
+ "journey": {
16
+ "path": "examples/design-release-maps/journey.json",
17
+ "sha256": "956fbe3e9d4eb3519d714912ec89bfaedd2a8c2b8882d06dc5ee9c4c279439a9"
18
+ },
19
+ "nodes": [
20
+ {
21
+ "id": "reset-route",
22
+ "label": "Reset route",
23
+ "kind": "route",
24
+ "origin": "proposed",
25
+ "requirementIds": [
26
+ "reset-entry"
27
+ ],
28
+ "summary": "Open /reset-password to begin the flow.",
29
+ "description": "Proposed route responsibility: enter the reset flow using the existing application router. The route and surrounding navigation have not been inspected. Reuse the actual project structure rather than treating this illustrative path as an approved architecture decision.",
30
+ "icon": "code"
31
+ },
32
+ {
33
+ "id": "reset-form",
34
+ "label": "Reset form",
35
+ "kind": "component",
36
+ "origin": "proposed",
37
+ "requirementIds": [
38
+ "reset-entry",
39
+ "reset-error-retry"
40
+ ],
41
+ "summary": "Collect an address and retain it on retry.",
42
+ "description": "Proposed form responsibility: collect the address, follow agreed validation and preserve input during recoverable failure. Inspect existing components before choosing an implementation. Form semantics and behavior need explicit review and real browser evidence.",
43
+ "icon": "screen"
44
+ },
45
+ {
46
+ "id": "reset-mutation",
47
+ "label": "Reset service",
48
+ "kind": "data",
49
+ "origin": "unknown",
50
+ "requirementIds": [
51
+ "reset-request",
52
+ "reset-expiry"
53
+ ],
54
+ "summary": "Agree the request and response contract.",
55
+ "description": "The endpoint, request payload, validation, response policy and expiry behavior are unknown. Obtain the actual project API and product decisions before implementation. This node does not claim that a service exists or prescribe a new backend.",
56
+ "icon": "service"
57
+ },
58
+ {
59
+ "id": "reset-feedback",
60
+ "label": "Request feedback",
61
+ "kind": "state",
62
+ "origin": "proposed",
63
+ "requirementIds": [
64
+ "reset-error-retry",
65
+ "reset-confirmation",
66
+ "reset-focus"
67
+ ],
68
+ "summary": "Show confirmation or a recoverable error.",
69
+ "description": "Proposed feedback responsibility: present the agreed confirmation state after an accepted request and useful recovery after failure. Define focus and announcements with the actual application. The synthetic source does not establish these transitions or their accessibility.",
70
+ "icon": "decision"
71
+ },
72
+ {
73
+ "id": "browser-case",
74
+ "label": "Verify recovery",
75
+ "kind": "verification",
76
+ "origin": "unknown",
77
+ "requirementIds": [
78
+ "reset-error-retry",
79
+ "reset-confirmation",
80
+ "reset-focus",
81
+ "reset-expiry"
82
+ ],
83
+ "summary": "Check retry, focus, and announcements.",
84
+ "description": "Verification is pending. Map approved design cases to actual route/state/fixture setup, then observe failed submission, retained input, retry and confirmation in the real browser. Assess keyboard and screen-reader behavior separately from visual comparison. No provider or result is supplied here.",
85
+ "icon": "check"
86
+ }
87
+ ],
88
+ "edges": [
89
+ {
90
+ "id": "route-form",
91
+ "from": "reset-route",
92
+ "to": "reset-form",
93
+ "label": "Compose",
94
+ "origin": "proposed",
95
+ "requirementIds": [
96
+ "reset-entry"
97
+ ]
98
+ },
99
+ {
100
+ "id": "form-request",
101
+ "from": "reset-form",
102
+ "to": "reset-mutation",
103
+ "label": "Submit",
104
+ "origin": "unknown",
105
+ "requirementIds": [
106
+ "reset-request"
107
+ ]
108
+ },
109
+ {
110
+ "id": "request-feedback",
111
+ "from": "reset-mutation",
112
+ "to": "reset-feedback",
113
+ "label": "Handle result",
114
+ "origin": "proposed",
115
+ "requirementIds": [
116
+ "reset-error-retry"
117
+ ]
118
+ },
119
+ {
120
+ "id": "feedback-evidence",
121
+ "from": "reset-feedback",
122
+ "to": "browser-case",
123
+ "label": "Verify behavior",
124
+ "origin": "proposed",
125
+ "requirementIds": [
126
+ "reset-error-retry"
127
+ ]
128
+ }
129
+ ],
130
+ "requirements": [
131
+ {
132
+ "id": "reset-entry",
133
+ "text": "Proposed: map the entry screen to the project route and existing form conventions after inspecting code.",
134
+ "origin": "proposed"
135
+ },
136
+ {
137
+ "id": "reset-request",
138
+ "text": "Unknown: endpoint, validation and response policy require the actual project contract.",
139
+ "origin": "unknown"
140
+ },
141
+ {
142
+ "id": "reset-error-retry",
143
+ "text": "Proposed: retain the entered address on failure; verify retry and accessible feedback in the real browser.",
144
+ "origin": "proposed"
145
+ },
146
+ {
147
+ "id": "reset-confirmation",
148
+ "text": "Proposed: render confirmation only after the agreed response; final request policy remains unknown.",
149
+ "origin": "proposed"
150
+ },
151
+ {
152
+ "id": "reset-expiry",
153
+ "text": "Unknown: expiry/repeated request behavior must follow the agreed product/security contract.",
154
+ "origin": "unknown"
155
+ },
156
+ {
157
+ "id": "reset-focus",
158
+ "text": "Proposed: implement and verify keyboard focus and announced error recovery.",
159
+ "origin": "proposed"
160
+ }
161
+ ],
162
+ "findings": [
163
+ {
164
+ "id": "code-not-inspected",
165
+ "message": "No application code, route or component identity has been inspected for this synthetic example.",
166
+ "severity": "blocking",
167
+ "status": "open"
168
+ },
169
+ {
170
+ "id": "provider-pending",
171
+ "message": "No approved design cases, actual provider or browser evidence are supplied.",
172
+ "severity": "blocking",
173
+ "status": "open"
174
+ }
175
+ ],
176
+ "presentation": {
177
+ "fontFamily": "Inter"
178
+ },
179
+ "summary": "Connect the reset flow to application responsibilities.",
180
+ "description": "This unapproved integration proposal links the synthetic journey to a route, form, service contract, feedback states and verification work. No application code, API or browser behavior has been inspected. Every proposed responsibility needs project evidence and review before implementation."
181
+ }
@@ -0,0 +1,187 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "kind": "journey-map",
4
+ "id": "reset-password-journey",
5
+ "title": "Reset password",
6
+ "goal": "Request password-reset instructions and understand the next step.",
7
+ "sources": [
8
+ {
9
+ "id": "sample-brief",
10
+ "kind": "other",
11
+ "path": "examples/design-release-maps/sample-source.md",
12
+ "sha256": "ab97f0a30f066e9a9f2689d1624608ea91287751cbc6a613542c0e97de187a96"
13
+ }
14
+ ],
15
+ "nodes": [
16
+ {
17
+ "id": "address-entry",
18
+ "label": "Enter email",
19
+ "kind": "screen",
20
+ "origin": "observed",
21
+ "sourceIds": [
22
+ "sample-brief"
23
+ ],
24
+ "requirementIds": [
25
+ "reset-entry"
26
+ ],
27
+ "summary": "Provide an address for reset instructions.",
28
+ "description": "The synthetic brief includes an address-entry screen. No Penpot frame or application has been inspected. Field validation, submission behavior and the final wording still require project evidence or review.",
29
+ "icon": "screen"
30
+ },
31
+ {
32
+ "id": "confirmation",
33
+ "label": "Check your email",
34
+ "kind": "screen",
35
+ "origin": "observed",
36
+ "sourceIds": [
37
+ "sample-brief"
38
+ ],
39
+ "requirementIds": [
40
+ "reset-confirmation"
41
+ ],
42
+ "summary": "Explain the next step after the request.",
43
+ "description": "The synthetic brief includes a confirmation screen. Its success transition and response messaging are proposed, not observed. Confirm the actual request and account-disclosure policy before implementing this state.",
44
+ "icon": "screen"
45
+ },
46
+ {
47
+ "id": "recoverable-error",
48
+ "label": "Retry request",
49
+ "kind": "state",
50
+ "origin": "proposed",
51
+ "requirementIds": [
52
+ "reset-error-retry"
53
+ ],
54
+ "summary": "Keep the address and offer a retry.",
55
+ "description": "Proposed recovery preserves the entered address after a failed request, explains the failure and allows another attempt. No inspected design or implementation establishes this behavior yet. Review it, then verify a real failure and retry.",
56
+ "icon": "action"
57
+ },
58
+ {
59
+ "id": "expiry-policy",
60
+ "label": "Request policy",
61
+ "kind": "decision",
62
+ "origin": "unknown",
63
+ "requirementIds": [
64
+ "reset-expiry"
65
+ ],
66
+ "summary": "Decide expiry and repeat-request behavior.",
67
+ "description": "The source provides no expiry, repeat-request or disclosure policy. Obtain the actual product and service contract before deciding what the interface should say or do. This is an unresolved requirement, not an inferred security rule.",
68
+ "icon": "decision"
69
+ },
70
+ {
71
+ "id": "focus-recovery",
72
+ "label": "Accessible recovery",
73
+ "kind": "behavior",
74
+ "origin": "proposed",
75
+ "requirementIds": [
76
+ "reset-focus"
77
+ ],
78
+ "summary": "Announce errors and guide keyboard focus.",
79
+ "description": "Proposed accessible recovery should explain failures to screen-reader users and support keyboard correction and retry. Exact focus destinations, announcement behavior and implementation checks remain to be defined and tested.",
80
+ "icon": "check"
81
+ }
82
+ ],
83
+ "edges": [
84
+ {
85
+ "id": "request-success",
86
+ "from": "address-entry",
87
+ "to": "confirmation",
88
+ "label": "Success",
89
+ "origin": "proposed",
90
+ "requirementIds": [
91
+ "reset-confirmation"
92
+ ]
93
+ },
94
+ {
95
+ "id": "request-failure",
96
+ "from": "address-entry",
97
+ "to": "recoverable-error",
98
+ "label": "Failure",
99
+ "origin": "proposed",
100
+ "requirementIds": [
101
+ "reset-error-retry"
102
+ ]
103
+ },
104
+ {
105
+ "id": "retry",
106
+ "from": "recoverable-error",
107
+ "to": "address-entry",
108
+ "label": "Retry",
109
+ "origin": "proposed",
110
+ "requirementIds": [
111
+ "reset-error-retry"
112
+ ]
113
+ },
114
+ {
115
+ "id": "expiry-decision",
116
+ "from": "address-entry",
117
+ "to": "expiry-policy",
118
+ "label": "Resolve policy",
119
+ "origin": "unknown",
120
+ "requirementIds": [
121
+ "reset-expiry"
122
+ ]
123
+ },
124
+ {
125
+ "id": "focus-requirement",
126
+ "from": "recoverable-error",
127
+ "to": "focus-recovery",
128
+ "label": "Accessible feedback",
129
+ "origin": "proposed",
130
+ "requirementIds": [
131
+ "reset-focus"
132
+ ]
133
+ }
134
+ ],
135
+ "requirements": [
136
+ {
137
+ "id": "reset-entry",
138
+ "text": "The synthetic source includes an address-entry screen.",
139
+ "origin": "observed",
140
+ "sourceIds": [
141
+ "sample-brief"
142
+ ]
143
+ },
144
+ {
145
+ "id": "reset-confirmation",
146
+ "text": "The synthetic source includes a confirmation screen; its actual transition is proposed.",
147
+ "origin": "observed",
148
+ "sourceIds": [
149
+ "sample-brief"
150
+ ]
151
+ },
152
+ {
153
+ "id": "reset-error-retry",
154
+ "text": "Proposed: preserve the entered address after a failed request and allow retry.",
155
+ "origin": "proposed"
156
+ },
157
+ {
158
+ "id": "reset-expiry",
159
+ "text": "Unknown: expiry policy and treatment of repeated requests require a product/security decision.",
160
+ "origin": "unknown"
161
+ },
162
+ {
163
+ "id": "reset-focus",
164
+ "text": "Proposed: focus and announced errors must support keyboard and screen-reader recovery.",
165
+ "origin": "proposed"
166
+ }
167
+ ],
168
+ "findings": [
169
+ {
170
+ "id": "decide-request-contract",
171
+ "message": "No actual API contract or account-disclosure policy has been supplied.",
172
+ "severity": "blocking",
173
+ "status": "open"
174
+ },
175
+ {
176
+ "id": "design-error-state",
177
+ "message": "Recovery and accessible error presentation are proposals; no inspected frame supports them.",
178
+ "severity": "warning",
179
+ "status": "open"
180
+ }
181
+ ],
182
+ "presentation": {
183
+ "fontFamily": "Inter"
184
+ },
185
+ "summary": "Request reset instructions and recover from failure.",
186
+ "description": "This unapproved planning fixture is grounded only in the synthetic brief. It illustrates the proposed reset flow without claiming inspected Penpot frames, actual application behavior or owner agreement. Request policy, recovery and accessibility behavior remain explicit decisions and verification work."
187
+ }
@@ -0,0 +1,8 @@
1
+ # Synthetic reset-password source
2
+
3
+ This is illustrative fixture text, not a Penpot inspection or approved product
4
+ requirement. It supplies reproducible local source bytes for the map examples.
5
+
6
+ The sample includes an address-entry screen and a confirmation screen.
7
+ The intended user goal is to request password-reset instructions.
8
+ No API, expiry policy, error recovery or focus behavior is established here.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@penpair/web",
3
- "version": "0.3.7",
3
+ "version": "0.4.0",
4
4
  "description": "The penpair command for Penpot on the web (design.penpot.app or your team's own): guided setup, a safe write protocol, skills for AI agents. Includes everything in @penpair/core.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -48,7 +48,7 @@
48
48
  "access": "public"
49
49
  },
50
50
  "dependencies": {
51
- "@penpair/core": "0.3.7"
51
+ "@penpair/core": "0.4.0"
52
52
  },
53
53
  "scripts": {
54
54
  "build": "tsc -p tsconfig.json",