dsh-plugin-message-edit 1.0.0 → 1.1.0

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.
@@ -1,75 +1,147 @@
1
- # Development & Testing Guide
1
+ # Development & Testing Guide
2
+
3
+ This guide covers building, testing, packaging, and installing `dsh-plugin-message-edit`.
4
+
5
+ ---
6
+
7
+ ## 1. Repository Structure
8
+
9
+ ```
10
+ dsh-plugin-message-edit/
11
+ ├── lib/
12
+ │ ├── index.js # Host-side Cordis plugin (routes, session log processing)
13
+ │ ├── tree-logic.js # Pure tree algorithms (shared with client and tests)
14
+ │ └── client.js # Generated client bundle (wrapped from plugin.client.js)
15
+ ├── plugin.client.js # Source client-side UI and React components
16
+ ├── scripts/
17
+ │ └── build-client.mjs # Build script wrapping plugin.client.js into lib/client.js
18
+ ├── test/
19
+ │ └── tree.test.mjs # Automated test suite (36+ unit tests)
20
+ ├── cordis.patch.yml # Service dependencies and injection metadata
21
+ ├── docs/ # Technical architecture and data model documentation
22
+ └── package.json
23
+ ```
24
+
25
+ ---
26
+
27
+ ## 2. Build Pipeline
28
+
29
+ The client component [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js) is written in browser-compatible JavaScript. Before distribution or testing, it is wrapped with a Cordis module preamble into [`lib/client.js`](file:///D:/dsh-plugin-message-edit/lib/client.js).
30
+
31
+ ### Build Client
32
+ ```bash
33
+ npm run build
34
+ ```
35
+ Executes `node scripts/build-client.mjs` to regenerate `lib/client.js`.
36
+
37
+ ### Check Build Integrity
38
+ ```bash
39
+ node scripts/build-client.mjs --check
40
+ ```
41
+ Exits with code 1 if `lib/client.js` is out of date relative to `plugin.client.js`.
42
+
43
+ ---
44
+
45
+ ## 3. Testing
46
+
47
+ The project includes an automated test suite verifying tree construction, sibling fan-out, ghost recovery, active path calculation, and ring index calculation.
48
+
49
+ ```bash
50
+ npm test
51
+ ```
52
+ Automatically builds the client first, checks that it matches the source, then
53
+ runs the Node test runner. Install development dependencies with `npm ci` on a
54
+ fresh checkout before running tests (Node 22.19+ or Node 24).
55
+
56
+ - `test/tree.test.mjs`: 36 assertions covering branch and version-tree behavior.
57
+ - `test/client-images.test.mjs`: loads the generated client through its module
58
+ loader, mounts its registered user-message component using React and jsdom,
59
+ and checks image delegation, multiple/image-only messages, old-host fallback,
60
+ text-only messages, and entering/cancelling an edit. Host services and the image
61
+ gallery are test doubles; these are not full DSH integration tests.
62
+ - `test/client-registration.test.mjs`: checks module dependencies and visible
63
+ failures when services or registrations are unavailable.
64
+ - `test/session-record.test.mjs` and `test/host-compatibility.test.mjs`: cover
65
+ legacy/current session shapes, live/resumed edit requests, retained images,
66
+ retry ancestry, nested version markers, and cache invalidation.
67
+
68
+ GitHub Actions runs these checks for pull requests and branch pushes, on
69
+ Linux (Node 22 and 24) and Windows (Node 22). It also runs:
70
+
71
+ ```bash
72
+ npm run check:package
73
+ ```
74
+
75
+ This creates a real npm archive in a temporary directory, verifies that runtime
76
+ entry points are present and development-only directories are absent, checks
77
+ the host entry's syntax, and removes the temporary archive. `npm pack` and
78
+ `npm publish` now build the client automatically through `prepack`, preventing
79
+ an old or missing generated client from being shipped.
80
+
81
+ The workflow needs to be pushed to GitHub to run there. Making its checks
82
+ mandatory before merging is a separate repository ruleset/branch-protection
83
+ setting; adding the workflow alone does not block the Merge button.
84
+
85
+ Before accepting an image-rendering change, also check it in a running DSH:
86
+ send text with one/multiple images and an image-only message, open an image in
87
+ the native viewer, enter/cancel an edit, then verify the original attachments
88
+ survive an edit submission. CI's gallery double cannot verify native image
89
+ loading, lightbox behavior, or compatibility with DSH's module injection.
90
+
91
+ To add new tests, edit [`test/tree.test.mjs`](file:///D:/dsh-plugin-message-edit/test/tree.test.mjs).
92
+
93
+ ### Optional real DSH acceptance
94
+
95
+ `test/fixtures/dsh-acceptance.mjs` is an offline model adapter and live/cold
96
+ session fixture for an installed official DSH `0.1.5-rc.2` runtime. Mount it
97
+ only in a new temporary home whose name contains `message-edit-dsh-qa-`.
98
+ Set `DSH_HOME` to that home and `DSH_QA_MODULES` to the official runtime's
99
+ `node_modules` directory. Use a separate Web profile with the base/Web bundles,
100
+ a built copy of this plugin, and a loader entry for the fixture. Never point
101
+ this fixture at an existing user's DSH home.
102
+
103
+ Once the isolated server prints its URL, run:
104
+
105
+ ```bash
106
+ node scripts/verify-dsh-acceptance.mjs http://127.0.0.1:61587
107
+ ```
108
+
109
+ The verifier exercises real HTTP edit/retry operations, local model execution,
110
+ image retention, unchanged source messages, and nested branch markers. Restart
111
+ the same isolated server and repeat to exercise persisted branches. The fixture
112
+ adds `/qa/state` and `/qa/followup` endpoints solely for this disposable test.
113
+ Browser acceptance additionally checks the settings entry, version switcher,
114
+ thumbnails, and native original-image viewer. No remote API key is needed.
115
+
116
+ For an automated fresh-boot and restart run, set `DSH_QA_MODULES` to the
117
+ official runtime's `node_modules` directory and run:
2
118
 
3
- This guide covers building, testing, packaging, and installing `dsh-plugin-message-edit`.
4
-
5
- ---
6
-
7
- ## 1. Repository Structure
8
-
9
- ```
10
- dsh-plugin-message-edit/
11
- ├── lib/
12
- │ ├── index.js # Host-side Cordis plugin (routes, session log processing)
13
- │ ├── tree-logic.js # Pure tree algorithms (shared with client and tests)
14
- │ └── client.js # Generated client bundle (wrapped from plugin.client.js)
15
- ├── plugin.client.js # Source client-side UI and React components
16
- ├── scripts/
17
- │ └── build-client.mjs # Build script wrapping plugin.client.js into lib/client.js
18
- ├── test/
19
- │ └── tree.test.mjs # Automated test suite (36+ unit tests)
20
- ├── cordis.patch.yml # Service dependencies and injection metadata
21
- ├── docs/ # Technical architecture and data model documentation
22
- └── package.json
23
- ```
24
-
25
- ---
26
-
27
- ## 2. Build Pipeline
28
-
29
- The client component [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js) is written in browser-compatible JavaScript. Before distribution or testing, it is wrapped with a Cordis module preamble into [`lib/client.js`](file:///D:/dsh-plugin-message-edit/lib/client.js).
30
-
31
- ### Build Client
32
- ```bash
33
- npm run build
34
- ```
35
- Executes `node scripts/build-client.mjs` to regenerate `lib/client.js`.
36
-
37
- ### Check Build Integrity
38
- ```bash
39
- node scripts/build-client.mjs --check
40
- ```
41
- Exits with code 1 if `lib/client.js` is out of date relative to `plugin.client.js`.
42
-
43
- ---
44
-
45
- ## 3. Testing
46
-
47
- The project includes an automated test suite verifying tree construction, sibling fan-out, ghost recovery, active path calculation, and ring index calculation.
48
-
49
- ```bash
50
- npm test
51
- ```
52
- Runs the build check and executes `test/tree.test.mjs`.
53
-
54
- To add new tests, edit [`test/tree.test.mjs`](file:///D:/dsh-plugin-message-edit/test/tree.test.mjs).
55
-
56
- ---
57
-
58
- ## 4. Local Installation into DSH Desktop
59
-
60
- ### Step 1: Build and Package
61
- ```bash
62
- npm run build
63
- npm pack
64
- ```
65
- This produces a tarball: `dsh-plugin-message-edit-0.1.0.tgz`.
66
-
67
- ### Step 2: Install into DSH Profile
68
- To install into the DSH Desktop profile:
69
119
  ```bash
70
- dsh plugin --profile desktop add file:/path/to/dsh-plugin-message-edit-0.1.0.tgz
120
+ node scripts/run-dsh-acceptance.mjs
71
121
  ```
72
- Or sync files directly into `~/.dsh/profiles/desktop/node_modules/dsh-plugin-message-edit/`.
73
122
 
74
- ### Step 3: Restart DSH Desktop
75
- Restart DSH Desktop to reload the host-side plugin in the server process and mount the updated client interface.
123
+ The runner owns and removes a unique temporary home, boots the official CLI
124
+ on an OS-selected port, runs the verifier, restarts the host, and repeats.
125
+ The restart pass explicitly resumes and retries an already seeded branch.
126
+ CI runs this on Linux and Windows in addition to the regression suite.
127
+
128
+ ---
129
+
130
+ ## 4. Local Installation into DSH Desktop
131
+
132
+ ### Step 1: Build and Package
133
+ ```bash
134
+ npm run build
135
+ npm pack
136
+ ```
137
+ This produces a tarball: `dsh-plugin-message-edit-0.1.0.tgz`.
138
+
139
+ ### Step 2: Install into DSH Profile
140
+ To install into the DSH Desktop profile:
141
+ ```bash
142
+ dsh plugin --profile desktop add file:/path/to/dsh-plugin-message-edit-0.1.0.tgz
143
+ ```
144
+ Or sync files directly into `~/.dsh/profiles/desktop/node_modules/dsh-plugin-message-edit/`.
145
+
146
+ ### Step 3: Restart DSH Desktop
147
+ Restart DSH Desktop to reload the host-side plugin in the server process and mount the updated client interface.
@@ -1,96 +1,96 @@
1
- # Tree Data Model & Algorithms
2
-
3
- This document details how conversation versions and turns are represented, branched, and visualized in `dsh-plugin-message-edit`.
4
-
5
- ---
6
-
7
- ## 1. Dual-Level Representation
8
-
9
- There are two distinct levels of data representation in the system:
10
-
11
- 1. **Storage Level (Session DAG)**:
12
- - DSH enforces session-level isolation. Each branch is a distinct DSH session record with `parentSession` and `seedLength`.
13
- - The host maintains durable `message-tree/version` markers detailing which turn was edited/retried and what changed.
14
-
15
- 2. **Presentation Level (Turn-Level Branching Tree)**:
16
- - A user thinks of conversation branching at the **message/turn** level, not the session container level.
17
- - `buildTurnTree` projects the session versions into individual turn nodes.
18
-
19
- ```
20
- Session DAG (Storage):
21
- Session A (Original) ──[edit turn 1]──> Session B (Fork)
22
-
23
- Turn Tree (Visualization):
24
- [Root Conversation]
25
- / \
26
- [A: Turn 1 (1/2)] [B: Turn 1 (2/2) - Edited]
27
- | |
28
- [A: Turn 2] [B: Turn 2]
29
- ```
30
-
31
- ---
32
-
33
- ## 2. Core Algorithms
34
-
35
- ### 2.1 Turn Tree Construction (`buildTurnTree`)
36
- *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L173), [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L415)*
37
-
38
- Transforms `versions` into an array of turn nodes:
39
- 1. **Root Conversation Node (`${rootSessionId}#root`)**: Represents the origin anchor of the conversation.
40
- 2. **Root Session Turns**:
41
- - Turn 1 hangs off `${rootSessionId}#root`.
42
- - Turn $k$ ($k > 1$) hangs off `${rootSessionId}#t${k-1}`.
43
- 3. **Forked Session Turns**:
44
- - For a session branched at `targetTurn = T`:
45
- - If $T = 1$: Turn 1 hangs off `${rootSessionId}#root` (sibling of the original Turn 1).
46
- - If $T > 1$: Turn $T$ hangs off `${parentSessionId}#t${T-1}` (sibling of parent's Turn $T$).
47
- - Subsequent turns $T+1, T+2, \dots$ hang off the previous turn in the same session (`${sessionId}#t${k-1}`).
48
- 4. **Safety Fallback**: Any node whose computed `parentId` does not exist in the graph is automatically attached to `${rootSessionId}#root`, preventing disconnected subtrees.
49
-
50
- ### 2.2 Sibling Fan-Out (`attachParentId`)
51
- *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L9)*
52
-
53
- When a user edits Turn 1 repeatedly (e.g. Turn 1 $\rightarrow$ Edit 1 $\rightarrow$ Edit 2 while viewing Edit 1):
54
- - Without fan-out, edits form a chain: $A \rightarrow B \rightarrow C$.
55
- - `attachParentId` traverses up versions of the same turn and stops at the first session that is *not* an edit of that turn ($A$).
56
- - Result: Both Edit 1 and Edit 2 hang off $A$ as sibling branches.
57
-
58
- ### 2.3 Ghost Ancestor Recovery (`ancestorChainFromLog` & `collectFamily`)
59
- *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L110-L171)*
60
-
61
- If an intermediate session in a family is deleted by the user in DSH:
62
- - The deleted session's own event log is gone.
63
- - However, its descendant sessions inherited its prefix log (including the `message-tree/version` marker describing the deleted parent).
64
- - `ancestorChainFromLog` inspects the surviving descendant's seed events to reconstruct deleted ancestors as **ghost nodes** (`deleted: true`).
65
- - `collectFamily` ensures the family graph remains fully connected even when intermediate nodes are deleted.
66
-
67
- ### 2.4 Active Path Calculation
68
- *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L318-L339)*
69
-
70
- To highlight only the active branch path without highlighting superseded sibling branches:
71
- 1. Locate the latest turn node in `currentSessionId`.
72
- 2. Walk upwards following `parentId` pointers until reaching `${rootSessionId}#root`.
73
- 3. Mark only nodes on this walk with `onCurrentPath = true`.
74
-
75
- ### 2.5 Bubble Version Ring (`ringFor`)
76
- *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L30-L68)*
77
-
78
- Calculates the `‹ n/m ›` counter under a message at `turn` while viewing `sessionId`:
79
- - Walks parent links to find the common fork point for that turn.
80
- - Filters out deleted/ghost sessions (renumbering over surviving versions).
81
- - Returns `{ alternatives, index }`. If fewer than 2 alternatives exist, returns `null` (counter is hidden).
82
-
83
- ---
84
-
85
- ## 3. Graph Layout & Springs
86
-
87
- *Location: [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L567-L612)*
88
-
89
- - **Tidy Tree Layout (`layoutTurnTree`)**:
90
- - Leaf nodes take successive horizontal slots (`cursor * SLOT_X`, where `SLOT_X = 206px`).
91
- - Parent nodes center horizontally over their children (`(min_x + max_x) / 2`).
92
- - Depths scale vertically (`depth * SLOT_Y`, where `SLOT_Y = 132px`).
93
- - **Spring Physics (`springs.current`)**:
94
- - Cards smoothly animate to their target coordinates using critically-damped spring equations ($k = 190, c = 24$).
95
- - New cards spawn at their parent's coordinates and spring outward.
96
- - Edges are rendered as cubic SVG bezier curves connecting parent card bottoms to child card tops.
1
+ # Tree Data Model & Algorithms
2
+
3
+ This document details how conversation versions and turns are represented, branched, and visualized in `dsh-plugin-message-edit`.
4
+
5
+ ---
6
+
7
+ ## 1. Dual-Level Representation
8
+
9
+ There are two distinct levels of data representation in the system:
10
+
11
+ 1. **Storage Level (Session DAG)**:
12
+ - DSH enforces session-level isolation. Each branch is a distinct DSH session record with `parentSession`, `isSeeded`, and a separate `inheritedEventCount`.
13
+ - The host maintains durable `message-tree/version` markers detailing which turn was edited/retried and what changed.
14
+
15
+ 2. **Presentation Level (Turn-Level Branching Tree)**:
16
+ - A user thinks of conversation branching at the **message/turn** level, not the session container level.
17
+ - `buildTurnTree` projects the session versions into individual turn nodes.
18
+
19
+ ```
20
+ Session DAG (Storage):
21
+ Session A (Original) ──[edit turn 1]──> Session B (Fork)
22
+
23
+ Turn Tree (Visualization):
24
+ [Root Conversation]
25
+ / \
26
+ [A: Turn 1 (1/2)] [B: Turn 1 (2/2) - Edited]
27
+ | |
28
+ [A: Turn 2] [B: Turn 2]
29
+ ```
30
+
31
+ ---
32
+
33
+ ## 2. Core Algorithms
34
+
35
+ ### 2.1 Turn Tree Construction (`buildTurnTree`)
36
+ *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L173), [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L415)*
37
+
38
+ Transforms `versions` into an array of turn nodes:
39
+ 1. **Root Conversation Node (`${rootSessionId}#root`)**: Represents the origin anchor of the conversation.
40
+ 2. **Root Session Turns**:
41
+ - Turn 1 hangs off `${rootSessionId}#root`.
42
+ - Turn $k$ ($k > 1$) hangs off `${rootSessionId}#t${k-1}`.
43
+ 3. **Forked Session Turns**:
44
+ - For a session branched at `targetTurn = T`:
45
+ - If $T = 1$: Turn 1 hangs off `${rootSessionId}#root` (sibling of the original Turn 1).
46
+ - If $T > 1$: Turn $T$ hangs off `${parentSessionId}#t${T-1}` (sibling of parent's Turn $T$).
47
+ - Subsequent turns $T+1, T+2, \dots$ hang off the previous turn in the same session (`${sessionId}#t${k-1}`).
48
+ 4. **Safety Fallback**: Any node whose computed `parentId` does not exist in the graph is automatically attached to `${rootSessionId}#root`, preventing disconnected subtrees.
49
+
50
+ ### 2.2 Sibling Fan-Out (`attachParentId`)
51
+ *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L9)*
52
+
53
+ When a user edits Turn 1 repeatedly (e.g. Turn 1 $\rightarrow$ Edit 1 $\rightarrow$ Edit 2 while viewing Edit 1):
54
+ - Without fan-out, edits form a chain: $A \rightarrow B \rightarrow C$.
55
+ - `attachParentId` traverses up versions of the same turn and stops at the first session that is *not* an edit of that turn ($A$).
56
+ - Result: Both Edit 1 and Edit 2 hang off $A$ as sibling branches.
57
+
58
+ ### 2.3 Ghost Ancestor Recovery (`ancestorChainFromLog` & `collectFamily`)
59
+ *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L110-L171)*
60
+
61
+ If an intermediate session in a family is deleted by the user in DSH:
62
+ - The deleted session's own event log is gone.
63
+ - However, its descendant sessions inherited its prefix log (including the `message-tree/version` marker describing the deleted parent).
64
+ - `ancestorChainFromLog` inspects the surviving descendant's seed events to reconstruct deleted ancestors as **ghost nodes** (`deleted: true`).
65
+ - `collectFamily` ensures the family graph remains fully connected even when intermediate nodes are deleted.
66
+
67
+ ### 2.4 Active Path Calculation
68
+ *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L318-L339)*
69
+
70
+ To highlight only the active branch path without highlighting superseded sibling branches:
71
+ 1. Locate the latest turn node in `currentSessionId`.
72
+ 2. Walk upwards following `parentId` pointers until reaching `${rootSessionId}#root`.
73
+ 3. Mark only nodes on this walk with `onCurrentPath = true`.
74
+
75
+ ### 2.5 Bubble Version Ring (`ringFor`)
76
+ *Location: [`lib/tree-logic.js`](file:///D:/dsh-plugin-message-edit/lib/tree-logic.js#L30-L68)*
77
+
78
+ Calculates the `‹ n/m ›` counter under a message at `turn` while viewing `sessionId`:
79
+ - Walks parent links to find the common fork point for that turn.
80
+ - Filters out deleted/ghost sessions (renumbering over surviving versions).
81
+ - Returns `{ alternatives, index }`. If fewer than 2 alternatives exist, returns `null` (counter is hidden).
82
+
83
+ ---
84
+
85
+ ## 3. Graph Layout & Springs
86
+
87
+ *Location: [`plugin.client.js`](file:///D:/dsh-plugin-message-edit/plugin.client.js#L567-L612)*
88
+
89
+ - **Tidy Tree Layout (`layoutTurnTree`)**:
90
+ - Leaf nodes take successive horizontal slots (`cursor * SLOT_X`, where `SLOT_X = 206px`).
91
+ - Parent nodes center horizontally over their children (`(min_x + max_x) / 2`).
92
+ - Depths scale vertically (`depth * SLOT_Y`, where `SLOT_Y = 132px`).
93
+ - **Spring Physics (`springs.current`)**:
94
+ - Cards smoothly animate to their target coordinates using critically-damped spring equations ($k = 190, c = 24$).
95
+ - New cards spawn at their parent's coordinates and spring outward.
96
+ - Edges are rendered as cubic SVG bezier curves connecting parent card bottoms to child card tops.