wiki-viewer 2.16.0 → 2.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/build-manifest.json +3 -3
  3. package/.next/standalone/.next/prerender-manifest.json +3 -3
  4. package/.next/standalone/.next/required-server-files.json +4 -4
  5. package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  6. package/.next/standalone/.next/server/app/_global-error.html +1 -1
  7. package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
  8. package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/.next/standalone/.next/server/app/api/agent/activity/route.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/api/agent/events/[...path]/route.js.nft.json +1 -1
  16. package/.next/standalone/.next/server/app/api/agent/files/[...path]/route.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/api/agent/fs/file/[...path]/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/agent/fs/ls/[[...path]]/route.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/agent/fs/move/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/agent/fs/search/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/agent/settings/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/agent/sidecar/[...path]/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/app-proxy/[...path]/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/assets/[...path]/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/pdf/save/route.js.nft.json +1 -1
  26. package/.next/standalone/.next/server/app/api/share/[token]/asset/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/share/[token]/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/share/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/system/browse/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/api/system/reveal/route.js.nft.json +1 -1
  31. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/branch/route.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/open/route.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/refresh/route.js.nft.json +1 -1
  34. package/.next/standalone/.next/server/app/api/system/workspaces/[id]/route.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/api/system/workspaces/route.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/api/upload/[...path]/route.js.nft.json +1 -1
  37. package/.next/standalone/.next/server/app/api/wiki/app/route.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/api/wiki/backlinks/route.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/api/wiki/content/route.js.nft.json +1 -1
  40. package/.next/standalone/.next/server/app/api/wiki/download/route.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/api/wiki/folder/route.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/api/wiki/git-branches/route.js.nft.json +1 -1
  43. package/.next/standalone/.next/server/app/api/wiki/git-checkout/route.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/api/wiki/git-diff/route.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/api/wiki/git-file-info/route.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/api/wiki/git-history/route.js.nft.json +1 -1
  47. package/.next/standalone/.next/server/app/api/wiki/git-pull/route.js.nft.json +1 -1
  48. package/.next/standalone/.next/server/app/api/wiki/move/route.js.nft.json +1 -1
  49. package/.next/standalone/.next/server/app/api/wiki/new-file/route.js.nft.json +1 -1
  50. package/.next/standalone/.next/server/app/api/wiki/outlinks/route.js.nft.json +1 -1
  51. package/.next/standalone/.next/server/app/api/wiki/page/route.js.nft.json +1 -1
  52. package/.next/standalone/.next/server/app/api/wiki/presence/route.js.nft.json +1 -1
  53. package/.next/standalone/.next/server/app/api/wiki/route.js.nft.json +1 -1
  54. package/.next/standalone/.next/server/app/api/wiki/scratch/route.js.nft.json +1 -1
  55. package/.next/standalone/.next/server/app/api/wiki/search/route.js.nft.json +1 -1
  56. package/.next/standalone/.next/server/app/api/wiki/slugs/route.js.nft.json +1 -1
  57. package/.next/standalone/.next/server/app/api/wiki/upload/route.js.nft.json +1 -1
  58. package/.next/standalone/.next/server/app/api/wiki/watch/route.js.nft.json +1 -1
  59. package/.next/standalone/.next/server/app/page/react-loadable-manifest.json +4 -5
  60. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/s/[token]/page_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/signin/page_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/chunks/[root-of-the-server]__0ltsov-._.js +1 -1
  64. package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_system_workspaces_[id]_open_route_actions_1087xu7.js +1 -1
  65. package/.next/standalone/.next/server/chunks/ssr/0.u4_next_0kf.7hj._.js +1 -1
  66. package/.next/standalone/.next/server/chunks/ssr/0.u4_next_dist_0xugq-k._.js +1 -1
  67. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_chunk-5ZQYHXKU_mjs_0z1.978._.js +1 -1
  68. package/.next/standalone/.next/server/chunks/ssr/12y~_mermaid_dist_chunks_mermaid_core_chunk-KSCS5N6A_mjs_0v4oeem._.js +1 -1
  69. package/.next/standalone/.next/server/chunks/ssr/_01eqklo._.js +1 -1
  70. package/.next/standalone/.next/server/chunks/ssr/_0858xdh._.js +1 -1
  71. package/.next/standalone/.next/server/chunks/ssr/_0k6g8yo._.js +3 -3
  72. package/.next/standalone/.next/server/chunks/ssr/_0sr4wj.._.js +1 -1
  73. package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_06613~i._.js +1 -1
  74. package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0o~d81.._.js +1 -1
  75. package/.next/standalone/.next/server/chunks/ssr/node_modules__pnpm_0szp2v0._.js +1 -1
  76. package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
  77. package/.next/standalone/.next/server/pages/500.html +1 -1
  78. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  80. package/.next/standalone/.next/static/chunks/0.5ywu7_j899v.js +12 -0
  81. package/.next/standalone/.next/static/chunks/{0tjcc1wg-57l1.js → 065s1_n.1k11t.js} +1 -1
  82. package/.next/standalone/.next/static/chunks/{0y~5s_skz1l9a.js → 0ccw-uc_3ao.w.js} +1 -1
  83. package/.next/standalone/.next/static/chunks/{0ii.akeq7k4pf.js → 0f8kfd9d6kq0b.js} +1 -1
  84. package/.next/standalone/.next/static/chunks/0lsw6lsaj7lby.js +1 -0
  85. package/.next/standalone/.next/static/chunks/0rpxacw15~o7k.js +12 -0
  86. package/.next/standalone/.next/static/chunks/{0r7d-1tq61fa1.js → 0s2h7kvkrtq~p.js} +1 -1
  87. package/.next/standalone/.next/static/chunks/{0enm-3xy21jcx.js → 0tzi1~wpigp2y.js} +2 -2
  88. package/.next/standalone/.next/static/chunks/117dkzyg4c~us.js +1 -0
  89. package/.next/standalone/.next/static/chunks/{0j7kp7w2oum68.js → 11pa_9ucyrzh9.js} +1 -1
  90. package/.next/standalone/.scratch/tweak-running-app/craft-checklist.md +52 -0
  91. package/.next/standalone/.scratch/tweak-running-app/issues/01-isolated-origin.md +108 -0
  92. package/.next/standalone/.scratch/tweak-running-app/issues/01-runtime-containment.md +18 -0
  93. package/.next/standalone/.scratch/tweak-running-app/issues/02-injection-transform.md +18 -0
  94. package/.next/standalone/.scratch/tweak-running-app/issues/02-tracer-bullet.md +48 -0
  95. package/.next/standalone/.scratch/tweak-running-app/issues/03-accept-carbonize-mutator.md +19 -0
  96. package/.next/standalone/.scratch/tweak-running-app/issues/03-run-robustness.md +37 -0
  97. package/.next/standalone/.scratch/tweak-running-app/issues/04-multifile-safety.md +37 -0
  98. package/.next/standalone/.scratch/tweak-running-app/issues/04-static-tracer-bullet.md +27 -0
  99. package/.next/standalone/.scratch/tweak-running-app/issues/05-batch-parity.md +43 -0
  100. package/.next/standalone/.scratch/tweak-running-app/issues/05-dynamic-node-app.md +22 -0
  101. package/.next/standalone/.scratch/tweak-running-app/issues/06-remote-transport-bridge.md +20 -0
  102. package/.next/standalone/.scratch/tweak-running-app/issues/07-retire-bespoke-web-tweak.md +17 -0
  103. package/.next/standalone/.scratch/tweak-running-app/keep-ticket.md +74 -0
  104. package/.next/standalone/.scratch/tweak-running-app/mockups.html +305 -0
  105. package/.next/standalone/.scratch/tweak-running-app/ops1-updated.md +215 -0
  106. package/.next/standalone/.scratch/tweak-running-app/ops12-continuation.md +44 -0
  107. package/.next/standalone/.scratch/tweak-running-app/ops22-context.md +84 -0
  108. package/.next/standalone/.scratch/tweak-running-app/pivot-note.md +25 -0
  109. package/.next/standalone/.scratch/tweak-running-app/qa-live-engine.ts +104 -0
  110. package/.next/standalone/.scratch/tweak-running-app/spike.md +256 -0
  111. package/.next/standalone/.scratch/tweak-running-app/suggest-current-assessment.md +39 -0
  112. package/.next/standalone/.scratch/tweak-running-app/suggest-inline-interaction-spec.md +320 -0
  113. package/.next/standalone/.scratch/tweak-running-app/tweak-interaction-spec.md +161 -0
  114. package/.next/standalone/.scratch/tweak-running-app/two-actions-interaction-spec.md +234 -0
  115. package/.next/standalone/.scratch/tweak-running-app/unified-live-spec.md +261 -0
  116. package/.next/standalone/package.json +1 -1
  117. package/.next/standalone/server.js +1 -1
  118. package/package.json +1 -1
  119. package/.next/standalone/.next/static/chunks/04jvwlttwa_sw.js +0 -12
  120. package/.next/standalone/.next/static/chunks/161kgz.8h.3iq.js +0 -1
  121. package/.next/standalone/.next/static/chunks/16kzqur8~yg6b.js +0 -12
  122. /package/.next/standalone/.next/static/{7EbGXTo8ObPhr7UobKARQ → zwUX5EOT_44n1RoB_W9Bo}/_buildManifest.js +0 -0
  123. /package/.next/standalone/.next/static/{7EbGXTo8ObPhr7UobKARQ → zwUX5EOT_44n1RoB_W9Bo}/_clientMiddlewareManifest.js +0 -0
  124. /package/.next/standalone/.next/static/{7EbGXTo8ObPhr7UobKARQ → zwUX5EOT_44n1RoB_W9Bo}/_ssgManifest.js +0 -0
@@ -0,0 +1,320 @@
1
+ # Inline Suggesting — interaction spec (T1 + T2)
2
+
3
+ Reference-only design artifact. Upgrades Suggest from side-mounted overlay
4
+ cards (`suggestion-card.tsx`) to a GDocs-style inline redline surface. Coheres
5
+ with `two-actions-interaction-spec.md`: Suggest's entry point stays human-only;
6
+ the agent's rewrite arrives as a suggestion and is reviewed on THIS surface.
7
+ That spec's "suggestion-card (existing, unchanged)" becomes "this surface".
8
+ Scope: T1 (perception) + T2 (granularity) only. Comment untouched.
9
+
10
+ ---
11
+
12
+ ## 0. Model recap (drives every rendering decision)
13
+
14
+ - Kinds `replace | insertAfter | insertBefore | delete`; status
15
+ `pending | accepted | rejected` (`types.ts:72-90`).
16
+ - T2 adds `from`/`to` offsets within a block. Absent (T1-era data) → range =
17
+ whole block. **All rendering keys off range, not kind-era** — this is what
18
+ makes the T1→T2 boundary coherent (§7).
19
+ - Suggestions carry `by`, optional `basisDetail`, and — per the tweak re-home
20
+ — `fromInstructionId` linking to an instruction comment.
21
+ - Settling writes .md (accept) or discards (reject); accept supersedes other
22
+ pending suggestions on the same ref; 409 → one retry (unchanged).
23
+
24
+ Rendering mechanism (technical shape, for the frontend agent): a ProseMirror
25
+ **plugin decoration set**, NOT document marks, NOT the old absolute overlay.
26
+ Decorations never enter the doc (can't leak into serialized .md), ride
27
+ position mapping through concurrent edits, and give suggesting-mode capture a
28
+ channel for local not-yet-persisted redline. Widget decorations carry block
29
+ shells; inline decorations carry text ranges.
30
+ ---
31
+
32
+ ## 1. Inline rendering grammar
33
+
34
+ Change type owns color; author owns texture. Survives dark mode, stays
35
+ colorblind-legible (shape + position, not hue), never collides with the muted
36
+ olive/gold palette.
37
+
38
+ | Suggestion | In-prose appearance | Tokens |
39
+ |---|---|---|
40
+ | **Inserted range** | Green text, 2px solid green underline, `--success-soft` 8%-alpha wash | `--success`, `--success-soft` |
41
+ | **Deleted range** | Red text, 1.5px strikethrough, 60% opacity | `--destructive` |
42
+ | **Replacement** | Strikethrough deleted run *immediately followed* by green-underlined inserted run, SAME paragraph flow — GDocs "oldnew" pattern; never two stacked paragraphs | both |
43
+ | **Block insert (insertAfter/Before)** | Full-width "ghost block": the proposed markdown rendered at document typography, `--success-soft` background, 2px `--success` left bar, 10px corner radius, no strikethrough | `--success`, `--success-soft` |
44
+ | **Block delete** | Existing block keeps document typography but content strands get strikethrough; block gets `--destructive-soft` 8% wash + 2px `--destructive` left bar | `--destructive`, `--destructive-soft` |
45
+ | **Hover (desktop)** | The full range (all runs of one suggestion) brightens +12% and a 1px outline in the change color appears | — |
46
+
47
+ ```
48
+ word-level replace: The report was ̶w̶r̶i̶t̶t̶e̶n carefully revised by Alice
49
+ └ red strike └── green underline ─┘
50
+ block delete: ▍~~This entire paragraph is struck at 60% opacity.~~
51
+ ▍ (red bar + soft red wash on the block)
52
+ block insert: ▍┌ proposed new paragraph, fully rendered ────────┐
53
+ ▍│ at document typography, soft green card │
54
+ ▍└───────────────────────────────────────────────┘
55
+ ```
56
+
57
+ **Whole-block vs word-level reads differently on purpose:** range <100% of a
58
+ block → inline runs (seamless prose); range = 100% → block treatment (left bar
59
+ + wash). Threshold: ≥90% of trimmed block text → block treatment (avoids a
60
+ one-char-untouched eyesore).
61
+
62
+ **Author attribution — human vs agent:**
63
+
64
+ - **Human:** label lives in the popover header and the caret chip at the range
65
+ edge. Redline color never encodes author (hue-per-author collapses past 2).
66
+ - **Agent (`by` starts with `ai:` or carries `fromInstructionId`):** underline
67
+ / strike renders **hatched** (repeating-linear-gradient, 45°, 4px period)
68
+ and a `✦` glyph sits at the range's leading edge. Hatching reads
69
+ machine-drawn in both themes without changing hue — agent and human editing
70
+ the same sentence stay comparable at a glance.
71
+
72
+ ```
73
+ human: ────────────── solid green underline
74
+ agent: ✦ ░░▒▒░░▒▒░░▒▒░░ hatched green underline + diamond
75
+ ```
76
+
77
+ **Attribution chip:** every range gets a 16px caret `▾` badge at its end
78
+ (superscript, change-color); N>1 overlapping → `▾2` (§3). This chip is the
79
+ touch click target. Redline applies equally inside code blocks/lists —
80
+ decorations never re-set font.
81
+
82
+ ---
83
+
84
+ ## 2. Review affordance — the inline popover
85
+
86
+ **Accept/Reject live in a popover anchored at the change, opened by click or
87
+ tap. No margin gutter, no permanent side rail.** Rationale: the redline is
88
+ already in place; a persistent control column duplicates the comment-pip
89
+ geometry and dies on narrow/touch layouts.
90
+
91
+ ### 2.1 Open/close
92
+
93
+ - Desktop: click any redline run (or hover 400ms → preview-popover).
94
+ - Touch: tap the `▾` caret badge (44×44 hit slop).
95
+ - One popover at a time. Esc / outside-click closes; accept/reject closes and
96
+ moves focus to the next pending suggestion.
97
+ - Popover is a portal off the decoration's DOM rect, flipping on collision,
98
+ max-width 340px, `var(--radius)`, `--shadow-golden-pop`, `var(--popover)` —
99
+ same visual register as the comment composer.
100
+
101
+ ### 2.2 Anatomy
102
+
103
+ ```
104
+ ┌─ suggestion popover ────────────────────────────────────────┐
105
+ │ ░✦░ agent suggests replacing · Alice · 2m ago (header)│
106
+ │ In response to: "Tighten this paragraph…" ↗ (if linked) │
107
+ │ ┌─────────────────────────────────────────────────────────┐ │
108
+ │ │ The report was ̶w̶r̶i̶t̶t̶e̶n carefully revised by the… (diff) │ │
109
+ │ └─────────────────────────────────────────────────────────┘ │
110
+ │ Reason: clearer prose (if present) │
111
+ │ [ ✓ Accept ] [ ✕ Reject ] 44px touch targets │
112
+ └─────────────────────────────────────────────────────────────┘
113
+ ```
114
+
115
+ - **Header** = `{✦ agent|author label} suggests {kind verb}`; agent rows get
116
+ the hatched swatch.
117
+ - **"In response to:"** renders ONLY when `fromInstructionId` is set; 48-char
118
+ truncate; `↗` scrolls to the instruction thread. Same linkage line as the
119
+ two-actions spec — moved, not redesigned.
120
+ - **Diff pane = word-level diff of the suggestion's range**, even for T1
121
+ whole-block data (the block IS the range). Same §1 grammar, miniaturized,
122
+ rendered at document typography. >12 changed segments collapses behind a
123
+ "show full text" toggle.
124
+ - Accept primary (`--primary` fill), Reject outline; `busy` disables both at
125
+ opacity-50 (existing pattern). 409-after-retry swaps the buttons for
126
+ `⚠ Document changed since this suggestion` + [Refresh review] — unchanged
127
+ semantics from the current card.
128
+
129
+ ### 2.3 Keyboard & a11y
130
+
131
+ Tab reaches each redline range as one stop (role `button`, aria-label
132
+ `"Suggestion by {by}: {kind}, press Enter to review"`); Enter opens the
133
+ popover; ⌥Enter accepts directly. Popover traps focus; aria-live announces
134
+ "Suggestion accepted".
135
+
136
+ ### 2.4 Review navigation
137
+
138
+ With ≥1 pending suggestion, the status bar gains `✎ 3 suggestions`: click →
139
+ dropdown (author, kind, diff preview) + prev/next arrows that scroll-into-view
140
+ and open each popover. This inherits the "where are my suggestions?" job of
141
+ today's always-visible side cards without pinning N cards beside blocks.
142
+
143
+ ---
144
+
145
+ ## 3. Overlapping suggestions
146
+
147
+ Two+ pending suggestions whose ranges intersect (T2) or share a ref (T1):
148
+
149
+ **In prose:** only ONE suggestion renders its full redline per overlapping
150
+ cluster — the most recent. Others collapse into a **stack stripe**: the caret
151
+ badge reads `▾N` with N−1 two-px tick marks stacked below it in each
152
+ suggestion's change color. The document reads exactly once; no
153
+ strike-on-green pile-ups. Hatched/solid texture survives in the stripe, so
154
+ "the agent's and mine overlap here" is legible from the badge alone.
155
+
156
+ ```
157
+ The report was ̶w̶r̶i̶t̶t̶e̶n revised▾2
158
+ │ red tick (Alice's)
159
+ ┆ green tick (agent's, hatched → agent is stacked)
160
+ ```
161
+
162
+ **In the popover (the switcher):** header gains `◂ 1 of 3 ▸`. Arrows/arrow
163
+ keys cycle the active suggestion; the diff pane re-renders; **Accept/Reject
164
+ always act on the active item**. The prose redline swaps live as you cycle, so
165
+ each option previews in context before settling.
166
+
167
+ **Settling semantics (unchanged engine, extended):** accepting a suggestion
168
+ supersedes overlapping pending ones on the same ref (today: same ref; T2:
169
+ intersecting ranges). The popover of a superseded suggestion shows
170
+ `Settled by another suggestion` and offers `[Dismiss]`. Rejecting one never
171
+ touches the others.
172
+
173
+ **Creation-time collision:** starting a Suggest on a range with a pending
174
+ suggestion shows a non-blocking composer note: "Alice already suggested a
175
+ change here — yours will stack." Never blocked, always informed.
176
+
177
+ ---
178
+
179
+ ## 4. Suggesting mode — live redline, no silent revert
180
+
181
+ Keep the Editing/Suggesting radio and the banner. Kill capture-then-silent-revert.
182
+
183
+ **T1 (block-granular capture, live presentation):**
184
+
185
+ 1. Typing behaves like editing; no mid-typing doc rewrite.
186
+ 2. Block-exit flush (existing `onSelectionUpdate` trigger) posts the op as
187
+ today — but the revert becomes a **crossfade swap**: typed text fades
188
+ (200ms, `--motion-slow`) from normal text into the §1 redline of the
189
+ just-posted suggestion, in place. Content never vanishes; it visibly
190
+ converts into "pending" state. Nothing is silent.
191
+ 3. Entering the mode re-snapshots and shows: `Suggesting — your edits appear
192
+ as suggestions, not saved text.` plus a `2 pending from you` badge.
193
+
194
+ **T2 (true live):** the capture hook diffs the active block vs its snapshot per
195
+ transaction (debounced 300ms) and renders the delta as §1 redline AS YOU TYPE.
196
+ Ops still post only on block-exit; live redline is local decoration state, so
197
+ undo shrinks it and block-exit posts the final range.
198
+
199
+ **Mode exit:** flush everything, then one toast: `3 suggestions created —
200
+ review in the ✎ chip`. Exit never discards; a failed post leaves the text as
201
+ plain editing-mode text plus `Couldn't create suggestion — kept as plain edit`
202
+ (error state, §8). Redline visibility is the proof of posting, killing the old
203
+ silent-data-loss failure mode.
204
+
205
+ ---
206
+
207
+ ## 5. Agent suggestions — the tweak re-home lands here
208
+
209
+ An agent's rewrite arrives as ordinary `suggestion.add` carrying
210
+ `fromInstructionId` and `by: "ai:<id>"`. On this surface:
211
+
212
+ 1. **Prose:** hatched redline + `✦` leading glyph (§1) — unmistakable at
213
+ distance, same color grammar as human.
214
+ 2. **Popover:** ✦ header + hatched swatch + `agent`, with the
215
+ `In response to: "<instruction>" ↗` line under the header (italic,
216
+ `--muted-foreground`, 48-char truncate); `basisDetail` stays a separate
217
+ line below the diff.
218
+ 3. **Instruction stub (two-spec §4):** its `✦ answered — review the
219
+ suggestion below` line now scrolls to the redline and opens the popover.
220
+ The stacked "card above card" picture dissolves — no side card exists; the
221
+ stub remains, the suggestion is inline.
222
+ 4. **Settling linkage (two-spec §5, unchanged):** Accept or Reject resolves
223
+ the instruction. 409 drift still shows the popover refresh state.
224
+ 5. **Arrives while user is suggesting:** renders identically (target-anchored,
225
+ not edit-derived); obeys §3 overlap rules if ranges intersect.
226
+
227
+ Nothing else differs from human — no extra buttons, no auto-accept, no
228
+ agent-only popover variant. One review loop, two textures.
229
+
230
+ ---
231
+
232
+ ## 6. View-mode Suggest (the missing button)
233
+
234
+ Parity with `ViewModeCommentButton` (named by ux-contracts §6.1, today only
235
+ implemented for Comment):
236
+
237
+ - **Placement:** identical selection-following floating button, rendered
238
+ BESIDE the Comment button when a selection exists in read-only view:
239
+ `[ 💬 Comment | ✎ Suggest ]`. Same geometry, same `touch-target` sizing,
240
+ same token set as the comment twin.
241
+ - **Capability:** read-only humans CAN propose. Opens the existing
242
+ SuggestEditPopover pre-filled with the selection's block markdown. Posting
243
+ `suggestion.add` writes only the sidecar — never the .md — so view mode
244
+ stays read-only-for-human with zero integrity risk (the contract's intent).
245
+ - **View-mode popovers:** clicking an existing redline gives the §2.2 popover
246
+ with the action row replaced by `Suggested by {by} · awaiting a reviewer`
247
+ (the card's existing `readOnly` prop models this).
248
+
249
+ ---
250
+
251
+ ## 7. Tier boundary — T1 shipped alone
252
+
253
+ T1 without T2 means every suggestion's range = one whole block:
254
+
255
+ - `replace` → old block in block-strike treatment immediately followed by the
256
+ proposed block as a green ghost insert, in place. Reads "this paragraph
257
+ becomes this paragraph" — verbose for a one-word fix (the known coarse cost)
258
+ but spatially coherent and scroll-stable.
259
+ - `insertAfter/Before` → ghost block; `delete` → red block treatment. Both
260
+ already final form.
261
+ - The popover already shows the WORD-LEVEL diff of the whole-block pair, so the
262
+ reviewer finds the one changed word instantly even at T1. Deliberate
263
+ pressure valve: diff granularity ships at T1 in the popover; range
264
+ granularity ships at T2 in the prose.
265
+
266
+ T2 then only narrows WHAT the redline spans. No grammar change at the cutover;
267
+ no re-learning; settled T1-era suggestions are archived/inert anyway.
268
+
269
+ ---
270
+
271
+ ## 8. States| State | Prose | Popover / chrome |
272
+ |---|---|---|
273
+ | **Composing** (bubble/popover draft) | none yet; block gets 2px `--primary` dashed outline while composer open | SuggestEditPopover as today, kind chips unchanged |
274
+ | **Suggesting-mode typing (T2)** | live local redline, slightly desaturated (60% alpha) until posted (signals "not yet a suggestion") | banner + `pending from you` count |
275
+ | **Pending (human)** | solid redline + `▾` badge | §2.2 popover; in `✎ N suggestions` chip |
276
+ | **Pending (agent)** | hatched redline + ✦ | §2.2 popover + `In response to` line |
277
+ | **Accepted** | redline crossfades to plain text as the .md write lands; 150ms `--motion-base` | toast `Suggestion accepted`; instruction linked → resolves |
278
+ | **Rejected** | redline fades out (inserts vanish, strikes restore to plain) | toast `Suggestion rejected`; suggestion archived |
279
+ | **Drift / stale anchor** (`stale` flag, ref/textHash miss) | block shows redline at last known position with `--warning` outline + ⚠ in caret badge | popover replaces actions: `⚠ Target text changed` + [Re-anchor] [Reject] [Dismiss]; Re-anchor reopens composer on best-effort match |
280
+ | **Overlapping** | one full redline + `▾N` stack stripe | `◂ 1 of N ▸` switcher; settle-active-only |
281
+ | **409 collision on accept** | redline dims to 60% | `Document changed since this suggestion` + [Refresh review] |
282
+ | **Post failure (suggesting flush)** | typed text stays as NORMAL text (never silently kept as pretend-suggestion) | error toast `Couldn't create suggestion — kept as plain edit` + [Retry] |
283
+ | **Empty** | none | `✎` chip absent; zero other chrome |
284
+
285
+ ---
286
+
287
+ ## Summary
288
+
289
+ 1. Inline redline lives in a ProseMirror decoration set (never doc marks),
290
+ grammar: green underline insert / red strike delete / strike→green replace,
291
+ block treatments for 100%-of-block ranges.
292
+ 2. Accept/Reject move into one inline popover per change with a word-level
293
+ diff pane and the instruction linkage line; a status-bar `✎ N` chip +
294
+ arrows carries navigation that the always-visible side cards used to own.
295
+ 3. Overlaps render as one redline + a stacked `▾N` stripe, with a
296
+ `1 of N` switcher in the popover; settling supersedes the rest.
297
+ 4. Suggesting mode keeps its radio but stops silently reverting: T1 crossfades
298
+ typed text into posted redline on block-exit; T2 renders live local redline
299
+ as you type.
300
+ 5. Agent suggestions are the same grammar with hatched texture + ✦ + the
301
+ `In response to` line — one review loop, two textures, tweak re-home intact.
302
+ 6. T1-alone is coherent: whole-block redline in place + word-level diff in the
303
+ popover; T2 only narrows prose granularity, changes no grammar.
304
+ 7. **Riskiest assumption:** ProseMirror decorations stay stable and cheap
305
+ across whole-document re-renders driven by our fs-event sidecar reloads —
306
+ if decoration positions flicker or drop during concurrent .md writes (the
307
+ same events that trigger today's "coarse whole-sidecar reload"), inline
308
+ redline will visibly jump, which reads far worse than a card ever did.
309
+ Needs a spike on decoration mapping through `setContent` refreshes before
310
+ T1 build.
311
+
312
+ ## Open questions
313
+
314
+ 1. T2 overlap: does same-ref supersede become range-intersection supersede in
315
+ the applier (engine change) or stay client-side gating? Spec assumes
316
+ range-intersection; engine owner confirms.
317
+ 2. `fromInstructionId` dedicated field vs reusing `inResponseTo` on the block
318
+ op — recommend dedicated; owned by two-actions spec open q2.
319
+ 3. Word-diff tokenization must treat markdown punctuation (link/emphasis
320
+ syntax) as separators or redline gets noisy; fix rules with frontend agent.
@@ -0,0 +1,161 @@
1
+ # Tweak — re-homed interaction spec (OPS-22)
2
+
3
+ Reference-only design artifact. Rebuilds the Tweak interaction on the proof sidecar engine.
4
+ No live engine, no polling, no variants, no speculative preview.
5
+
6
+ Composed vocabulary used throughout:
7
+ - **Comment** = discuss (human conversation about a target).
8
+ - **Suggest** = propose (a human authors an edit for review).
9
+ - **Tweak** = delegate (ask an agent to change a target; result arrives as a suggestion or reply).
10
+
11
+ ---
12
+
13
+ ## A. Entry point — third bubble-menu action
14
+
15
+ **Decision: Tweak is a third, sibling action next to Comment and Suggest. Not a kind toggle inside Comment.**
16
+
17
+ ```
18
+ [ B I U S <> | sup sub | link | 💬 Comment ✎ Suggest ✨ Tweak ]
19
+ (discuss) (propose) (delegate)
20
+ ```
21
+
22
+ - Icon: `Wand2` (or `Sparkles`), same 28px button geometry as Comment/Suggest in `bubble-menu.tsx`. Title: "Tweak — ask an agent to change this".
23
+ - Justification against existing semantics:
24
+ - Comment and Suggest are both **human-authored**. The unit of work the user commits to is "write words" (an opinion or a concrete edit).
25
+ - Tweak is **agent-bound**: the user commits to "describe intent", and the artifact's lifecycle (queued → sent → answered) is owned by the machine side. The two affordances diverge immediately after the first click.
26
+ - Folding Tweak into Comment with a kind toggle makes the dominant "discuss" path pay a mode choice it doesn't need, and hides the only agent entry point behind the least-agent-looking button in the UI.
27
+ - It also matches the surface truth: on text/code the entry point already exists as a separate pip, so parity is "one pip per intent", not "one pip with a toggle".
28
+ - Read-only mode: Comment stays as-is. Tweak shows too (writing an instruction-comment is not a doc edit).
29
+
30
+ ### Text/code source viewer
31
+ The existing Comment pip gains a sibling: **Tweak** pip on the same selection→lineAnchor detection. Same targeting panel. No new selection machinery.
32
+
33
+ ### HTML preview iframe
34
+ **No Tweak affordance inside the iframe.** No element picker, no anchoring into the sandbox. See E.
35
+
36
+ ---
37
+
38
+ ## B. Default dispatch — instruction-comment (option a)
39
+
40
+ **Decision: dispatch = persist `comment.add {kind:"instruction", instructionState:"queued"}` at the moment the user confirms the tweak. Copy-as-prompt is an ever-present secondary, not the default.**
41
+
42
+ Rationale:
43
+ 1. **Durable and per-target by construction.** The artifact survives refresh, navigation, agent restarts. Copy-prompt dies the moment the user copies something else or the tab closes.
44
+ 2. **Agent idleness is not a failure mode.** The instruction waits in the sidecar; the agent picks it up on its own schedule via its normal snapshot/activity flow. "Queued" is a true statement, not a fake progress bar. PM's worry ("agent may be idle") is a *labeling* problem, not a dispatch problem — solve with copy, not with ephemerality.
45
+ 3. **One engine.** Option (b) reintroduces a second channel (clipboard) as the primary path and leaves the `instruction` kind — which already exists in the store — permanently UI-less. That leaves dead schema, which is worse than dormant lifecycle.
46
+ 4. The cost of (a) is honest-state UX (must visibly show "waiting", never imply work in progress). That cost is paid once, in the instruction card, and is itemized in F.
47
+
48
+ Trade-off being accepted: if the user's agent never integrates with the sidecar, instructions dead-end at queued. Mitigation: every pending instruction card has **Copy as prompt** one click away, and nothing in the UI claims the agent is running.
49
+
50
+ No client dispatcher in v1. "Dispatch" = write the op. The dormant queued→sent→answered lifecycle is driven by the agent's own reads/writes, exactly as designed in `types.ts`.
51
+
52
+ ---
53
+
54
+ ## C. Queue — dead. Each tweak persists immediately.
55
+
56
+ **Decision: no queue bar, no batch step, no Cancel-all.** Cmd+Enter in the targeting panel writes the instruction-comment immediately. "Batch" = "the agent has N pending instructions", visible as N pips in the doc.
57
+
58
+ - The old queue existed because the live engine needed a single dispatch moment for its variants round-trip. That reason is gone.
59
+ - With instruction-comments, the sidecar *is* the queue. A second in-memory queue duplicates durable state, reintroduces lost-on-refresh risk, and forces Cancel semantics ("delete queued" vs "delete persisted") that don't exist anywhere else in the app.
60
+ - **Dedup-in-place survives**: re-targeting a block/line that already has a pending instruction opens the panel prefilled with that instruction's text; Cmd+Enter updates it (`comment.update`), count does not grow.
61
+ - Resulting chrome:
62
+
63
+ ```
64
+ ┌─ targeting panel (anchored to block, as before) ──────────────────┐
65
+ │ ✨ Tweak this block [Copy as prompt] │
66
+ │ ┌──────────────────────────────────────────────────────────────┐ │
67
+ │ │ What should change? │ │
68
+ │ └──────────────────────────────────────────────────────────────┘ │
69
+ │ ⏎ Ask agent esc Cancel │
70
+ └───────────────────────────────────────────────────────────────────┘
71
+
72
+ no bottom bar anywhere.
73
+
74
+ ┌─ pending instruction pip (block gutter, md) ───────────────────────┐
75
+ │ ✨ you · queued — waiting for your agent [Copy prompt] │
76
+ │ "Tighten this paragraph, keep the citation." │
77
+ │ [Remove] │
78
+ └────────────────────────────────────────────────────────────────────┘
79
+ ```
80
+
81
+ - Doc-level affordance (cheap, optional v1): toolbar chip "✨ N pending" when N>0; click = jump to first. This replaces the queue bar's only real value (visibility of outstanding work).
82
+
83
+ ---
84
+
85
+ ## D. Review loop
86
+
87
+ ### Markdown — full loop, existing machinery
88
+ 1. Agent reads the queued instruction, writes `suggestion.add` (replace/insert/delete) + flips the instruction to `answered` (link via `fromCommentId`/basis pointing at the instruction id).
89
+ 2. User sees the existing `suggestion-card.tsx` inline: two-pane replace diff, kindVerb header, Accept/Reject.
90
+ 3. Accept pushes the block op with `baseRevision`; 409 → retry once with `getLatestRevision()` (already implemented) → doc writes. The instruction is resolved when its linked suggestion settles.
91
+ 4. If the agent replies without a suggestion (a question/clarification), it lands as a **comment reply threaded on the instruction pip**. User can answer in-thread or dismiss the instruction.
92
+
93
+ ### Text/code — degraded, honest loop (comments only)
94
+ - Instruction persists at the lineAnchor, pip appears in gutter, same queued/answered states.
95
+ - The agent **cannot suggest**. Its answer arrives as a **comment reply containing the proposed lines in a fenced code block**, plus prose.
96
+ - The user edits the file by hand. There is no Accept button.
97
+ - The instruction card on these surfaces adds one line of expectation-setting copy:
98
+ > "Comments only on this file type — the agent replies here; you apply the change."
99
+ - This is stated at *dispatch* time (footnote under the panel's Ask button), not discovered at answer time.
100
+
101
+ ### HTML
102
+ No tweak in the preview. User path: switch to source view → line-anchored instruction → hand-apply. See E.
103
+
104
+ ---
105
+
106
+ ## E. Surface scope — ship markdown first
107
+
108
+ **Ship: markdown-only tweak.** Then text/code (cheap incremental). HTML gets nothing beyond the source-view path.
109
+
110
+ | Surface | Ships | Tweak gets | Honest limit |
111
+ |---|---|---|---|
112
+ | Markdown .md | v1 | instruction + suggestion + Accept-writes-doc | none |
113
+ | Text/code | v1.1 (same engine, presumable immediately after) | instruction + comment-reply answer | no suggestions, no Accept; user hand-edits |
114
+ | HTML | never in iframe | nothing | preview has no anchor model; all collaboration happens in source view |
115
+
116
+ Explicit scope statement for the roadmap: **in-place accept exists only where suggestions exist, and suggestions exist only in markdown.** Any tweak on any other surface ends in "read the reply, edit by hand". The UI must say this at the moment of dispatch, not bury it.
117
+
118
+ Html-iframe element-picking from the pre-removal build is permanently dropped with its engine. Do not resurrect.
119
+
120
+ ---
121
+
122
+ ## F. States
123
+
124
+ 1. **Targeting**
125
+ - md: select text/block → bubble menu ✨ → panel anchored. Text/code: selection → Tweak pip → same panel. Footer always: [Copy as prompt] secondary + ⏎ Ask agent primary + esc Cancel. Outside-click with empty input = cancel (unchanged from pre-removal build).
126
+ - Non-md panel footnote: "Agent answers as a comment on this line."
127
+ 2. **No-agent**
128
+ - Tweak still writes the instruction (durability doesn't depend on a live agent). Panel secondary button label: "Copy as prompt".
129
+ - Where agent status is surfaced (toolbar chip / AI panel entry): "Connect an agent" CTA opens the AI panel. Pending instruction is preserved (it's in the sidecar, so nothing to preserve — that's the point of B).
130
+ - Copy-as-prompt emits the exact pre-removal format: `Edit the file \`X\` (a Markdown document). Apply these changes:\n\n1. \`<snippet>\`: <instruction>`; single tweak → same string minus numbering OR keep numbering for format stability (recommend: keep `1.` always, one code path). Clipboard gate + "Show" textarea fallback unchanged.
131
+ 3. **Dispatched/pending** (`queued`/`sent`)
132
+ - Pip + expanded card: instruction text, state line "waiting for your agent", [Copy prompt], [Remove]. No spinner claiming activity. Copy states "Your agent will pick this up when it next reads this file."
133
+ 4. **Answered**
134
+ - md: suggestion-card inline (existing Accept/Reject). Instruction collapses to a settled state linked from the card's reason line ("In response to: <instruction text>").
135
+ - text/code: reply in instruction thread with fenced proposal; card gains [Mark done] (human-settled, no doc write).
136
+ 5. **Error/drift**
137
+ - Accept collision: handled (409 + single retry in suggestion-card). Fingerprint mismatch after retry: card shows "Document changed since this suggestion — refresh review"; user re-reviews.
138
+ - Target moved/deleted (stale ref/textHash): instruction pip turns to "⚠ target changed" with [Re-anchor] (re-open targeting on best-effort current match), [Copy prompt], [Remove]. Never silently drop.
139
+ 6. **Empty**
140
+ - No pending instructions + no open panel = zero chrome. No empty queue bar, no placeholder states.
141
+
142
+ State→copy-prompt presence summary: visible in **every** state except Targeting-with-agent-idle (where it's the secondary anyway). It is the universal escape hatch, not a mode.
143
+
144
+ ---
145
+
146
+ ## Open questions
147
+
148
+ 1. Does the agent actually flip `instructionState` queued→sent→answered, or does it simply answer and we infer? (Server/route contract — needs an ops decision, not a UX one.)
149
+ 2. Suggestion↔instruction linkage: add `fromInstructionId` to suggestion, or reuse existing basis fields? Prefer explicit id; basis strings are for humans.
150
+ 3. Telemetry: none in v1, but "how many tweaks dead-end at queued for >24h" is the metric that validates decision B. Worth a follow-up.
151
+
152
+ ---
153
+
154
+ ## Summary (recommended design)
155
+
156
+ 1. Third bubble-menu action ✨ Tweak (delegate) beside Comment (discuss) and Suggest (propose); matching pip in text/code viewer; nothing in the HTML iframe.
157
+ 2. Dispatch persists a durable `instruction`-kind comment immediately — copy-as-prompt is the constant secondary/escape hatch, never the default.
158
+ 3. Queue bar is deleted; the sidecar is the queue; dedup-in-place survives.
159
+ 4. Markdown review = existing suggestion-card Accept/Reject; text/code review = agent reply in thread + human hand-edit, with the limitation stated at dispatch time.
160
+ 5. Ship markdown-only first; text/code follows on the same engine; HTML collaborates only via source view.
161
+ 6. **Riskiest assumption:** the user's chat agent actually consumes the sidecar and answers with a suggestion op — the dormant instruction lifecycle becoming real is the entire bet of option B; if it never integrates, every tweak ends at "queued + copy prompt", and the UI must already be honest about that on day one.