deepseek-coordination 0.1.1 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +271 -14
- package/README.zh-CN.md +160 -11
- package/client.js +338 -0
- package/cordis.patch.yml +40 -0
- package/package.json +22 -2
- package/src/host/bin/dsh-os.ps1 +10 -1
- package/src/host/bin/dsh-os7.ps1 +16 -1
- package/src/plugin/index.js +4 -213
- package/src/plugin/invoke.js +301 -0
- package/src/plugin/settings.js +340 -0
- package/src/plugin/takeover.js +432 -0
- package/src/plugin/workflow.js +1464 -0
- package/tools/measure-cache.mjs +223 -0
package/README.md
CHANGED
|
@@ -68,15 +68,24 @@ deepseek-coordination/
|
|
|
68
68
|
├─ src/host/docs/os-spec.md full specification: contracts, error codes, golden matrix
|
|
69
69
|
├─ src/host/docs/permission-model.md the authorization model and its residual risk
|
|
70
70
|
├─ src/plugin/index.js host plugin: registers the `dsh_os` tool
|
|
71
|
+
├─ src/plugin/invoke.js shared invocation layer: artefact/edition resolution, spawn, envelope parsing
|
|
72
|
+
├─ src/plugin/settings.js settings service + the panel's two HTTP routes (`/api/dsh-os/*`)
|
|
73
|
+
├─ src/plugin/takeover.js `tools/execute` interception: answers the built-in read/write/edit/pwsh through dsh-os
|
|
74
|
+
├─ src/plugin/workflow.js intent-first workflow: the `workflow_plan` tool, its phase gates and its prompt section
|
|
75
|
+
├─ client.js browser half: the `dsh.client` artifact that renders the settings panel
|
|
71
76
|
├─ src/permission/index.js consent delegate: the only writer of permission.json, registers five `os_permission_*` tools
|
|
72
77
|
├─ tools/build.ps1 assembles the three generated scripts; `-Check` fails on drift
|
|
73
78
|
├─ tools/parse-check.ps1 parse-checks each module under the runtime that will execute it, and reports non-ASCII bytes and a BOM
|
|
74
79
|
├─ tools/json-field.mjs reads one field of an envelope by dotted path (used by the shell suites; `sed` cannot see into a nested object)
|
|
75
|
-
├─ tools/verify-bundle.mjs verifies the loader's resolution contract (
|
|
76
|
-
├─ tools/verify-plugin-load.mjs runs the real `apply()` path (
|
|
80
|
+
├─ tools/verify-bundle.mjs verifies the loader's resolution contract (16 cases)
|
|
81
|
+
├─ tools/verify-plugin-load.mjs runs the real `apply()` path, including the workflow state machine (30 cases)
|
|
82
|
+
├─ tools/verify-package.mjs packs the package and asserts every runtime-resolved file ships (21 cases)
|
|
77
83
|
├─ tools/verify-editions.mjs proves both PowerShell artefacts are reachable (14 cases)
|
|
78
|
-
├─
|
|
84
|
+
├─ tools/probe-pipeline.mjs drives the interception end to end against a stubbed verb layer (23 cases)
|
|
85
|
+
├─ tests/ golden (Windows/POSIX/desktop injection), table conformance, encoding, plugin, permission, takeover, workflow suites
|
|
79
86
|
│ ├─ conformance.ps1 command.txt and spec/verbs.tsv must describe the same surface (5 cases)
|
|
87
|
+
│ ├─ takeover.test.mjs byte-level format preservation, read windowing, eligibility (41 cases)
|
|
88
|
+
│ ├─ workflow.test.mjs phase gates, the user's choice, and the KV-cache invariant (30 cases)
|
|
80
89
|
│ ├─ smoke-desktop-sh.sh what the POSIX desktop verbs actually inject (52 cases)
|
|
81
90
|
│ └─ fake-xdotool, fake-wmctrl recording stand-ins, so the desktop verbs can be verified with no X server
|
|
82
91
|
└─ permission.json authorization data (an empty file in the repository; that is the initial state)
|
|
@@ -115,8 +124,13 @@ precisely the class of encoding fault this project exists to remove.
|
|
|
115
124
|
|
|
116
125
|
## Getting started
|
|
117
126
|
|
|
118
|
-
|
|
127
|
+
Published on npm, so a DSH profile can install it as a bundle:
|
|
119
128
|
|
|
129
|
+
```console
|
|
130
|
+
dsh plugin install deepseek-coordination
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Or work from source:
|
|
120
134
|
```console
|
|
121
135
|
git clone https://github.com/eloyhere/deepseek-coordination.git
|
|
122
136
|
cd deepseek-coordination
|
|
@@ -145,11 +159,25 @@ because batch-file argument handling is itself a source of quoting bugs.
|
|
|
145
159
|
### Using it from the harness
|
|
146
160
|
|
|
147
161
|
`package.json` declares `dsh.bundle.patch`, so the package installs into a DSH profile as a bundle and
|
|
148
|
-
|
|
162
|
+
inserts five rows:
|
|
163
|
+
|
|
164
|
+
| Row | Entry | What it does |
|
|
165
|
+
|---|---|---|
|
|
166
|
+
| `dsh-os` | `.` | registers the `dsh_os` tool |
|
|
167
|
+
| `dsh-os-settings` | `./settings` | the settings the panel edits, plus its two HTTP routes |
|
|
168
|
+
| `dsh-os-takeover` | `./takeover` | answers the built-in `read`/`write`/`edit`/`pwsh` through dsh-os while enabled |
|
|
169
|
+
| `dsh-os-permission` | `./permission` | the consent delegate, the only writer of `permission.json` |
|
|
170
|
+
| `dsh-os-workflow` | `./workflow` | the intent-first workflow tool and its system-prompt section |
|
|
171
|
+
|
|
172
|
+
The rows must name **different module specifiers**: `name` is resolved as a package specifier, and giving
|
|
173
|
+
two rows the bare package name loads the same module twice, which throws `tool "dsh_os" is already
|
|
174
|
+
registered` and aborts the whole entry. That is why the subpath exports exist.
|
|
149
175
|
|
|
150
176
|
| Tool | Purpose |
|
|
151
177
|
|---|---|
|
|
152
178
|
| `dsh_os` | Run one verb of the surface and return its JSON envelope verbatim |
|
|
179
|
+
| `os_permission_*` | Read, grant, revoke and audit durable authorization decisions |
|
|
180
|
+
| `workflow_plan` | Run one phase of the intent-first workflow (see [The workflow](#the-workflow)) |
|
|
153
181
|
|
|
154
182
|
One tool rather than one per verb, for three reasons: the verb table is the contract and lives in
|
|
155
183
|
`src/host/spec/verbs.tsv`, so no schema needs regenerating when a verb is added; an agent discovers the
|
|
@@ -165,6 +193,48 @@ shape, so there is no second contract to drift from the script's.
|
|
|
165
193
|
The implementation is spawned by absolute path with an argv array, never through a shell string, so a
|
|
166
194
|
value containing spaces stays one value.
|
|
167
195
|
|
|
196
|
+
### Taking over the built-in operations
|
|
197
|
+
|
|
198
|
+
`dsh_os` is a tool an agent has to be *told* to use, and it never is: the built-in `read`/`write`/`edit`/
|
|
199
|
+
`pwsh` tools are right there, they overlap almost completely, and nothing in the prompt prefers one over
|
|
200
|
+
the other. The guarantees this project exists for therefore stay unexercised — the plugin is installed
|
|
201
|
+
and imperceptible.
|
|
202
|
+
|
|
203
|
+
**Takeover** removes the choice. Enable it in **Settings → DSH OS** and the built-in calls are answered
|
|
204
|
+
by the same machinery, whether or not the agent cooperates. Per-tool switches let you redirect only
|
|
205
|
+
`write`, or only `edit`, and leave the rest alone.
|
|
206
|
+
|
|
207
|
+
It works through the `tools/execute` around-dispatch waterfall: a handler returns `{ value }` without
|
|
208
|
+
calling `next()`, and that value is then validated against the **built-in** tool's own `output.schema`
|
|
209
|
+
and rendered by the **built-in** `output.render`. The model-facing text, the diff cards and the
|
|
210
|
+
presentation metadata are therefore unchanged; only the byte-level source of the value is.
|
|
211
|
+
|
|
212
|
+
**What it does not take over, and why:**
|
|
213
|
+
|
|
214
|
+
* `glob` and `grep`. This surface has no content-search verb at all — `file search` matches *names* — so
|
|
215
|
+
`grep`'s `lineNumber`/`line` cannot be produced, and glob's ignore rules and mtime ordering have no
|
|
216
|
+
equivalent. Redirecting them would silently return a different result set.
|
|
217
|
+
* Background commands and sandbox escalations. A background call's result is a harness job id, which
|
|
218
|
+
this surface cannot mint, and `sandbox_permissions` is the harness's own approval seam.
|
|
219
|
+
* Relative paths. The built-in tools resolve them against the session workspace through `ctx.fs`; this
|
|
220
|
+
plugin has no equivalent resolution, and guessing the process working directory would read a different
|
|
221
|
+
file than the one named.
|
|
222
|
+
|
|
223
|
+
**Two properties are deliberately given up, and you should know them before enabling it:**
|
|
224
|
+
|
|
225
|
+
1. **The filesystem sandbox does not bound a redirected call.** The built-in write path goes through
|
|
226
|
+
`ctx.fs`, which is where the sandbox policy and the read-before-write observation guard live. A
|
|
227
|
+
redirected write runs the verb directly, so dsh-os's own protected-path check is what remains. This
|
|
228
|
+
is a real reduction in defence, not a detail.
|
|
229
|
+
2. **The reported text and the bytes on disk can differ.** With the default *follow the target* format, a
|
|
230
|
+
CRLF file stays CRLF and a BOM stays a BOM, while `value.after` is normalised LF text — which is what
|
|
231
|
+
the built-in `read` would show and what the diff card compares. Choose *same as built-in* if you would
|
|
232
|
+
rather the takeover change nothing about the bytes.
|
|
233
|
+
|
|
234
|
+
Every redirected call falls back to the built-in implementation on any failure, so takeover can change
|
|
235
|
+
how a result is produced but never whether the caller gets one. The panel's activity feed shows what was
|
|
236
|
+
actually taken over and why anything fell back.
|
|
237
|
+
|
|
168
238
|
**Choosing the PowerShell edition.** The default remains the 5.1 artefact (`bin/dsh-os.ps1`),
|
|
169
239
|
**deliberately unchanged**: a deployment that already works should not change behaviour underneath
|
|
170
240
|
itself because a plugin was upgraded. To select the 7.6 artefact (`bin/dsh-os7.ps1`):
|
|
@@ -179,13 +249,177 @@ install locations, because `pwsh` is not always on `PATH` — it is not on this
|
|
|
179
249
|
environment, while 7.6.6 is plainly installed in the standard location.
|
|
180
250
|
|
|
181
251
|
> [!IMPORTANT]
|
|
182
|
-
> **The bundle now loads in a live desktop session.** The plugin
|
|
183
|
-
>
|
|
252
|
+
> **The bundle now loads in a live desktop session.** The plugin is installed into the active profile as
|
|
253
|
+
> a published npm package, and the tools it registers — `dsh_os` plus the five `os_permission_*` tools —
|
|
184
254
|
> are present and callable in a running session. What is still **not** proven is the part that
|
|
185
255
|
> `tools/verify-bundle.mjs` cannot cover: that the *harness's own loader* composes the bundle by the
|
|
186
256
|
> same contract the verifier checks, rather than by a shortcut that happens to work here. The
|
|
187
257
|
> verifier remains the evidence; the live session is corroboration.
|
|
188
258
|
|
|
259
|
+
### The workflow
|
|
260
|
+
|
|
261
|
+
Every other row in this package makes an OS call **correct**. This one makes a *request* decided before it
|
|
262
|
+
is executed. The failure mode it removes is a request that was never a specification: the model starts
|
|
263
|
+
editing files, and the divergence is discovered at review — after the tokens have been spent.
|
|
264
|
+
|
|
265
|
+
`dsh-os-workflow` registers one tool, `workflow_plan`, and one system-prompt section.
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
intake -> plan -> choose -> run -> done
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
| Phase | Action | What it establishes |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| intake | `start` | the prompt **verbatim**, plus 1–3 questions about intent *or* the assumptions being made |
|
|
274
|
+
| plan | `plan` | the approach and process, **at least two** alternatives with pros and cons, the criteria, and a recommendation |
|
|
275
|
+
| choose | `choose` | **the user's decision**, taken through `ctx.userQuestions` inside the call |
|
|
276
|
+
| run | `run` | execution, its steps, and — in the same call — the outcome that closes the workflow |
|
|
277
|
+
| — | `note` | a remark or a decision made during execution |
|
|
278
|
+
| — | `checkpoint` | folds the record into one file and reports a summary |
|
|
279
|
+
| — | `status`, `reset` | read the state; discard it and start again |
|
|
280
|
+
|
|
281
|
+
> [!IMPORTANT]
|
|
282
|
+
> **The tool is named `workflow_plan`, not `workflow`, and the reason is a real collision.** A host
|
|
283
|
+
> composition already exposes a built-in `workflow` tool — the JavaScript multi-agent orchestrator from
|
|
284
|
+
> `@deepseek-ai/dsh-tool-workflow`. Two tools sharing one name in one scope collide: the second
|
|
285
|
+
> registration aborts, or both resolve and the model is offered two unrelated tools under one name. Since
|
|
286
|
+
> this row's tool lives in the host plane while `workflow` is mounted per agent preset, no test in this
|
|
287
|
+
> repository would have caught it; it was caught by reading the live tool catalog. `_plan` also says what
|
|
288
|
+
> this tool is: the planning half of a workflow.
|
|
289
|
+
|
|
290
|
+
**The gates are real.** A phase whose entry has not been recorded is refused, and the refusal names the
|
|
291
|
+
phase that must run first. `choose` cannot be answered by the model: the question goes to the user through
|
|
292
|
+
the same seam `ask_user_question` uses, and nothing else can move the workflow into `run`.
|
|
293
|
+
|
|
294
|
+
**Four calls, one question.** The unavoidable path is `start` → `plan` → `choose` → `run`. It was
|
|
295
|
+
originally eight calls and two questions, and the difference is three deliberate fusions — each of which
|
|
296
|
+
removes a round-trip without removing a decision:
|
|
297
|
+
|
|
298
|
+
* **design and options are one call**, because they are one decision. This is the QOC shape (Questions,
|
|
299
|
+
Options, Criteria) and the ADR shape (Context, Decision, Consequences): a decision record holds the
|
|
300
|
+
alternatives it was chosen between and the criteria it was judged against. Splitting them made the
|
|
301
|
+
second call re-derive the first and made the user read the same architecture twice.
|
|
302
|
+
* **a clear request costs zero questions.** `start` accepts `assumptions` instead of `questions`, so an
|
|
303
|
+
unambiguous prompt is not interrupted — and the assumption is on record where the user can correct it,
|
|
304
|
+
which is cheaper than the question it replaced. The evidence for *minimal, relevant* questions over
|
|
305
|
+
interrogation is the Ask-to-Act line of work ([arXiv:2504.00907](https://arxiv.org/abs/2504.00907)).
|
|
306
|
+
* **a single-step execution closes in the same call it opens.** Passing `outcome` to `run` finishes the
|
|
307
|
+
workflow; only multi-step work needs a second call.
|
|
308
|
+
|
|
309
|
+
**The compression is the other half.** A plan narrated into the conversation is re-sent with every later
|
|
310
|
+
step, which is what makes a short task expensive. So it never enters the conversation: each phase writes a
|
|
311
|
+
real document and returns a line — phase, summary, file path, one next call. `tests/workflow.test.mjs`
|
|
312
|
+
measures it and holds every phase envelope under 2000 characters; in the current build the four calls of a
|
|
313
|
+
full workflow total about 1.8 kB, roughly 500 tokens, for a plan document measured in kilobytes. This shape
|
|
314
|
+
is measured, not stylistic: pruning plus summarisation improves completion while cutting tokens and runtime
|
|
315
|
+
([Less Context, Better Agents](https://export.arxiv.org/pdf/2608.16370)), and a planner/executor split
|
|
316
|
+
matches interleaved reasoning at a fraction of the token cost
|
|
317
|
+
([Plan Once, Ground Locally](https://zenodo.org/records/22904595)).
|
|
318
|
+
|
|
319
|
+
**Where the cost goes**, stated as complexity because that is what it is:
|
|
320
|
+
|
|
321
|
+
| Path | Cost |
|
|
322
|
+
|---|---|
|
|
323
|
+
| reading state | once per workflow call, cached on the file's mtime — and **never** on a model step, because the prompt section reads no state at all |
|
|
324
|
+
| a phase transition | one atomic state write; the first version wrote it three times per action |
|
|
325
|
+
| a note during execution | one `appendFileSync` append, O(1), bounded by compaction — not the read-push-rewrite that is O(n²) over a workflow |
|
|
326
|
+
| the prompt section | a **constant**: no read, no lookup, no formatting, identical at every phase of every workflow |
|
|
327
|
+
|
|
328
|
+
### The KV cache effect, which is why the prompt section is a constant
|
|
329
|
+
|
|
330
|
+
The section this row registers into the system prompt is a **constant string**. That is not tidiness; it is
|
|
331
|
+
the design, and the reason is that the prompt is the worst possible place for anything that moves.
|
|
332
|
+
|
|
333
|
+
**What this package measured.** `tools/measure-cache.mjs` drives the real endpoint
|
|
334
|
+
(`deepseek-flash`, `max_tokens: 1`, one request per variant after one warm request on the same lineage) and
|
|
335
|
+
reports `cache_read_input_tokens` for the shapes this plugin actually produces. Measured twice, with
|
|
336
|
+
identical results:
|
|
337
|
+
|
|
338
|
+
| Shape | Cache read | Total input | Hit |
|
|
339
|
+
|---|---:|---:|---:|
|
|
340
|
+
| an unchanged request (control) | 5,632 | 5,780 | 97.4% |
|
|
341
|
+
| appending a user message | 5,632 | 5,784 | 97.4% |
|
|
342
|
+
| appending a tool result | 5,632 | 5,853 | 96.2% |
|
|
343
|
+
| **one workflow: four appended calls** | 5,632 → 5,888 | ~5,900 each | **96.6% mean** |
|
|
344
|
+
|
|
345
|
+
So the row's own shape is measured warm: four calls, 23,040 cached tokens, every call above 96%.
|
|
346
|
+
|
|
347
|
+
**What this package did NOT manage to measure, and will not claim.** The experiment also tries the
|
|
348
|
+
counterfactual — rewrite the leading system prompt, edit a tool description, reorder two tools — and those
|
|
349
|
+
readings are **not reproducible**: the first run reported 0.0% for all of them, the second reported 97%. The
|
|
350
|
+
provider documents the reason and it is worth knowing: it persists a **common prefix detected across
|
|
351
|
+
requests**, so by the second run the repeated identical prefixes had been learned and a mutation still
|
|
352
|
+
matched. Per-variant A/B isolation on a shared account cannot settle the mutation side, so
|
|
353
|
+
`tools/measure-cache.mjs` asserts only the append side, which is stable, and prints the rest as
|
|
354
|
+
observations rather than claims.
|
|
355
|
+
|
|
356
|
+
**The counterfactual rests on independent evidence instead**, and it is strong:
|
|
357
|
+
|
|
358
|
+
* the provider's own rule is that a cache hit requires a request to **fully match a persisted cache prefix
|
|
359
|
+
unit**, and the system prompt sits before the tool catalog and the entire history
|
|
360
|
+
([Context Caching](https://api-docs.deepseek.com/guides/kv_cache/));
|
|
361
|
+
* a ten-variant experiment on the same endpoint found appending a message kept a **98.7%** hit while adding
|
|
362
|
+
one line at the **end** of the system prompt produced **0.0%**
|
|
363
|
+
([prefix-stability chapter](https://github.com/diguike/book-deepseek-harness/blob/main/book/11-prefix-stability.md));
|
|
364
|
+
* the harness itself paid for this lesson: the sandbox policy used to live in the system prompt, and one
|
|
365
|
+
permission switch dropped cache reads from **~14,700 tokens to 256**. The fix was to move the changing
|
|
366
|
+
fact out of the prompt into the message sequence
|
|
367
|
+
([DSH agent note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-09-02-in-history-system-prompt-replacement.md)).
|
|
368
|
+
|
|
369
|
+
**The first version of this row rendered the current phase into that section**, so a four-call workflow
|
|
370
|
+
re-wrote the system prompt three times. Whether or not the provider happens to forgive that on a given day,
|
|
371
|
+
the row has no business depending on it — so the fix is structural, not cosmetic:
|
|
372
|
+
|
|
373
|
+
* the section is a constant, so this row's **cache effect is `prefix-stable`**: zero prompt changes per
|
|
374
|
+
workflow, asserted by `tests/workflow.test.mjs` against the registered text at every phase, and again
|
|
375
|
+
after deleting `state.json` outright;
|
|
376
|
+
* the phase and the **one next call** travel in the `workflow_plan` tool's own result instead — appended
|
|
377
|
+
history, measured above at 96–97%;
|
|
378
|
+
* a step taken out of order is refused, and the refusal names the phase to record first, so the model can
|
|
379
|
+
recover from a stale plan without the prompt having told it anything;
|
|
380
|
+
* the tool **description** carries the discovery text. It lives in the tool catalog: inside the cached
|
|
381
|
+
prefix, sent once, and unchanged for the life of the session.
|
|
382
|
+
|
|
383
|
+
> [!IMPORTANT]
|
|
384
|
+
> **The consequence a reader should draw for their own plugins.** Anything that changes per step — the
|
|
385
|
+
> time, the current directory, a branch, a permission, a phase — must not be written into a prompt section.
|
|
386
|
+
> This harness gives that content its own path: register it with `ctx.systemPrompt.context()` and it is
|
|
387
|
+
> delivered as an appended user message, re-projected only when it changes. A section is for text that is
|
|
388
|
+
> stable for the session.
|
|
389
|
+
|
|
390
|
+
**Where the state lives.** `$DSH_HOME/dsh-os/workflows/<session>/`, beside the settings document and for
|
|
391
|
+
the same reason: the package directory is replaced on upgrade, and the workspace belongs to the user.
|
|
392
|
+
`state.json` is the compact record; every other file is the detail. A finished workflow is archived rather
|
|
393
|
+
than overwritten, so the next one starts clean and the last one stays readable.
|
|
394
|
+
|
|
395
|
+
```
|
|
396
|
+
state.json the compact record: phase, intent, decisions, steps, artifact paths
|
|
397
|
+
journal.jsonl append-only: notes and steps, bounded, folded in by a checkpoint
|
|
398
|
+
01-intent.md the prompt verbatim, then the questions and answers (or the assumptions)
|
|
399
|
+
02-plan.md approach and process, alternatives with pros and cons, criteria, the decision
|
|
400
|
+
03-execution.md chosen option, goal, steps, files in scope, constraints
|
|
401
|
+
04-outcome.md what was delivered, and any step left open
|
|
402
|
+
05-checkpoint.md the fold: everything decided so far, written when a compaction is wanted
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**What is configured.** `workflowRoot` moves the directory; `autoCompact` (default `true`) controls
|
|
406
|
+
whether a checkpoint reports the compaction the engine may perform, and `maxCheckpoints` bounds it. The
|
|
407
|
+
structural half — detail to disk, one line back — is **not** configurable, because it is the reason the
|
|
408
|
+
feature saves anything.
|
|
409
|
+
|
|
410
|
+
> [!WARNING]
|
|
411
|
+
> **This row asks the user questions, so the deployment must be able to answer them.** It injects
|
|
412
|
+
> `userQuestions`; a host without it registers nothing rather than registering a tool that would fail on
|
|
413
|
+
> its second call. Two calls block on a human: `start` when questions are given, and `choose` always.
|
|
414
|
+
|
|
415
|
+
> [!NOTE]
|
|
416
|
+
> **What this row does not do: it does not invoke the engine's compaction service.** `ctx.compaction`
|
|
417
|
+
> exists, and calling it from here would mean constructing a session-shaped context whose contract is not
|
|
418
|
+
> published — a guess dressed up as a feature. What the plugin does instead is deterministic and
|
|
419
|
+
> inspectable: the detail leaves the conversation for a file, and `checkpoint` records the boundary where a
|
|
420
|
+
> compaction is safe. That is why the compression is measurable by `tests/workflow.test.mjs` rather than
|
|
421
|
+
> merely asserted.
|
|
422
|
+
|
|
189
423
|
---
|
|
190
424
|
|
|
191
425
|
## JSON contract
|
|
@@ -546,7 +780,7 @@ been closed by three verifiers, and they found one real defect:
|
|
|
546
780
|
| Verifier | What it proves | Cases |
|
|
547
781
|
|---|---|---|
|
|
548
782
|
| `tools/verify-bundle.mjs` | the loader's **resolution** contract: the manifest header, the patch file, every row resolving from a profile-style `node_modules`, every resolved module exporting the Cordis shape the loader calls (`apply`/`inject`/`name`), the two rows resolving to **different** modules (a shared specifier registers the same tool twice and aborts the whole entry), and the version gate | 10 |
|
|
549
|
-
| `tools/verify-plugin-load.mjs` | the real `apply()` path: the host plugin registers `dsh_os` with a parameters schema whose **root type is `object`**, the permission plugin registers its five tools, and **the registered handler really does spawn the script** and return a real envelope (`selfcheck` → ok, `verbCount` 86); a mutating verb without `allow` is refused with a typed `E_PERMISSION` rather than throwing |
|
|
783
|
+
| `tools/verify-plugin-load.mjs` | the real `apply()` path: the host plugin registers `dsh_os` with a parameters schema whose **root type is `object`**, the permission plugin registers its five tools, and **the registered handler really does spawn the script** and return a real envelope (`selfcheck` → ok, `verbCount` 86); a mutating verb without `allow` is refused with a typed `E_PERMISSION` rather than throwing. It also covers the workflow row, whose failure would otherwise be silent: the tool and its prompt section **are** registered, and `start → plan → choose → run` runs through the real handler with its gates intact | 30 |
|
|
550
784
|
| `tools/verify-editions.mjs` | edition selection: the default is still PowerShell 5.1; `powershellEdition: '7.6'` and `DSH_OS_POWERSHELL_EDITION` select the 7.6 artefact; `'auto'` uses 7.6 only when the artefact and a working `pwsh` are present **together**; configuration beats the environment; an explicit `scriptPath` disables the choice. `-ExecutionPolicy` is passed only to the 5.1 executable | 14 |
|
|
551
785
|
|
|
552
786
|
The bundle verifier links the package into a throwaway profile sandbox by junction, so resolution
|
|
@@ -572,7 +806,7 @@ by a **later** case.
|
|
|
572
806
|
|
|
573
807
|
That defect is **fixed**: the parser case in `tests/golden.ps1` now creates its own fixture
|
|
574
808
|
(`$fixtureDir = Join-Path $sandbox 'parser\verify'`, then `New-Item` and `Set-Content`) and asserts
|
|
575
|
-
against it. The current measurement is **
|
|
809
|
+
against it. The current measurement is **50 passed, 0 failed**.
|
|
576
810
|
|
|
577
811
|
### Two real POSIX defects found by `tests/smoke-desktop-sh.sh`
|
|
578
812
|
|
|
@@ -638,10 +872,19 @@ stand-in tools exposed both:
|
|
|
638
872
|
no acceptance step depends on it. (The plugin's *installation* path does: the package manager
|
|
639
873
|
resolves the repository with `git ls-remote`, so a machine where the harness cannot find `git` on
|
|
640
874
|
`PATH` cannot install the bundle.)
|
|
875
|
+
* **PowerShell 7.6 is exercised, but not across the whole surface.** The 7.6 artefact definitely runs:
|
|
876
|
+
driving the plugin's real `resolveInvocation` path with `DSH_OS_POWERSHELL_EDITION=7.6` selects
|
|
877
|
+
`bin/dsh-os7.ps1` and the runtime that answers is **PowerShell 7.6.6**, while the default stays on
|
|
878
|
+
5.1.19041.3031 — that is what `tools/verify-editions.mjs` asserts and what the 14 cases measured here
|
|
879
|
+
confirm. What has *not* been done is a run of every verb against `dsh-os7.ps1`; its fidelity as a port
|
|
880
|
+
rests on the identical 82-verb dispatch sets, a per-module function-name comparison and the window
|
|
881
|
+
group's golden cases. That is a real gap in the evidence, not a claim of full parity.
|
|
641
882
|
|
|
642
883
|
### What is not proven
|
|
643
884
|
|
|
644
|
-
* That the
|
|
885
|
+
* That the loader composes the bundle **by the contract `tools/verify-bundle.mjs` checks**, rather than
|
|
886
|
+
by a path that happens to work on this machine. The weaker claim — that `dsh_os` and the five
|
|
887
|
+
`os_permission_*` tools appear and are callable in an active session — has since been observed.
|
|
645
888
|
* Complete verb parity between the two platforms — in neither direction.
|
|
646
889
|
* The enforcement effect of layer A on a machine where the harness and the delegate share one identity
|
|
647
890
|
— by construction that case degrades to layer B.
|
|
@@ -665,15 +908,29 @@ npm run test:all # runs everything below, in order
|
|
|
665
908
|
|---|---|---|---|
|
|
666
909
|
| Table conformance | `powershell -File tests/conformance.ps1` | 5 | **5 passed, 0 failed** |
|
|
667
910
|
| Encoding contracts | `powershell -File tests/encoding.ps1` | 32 build modules | **all contracts hold** |
|
|
668
|
-
| Windows golden | `powershell -File tests/golden.ps1` |
|
|
911
|
+
| Windows golden | `powershell -File tests/golden.ps1` | 50 | **50 passed, 0 failed** |
|
|
669
912
|
| POSIX golden | `bash tests/golden-sh.sh` | 116 | **116 passed, 0 failed** |
|
|
670
913
|
| POSIX desktop injection | `bash tests/smoke-desktop-sh.sh` | 52 | **52 passed, 0 failed** |
|
|
671
914
|
| Plugin | `node tests/plugin.test.mjs` | 13 | **13 passed, 0 failed** |
|
|
915
|
+
| Takeover interceptions | `node tests/takeover.test.mjs` | 41 | **41 passed, 0 failed** |
|
|
672
916
|
| Permission delegate | `node tests/permission.test.mjs` | 18 | **18 passed, 0 failed** |
|
|
673
|
-
|
|
|
674
|
-
|
|
|
917
|
+
| Workflow | `node tests/workflow.test.mjs` | 30 | **30 passed, 0 failed** |
|
|
918
|
+
| Bundle resolution | `node tools/verify-bundle.mjs` | 16 | **16 passed, 0 failed** |
|
|
919
|
+
| Package completeness | `node tools/verify-package.mjs` | 21 | **21 passed, 0 failed** |
|
|
920
|
+
| Plugin load | `node tools/verify-plugin-load.mjs` | 30 | **30 passed, 0 failed** |
|
|
675
921
|
| Edition selection | `node tools/verify-editions.mjs` | 14 | **14 passed, 0 failed** |
|
|
676
922
|
| Build drift | `powershell -File tools/build.ps1 -Check` | 3 artefacts | **no drift** |
|
|
923
|
+
| Prefix cache (`npm run measure:cache`) | `node tools/measure-cache.mjs` | 2 claims | **PASS — 96.6% mean hit over four workflow calls** |
|
|
924
|
+
|
|
925
|
+
`measure:cache` is **not** part of `test:all`: it calls the real provider, so it needs
|
|
926
|
+
`DEEPSEEK_API_KEY` (read from `$DSH_HOME/.credentials.yaml`) and it costs tokens. It exits non-zero only
|
|
927
|
+
when a measured claim does not hold.
|
|
928
|
+
|
|
929
|
+
> [!NOTE]
|
|
930
|
+
> **`test:desktop` needs `bash`, and this machine has none.** `npm run test:all` therefore ends with
|
|
931
|
+
> `'bash' is not recognized` on a Windows host without Git Bash or WSL on `PATH`. That is an environment
|
|
932
|
+
> gap in the runner, not a failing assertion: every suite above it ran and passed in the same invocation.
|
|
933
|
+
> The POSIX suites remain unverified here for the same reason they always were — they need a POSIX host.
|
|
677
934
|
|
|
678
935
|
### Conformance of the two tables
|
|
679
936
|
|
|
@@ -812,7 +1069,7 @@ Copyright (c) 2026 Eloy Kim
|
|
|
812
1069
|
|
|
813
1070
|
The licence file is pure ASCII, LF-terminated and BOM-free, consistent with the rest of the
|
|
814
1071
|
repository's encoding contracts. One earlier description needs correcting: it is **not** listed in
|
|
815
|
-
`package.json`'s `files` whitelist (which has
|
|
1072
|
+
`package.json`'s `files` whitelist (which has 20 entries; it includes `src/host/bin/dsh-os7.ps1` but
|
|
816
1073
|
neither `LICENSE` nor `README.zh-CN.md`), so the file stays in a checkout but is not distributed with
|
|
817
1074
|
the published package.
|
|
818
1075
|
|