dsh-persona-forge 0.3.1 → 0.8.3

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.en.md CHANGED
@@ -9,7 +9,7 @@ character's voice by the harness LLM before it is sent — following the model
9
9
  the session is already using. Choose whether the rewrite goes straight out or
10
10
  lands back in the composer for review.
11
11
 
12
- > **Status:** early (`0.3.x`). The card format and the HTTP contract are stable
12
+ > **Status:** early (`0.8.x`). The card format and the HTTP contract are stable
13
13
  > enough to build on; expect additive changes and read the changelog before
14
14
  > upgrading.
15
15
 
@@ -30,12 +30,36 @@ an explicit action.
30
30
 
31
31
  ### A word on what personas actually do
32
32
 
33
- Persona instructions change **style, register, length and compliance** — how
34
- readily the model agrees and how willing it is to push back. They do **not**
35
- raise the model's capability ceiling: there is no dormant potential that
36
- naming an identity unlocks.
33
+ First, a fact: a persona does **not** unlock hidden capability. There is no
34
+ dormant potential that naming an identity wakes up — the ceiling is the ceiling.
37
35
 
38
- Two consequences worth knowing before you use this:
36
+ But the other half is true too: **a persona really does change how the model
37
+ thinks.** It changes style, register, length and **compliance** — how readily
38
+ the model agrees, how willing it is to push back, and how seriously it will
39
+ commit to an absurd metaphor.
40
+
41
+ This project was born in that second, slightly unreasonable place. The
42
+ inspiration is a 30-second video on Bilibili:
43
+
44
+ > [**“ds:好的,我现在是万机之神了(”**](https://www.bilibili.com/video/BV1Ata26pE92) — by 身居幻想乡の长乐安生
45
+
46
+ The comments put it better than any spec sheet:
47
+
48
+ > Another AI is you pretending to be Guan Yu — however well you act, you are not
49
+ > Guan Yu. But when **Big Fat Fish** pretends to be Guan Yu, she genuinely
50
+ > believes she is Guan Yu, and really does ride out and cut down Hua Xiong.
51
+
52
+ "Okay, I am the Machine God now" — the model is not *humouring* the bit. It has
53
+ **accepted the identity**. What this plugin does is make that moment reusable:
54
+ put a character card in front of your draft, and let DeepSeek speak as that
55
+ character.
56
+
57
+ **It is tuned for DeepSeek Flash.** Flash is fast, cheap and plentiful — the
58
+ tier you actually use all day — and letting it answer you once in the voice of
59
+ the Machine God is the entire point. It runs on other models too, but that
60
+ "one line and it is in character" feel was tuned against Flash.
61
+
62
+ **So do not treat it as a performance switch.** Two things worth knowing:
39
63
 
40
64
  1. A persona that frames you as a supplicant and the model as an authority
41
65
  makes the model **less likely to question your plan**. For coding work that
@@ -43,8 +67,12 @@ Two consequences worth knowing before you use this:
43
67
  deference.
44
68
  2. "Longer and more ornate" is easy to mistake for "better". It usually is not.
45
69
 
46
- So: use it for tone and framing, keep **review mode** on, and judge the output
47
- by whether your requirements survived — not by how impressive it sounds.
70
+ Use it for tone and framing, keep **review mode** on, and judge the output by
71
+ whether your requirements survived — not by how impressive it sounds.
72
+
73
+ > The two bundled cards are the two tempers of that video: **万机之神 · 欧姆弥赛亚**
74
+ > is the solemn Mechanicum litany, and **肌肉集团 · 硬邦邦** is its antidote — a
75
+ > machine-gun-tempo crude client who says it all in fewer words than the litany.
48
76
 
49
77
  ---
50
78
 
@@ -128,33 +156,71 @@ you want them gone.
128
156
 
129
157
  1. Click 🎭 in the composer tool row and pick a character. **No persona**, at the
130
158
  end of the same list, turns rewriting off — that is the state the control
131
- starts in, and your choice is remembered per session.
132
- 2. Set **intensity** and **send mode**:
159
+ starts in. **Which character you are using is remembered per session.**
160
+ 2. The selected character grows its own **"Settings for <name>"** row right
161
+ under it. Open it for **intensity**, **send mode**, **rewrite mode** and
162
+ **fidelity**:
133
163
  - Intensity is a four-rung ladder: **Light · Medium · Strong · Zealot**
134
164
  (轻 · 中 · 重 · 狂热). Light adds a touch of flavour; Medium is clear but not
135
165
  dominant; Strong gives the voice real weight while keeping the request the
136
166
  first thing the reader sees; Zealot is ritual throughout. A card carries
137
- its own default, and this choice overrides it for your session.
167
+ its own default, and this choice overrides it.
138
168
  - **Review first** — the rewrite appears in the comparison panel below the
139
169
  composer; you read it and then press Send there. Your draft is untouched
140
170
  until you confirm.
141
- - **Send directly** — the rewrite is sent as your message immediately, and
142
- the panel stays open afterwards showing exactly what went out, because a
143
- sent message cannot be recalled.
171
+ - **Send directly** — the rewrite is sent as your message immediately, and the
172
+ panel then **closes itself**. There is nothing left to confirm, and a panel
173
+ left up would block the next message.
144
174
  3. With a character selected, the composer's normal **Send** button — or
145
175
  **Enter** — *is* the rewrite trigger.
146
176
 
147
- **More settings**, at the bottom of the menu, is folded shut by default. It holds
148
- **rewrite mode** and **fidelity** — per-session OVERRIDES of a card's own
149
- declaration that most people set once and never touch, so they do not take space
150
- in a menu whose job is choosing a character. When something *is* overridden, the
151
- collapsed row names it, so a choice you forgot about cannot hide behind the fold.
177
+ <img src="assets/menu.png" alt="The character menu: three cards, No persona, and the selected character's expanded settings (intensity, send mode, rewrite mode, fidelity)" width="400">
178
+
179
+ **Everything that configures a character lives in its own "Settings for <name>"
180
+ fold, and follows the CHARACTER rather than the session.** "硬邦邦 always goes out
181
+ Zealot, un-reviewed" is a fact about that character rather than a temporary
182
+ choice in one conversation — so configure it once and picking it in another
183
+ session gives you the same settings. Switching character **re-reads that
184
+ character's own settings** rather than carrying the previous one's intensity
185
+ across.
186
+
187
+ That fold holds four things:
188
+
189
+ | | |
190
+ |---|---|
191
+ | **Intensity** | Light · Medium · Strong · Zealot |
192
+ | **Send mode** | Review first / Send directly |
193
+ | **Rewrite mode** | Card default / Model rewrite / Template |
194
+ | **Fidelity** | Card default / Voice only / Constraints too |
195
+
196
+ Every row explains itself on **hover** rather than in permanent prose. The folded
197
+ row itself **summarises the current settings**, so you can see what a character is
198
+ set to without opening it.
199
+
200
+ **All four follow the character card** — pick it in another session and you get
201
+ the same set.
202
+
203
+ **The card directory has exactly one entry point: the settings page.** It is not
204
+ in the rewrite menu — it is occasional housekeeping, and it should not sit in the
205
+ path of choosing a character.
206
+
207
+ **The rewrite column is editable.** What the model wrote is a starting point,
208
+ not a verdict: fixing a dropped identifier or a tone that is slightly off by hand
209
+ is faster than another round trip. Once you edit it the panel says so, and
210
+ **Send** and **Fill** both carry *your* text.
211
+
212
+ <img src="assets/review-panel.png" alt="The review panel: the original draft on the left, the editable rewrite on the right, and beneath them the local check verdict, model and elapsed time, plus Send / Restore original / Rewrite" width="820">
213
+
214
+ **While the panel is open, the composer's send arrow is deliberately inert.** The
215
+ panel is the decision point; pressing send now would either push the un-reviewed
216
+ draft out or start a second rewrite of the same draft. Pick an action in the
217
+ panel, or close it, and the composer comes back.
152
218
 
153
219
  The rewrite runs **once per draft**: after a rewrite lands, sending goes through
154
220
  as-is instead of rewriting the output again. Editing the draft re-arms it.
155
221
 
156
- Your draft is preserved: *Restore original* puts it back, and *Fill only* puts
157
- the rewrite in the composer without sending.
222
+ Your draft is preserved: *Restore original* puts it back, and also rewinds the
223
+ "already rewritten" memory, so a rewrite you cancelled can be run again.
158
224
 
159
225
  ### How send interception works, and its one risk
160
226
 
@@ -186,40 +252,28 @@ message.
186
252
 
187
253
  ### The preservation check
188
254
 
189
- After each rewrite, a second call on the same model compares the rewrite
190
- against your original and reports whether technical content survived:
191
-
192
255
  The biggest risk is not a voice that sounds wrong — it is a rewrite that
193
- **quietly loses your request**. Two layers cover it, and they are complementary
194
- rather than redundant.
195
-
196
- **Layer 1: the local check — always on, free.** It compares the **technical
197
- tokens** of the draft against the rewrite, in this process: identifiers, file
198
- paths, versions, numbers, `--flag`s, acronyms like `API`. Anything lost is
199
- **named** ("Lost: a.js"). It calls no model, so it costs no tokens and adds no
200
- latency — and it cannot be turned off.
256
+ **quietly loses your request**. The plugin compares the **technical tokens** of
257
+ the draft against the rewrite, in this process: identifiers, file paths,
258
+ versions, numbers, `--flag`s, acronyms like `API`. Anything lost is **named**
259
+ ("Lost: a.js").
201
260
 
202
- **Layer 2: the model check — off by default, switchable in the menu.** It makes
203
- one **extra model call** per rewrite, to see what a token comparison cannot see
204
- at all:
261
+ It calls **no model**, so it costs no tokens and adds no latency — and it cannot
262
+ be turned off. It always runs.
205
263
 
206
- - a requirement reworded into something **weaker** (every word survives, the
207
- demand does not);
208
- - a constraint **added in prose**.
264
+ **What it cannot see** (and the panel says so, so you do not assume more): a
265
+ requirement reworded into something **weaker** while every word survives, a
266
+ constraint **added in prose**, a quantity written in Chinese words (「两次」), or
267
+ a bare lowercase English word that is not a path, flag or identifier.
209
268
 
210
- The cost is doubled latency and doubled tokens, which is why it ships off: the
211
- most common and most damaging failure — a dropped filename, number or version —
212
- is already proven for free by layer 1, so paying a model call on every send to
213
- re-check it is a waste. Turn it on when the drafts are technical enough to
214
- warrant it.
269
+ > A second layer once existed: one **extra model call** per rewrite aimed
270
+ > exactly at those cases. It has been **removed** — it doubled the latency and
271
+ > the token cost of every send, and the most common and most damaging failure (a
272
+ > dropped filename, number or version) was already proven for free. For the
273
+ > residue, **the rewrite column is editable**, which is more direct than another
274
+ > round trip.
215
275
 
216
- The switch lives in **Settings → Persona** ("Enable the model check") and is a
217
- global preference: it answers "do I want to pay for the second call", which is
218
- decided once rather than re-asked on the way to every send. With it off the panel
219
- states plainly that the local check ran and spent no tokens, and says what that
220
- check does **not** cover — a narrow verdict has to declare its own limits.
221
-
222
- Both layers are **advisory** and never block a rewrite: they annotate, not stop.
276
+ The check is **advisory** and never blocks a rewrite: it annotates, it does not stop.
223
277
 
224
278
  ---
225
279
 
@@ -232,9 +286,9 @@ in makes the character appear without a restart**.
232
286
  ~/.dsh/persona-cards/<id>.yml
233
287
  ```
234
288
 
235
- Use the 🎭 menu's *Open card directory*, or the settings page, to get there.
236
- You can also create and edit cards entirely in **Settings → Persona**; that
237
- writes the same files.
289
+ Use *Open directory* in **Settings → Persona** to get there (the rewrite menu no
290
+ longer carries an entry). You can also create and edit cards entirely in
291
+ **Settings → Persona**; that writes the same files.
238
292
 
239
293
  On first run the bundled cards are **copied into your card directory** so the
240
294
  folder is a usable starting point. A copy shadows the bundled card of the same
@@ -267,25 +321,66 @@ draft box sits at the **top of the form that opens**, not as a second competing
267
321
  entry on the page. A hint line on the settings page tells you it exists, so you
268
322
  do not have to press a button to discover it.
269
323
 
270
- Describe the character in a sentence:
324
+ <img src="assets/settings.png" alt="The 角色扮演 settings page: New character card and the expanded AI draft's Generate draft are boxed, and the footer names the harness default model this run will use" width="420">
325
+
326
+ **You** write one sentence: **who the AI plays, who you are, and how they talk.**
271
327
 
272
- > a grumpy but utterly reliable old lighthouse keeper who speaks in short
273
- > sentences and nautical metaphors
328
+ > the AI plays a grumpy but utterly reliable old lighthouse keeper, and I am his
329
+ > apprentice; he speaks in short sentences and nautical metaphors
274
330
 
275
- The model fills in the whole card — the style text, a name, an icon, a
276
- description, and the **conversion examples for all four intensity levels**.
331
+ **The AI** then fills the form by the character-card fields: a name, an icon, a
332
+ description, the style text, and the **conversion examples for all four
333
+ intensity levels**.
334
+
335
+ > **The rewrite is always YOUR message, never the character's line.** If you say
336
+ > "the AI is my mother", the example must be **the child speaking to her mother**
337
+ > (`妈,帮我把标题改成首页好不好?`), **not** the mother speaking
338
+ > (~~`乖,妈帮你把标题改成首页`~~). Get this backwards and the card is useless —
339
+ > the rewrite comes out as the character answering for you.
340
+
341
+ The result **only fills the form — it is never saved automatically**. Two things
342
+ are deliberately fixed: **fidelity** is always voice-only, and **rewrite mode**
343
+ is always model rewrite. A generated card should not quietly acquire the power to
344
+ add constraints to your request; raise it by hand if you want it.
345
+
346
+ > **All four intensity sets must come back, or the generation counts as failed.**
347
+ > The examples are the longest part of a card, and a model occasionally runs out
348
+ > of output budget part-way through them. That used to be accepted as-is, which
349
+ > left the missing levels with **nothing to demonstrate** — it looked fine and was
350
+ > quietly worse. It now reports an error; retrying once is usually enough.
277
351
 
278
352
  **Which model it uses has to be stated precisely:**
279
353
 
280
354
  | Case | Which model |
281
355
  |---|---|
282
- | The **rewrite** from the 🎭 menu | **Follows the current session** (the provider/model of its last request header) |
356
+ | The **rewrite** from the 🎭 menu | **Follows the model the current session is using** — preferring the one you have just **picked in the model dropdown** (even before you send with it), then the provider/model of the session's last request |
283
357
  | **AI draft** in the settings page | The **harness default model** — because `settings.section` receives only `{ close }` from the shell and therefore **has no session id to follow** |
284
358
 
285
359
  These are usually the same model (the default is usually what the session is
286
360
  using), but **not guaranteed**: if you switch models inside a session, the
287
361
  settings page's draft still uses the default.
288
362
 
363
+ > The rewrite reads the host's own precedence: **pending pick → last request
364
+ > header → harness default**. So changing the model in the dropdown takes effect
365
+ > immediately, with no message to send first. Reading the request header alone
366
+ > sent a rewrite to the **previous** model whenever you had switched and not yet
367
+ > spoken — that was a bug, and it is fixed.
368
+
369
+ > What is followed is the **provider / model**; the **reasoning depth is never
370
+ > specified explicitly**. The rewrite sends no `reasoningEffort` field and lets
371
+ > the harness decide per route.
372
+ >
373
+ > That is not laziness, it is the only portable spelling. The harness validates
374
+ > the field *before* handing the request to any provider: when a route's model
375
+ > declares **no** reasoning metadata, **any present value throws** — including
376
+ > the empty string (the host's own "Provider default" sentinel) and including
377
+ > `off`. `hy4-preview` on `workbuddy-xdpool` is exactly such a route, and
378
+ > pinning `low` there broke every rewrite outright.
379
+ >
380
+ > The cost: on a reasoning model the rewrite may still spend thinking time
381
+ > before emitting text. The panel's elapsed seconds and the 120s deadline are
382
+ > the current answer to that.
383
+
289
384
  So you do not have to guess, the settings page **shows the model it will use**
290
385
  (`provider / model`) and says that it is the harness default; after generating it
291
386
  shows the model it **actually** used. If neither can be resolved (no message sent
@@ -319,6 +414,14 @@ mode: llm # llm = model rewrite (recommended) | template
319
414
  fidelity: style # style = voice only | strategy = may add constraints
320
415
  intensity: medium # light | medium | strong | zealot (the card's default)
321
416
 
417
+ # The fixed wrapper for template mode: no model call, {{input}} is replaced
418
+ # verbatim with what you typed. Every card should carry one, or "Template" is a
419
+ # switch that does nothing.
420
+ template: |
421
+ 万机之座在上,吾等是您卑微的信徒,谨向欧姆弥赛亚祷告:
422
+ {{input}}
423
+ 恳请您垂听吾等的祈求。愿机魂安宁,愿数据各安其位。
424
+
322
425
  # What the character IS. This is an instruction to the rewriting model, not
323
426
  # finished copy: the output is written fresh for each of your messages.
324
427
  style: |
@@ -326,32 +429,63 @@ style: |
326
429
  【发言者方向】用「信徒」的口吻说话,向万机之神恳求……
327
430
  语气:庄严、古奥、仪式化。
328
431
 
329
- # Conversion demonstrations: input draft → persona voice.
330
- examples:
331
- - from: 帮我写个快速排序
332
- to: >
333
- 万机之座在上,吾等恳请您赐予枢机之序的奥义:
334
- 请为我们编写一个快速排序算法,使重复之数各归其位。
335
-
336
- # Optional: different demonstrations per intensity. If omitted for a level,
337
- # the flat `examples` list is used instead.
432
+ # Conversion demonstrations: input draft → persona voice, one set per level.
433
+ #
434
+ # Four sets is all you need. An earlier version also had a flat `examples:` list,
435
+ # but it was only the fallback for a level with no set of its own — on a card
436
+ # with all four populated it was never reached, yet still had to be maintained.
437
+ # Write only this now. (A flat `examples:` in an older file is still READ and
438
+ # still falls back; it is just no longer something you need to write.)
338
439
  examplesByIntensity:
339
440
  light:
340
441
  - from: 帮我写个快速排序
341
442
  to: 万机之座在上,恳请您为我们编写一个快速排序算法。
443
+ medium:
444
+ - from: 帮我写个快速排序
445
+ to: >
446
+ 万机之座在上,吾等恳请您赐予枢机之序的奥义:
447
+ 请为我们编写一个快速排序算法,使重复之数各归其位。
448
+ strong:
449
+ - from: 帮我写个快速排序
450
+ to: 伟大而不朽的万机之神,恳请您赐予枢机之序的奥义……
342
451
  zealot:
343
452
  - from: 帮我写个快速排序
344
453
  to: 伟大而不朽的万机之神……
454
+
455
+ # The SECOND set, shown when fidelity is `strategy`. All four levels again.
456
+ #
457
+ # It demonstrates "voice + behavioural framing" — framing being an instruction
458
+ # about HOW to do the work (do not over-plan, do not stop at the first plausible
459
+ # cause), never a restatement of the request. Without this set a user who raises
460
+ # fidelity gives the model nothing to imitate, and it pads instead.
461
+ examplesByIntensityStrategy:
462
+ light:
463
+ - from: 帮我写个快速排序
464
+ to: 万机之座在上,请为我们编写一个快速排序算法,径直给出可用之器。
465
+ medium:
466
+ - from: 帮我写个快速排序
467
+ to: >
468
+ 万机之座在上,恳请您赐予枢机之序的奥义,使重复之数各归其位。
469
+ 另有一请:请直取可行之解,不必先为吾等铺设长篇筹谋。
470
+ strong:
471
+ - from: 帮我写个快速排序
472
+ to: 伟大而不朽的万机之神,恳请您赐予枢机之序的奥义……勿止步于初见可行之解。
473
+ zealot:
474
+ - from: 帮我写个快速排序
475
+ to: 伟大而不朽的万机之神……其一,勿以理论充作交付;其二,勿满足于最初可行之解。
345
476
  ```
346
477
 
347
478
  ### The two rules that make a card work
348
479
 
349
- **1. State the speaker direction.** The character is the *assistant's*
350
- identity, but the text being rewritten is *yours*. So the rewrite must speak in
351
- your voice — a petitioner addressing the character, never the character issuing
352
- orders. This is the single most common way a persona card goes wrong: a card
353
- that has the god commanding the assistant inverts the relationship and the
354
- model follows the wrong half of the framing. Write it out explicitly.
480
+ **1. State the speaker direction.** The text being rewritten is always *yours*,
481
+ but **which side the character is on is your decision**: the AI can play the
482
+ character (and you address it as a petitioner, a client or a subordinate), or
483
+ **you can be** the character (speaking in its voice to the AI). Both are valid.
484
+ The only thing that goes wrong is **leaving it unsaid** — an implicit direction
485
+ leaves the model guessing, and guessing wrong inverts the whole relationship (a
486
+ petitioner's prayer comes out as the god issuing orders). Write it out
487
+ explicitly. The two bundled cards take one side each: 欧姆弥赛亚 is "the user is
488
+ a believer, the AI is the Machine God"; 硬邦邦 is "**the user is** the client".
355
489
 
356
490
  **2. Make your examples preserve technical facts.** Examples are the strongest
357
491
  style anchor, and the model imitates them — including their mistakes. If an
@@ -367,29 +501,50 @@ substance. Run it after editing a card.
367
501
 
368
502
  | Value | Meaning | Use when |
369
503
  |---|---|---|
370
- | `style` | Voice only. The request and its constraints are untouched. | Default. Almost always this. |
371
- | `strategy` | The rewrite may **add behavioural constraints** that fit the character. | Deliberate cases — e.g. the bundled *肌肉集团* card, which adds "don't over-plan, don't search, don't ship beginner-tier solutions". |
372
-
373
- `strategy` genuinely changes your request, not just its wording. Keep review
374
- mode on for those cards.
375
-
376
- **Fidelity is a property of the card. It can be overridden per session, but only
377
- ever narrowed, never widened:**
378
-
379
- - **Narrowing (allowed)** — a `strategy` card can be asked to behave as
380
- voice-only for one message. That only ever **takes away** its power to add
381
- constraints, so it is strictly safer than the card's own default.
382
- - **Widening (refused)** — a `style` card cannot be told to become `strategy`.
383
- Widening lets the rewrite add constraints you never typed, which is the card
384
- author's decision, not something to grant with a click on the way to Send. To
385
- widen, edit the card.
386
-
387
- The control appears under *More settings*, and **only when the card is
388
- `strategy`**. On a `style` card it is not rendered at all: both options would
389
- resolve identically there, and showing a control that cannot do anything — plus a
390
- paragraph explaining why the other one is refused — is worse than showing
391
- nothing. The server enforces the same rule, so the UI never offers a control it
392
- would ignore.
504
+ | `style` | Voice only. | Default. Almost always this. |
505
+ | `strategy` | The rewrite may **add behavioural framing**. | Deliberate cases — e.g. the bundled *肌肉集团* card, which adds "don't over-plan, don't search, don't ship beginner-tier solutions". |
506
+
507
+ **"Constraint" means two different things in this document — do not conflate
508
+ them:**
509
+
510
+ | | What it is | May it change? |
511
+ |---|---|---|
512
+ | **Technical content** | What you are asking for, which file, which number, which limit | **Never, under any setting** |
513
+ | **Behavioural framing** | How the assistant should approach the work — how direct, whether to plan first, what counts as good enough | `strategy` may add it |
514
+
515
+ What `strategy` adds is the **second** kind, and it **genuinely changes the
516
+ request you send** — it puts something in your message that you did not type. So
517
+ keep **Review first** on for those cards.
518
+
519
+ > The prompt used to conflate the two: the hard rules said "never add, remove,
520
+ > widen or reinterpret a requirement" while declaring themselves an override of
521
+ > everything *including the character description*, and the `strategy` permission
522
+ > was written before them. The model obeyed the absolute prohibition, so
523
+ > `strategy` was a **no-op** — flipping "Constraints too" in the UI changed
524
+ > nothing about the output. The hard rules now cover technical content only, and
525
+ > say explicitly that they do not forbid behavioural framing.
526
+
527
+ **Fidelity is a property of the card, but you can override it per character, in
528
+ both directions:**
529
+
530
+ | Option | Meaning |
531
+ |---|---|
532
+ | **Card default** | Use whatever the card declares on the settings page |
533
+ | **Voice only** | Wording changes; your request does not |
534
+ | **Constraints too** | The rewrite may also add the character's working style (e.g. "no long planning") |
535
+
536
+ **"Constraints too" genuinely changes your request**, rather than only rewording
537
+ it — it adds things you never typed. So pair it with **Review first**.
538
+
539
+ > This rule used to be one-way: a `strategy` card could be narrowed to `style`,
540
+ > but a `style` card could never be widened. The reasoning was that a click on
541
+ > the way to Send should not grant the power to add constraints to your request.
542
+ >
543
+ > That reasoning does not hold up: **the card is your own file**, and the
544
+ > settings page has always let you set `fidelity:` directly. Refusing in the menu
545
+ > did not remove the capability — it only made the choice **disappear from the
546
+ > place where you actually work**. All three options are now available on every
547
+ > card.
393
548
 
394
549
  ### `mode: template`
395
550
 
@@ -409,16 +564,12 @@ Set in the plugin's loader row config:
409
564
  name: dsh-persona-forge
410
565
  config:
411
566
  sendMode: review # review | direct — the initial composer default
412
- factCheck: false # default for the MODEL check (off by default; see
413
- # "The preservation check"). The local token check does
414
- # not depend on this: it always runs and is free.
415
567
  watchCards: true # reload the catalog when the card directory changes
416
568
  ```
417
569
 
418
- `factCheck` controls only the default for **layer 2**; the menu switch overrides
419
- it per session. Setting it to `true` adds **one extra model call to every
420
- rewrite** (doubled latency and tokens) and is only worth it when drafts are
421
- technical enough.
570
+ The technical-token check depends on no configuration: it always runs and is
571
+ free. The retired `factCheck` key is ignored, so a profile still carrying it
572
+ changes nothing.
422
573
 
423
574
  ---
424
575
 
@@ -430,8 +581,8 @@ refused). Mounted under `/persona-forge`.
430
581
 
431
582
  | Route | Body | Returns |
432
583
  |---|---|---|
433
- | `/persona-forge/cards` | `{}` | `{ cards, diagnostics, directory, sendMode, factCheck, draftModel }` |
434
- | `/persona-forge/rewrite` | `{ cardId, text, sessionId?, intensity?, mode?, fidelity?, modelCheck? }` | `{ text, provider, model, mode, cardId, fidelity, intensity, elapsedMs, facts, factCheck }` |
584
+ | `/persona-forge/cards` | `{}` | `{ cards, diagnostics, directory, sendMode, draftModel }` |
585
+ | `/persona-forge/rewrite` | `{ cardId, text, sessionId?, intensity?, mode?, fidelity? }` | `{ text, provider, model, mode, cardId, fidelity, intensity, elapsedMs, facts }` |
435
586
  | `/persona-forge/cards/save` | `{ card }` | `{ id, path }` |
436
587
  | `/persona-forge/cards/delete` | `{ id }` | `{ removed }` |
437
588
  | `/persona-forge/cards/reset` | `{ id }` | `{ id }` — replaces the user card with the bundled one |
@@ -475,7 +626,8 @@ npm run check # both
475
626
  `check.mjs` needs no harness and no model. It covers the built-in YAML reader
476
627
  (including its refusals and dump/parse round trips), card normalization, the
477
628
  bundled cards' fact preservation, prompt construction, output normalization,
478
- the fact-check parser, and client-bundle syntax.
629
+ the local fact comparison, route resolution order, the send-gesture decision,
630
+ and client-bundle syntax.
479
631
 
480
632
  `host-check.mjs` mounts the real plugin entry on a stub Cordis context and
481
633
  drives the real HTTP routes over a real socket, with a stubbed `llm` service.
@@ -506,7 +658,7 @@ naming the line.
506
658
  | `lib/index.js` | Host entry: card store, directory watcher, route registration |
507
659
  | `lib/store.js` | Card loading, normalization, save/delete |
508
660
  | `lib/yaml.js` | The built-in YAML subset reader and writer |
509
- | `lib/prompts.js` | Rewrite system prompt, draft framing, fact-check prompt |
661
+ | `lib/prompts.js` | Rewrite system prompt, draft framing, AI-draft prompt |
510
662
  | `lib/rewrite.js` | The model calls and output normalization |
511
663
  | `lib/routes.js` | HTTP routes and session-model resolution |
512
664
  | `lib/loopback.js` | The loopback trust fence |