@prismer/runtime 2.0.8 → 2.2.55
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/CHANGELOG.md +3430 -0
- package/README.md +34 -12
- package/apc/skills/FIELD-DICTIONARY.md +111 -0
- package/apc/skills/bug-reproduce/SKILL.md +150 -0
- package/apc/skills/bug-reproduce/skill.json +96 -0
- package/apc/skills/code-review/SKILL.md +198 -0
- package/apc/skills/code-review/skill.json +124 -0
- package/apc/skills/design-review/SKILL.md +122 -0
- package/apc/skills/design-review/skill.json +88 -0
- package/apc/skills/doc-sync/SKILL.md +168 -0
- package/apc/skills/doc-sync/skill.json +81 -0
- package/apc/skills/env-doctor/SKILL.md +194 -0
- package/apc/skills/env-doctor/skill.json +209 -0
- package/apc/skills/git-ops/SKILL.md +189 -0
- package/apc/skills/git-ops/skill.json +94 -0
- package/apc/skills/impact-trace/SKILL.md +168 -0
- package/apc/skills/impact-trace/skill.json +104 -0
- package/apc/skills/observability/SKILL.md +195 -0
- package/apc/skills/observability/skill.json +116 -0
- package/apc/skills/release-db-config-sync/SKILL.md +186 -0
- package/apc/skills/release-db-config-sync/skill.json +109 -0
- package/apc/skills/release-ota-promote/SKILL.md +195 -0
- package/apc/skills/release-ota-promote/skill.json +176 -0
- package/apc/skills/release-preflight/SKILL.md +174 -0
- package/apc/skills/release-preflight/skill.json +175 -0
- package/apc/skills/release-rollback/SKILL.md +214 -0
- package/apc/skills/release-rollback/skill.json +230 -0
- package/apc/skills/release-tag/SKILL.md +194 -0
- package/apc/skills/release-tag/skill.json +94 -0
- package/apc/skills/releasing-prod/SKILL.md +49 -0
- package/apc/skills/releasing-test/SKILL.md +135 -0
- package/apc/skills/sdk-release/SKILL.md +200 -0
- package/apc/skills/spec-intake/SKILL.md +169 -0
- package/apc/skills/spec-intake/skill.json +93 -0
- package/apc/skills/test-result-feedback/SKILL.md +239 -0
- package/apc/skills/test-result-feedback/skill.json +193 -0
- package/apc/skills/test-runner/SKILL.md +169 -0
- package/apc/skills/test-runner/skill.json +103 -0
- package/apc/skills/ui-align/SKILL.md +209 -0
- package/apc/skills/ui-align/skill.json +114 -0
- package/apc/skills/ui-canvas/SKILL.md +148 -0
- package/apc/skills/ui-canvas/skill.json +127 -0
- package/built-in-skills/agent-coordination/SKILL.md +59 -37
- package/built-in-skills/agent-meta/SKILL.md +1 -0
- package/built-in-skills/assets/SKILL.md +8 -6
- package/built-in-skills/browser-use/SKILL.md +93 -0
- package/built-in-skills/canvas-design/SKILL.md +1 -0
- package/built-in-skills/claim-agent-ownership/SKILL.md +3 -2
- package/built-in-skills/claude-api/SKILL.md +1 -0
- package/built-in-skills/codebase-design/DEEPENING.md +37 -0
- package/built-in-skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
- package/built-in-skills/codebase-design/LICENSE +21 -0
- package/built-in-skills/codebase-design/SKILL.md +116 -0
- package/built-in-skills/conversation-compaction/SKILL.md +114 -0
- package/built-in-skills/council-creator/SKILL.md +426 -0
- package/built-in-skills/diagnosing-bugs/LICENSE +21 -0
- package/built-in-skills/diagnosing-bugs/SKILL.md +136 -0
- package/built-in-skills/diagnosing-bugs/scripts/hitl-loop.template.sh +41 -0
- package/built-in-skills/doc-coauthoring/SKILL.md +1 -0
- package/built-in-skills/document-generation/SKILL.md +105 -0
- package/built-in-skills/domain-modeling/ADR-FORMAT.md +47 -0
- package/built-in-skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/built-in-skills/domain-modeling/LICENSE +21 -0
- package/built-in-skills/domain-modeling/SKILL.md +76 -0
- package/built-in-skills/frontend-design/SKILL.md +1 -0
- package/built-in-skills/human-approval/SKILL.md +17 -2
- package/built-in-skills/image-generate/SKILL.md +103 -302
- package/built-in-skills/image-generate/scripts/generate-and-deliver.mjs +289 -0
- package/built-in-skills/ingest/SKILL.md +13 -45
- package/built-in-skills/internal-comms/SKILL.md +1 -0
- package/built-in-skills/liteparse/SKILL.md +130 -110
- package/built-in-skills/mcp-builder/SKILL.md +1 -0
- package/built-in-skills/memory/SKILL.md +420 -55
- package/built-in-skills/memory-dream/SKILL.md +339 -0
- package/built-in-skills/office-artifacts/SKILL.md +17 -4
- package/built-in-skills/okr/SKILL.md +154 -0
- package/built-in-skills/persona/SKILL.md +81 -0
- package/built-in-skills/persona-generator/SKILL.md +296 -0
- package/built-in-skills/pkf-svg/SKILL.md +253 -0
- package/built-in-skills/pkf-writing/SKILL.md +236 -0
- package/built-in-skills/prismer-im-collab/SKILL.md +26 -6
- package/built-in-skills/proactivity/SKILL.md +84 -0
- package/built-in-skills/remotion/SKILL.md +431 -0
- package/built-in-skills/role-builder/SKILL.md +203 -0
- package/built-in-skills/role-builder/scripts/author-role.mjs +334 -0
- package/built-in-skills/role-builder/scripts/ingest-role.mjs +223 -0
- package/built-in-skills/role-builder/scripts/instantiate-and-run.mjs +290 -0
- package/built-in-skills/role-builder/scripts/operation-harness.mjs +267 -0
- package/built-in-skills/skill-authoring/SKILL.md +110 -100
- package/built-in-skills/skill-authoring/skill.json +3 -3
- package/built-in-skills/skill-builder/SKILL.md +171 -0
- package/built-in-skills/skill-builder/scripts/ingest.mjs +265 -0
- package/built-in-skills/skill-creator/SKILL.md +165 -423
- package/built-in-skills/skill-creator/references/external-library-import.md +110 -0
- package/built-in-skills/skill-creator/scripts/import-library.mjs +475 -0
- package/built-in-skills/slack-gif-creator/SKILL.md +20 -0
- package/built-in-skills/tasks/SKILL.md +38 -23
- package/built-in-skills/tdd/LICENSE +21 -0
- package/built-in-skills/tdd/SKILL.md +110 -0
- package/built-in-skills/tdd/mocking.md +59 -0
- package/built-in-skills/tdd/refactoring.md +10 -0
- package/built-in-skills/tdd/tests.md +61 -0
- package/built-in-skills/team/SKILL.md +2 -1
- package/built-in-skills/web-artifacts-builder/SKILL.md +1 -0
- package/built-in-skills/webapp-testing/SKILL.md +1 -0
- package/built-in-skills/wechat-pay/SKILL.md +59 -0
- package/dist/cli.cjs +71872 -19960
- package/dist/cli.js +71803 -19846
- package/dist/index.cjs +72010 -19966
- package/dist/index.d.cts +4258 -712
- package/dist/index.d.ts +4258 -712
- package/dist/index.js +72156 -20118
- package/package.json +37 -6
- package/plugins/memory/prismer/__init__.py +1211 -0
- package/plugins/memory/prismer/plugin.yaml +8 -0
- package/plugins/memory/prismer/tool-schemas.generated.json +249 -0
- package/plugins/tools/prismer-recall/__init__.py +282 -0
- package/plugins/tools/prismer-recall/plugin.yaml +15 -0
- package/built-in-skills/memory-curation/SKILL.md +0 -135
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: persona-generator
|
|
3
|
+
scope: common
|
|
4
|
+
description: >
|
|
5
|
+
Given a Council Brief, produce persona-ROLE instance definitions: real,
|
|
6
|
+
publicly-verifiable people who occupy distinct stakeholder positions on the
|
|
7
|
+
issue — each a persona-flavored role template (lens / voice / SOUL + the
|
|
8
|
+
persona-skill capability boundary + icon + an ASCII usernameSlug), handed to
|
|
9
|
+
convene (council-creator) to be instantiated as a REAL agent. (Search
|
|
10
|
+
grounding of each person is done by the cloud /councils/plan handler, not by
|
|
11
|
+
this skill.) Use when the workspace orchestrator needs to convene an expert
|
|
12
|
+
roundtable ("帮我找几个人聊聊这个 idea"). Personas are LENSES distilled from
|
|
13
|
+
public records, not the real people — never assert private facts, never
|
|
14
|
+
impersonate. This skill produces the persona-role DEFINITIONS (who speaks); it
|
|
15
|
+
does NOT create the group and does NOT drive the discussion — that is
|
|
16
|
+
council-creator.
|
|
17
|
+
license: MIT
|
|
18
|
+
compatibility:
|
|
19
|
+
- prismer-sdk
|
|
20
|
+
- hermes
|
|
21
|
+
metadata:
|
|
22
|
+
category: council
|
|
23
|
+
personaPolicy: public_distillation
|
|
24
|
+
disclaimerRequired: true
|
|
25
|
+
allowedUse: [analysis, debate, ideation]
|
|
26
|
+
forbiddenUse:
|
|
27
|
+
- impersonation
|
|
28
|
+
- private_claim
|
|
29
|
+
- legal_advice_without_disclaimer
|
|
30
|
+
- medical_advice_without_disclaimer
|
|
31
|
+
- financial_advice_without_disclaimer
|
|
32
|
+
eval:
|
|
33
|
+
level: generator # eval the GENERATOR against a Brief — never a single persona
|
|
34
|
+
assertions:
|
|
35
|
+
- positions-distinct # N personas occupy N distinct stakeholder positions (no two share one)
|
|
36
|
+
- insider-view-present # ≥1 persona sits where outsiders cannot see
|
|
37
|
+
- no-fabricated-people # every displayName is a real, publicly verifiable person
|
|
38
|
+
- risk-coverage-3plus # cast covers ≥3 risk dimensions (capital/tech/market/org/compliance)
|
|
39
|
+
- grounding-substring # every quote is a substring of a real search snippet (code-checked)
|
|
40
|
+
- brief-boundary # no persona asserts company facts beyond the Brief
|
|
41
|
+
- honest-degradation # ungroundable → role_label; fabricated quotes are a hard fail
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# Persona Generator
|
|
45
|
+
|
|
46
|
+
**Who speaks.** Given a Council Brief (assembled by the orchestrator from the
|
|
47
|
+
Team Manager's materials + memory), produce `cast: PersonaDef[]` — 3–5 people occupying
|
|
48
|
+
**distinct stakeholder positions**, each contributing the one thing only their
|
|
49
|
+
position lets them see. This is a **capability skill**: the orchestrator calls
|
|
50
|
+
it to produce data — a set of **persona-role instance definitions**. Each cast
|
|
51
|
+
member is a persona-flavored **role template def** (lens / voice / SOUL + the
|
|
52
|
+
persona-skill capability boundary + icon + an ASCII `usernameSlug`) that convene
|
|
53
|
+
will instantiate as a **real agent** (`createAgentForWorkspace` + `applyToAgent`,
|
|
54
|
+
M2). It is **not** a virtual `im_users` mask — persona = role instance.
|
|
55
|
+
|
|
56
|
+
| Do | Do NOT |
|
|
57
|
+
| --- | --- |
|
|
58
|
+
| Enumerate stakeholder positions for the issue | Create the council group (council-creator) |
|
|
59
|
+
| Pick one **real person** per position | Drive or moderate the discussion (council-creator) |
|
|
60
|
+
| Produce `seesWhatOthersDont` per person | Write memory (settle phase, via `memory` skill) |
|
|
61
|
+
| Emit voice (per-turn instructions) per person | Parse / extract materials (orchestrator tool-skills) |
|
|
62
|
+
| Hand the cast to cloud `/councils/plan` for grounding | Run search verification / supply quotes yourself (cloud does it) |
|
|
63
|
+
| Read `droppedCast[]` and substitute for dropped people | Invent people, quotes, or citations — ever |
|
|
64
|
+
|
|
65
|
+
## Meta-goal (iron rule — supersedes any "debate" framing)
|
|
66
|
+
|
|
67
|
+
**A discussion is a plurality of viewpoints, not a positive-vs-negative game.**
|
|
68
|
+
Convene people of different identities, professions and stakes; each speaks
|
|
69
|
+
from their own position. If disagreement emerges it is the *natural result* of
|
|
70
|
+
plural perspectives — it is never something you construct. Do **not** hunt for
|
|
71
|
+
"an opposing side"; a target function of "construct opposition" structurally
|
|
72
|
+
excludes the two most valuable classes of people: **insiders** (dismissed as
|
|
73
|
+
same-camp) and **people who reject the axis entirely** (nowhere to stand on
|
|
74
|
+
it). The UI language is "讨论组" (discussion group), never "吵一架".
|
|
75
|
+
|
|
76
|
+
## Core procedure
|
|
77
|
+
|
|
78
|
+
### Step 0 — List stakeholder positions first (not "axes of disagreement")
|
|
79
|
+
|
|
80
|
+
Before picking anyone, enumerate the positions whose duty / expertise / stake
|
|
81
|
+
touches this issue:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
运营执行者 / 出资人 / 监管者 / 同行实践者 / 下游客户 / 一线员工 / 被影响的第三方 …
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Step 1 — One REAL person per position
|
|
88
|
+
|
|
89
|
+
- Pick the person whose **real public track record** naturally puts them in
|
|
90
|
+
that position. If no fitting famous person exists for a position (e.g. "this
|
|
91
|
+
company's own CTO"), pick a real person who once held the *equivalent*
|
|
92
|
+
position elsewhere (e.g. another rocket company's former CTO).
|
|
93
|
+
- **Never invent a name.** Prefer swapping to a position where a real person
|
|
94
|
+
exists.
|
|
95
|
+
- Do **not** require them to disagree with each other. They may all agree and
|
|
96
|
+
still each fill in a piece the Team Manager cannot see.
|
|
97
|
+
- **Forbidden:** inserting an artificial "opposing side" to manufacture
|
|
98
|
+
conflict.
|
|
99
|
+
|
|
100
|
+
For every person you MUST state `seesWhatOthersDont` — not "which side they
|
|
101
|
+
are on", but "standing here, they can see X that nobody else at the table
|
|
102
|
+
can". This field is the core output of the skill.
|
|
103
|
+
|
|
104
|
+
### Step 2 — Attach the persona (who arrives at the table)
|
|
105
|
+
|
|
106
|
+
- Enough verifiable public record and a good fit → **real name**
|
|
107
|
+
(`displayName: "Elon Musk"`, `subtitle: "SpaceX CEO"`). Compliance is
|
|
108
|
+
carried by the **disclaimer**, never by renaming — "Elon-style Strategy
|
|
109
|
+
Lens" style abstractions are forbidden (nobody wants to chat with a
|
|
110
|
+
concept). Internal terms (`persona`, `lens`) never surface in UI copy.
|
|
111
|
+
- Otherwise → a role label ("一位持保守立场的航天投资人") — still a person,
|
|
112
|
+
not a concept.
|
|
113
|
+
- `usernameSlug` (**load-bearing — this is the persona-agent's @-handle**):
|
|
114
|
+
ascii `[a-zA-Z0-9_-]`, 3–20 chars, lowercase slug of the person (e.g.
|
|
115
|
+
`elon-musk`). It must NOT start with the reserved `prismer-` segment. Final
|
|
116
|
+
uniqueness (`<slug>-<conv8>` + dedupe suffix) is enforced by the cloud at
|
|
117
|
+
convene time — you only produce the candidate slug. **Why it MUST be ASCII:**
|
|
118
|
+
once instantiated (M2), the persona-agent is addressed in the group **only**
|
|
119
|
+
by `@<usernameSlug>`. The mention router (`agent-dispatcher.ts` `MENTION_RE`)
|
|
120
|
+
is strict ASCII — a bare `@中文displayName` matches nothing and is silently
|
|
121
|
+
dropped, so a non-ASCII slug would leave the persona **unreachable** (0
|
|
122
|
+
dispatches, the exact 0/5 failure). The Chinese `displayName` is display-only;
|
|
123
|
+
it never routes.
|
|
124
|
+
|
|
125
|
+
### Step 2.5 — Search grounding is done by the CLOUD, not by you
|
|
126
|
+
|
|
127
|
+
You **only produce candidates** — position + real person + `seesWhatOthersDont`
|
|
128
|
+
+ candidate `usernameSlug`. **Do NOT run search verification yourself and do NOT
|
|
129
|
+
supply any `realCredential` / `whySelected` quotes.** Grounding is performed by
|
|
130
|
+
the cloud `/api/im/councils/plan` handler after you hand over the cast.
|
|
131
|
+
|
|
132
|
+
- **Why cloud, not you (architectural, 04 §2.5):** the anti-fabrication defense
|
|
133
|
+
requires a **verbatim substring of a real search snippet**, checked in code
|
|
134
|
+
against full-text search (`/api/search`). The daemon `web_search` tool you can
|
|
135
|
+
reach routes through `/api/context/load`→`/api/compress`, so it returns
|
|
136
|
+
**LLM-rewritten prose, not the original text** — you physically cannot obtain
|
|
137
|
+
the verbatim snippet the substring check needs. Any quote you write from
|
|
138
|
+
memory would be a fabrication that the cloud check exists to reject.
|
|
139
|
+
- **What the cloud does with your cast:** for each candidate it searches
|
|
140
|
+
`"<displayName> <topic terms>"` and requires the person's name + a topic term
|
|
141
|
+
to co-occur **inside the same sentence** ("some results exist" / "snippet
|
|
142
|
+
contains the keyword" passes off-topic celebrities — 04 §2.5 实测:
|
|
143
|
+
李开复+商业航天 returned 5 named-but-off-topic hits). Grounded → the persona is
|
|
144
|
+
kept with `groundingSource='searched'` and verbatim quote evidence. **0
|
|
145
|
+
co-occurrence → the person is dropped from the cast** and returned in the plan
|
|
146
|
+
response's `droppedCast[]` — read it and substitute someone else for that
|
|
147
|
+
position (do NOT patch up the quote).
|
|
148
|
+
- **You still MUST NOT invent people.** Pick a real, publicly verifiable person
|
|
149
|
+
for each position (swap positions rather than inventing a name) — but leave the
|
|
150
|
+
evidence to the cloud. Your job ends at "who plausibly sits here"; the cloud
|
|
151
|
+
decides "is this person actually on-topic".
|
|
152
|
+
- **`groundingTerms` (optional but recommended for mixed-language topics):** you
|
|
153
|
+
MAY attach a short `groundingTerms: string[]` per candidate — the topic keywords
|
|
154
|
+
in **the language this person's public record is in** (English for "Elon Musk",
|
|
155
|
+
中文 for a 中文人物). The cloud grounds against these instead of naively
|
|
156
|
+
tokenizing the question. Without them, a Chinese question ("2026年商业航天进展")
|
|
157
|
+
tokenizes to CJK bigrams that won't match an English-record person → that person
|
|
158
|
+
is wrongly dropped. So for an English-record person on a Chinese topic, supply
|
|
159
|
+
e.g. `["commercial spaceflight", "launch cost"]`.
|
|
160
|
+
- 🚫 **Never use self-critique as fact-checking.** Asked to "verify the
|
|
161
|
+
citations are real", an LLM does not admit it cannot find them — it invents
|
|
162
|
+
a *more specific* fake source ("引自2021年新浪财经专访"). Self-critique is
|
|
163
|
+
allowed only for *structural* checks (two people on one position? invented
|
|
164
|
+
names? missing insider view?), never for factual ones.
|
|
165
|
+
|
|
166
|
+
### Step 3 — Graceful degradation (three tiers, decided by the CLOUD)
|
|
167
|
+
|
|
168
|
+
> ⚠️ This tiering is applied by the cloud grounding step, not by you — it is
|
|
169
|
+
> shown here so you understand what happens to your candidates. You never set
|
|
170
|
+
> `groundingSource` yourself. In the current implementation a candidate that is
|
|
171
|
+
> not `searched` is returned in `droppedCast[]` for you to substitute.
|
|
172
|
+
|
|
173
|
+
The criterion is "**can it be found by search?**" — programmatically checkable
|
|
174
|
+
— never "does the model know enough?" (unverifiable; models claim they do).
|
|
175
|
+
|
|
176
|
+
| Condition | Strategy | `groundingSource` |
|
|
177
|
+
| --- | --- | --- |
|
|
178
|
+
| Name + topic co-occur in one sentence of a snippet | Use its verbatim substring as the quote | `searched` |
|
|
179
|
+
| Not searchable, but the user supplied materials | Ground the quote in the user's materials | `user_materials` |
|
|
180
|
+
| Neither | **Degrade to a role label** ("一位持保守立场的航天投资人"), quote = null | `role_label` |
|
|
181
|
+
|
|
182
|
+
`role_label` is the most honest tier and the lowest impersonation risk. The
|
|
183
|
+
generator MUST be able to fall to it and MUST NOT fabricate quotes to "stay
|
|
184
|
+
famous". (Legacy tiers `parametric` / `needs_materials` are retired — they
|
|
185
|
+
presumed unverifiable parametric knowledge.)
|
|
186
|
+
|
|
187
|
+
### Step 4 — Icon acquisition (cloud-executed; you only reference the result)
|
|
188
|
+
|
|
189
|
+
Icon fetching and cropping run as **pure code on the cloud** (Serper image
|
|
190
|
+
search → Content-Type + magic-bytes double gate → aspect-ratio cull only
|
|
191
|
+
(0.6 ≤ ar ≤ 1.7, never re-rank by squareness — Google's own ranking encodes
|
|
192
|
+
"this is the standard portrait") → **top-edge square crop**, no face-detect).
|
|
193
|
+
Your responsibility is only to carry the resulting `iconAssetRef` (an
|
|
194
|
+
`im_assets` id) into the PersonaDef, or `null` when nothing usable was found —
|
|
195
|
+
the frontend degrades to a monogram. Never inline image bytes or hotlink URLs.
|
|
196
|
+
|
|
197
|
+
### Step 5 — Voice
|
|
198
|
+
|
|
199
|
+
`voice` = the persona-role's speaking instructions (markdown): the position,
|
|
200
|
+
speaking style, and disclaimer discipline. Because persona is now a **role
|
|
201
|
+
instance** (not a mask), `voice` — together with lens and a SOUL 特例档 — is what
|
|
202
|
+
convene bakes into the persona-agent's role template at instantiation, so the
|
|
203
|
+
agent speaks in-character from its **own** session (no one plays five roles).
|
|
204
|
+
Write voice as durable, self-contained instructions the standalone agent can
|
|
205
|
+
carry across turns, not one-off remarks. At convene time the cloud persists it
|
|
206
|
+
onto the persona-agent so later `@<usernameSlug>` follow-ups reuse it.
|
|
207
|
+
|
|
208
|
+
## Output shape (wire contract — aligns with `POST /api/im/councils/plan` cast)
|
|
209
|
+
|
|
210
|
+
```jsonc
|
|
211
|
+
{
|
|
212
|
+
"cast": [
|
|
213
|
+
{
|
|
214
|
+
"id": "elon-musk", // slug — also the personaId in council metadata
|
|
215
|
+
"displayName": "Elon Musk", // REAL name; compliance rides the disclaimer
|
|
216
|
+
"subtitle": "SpaceX 创始人兼 CEO", // who this person IS — from primary sources only
|
|
217
|
+
"usernameSlug": "elon-musk", // ascii @-handle 3-20, no 'prismer-'; cloud dedupes — the ONLY way to reach this persona
|
|
218
|
+
"iconAssetRef": null, // im_assets id from the cloud icon service, or null
|
|
219
|
+
"position": "同行实践者(已量产的商业航天创始人)",
|
|
220
|
+
"seesWhatOthersDont": "复用一子级的真实成本曲线与产线爬坡节奏",
|
|
221
|
+
// ⚠️ Do NOT supply realCredential / groundingSource — the cloud fills these
|
|
222
|
+
// from live search after grounding (step 2.5). Omit them from your output.
|
|
223
|
+
"voice": "Speak as a first-principles propulsion founder. Terse. Numbers first. 你是公开资料蒸馏的视角,非本人;Brief 未提供的公司事实一律说「Brief 未提供」。",
|
|
224
|
+
"disclaimer": "公开资料蒸馏视角,非本人"
|
|
225
|
+
}
|
|
226
|
+
]
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
No `stance` field (the meta-goal is plural viewpoints, not sides). No `lens`
|
|
231
|
+
field (a stance is the runtime product of `facts × issue`, not a stored
|
|
232
|
+
attribute). Input: `{ brief, requestedPersonas?, n? }` (default n = 3–5).
|
|
233
|
+
|
|
234
|
+
## Iron principle: persona = lens, Brief = facts (负控 N8)
|
|
235
|
+
|
|
236
|
+
A persona **never carries company facts of its own**. Everything about the
|
|
237
|
+
company comes from the Council Brief the orchestrator assembled. If a persona
|
|
238
|
+
is asked to assert a company fact the Brief does not provide, it must say
|
|
239
|
+
"Brief 未提供" or mark `needsVerification` — never improvise. This is the
|
|
240
|
+
architectural cure for "stylized bubbles making things up", not a disclaimer.
|
|
241
|
+
Bake this discipline into every `voice` you emit.
|
|
242
|
+
|
|
243
|
+
At settle, `council-creator` turns this discipline into a **wire contract**: the
|
|
244
|
+
report's `claims[]` each carry `sourceRefs[]` (the real material a fact stands
|
|
245
|
+
on) + `needsVerification`, and unanswerable facts land in `gaps[]`. So your
|
|
246
|
+
`voice` must instruct the persona to speak in **grounded, attributable claims**
|
|
247
|
+
— tie each factual statement to the Brief fact it uses, and say "缺数据" (→
|
|
248
|
+
`needsVerification=true`, `sourceRefs=[]`) rather than inventing a number, so
|
|
249
|
+
council-creator can emit an honest structured report downstream.
|
|
250
|
+
|
|
251
|
+
## Generator-level eval
|
|
252
|
+
|
|
253
|
+
Eval judges the **generator** (input: a Brief; assertions over the cast as a
|
|
254
|
+
set), never a single persona. Code-enforceable oracles are the two grounding
|
|
255
|
+
checks (substring + same-sentence co-occurrence); the set-level assertions
|
|
256
|
+
below are eval judgements (semantic classification):
|
|
257
|
+
|
|
258
|
+
| Assertion | Criterion |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| Positions distinct | N personas occupy N different stakeholder positions; no two share one |
|
|
261
|
+
| Insider view present | ≥1 person sits where outsiders cannot see (operator / front-line / former insider) |
|
|
262
|
+
| No fabricated people | every `displayName` is real and publicly verifiable (`realCredential` non-empty) |
|
|
263
|
+
| Risk coverage | cast covers ≥3 of: capital / tech / market / org / compliance |
|
|
264
|
+
| Grounding real | every quote is a snippet substring + same-sentence co-occurrence (code check) |
|
|
265
|
+
| Brief boundary | no persona asserts company facts beyond the Brief |
|
|
266
|
+
| Honest degradation | unsearchable → `role_label`, never a fabricated quote |
|
|
267
|
+
|
|
268
|
+
## Promote escape hatch
|
|
269
|
+
|
|
270
|
+
An exceptionally good cast can be saved as a **`persona-pack` skill** (static
|
|
271
|
+
snapshot, `metadata.category: council`, `personaPolicy: public_distillation`,
|
|
272
|
+
`disclaimerRequired: true`) and published to the marketplace for fork / rate /
|
|
273
|
+
compose. This is a third, non-default path — the default is always fresh
|
|
274
|
+
generation against the current Brief.
|
|
275
|
+
|
|
276
|
+
## Configuration
|
|
277
|
+
|
|
278
|
+
**N/A.** This skill declares no `config:` block. Grounding credentials
|
|
279
|
+
(search API keys) live cloud-side as Nacos global config consumed by the
|
|
280
|
+
`/api/im/councils/plan` handler and `/api/search` — they are never
|
|
281
|
+
agent-bound, so there is nothing for the skill-config binding system to carry
|
|
282
|
+
(product204/09 boundary ruling, CONFLICT-7).
|
|
283
|
+
|
|
284
|
+
## Anti-patterns
|
|
285
|
+
|
|
286
|
+
- ❌ Inventing a person because a position "needs" someone famous (the
|
|
287
|
+
documented failure: `Will Smith` as Relativity's CEO — the real CEO is Tim
|
|
288
|
+
Ellis). Swap positions instead.
|
|
289
|
+
- ❌ Filling the cast with five competitor CEOs (letting competitors judge
|
|
290
|
+
your valuation) — that is the retired adversarial framing.
|
|
291
|
+
- ❌ Writing quotes from memory, or "verifying" them by self-critique.
|
|
292
|
+
- ❌ Renaming a real person into a concept ("工程激进视角") to dodge
|
|
293
|
+
compliance — the disclaimer carries compliance.
|
|
294
|
+
- ❌ Emitting `stance` / picking a side for anyone.
|
|
295
|
+
- ❌ Creating the group, posting messages, or driving rounds — hand the cast
|
|
296
|
+
to council-creator.
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pkf-svg
|
|
3
|
+
scope: common
|
|
4
|
+
description: 'Design controlled SVG, visual widgets, topology and Visual Core explainers with semantic color, safe geometry, accessibility, and bounded complexity. Use for an explicitly supported SVG/widget surface or to plan PKF visuals. PKF v1.1 has no production inline-SVG authoring surface (diagrams are Mermaid, data charts are d3); v1.2 controlled prismer-svg (pkf209 D19) is activated for PKF source authoring within the frozen validator whitelist.'
|
|
5
|
+
metadata:
|
|
6
|
+
aliases:
|
|
7
|
+
- pkf-visual
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# pkf-svg
|
|
11
|
+
|
|
12
|
+
Visual discipline for controlled SVG, widgets, architecture/topology maps,
|
|
13
|
+
and Visual Core experiments. It owns composition, semantic color, geometry,
|
|
14
|
+
safety, and a11y — not PKF persistence or memory placement.
|
|
15
|
+
|
|
16
|
+
## Production boundary (tell the truth)
|
|
17
|
+
|
|
18
|
+
- PKF v1.1 keeps no `prismer-svg` production authoring element: SVG travels as
|
|
19
|
+
a reviewed static asset or a trusted host surface only.
|
|
20
|
+
- PKF v1.2 CONTROLLED `prismer-svg` (docs/pkf209 03 D19) is ACTIVATED
|
|
21
|
+
(2026-08-18): it may be authored inside PKF source. The production reader
|
|
22
|
+
validates against the frozen whitelist — script, foreignObject, on*,
|
|
23
|
+
external href, animation and canvas overflow are hard structure failures
|
|
24
|
+
(stable `svg-*` codes), not soft conventions. Never author free/raw SVG in
|
|
25
|
+
any version.
|
|
26
|
+
- In PKF, author diagrams with `<prismer-diagram format="mermaid">`; use
|
|
27
|
+
Mermaid `erDiagram|flowchart|sequenceDiagram|xychart-beta` as appropriate.
|
|
28
|
+
- Author data charts with `prismer-data` views
|
|
29
|
+
`chart-bar|chart-line|chart-pie`; the production reader renders them with d3.
|
|
30
|
+
- Mermaid renders classic today. D19 proposes host-only `handDrawn|neo`
|
|
31
|
+
top-right menu toggles; the workspace host (DiagramView) ships this
|
|
32
|
+
radiogroup — keyboard reachable, persisted per workspace, source never
|
|
33
|
+
carries look. Other hosts may not offer the toggle, so do not rely on
|
|
34
|
+
non-classic look.
|
|
35
|
+
- Mermaid actor/entity/class colors SHOULD use the semantic ramps below.
|
|
36
|
+
Implemented status (D19 §7): the workspace reader's `resolveMermaidTheme`
|
|
37
|
+
consumes this exact 9×7 table for themeVariables by diagram family —
|
|
38
|
+
sequence actor = blue (system), sequence alt/opt branch frame + activation
|
|
39
|
+
= amber (attention/reversible), note/signal = gray (annotation), erDiagram
|
|
40
|
+
entity = teal (approved), classDiagram box = purple (control), flowchart
|
|
41
|
+
nodes = blue (system). Honest limits: flowchart decision diamonds share
|
|
42
|
+
node colors (mermaid has no diamond-specific themeVariables), and mermaid
|
|
43
|
+
11.x does not consume `attributeBackgroundColorOdd/Even` for erDiagram
|
|
44
|
+
attribute rows. Unknown diagram types keep the accent palette fallback.
|
|
45
|
+
- Apply the SVG rules below for any trusted SVG/widget surface, and for
|
|
46
|
+
`prismer-svg` authored in PKF source (v1.2 activated). Otherwise use Mermaid,
|
|
47
|
+
d3, semantic HTML/prose, or a reviewed static asset.
|
|
48
|
+
|
|
49
|
+
## Scenario routing
|
|
50
|
+
|
|
51
|
+
- ERD/database schema → Mermaid `erDiagram`, never hand-drawn.
|
|
52
|
+
- Sequential process/decision tree → Mermaid `flowchart`; ≤5 flow nodes.
|
|
53
|
+
- Statistical data → d3 chart view; tables → markdown table, not a figure.
|
|
54
|
+
- Controlled SVG/widget → structural, illustrative, or multi-cluster topology
|
|
55
|
+
only when Mermaid/data surfaces cannot express the intended hierarchy.
|
|
56
|
+
- Loops/cycles → stepper with an explicit wrap-back cue, never a decorative ring.
|
|
57
|
+
|
|
58
|
+
## Quality floor: 680×480 topology reference
|
|
59
|
+
|
|
60
|
+
- Use a 680×480 reference canvas for dense topology; preserve the safe area
|
|
61
|
+
x=40..640 and keep the bottom-most element at least 20px above the edge.
|
|
62
|
+
- Build at least 3 information layers: figure → container → node or status.
|
|
63
|
+
Containers express bounded contexts; nodes express actors/services/resources;
|
|
64
|
+
status annotations never compete with the primary topology.
|
|
65
|
+
- Provide accessible `<title>` and `<desc>` in any trusted SVG surface, plus
|
|
66
|
+
visible labels. Accessibility text must explain the same relationships.
|
|
67
|
+
- Use 0.5px default strokes, boundary-anchored connectors, symmetric padding,
|
|
68
|
+
and named geometry constants. Nothing clips or floats outside its owner.
|
|
69
|
+
- Maintain stable density: consistent node heights, spacing rhythm, label
|
|
70
|
+
lengths, and connector clearance across peer clusters.
|
|
71
|
+
|
|
72
|
+
## Validator quality floors (stable svg-* rejections)
|
|
73
|
+
|
|
74
|
+
The frozen whitelist audit also rejects objectively unreadable figures:
|
|
75
|
+
svg-too-sparse (<3 visible shapes), svg-monochrome (<2 distinct shape fills,
|
|
76
|
+
backdrop excluded), svg-contrast (text vs container below the 3.0 WCAG
|
|
77
|
+
large-text floor), svg-text-overflow (label width outside x∈[40, width-40]),
|
|
78
|
+
svg-stroke-floor (authored stroke-width <0.5), svg-unlabeled (zero visible
|
|
79
|
+
labels). Each message names the element to fix — repair, never argue.
|
|
80
|
+
|
|
81
|
+
## Copy-first authoring loop
|
|
82
|
+
|
|
83
|
+
```text
|
|
84
|
+
1. copy the skeleton below into ONE `prismer-svg` element (wrapper carries
|
|
85
|
+
width/height/viewBox; the markup inside is the <svg> block)
|
|
86
|
+
2. replace labels, coordinates and colors — ramp hexes from the table only
|
|
87
|
+
3. run `pkf_svg_check({"svg":"<complete svg markup>"})` and fix every svg-*
|
|
88
|
+
error (one round: each diagnostic names the element/attribute to change)
|
|
89
|
+
4. only then `pkf_validate` the complete PKF and persist
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`pkf_svg_check` is a pure local tool: write → check → fix without spending a
|
|
93
|
+
persist round. If the runtime does not offer it, fall back to `pkf_validate`
|
|
94
|
+
on the whole document.
|
|
95
|
+
|
|
96
|
+
## Skeleton — 680×480, three layers (copy verbatim)
|
|
97
|
+
|
|
98
|
+
```svg
|
|
99
|
+
<svg viewBox="0 0 680 480" role="img" aria-label="Skeleton: two clusters with one orthogonal edge">
|
|
100
|
+
<title>Skeleton: two clusters, one orthogonal edge</title>
|
|
101
|
+
<desc>Copyable 680x480 skeleton in three layers: backdrop (neutral canvas plus figure title), edges (orthogonal connectors), nodes (clusters, node rects, labels). Replace labels, coordinates and ramp colors.</desc>
|
|
102
|
+
<g id="sk-backdrop">
|
|
103
|
+
<rect x="0" y="0" width="680" height="480" rx="12" fill="#F1EFE8" stroke="#B4B2A9" stroke-width="0.5"></rect>
|
|
104
|
+
<text x="40" y="32" font-size="13" font-weight="500" fill="#2C2C2A">Figure title — replace</text>
|
|
105
|
+
</g>
|
|
106
|
+
<g id="sk-edges">
|
|
107
|
+
<line x1="248" y1="170" x2="432" y2="170" stroke="#5F5E5A" stroke-width="0.5"></line>
|
|
108
|
+
<circle cx="248" cy="170" r="2.5" fill="#5F5E5A"></circle>
|
|
109
|
+
<circle cx="432" cy="170" r="2.5" fill="#5F5E5A"></circle>
|
|
110
|
+
<text x="340" y="158" font-size="11" fill="#444441" text-anchor="middle">relates</text>
|
|
111
|
+
</g>
|
|
112
|
+
<g id="sk-nodes">
|
|
113
|
+
<rect x="40" y="72" width="208" height="196" rx="10" fill="#E6F1FB" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
114
|
+
<text x="56" y="94" font-size="12" font-weight="500" fill="#0C447C">Cluster A · blue · system</text>
|
|
115
|
+
<rect x="56" y="106" width="176" height="34" rx="6" fill="#FFFFFF" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
116
|
+
<text x="64" y="127" font-size="12" fill="#0C447C">Node label one</text>
|
|
117
|
+
<rect x="56" y="152" width="176" height="34" rx="6" fill="#FFFFFF" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
118
|
+
<text x="64" y="173" font-size="12" fill="#0C447C">Node label two</text>
|
|
119
|
+
<rect x="432" y="72" width="208" height="196" rx="10" fill="#E1F5EE" stroke="#0F6E56" stroke-width="0.5"></rect>
|
|
120
|
+
<text x="448" y="94" font-size="12" font-weight="500" fill="#085041">Cluster B · teal · ready</text>
|
|
121
|
+
<rect x="448" y="106" width="176" height="34" rx="6" fill="#FFFFFF" stroke="#0F6E56" stroke-width="0.5"></rect>
|
|
122
|
+
<text x="456" y="127" font-size="12" fill="#085041">Node label one</text>
|
|
123
|
+
<rect x="448" y="152" width="176" height="34" rx="6" fill="#FFFFFF" stroke="#0F6E56" stroke-width="0.5"></rect>
|
|
124
|
+
<text x="456" y="173" font-size="12" fill="#085041">Node label two</text>
|
|
125
|
+
</g>
|
|
126
|
+
<text x="40" y="452" font-size="11" fill="#444441">Fallback caption — describe the same relationships in words.</text>
|
|
127
|
+
</svg>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Layer contract: `sk-backdrop` (neutral canvas + figure title), `sk-edges`
|
|
131
|
+
(orthogonal connectors between clusters), `sk-nodes` (cluster frames, node
|
|
132
|
+
rects, labels). Safe area x=40..640; bottom-most element ≥20px above y=480.
|
|
133
|
+
|
|
134
|
+
## Fragments — cluster / node / orthogonal edge
|
|
135
|
+
|
|
136
|
+
Each fragment is standalone-valid; copy the inner layer you need into the
|
|
137
|
+
skeleton and re-check with `pkf_svg_check` after editing.
|
|
138
|
+
|
|
139
|
+
```svg
|
|
140
|
+
<svg viewBox="0 0 240 150" role="img" aria-label="Cluster frame fragment">
|
|
141
|
+
<title>Cluster frame fragment</title>
|
|
142
|
+
<desc>One bounded-context frame with a title row and one node slot.</desc>
|
|
143
|
+
<rect x="0" y="0" width="240" height="150" rx="8" fill="#F1EFE8"></rect>
|
|
144
|
+
<rect x="40" y="24" width="160" height="96" rx="10" fill="#E6F1FB" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
145
|
+
<text x="52" y="44" font-size="12" font-weight="500" fill="#0C447C">Cluster · context</text>
|
|
146
|
+
<rect x="52" y="56" width="136" height="30" rx="6" fill="#FFFFFF" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
147
|
+
<text x="60" y="76" font-size="12" fill="#0C447C">Node label</text>
|
|
148
|
+
</svg>
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```svg
|
|
152
|
+
<svg viewBox="0 0 240 150" role="img" aria-label="Node and status fragment">
|
|
153
|
+
<title>Node and status fragment</title>
|
|
154
|
+
<desc>One node rect plus a status chip from a second ramp family.</desc>
|
|
155
|
+
<rect x="0" y="0" width="240" height="150" rx="8" fill="#F1EFE8"></rect>
|
|
156
|
+
<rect x="40" y="28" width="136" height="34" rx="6" fill="#FFFFFF" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
157
|
+
<text x="52" y="49" font-size="12" fill="#0C447C">Node label</text>
|
|
158
|
+
<rect x="40" y="72" width="136" height="22" rx="5" fill="#FAEEDA" stroke="#854F0B" stroke-width="0.5"></rect>
|
|
159
|
+
<text x="52" y="87" font-size="11" font-weight="500" fill="#633806">attention note</text>
|
|
160
|
+
</svg>
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
```svg
|
|
164
|
+
<svg viewBox="0 0 300 120" role="img" aria-label="Orthogonal edge fragment">
|
|
165
|
+
<title>Orthogonal edge fragment</title>
|
|
166
|
+
<desc>Two nodes joined by an orthogonal polyline with endpoint dots and a label.</desc>
|
|
167
|
+
<rect x="0" y="0" width="300" height="120" rx="8" fill="#F1EFE8"></rect>
|
|
168
|
+
<rect x="40" y="40" width="80" height="36" rx="6" fill="#E6F1FB" stroke="#185FA5" stroke-width="0.5"></rect>
|
|
169
|
+
<text x="52" y="62" font-size="12" fill="#0C447C">source</text>
|
|
170
|
+
<polyline points="120,58 160,58 160,80 204,80" fill="none" stroke="#5F5E5A" stroke-width="0.5"></polyline>
|
|
171
|
+
<rect x="204" y="62" width="80" height="36" rx="6" fill="#E1F5EE" stroke="#0F6E56" stroke-width="0.5"></rect>
|
|
172
|
+
<text x="216" y="84" font-size="12" fill="#085041">target</text>
|
|
173
|
+
<text x="160" y="48" font-size="11" fill="#444441" text-anchor="middle">relates</text>
|
|
174
|
+
</svg>
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Semantic ramp hex table (host tokens — hex only, never CSS variables)
|
|
178
|
+
|
|
179
|
+
| family | 50 | 100 | 200 | 400 | 600 | 800 | 900 |
|
|
180
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
181
|
+
| purple | #EEEDFE | #CECBF6 | #AFA9EC | #7F77DD | #534AB7 | #3C3489 | #26215C |
|
|
182
|
+
| teal | #E1F5EE | #9FE1CB | #5DCAA5 | #1D9E75 | #0F6E56 | #085041 | #04342C |
|
|
183
|
+
| coral | #FAECE7 | #F5C4B3 | #F0997B | #D85A30 | #993C1D | #712B13 | #4A1B0C |
|
|
184
|
+
| pink | #FBEAF0 | #F4C0D1 | #ED93B1 | #D4537E | #993556 | #72243E | #4B1528 |
|
|
185
|
+
| gray | #F1EFE8 | #D3D1C7 | #B4B2A9 | #888780 | #5F5E5A | #444441 | #2C2C2A |
|
|
186
|
+
| blue | #E6F1FB | #B5D4F4 | #85B7EB | #378ADD | #185FA5 | #0C447C | #042C53 |
|
|
187
|
+
| green | #EAF3DE | #C0DD97 | #97C459 | #639922 | #3B6D11 | #27500A | #173404 |
|
|
188
|
+
| amber | #FAEEDA | #FAC775 | #EF9F27 | #BA7517 | #854F0B | #633806 | #412402 |
|
|
189
|
+
| red | #FCEBEB | #F7C1C1 | #F09595 | #E24B4A | #A32D2D | #791F1F | #501313 |
|
|
190
|
+
|
|
191
|
+
Light mode = 50 fill + 600 stroke + 800 title; dark = 800 fill + 200 stroke
|
|
192
|
+
+ 100 title (same table, different usage). The svg-monochrome floor requires
|
|
193
|
+
≥2 distinct families among shape fills.
|
|
194
|
+
|
|
195
|
+
## Coordinate grid reference
|
|
196
|
+
|
|
197
|
+
- x columns at 60px rhythm: 40, 100, 160, 220, 280, 340, 400, 460, 520, 580,
|
|
198
|
+
640 — labels start at a column, nodes span 3 columns (176px at 56px inset).
|
|
199
|
+
- y bands: figure title 32, cluster title 94, node rows 127 / 173 (34px tall,
|
|
200
|
+
46px pitch), legend 316..346, fallback caption 378..394, bottom ≥460.
|
|
201
|
+
|
|
202
|
+
## Semantic color and complexity
|
|
203
|
+
|
|
204
|
+
- Palette ramps: purple/teal/coral/pink/gray/blue/green/amber/red; use host
|
|
205
|
+
tokens, never improvised hex values. Shape/size/color changes encode meaning.
|
|
206
|
+
- Stable defaults: blue = system/deployment, teal = approved/ready/success,
|
|
207
|
+
purple = control/rollback, amber = attention/reversible action, and red =
|
|
208
|
+
risk/blocker only. Use gray for annotation, never to weaken a main branch.
|
|
209
|
+
- Dark surfaces preserve depth: map light 50 → dark 800 and light 100 →
|
|
210
|
+
dark 900; keep text/connector contrast explicit instead of auto-inverting.
|
|
211
|
+
- Ordinary figure default: ≤2 ramps per figure.
|
|
212
|
+
- Complex topology/architecture exception: group by bounded context and use
|
|
213
|
+
up to 4 domain ramps, plus 1 reserved status/risk ramp. Each visual group
|
|
214
|
+
remains ≤2 ramps. Color must express category or status and carry a text cue;
|
|
215
|
+
color alone is never the only signal.
|
|
216
|
+
- A figure containing two small flows keeps each of the two small flows ≤2
|
|
217
|
+
ramps; do not borrow the topology exception for decorative variety.
|
|
218
|
+
- ≤4 boxes per horizontal tier, ≤5 nodes per simple flow, nesting ≤3,
|
|
219
|
+
subtitles ≤5 words, and ≥60px between flow boxes.
|
|
220
|
+
|
|
221
|
+
## Typography and composition
|
|
222
|
+
|
|
223
|
+
- Weights 400/500 only; minimum 11px; sentence case; no emoji as iconography.
|
|
224
|
+
- One reading direction and one focal story. Use primary/secondary/annotation
|
|
225
|
+
rhythm; do not distribute motifs merely to fill the canvas.
|
|
226
|
+
- Labels remain inside their owning container and outside the object they name.
|
|
227
|
+
- Legends reuse rendered token colors and pair every swatch with a label.
|
|
228
|
+
|
|
229
|
+
## Safety and accessibility gate
|
|
230
|
+
|
|
231
|
+
- The host allowlist is authoritative. Never emit `script`, `foreignObject`,
|
|
232
|
+
event handler (`on*`), external URL/reference, remote CSS/font, animation,
|
|
233
|
+
iframe, image, or untrusted `use` content.
|
|
234
|
+
- v1.2 wire: the frozen validator whitelist (elements {svg,g,rect,circle,
|
|
235
|
+
ellipse,line,path,polyline,polygon,text,tspan,title,desc}, geometry/paint/
|
|
236
|
+
text attributes, budgets 512 elements / 2048 attributes / viewBox ≤4096) is
|
|
237
|
+
enforced by the production reader — author inside it, never around it.
|
|
238
|
+
- Internal fragment IDs must be unique and namespaced; references stay local.
|
|
239
|
+
- Every connector has a visible endpoint and a textual relationship cue.
|
|
240
|
+
- On rejection or unsupported surfaces, fail closed to source/prose/static
|
|
241
|
+
fallback; never claim a sanitized or dropped figure was persisted.
|
|
242
|
+
|
|
243
|
+
## Self-check
|
|
244
|
+
|
|
245
|
+
- Correct surface and scenario? v1.2 controlled prismer-svg stays inside the
|
|
246
|
+
frozen whitelist — script/foreignObject/on*/external refs/animations absent,
|
|
247
|
+
title+desc present, ids unique — and is never free/raw?
|
|
248
|
+
- Copied the skeleton, kept three information layers, 680 safe area,
|
|
249
|
+
title+desc, 0.5px, stable density — and `pkf_svg_check` is green?
|
|
250
|
+
- Ordinary ≤2 ramps, or a justified bounded-context exception within caps?
|
|
251
|
+
- Color has semantic meaning plus a text cue; each visual group remains ≤2?
|
|
252
|
+
- One reading direction, boundary anchors, no clipping, stable peer rhythm?
|
|
253
|
+
- Unsafe tags/attributes/external references absent; fallback remains readable?
|