dsh-context-compression-improved 0.1.1 → 0.2.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.
- package/.gitattributes +1 -0
- package/.github/workflows/ci.yml +39 -0
- package/CHANGELOG.ja.md +39 -0
- package/CHANGELOG.ko.md +39 -0
- package/CHANGELOG.md +135 -0
- package/CHANGELOG.zh.md +39 -0
- package/CONTRIBUTING.md +22 -0
- package/README.ja.md +104 -0
- package/README.ko.md +103 -0
- package/README.md +89 -12
- package/README.zh.md +87 -12
- package/SECURITY.md +18 -0
- package/THIRD_PARTY_NOTICES.md +7 -31
- package/docs/installation.ja.md +76 -0
- package/docs/installation.ko.md +76 -0
- package/docs/installation.md +76 -0
- package/docs/installation.zh.md +76 -0
- package/docs/repair-log.md +582 -0
- package/eslint.config.js +30 -0
- package/package.json +85 -82
- package/packages/selector/LICENSE +21 -0
- package/packages/selector/README.md +26 -0
- package/packages/selector/README.zh.md +26 -0
- package/packages/selector/THIRD_PARTY_NOTICES.md +38 -0
- package/packages/selector/docs/history-tool-call-working-set-spec.md +112 -0
- package/packages/selector/docs/native-tool-result-selector-spec.md +34 -0
- package/packages/selector/docs/subagent-cache-reuse-spec.md +46 -0
- package/packages/selector/lib/style.css +308 -0
- package/packages/selector/package.json +115 -0
- package/{screenshots.json → packages/selector/screenshots.json} +6 -6
- package/packages/selector/src/client/CompressionProfileControls.tsx +229 -0
- package/packages/selector/src/client/CompressionProfileSelector.module.css +170 -0
- package/packages/selector/src/client/CompressionProfileSelector.tsx +79 -0
- package/packages/selector/src/client/CustomPolicyEditor.tsx +216 -0
- package/packages/selector/src/client/EstimatorControls.tsx +281 -0
- package/packages/selector/src/client/decode.ts +49 -0
- package/packages/selector/src/client/index.ts +111 -0
- package/packages/selector/src/client/locales.ts +198 -0
- package/packages/selector/src/client/preset-options.ts +70 -0
- package/packages/selector/src/client/settings-section.tsx +126 -0
- package/packages/selector/src/css-modules.d.ts +6 -0
- package/packages/selector/src/deepseek-v4-tokenizer.ts +210 -0
- package/packages/selector/src/estimator-catalog.ts +104 -0
- package/packages/selector/src/index.ts +327 -0
- package/packages/selector/src/invariant.ts +113 -0
- package/packages/selector/src/preset-overlay.ts +567 -0
- package/packages/selector/src/profiles.ts +342 -0
- package/packages/selector/src/pruner/content.ts +188 -0
- package/packages/selector/src/pruner/session.ts +94 -0
- package/packages/selector/src/pruner/state.ts +43 -0
- package/packages/selector/src/pruner/tuning.ts +23 -0
- package/packages/selector/src/pruner/types.ts +60 -0
- package/packages/selector/src/pruner.ts +2144 -0
- package/packages/selector/src/runtime/adaptive-cost.ts +194 -0
- package/packages/selector/src/runtime/audit.ts +215 -0
- package/packages/selector/src/runtime/config.ts +613 -0
- package/packages/selector/src/runtime/custom-policy.ts +278 -0
- package/packages/selector/src/runtime/deepseek-official-pricing.ts +298 -0
- package/packages/selector/src/runtime/deepseek-v4-vision-tokens.ts +254 -0
- package/packages/selector/src/runtime/measurement.ts +403 -0
- package/packages/selector/src/runtime/reducers.ts +656 -0
- package/packages/selector/src/runtime/retrieve.ts +457 -0
- package/packages/selector/src/runtime/session-events.ts +17 -0
- package/packages/selector/src/runtime/tail-trim.ts +166 -0
- package/packages/selector/src/runtime/token-count.ts +72 -0
- package/packages/selector/src/runtime/tokenpilot/dedup.ts +81 -0
- package/packages/selector/src/runtime/tokenpilot/estimator.ts +183 -0
- package/packages/selector/src/runtime/tokenpilot/locator.ts +128 -0
- package/packages/selector/src/runtime/tokenpilot/read-state.ts +77 -0
- package/packages/selector/src/runtime/types.ts +309 -0
- package/packages/selector/src/runtime/value.ts +48 -0
- package/packages/selector/tests/auto-compact.client.spec.tsx +226 -0
- package/packages/selector/tests/built/client-artifact.spec.ts +51 -0
- package/packages/selector/tests/cache-prefix-audit.spec.ts +123 -0
- package/packages/selector/tests/code-skeleton.client.spec.ts +88 -0
- package/packages/selector/tests/custom-contract.client.spec.ts +202 -0
- package/packages/selector/tests/estimator-catalog.spec.ts +70 -0
- package/packages/selector/tests/estimator-channel.client.spec.tsx +247 -0
- package/packages/selector/tests/estimator-route-registration.host.spec.ts +176 -0
- package/packages/selector/tests/host-preset-overlay.host.spec.ts +204 -0
- package/packages/selector/tests/preset-options-write.client.spec.ts +181 -0
- package/packages/selector/tests/preset-overlay-loader.e2e.host.spec.ts +196 -0
- package/packages/selector/tests/preset-overlay.host.spec.ts +243 -0
- package/packages/selector/tests/profiles.client.spec.tsx +434 -0
- package/packages/selector/tests/public/package-contract.client.spec.ts +33 -0
- package/packages/selector/tests/runtime/adaptive-cost.spec.ts +167 -0
- package/packages/selector/tests/runtime/audit.spec.ts +129 -0
- package/packages/selector/tests/runtime/auto-compact-config.spec.ts +523 -0
- package/packages/selector/tests/runtime/code-skeleton.spec.ts +141 -0
- package/packages/selector/tests/runtime/deepseek-official-pricing.spec.ts +186 -0
- package/packages/selector/tests/runtime/deepseek-v4-tokenizer.spec.ts +122 -0
- package/packages/selector/tests/runtime/deepseek-v4-vision-tokens.spec.ts +122 -0
- package/packages/selector/tests/runtime/fixtures/profile-baseline.json +273 -0
- package/packages/selector/tests/runtime/fixtures/tokenizer-golden.json +106 -0
- package/packages/selector/tests/runtime/fixtures/vision-golden.json +459 -0
- package/packages/selector/tests/runtime/public/public-runtime.spec.ts +2531 -0
- package/packages/selector/tests/runtime/session-events.spec.ts +27 -0
- package/packages/selector/tests/runtime/tokenizer-golden.spec.ts +53 -0
- package/packages/selector/tests/runtime/tokenpilot/dedup.spec.ts +52 -0
- package/packages/selector/tests/runtime/tokenpilot/estimator.spec.ts +56 -0
- package/packages/selector/tests/runtime/tokenpilot/locator.spec.ts +76 -0
- package/packages/selector/tests/runtime/tokenpilot/profile-baseline.spec.ts +100 -0
- package/packages/selector/tests/runtime/tokenpilot/read-state.spec.ts +58 -0
- package/packages/selector/tests/runtime/value.spec.ts +23 -0
- package/packages/selector/tests/standing-generation.host.spec.ts +631 -0
- package/packages/selector/tests/subagent-cache-reuse.host.spec.ts +250 -0
- package/packages/selector/tests/support/cache-prefix-audit.ts +105 -0
- package/packages/selector/tests/support/mock-adapter.ts +37 -0
- package/packages/selector/tests/support/ui-primitives.tsx +34 -0
- package/packages/selector/tsconfig.json +11 -0
- package/packages/selector/tsdown.client.config.ts +102 -0
- package/packages/selector/tsdown.config.ts +20 -0
- package/pnpm-workspace.yaml +19 -0
- package/scripts/capture-profile-baseline.ts +80 -0
- package/scripts/generate-tokenizer-fixtures.py +81 -0
- package/scripts/generate-vision-fixtures.py +208 -0
- package/scripts/packed-components-smoke.ts +713 -0
- package/scripts/packed-install-e2e.ts +1072 -0
- package/scripts/verify-release.ts +300 -0
- package/tests/TEST_INVENTORY.md +42 -0
- package/tsconfig.base.json +18 -0
- package/tsconfig.json +7 -0
- package/tsconfig.scripts.json +13 -0
- package/tsconfig.tests.json +15 -0
- package/vitest.built.config.ts +9 -0
- package/vitest.config.ts +43 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/LICENSE.DeepSeek-V4-Pro.txt +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/manifest.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4/tokenizer_config.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/LICENSE.DeepSeek-V4-Flash-Vision-Exp.txt +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/manifest.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer.json +0 -0
- /package/{assets → packages/selector/assets}/deepseek-v4-vision-exp/tokenizer_config.json +0 -0
- /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-profiles.jpg +0 -0
- /package/{assets → packages/selector/assets}/screenshots/context-compression-selector-settings.png +0 -0
- /package/{cordis.patch.yml → packages/selector/cordis.patch.yml} +0 -0
- /package/{dsh.plugin.json → packages/selector/dsh.plugin.json} +0 -0
- /package/{lib → packages/selector/lib}/client.d.ts +0 -0
- /package/{lib → packages/selector/lib}/client.js +0 -0
- /package/{lib → packages/selector/lib}/config.js +0 -0
- /package/{lib → packages/selector/lib}/index.d.ts +0 -0
- /package/{lib → packages/selector/lib}/index.js +0 -0
- /package/{lib → packages/selector/lib}/invariant.d.ts +0 -0
- /package/{lib → packages/selector/lib}/invariant.js +0 -0
- /package/{lib → packages/selector/lib}/pruner.d.ts +0 -0
- /package/{lib → packages/selector/lib}/pruner.js +0 -0
- /package/{lib → packages/selector/lib}/tail-trim.js +0 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
# Repair log
|
|
2
|
+
|
|
3
|
+
Cross-version ledger of defects that break the **install / boot** path. Every branch and
|
|
4
|
+
version must leave its verdict here (affected / unaffected / fixed) before it ships.
|
|
5
|
+
|
|
6
|
+
Read this file before starting a new line (see `CONTRIBUTING.md`). A defect class already
|
|
7
|
+
recorded here is expected to be *re-checked*, not re-discovered.
|
|
8
|
+
|
|
9
|
+
- One defect, one `D#` id.
|
|
10
|
+
- One entry must contain: symptom / root cause / evidence / affected surface / fix / verification.
|
|
11
|
+
- Evidence means a command and its observed output, not a description of the code.
|
|
12
|
+
|
|
13
|
+
Defects that ship to the **settings surface** rather than the boot path use the `U#` series and
|
|
14
|
+
keep the same six-part shape. They are recorded here because they are reported the same way —
|
|
15
|
+
from a real machine, by someone who cannot tell a gate from a bug.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## D1 — Host plugin entry statically imports a sibling package
|
|
20
|
+
|
|
21
|
+
**Symptom.** `dsh web` aborts while loading the plugin tree:
|
|
22
|
+
|
|
23
|
+
```text
|
|
24
|
+
failed to import loader entry context-compression-improved-bundle
|
|
25
|
+
(dsh-context-compression-improved): Cannot find package
|
|
26
|
+
'dsh-context-compression-improved-runtime' imported from
|
|
27
|
+
<profile>/node_modules/dsh-context-compression-improved/packages/selector/lib/index.js
|
|
28
|
+
code: 'ERR_MODULE_NOT_FOUND'
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Root cause.** The repository was installed whole via a git dependency, so pnpm only
|
|
32
|
+
installs what the **root** manifest declares. The root manifest declared no
|
|
33
|
+
`dependencies`, so the sibling package was never materialized — and
|
|
34
|
+
`packages/selector/lib/index.js:3` uses a top-level static `import` of it, which throws
|
|
35
|
+
during module evaluation.
|
|
36
|
+
|
|
37
|
+
**Cascade.** The Host entry never loads → `settings.register('context-compression', …)`
|
|
38
|
+
never runs → the browser `SettingsScopeSnapshot.writable` stays at its initial `false` →
|
|
39
|
+
every control in the settings panel renders `disabled`. The panel itself is still visible
|
|
40
|
+
because the client bundle is resolved from `dsh.client` metadata and is unaffected by this
|
|
41
|
+
failure.
|
|
42
|
+
|
|
43
|
+
**Evidence (committed artifacts, not a working tree).**
|
|
44
|
+
|
|
45
|
+
| Branch | `packages/selector/lib/index.js` |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| `feat/ctx-preset-v2` @ `455f74f` | `L3: import { … } from "dsh-context-compression-improved-runtime";` — the crash site |
|
|
48
|
+
| `compat/0.1.5` @ `d7c592d` | no top-level import; only `modulePath("…-runtime", import.meta.resolve("…-runtime"))` |
|
|
49
|
+
|
|
50
|
+
**Affected.** `feat/ctx-preset-v2` (the crash itself).
|
|
51
|
+
|
|
52
|
+
**Fix.** Merge into a single package: the entry imports `./runtime/config.ts` from inside
|
|
53
|
+
its own package.
|
|
54
|
+
|
|
55
|
+
**Verification.** `grep` over `lib/**/*.js` must find no
|
|
56
|
+
`from "dsh-context-compression-improved-runtime"`. The frozen provenance literal (D5 note
|
|
57
|
+
below) is the only remaining occurrence and is required.
|
|
58
|
+
|
|
59
|
+
**Status.** Fixed on the merge branch.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## D2 — Root manifest missing the install contract
|
|
64
|
+
|
|
65
|
+
**Symptom.** `dsh plugin add github:<owner>/<repo>` completes, yet the profile gains no
|
|
66
|
+
usable plugin: no bundle row, no resolvable entry.
|
|
67
|
+
|
|
68
|
+
**Root cause.** A git install reads the **root** manifest. Without `main` / `exports` /
|
|
69
|
+
`dsh.client.inject` / `dsh.bundle.patch` there is nothing for the harness to mount; and if
|
|
70
|
+
the root name is still a private workspace name with `"private": true`, pnpm installs a
|
|
71
|
+
private package nothing can load.
|
|
72
|
+
|
|
73
|
+
**Evidence.**
|
|
74
|
+
|
|
75
|
+
| Branch | Root manifest |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `feat/ctx-preset-v2` @ `455f74f` | correct name, has `main`/`exports`/`dsh`; but `dependencies` **empty**, `exports` missing `./pruner` and `./invariant` |
|
|
78
|
+
| `compat/0.1.5` @ `d7c592d` | `dsh-context-compression-improved-workspace`, `private: true`, **no** `main`/`exports`/`dsh`/`dependencies` |
|
|
79
|
+
| `main` / `baseline/pre-god-module-split` @ `e337bf5` | same as compat, plus **zero** `lib/` artifacts committed |
|
|
80
|
+
|
|
81
|
+
**Affected.** `compat/0.1.5`, `baseline/pre-god-module-split`, `main` (fully);
|
|
82
|
+
`feat/ctx-preset-v2` (partially: empty dependencies, incomplete exports).
|
|
83
|
+
|
|
84
|
+
`baseline/pre-god-module-split` is a pre-split baseline, not a distribution: it has no
|
|
85
|
+
install contract and no built artifacts. Do not use it as a rollback target.
|
|
86
|
+
|
|
87
|
+
**Fix.** The root manifest carries the install contract: `name`, `main`, `types`,
|
|
88
|
+
`exports`, `dependencies`, `dsh`. `dependencies` lists the real third-party runtime
|
|
89
|
+
dependencies (`@huggingface/tokenizers`, `js-yaml`).
|
|
90
|
+
|
|
91
|
+
**Verification.** `git show <branch>:package.json` field-by-field; after a real
|
|
92
|
+
`dsh plugin add`, confirm `node_modules/<pkg>` exists and contains `lib/index.js`.
|
|
93
|
+
|
|
94
|
+
**Status.** Fixed on the merge branch for `dependencies` / `./pruner`. `./invariant` was
|
|
95
|
+
added to the root `exports` by the closure commit — the root and the package manifest now
|
|
96
|
+
export the same four subpaths (`.`, `./invariant`, `./pruner`, `./client`). See the
|
|
97
|
+
verification ledger below for the command-level evidence.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## D3 — Build chunks not committed: the same crash under a different filename
|
|
102
|
+
|
|
103
|
+
**Symptom.** After the merge, the built entry starts with:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// packages/selector/lib/index.js:1
|
|
107
|
+
import { … } from "./config.js";
|
|
108
|
+
// packages/selector/lib/invariant.js:1
|
|
109
|
+
import { … } from "./tail-trim.js";
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
tsdown splits a shared chunk out of every entry that reuses a module.
|
|
113
|
+
|
|
114
|
+
**Root cause.** This repository commits `lib/` because pnpm refuses a `prepare` script on a
|
|
115
|
+
git dependency, so consumers can only install prebuilt artifacts. After the merge the new
|
|
116
|
+
artifacts — `lib/config.js`, `lib/tail-trim.js`, `lib/pruner.js`, `lib/pruner.d.ts`,
|
|
117
|
+
`src/deepseek-v4-tokenizer.ts` — were **untracked** while `lib/index.js` already imported
|
|
118
|
+
them. Pushing in that state makes a git install fetch an entry whose chunk does not exist:
|
|
119
|
+
the identical `ERR_MODULE_NOT_FOUND`, only the specifier changes from the sibling package
|
|
120
|
+
to `./config.js`.
|
|
121
|
+
|
|
122
|
+
A git install fetches exactly the **tracked** tree, so `pnpm test:e2e:packed` cannot catch
|
|
123
|
+
this: `npm pack` builds from the working tree and happily includes untracked files.
|
|
124
|
+
|
|
125
|
+
**Evidence.**
|
|
126
|
+
|
|
127
|
+
- `git ls-files packages/selector/lib` listed only the seven pre-merge files.
|
|
128
|
+
- `git status --porcelain` showed the five untracked artifacts above.
|
|
129
|
+
- Before the gate was added, `pnpm verify:release` printed
|
|
130
|
+
`release verification: OK` — the release gate did not cover the artifact graph.
|
|
131
|
+
- Contrast with history, showing this is merge-introduced: on `feat/ctx-preset-v2` and
|
|
132
|
+
`compat/0.1.5`, `packages/runtime/lib/tail-trim.js` is committed, and the selector entry
|
|
133
|
+
is self-contained with no relative imports.
|
|
134
|
+
|
|
135
|
+
**Affected.** Any release built from the merged single-package layout, i.e. every future
|
|
136
|
+
version of this repository. Treat it as a permanent gate, not a one-off fix.
|
|
137
|
+
|
|
138
|
+
**Fix.**
|
|
139
|
+
1. Commit `lib/**` together with the sources, including every chunk and entry.
|
|
140
|
+
2. Gate it: every relative import in a shipped artifact must (a) resolve to an existing
|
|
141
|
+
file, (b) be covered by the package `files` allowlist (packed install), and (c) be
|
|
142
|
+
git-tracked (git install).
|
|
143
|
+
|
|
144
|
+
The gate is scoped to the **import graph**. Unreferenced build by-products such as
|
|
145
|
+
`lib/style.css` are excluded: its rules are already carried inline by `lib/client.js`
|
|
146
|
+
(the `\0dsh-context-compression-css:` region), nothing in the repository reads the file,
|
|
147
|
+
and the `files` allowlist deliberately drops it from the tarball. Requiring it would mean
|
|
148
|
+
widening the allowlist to ship dead weight.
|
|
149
|
+
|
|
150
|
+
**Verification.** `scripts-dist/verify-release.js` (compiled from `scripts/verify-release.ts`). Observed:
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
# untracked chunk present
|
|
154
|
+
Error: release verification: packages/selector/lib/config.js is not committed;
|
|
155
|
+
a git install would fetch an incomplete artifact graph
|
|
156
|
+
|
|
157
|
+
# after git add (the gate reads the index, so staging is enough)
|
|
158
|
+
release verification: OK
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**Status.** Gate implemented and verified red → green. The root-cause fix is the
|
|
162
|
+
`git add`, applied in the merge commit.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## D4 — Consumer profile leftovers
|
|
167
|
+
|
|
168
|
+
**Symptom.** Even with the package fixed, a profile can still install the wrong thing or
|
|
169
|
+
fail to install.
|
|
170
|
+
|
|
171
|
+
**Evidence (`web` profile).**
|
|
172
|
+
|
|
173
|
+
- `package.json` depended on `…-selector-workspace` at
|
|
174
|
+
`github:<owner>/<repo>#baseline/pre-god-module-split` — a spec **without**
|
|
175
|
+
`&path:/packages/selector`, which resolves to the private workspace root and installs
|
|
176
|
+
nothing usable.
|
|
177
|
+
- `pnpm-workspace.yaml` kept an `overrides` entry pointing at the now-deleted
|
|
178
|
+
`packages/runtime` path.
|
|
179
|
+
- `node_modules` held nothing but the useless workspace package.
|
|
180
|
+
|
|
181
|
+
**Affected.** Any profile that installs this plugin.
|
|
182
|
+
|
|
183
|
+
**Fix.** Remove the stale dependency and the dead override; install by the single-package
|
|
184
|
+
spec (no `&path:` needed once the root manifest is the install face).
|
|
185
|
+
|
|
186
|
+
**Correction.** An earlier analysis claimed `allowBuilds` for `@huggingface/tokenizers` was
|
|
187
|
+
a blocker. `@huggingface/tokenizers@0.1.3` has no `install`/`postinstall` script — `dist/`
|
|
188
|
+
ships prebuilt — and the repository's own packed E2E writes a consumer workspace manifest
|
|
189
|
+
of just `packages: []`. Adding the `allowBuilds` entry is precautionary, not required.
|
|
190
|
+
|
|
191
|
+
**Status.** Delivered as a manual script; not executed by the maintainer agent.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## D5 — `compat/0.1.5` predates the install contract
|
|
196
|
+
|
|
197
|
+
**Verdict: it must inherit.** `compat/0.1.5` @ `d7c592d` is four commits behind
|
|
198
|
+
`feat/ctx-preset-v2` (`0b20bf2`, `cd03575`, `4635d19`, `455f74f`), so what it needs is not
|
|
199
|
+
merely the merge but the whole install contract:
|
|
200
|
+
|
|
201
|
+
1. the root manifest contract (D2) — it currently cannot be mounted at all;
|
|
202
|
+
2. the single-package merge (structural fix for D1) — it is still two packages, with
|
|
203
|
+
neither manifest declaring the other as an installable dependency, so a git install
|
|
204
|
+
cannot materialize the runtime package: the same root cause, a different presentation;
|
|
205
|
+
3. the artifact-graph gate (D3) and the profile hygiene (D4).
|
|
206
|
+
|
|
207
|
+
Its entry lacks a top-level cross-package import only because it resolves the runtime
|
|
208
|
+
package lazily through `import.meta.resolve`. That is why it fails as "no bundle to load"
|
|
209
|
+
instead of a load-time throw. Same disease, different symptom — do not read it as
|
|
210
|
+
"unaffected".
|
|
211
|
+
|
|
212
|
+
**Replay, not merge.** `compat/0.1.5` carries 0.1.5-rc.2-only adaptations (client slot
|
|
213
|
+
names, `engines.dsh`, pinned overrides, lazy service resolution, estimator host route,
|
|
214
|
+
`/api` route prefix). A `git merge` would drag the old-line adaptations back, so the merge
|
|
215
|
+
is replayed file by file on that branch instead.
|
|
216
|
+
|
|
217
|
+
**Status.** Not started.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## D6 — Consumer profile blocks dependency reconciliation (host precondition)
|
|
222
|
+
|
|
223
|
+
**Scope note.** This is *not* a defect of this repository. It is recorded here because it
|
|
224
|
+
blocks the real-machine verification step that every branch needs, and because CI is
|
|
225
|
+
structurally blind to it.
|
|
226
|
+
|
|
227
|
+
**Symptom.** `pnpm install` in the `web` profile fails, at a **different path on every run**:
|
|
228
|
+
|
|
229
|
+
```text
|
|
230
|
+
cannot create directory at "...\node_modules\katex\node_modules\commander_pacquet-stage_…":
|
|
231
|
+
拒绝访问。 (os error 5)
|
|
232
|
+
|
|
233
|
+
failed to remove existing directory "...\node_modules\better-sqlite3" prior to swap:
|
|
234
|
+
拒绝访问。 (os error 5)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Progress advances each run (`added 151 → 159 → 205`). The install is not wedged — it is
|
|
238
|
+
walking a list of blockers, one per run.
|
|
239
|
+
|
|
240
|
+
**Root cause — two independent sources of `os error 5`. Do not conflate them.**
|
|
241
|
+
|
|
242
|
+
1. **ACL denies write**, at exactly one directory: `node_modules\katex\node_modules` lists
|
|
243
|
+
only `BUILTIN\Users: ReadAndExecute` and does not name the interactive user at all.
|
|
244
|
+
2. **A mapped native module is held by a live process.** `better-sqlite3` grants the
|
|
245
|
+
interactive user `FullControl` on both itself and its nested `node_modules`, yet deletion
|
|
246
|
+
still fails. `session-query-sqlite` is enabled and `dsh web` is running, so
|
|
247
|
+
`better-sqlite3\build\Release\better_sqlite3.node` is mapped into that process; Windows
|
|
248
|
+
returns `ERROR_ACCESS_DENIED` when asked to delete a mapped image. No ACL change fixes
|
|
249
|
+
this one — only stopping the process does.
|
|
250
|
+
|
|
251
|
+
**Criterion warning — never test this by ownership.** Measured here:
|
|
252
|
+
|
|
253
|
+
| criterion | flagged |
|
|
254
|
+
| --- | --- |
|
|
255
|
+
| owner is not the interactive user | **211** |
|
|
256
|
+
| the interactive user actually lacks write | **0** of 269 package dirs, plus **1** nested |
|
|
257
|
+
|
|
258
|
+
Ownership is `BUILTIN\Administrators` almost everywhere (the profile was evidently produced
|
|
259
|
+
by one elevated install), while the interactive user holds explicit `FullControl` in nearly
|
|
260
|
+
all of them. An ownership-based fix would take ownership of 211 directories to solve one.
|
|
261
|
+
Test the **effective write right** instead.
|
|
262
|
+
|
|
263
|
+
**Affected.** Every real-machine verification on this host, on every branch — including the
|
|
264
|
+
`compat/0.1.5` replay, which cannot be validated anywhere else.
|
|
265
|
+
|
|
266
|
+
**Fix.** Stop `dsh web`; grant write on the one genuinely denied directory; re-run
|
|
267
|
+
`pnpm install --no-frozen-lockfile`; restart. Delivered as
|
|
268
|
+
`DSH-ccp-单包修复-04-node_modules依赖收敛阻塞.ps1` — diagnose / repair / re-verify, dry-run
|
|
269
|
+
capable, and it never stops the service for you.
|
|
270
|
+
|
|
271
|
+
**Verification.** The script's `-DryRun` reports one true target and names the one path.
|
|
272
|
+
|
|
273
|
+
**Status. Resolved** in the maintenance window. The consumer ran a plain `pnpm install` in
|
|
274
|
+
the profile and it completed: `Packages: +256 -16`, `Progress: resolved 256, reused 256`,
|
|
275
|
+
`Done in 11.1s using pnpm v12.4.1`, exit 0.
|
|
276
|
+
|
|
277
|
+
**Resolution evidence (read-only re-check after the install):**
|
|
278
|
+
|
|
279
|
+
| Check | Result |
|
|
280
|
+
| --- | --- |
|
|
281
|
+
| `pnpm-lock.yaml` references `dsh-context-compression-selector-workspace` | **none** — lockfile and `package.json` agree again |
|
|
282
|
+
| `node_modules\dsh-context-compression*` | exactly one directory, `dsh-context-compression-improved` |
|
|
283
|
+
| `*_pacquet-stage_*` residue | none |
|
|
284
|
+
| plugin `packages/selector/lib/**` | all ten artifacts present, `invariant.js` 3927 B — byte-size identical to the source build |
|
|
285
|
+
| `dsh.profile.bundles` row | `dsh-context-compression-improved` still listed |
|
|
286
|
+
|
|
287
|
+
Two things follow for the record. First, the lockfile inconsistency tracked here was a
|
|
288
|
+
**symptom** of the failed installs, not a separate defect: once one install succeeded, it
|
|
289
|
+
cleared itself — no manual lockfile surgery was needed. Second, the blockage cleared **by
|
|
290
|
+
retrying**, without the elevated ACL repair. That does not retire the finding — the `katex`
|
|
291
|
+
ACL hole is still real and will bite the next replacement of that subtree — but it does mean
|
|
292
|
+
the earlier per-run progress (`added 151 → 159 → 205`) was the decisive mechanism: each
|
|
293
|
+
attempt converged further and the last one finished. Record it as *cleared by convergence*,
|
|
294
|
+
not as *fixed by the script*; `-04` remains the tool of record if a future install stalls on
|
|
295
|
+
the same signature.
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## D7 — The estimator catalog route never registered: a detached method lost `this`
|
|
300
|
+
|
|
301
|
+
**Symptom.** The settings card's host-route dropdowns stayed empty. An authenticated
|
|
302
|
+
`GET /api/dsh-context-compression-improved/estimator-catalog` answered 404 while a sibling
|
|
303
|
+
plugin's `/api/dsh-perm-gate/receiver` answered 200 in the same breath.
|
|
304
|
+
|
|
305
|
+
**Root cause.** `asWebServer` duck-typed the `webServer` service by pulling `register` off it
|
|
306
|
+
and returning a fresh wrapper:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
const register = value?.register
|
|
310
|
+
return { register } // `this` is now the wrapper
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
`dsh-host-webserver`'s `register` reads its own route tables:
|
|
314
|
+
|
|
315
|
+
```js
|
|
316
|
+
const table = route.kind === "exact" ? this.exact : this.prefixes;
|
|
317
|
+
if (table.has(route.path)) … // L178 — `table` is undefined, TypeError
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`this` was the wrapper, so `this.exact` / `this.prefixes` were `undefined`, the host threw
|
|
321
|
+
inside `register`, and the plugin's catch-all swallowed it. **The helper had never once
|
|
322
|
+
registered a route since it was written.**
|
|
323
|
+
|
|
324
|
+
**Why it stayed invisible.** On the 0.1.2 host the `dsh web` terminal prints no plugin
|
|
325
|
+
`ctx.logger` output at all — a full boot produced 1350 bytes containing only Node's
|
|
326
|
+
experimental warning and two `dsh web:` lines — and a plugin-load failure travels the same
|
|
327
|
+
logger. A swallowed throw and a plugin that quietly did nothing were observationally
|
|
328
|
+
identical. Once the diagnostic moved to `console` (as `dsh-perm-gate` already does on this
|
|
329
|
+
host), the real frame appeared:
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
TypeError: Cannot read properties of undefined (reading 'has')
|
|
333
|
+
at Object.register (dsh-host-webserver/lib/index.js:178:13)
|
|
334
|
+
at Object.apply (cordis/lib/index.js:120:36)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**Fix.** `return value as WebServerLike` — pass the service itself; `dsh-perm-gate` works
|
|
338
|
+
for exactly this reason.
|
|
339
|
+
|
|
340
|
+
**Verification.** Cordis does **not** bind service methods: on a bare `Context`, both a
|
|
341
|
+
detached call and a wrapped one lose `this`. On the real host, after installing this fix,
|
|
342
|
+
`/api/dsh-context-compression-improved/estimator-catalog` answers **200** with a full
|
|
343
|
+
catalog (four provider groups), `/endpoint/…` answers 200, perm-gate stays 200, and a
|
|
344
|
+
garbage path stays 401.
|
|
345
|
+
|
|
346
|
+
**Guard.** `packages/selector/tests/estimator-route-registration.host.spec.ts` now mounts a
|
|
347
|
+
stand-in whose `register` reads its tables off `this`, mirroring the host. Against the
|
|
348
|
+
previous implementation it fails **3 of 5** cases with `expected [] to deeply equal […]` —
|
|
349
|
+
the same empty route table the host exhibited. The earlier version of that stand-in recorded
|
|
350
|
+
routes in a closure, so it could never have caught this.
|
|
351
|
+
|
|
352
|
+
**Withdrawn hypotheses.** Everything this ledger recorded before the fix about the cause —
|
|
353
|
+
narrowing the injection gate, isolation scope, and moving to the `connection` service — was
|
|
354
|
+
a false trail produced by the silence. Two of them are worth keeping as *non*-causes:
|
|
355
|
+
isolation is opt-in and this row never opted in, and `connection.rpc` is the wrong transport
|
|
356
|
+
here for reasons the project's own `upgrade-pitfalls` §2.1 records independently.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## U1 — The estimator card vanished off TokenPilot-inspired instead of explaining its gate
|
|
361
|
+
|
|
362
|
+
**Symptom.** With any profile other than TokenPilot-inspired selected, the Settings page showed
|
|
363
|
+
no estimator section at all. A reader who had configured nothing could not tell whether the
|
|
364
|
+
feature was missing, broken, or gated — the first real-machine report of Defect A was exactly
|
|
365
|
+
this, and it was filed alongside D7 even though the two have nothing in common.
|
|
366
|
+
|
|
367
|
+
**Root cause.** The render was `current !== 'tokenpilot-inspired' ? null : <EstimatorControls …/>`.
|
|
368
|
+
The condition is right: `runtime/config.ts` merges `presetOptions` over the tokenpilot-inspired
|
|
369
|
+
defaults alone (`mergePresetOptions`), and `resolvePolicy(config, 'balanced').presetOptions` is
|
|
370
|
+
`undefined`, so no other profile can carry an estimator channel. What was wrong is that the gate
|
|
371
|
+
was *rendered as nothing*. The profile card that unlocks the section sits elsewhere on the page,
|
|
372
|
+
and the one element that would have named it was the section being hidden — a gate the reader
|
|
373
|
+
cannot see is indistinguishable from an absent feature.
|
|
374
|
+
|
|
375
|
+
**Fix.** Render `EstimatorInactiveNotice` in place of the controls: same `<section>`, same
|
|
376
|
+
`#context-compression-estimator-title` heading anchor, one paragraph naming the current profile
|
|
377
|
+
and the profile that unlocks the card. Losing the heading was half the defect, so the heading
|
|
378
|
+
stays. **The gate is unchanged** — no estimator control exists off TokenPilot-inspired, and no
|
|
379
|
+
other profile gains `presetOptions`.
|
|
380
|
+
|
|
381
|
+
**Evidence.** `packages/selector/tests/estimator-channel.client.spec.tsx` mounts the section with
|
|
382
|
+
`profile: 'balanced'` and asserts the anchor still reads `Estimator (optional)`, that the notice
|
|
383
|
+
contains the current profile label and `Select TokenPilot-inspired`, and that no channel select,
|
|
384
|
+
no provider input and no `input[list]` exist. Counter-proof: substituting `null` for the notice
|
|
385
|
+
turns that case red (`expected undefined to be 'Estimator (optional)'`) while the other six stay
|
|
386
|
+
green — the guard fails against the pre-fix behaviour, so it is not vacuous.
|
|
387
|
+
|
|
388
|
+
**Affected surface.** Client bundle only: `src/client/CompressionProfileSelector.tsx`,
|
|
389
|
+
`src/client/locales.ts` (both dictionaries; `en` satisfies the full key set), `lib/client.js`,
|
|
390
|
+
`lib/client.d.ts`. No runtime, config, or persistence change, and no change to
|
|
391
|
+
`presetOptions` semantics.
|
|
392
|
+
|
|
393
|
+
**Still open.** The save affordance remains unexplained in the UI — fields commit on change or
|
|
394
|
+
blur with only a transient busy state, so "did that save?" has no answer on screen. That is a
|
|
395
|
+
separate, larger change (explicit save button plus three-state feedback) and is not fixed here.
|
|
396
|
+
|
|
397
|
+
---
|
|
398
|
+
|
|
399
|
+
## Verification ledger — `feat/ctx-preset-v2` closure
|
|
400
|
+
|
|
401
|
+
Closure = `935d501` + the root-`exports` completion (`./invariant`). The working tree held
|
|
402
|
+
exactly that one manifest change; `lib/**` rebuilt byte-identically, so the build is
|
|
403
|
+
deterministic.
|
|
404
|
+
|
|
405
|
+
### Ten gates
|
|
406
|
+
|
|
407
|
+
| # | Command | Result |
|
|
408
|
+
| --- | --- | --- |
|
|
409
|
+
| 1 | `pnpm install` | exit 0 |
|
|
410
|
+
| 2 | `pnpm build` | exit 0 |
|
|
411
|
+
| 3 | `pnpm typecheck` | exit 0 |
|
|
412
|
+
| 4 | `pnpm lint` | exit 0 |
|
|
413
|
+
| 5 | `pnpm test` | exit 1 — nondeterministic Windows set, see below |
|
|
414
|
+
| 6 | `pnpm test:built` | exit 0 |
|
|
415
|
+
| 7 | `pnpm verify:release` | exit 0 |
|
|
416
|
+
| 8 | `pnpm pack:dry-run` | exit 0 |
|
|
417
|
+
| 9 | `pnpm test:e2e:packed` (`DSH_E2E_MODE=dev`) | exit 0 |
|
|
418
|
+
| 10 | `pnpm test:e2e:packed` (release mode) | exit 0 — upgrade leg skipped until the first publish, see below |
|
|
419
|
+
|
|
420
|
+
### Gate 5 — the failure set is nondeterministic; do not cite it as "4 known failures"
|
|
421
|
+
|
|
422
|
+
A single `pnpm test` run is not a baseline. Eight interleaved runs (four at `455f74f`,
|
|
423
|
+
four at the closure commit), same machine, nothing else running:
|
|
424
|
+
|
|
425
|
+
| Run | `455f74f` failed | closure failed |
|
|
426
|
+
| --- | --- | --- |
|
|
427
|
+
| 1 | 3 | 5 |
|
|
428
|
+
| 2 | 3 | 2 |
|
|
429
|
+
| 3 | 4 | 3 |
|
|
430
|
+
| 4 | 3 | 4 |
|
|
431
|
+
|
|
432
|
+
A ninth run, taken while a packed-install E2E ran concurrently, reported **9** failures at
|
|
433
|
+
`455f74f` — the same commit that reported 3 in three other runs. The count tracks machine
|
|
434
|
+
load, not code.
|
|
435
|
+
|
|
436
|
+
Per test name, over the four quiet runs on each side:
|
|
437
|
+
|
|
438
|
+
| Test | `455f74f` | closure |
|
|
439
|
+
| --- | --- | --- |
|
|
440
|
+
| `preset-overlay.host.spec.ts` > uses owner-only files and starts a new generation after source content changes | 4/4 | 4/4 |
|
|
441
|
+
| `standing-generation.host.spec.ts` > keeps one fully-identical generation under concurrent composition of the same identity | 3/4 | 3/4 |
|
|
442
|
+
| `standing-generation.host.spec.ts` > keeps one generation under concurrent and repeated composition of the same identity | 3/4 | 3/4 |
|
|
443
|
+
| `standing-generation.host.spec.ts` > separates colliding equal-size generations on a whole-second metadata surface before publish | 3/4 | 3/4 |
|
|
444
|
+
| `standing-generation.host.spec.ts` > switches the standing generation for an equal-length source change at a fixed threshold | 0/4 | 1/4 |
|
|
445
|
+
|
|
446
|
+
Four of the five fail on both sides at identical frequency: pre-existing. The fifth failed
|
|
447
|
+
once in four runs on the closure side and never on the baseline side, inside the same
|
|
448
|
+
timing-sensitive `describe` block as three tests that are 3/4 flaky on *both* sides. It is
|
|
449
|
+
recorded as **not excluded**, not as clean.
|
|
450
|
+
|
|
451
|
+
The visible causes are Windows-only: an owner-only permission assertion (`0o700` against
|
|
452
|
+
`0o666`), `fs.rename` `EPERM` during publish under concurrency, and mtime-window
|
|
453
|
+
assertions that assume a coarser clock.
|
|
454
|
+
|
|
455
|
+
**Consequence for review.** Any criterion phrased as "the failure set matches the baseline
|
|
456
|
+
item by item" is unsatisfiable — the baseline has no single failure set. Compare
|
|
457
|
+
*ever-failed* sets over at least three interleaved runs per side instead.
|
|
458
|
+
|
|
459
|
+
### Gate 10 — a defect in this repository, not an external precondition
|
|
460
|
+
|
|
461
|
+
Release mode builds the upgrade leg of its fixture from the *published* previous release.
|
|
462
|
+
The fixture named that predecessor as the pinned literal `0.1.0-beta.2`, a version that only
|
|
463
|
+
ever existed under this package's pre-rename name: it 404s under the published name and
|
|
464
|
+
appears nowhere in the repository's 12-revision history. Every release-mode run therefore
|
|
465
|
+
aborted with `release gate requires the published previous release for the upgrade leg:
|
|
466
|
+
packument responded 404`.
|
|
467
|
+
|
|
468
|
+
This was recorded here as an external precondition. That was wrong. The literal was a
|
|
469
|
+
fixture defect — exactly the hardcoded-version class this repository's own doctrine names —
|
|
470
|
+
and it is fixed: the predecessor is now whatever the registry actually offers, resolved by
|
|
471
|
+
publication order from `packument.time` (not semver order, which avoids a semver dependency
|
|
472
|
+
and handles prereleases), and `predecessorFound` keys both release-mode gates instead of a
|
|
473
|
+
hardcoded version.
|
|
474
|
+
|
|
475
|
+
| Registry state | `upgradeLeg` | Release outcome |
|
|
476
|
+
| --- | --- | --- |
|
|
477
|
+
| package unpublished (404) | `skipped-package-not-published` | pass, skip recorded |
|
|
478
|
+
| published, no other version | `skipped-no-earlier-release` | pass, skip recorded |
|
|
479
|
+
| published, earlier version exists | `installed` | pass only when the leg installed |
|
|
480
|
+
| any other fetch or tarball failure | — | throws |
|
|
481
|
+
|
|
482
|
+
Only the third row runs the leg. Where a predecessor exists the gate stays fail-closed: a
|
|
483
|
+
leg reporting anything other than `installed` throws. Where none exists the skip is never
|
|
484
|
+
silent — it is emitted as `UPGRADE_LEG_SKIPPED <reason>` in the report, and
|
|
485
|
+
`officialCloneSmoke` falls back to that marker rather than passing vacuous on a package that
|
|
486
|
+
has never shipped.
|
|
487
|
+
|
|
488
|
+
This is a one-time bridge. Once the first version is published, `predecessorFound` is true
|
|
489
|
+
on every subsequent run and the upgrade leg is a hard requirement again — which is the
|
|
490
|
+
state the gate was always meant to enforce.
|
|
491
|
+
|
|
492
|
+
### Criterion withdrawn
|
|
493
|
+
|
|
494
|
+
The earlier merge plan carried "the overlay identity hash is unchanged across the merge".
|
|
495
|
+
That is unsatisfiable by construction: identity is `sha256(preset.id ‖ source ‖
|
|
496
|
+
JSON.stringify({modules, autoCompactThresholdPercent}))` computed over **absolute** module
|
|
497
|
+
paths, so collapsing two packages into one necessarily changes it. The replacement is
|
|
498
|
+
structural — `canonicalCompressionRows` stays byte-identical and the only permitted
|
|
499
|
+
difference in the generated YAML is the `tool-result-pruner` row's `name` resolving to the
|
|
500
|
+
merged package.
|
|
501
|
+
|
|
502
|
+
### `invariant.ts` literal decision
|
|
503
|
+
|
|
504
|
+
The merged package keeps **one** invariant companion, and it is the runtime's: `name =
|
|
505
|
+
'context-compression-selector-runtime-invariant'`, `PACKAGE_NAME =
|
|
506
|
+
'dsh-context-compression-improved-runtime'`. Both stay verbatim.
|
|
507
|
+
|
|
508
|
+
- The string is the frozen provenance literal (inheritance rule 4) and is written into
|
|
509
|
+
durable session logs, so renaming it would discard historical tail-trim entries.
|
|
510
|
+
- The companion's real checks (`validatePublishedTailTrim`, `sessionEvents`) belong to the
|
|
511
|
+
runtime, so the runtime identity is the semantically correct one.
|
|
512
|
+
- The pre-merge `packages/selector/src/invariant.ts` companion
|
|
513
|
+
(`client-ui-context-compression-selector-invariant`, `PACKAGE_NAME =
|
|
514
|
+
'dsh-context-compression-improved'`) installed **no** checks (`install = () => {}`) and
|
|
515
|
+
was dropped by the merge. Nothing is lost: its only effect was reserving a name in the
|
|
516
|
+
invariant registry, which is keyed by the installing package either way.
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## Affected-surface matrix
|
|
521
|
+
|
|
522
|
+
| Branch / version | D1 entry import | D2 install contract | D3 artifact chunks | D4 profile | Verdict |
|
|
523
|
+
| --- | --- | --- | --- | --- | --- |
|
|
524
|
+
| `feat/ctx-preset-v2` @ `455f74f` | **present (crash)** | partial | n/a before merge | needed | affected; D1/D2 fixed on merge branch |
|
|
525
|
+
| merged single package | eliminated | fixed (all four subpaths) | **gated** | delivered | this change |
|
|
526
|
+
| `compat/0.1.5` @ `d7c592d` | no (lazy) | **absent** | n/a | needed | **inherits the whole contract** |
|
|
527
|
+
| `baseline/pre-god-module-split`, `main` @ `e337bf5` | no | **absent, no `lib/`** | n/a | needed | not distributable by design |
|
|
528
|
+
| any future single-package release | structurally impossible | — | **permanent gate** | — | inherits D3 |
|
|
529
|
+
| every branch on this host | — | — | — | — | was **blocked by D6**; the profile install now completes, so real-machine verification is unblocked |
|
|
530
|
+
|
|
531
|
+
### D7 per branch — the fix must travel one way only
|
|
532
|
+
|
|
533
|
+
`asWebServer` has two different bodies across the branches, and they are not equivalent:
|
|
534
|
+
|
|
535
|
+
| Branch | `asWebServer` returns | Verdict |
|
|
536
|
+
| --- | --- | --- |
|
|
537
|
+
| `compat/0.1.5` @ `d7c592d` | `value as WebServerLike` — the service itself | **correct; never had D7** |
|
|
538
|
+
| `feat/ctx-preset-v2` @ `e588f1c`, `ts/0.1.2+` @ `0eb5183` | `{ register }` — a detached method | **carries D7** |
|
|
539
|
+
| `baseline/pre-god-module-split`, `main` @ `e337bf5` | no such helper | n/a |
|
|
540
|
+
|
|
541
|
+
The defect was introduced on the V2 line, not inherited from the 0.1.x line. That inverts the
|
|
542
|
+
usual direction of these hand-offs: **the 0.1.5 replay must not copy this helper out of
|
|
543
|
+
`feat/ctx-preset-v2`.** Take `compat`'s body, or the fixed one from `00afcfc`; they agree.
|
|
544
|
+
|
|
545
|
+
## Inheritance rules
|
|
546
|
+
|
|
547
|
+
1. **The root manifest is the install contract**: `name`, `main`, `types`, `exports`,
|
|
548
|
+
`dependencies`, `dsh` — all six. A new subpath export is declared in both the root and
|
|
549
|
+
the package manifest.
|
|
550
|
+
2. **No top-level cross-package import inside one package.** `lib/**/*.js` may only
|
|
551
|
+
reference the frozen provenance literal.
|
|
552
|
+
3. **`lib/**` ships in the same commit as its sources**, and every chunk an entry imports
|
|
553
|
+
must be committed. Enforced by `pnpm verify:release`.
|
|
554
|
+
4. **The provenance literal `dsh-context-compression-improved-runtime` is frozen.** It is
|
|
555
|
+
already written into durable session logs (`source.plugin` on tail-trim manifests) and
|
|
556
|
+
validated on read, so renaming it silently discards historical tail-trim entries. This
|
|
557
|
+
covers every occurrence, including `src/invariant.ts`'s `PACKAGE_NAME` and companion
|
|
558
|
+
`name` — the merge does **not** rename them to match the merged package name.
|
|
559
|
+
5. **Profile-side hygiene**: install spec that actually resolves, no dead `overrides`. And
|
|
560
|
+
before trusting any profile-side install result: reconcile dependencies **with the host
|
|
561
|
+
stopped**, and test a directory's write right — never its owner (D6).
|
|
562
|
+
6. **Never detach a method off a host service.** Duck typing that returns `{ register }`
|
|
563
|
+
instead of the service loses `this`, and `dsh-host-webserver.register` reads its route
|
|
564
|
+
tables off `this`. Pass the service object itself; `dsh-perm-gate` does, which is why it
|
|
565
|
+
serves its routes. The same trap applies to any service whose methods touch instance
|
|
566
|
+
state, so check the contract before narrowing a service to a single method (D7).
|
|
567
|
+
7. **A conditional the user cannot see is a defect, not a design.** If a section renders only
|
|
568
|
+
under some profile, mode, or capability, the hidden branch must say what is missing and what
|
|
569
|
+
would restore it — and keep its heading and anchor id so the panel is still findable where
|
|
570
|
+
the reader last saw it. Gating *semantics* are not what is on trial here; hiding the *reason*
|
|
571
|
+
is (U1).
|
|
572
|
+
|
|
573
|
+
## Change log
|
|
574
|
+
|
|
575
|
+
| Date | Entry | Action |
|
|
576
|
+
| --- | --- | --- |
|
|
577
|
+
| 2026-09-14 | D1–D5 | Ledger created; D1 fixed; D2 fixed for `dependencies`/`./pruner`; D3 gate added and verified; D4 delivered as a script; D5 recorded for `compat/0.1.5` |
|
|
578
|
+
| 2026-09-14 | D2 | `./invariant` added to the root `exports`; root and package manifests now agree. Ten-gate closure ledger added, with the gate-5 flakiness evidence and the withdrawn identity-hash criterion |
|
|
579
|
+
| 2026-09-14 | D6 | Profile dependency-reconciliation blocker diagnosed: two independent `os error 5` sources (one ACL-denied directory; mapped native modules held by the live host), plus the ownership-vs-write-right criterion warning. Delivered as a dry-runnable script |
|
|
580
|
+
| 2026-09-15 | D6 | **Resolved.** One plain `pnpm install` in the profile converged (`+256 -16`, exit 0); the lockfile repointed itself and the `_pacquet-stage_` residue is gone. Cleared by convergence over successive attempts, not by the ACL repair — the `katex` denial stays on record |
|
|
581
|
+
| 2026-09-15 | D7 | **The estimator catalog route had never registered at all.** `asWebServer` detached `register` from the service, `this` became the wrapper, the host threw inside `register`, and a catch-all swallowed it. Fixed by passing the service itself; 200 verified on the real host; the injection, isolation and transport hypotheses recorded earlier are withdrawn |
|
|
582
|
+
| 2026-09-15 | U1 | **The estimator card was hidden by its own gate.** Off TokenPilot-inspired the section rendered `null`, so the reader saw a missing feature rather than a gated one. The heading and anchor are now kept and the hidden branch names the profile that unlocks the card; the gate itself is unchanged. Guard added with a counter-proof; the save-affordance question stays open |
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import js from '@eslint/js'
|
|
2
|
+
import globals from 'globals'
|
|
3
|
+
import tseslint from 'typescript-eslint'
|
|
4
|
+
|
|
5
|
+
export default tseslint.config(
|
|
6
|
+
{
|
|
7
|
+
ignores: ['**/node_modules/**', '**/lib/**', '**/dist/**', '**/scripts-dist/**', '**/coverage/**', 'docs/**'],
|
|
8
|
+
},
|
|
9
|
+
js.configs.recommended,
|
|
10
|
+
...tseslint.configs.recommended,
|
|
11
|
+
{
|
|
12
|
+
files: ['packages/**/*.{ts,tsx}', 'scripts/**/*.{js,mjs,ts}', 'eslint.config.js'],
|
|
13
|
+
languageOptions: {
|
|
14
|
+
globals: { ...globals.browser, ...globals.node },
|
|
15
|
+
},
|
|
16
|
+
rules: {
|
|
17
|
+
'@typescript-eslint/no-unused-vars': ['error', {
|
|
18
|
+
argsIgnorePattern: '^_',
|
|
19
|
+
varsIgnorePattern: '^_',
|
|
20
|
+
caughtErrorsIgnorePattern: '^_',
|
|
21
|
+
destructuredArrayIgnorePattern: '^_',
|
|
22
|
+
}],
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
// Terminal normalization intentionally matches ANSI/BEL control characters.
|
|
27
|
+
files: ['packages/selector/src/runtime/reducers.ts'],
|
|
28
|
+
rules: { 'no-control-regex': 'off' },
|
|
29
|
+
},
|
|
30
|
+
)
|