@yargote/artifacts-cli 0.1.0-beta.5 → 0.1.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -9
- package/package.json +3 -3
- package/skills/artifacts-cli/SKILL.md +16 -4
- package/skills/artifacts-cli/references/commands.md +40 -1
- package/skills/artifacts-cli/references/verify-retained.md +119 -0
- package/skills/artifacts-cli/scripts/prepare-retained.mjs +149 -0
- package/skills/artifacts-design/SKILL.md +12 -2
- package/skills/artifacts-design/references/canvas.md +100 -0
- package/skills/artifacts-design/references/local-checks.md +70 -0
- package/skills/artifacts-design/scripts/embed-fonts.mjs +208 -7
- package/skills/artifacts-design/scripts/verify-local.mjs +333 -0
- package/src/index.js +14 -3
- package/src/operations.d.ts +48 -2
- package/src/operations.js +7 -1
package/README.md
CHANGED
|
@@ -11,9 +11,9 @@ Use Node 24 or later. The beta targets macOS and Linux. Windows has not been ver
|
|
|
11
11
|
Choose one persistent installation:
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
npm install --global @yargote/artifacts-cli@0.1.0-beta.
|
|
14
|
+
npm install --global @yargote/artifacts-cli@0.1.0-beta.7
|
|
15
15
|
# Or, with pnpm and an existing global bin directory on PATH:
|
|
16
|
-
pnpm add --global @yargote/artifacts-cli@0.1.0-beta.
|
|
16
|
+
pnpm add --global @yargote/artifacts-cli@0.1.0-beta.7
|
|
17
17
|
artifacts --version
|
|
18
18
|
```
|
|
19
19
|
|
|
@@ -22,18 +22,18 @@ If pnpm reports that no global bin directory exists, run `pnpm setup` and reopen
|
|
|
22
22
|
For one-off execution, pin the same version:
|
|
23
23
|
|
|
24
24
|
```sh
|
|
25
|
-
npx --yes @yargote/artifacts-cli@0.1.0-beta.
|
|
26
|
-
pnpm dlx @yargote/artifacts-cli@0.1.0-beta.
|
|
25
|
+
npx --yes @yargote/artifacts-cli@0.1.0-beta.7 --help
|
|
26
|
+
pnpm dlx @yargote/artifacts-cli@0.1.0-beta.7 --help
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
`@beta` follows future prereleases. Exact versions remain reproducible. No stable `latest` release is part of this beta. An organization's pnpm minimum release age or trust policy can delay a new release. Follow that policy rather than disabling it globally.
|
|
30
30
|
|
|
31
31
|
## Complete first use
|
|
32
32
|
|
|
33
|
-
An existing Artifacts account is required. Select the
|
|
33
|
+
An existing Artifacts account is required. Select the REST OAuth resource, which is the API origin without `/mcp`. When upgrading an older deployment, complete the [API migration](../../docs/deployment.md#migrate-the-api-hostname) before using the address below.
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
export ARTIFACTS_SERVER='https://artifacts-
|
|
36
|
+
export ARTIFACTS_SERVER='https://artifacts-api.yersonargotev.workers.dev'
|
|
37
37
|
artifacts login
|
|
38
38
|
artifacts whoami
|
|
39
39
|
```
|
|
@@ -64,7 +64,7 @@ artifacts export ARTIFACT_ID --revision REVISION_ID --out artifact.zip
|
|
|
64
64
|
artifacts logout
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
A publication URL requires sign-in. It opens for the owner and for a user whose verified email equals an email share of the artifact. That user sees only the published revision and its attachments. A retained request ID appears on stderr before each mutation. If the response is lost, run `artifacts retry REQUEST_ID` with the same server and state directory. Keep that state directory when upgrading, reinstalling, or switching between global and one-off execution.
|
|
68
68
|
|
|
69
69
|
## Upgrade or uninstall
|
|
70
70
|
|
|
@@ -82,7 +82,9 @@ Run `artifacts logout` before uninstalling if you want to clear credentials. Uni
|
|
|
82
82
|
|
|
83
83
|
Skills, help, and version commands work without a server or login. Skills commands do not read credentials or create private state. They accept and ignore global `--server` and `--state-dir` values, including their environment defaults.
|
|
84
84
|
|
|
85
|
-
`--server RESOURCE` selects the exact canonical OAuth resource. `ARTIFACTS_SERVER` supplies the same value.
|
|
85
|
+
`--server RESOURCE` selects the exact canonical REST OAuth resource. `ARTIFACTS_SERVER` supplies the same value. Use the configured `API_URL` origin to call REST under `/v1`. The CLI has no production default and does not rewrite the supplied URL. Older deployments without `API_URL` still use their complete MCP resource and routes under `/mcp/v1`.
|
|
86
|
+
|
|
87
|
+
A hostname change requires a new `artifacts login --server RESOURCE`. Credentials and retained operations remain bound to their exact original resource. Do not move an old operation file to the new resource or redirect its bearer requests.
|
|
86
88
|
|
|
87
89
|
`--state-dir DIR` overrides `ARTIFACTS_STATE_DIR`. The default is `~/.local/state/artifacts`. The state directory must be private and owned by the current user. Credentials are separate from operation files. Profiles bind credentials to the exact resource. Retained operation paths hash the resource and request ID, so request IDs cannot select filesystem paths.
|
|
88
90
|
|
|
@@ -124,6 +126,9 @@ artifacts publish [ID] --command FILE
|
|
|
124
126
|
artifacts export ID --revision REV --out FILE
|
|
125
127
|
artifacts move ID --project NAME [--request-id ID]
|
|
126
128
|
artifacts move ID --no-project [--request-id ID]
|
|
129
|
+
artifacts share ID --email EMAIL [--notify] [--request-id ID]
|
|
130
|
+
artifacts shares ID
|
|
131
|
+
artifacts unshare ID SHARE_ID [--request-id ID]
|
|
127
132
|
artifacts retry REQUEST_ID
|
|
128
133
|
artifacts skills [list]
|
|
129
134
|
artifacts skills get NAME [--full]
|
|
@@ -197,6 +202,27 @@ Every artifact read and list item includes `project`, a name or null. `projects`
|
|
|
197
202
|
|
|
198
203
|
Version `0.1.0-beta.4` and earlier parse artifact responses strictly and fail with `invalid_response` against a server that returns `project`. Upgrade the CLI together with the server.
|
|
199
204
|
|
|
205
|
+
## Share publication access
|
|
206
|
+
|
|
207
|
+
These commands are available from `0.1.0-beta.6`. Check installed help for support.
|
|
208
|
+
|
|
209
|
+
```sh
|
|
210
|
+
artifacts share ARTIFACT_ID --email ana@example.com --request-id share-ana-001
|
|
211
|
+
artifacts shares ARTIFACT_ID
|
|
212
|
+
artifacts unshare ARTIFACT_ID SHARE_ID --request-id unshare-ana-001
|
|
213
|
+
artifacts retry share-ana-001
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
`share` trims and lowercases the email and prints one share as JSON with `shareId`, `artifactId`, `kind: "email"`, `value`, and `createdAt`. `shares` prints `{ "items": [...] }` with current grants and retains no operation. Take `SHARE_ID` from that output. `unshare` and a successful retry of an unshare exit zero with empty stdout. All three commands require the owner's `artifacts:publish` scope.
|
|
217
|
+
|
|
218
|
+
A signed-in user whose verified email matches a share can view the published revision and its attachments. History, export, edits, and project moves remain owner-only. Shares created before first publication remain stored, but the URL opens only after publication. Add `--notify` to request one invitation email. Invited people can sign in with an email code when the server enables email.
|
|
219
|
+
|
|
220
|
+
Share and unshare retain the complete normalized command before submission. Recover an uncertain result with `retry REQUEST_ID`. A replay confirms the original operation, so a historical share replay does not restore a revoked grant. An old unshare replay leaves a deliberate new share intact. Run `shares ARTIFACT_ID` to verify current grants.
|
|
221
|
+
|
|
222
|
+
Share with `--notify` to request one email attempt. Access succeeds when mail is disabled or the owner has used the daily allowance. The JSON result adds `notification.status` as `requested` or `skipped`, with `disabled` or `rate_limited` as the skip reason. `requested` does not confirm delivery. A crash after commit can lose the message. An exact retry preserves the captured flag and never sends a second email. Use a new deliberate command to request another attempt for existing access.
|
|
223
|
+
|
|
224
|
+
Owners can reserve 100 invitations per UTC day. Revoke, deletion, and failed delivery do not refund reservations. New access is limited to 100 people per artifact and 1000 grants per owner. Existing access remains listable and revocable.
|
|
225
|
+
|
|
200
226
|
## Complete commands and edits
|
|
201
227
|
|
|
202
228
|
`--command FILE` accepts a complete create, update, or publish command. It cannot be combined with other mutation options. For update and publish, an optional positional artifact ID must equal the command's `artifactId`. Every field, including the omission of `response`, is retained.
|
|
@@ -228,7 +254,7 @@ Publish always selects an immutable revision. `--expected-version 0` expects no
|
|
|
228
254
|
|
|
229
255
|
Each invocation resolves one access token for its reads, owner check, and submission. A concurrent login cannot change the command owner between these requests. Every mutation verifies the owner and retains an exclusive private operation file before submission. Missing request IDs default to random UUIDs. The operation stores its exact command, owner, and resource. It contains no credentials. Existing operation files require explicit `retry` and are never replaced by another preparation.
|
|
230
256
|
|
|
231
|
-
`retry REQUEST_ID` verifies the current resource and owner, then sends the unchanged command once. Retry does not read a new head, change expected versions, add a response projection, or rebase a conflict. Its diagnostic identifies the
|
|
257
|
+
`retry REQUEST_ID` verifies the current resource and owner, then sends the unchanged command once. Retry does not read a new head, change expected versions, add a response projection, or rebase a conflict. Its diagnostic identifies replay of the original operation and warns that replay does not establish current state. A fresh `read` or `shares` returns current state.
|
|
232
258
|
|
|
233
259
|
If a response is lost after commit, retry recovers the original receipt. If a conflict occurs, the retained operation remains available for inspection. A new read and a deliberate new command with a new request ID are required to incorporate another writer's changes.
|
|
234
260
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yargote/artifacts-cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Command-line client for the Artifacts REST API.",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"skills"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@yargote/artifacts-contracts": "0.1.0-beta.
|
|
31
|
-
"@yargote/artifacts-client": "0.1.0-beta.
|
|
30
|
+
"@yargote/artifacts-contracts": "0.1.0-beta.7",
|
|
31
|
+
"@yargote/artifacts-client": "0.1.0-beta.7",
|
|
32
32
|
"openid-client": "6.8.8",
|
|
33
33
|
"zod": "4.6.5"
|
|
34
34
|
},
|
|
@@ -17,11 +17,11 @@ Run `artifacts whoami` before the first mutation when the current identity is no
|
|
|
17
17
|
|
|
18
18
|
A revision is a complete snapshot with `metadata`, `content`, and `files`. The command reference contains its JSON shape. Use the requested title, language, content, and attachments. Do not include unrelated files or credentials.
|
|
19
19
|
|
|
20
|
-
Use self-contained HTML for interactive content. Inline styles and scripts and embed required assets as data URLs. External requests, CDN dependencies, relative executable assets, forms, browser storage, and multi-file websites are unsupported. Attachments are downloads, not importable website resources. Markdown and text display inertly.
|
|
20
|
+
Use self-contained HTML for interactive content. For several HTML boards in one canvas, use `design` and follow the design skill's `references/canvas.md`. Inline styles and scripts and embed required assets as data URLs. External requests, CDN dependencies, relative executable assets, forms, browser storage, and multi-file websites are unsupported. Attachments are downloads, not importable website resources. Markdown and text display inertly.
|
|
21
21
|
|
|
22
22
|
Keep the whole revision within 1 MiB of decoded content, metadata, attachment paths, media types, and attachment bytes. At most 32 attachments are accepted. Base64 changes wire size, not the decoded limit. If required content exceeds a limit, explain the constraint without silently removing it.
|
|
23
23
|
|
|
24
|
-
Before writing or restyling HTML, run `artifacts skills get artifacts-design --full` and follow it. It owns typography, embedded fonts, color, layout, and the rendered checks. Finish those checks before saving when browser tools are available. A successful CLI write proves persistence, not visual quality or browser compatibility. Interactive HTML follows the server's Chrome policy; do not promise it in every browser.
|
|
24
|
+
Before writing or restyling HTML or design boards, run `artifacts skills get artifacts-design --full` and follow it. It owns typography, embedded fonts, color, layout, and the rendered checks. Finish those checks before saving when browser tools are available. A successful CLI write proves persistence, not visual quality or browser compatibility. Interactive HTML follows the server's Chrome policy; do not promise it in every browser.
|
|
25
25
|
|
|
26
26
|
## Create or revise
|
|
27
27
|
|
|
@@ -41,7 +41,17 @@ Use `move ID --project NAME` to file an existing artifact under a project and `m
|
|
|
41
41
|
|
|
42
42
|
Publish when the user requests publication, a shareable artifact URL, or a change to the live artifact. Use the exact saved revision selected by the user or returned by the edit. Read the current publication version before preparing publication. Use zero only when the artifact is unpublished.
|
|
43
43
|
|
|
44
|
-
A published URL
|
|
44
|
+
A published URL requires sign-in by the owner or a user whose verified email matches a current share. Publishing a historical revision leaves the draft head unchanged. Report the returned protected URL only after the publication succeeds and its current state is verified.
|
|
45
|
+
|
|
46
|
+
## Share publication access
|
|
47
|
+
|
|
48
|
+
Check installed help for `share`, `shares`, and `unshare` support. These commands are available from `0.1.0-beta.6`. Share only when the user requests access for an exact email. Use `share ARTIFACT_ID --email EMAIL`, then verify the normalized email with `shares ARTIFACT_ID`. Use the observed `shareId` with `unshare ARTIFACT_ID SHARE_ID` to revoke it. Unshare and its successful retry exit zero with empty stdout.
|
|
49
|
+
|
|
50
|
+
A matching signed-in user must have a verified email. Access covers the published revision and its attachments. History, export, editing, and project moves stay owner-only. A share before first publication remains stored and waits for publication. Add `--notify` when the user asks for an invitation email. Invited people can sign in with an email code when the server enables email.
|
|
51
|
+
|
|
52
|
+
Recover uncertain share or unshare results with `retry REQUEST_ID`. Replay confirms the original operation and does not establish current grants. An old share replay does not restore revoked access. An old unshare replay does not remove a deliberate new share. Verify current grants with `shares ARTIFACT_ID`.
|
|
53
|
+
|
|
54
|
+
When the user asks to notify an invited person, add `--notify` to `artifacts share`. Preserve that flag in the retained command and use `artifacts retry` after an uncertain response. Report `requested` as an invitation request, never as confirmed delivery. A skipped notification still grants access. A new deliberate share command can request another attempt for existing access, subject to the owner's daily allowance.
|
|
45
55
|
|
|
46
56
|
## Recover an uncertain write
|
|
47
57
|
|
|
@@ -53,10 +63,12 @@ A `request_conflict` means the ID was used with different arguments. Do not desc
|
|
|
53
63
|
|
|
54
64
|
## Verify and deliver
|
|
55
65
|
|
|
56
|
-
After create or update, read the explicit returned revision. Use `read ARTIFACT_ID --revision REVISION_ID --out FILE --quiet` to retain the full response without printing its snapshot. Check that the installed `--help` supports `--quiet` first. For older versions, use the command workflow's stdout redirection fallback. Compare the saved complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check that its parent is the head used for preparation. Compact responses cannot verify omitted content.
|
|
66
|
+
Retain successful create, update, or edit stdout in a private receipt file. After create or update, read the explicit returned revision. Use `read ARTIFACT_ID --revision REVISION_ID --out FILE --quiet` to retain the full response without printing its snapshot. Check that the installed `--help` supports `--quiet` first. For older versions, use the command workflow's stdout redirection fallback. Compare the saved complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check that its parent is the head used for preparation. Compact responses cannot verify omitted content.
|
|
57
67
|
|
|
58
68
|
After publication, read current publication and compare revision, version, and URL with the committed result. A replayed receipt describes the original commit; another writer may have changed the current publication afterward. Report that difference.
|
|
59
69
|
|
|
70
|
+
For real panel and requested protected-publication checks, follow [the exact saved revision recipe](references/verify-retained.md). It prepares the existing verifier inputs from the write receipt and independent full read. It also gives an installed-browser route when the complete verifier runtime is unavailable. Report persistence, local rendering, panel rendering, and protected publication separately. Input preparation is not a browser pass.
|
|
71
|
+
|
|
60
72
|
Follow list and history cursors when the request needs more than the first page. Export an explicit retained revision to the requested destination. The CLI checks the archive digest. Confirm that the file exists and inspect its manifest and relevant content before linking it.
|
|
61
73
|
|
|
62
74
|
Report the title, saved revision, and draft or published state in the user's language. Link the actual export or returned protected publication URL. Distinguish persistence from any visual or browser checks that did not run. Keep local credential and session identifiers private.
|
|
@@ -61,7 +61,7 @@ Save this JSON as `snapshot.json`, replacing its content with the requested arti
|
|
|
61
61
|
}
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
`metadata.description` and `files` are required even when empty. Content kind is `html`, `markdown`, or `
|
|
64
|
+
`metadata.description` and `files` are required even when empty. Content kind is `html`, `markdown`, `text`, or `design`. Binary files use `encoding: "base64"` and canonical padded base64. Paths are unique normalized relative paths. Do not use empty, dot, parent, absolute, backslash, percent, or colon path segments. A file cannot also be another file's directory.
|
|
65
65
|
|
|
66
66
|
```sh
|
|
67
67
|
artifacts create --snapshot snapshot.json
|
|
@@ -75,6 +75,20 @@ For `read`, `--out FILE --quiet` saves the returned response without printing su
|
|
|
75
75
|
|
|
76
76
|
Check installed help for `--quiet` support before running these examples. Older versions, including `0.1.0-beta.1`, can suppress stdout with `artifacts read ARTIFACT_ID --out saved.json > /dev/null`. This redirection preserves stderr and the command's exit status. Use the same fallback for the revision reads below.
|
|
77
77
|
|
|
78
|
+
## Prepare a design canvas
|
|
79
|
+
|
|
80
|
+
Read the design skill's `references/canvas.md` through `artifacts skills get artifacts-design --full`. Keep the index in `my-canvas/canvas.json` and complete UTF-8 board documents in `my-canvas/boards/*.html`.
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
artifacts skills path artifacts-cli
|
|
84
|
+
node <returned-skill-directory>/scripts/prepare-retained.mjs ./my-canvas > snapshot.json
|
|
85
|
+
artifacts create --snapshot snapshot.json
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The positional form accepts exactly one directory. It preserves the index text and board HTML, sorts snapshot files by path, validates the complete `design` snapshot with the installed contracts, and rejects missing or orphan boards. It uses the directory basename as the title and an empty description. Edit the emitted metadata before saving when needed. Keep the script under the path returned by `skills path` so its package dependencies resolve. All board data, embedded fonts, index text, and metadata share the 1 MiB budget.
|
|
89
|
+
|
|
90
|
+
The existing named-option form of `prepare-retained.mjs` builds a private retained-verification config from saved receipts and an independent read. It does not prepare a content snapshot. The two input forms cannot be combined.
|
|
91
|
+
|
|
78
92
|
## Organize projects
|
|
79
93
|
|
|
80
94
|
Check that installed help lists `projects` and `move` before using them.
|
|
@@ -158,6 +172,31 @@ The publication example applies only to an unpublished artifact. For an existing
|
|
|
158
172
|
|
|
159
173
|
History returns `nextBeforeSequence`; pass it as `--before-sequence N` to continue. List returns `nextCursor`; pass it as `--cursor CURSOR`. Do not substitute an offset or assume that one page contains everything.
|
|
160
174
|
|
|
175
|
+
## Share publication access
|
|
176
|
+
|
|
177
|
+
Check installed help for these commands. They are available from `0.1.0-beta.6`.
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
artifacts share ARTIFACT_ID --email ana@example.com --request-id share-ana-001
|
|
181
|
+
artifacts shares ARTIFACT_ID
|
|
182
|
+
artifacts unshare ARTIFACT_ID SHARE_ID --request-id unshare-ana-001
|
|
183
|
+
artifacts retry share-ana-001
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Share requires the owner's `artifacts:publish` scope, trims and lowercases the email, and prints a share with `shareId`, `artifactId`, `kind: "email"`, `value`, and `createdAt`. `shares` requires the same scope and prints `{ "items": [...] }` with current grants without retaining a mutation. Use an observed `shareId` to unshare. Unshare requires the same scope. Unshare and its successful retry exit zero with empty stdout.
|
|
187
|
+
|
|
188
|
+
The publication admits the owner and signed-in users whose verified email matches a current share. Guests see the published revision and attachments. History, export, editing, and project moves remain owner-only. Shares before first publication wait for publication. Add `--notify` to request one invitation email. Invited people can sign in with an email code when the server enables email.
|
|
189
|
+
|
|
190
|
+
Share and unshare retain complete normalized commands. `retry REQUEST_ID` replays the original operation. A historical share replay does not regrant revoked access, and an old unshare replay leaves a deliberate new share intact. Verify current grants with `shares ARTIFACT_ID` after a replay.
|
|
191
|
+
|
|
192
|
+
Share with `--notify` to request one email attempt. Access succeeds when mail is disabled or the owner has used the daily allowance. The JSON result adds `notification.status` as `requested` or `skipped`, with `disabled` or `rate_limited` as the skip reason. `requested` does not confirm delivery. A crash after commit can lose the message. An exact retry preserves the captured flag and never sends a second email. Use a new deliberate command to request another attempt for existing access.
|
|
193
|
+
|
|
194
|
+
Owners can reserve 100 invitations per UTC day. Revoke, deletion, and failed delivery do not refund reservations. New access is limited to 100 people per artifact and 1000 grants per owner. Existing access remains listable and revocable.
|
|
195
|
+
|
|
196
|
+
## Verify the exact saved revision
|
|
197
|
+
|
|
198
|
+
Follow [the retained revision recipe](verify-retained.md) after successful authoring and requested publication. Retain each successful write stdout in a private receipt file. The recipe extracts its IDs, performs a full independent explicit-revision read, and prepares the existing verifier config without authoring or account mutations. It includes an installed-browser path and identifies the additional capabilities for complete verification.
|
|
199
|
+
|
|
161
200
|
## Retry and export
|
|
162
201
|
|
|
163
202
|
```sh
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Verify the saved revision in the panel
|
|
2
|
+
|
|
3
|
+
Use the successful create, update, or edit result and an independent full read to select the exact saved revision. Use an authorized browser session to inspect its real panel and, when requested, its protected publication. Preparation alone does not prove persistence or browser behavior.
|
|
4
|
+
|
|
5
|
+
## Retain the authoring results
|
|
6
|
+
|
|
7
|
+
Before the authorized write, use `umask 077` and save successful stdout to a new private receipt file. For example, add `> write-result.json` to the create or update command you already intend to run. Keep stderr and the retained request ID for recovery. Continue only after exit zero. Do not repeat a write to obtain a receipt after an uncertain response. Follow [retry recovery](commands.md#retry-and-export).
|
|
8
|
+
|
|
9
|
+
If publication was requested, also retain the successful publish stdout as `publish-result.json`. Finish that authorized publication before the independent read below. Do not publish, share, or revoke access merely to verify an artifact.
|
|
10
|
+
|
|
11
|
+
## Prepare the exact selection
|
|
12
|
+
|
|
13
|
+
Use Node 24+ and a CLI whose help lists `skills path` and `read --quiet`. For older read output, use the [stdout fallback](commands.md#create-a-complete-snapshot). The installed helper requires only Node built-ins. It makes no service request and does not open a browser.
|
|
14
|
+
|
|
15
|
+
Set `observed_panel_origin` to the panel origin observed in the authorized browser or supplied service configuration. Do not derive it from `ARTIFACTS_SERVER` or the publication host. Replace the example origin before execution.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
umask 077
|
|
19
|
+
observed_panel_origin=https://panel.example.test
|
|
20
|
+
skill_directory=$(artifacts skills path artifacts-cli)
|
|
21
|
+
artifact_id=$(node -e '
|
|
22
|
+
try {
|
|
23
|
+
const id = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).artifactId;
|
|
24
|
+
if (typeof id !== "string" || id.length === 0) throw new Error();
|
|
25
|
+
process.stdout.write(id);
|
|
26
|
+
} catch {
|
|
27
|
+
console.error("Cannot extract the artifact ID from the write receipt");
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
' write-result.json)
|
|
31
|
+
revision_id=$(node -e '
|
|
32
|
+
try {
|
|
33
|
+
const id = JSON.parse(require("node:fs").readFileSync(process.argv[1], "utf8")).revision.id;
|
|
34
|
+
if (typeof id !== "string" || id.length === 0) throw new Error();
|
|
35
|
+
process.stdout.write(id);
|
|
36
|
+
} catch {
|
|
37
|
+
console.error("Cannot extract the revision ID from the write receipt");
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
' write-result.json)
|
|
41
|
+
artifacts read "$artifact_id" --revision "$revision_id" --response full --out saved-read.json --quiet
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Stop if any extraction or read fails. A compact write receipt can select a revision. The independent read must contain the complete snapshot. Compare its complete metadata, content, and decoded attachment bytes with the intended snapshot. For an update, check the saved parent against the head used to prepare that update.
|
|
45
|
+
|
|
46
|
+
Save `checks.json` with the requested viewport sizes and meaningful expectations for this document. Replace the example selectors and values with intended content behavior. Expectations run in order inside the content iframe after a fresh load at each viewport.
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"viewports": [{ "width": 1440, "height": 900 }, { "width": 390, "height": 844 }],
|
|
51
|
+
"expectations": [
|
|
52
|
+
{ "kind": "text", "selector": "#total", "text": "5.00" },
|
|
53
|
+
{ "kind": "click", "selector": "#change" },
|
|
54
|
+
{ "kind": "text", "selector": "#total", "text": "28.40" }
|
|
55
|
+
],
|
|
56
|
+
"timeoutMs": 10000
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Use `text` for exact rendered text, `visible` for visibility, `count` for an element count, or `attribute` for an exact attribute value. Use `name` and `value` with `attribute`. Interactions also support `fill` with `value`, `check` with `checked`, and `select` with a `values` array. Do not use arbitrary JavaScript or target panel controls in these expectations.
|
|
61
|
+
|
|
62
|
+
Prepare a new private config with a future evidence directory that does not yet exist.
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
node "$skill_directory/scripts/prepare-retained.mjs" \
|
|
66
|
+
--receipt write-result.json --read saved-read.json \
|
|
67
|
+
--panel-origin "$observed_panel_origin" --checks checks.json \
|
|
68
|
+
--evidence "$PWD/new-retained-evidence" --out retained-config.json
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
When publication was requested, add `--publication-receipt publish-result.json` to that invocation. The helper compares its artifact, revision, version, and URL with the current publication in the saved read. A different publication or concurrent change fails preparation. Report the difference. Do not replace expected versions or publish again to make verification pass.
|
|
72
|
+
|
|
73
|
+
The helper derives all selected IDs and URLs from these inputs. It accepts a newer draft head while keeping the saved revision pinned and reports `headChanged: true`. It emits `status: "prepared"`, never a verification pass. Without a publication receipt, it omits publication coverage even when another revision is published. Existing output files are preserved.
|
|
74
|
+
|
|
75
|
+
## Inspect with an authorized browser
|
|
76
|
+
|
|
77
|
+
This route works with the installed CLI, its bundled files, and browser-control capabilities. It requires no repository checkout. Use a user-supplied session or complete the normal sign-in flow. CLI OAuth login does not supply panel cookies. Keep browser authentication inside the browser. Never extract credentials to construct a Playwright context or state file.
|
|
78
|
+
|
|
79
|
+
After sign-in setup, perform these read-only checks on the selected artifact. Do not create revisions, publish, share, log out, or revoke sessions. Normal protected-viewer admission can consume its one-use grant through the existing GET handoff.
|
|
80
|
+
|
|
81
|
+
1. Read `retained-config.json` privately and open its exact `panelUrl`. Compare the artifact, revision, digest, and publication version with `expected`. Current panels expose `data-artifact-id`, `data-revision-id`, `data-revision-sha256`, and `data-publication-version`. Version zero means no publication. Report a newer head separately. Never switch the selected revision to the head.
|
|
82
|
+
2. Establish the actual browser version and available frame, response, console, download, and viewport controls. Record missing capabilities. A browser name, load event, or empty console does not establish that content loaded.
|
|
83
|
+
3. Inspect the actual preview iframe source, pinned revision, sandbox, referrer policy, and dimensions. Wait for a distinctive expected element within the configured deadline. Exercise each authorized content control and record the resulting visible state. Repeat at each available requested viewport and check keyboard interactions and overflow where relevant.
|
|
84
|
+
4. When response inspection or native DevTools is available, inspect the iframe request. Record completion, status, content type, CSP, connection allowlist, cache policy, and DNS policy. Distinguish interactive content, inert fallback, and error responses. A pending request has no final response evidence. A top-level preview navigation deliberately returns 403 and cannot replace this iframe check.
|
|
85
|
+
5. When the config includes `publicationUrl`, open that separate protected viewer through normal admission. Compare its selected revision, digest, and version with `expected`. Observe the real content and repeat the authorized interactions. Open `Archivos` and compare the complete attachment inventory and revision-bound links. Check protected download bytes when that capability is available. Missing attachment or response evidence remains unavailable.
|
|
86
|
+
6. After publication interactions, reread the same explicit revision with the quoted IDs into a new private file. Compare its current `publication.revisionId`, `version`, and `url` with the saved read and publish receipt. Reload the selected panel and compare its publication markers again. Report a changed publication as a concurrent change. Do not report protected-publication success for a different selected revision.
|
|
87
|
+
7. If the frame is blank, distinguish admission, pending transport, policy, browser compatibility, content execution, and browser-control failures from observed evidence. If debugger attachment stalls it, disconnect through normal browser controls, reload the same pinned page, and repeat the interaction with native controls. Preserve authentication and containment. Attribute a debugger failure only when that comparison demonstrates it.
|
|
88
|
+
8. Save a sanitized report and private screenshots. Include selected IDs, digest, browser version, viewport sizes, actions, resulting visible states, observed response policies, and unavailable checks. Exclude cookies, tokens, viewer grants, callback queries, raw authentication requests, and private content from diagnostic logs.
|
|
89
|
+
|
|
90
|
+
This is partial browser verification. Do not return the complete verifier's pass verdict. Missing exact bytes, ZIP, policy, attachment, console, or viewport evidence must remain explicit.
|
|
91
|
+
|
|
92
|
+
## Run the existing complete verifier when available
|
|
93
|
+
|
|
94
|
+
The CLI package does not install Playwright, `tsx`, Chrome, or the repository's verifier and rendering-policy modules. Complete automated verification needs an operator-provided installation of that existing verifier with its dependencies and actual supported Chrome. It also needs an explicitly supplied authenticated Playwright `BrowserContext` or owner-private storage-state file prepared through an approved sign-in flow.
|
|
95
|
+
|
|
96
|
+
For repository operators, run this command from the repository root. The paths in this command are repository capabilities, not paths inside an installed CLI package.
|
|
97
|
+
|
|
98
|
+
```sh
|
|
99
|
+
node --import tsx .agents/skills/verify-artifacts/scripts/verify-retained.ts \
|
|
100
|
+
--config /private/path/retained-config.json \
|
|
101
|
+
--storage-state /private/path/browser-state.json
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Pass the prepared config unchanged. The existing verifier validates the full schema and canonical snapshot hash. It checks pinned responses, markers, policies, interactions, ZIP, attachment bytes, viewports, and publication state before and after viewer checks. It writes private evidence and fails on mismatches or concurrent publication changes. Use its callable `verifyRetainedArtifact(context, config)` when an authorized context is supplied. Do not build another viewer or weaken sandbox policies to establish deployed behavior.
|
|
105
|
+
|
|
106
|
+
If this runtime or authenticated input is unavailable, use the partial route above and name that limit. If browser access is unavailable too, report only the persistence checks actually completed.
|
|
107
|
+
|
|
108
|
+
## Deliver the observed outcomes
|
|
109
|
+
|
|
110
|
+
Report each outcome separately, with its evidence and missing checks.
|
|
111
|
+
|
|
112
|
+
| Outcome | Evidence needed |
|
|
113
|
+
| --- | --- |
|
|
114
|
+
| Persistence | Full independent explicit-revision read and complete intended-snapshot comparison. Name any exact export check separately. |
|
|
115
|
+
| Local rendering | Observed local document layout and interactions. This does not prove hosted rendering. |
|
|
116
|
+
| Panel rendering | Exact real panel selection, pinned iframe, visible content, and observed interaction. Name missing response, policy, byte, console, or viewport checks. |
|
|
117
|
+
| Protected publication | Separate authorized viewer admission for the selected published revision, visible content, interactions, and attachment checks. Report concurrent changes and unavailable checks. |
|
|
118
|
+
|
|
119
|
+
Use `passed`, `failed`, `unavailable`, or `not requested` for each outcome as the evidence permits. Keep session identifiers and private evidence private. A successful helper invocation establishes only input preparation.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { lstat, readFile, readdir, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
|
|
6
|
+
function requireValue(condition, message) {
|
|
7
|
+
if (!condition) throw new Error(message);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function object(value) {
|
|
11
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function prepareRetainedSelection({ receipt, expected, checks, panelOrigin, evidenceDirectory, publicationReceipt }) {
|
|
15
|
+
requireValue(object(receipt) && object(receipt.revision), 'Write receipt must identify a saved revision');
|
|
16
|
+
requireValue(
|
|
17
|
+
typeof receipt.artifactId === 'string' && receipt.artifactId.length > 0 &&
|
|
18
|
+
typeof receipt.revision.id === 'string' && receipt.revision.id.length > 0 &&
|
|
19
|
+
/^[a-f0-9]{64}$/.test(receipt.revision.sha256),
|
|
20
|
+
'Write receipt must contain artifact, revision, and SHA-256',
|
|
21
|
+
);
|
|
22
|
+
const snapshot = expected?.revision?.snapshot;
|
|
23
|
+
requireValue(
|
|
24
|
+
object(expected) && object(snapshot) && object(snapshot.metadata) &&
|
|
25
|
+
typeof snapshot.metadata.title === 'string' && typeof snapshot.metadata.description === 'string' &&
|
|
26
|
+
object(snapshot.content) && ['html', 'markdown', 'text', 'design'].includes(snapshot.content.kind) &&
|
|
27
|
+
typeof snapshot.content.text === 'string' && Array.isArray(snapshot.files),
|
|
28
|
+
'Independent read must contain the full snapshot, not a compact response',
|
|
29
|
+
);
|
|
30
|
+
requireValue(expected.artifactId === receipt.artifactId && expected.revision.id === receipt.revision.id && expected.revision.sha256 === receipt.revision.sha256, 'Independent read differs from the saved write receipt');
|
|
31
|
+
requireValue(typeof expected.headRevisionId === 'string' && expected.headRevisionId.length > 0, 'Independent read must identify its current draft head');
|
|
32
|
+
requireValue(object(checks) && Object.keys(checks).every((key) => ['viewports', 'expectations', 'timeoutMs'].includes(key)) && Array.isArray(checks.viewports) && checks.viewports.length > 0 && Array.isArray(checks.expectations), 'Checks must contain viewports and expectations, with optional timeoutMs');
|
|
33
|
+
let panel;
|
|
34
|
+
try { panel = new URL(panelOrigin); } catch { throw new Error('Panel origin must be an observed HTTP origin'); }
|
|
35
|
+
requireValue(['http:', 'https:'].includes(panel.protocol) && !panel.username && !panel.password && panel.pathname === '/' && !panel.search && !panel.hash, 'Panel origin must be an observed HTTP origin without a path, credentials, query, or fragment');
|
|
36
|
+
panel.pathname = `/artifacts/${encodeURIComponent(expected.artifactId)}`;
|
|
37
|
+
panel.searchParams.set('revisionId', expected.revision.id);
|
|
38
|
+
let publicationUrl;
|
|
39
|
+
if (publicationReceipt !== undefined) {
|
|
40
|
+
const current = expected.publication;
|
|
41
|
+
requireValue(
|
|
42
|
+
object(publicationReceipt) && object(current) &&
|
|
43
|
+
publicationReceipt.artifactId === expected.artifactId &&
|
|
44
|
+
publicationReceipt.revisionId === expected.revision.id && publicationReceipt.revisionId === current.revisionId &&
|
|
45
|
+
Number.isInteger(publicationReceipt.version) && publicationReceipt.version > 0 && publicationReceipt.version === current.version &&
|
|
46
|
+
typeof publicationReceipt.url === 'string' && publicationReceipt.url === current.url,
|
|
47
|
+
'Publication differs from the requested receipt or selected revision; report the changed or different publication',
|
|
48
|
+
);
|
|
49
|
+
let publication;
|
|
50
|
+
try { publication = new URL(current.url); } catch { throw new Error('Publication receipt must contain a stable HTTP URL'); }
|
|
51
|
+
requireValue(['http:', 'https:'].includes(publication.protocol) && !publication.username && !publication.password && !publication.search && !publication.hash && publication.pathname === `/a/${encodeURIComponent(expected.artifactId)}`, 'Publication receipt must contain the selected artifact stable HTTP URL');
|
|
52
|
+
publicationUrl = publication.href;
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
expected,
|
|
56
|
+
panelUrl: panel.href,
|
|
57
|
+
...(publicationUrl === undefined ? {} : { publicationUrl }),
|
|
58
|
+
...checks,
|
|
59
|
+
evidenceDirectory: resolve(evidenceDirectory),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function canvasContracts() {
|
|
64
|
+
const name = '@yargote/artifacts-contracts';
|
|
65
|
+
let path;
|
|
66
|
+
try { path = createRequire(import.meta.url).resolve(name); }
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (error.code !== 'MODULE_NOT_FOUND') throw error;
|
|
69
|
+
path = createRequire(new URL('../../../apps/cli/package.json', import.meta.url)).resolve(name);
|
|
70
|
+
}
|
|
71
|
+
return import(pathToFileURL(path).href);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async function prepareCanvasDirectory(input) {
|
|
75
|
+
const directory = resolve(input);
|
|
76
|
+
requireValue((await lstat(directory)).isDirectory(), 'Canvas input must be a directory');
|
|
77
|
+
const { canvasIndexSchema, snapshotSchema } = await canvasContracts();
|
|
78
|
+
const indexFile = join(directory, 'canvas.json');
|
|
79
|
+
const indexInfo = await lstat(indexFile);
|
|
80
|
+
requireValue(indexInfo.isFile(), 'canvas.json must be a regular file');
|
|
81
|
+
requireValue(indexInfo.size <= 1_048_576, 'canvas.json exceeds the shared 1 MiB revision budget');
|
|
82
|
+
const indexBytes = await readFile(indexFile);
|
|
83
|
+
const text = indexBytes.toString('utf8');
|
|
84
|
+
requireValue(Buffer.from(text, 'utf8').equals(indexBytes), 'canvas.json must contain valid UTF-8');
|
|
85
|
+
const index = canvasIndexSchema.parse(JSON.parse(text));
|
|
86
|
+
const boardDirectory = join(directory, 'boards');
|
|
87
|
+
requireValue((await lstat(boardDirectory)).isDirectory(), 'boards must be a directory, not a symbolic link');
|
|
88
|
+
const entries = await readdir(boardDirectory, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const path = `boards/${entry.name}`;
|
|
91
|
+
requireValue(entry.isFile(), `Canvas board ${path} must be a regular file`);
|
|
92
|
+
requireValue(Object.hasOwn(index.boards, path), `Orphan canvas board ${path} is absent from canvas.json`);
|
|
93
|
+
}
|
|
94
|
+
const files = [];
|
|
95
|
+
for (const path of [...index.order].sort()) {
|
|
96
|
+
const filename = join(directory, path);
|
|
97
|
+
let info;
|
|
98
|
+
try { info = await lstat(filename); }
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (error.code === 'ENOENT') throw new Error(`Canvas board ${path} has no matching file`);
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
requireValue(info.isFile(), `Canvas board ${path} must be a regular file`);
|
|
104
|
+
requireValue(info.size <= 1_048_576, `Canvas board ${path} exceeds the shared 1 MiB revision budget`);
|
|
105
|
+
const bytes = await readFile(filename);
|
|
106
|
+
const data = bytes.toString('utf8');
|
|
107
|
+
requireValue(Buffer.from(data, 'utf8').equals(bytes), `Canvas board ${path} must contain valid UTF-8`);
|
|
108
|
+
files.push({ path, mediaType: 'text/html', encoding: 'utf8', data });
|
|
109
|
+
}
|
|
110
|
+
return snapshotSchema.parse({
|
|
111
|
+
metadata: { title: basename(directory), description: '' },
|
|
112
|
+
content: { kind: 'design', text },
|
|
113
|
+
files,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const required = ['--receipt', '--read', '--panel-origin', '--checks', '--evidence', '--out'];
|
|
118
|
+
try {
|
|
119
|
+
const args = process.argv.slice(2);
|
|
120
|
+
if (args.length === 1 && !args[0].startsWith('--')) {
|
|
121
|
+
console.log(JSON.stringify(await prepareCanvasDirectory(args[0]), null, 2));
|
|
122
|
+
} else {
|
|
123
|
+
const options = new Map();
|
|
124
|
+
requireValue(args.length % 2 === 0, 'Use named options with one value each');
|
|
125
|
+
for (let index = 0; index < args.length; index += 2) {
|
|
126
|
+
const name = args[index];
|
|
127
|
+
requireValue([...required, '--publication-receipt'].includes(name) && !options.has(name) && typeof args[index + 1] === 'string' && args[index + 1].length > 0, 'Use each supported option once with a nonempty value');
|
|
128
|
+
options.set(name, args[index + 1]);
|
|
129
|
+
}
|
|
130
|
+
requireValue(required.every((name) => options.has(name)), 'Required options are --receipt --read --panel-origin --checks --evidence --out');
|
|
131
|
+
const json = async (name) => {
|
|
132
|
+
try { return JSON.parse(await readFile(options.get(name), 'utf8')); }
|
|
133
|
+
catch { throw new Error(`Cannot read valid JSON for ${name}`); }
|
|
134
|
+
};
|
|
135
|
+
const receipt = await json('--receipt');
|
|
136
|
+
const expected = await json('--read');
|
|
137
|
+
const checks = await json('--checks');
|
|
138
|
+
const publication = options.has('--publication-receipt') ? await json('--publication-receipt') : undefined;
|
|
139
|
+
const config = prepareRetainedSelection({ receipt, expected, checks, panelOrigin: options.get('--panel-origin'), evidenceDirectory: options.get('--evidence'), publicationReceipt: publication });
|
|
140
|
+
try { await writeFile(options.get('--out'), `${JSON.stringify(config, null, 2)}\n`, { flag: 'wx', mode: 0o600 }); }
|
|
141
|
+
catch (error) { throw new Error(error?.code === 'EEXIST' ? 'Config output already exists; choose a new output path' : 'Cannot create the private config output'); }
|
|
142
|
+
const headChanged = expected.headRevisionId !== expected.revision.id;
|
|
143
|
+
console.log(JSON.stringify({ status: 'prepared', headChanged, publication: publication === undefined ? 'not-requested' : 'requested' }));
|
|
144
|
+
if (headChanged) console.error('Current draft head differs from the selected saved revision. The selection remains pinned.');
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.error(error.message);
|
|
148
|
+
process.exitCode = 1;
|
|
149
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: artifacts-design
|
|
3
|
-
description: Design
|
|
3
|
+
description: Design HTML documents and multi-board design canvases of a retained Artifact with embedded fonts, light and dark Catppuccin tokens, purposeful layout, and rendered checks. Use before writing or restyling HTML or design boards for the artifacts or artifacts-cli skills.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Design HTML artifacts
|
|
@@ -9,6 +9,8 @@ The calling skill owns the snapshot, size limit, and save workflow. This skill o
|
|
|
9
9
|
|
|
10
10
|
The document renders in a sandbox that blocks every network request. Fonts, images, and icons load only as `data:` URIs. External stylesheets, Google Fonts links, and CDN scripts silently fail. Inline all CSS and JavaScript.
|
|
11
11
|
|
|
12
|
+
For multiple boards, read [canvas authoring](references/canvas.md) before writing the index or HTML. It defines the board files, pages, geometry, shared font budget, and complete snapshot workflow.
|
|
13
|
+
|
|
12
14
|
## Choose the composition
|
|
13
15
|
|
|
14
16
|
Before writing HTML, identify the reader's task, the facts and assets available, and what the reader should understand or do first. Use supplied facts. Label illustrative inputs and assumptions, and leave unavailable evidence absent.
|
|
@@ -49,7 +51,7 @@ After the HTML file is complete, embed the fonts it names:
|
|
|
49
51
|
node <skill-directory>/scripts/embed-fonts.mjs page.html
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
For the CLI, `<skill-directory>` is the output of `artifacts skills path artifacts-design`. The script rewrites the file in place and is safe to rerun after edits. It
|
|
54
|
+
For the CLI, `<skill-directory>` is the output of `artifacts skills path artifacts-design`. The script rewrites the file in place and is safe to rerun after edits. It selects families from CSS `font-family` and `font` declarations in style elements and inline styles, including referenced custom properties. Comments, content text, and JavaScript strings do not select fonts. It adds italic faces for CSS `font-style: italic`, italic `font` shorthand, or real `<em>`, `<i>`, and `<cite>` elements. It refuses to write a page above 1 MiB. Three families add about 135 KB. Newsreader adds about 80 to 165 KB. Do not name another family unless you embed its `woff2` yourself, because an unembedded face silently falls back. Without Node, keep the fallback stacks and report that the fonts were not embedded.
|
|
53
55
|
|
|
54
56
|
## Color
|
|
55
57
|
|
|
@@ -77,6 +79,14 @@ Give the `<title>` a specific name of two to four words, or the name the user al
|
|
|
77
79
|
|
|
78
80
|
## Check the rendered page
|
|
79
81
|
|
|
82
|
+
Run the local helper after embedding fonts and before saving:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
node <skill-directory>/scripts/verify-local.mjs page.html --evidence ./page-evidence
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For the CLI, resolve `<skill-directory>` with `artifacts skills path artifacts-design`. Read [local checks](references/local-checks.md) for existing runtime requirements, optional declarative control expectations, browser selection, and report fields. The helper never installs a dependency, changes the HTML, saves, publishes, or requires service credentials. Its report proves **renderizado local** and leaves visual review required. It does not verify the retained revision or authenticated publication.
|
|
89
|
+
|
|
80
90
|
Run the [interface checks](references/interface-checks.md) before saving. They distinguish required checks from visual judgment. Render the finished page at 375 px and 1280 px in both light and dark when browser tools are available. Fix observed failures and repeat the affected checks. Report checks you could not run.
|
|
81
91
|
|
|
82
92
|
The license notices for bundled fonts and adapted guidance are in [third-party notices](THIRD-PARTY-NOTICES.md).
|