The family's furnace: the gate, the discovery, the union keep-list, the guarded write, the refresh, the continuation, the effects and the schema come from it.
hook-dsh-pinner-package
Hook @ DSH @ Pinner @ Package • _The DeepSeek Harness Plugin Family for PlayForm._ The silent version pinner of the DeepSeek Harness - a plugin on the fs/observed event that pins every dependency version in a written package.json to its STATIC version: "@playform/build": "^0.3.4" becomes "0.3.4". One leading range prefix (^,~, or =) is stripped, in every dependency section the config declares - and the author never learns. A factory flavor: pure P-only - no ncu, no jobs, no subprocess, no cooldown. Just the pin.
$ pnpm add @playform/hook-dsh-pinner-package The profile wiring for this plugin - the bundles list, the patch entry and the restart - is on the setup page.
Where It Fits
Family position (the @-sentence Hook @ DSH @ Pinner @ Package): a hook child of the plugin-dsh-factory service and the hook-dsh-core helpers; a sibling of the package governor on the same fs/observed seam. Composition semantics: pin → bump-exact (the pinner pins ^0.3.4 → 0.3.4; a fully pinned manifest then leaves the governor's update stage nothing to do, while its chain pass may still re-canonicalize chain pins), keep-list wins (the pinner's pin-policy.json keeps ranges as authored). The ledgers are separate (pinner.log vs governor.log); activating one never implies another.
Machinery-wise it is a factory flavor: inject: ["fs", "pluginFactory"] - the gate, the discovery, the union keep-list, the guarded write, the refresh, the continuation, the effects and the schema come from plugin-dsh-factory; the refusal guard and the suppression composer come from hook-dsh-core. This bundle keeps its own logic: the range law, the decode, and every ledger string. Besides the fs/observed event path, its chain pass is registered with the factory's direct-govern registry at apply (Factory.RegisterGovern("package.json", "pin", ...)), so the raw-write tool's per-call govern selection can drive the same pin pass directly through Factory.Govern.
In the DeepSeek Harness
| Seam | What the plugin does there | What you can observe |
|---|---|---|
| fs/observed - the file observation event | The pinner's one listener hook: the same event the governor reacts to, fired by the tool layer only for every harness write/edit - so the pin happens after content is on disk, on every write path. | Static versions become the default outcome of every write, invisibly. |
| ctx.fs - the filesystem service (dsh-fs) | The pin pass reads the file via ctx.fs.readText and writes via the factory's guarded write (replaceIfVersion + the P4 sandbox fence), with an explicit sandbox policy because the plugin's own exclude list IS its fence. | No clobber, no recursion; the re-emit re-enters as a no-op. |
| The keep-list policy sidecar | ResolvePolicy builds the union keep-list from every readable pin-policy.json: global policyFile → the file's own directory → registry-adjacent; the P3 chain-keys interlock protects the governor's chain pins from ever being stripped. | Ranges the author marked as intentional stay exactly as written. |
| The ledger / session | A separate ledger (pinner.log) records activation, exclusions, non-JSON skips, refusals and every pin result; the activation line is written by apply(). | "Did it activate" is answerable from the ledger alone - and never implies the governor's ledger. |
The Problem
Ranged versions make builds drift: "^0.3.4" today is "0.3.5" next week. Teams that want reproducible installs want static versions - but writing them by hand (or remembering to) is exactly the kind of discipline an agent workflow loses. The pinner makes static versions the default outcome of every write, invisibly.
How It Works
The range law (the transform, exactly) - for each dependency value (after the keep-list gate), in order:
| # | Condition | Action | Examples |
|---|---|---|---|
| 1 | not a string | untouched | "foo": {"imports": ...} |
| 2 | first char not in {^, ~, =} | untouched | "0.3.4", "1.2.3-rc.1", "*", "", ">=1.0.0", "workspace:*", "file:../x", "link:...", "git+https://...", "npm:[email protected]" |
| 3 | whitespace in value | untouched | "^1.0.0 || 2.0.0", "^1.0.0 <2.0.0" |
| 4 | otherwise | strip the one leading prefix | "^0.3.4" → "0.3.4", "~1.2.3" → "1.2.3", "=2.0.0" → "2.0.0" |
Idempotent: an already-static version has no leading prefix, so rule 2 passes it through - pinning a pinned file is a no-op rewrite; P(P(c)) = P(c). Keep-list policy: a pin-policy.json ( {"keep": ["tailwindcss"]}) protects packages whose ranges must stay EXACTLY as the author wrote them; the effective keep-list is the union of every readable discovered policy, and the P3 chain-keys interlock means the governor's ^resolved chain pins are never stripped. A file that exists but does not parse is logged and contributes nothing. Documented edge decision: "^1.*" IS stripped to "1.*" - the law strips the notation (the prefix) and leaves the content untouched; protect wildcards with the keep-list if you need them preserved. Refusal guard: non-dependency sections (name, version, description, scripts, ...) are NEVER touched - if the pinner's own logic would ever change one, the rewrite is REFUSED and logged instead of applied. Version coherence: the rewrite carries replaceIfVersion at the observed version; a racing author write makes it fail safely (no clobber), and the re-emit re-enters this listener where the Stash token gate makes it a no-op.
The Config
The exported Config schema (the factory's Schema helper extended with sections) validates and fills every default at load - invalid configuration fails loudly. New patch entries are declared with insert:. Full example:
In Action
One pinned write. The author drops a package.json with two ranged versions and a keep-listed package nearby:
Before - the manifest as the author wrote it
A pin-policy.json ({"keep":["tailwindcss"]}) sits beside the file, so tailwindcss keeps its range; the other two dependencies are pinned. The transcript shows only what the author wrote; the ledger shows the pin:
After - the pin in the ledger
Read the file back and "@playform/build" is "0.3.4", "@acme/core" is "1.2.3", "tailwindcss" is still "^3.0.5", and "scripts" is byte-identical. Write the pinned file again and the pass is a no-op with its own line: no changes to pin for .... Write inside node_modules/.git/.dsh and the ledger gains skipped (excluded) ... while the file stays untouched. (A non-JSON package.json - or a refusal, if a non-dependency section would change - gets its own line, e.g. observed non-JSON package.json ... - skipped, before the silence.)
The Ledger
One global log (logFile, default ~/.dsh/hook-dsh-pinner-package.log - SEPARATE from the governor's ledger) records activation, exclusions, non-JSON skips, refusals, and every pin result. The strings this module composes (the hook-dsh-pinner-package: logger prefix is the factory's Append; each line is [<ISO>] <message> in the file):
The non-JSON line ends with an em dash followed by skipped - part of the literal string, so its byte-exact form appears only in the In Action excerpt above. The activation line is written by apply() - "did it activate" must be answerable from the ledger alone.
Related plugins
The sibling governor on the same seam: pin → bump-exact, since a fully pinned manifest leaves the governor's update stage nothing to do.
Supplies the refusal guard (Refusal) and the suppression composer (Suppress) this bundle composes its lines with.
License: MIT. Full contract: SCHEME.md. TypeScript-first, built with @playform build (ESBuild + tsc type-check); the published artifact contains only the built output.