@penpair/web 0.3.7 → 0.4.1
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 +1 -1
- package/docs/approvals.md +33 -2
- package/docs/artifacts.md +23 -3
- package/docs/browser-sessions.md +147 -0
- package/docs/compatibility.md +4 -2
- package/docs/copy-between-pages.md +54 -0
- package/docs/coverage.md +20 -4
- package/docs/design-releases.md +164 -0
- package/docs/guide.md +20 -3
- package/docs/handoff.md +1 -1
- package/docs/integration-maps.md +171 -0
- package/docs/journey-maps.md +139 -0
- package/docs/layout-settling.md +17 -1
- package/docs/mcp.md +2 -0
- package/docs/run-file.md +2 -0
- package/docs/troubleshooting.md +1 -1
- package/examples/design-release-maps/README.md +72 -0
- package/examples/design-release-maps/integration.json +181 -0
- package/examples/design-release-maps/journey.json +187 -0
- package/examples/design-release-maps/sample-source.md +8 -0
- package/package.json +2 -2
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
|
|
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,
|
|
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.
|
|
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,
|
|
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`.
|
|
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
|
-
|
|
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.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Recovering a suspended Penpot tab
|
|
2
|
+
|
|
3
|
+
Penpair executes live design operations through the Penpot browser plugin. A
|
|
4
|
+
reachable Penpot server does not establish that its editor tab is responsive.
|
|
5
|
+
Browser timer throttling, freezing and discarding are separate from computer
|
|
6
|
+
sleep; keeping the computer awake does not prevent them.
|
|
7
|
+
|
|
8
|
+
## Wake an existing tab
|
|
9
|
+
|
|
10
|
+
From the configured project, with an owner-confirmed file pin and an existing
|
|
11
|
+
MCP connection:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
penpair wake --debug-port 9222
|
|
15
|
+
penpair wake --debug-port 9222 --timeout-ms 10000 --json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The browser must already expose a debugging endpoint on `127.0.0.1` at the
|
|
19
|
+
specified port. Penpair does not start a browser or enable debugging in an
|
|
20
|
+
existing browser. `--timeout-ms` accepts 1000–30000; the default 5000 ms budget
|
|
21
|
+
covers activation and verification together.
|
|
22
|
+
|
|
23
|
+
Penpair lists the browser's targets, requires exactly one editor page matching
|
|
24
|
+
both the project's Penpot origin and pinned `file-id`, then requests activation
|
|
25
|
+
through `/json/activate/<target-id>`. It refuses duplicate matching tabs rather
|
|
26
|
+
than choosing one. Hosted deployments require an exact origin. For a local
|
|
27
|
+
instance, both `http://127.0.0.1:<frontend-port>` and
|
|
28
|
+
`http://localhost:<frontend-port>` are accepted, matching Penpair's existing
|
|
29
|
+
opening commands. Other hosts and ports are refused; duplicate matching tabs
|
|
30
|
+
across the two aliases still fail.
|
|
31
|
+
|
|
32
|
+
After activation, Penpair performs a fresh read with a challenge and checks the
|
|
33
|
+
actual file ID. Exit 0 means activation was acknowledged and that read verified
|
|
34
|
+
the intended editor. Activation can succeed while readiness fails: JSON then
|
|
35
|
+
reports `activated: true, ready: false`. A browser may also leave a minimized
|
|
36
|
+
window minimized; check the actual window when activation alone is insufficient.
|
|
37
|
+
|
|
38
|
+
This command does not reload, navigate, restart the plugin, inject page code or
|
|
39
|
+
resume a write. Its `keepAlive: "none"` result means activation is momentary
|
|
40
|
+
recovery, with no sustained keep-alive mechanism. Browser focus can affect legacy
|
|
41
|
+
MCP routing, which is why a fresh identity check follows activation. A target can
|
|
42
|
+
navigate between discovery and activation; the acknowledgment is not an atomic
|
|
43
|
+
guarantee of tab identity or exclusive ownership.
|
|
44
|
+
|
|
45
|
+
If a write became uncertain, retain its journal, inspect the intended target
|
|
46
|
+
independently and reconcile it before explicitly resuming. Waking a tab does not
|
|
47
|
+
establish whether the previous write completed. See [recovery](recovery.md).
|
|
48
|
+
|
|
49
|
+
## Understand doctor evidence
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
penpair doctor --json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Doctor stays read-only: it does not activate tabs or install a keep-alive.
|
|
56
|
+
The execution check exposes `evidence.pluginHealth`:
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"state": "suspended",
|
|
61
|
+
"heartbeatAgeMs": 36000,
|
|
62
|
+
"heartbeatSource": "upstream-error",
|
|
63
|
+
"keepAlive": { "method": "unknown", "verified": false }
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Field | Meaning |
|
|
68
|
+
| --- | --- |
|
|
69
|
+
| `state` | `responsive` after a verified fresh read; `suspended` when the upstream failure explicitly reports suspension; otherwise `unknown`. |
|
|
70
|
+
| `heartbeatAgeMs` | Age reported in an upstream error such as `no heartbeat for 36s`; `null` when unavailable. |
|
|
71
|
+
| `heartbeatSource` | `upstream-error` when an age was parsed, otherwise `unavailable`. |
|
|
72
|
+
| `keepAlive` | The current connection does not expose independently verified browser keep-alive telemetry, so its method remains `unknown` and `verified` remains `false`. |
|
|
73
|
+
|
|
74
|
+
The check's `observedAt` records when Penpair observed this evidence. A successful
|
|
75
|
+
read does not imply a zero-second heartbeat age or prove that the next background
|
|
76
|
+
request will succeed. A generic timeout does not establish suspension.
|
|
77
|
+
|
|
78
|
+
## Browser settings and a dedicated window
|
|
79
|
+
|
|
80
|
+
Chrome's **Settings → Performance → Always keep these sites active** exclusion
|
|
81
|
+
can prevent Memory Saver from deactivating the configured Penpot site. It does
|
|
82
|
+
not establish that background timers or Penpot layout will run without delay.
|
|
83
|
+
See [Chrome's performance settings](https://support.google.com/chrome/answer/12929150?hl=en).
|
|
84
|
+
|
|
85
|
+
For an explicit experiment, start a separate Chrome instance with its own profile
|
|
86
|
+
outside the repository. This macOS example uses an installed Google Chrome:
|
|
87
|
+
|
|
88
|
+
```sh
|
|
89
|
+
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
|
|
90
|
+
--user-data-dir="$HOME/Library/Application Support/Penpair/browser-profile" \
|
|
91
|
+
--remote-debugging-address=127.0.0.1 \
|
|
92
|
+
--remote-debugging-port=9222 \
|
|
93
|
+
--disable-background-timer-throttling \
|
|
94
|
+
--disable-renderer-backgrounding \
|
|
95
|
+
--disable-backgrounding-occluded-windows \
|
|
96
|
+
--new-window https://design.penpot.app
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Use the project's configured origin for a different deployment. These launch
|
|
100
|
+
switches do not retrofit an already running ordinary browser instance. Since
|
|
101
|
+
Chrome 136, remote debugging requires a non-default user-data directory. Keep
|
|
102
|
+
that profile private: it can retain account cookies and cached designs. Keep the
|
|
103
|
+
debugging port on loopback and do not expose it on a network. See
|
|
104
|
+
[Chrome's debugging changes](https://developer.chrome.com/blog/remote-debugging-port/).
|
|
105
|
+
|
|
106
|
+
Login and plugin connection in that dedicated profile remain human setup steps;
|
|
107
|
+
Penpair does not copy credentials or complete them automatically. The flags above
|
|
108
|
+
are an experimental configuration, not a verified guarantee of background
|
|
109
|
+
Penpot operation. No managed-browser lifecycle, browser dependency or browser
|
|
110
|
+
download is added by the wake command.
|
|
111
|
+
|
|
112
|
+
A silent audio track does **not** count for Chrome's audio timer-throttling
|
|
113
|
+
exemption. Web Workers, SharedWorkers and Web Locks have different lifecycle
|
|
114
|
+
behavior, but are not verified here as a complete solution for the Penpot plugin.
|
|
115
|
+
Penpair does not inject these workarounds. See
|
|
116
|
+
[Chrome timer throttling](https://developer.chrome.com/blog/timer-throttling-in-chrome-88)
|
|
117
|
+
and [the Page Lifecycle API](https://developer.chrome.com/docs/web-platform/page-lifecycle-api).
|
|
118
|
+
|
|
119
|
+
## Verification status and the remaining acceptance trial
|
|
120
|
+
|
|
121
|
+
The initial manual trial raised an existing Penpot window through native browser
|
|
122
|
+
controls without reloading it. Port 9222 was unavailable, and the chat's MCP
|
|
123
|
+
connection reported no connected Penpot instance. That trial therefore did not
|
|
124
|
+
prove restored plugin heartbeats. A separate real CDP trial used the installed
|
|
125
|
+
Brave browser with a temporary unauthenticated profile and the three background
|
|
126
|
+
flags above. The wake implementation changed a disposable local fixture page
|
|
127
|
+
from hidden to visible. No Penpot account or design was loaded, so this proves
|
|
128
|
+
browser activation only. Automated tests also cover target selection, activation
|
|
129
|
+
requests, deadlines and refusal paths using fake and local HTTP endpoints.
|
|
130
|
+
|
|
131
|
+
The required sustained-background acceptance trial remains **unverified**. Use an
|
|
132
|
+
owner-authorized disposable file and the intended installed browser build:
|
|
133
|
+
|
|
134
|
+
1. Record the browser version, launch flags, profile, settings and initial doctor
|
|
135
|
+
evidence. Verify the file pin and establish a normal responsive connection.
|
|
136
|
+
2. Run 30 frames, one journaled write per frame, each lasting 20–60 seconds, with
|
|
137
|
+
the Penpot window behind another window. Do not manually focus or reload it.
|
|
138
|
+
3. Independently read back the results, export and visually inspect them. Record
|
|
139
|
+
heartbeat failures, layout timeouts and any required intervention.
|
|
140
|
+
4. Test minimized, occluded and screen-locked states separately; success in one
|
|
141
|
+
state does not establish the others. Computer sleep is a separate boundary.
|
|
142
|
+
5. If any write becomes uncertain, stop and reconcile before replay. Do not
|
|
143
|
+
increase retries or weaken journal, identity or layout checks to pass.
|
|
144
|
+
|
|
145
|
+
One successful activation is not a passing 30-frame run. The dedicated-browser
|
|
146
|
+
configuration and any future plugin keep-alive require that real acceptance
|
|
147
|
+
trial before they can be advertised as reliable background execution.
|
package/docs/compatibility.md
CHANGED
|
@@ -12,9 +12,11 @@ What has been checked, where, and what differs. Update it whenever a Penpot is p
|
|
|
12
12
|
| Deep check (`probe --deep --write`): flex layout, text, token set and binding, components and swap, clone, PNG export, clean-up | all steps pass, nothing left behind (run on a fresh instance's empty file) | all steps pass, nothing left behind (run twice) |
|
|
13
13
|
| `pages`, `tree`, `audit`, `tokens dump`, `export` (waits for a stable render), `archive` with `--apply`, a project snippet that writes (`run seed-demo`) | all in use | all verified on seeded sample content |
|
|
14
14
|
| `doctor` (external profile) | ok | ok |
|
|
15
|
+
| Cross-page clone followed by `appendChild` / `insertChild` | refused with either page active; scratch cleanup verified ([details](copy-between-pages.md)) | not tried |
|
|
15
16
|
| `init`, `up`, `doctor` for a Penpot penpair starts (a fresh instance on its own port, then the owner's account, key and plugin connection) | ok: 16 seconds to answer with the images cached; `doctor` all ok | not applicable |
|
|
16
17
|
|
|
17
18
|
## Differences found
|
|
19
|
+
- **Cross-page reparenting is refused on local 2.18.3.** Clones stay on the source page. Use native editor copy/paste; see [copying between pages](copy-between-pages.md).
|
|
18
20
|
- **A new token set starts inactive, with no themes in the file.** Seen on both the hosted Penpot and a fresh local instance. (An earlier note that local sets start active was wrong or applied to a file with themes.)
|
|
19
21
|
- **A token binding is recorded after the call, not during it.** On the hosted Penpot it appeared after about half a second to a few seconds; reading it straight away shows nothing. Code that checks a binding has to wait for it.
|
|
20
22
|
- **Applying the same token again removes the binding** (seen on the hosted Penpot, as in the local notes).
|
|
@@ -42,11 +44,11 @@ The `Scratch Penpot compatibility smoke` workflow is manual-dispatch only. Confi
|
|
|
42
44
|
|
|
43
45
|
Library helpers accept the project's guarded client:
|
|
44
46
|
|
|
45
|
-
- `waitForStableLayout(client, { shapeId, timeoutMs })`: prefers supported native completion; otherwise polls geometry.
|
|
47
|
+
- `waitForStableLayout(client, { shapeId, timeoutMs })`: prefers supported native completion; otherwise polls geometry. In `auto` mode a failed native wait can fall back to geometry. Budget overflow reports explicitly partial evidence; target changes, malformed samples and timeout cannot pass. Geometry stability does not establish token/font/pixel correctness; independent readback and stable exports remain necessary. Default operation/export and token verification paths use this helper; see [layout settling](layout-settling.md).
|
|
46
48
|
- `validateFileIntegrity(client)`: distinguishes unsupported from a valid empty issue list or actual referential-integrity problems.
|
|
47
49
|
- `createCheckpoint(client, { label, runId }, { apply })`: optional native version history. Without apply it inspects; with apply it saves a run-keyed named version once and independently finds it again. Reusing that run ID reuses an existing checkpoint; it does not provide atomic cross-client locking. No automatic restore/rollback is performed.
|
|
48
50
|
|
|
49
|
-
Current upstream references: [Penpot API](https://doc.plugins.penpot.app/interfaces/Penpot), [File API](https://doc.plugins.penpot.app/interfaces/File), [FileVersion API](https://doc.plugins.penpot.app/interfaces/FileVersion). Unit fixtures cover missing, present and throwing APIs.
|
|
51
|
+
Current upstream references: [Penpot API](https://doc.plugins.penpot.app/interfaces/Penpot), [File API](https://doc.plugins.penpot.app/interfaces/File), [FileVersion API](https://doc.plugins.penpot.app/interfaces/FileVersion). Unit fixtures cover missing, present and throwing APIs. A read-only layout check on local 2.18.3 on 2026-10-25 observed two existing boards: three equal geometry samples covered 192 nodes; a five-node budget returned `method: "partial"`, `nodeCount: 5`, `truncated: true` and both requested root IDs. This does not prove a live write on a page over 10,000 nodes, native wait timing, integrity validation or version-history behavior. Those remain unverified.
|
|
50
52
|
|
|
51
53
|
## Hosted archive candidate
|
|
52
54
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Copy boards between pages
|
|
2
|
+
|
|
3
|
+
Penpair currently cannot copy or move an existing board between pages through
|
|
4
|
+
the Penpot plugin API. There is no `copy-boards` command. Use native copy/paste
|
|
5
|
+
in the Penpot editor, leaving the originals in place, then independently inspect
|
|
6
|
+
the destination and compare stable exports. Keep component references, token
|
|
7
|
+
bindings, sizes and child counts intact. See [design release assembly](design-releases.md)
|
|
8
|
+
for its additional fidelity requirements.
|
|
9
|
+
|
|
10
|
+
## Tested API boundary
|
|
11
|
+
|
|
12
|
+
A disposable two-page probe on local Penpot 2.18.3 on 2026-10-25 tested a board
|
|
13
|
+
with one rectangle child. Each `clone()` stayed on the source page. Appending or
|
|
14
|
+
inserting the clone into a board on the other page failed with:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
Cannot modify a page that is not currently active
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Both `appendChild(clone)` and `insertChild(0, clone)` failed with either the
|
|
21
|
+
source page or the destination page active. Independent readback found both
|
|
22
|
+
clones on the source page, no destination children, and the original board and
|
|
23
|
+
child unchanged. Two destination exports were inspected and showed the empty
|
|
24
|
+
board. Both disposable pages were removed; a separate read confirmed their
|
|
25
|
+
absence and the original pages' names and top-level shape IDs were preserved.
|
|
26
|
+
This establishes a local API limitation, not hosted-version or component/token
|
|
27
|
+
copy fidelity.
|
|
28
|
+
|
|
29
|
+
The [upstream shape implementation](https://github.com/penpot/penpot/blob/develop/frontend/src/app/plugins/shape.cljs)
|
|
30
|
+
checks that both parent and child pages are active before either structural
|
|
31
|
+
operation. The [Board API](https://doc.plugins.penpot.app/interfaces/Board)
|
|
32
|
+
provides cloning and reparenting, but no cross-page transfer operation. The
|
|
33
|
+
[File API](https://doc.plugins.penpot.app/interfaces/File) exposes export,
|
|
34
|
+
validation and history, without a file-change submission method.
|
|
35
|
+
|
|
36
|
+
## File-change route
|
|
37
|
+
|
|
38
|
+
Penpair's connection calls the editor's MCP tools; it has no authenticated
|
|
39
|
+
Penpot backend file-change client or supported plugin method for submitting raw
|
|
40
|
+
file changes. Internal `$page` metadata does not provide that capability.
|
|
41
|
+
Changing a proxy's internals, reconstructing a board from its visible properties,
|
|
42
|
+
or patching backend file objects would not establish preserved component
|
|
43
|
+
overrides, token bindings, references, undo behavior or revision coordination.
|
|
44
|
+
|
|
45
|
+
There is therefore no safe file-change route in the current implementation.
|
|
46
|
+
Automatic cross-page copying stops at this boundary. A future supported transfer
|
|
47
|
+
API or separately validated native editor integration would need exact source
|
|
48
|
+
selection, a dry run, journaled recovery, independent fidelity readback and stable
|
|
49
|
+
exports before shipping a copy command. Moving must remain explicit.
|
|
50
|
+
|
|
51
|
+
For large pages, scope a writer's layout observation with repeatable
|
|
52
|
+
`--layout-root <shape-id>` and, when needed, `--layout-max-nodes <n>`; see
|
|
53
|
+
[layout settling](layout-settling.md). Reducing the observation scope does not
|
|
54
|
+
add a cross-page copy capability.
|
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.
|
|
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
|
|
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
|
|
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,164 @@
|
|
|
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
|
+
The [cross-page API assessment](copy-between-pages.md) records the tested
|
|
66
|
+
boundary and why there is no automatic file-change fallback.
|
|
67
|
+
Preserve visual fidelity, native components, token bindings, text and layout;
|
|
68
|
+
do not recreate frames from screenshots or normalize their dimensions. A source
|
|
69
|
+
using auto height must retain it. Floating-point geometry noise is not a reason
|
|
70
|
+
to change source sizing.
|
|
71
|
+
|
|
72
|
+
The live sequence is read-only inspection, pinned snapshot, one idempotent write,
|
|
73
|
+
independent readback and stable exports that are looked at. Snippets stay under
|
|
74
|
+
90 KB. If a write response is uncertain, inspect what exists before retrying.
|
|
75
|
+
Keep unmanaged content intact; a page name alone cannot authorize replacement.
|
|
76
|
+
Do not copy via a lossy or structurally incomplete fallback when native fidelity
|
|
77
|
+
cannot be established: report the missing proof.
|
|
78
|
+
|
|
79
|
+
## Supported native verification limits
|
|
80
|
+
|
|
81
|
+
Native verification is deliberately narrower than native copy/paste. Selecting a
|
|
82
|
+
screen for this workflow does not establish that its content can be verified.
|
|
83
|
+
The current verifier blocks these cases:
|
|
84
|
+
|
|
85
|
+
- **All component instances, copies, main instances and component roots.** The
|
|
86
|
+
inspection cannot establish complete override state. Retaining links during a
|
|
87
|
+
copy is required, but it does not prove override fidelity.
|
|
88
|
+
- **Groups without an explicit `maskedGroup` observation.** An absent mask flag
|
|
89
|
+
is unknown, not proof that the group is unmasked.
|
|
90
|
+
- **Unknown text range/style observations**, including unobserved mixed-range
|
|
91
|
+
state, and any other unknown or missing required inspection field.
|
|
92
|
+
- **Paths/vector shapes whose actual path content was not observed.** A path
|
|
93
|
+
type or matching raster export cannot establish the native path data.
|
|
94
|
+
- Unsupported shape content, incomplete hierarchy, unobservable interactions or
|
|
95
|
+
missing pinned source render/dependency evidence.
|
|
96
|
+
|
|
97
|
+
A selected screen containing one of these cases remains blocked with a reason;
|
|
98
|
+
it cannot be finalized by waiving the finding or accepting equal pixels. Retain
|
|
99
|
+
its native structure and source evidence, then obtain the missing observation or
|
|
100
|
+
extend and validate inspection support before retrying. Do not detach components,
|
|
101
|
+
flatten groups, remove styles or substitute raster images to bypass the gate.
|
|
102
|
+
Simple screens whose required fields are fully observed can be assessed, but
|
|
103
|
+
that is not a claim of complete support for every native Penpot feature.
|
|
104
|
+
|
|
105
|
+
## Verify the package
|
|
106
|
+
|
|
107
|
+
Supply a project-owned receipt list with explicit `sourceId` and
|
|
108
|
+
`targetFrameId` values to `verify --receipts FILE`. Check release frame identities
|
|
109
|
+
against the selected sources and retain immutable inspection/export evidence
|
|
110
|
+
with exact paths and hashes. Compare source and release frames independently,
|
|
111
|
+
including component/token identity and sizing
|
|
112
|
+
behavior, and inspect stable exports. A current rendering cannot rewrite the
|
|
113
|
+
bytes of an already recorded artifact. Native release objects remain mutable,
|
|
114
|
+
including changes inherited through shared components and tokens. A later change
|
|
115
|
+
is drift and needs a new reviewed generation, not a silent refresh of finalized
|
|
116
|
+
evidence.
|
|
117
|
+
|
|
118
|
+
New baseline evidence uses lossless WebP. Existing PNG baselines and image
|
|
119
|
+
approvals remain valid in their original format. Conversion must be a deliberate
|
|
120
|
+
migration with new evidence and review; never change approved bytes in place.
|
|
121
|
+
Penpot can supply PNG during export, so conversion must retain exact dimensions
|
|
122
|
+
and pixels. Quality 100 alone does not establish lossless encoding.
|
|
123
|
+
|
|
124
|
+
The release needs matching exact-artifact review records and no unresolved
|
|
125
|
+
blocking findings. Preserve the distinction between:
|
|
126
|
+
|
|
127
|
+
- Map review: the owner's supplied words bind an exact contract hash.
|
|
128
|
+
- Design image approval: the existing exact case/image approval protocol.
|
|
129
|
+
- Technical verification: independently observed structure and image evidence.
|
|
130
|
+
- Implementation acceptance: actual application behavior, browser evidence and
|
|
131
|
+
owner review, assessed separately after implementation.
|
|
132
|
+
|
|
133
|
+
## Finalize and continue
|
|
134
|
+
|
|
135
|
+
The release record progresses from `planned` through `awaiting-copy` and
|
|
136
|
+
`verified` to `final`. Finalization records the reviewed release identity and
|
|
137
|
+
immutable evidence. It must not promote missing evidence, waivers or inferred owner approval into a
|
|
138
|
+
successful design gate. Finalized means the design package is ready to guide
|
|
139
|
+
implementation; it does not mean the application has been built or checked.
|
|
140
|
+
|
|
141
|
+
Finalization previews by default. Only use `finalize --apply --by OWNER --note
|
|
142
|
+
EXACT_WORDS` after the owner has supplied those words for the exact candidate.
|
|
143
|
+
Keep journal/run identity when reconciling interrupted remote work.
|
|
144
|
+
|
|
145
|
+
Run checks again before using the package later. Missing or modified artifacts,
|
|
146
|
+
changed map/source hashes and changed release evidence must remain visible as
|
|
147
|
+
drift or incomplete evidence. A saved-source check does not contact Penpot;
|
|
148
|
+
live freshness requires an explicit fresh observation. Context must identify
|
|
149
|
+
its verification scope and any truncation. Release context includes actual frozen
|
|
150
|
+
`mapContexts`, exact release-page `mapTargets`, independently pending implementation
|
|
151
|
+
status and next actions. Linked detail navigation targets the release's own child
|
|
152
|
+
boards; each child contract retains its separate review requirement.
|
|
153
|
+
Its `proposedBaselines` is a configuration fragment with the release page, WebP
|
|
154
|
+
format, baseline root and explicit target `frameId` entries. Review and merge it
|
|
155
|
+
into the existing `baselines` block; context does not rewrite project config or
|
|
156
|
+
approve the new target image identities.
|
|
157
|
+
|
|
158
|
+
Use `penpair-handoff` with the approved integration context, then `penpair-verify`
|
|
159
|
+
for the actual frontend. Keep those later implementation results separately;
|
|
160
|
+
never rewrite the finalized design to match an implementation failure.
|
|
161
|
+
|
|
162
|
+
The [reset-password example](../examples/design-release-maps/README.md) is an
|
|
163
|
+
unapproved fixture showing decisions and gaps. It is not a finalized release or
|
|
164
|
+
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
|
|
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;
|
|
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.
|
|
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.
|