PLUGIN DETAIL ROLE: PINNER GATE: package.json

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.

CLI INSTALL COPIED
$ pnpm add @playform/hook-dsh-pinner-package
Namespace: @playform/hook-dsh-pinner-package Release: v0.0.1
Archetype: Hook Event: fs/observed Injects: fs, pluginFactory

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: A GOVERNANCE HOOK

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

WHERE THE PINNER OPERATES
SeamWhat the plugin does thereWhat you can observe
fs/observed - the file observation eventThe 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 sidecarResolvePolicy 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 / sessionA 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

WHY THE PINNER EXISTS

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 PIPELINE P = REWRITE ∘ PIN ∘ RESOLVE ∘ DECODE
fs/observed (target, {kind:"present", version}, actor) ── fired by the TOOL LAYER ONLY, for every harness write/edit, any thread ──► │ ▼ G1 Factory.Gate ── displayPath → actor ∈ mutationTools → kind "present" → │ Stash idempotence → basename "package.json" → excluded? │ excluded ──► ledger `skipped (excluded) <path>` (every other gate ▼ outcome is silent) outcome proceeds) G2 Factory.Discover ── nearest registry.json walk-up (an exempt plain-fs │ probe - it exists ONLY as a location for the co-located │ pin-policy.json); Stash seed (targetKey, version) ▼ g3 PIN PASS (detached, contained - Factory.Continue; the listener never │ awaits) ── ctx.fs.readText → union keep-list (ResolvePolicy: global │ policyFile → the file's own directory → registry-adjacent → built-in │ default) → Decode (JSON.parse, null on failure) → the RANGE LAW │ (the range law - ONE leading ^/~/= stripped per dep) → the REFUSAL │ GUARD (core's Refusal) → GuardedWrite (replaceIfVersion + the P4 │ fence) → ledger `pinned <path> (<N> versions)` → Refresh (P3 │ re-emit, same actor) ▼ SILENCE: the tool result shows exactly what the author wrote. The pinned state is discoverable only by a subsequent read - or in the ledger.

The range law (the transform, exactly) - for each dependency value (after the keep-list gate), in order:

#ConditionActionExamples
1not a stringuntouched"foo": {"imports": ...}
2first char not in {^, ~, =}untouched"0.3.4", "1.2.3-rc.1", "*", "", ">=1.0.0", "workspace:*", "file:../x", "link:...", "git+https://...", "npm:[email protected]"
3whitespace in valueuntouched"^1.0.0 || 2.0.0", "^1.0.0 <2.0.0"
4otherwisestrip 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

SCHEMA + DEFAULTS AT LOAD

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:

- insert: - id: hook-dsh-pinner-package name: "@playform/hook-dsh-pinner-package" config: log: true logFile: ~/.dsh/hook-dsh-pinner-package.log # SEPARATE ledger (own plugin) mutationTools: [write, edit, str_replace_editor] policyFile: "" # optional global pin-policy.json (else discovery, else built-in) sections: [dependencies, devDependencies, peerDependencies, optionalDependencies] exclude: [node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app]

In Action

ONE PINNED WRITE

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

{ "name": "@acme/tool", "scripts": { "build": "tsc" }, "dependencies": { "@playform/build": "^0.3.4", "tailwindcss": "^3.0.5", "@acme/core": "~1.2.3" } }

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

[2026-10-03T09:15:22.411Z] activated (pinner, logFile=~/.dsh/hook-dsh-pinner-package.log, sections=[dependencies, devDependencies, peerDependencies, optionalDependencies], exclude=[node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app]) [2026-10-03T09:16:01.880Z] pinned <project>/acme/tool/package.json (2 versions)

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

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):

activated (pinner, logFile=..., sections=[...], exclude=[...]) skipped (excluded) <path> observed non-JSON package.json <path> pinned <path> (N versions) no changes to pin for <path> REFUSED rewrite of <path>: non-dependency section "<name>" would change

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

12 TOTAL

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.

The sibling governor on the same seam: pin → bump-exact, since a fully pinned manifest leaves the governor's update stage nothing to do.

hook-dsh-core DSH FAMILY

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.