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/CHANGELOG.md CHANGED
@@ -6,6 +6,654 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.8.3] - 2026-10-11
10
+
11
+ ### Fixed
12
+
13
+ - **The review panel is no longer cut off at the top of a conversation.** It
14
+ renders in the composer stack, whose seat is `flex: none` — so a tall panel does
15
+ not compress the composer, it pushes the whole stack **up**. With no transcript
16
+ above to scroll, the overflow simply left the viewport: starting a session and
17
+ rewriting immediately showed only the panel's title bar, and during 改写中 that
18
+ was all you got.
19
+
20
+ The panel is now bounded and scrolls **inside** itself, with the header pinned
21
+ so 关闭 stays reachable. It can be any height without ever pushing itself
22
+ off-screen.
23
+
24
+ ### Changed
25
+
26
+ - **The composer menu names what the card actually declares.** "跟随卡片" was a
27
+ black box: the rewrite-mode and fidelity rows showed the same words whether the
28
+ card declared `llm` or `template`, `style` or `strategy`, so what would actually
29
+ run was invisible. They now read **跟随卡片(模型改写)** and
30
+ **跟随卡片(可加约束)**, and the folded summary line lists fidelity too — it is
31
+ the setting with real consequences.
32
+
33
+ - **The generated example set no longer repeats the generic rule.** The card
34
+ generator had been told, literally, to write `方块系统、合成表、生存模式` for
35
+ "写一个我的世界" — which is the *same* example the rewrite rule already carries,
36
+ so the set it produced was guaranteed to be redundant. The prompt now separates
37
+ the two explicitly: generic detail is already covered (do not write it),
38
+ character-specific demands are what this field is for. The bundled cards were
39
+ rewritten the same way — 欧姆弥赛亚 asks for a record left for those who come
40
+ after and refuses shortcuts; 硬邦邦 refuses a proof-of-concept handed over as a
41
+ deliverable.
42
+
43
+ ## [0.8.2] - 2026-10-10
44
+
45
+ ### Fixed
46
+
47
+ - **The card form now shows the second example set and the template.** 0.8.0
48
+ added both to the card format but left the settings form on the old shape, and
49
+ `save` writes the payload as the **whole card** rather than merging — so the
50
+ moment you edited a card's name, its implementation-detail example set was
51
+ deleted. The form now renders a textarea per intensity level for both sets, and
52
+ a template field for every mode.
53
+
54
+ - **A template is no longer lost when saving an llm card.** It was sent only for
55
+ a template-mode card, so an llm card that had one lost it on the next save —
56
+ and template mode is offered on every card, which is how that switch ended up
57
+ silently falling back to a model call.
58
+
59
+ - **The form's copy matches what the fields now mean:** the second set is labelled
60
+ "may add implementation detail", and the fidelity hint says the same instead of
61
+ the retired "behavioural constraints" wording.
62
+
63
+ ### Added
64
+
65
+ - **A check against this whole class of bug.** For every card field added since,
66
+ the form must both read it and write it back into the payload — checking only
67
+ that the name appears somewhere would pass on the read alone, which is exactly
68
+ how the write-back went missing. Confirmed red when the write-back is removed.
69
+
70
+ ## [0.8.1] - 2026-10-10
71
+
72
+ ### Changed
73
+
74
+ - **"Constraints" now means what it should: concrete IMPLEMENTATION requirements.**
75
+ The strategy fidelity was read as permission to add behavioural framing ("do
76
+ not over-plan"), which is not what a constraint is. It now permits adding the
77
+ concrete features and limits a competent take on the request implies but the
78
+ user did not spell out — for "写一个我的世界": 方块系统、合成表、生存模式、存档.
79
+
80
+ The hard rules said the opposite: "never add, remove, widen or reinterpret a
81
+ requirement", while declaring themselves an override of everything. That
82
+ cancelled the permission and made the setting a no-op. They are now about
83
+ PROTECTING what the draft states — nothing stated may be removed, weakened or
84
+ contradicted — and explicitly leave ADDING to the fidelity block.
85
+
86
+ Both bundled cards' strategy sets now demonstrate real additions (边界用例、
87
+ 三数取中、复现之证、入参校验) rather than tone advice.
88
+
89
+ ### Known limitation
90
+
91
+ - **The local fact check cannot see an added requirement written in Chinese.**
92
+ compareFacts reports an addition only for a token it can prove is structural
93
+ — a path, number, flag or code span. 方块系统 and 生存模式 are bare words, so they
94
+ pass unreported, and the panel's "新增" line stays empty even when the rewrite
95
+ added them. The added detail is still visible in the editable result column,
96
+ which is where a reviewer would catch it.
97
+
98
+
99
+ ## [0.8.0] - 2026-10-10
100
+
101
+ ### Added
102
+
103
+ - **Every card now carries TWO example sets**, and which one the model is shown
104
+ depends on the fidelity you chose. `examplesByIntensity` demonstrates the voice
105
+ alone; `examplesByIntensityStrategy` demonstrates the voice **plus the
106
+ behavioural framing** the rewrite may add ("do not over-plan", "do not stop at
107
+ the first plausible cause").
108
+
109
+ This fixes a real failure. Raising fidelity used to show the model the
110
+ *voice-only* examples — every one of which merely added a greeting — so it had
111
+ nothing to imitate and invented padding: "写一个我的世界" came back as
112
+ "就想要一个我的世界", a restatement rather than a framing.
113
+
114
+ - **Every card now carries a `template`.** Template mode fell back to a model call
115
+ on every card, because none of them had one: the switch appeared on and the
116
+ model ran anyway. The template substitutes `{{input}}` verbatim, so the wrapper
117
+ supplies the mouth and the user supplies the request.
118
+
119
+ - **The card generator produces both sets and a template**, and refuses a card
120
+ missing any of them, naming which. `DRAFT_MAX_TOKENS` goes to 8192, because a
121
+ complete card is now roughly twice the size of one example set.
122
+
123
+ ### Changed
124
+
125
+ - **`fidelity` is no longer a coin flip on a card with nothing to add.** The
126
+ bundled cards' strategy sets demonstrate concrete framing, so the setting
127
+ produces visibly different output instead of a louder restatement.
128
+
129
+ A card without `examplesByIntensityStrategy` still falls back to the voice-only
130
+ set, so cards written before this release keep working; only the generator
131
+ requires the new fields.
132
+
133
+ ## [0.7.3] - 2026-10-10
134
+
135
+ ### Fixed
136
+
137
+ - **"Constraints too" (fidelity: strategy) actually does something now.** The
138
+ hard rules said *"never add, remove, widen or reinterpret a requirement"* while
139
+ declaring themselves an override of **everything, including the character
140
+ description** — and the `strategy` permission to add behavioural framing was
141
+ written *before* them. The model obeyed the absolute prohibition, so flipping
142
+ the control changed nothing in the output: a setting that appeared to work and
143
+ did nothing.
144
+
145
+ The root cause is that **"constraint" was doing two jobs**. A message carries
146
+ (a) technical content — what is asked, under which files, numbers and limits —
147
+ and (b) behavioural framing — how the assistant should approach the work. The
148
+ hard rules now lock (a) absolutely and say explicitly that they do **not**
149
+ forbid (b); the fidelity block owns (b) and names what it may add.
150
+
151
+ - **Direct send closes its panel.** The message was sent correctly, but the panel
152
+ stayed up — and a settled panel sets `panelOpen`, which **swallows the
153
+ composer's send arrow**. The next thing you typed could not be sent until the
154
+ panel was dismissed by hand. Reported as "direct send works, but the review
155
+ card still shows up". Review mode still keeps its panel: that one *is* the
156
+ decision point.
157
+
158
+ A refused submit leaves the panel up, so the text is still reachable by hand.
159
+
160
+ ## [0.7.2] - 2026-10-10
161
+
162
+ ### Fixed
163
+
164
+ - **AI card drafting no longer writes the character's own line as the rewrite.**
165
+ Given "the AI is my mother, I am her child", the generator produced examples
166
+ where the **mother** spoke:
167
+
168
+ > `from`: 帮我把标题改成首页
169
+ > `to`: 乖,妈帮你把标题改成首页。 ← the MOTHER talking
170
+
171
+ That is the character answering, not the user asking — the rewrite is always
172
+ the **user's** message, so the card was unusable and the role was inverted.
173
+
174
+ The prompt was the cause: it said the later step rewrites the user's message
175
+ "into that voice", and that `to` is the request "rewritten in the voice". Both
176
+ leave *whose* voice open, and the generator resolved it to the character's.
177
+
178
+ The generator is now told plainly that **the output is always the user's
179
+ message and the character's own speech never appears**, with the direction
180
+ restated for both sides and a **worked wrong/right pair** using exactly this
181
+ case. The bundled cards already obeyed the rule — in both, `to` is the user
182
+ speaking (the client, the believer), never the character — so they are cited as
183
+ the standard.
184
+
185
+ ## [0.7.1] - 2026-10-10
186
+
187
+ ### Fixed
188
+
189
+ - **AI card drafting no longer runs out of output budget part-way through the
190
+ examples.** The draft call asked for **2048** output tokens, but a complete card
191
+ is bigger than that: each bundled card is ~1700 characters of CJK body, which
192
+ is well over 2000 tokens once the JSON wrapper and four intensity sets are
193
+ counted. `examplesByIntensity` is the longest field and the one written last, so
194
+ it was exactly what got cut. The budget is now 4096, matching the rewrite call.
195
+
196
+ - **A generated card must carry all four intensity sets, or the generation is
197
+ refused.** The parser accepted whatever rungs arrived, so a truncated answer
198
+ produced a card that looked complete in the form while two of its four settings
199
+ had **nothing to demonstrate** — the voice quietly vanished at those levels.
200
+ It now reports which levels are missing and asks the user to retry.
201
+
202
+ This was the second half of the same bug: raising the budget makes truncation
203
+ rare, and refusing the partial card means it can no longer pass unnoticed when
204
+ it does happen.
205
+
206
+ ## [0.7.0] - 2026-10-10
207
+
208
+ ### Fixed
209
+
210
+ - **AI card drafting no longer fails when the model appends a sentence.** The
211
+ recovery step that slices the outermost `{…}` out of the answer ran **only when
212
+ the text did not start with `{`**. A model that emitted clean JSON and then one
213
+ line of commentary — a very common habit — therefore skipped recovery and was
214
+ parsed whole, failing with:
215
+
216
+ > the model returned invalid JSON: Unexpected non-whitespace character after
217
+ > JSON at position 1051
218
+
219
+ Recovery now runs whenever the raw text does not parse on its own, so a leading
220
+ sentence, a trailing sentence, a code fence, or any combination of them all
221
+ survive. Text with no object in it is still rejected, and a legitimate object is
222
+ never rewritten on the way in.
223
+
224
+ ### Removed
225
+
226
+ - **The flat `examples:` list, from the form and from the bundled cards.** It was
227
+ the fallback for a card whose per-intensity sets were missing, but
228
+ `examplesFor` prefers `examplesByIntensity` — so on the bundled cards, which
229
+ populate all four rungs, the flat list of four was **dead data** that still had
230
+ to be read and kept in sync. The two bundled cards carried 4 flat examples plus
231
+ 8 per-intensity ones, and only the 8 ever ran.
232
+
233
+ A card file that still carries `examples:` is **still read** and still falls
234
+ back to it; the form simply no longer offers a second place to write the same
235
+ thing. Saving a card preserves any flat list it already had, because `save`
236
+ writes the payload as the whole card rather than merging — without that, editing
237
+ a legacy card's name would silently delete its examples.
238
+
239
+ ## [0.6.4] - 2026-10-10
240
+
241
+ ### Changed
242
+
243
+ - **The AI-draft box stopped saying the same thing three times.** The direction
244
+ rule was carried by the placeholder, restated in the description ("who the AI
245
+ plays, who you are, how they talk"), and then explained again in a paragraph
246
+ above the button. Three tellings of one instruction is not emphasis, it is
247
+ noise.
248
+
249
+ The paragraph is gone. The **placeholder** keeps the rule — it is the strongest
250
+ teaching signal in the box, and it demonstrates the direction in the shape of a
251
+ real example rather than describing it. The description is back to what the
252
+ form does: which card fields the AI fills.
253
+
254
+ The prompt layer still enforces the direction, and the card-authoring section
255
+ of the README still explains both sides in full; that is where an author
256
+ writing a card by hand needs it.
257
+
258
+ ### Added
259
+
260
+ - **A check against restating the rule.** The direction must not reappear as a
261
+ separate paragraph, and the description must not repeat the placeholder. The
262
+ regression was confirmed to fail the suite.
263
+
264
+ ## [0.6.3] - 2026-10-10
265
+
266
+ ### Fixed
267
+
268
+ - **The character may be on EITHER side of the conversation, and the prompts no
269
+ longer pretend otherwise.** Both the rewrite prompt and the card generator
270
+ asserted flatly that *the character is the ASSISTANT's identity*. That is false
271
+ for half the cards, and the bundled set proves it:
272
+
273
+ | Card | Who is the character |
274
+ |---|---|
275
+ | 万机之神 · 欧姆弥赛亚 | the **AI** — the user addresses it as a petitioner |
276
+ | 肌肉集团 · 硬邦邦 | the **user** — "**the user is** the client", addressing their own team |
277
+
278
+ The rewrite prompt now states both cases, tells the model to take the direction
279
+ from the card's style text, and defines the assistant-side default for a card
280
+ that is genuinely silent. The generator is told the same, and takes the
281
+ direction from the idea it is handed.
282
+
283
+ - **The AI-draft direction note no longer states the wrong rule.** It said "你是
284
+ 对角色说话的那个人,**不是角色本身**" — an absolute that contradicts 硬邦邦,
285
+ where you *are* the character. It now says the character can be on either side
286
+ and that picking one and saying so is all that is required.
287
+
288
+ The brief example and description keep asking for the same three things (who
289
+ the AI plays, who you are, how they talk) — the ambiguity was the bug, not the
290
+ two-sidedness.
291
+
292
+ ## [0.6.2] - 2026-10-10
293
+
294
+ ### Fixed
295
+
296
+ - **The AI-draft example now states the speaker direction.** The placeholder was
297
+ "一个脾气暴躁但极其可靠的老灯塔守夜人,说话简短、爱用航海比喻" — a character
298
+ and nothing else. It never said whether **you** play that character or the
299
+ **AI** does, which is precisely the ambiguity this plugin exists to avoid: the
300
+ character is the *assistant's* identity, and the message being rewritten is
301
+ always the *user's*.
302
+
303
+ The mismatch was sharpest because the generator's own system prompt **requires**
304
+ the style field to state the direction explicitly, while the example it was
305
+ handed omitted it. The example is the strongest teaching signal in that box, so
306
+ it now demonstrates what it demands:
307
+
308
+ > 例如:AI 扮演一位脾气暴躁但极其可靠的老灯塔守夜人,我是他的学徒;他说话简短、爱用航海比喻
309
+
310
+ The description asks for the same three things (who the AI plays, who you are,
311
+ how they talk), and the box carries a standing line stating the rule. Both
312
+ bundled cards already named it — 欧姆弥赛亚 is "a supplicant's prayer to the
313
+ Machine God", 硬邦邦 is "**the user** is the client".
314
+
315
+ - **The AI-draft copy said who does what, and what "the spec" actually is.**
316
+ It read "用一句话描述角色的思路,AI 会按同一套规范把表单填好" — with no
317
+ subject, so it was unclear whether *you* describe the character or the AI
318
+ does, and "同一套规范" pointed at an internal quality standard the user cannot
319
+ see or act on.
320
+
321
+ Both languages now name the actor and the fields: **you** write one sentence
322
+ about the character, and the AI fills the form by the character-card fields —
323
+ name, icon, description, style text, and a conversion example set per
324
+ intensity level. The hint also states the two things a generated card
325
+ deliberately pins: **fidelity is always voice-only** and **rewrite mode is
326
+ always model rewrite**, so a generated card never quietly acquires the power
327
+ to add constraints to your request.
328
+
329
+ ### Added
330
+
331
+ - **Checks pin the direction into the brief.** The placeholder must demonstrate
332
+ who plays whom and must name the user's own role; the description must ask for
333
+ it; the rule must be stated in the box; and the copy must carry no markdown,
334
+ since the shell renders it as plain text. Confirmed red against the old
335
+ placeholder.
336
+
337
+ - **Checks pin that copy to the behaviour.** The description must name the four
338
+ fields the generator fills and must not fall back to "规范", and the generated
339
+ card must stay pinned to `fidelity: 'style'` / `mode: 'llm'` with the copy
340
+ saying so. Confirmed red against the old wording.
341
+
342
+ ## [0.6.0] - 2026-10-10
343
+
344
+ ### Changed
345
+
346
+ - **Fidelity is now free in both directions, on every card.** It used to be
347
+ one-way — a `strategy` card could be narrowed to `style`, but a `style` card
348
+ could never be widened — and the control was rendered only on `strategy` cards.
349
+ The reasoning was that a click on the way to Send should not grant the power to
350
+ add constraints to your request.
351
+
352
+ That reasoning does not hold up: **the card is the user's own file**, and the
353
+ settings page has always let them set `fidelity:` directly. Refusing in the
354
+ menu did not remove the capability, it only made the choice disappear from the
355
+ place where the work happens. The server rule is now symmetric, and all three
356
+ options — card default, voice only, constraints too — appear on every card.
357
+
358
+ The honest framing is kept: "constraints too" genuinely changes the request, so
359
+ the tooltip says so and points at review mode.
360
+
361
+ - **The settings fold no longer explains itself in permanent prose.** Every row
362
+ and every option now carries its explanation as a **tooltip** on the control.
363
+ The fold had grown to four rows plus six lines of standing explanation, which
364
+ is a wall of text for a menu whose job is picking a character.
365
+
366
+ - **Rewrite mode and fidelity now follow the character too**, not the session.
367
+ They were already displayed under the character while being stored per session,
368
+ which meant the setting you saw and the setting you got could disagree after
369
+ switching character. All four settings are now read from, and written to, the
370
+ card's own keys.
371
+
372
+ ### Added
373
+
374
+ - **The check suite guards the READMEs.** The status line and the inspiration
375
+ link had both already rotted unnoticed, so three checks per README now pin the
376
+ current minor version, the video link and its author, and the named target
377
+ model.
378
+
379
+ ## [0.5.2] - 2026-10-10
380
+
381
+ ### Changed
382
+
383
+ - **The README's "what a persona changes" section now says where this came
384
+ from.** The old text was a flat disclaimer — personas do not raise the
385
+ capability ceiling, do not expect a performance switch. That is still true and
386
+ still stated, but it read like a warning label on a project whose whole point
387
+ is delight.
388
+
389
+ It now credits the source: [**“ds:好的,我现在是万机之神了(”**](https://www.bilibili.com/video/BV1Ata26pE92)
390
+ by 身居幻想乡の长乐安生 — a 30-second video whose comment section describes the
391
+ effect better than any spec sheet ("another AI is you pretending to be Guan
392
+ Yu; when Big Fat Fish pretends, she really rides out and cuts down Hua
393
+ Xiong"). It also names **DeepSeek Flash** as the model this is tuned for,
394
+ which was never said anywhere.
395
+
396
+ Both READMEs also carried a stale `0.3.x` status line; they now state `0.5.x`.
397
+
398
+ ### Added
399
+
400
+ - **The check suite now guards the READMEs.** The status line and the
401
+ inspiration link had both already rotted unnoticed, so three checks per README
402
+ now pin the current minor version, the video link and its author, and the
403
+ named target model.
404
+
405
+ ## [0.5.1] - 2026-10-10
406
+
407
+ ### Changed
408
+
409
+ - **"More settings" is gone; everything it held moved into the character's own
410
+ fold.** Having two folds split one idea across two places — intensity and send
411
+ mode under the character, rewrite mode and fidelity somewhere else — so
412
+ **rewrite mode** and **fidelity** now sit in the same **"Settings for
413
+ <name>"** fold as the rest. The folded row's summary names the intensity, the
414
+ send mode and the rewrite mode, so the whole configuration is legible without
415
+ opening it.
416
+
417
+ - **Every rewrite mode now says what it does.** The segmented control named
418
+ three options and explained none of them, so the difference between a model
419
+ rewrite and a template was something you had to guess:
420
+
421
+ - *Card default* — use whatever this card declares on the settings page.
422
+ - *Model rewrite* — calls the model to rewrite your draft in the character's
423
+ voice. Most flexible; costs one model call.
424
+ - *Template* — fills your draft into the card's preset template with no model
425
+ call. No latency, no tokens, but the voice is fixed.
426
+
427
+ ### Removed
428
+
429
+ - **"Open card directory" left the rewrite menu.** The settings page already has
430
+ an *Open directory* button, so the menu row was a second door to the same room
431
+ — and it put occasional housekeeping in the path of choosing a character.
432
+
433
+ ## [0.5.0] - 2026-10-10
434
+
435
+ ### Changed
436
+
437
+ - **Intensity and send mode now belong to the CHARACTER, not to the session.**
438
+ "硬邦邦 always goes out Zealot, un-reviewed" is a property of that character
439
+ rather than a choice about one conversation, so both are stored per card:
440
+ configure it once and picking that card in another session gives you the same
441
+ settings.
442
+
443
+ They also moved out of the shared block above the list and under the card they
444
+ apply to, as a folded **"Settings for <name>"** row. Sitting above every card,
445
+ they read as global switches for the whole menu — and switching character
446
+ silently carried the previous card's intensity over. The fold's summary line
447
+ shows the current rung and send mode, so the state is visible without opening
448
+ it.
449
+
450
+ Switching character now **re-reads that card's settings**; without that the
451
+ previous card's values stayed on screen and were then written to the new
452
+ card's key.
453
+
454
+ - **Picking a character no longer closes the menu.** Choosing a character and
455
+ configuring it are one act, and closing on pick forced a reopen to touch
456
+ intensity at all.
457
+
458
+ - **The card directory moved into "More settings".** It is housekeeping you do
459
+ occasionally, not something to scroll past on the way to choosing a character.
460
+
461
+ ## [0.4.1] - 2026-10-10
462
+
463
+ ### Removed
464
+
465
+ - **The model-based preservation check.** It made a second model call on every
466
+ rewrite to catch what the free token comparison cannot see — a requirement
467
+ reworded weaker, a constraint added in prose. It is gone, along with its
468
+ prompt, its verdict parser and every wire field (`factCheck`, `modelCheck`).
469
+
470
+ It doubled the latency and the token cost of every send to re-check what the
471
+ local comparison answers for nothing, and the settings page never offered a
472
+ working switch for it — a documented feature with no reachable control. For
473
+ the residue it was meant to cover, the rewrite column is now editable, which
474
+ is more direct than another round trip.
475
+
476
+ The retired `factCheck` config key is **ignored rather than rejected**, so a
477
+ profile still carrying it loads unchanged and simply makes no second call.
478
+
479
+ ### Fixed
480
+
481
+ - **Rewriting in one session no longer makes every other session look busy.**
482
+ The composer button read the panel's phase **globally**, so while any single
483
+ session was rewriting, every open composer rendered "改写中…". The panel is one
484
+ shared surface, so its phase is global — but what the button shows must be
485
+ scoped to its own session. It now reads the session-filtered panel, the same
486
+ value the send guards already used.
487
+
488
+ ## [0.4.0] - 2026-10-10
489
+
490
+ ### Added
491
+
492
+ - **The rewrite column is editable.** What the model wrote is a starting point,
493
+ not a verdict: fixing a dropped identifier or a tone that is slightly off by
494
+ hand is faster than another round trip. **Send** and **Fill** carry the
495
+ edited text, and the panel says when the text differs from what the model
496
+ produced, so nothing goes out silently different.
497
+
498
+ - **While the review panel is open, the composer's send arrow is inert.** The
499
+ panel is the decision point. Pressing send with it up either pushed the
500
+ un-reviewed draft out or started a **second** rewrite of the same draft —
501
+ the duplicate that appeared once the panel was on screen. Closing the panel,
502
+ or choosing an action in it, re-arms the composer. Direct-send mode is
503
+ unaffected: it submits programmatically rather than through a gesture.
504
+
505
+ ### Removed
506
+
507
+ - **The "Fill only" button, from the normal flow.** With an editable result,
508
+ writing the text into the composer without sending is the same act as
509
+ reviewing it, so the button was a second control for one idea. It survives
510
+ only for a shell that exposes no submit action, where it is the sole way to
511
+ get the text out.
512
+
513
+ ### Fixed
514
+
515
+ - **An edited result is no longer rewritten again.** `apply` recorded only the
516
+ untouched original as "already rewritten", so sending a hand-edited result
517
+ from the composer came back as a fresh rewrite of the user's own edit. Any
518
+ settled text is now recorded. *Restore original* still deletes the memory
519
+ outright, because that is a rewind rather than a settled result.
520
+
521
+ ## [0.3.5] - 2026-10-10
522
+
523
+ ### Fixed
524
+
525
+ - **The same sentence typed twice is rewritten both times.** The send guard
526
+ skipped a rewrite when the draft matched the last rewrite's result **or** its
527
+ original text. Restoring the original already clears that memory, so matching
528
+ `original` bought nothing and silently disabled the persona for any repeated
529
+ draft — type "讲个笑话", send it, type it again, and it went out
530
+ un-rewritten.
531
+
532
+ - **Interception can no longer disarm itself.** The send listeners were bound
533
+ to the composer card, found through the plugin's own button. A card that was
534
+ not found meant the whole feature silently did not arm — the plugin looked
535
+ dead, with no error anywhere. The listeners now sit on `window` and each
536
+ guard scopes its event to the composer card it belongs to, so a miss costs
537
+ one event rather than the feature.
538
+
539
+ ### Changed
540
+
541
+ - **The send-gesture decision is now a pure function** (`decideSend`) returning
542
+ `{ action, reason }`, and the client exposes `__internals` so the check suite
543
+ can boot the real bundle and drive it. This decision fails **silently** in
544
+ both directions — swallow a gesture that should have been sent and the
545
+ message vanishes; let one through that should have been rewritten and the
546
+ persona quietly does nothing — and neither throws, so the only symptom is "it
547
+ stopped working". Thirteen behavioural cases now cover it, replacing
548
+ source-shape assertions. The repeated-draft case was confirmed to fail
549
+ against the old predicate.
550
+
551
+ The pure-guard-plus-seam approach follows
552
+ [`dsh-prompt-hardener`](https://github.com/ffyfox/dsh-prompt-hardener), which
553
+ reached the same conclusion about this class of silent failure.
554
+
555
+ ## [0.3.4] - 2026-10-10
556
+
557
+ ### Fixed
558
+
559
+ - **One wedged rewrite no longer freezes the plugin for good.** The review
560
+ panel is a single shared surface, but the guards around it were global while
561
+ the send interceptor guarded per session. A single rewrite left stuck at
562
+ `loading` therefore poisoned everything after it: `start()` hit its global
563
+ `busy` guard and **silently did nothing** — no rewrite, no error — while in
564
+ another session the interceptor let the gesture through to the native send.
565
+ Reported as "after it hangs, it just sends my text and the plugin never
566
+ intercepts again".
567
+
568
+ - `runRewrite` now refuses only a concurrent run for the **same** session, so
569
+ one wedged session cannot block another.
570
+ - `start()` guards on the **per-session** busy, matching `runRewrite` and the
571
+ interceptor. A send gesture is now either rewritten or sent; it is never
572
+ swallowed into nothing.
573
+ - A client-side watchdog settles the panel after 135s even if the host never
574
+ answers. The host's own deadline is not enough: this half must stay usable
575
+ without depending on the other half being alive.
576
+
577
+ ## [0.3.3] - 2026-10-09
578
+
579
+ ### Fixed
580
+
581
+ - **A rewrite now runs on the model you picked, even before you send with
582
+ it.** Switching models in the composer and then rewriting used the
583
+ **previous** model: the route came only from the session's last request
584
+ header, which does not advance until a message is actually sent. Observed
585
+ live as the composer showing `hy4-preview` while the rewrite ran on a stale
586
+ `workbuddy-xdpool-global / deepseek-v4.1-flash` route.
587
+
588
+ The host's own precedence, in `api-session`'s `selectionFor`, is
589
+ **pending → request header → harness default**; the plugin implemented only
590
+ the last two. It now reads the `modelSelection` projection's `pending` first,
591
+ so a dropdown pick takes effect immediately. Every step is still optional and
592
+ falls through to the next, so a harness without that projection behaves
593
+ exactly as before.
594
+
595
+ - **A rewrite whose model never answers now fails instead of hanging
596
+ forever.** `for await (const chunk of llm.stream(...))` only exits when the
597
+ adapter stops yielding. A provider that accepts the request and then sends
598
+ nothing — a rate-limited model — yields no chunk, so the 120s deadline's
599
+ `abort()` was invisible to the loop and the call never returned: the panel
600
+ spun with no result, no error, and no timeout.
601
+
602
+ Each pull is now raced against the deadline, so the loop leaves on its own
603
+ and reports `TIMEOUT` instead of waiting on a stream that will never speak.
604
+ The stream is abandoned rather than drained, because draining a wedged
605
+ provider is the hang this guards against.
606
+
607
+ Two regression tests cover both, and both were confirmed to fail against the
608
+ old code: the route test goes red, and the deadline test hangs the suite
609
+ outright (`Detected unsettled top-level await`) — the reported symptom.
610
+
611
+ ## [0.3.2] - 2026-10-09
612
+
613
+ ### Changed
614
+
615
+ - **The rewrite stops naming a reasoning effort, so it works again on routes
616
+ that declare none.** An earlier attempt to speed slow rewrites up pinned
617
+ `reasoningEffort: 'low'`; on `workbuddy-xdpool` / `hy4-preview` every rewrite
618
+ then failed outright with
619
+ `does not support reasoning effort "low"`.
620
+
621
+ The harness validates the field before it reaches any provider: when a
622
+ route's model declares **no** reasoning metadata, **any present value**
623
+ throws — including `''`, which is the host's own "Provider default" sentinel
624
+ in the ACP session-config layer, and including `'off'`. Absence is therefore
625
+ the only portable way to ask for the route's default, and the call now leaves
626
+ the field out.
627
+
628
+ The latency this was meant to fix is **not** fixed by it. On a reasoning
629
+ model the rewrite may still spend thinking time before it emits anything; the
630
+ 120s deadline and the elapsed-seconds readout from 0.3.1 remain the honest
631
+ answer there.
632
+
633
+ ## [0.3.1] - 2026-10-09
634
+
635
+ ### Fixed
636
+
637
+ - **A rewrite waiting on a slow model no longer reads as frozen.** The panel
638
+ showed 改写中 with no sense of time, so a slow rewrite was indistinguishable
639
+ from a hang. The host has had a 120s deadline all along, so the panel was
640
+ never stuck forever — but two minutes of a silent spinner looks exactly like a
641
+ freeze. It now ticks elapsed seconds, and past 45s says the model may just be
642
+ slow and how to give up.
643
+
644
+ ### Removed
645
+
646
+ - **The settings page's model-check checkbox.** It duplicated the plugin row's
647
+ own `factCheck` config — a second setting for the same thing, sitting on a
648
+ page that is about cards. The host config is the single control and already
649
+ defaults to off. The checkbox, its dictionary keys and the client-side
650
+ preference behind it are gone.
651
+ - **The permanent drift-check instruction line.** The button's tooltip already
652
+ carried the same sentence. The page now renders only the check RESULT, which
653
+ is real feedback rather than the same copy twice.
654
+ - Three dictionary keys no route or component referenced:
655
+ `settings.ai.briefLabel`, `settings.changed`, `error.busy`.
656
+
9
657
  ## [0.3.0] - 2026-10-09
10
658
 
11
659
  ### Added
@@ -325,7 +973,29 @@ First public release.
325
973
  tools, and never enters the session log. What the session records is the text
326
974
  the user actually sent.
327
975
 
328
- [Unreleased]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.0...HEAD
976
+ [Unreleased]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.3...HEAD
977
+ [0.8.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.2...v0.8.3
978
+ [0.8.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.1...v0.8.2
979
+ [0.8.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.8.0...v0.8.1
980
+ [0.8.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.3...v0.8.0
981
+ [0.7.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.2...v0.7.3
982
+ [0.7.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.1...v0.7.2
983
+ [0.7.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.7.0...v0.7.1
984
+ [0.7.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.4...v0.7.0
985
+ [0.6.4]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.3...v0.6.4
986
+ [0.6.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.2...v0.6.3
987
+ [0.6.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.6.0...v0.6.2
988
+ [0.6.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.2...v0.6.0
989
+ [0.5.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.1...v0.5.2
990
+ [0.5.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.5.0...v0.5.1
991
+ [0.5.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.4.1...v0.5.0
992
+ [0.4.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.4.0...v0.4.1
993
+ [0.4.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.5...v0.4.0
994
+ [0.3.5]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.4...v0.3.5
995
+ [0.3.4]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.3...v0.3.4
996
+ [0.3.3]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.2...v0.3.3
997
+ [0.3.2]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.1...v0.3.2
998
+ [0.3.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.3.0...v0.3.1
329
999
  [0.3.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.2.1...v0.3.0
330
1000
  [0.2.1]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.2.0...v0.2.1
331
1001
  [0.2.0]: https://github.com/sugarmaster666/dsh-persona-forge/compare/v0.1.2...v0.2.0