PLUGIN DETAIL ROLE: GOVERNOR GATE: package.json

hook-dsh-governor-package

Hook @ DSH @ Governor @ Package • _The DeepSeek Harness Plugin Family for PlayForm._ The silent package.json governor of the DeepSeek Harness - a plugin on the fs/observed event (the cordis event every harness file write dispatches; the tool layer is the only dispatcher) that governs every package.json written by any agent, anywhere: a pure chain pass canonicalizes chain-governed dependency pins, then an update stage lets npm-check-updates bump the public ones - and the author never learns. A factory flavor: the family's furnace does the plumbing; this package is the model logic.

CLI INSTALL COPIED
$ pnpm add @playform/hook-dsh-governor-package
Namespace: @playform/hook-dsh-governor-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 @ Governor @ Package): a hook child of the plugin-dsh-factory service and the hook-dsh-core helpers; it has no hook children of its own. One of three governance hooks sharing the fs/observed seam, each gating on its own basename and writing its own ledger:

PluginBasename gateLedgerPass
hook-dsh-governor-package (this bundle)package.jsongovernor.logchain pass (→ ^resolved) + update stage (ncu)
hook-dsh-pinner-packagepackage.jsonpinner.logpin pass (^0.3.4 → 0.3.4; keep-list wins)
hook-dsh-governor-cargoCargo.tomlcargo-governor.logchain pass (bare caret / =exact) + cargo upgrade

Composition semantics when several are activated: pin → bump-exact (a fully pinned manifest leaves ncu nothing to do; the chain pass may still re-canonicalize chain pins), chain > strip > normalize (the cargo module's precedence), keep-list wins (the pinner's pin-policy.json keeps ranges as authored). The ledgers are separate; activating one never implies another. Machinery-wise it is a factory flavor: inject: ["fs", "pluginFactory"] - the gates, the discovery, the guarded write, the refresh, the continuation, the effects and the schema come from plugin-dsh-factory; the pure helpers (Suppress, the policy loader) come from hook-dsh-core. This bundle keeps only its own vocabulary: the Config extension, the chain pass, the update engine and every ledger string. Besides the fs/observed event path, its two steps are registered with the factory's direct-govern registry at apply (Factory.RegisterGovern("package.json", "canonicalize" | "update", ...)), so the raw-write tool's per-call govern selection can drive the same chain pass and update stage directly through Factory.Govern.

In the DeepSeek Harness

WHERE THE GOVERNOR OPERATES
SeamWhat the plugin does thereWhat you can observe
fs/observed - the file observation eventThe governor's one listener hook: fs/observed is the cordis event every harness file write dispatches, fired by the tool layer only - so it runs after content is on disk, on every write path (full writes, single-line edits, str_replace_editor patches alike).The fix happens where the write happens, without the author's tool result changing by a single byte.
ctx.fs - the filesystem service (dsh-fs)All rewrites go through the ctx.fs service (which dispatches no fs/* events - only the tool layer does), carrying replaceIfVersion at the observed version and an explicit sandbox policy because the plugin's own exclude list IS its fence.No recursion, no clobber; a racing author write fails safely.
ctx.jobs - the jobs facilityThe update stage runs inside the jobs envelope (kind governor-update, unowned) when a controller serves the context - the factory's Attach registers it from the root - with the detached contained continuation as the probed fallback.ncu runs off the author's critical path and never blocks the session.
ctx.subprocess (dsh-subprocess, bin mode)In updateMode bin, the ncu binary is driven through the subprocess seam with a fully-specified argv (ncuBin is absolute - host PATH != shell PATH).The external-tool path without shell PATH drift.
The ledger / sessionOne global log records activation, exclusions, chain-pass results and every update-stage dispatch; the activation line is written by apply() so "did it activate" is answerable from the ledger alone.The governed state is discoverable only by a subsequent read - or in the ledger.

The Problem

WHY THE GOVERNOR EXISTS

Agents edit package.json files constantly - and every edit can leave stale pins, stray version ranges, or deps that should track a governed registry. The fix must happen where the write happens, on every write path (full writes, single-line edits, str_replace_editor patches alike), without the author's tool result changing by a single byte. fs/observed is the only hook that runs after content is on disk - the fs/write-intent waterfall carries a version guard but never the content.

How It Works

THE PIPELINE G = U ∘ P
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 + Factory.Parse ── nearest registry.json walk-up │ (none / unreadable ──► ledger line, the chain pass is skipped) ▼ Stash seed (targetKey, version) ── our own re-emits re-enter as no-ops │ ▼ detached, contained (Factory.Continue - the listener never awaits) G3 CHAIN PASS (the pure transform) ── read → chain-governed pins │ (dep ∈ registry.effectiveLatest) canonicalized to ^<resolved> │ (strict: strip unknown - explicit only, never a default) → │ GuardedWrite (replaceIfVersion + the P4 fence) → ledger │ `governed <path> → <version>` → Refresh (P3 re-emit, same actor) │ (the factory's Continue also computes the union keep-list with the │ P3 chain keys - deliberately ignored by this transform) ▼ chained off the settled continuation G4 UPDATE STAGE ── passage gate → Filter (nothing public? skip) → │ first-wins update-policy pick → Dispatch: │ breaker → in-flight → cooldown → the jobs envelope │ (kind "governor-update", unowned) or the detached fallback │ ├─ updateMode "programmatic": ncu.run({packageFile, upgrade, │ │ silent, dep, concurrency, target, reject ∪ chain deps, │ │ allow, filter}) - no external binary │ └─ updateMode "bin": the ncu binary (ncuBin) via ctx.subprocess │ → Verify (verifyCommand, exit 127 non-fatal) → Settle → │ Refresh (U2: re-stat + re-emit the fresh version) ▼ SILENCE: the tool result shows exactly what the author wrote. The governed state is discoverable only by a subsequent read - or in the ledger.

Key properties, all enforced by construction. Anywhere mode, exclusion-first - no roots allowlist: any agent (main, subagent, workflow child) writing a package.json via the harness fs tools is governed, unless the path contains an excluded segment. No recursion - the governor's rewrites go through the ctx.fs service (which dispatches no fs/* events - only the tool layer does) and the update stage is a continuation, not an author tool call. Version coherence - the rewrite carries replaceIfVersion at the observed version; a racing author write makes it fail safely (no clobber). The cordis-trap guard - ncu's reject list is always policy.reject ∪ the chain-governed dep names; ncu must never bump a chain pin to public npm latest. Silence - the model-facing write result is built from the author's own content; a listener throw is contained logger-only (the core's Suppress composer); the ledger is best-effort.

The G4 update-stage envelope - the Dispatch/Settle pair of the diagram above (the gates, the jobs envelope, the breaker update, the U2 refresh) - lives in the core (@playform/hook-dsh-core); this module is a thin delegate that injects its own collections, child stage and ledger strings, so the flow is not forked per governance module.

The Config

SCHEMA + DEFAULTS AT LOAD

The exported Config schema (the factory's Schema helper extended with the module's own fields) validates and fills every default at load - invalid configuration fails loudly. New patch entries are declared with insert:. Full example:

- insert: - id: hook-dsh-governor-package name: "@playform/hook-dsh-governor-package" config: log: true logFile: ~/.dsh/hook-dsh-governor-package.log # global ledger updateCooldownMs: 3000 strict: false # explicit only; never default-delete unknown deps mutationTools: [write, edit, str_replace_editor] maxUpdateFailures: 3 # circuit breaker: pause a dir's update stage ncuBin: /usr/local/bin/ncu # absolute - host PATH != shell PATH updateMode: programmatic # "programmatic" (default) | "bin" policyFile: "" # optional global update-policy.json (else discovery, else built-in) exclude: [node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app]
FieldTypeDefaultMeaning
logbooleantruewrite the durable ledger file
logFilestring~/.dsh/hook-dsh-governor-package.logthe governor's global ledger
updateCooldownMsnumber3000cooldown between update-stage dispatches per directory
strictbooleanfalsestrip unknown deps - explicit only; never default-delete unknown deps
mutationToolsstring[][write, edit, str_replace_editor]the actor tools whose writes count as triggers
maxUpdateFailuresnumber3circuit breaker: pause a dir's update stage
ncuBinstring/usr/local/bin/ncuabsolute - host PATH != shell PATH
updateMode"programmatic" | "bin"programmaticncu as a library (no external binary) or via the ncu binary
policyFilestringoptional global update-policy.json (else discovery, else built-in)
excludestring[][node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app]the exclusion segments (the core's Default)

In Action

ONE GOVERNED WRITE

One governed write. The author writes a package.json anywhere with the write tool - one dep that should track the governed registry, one public dep left on a range:

Before - the manifest as the author wrote it

{ "name": "@acme/tool", "scripts": { "build": "tsc" }, "dependencies": { "@playform/build": "^0.3.4", "@acme/chain-core": "0.4.1" } }

The chain pass finds @acme/chain-core in the nearest registry.json (effective 0.4.5) and canonicalizes it to ^0.4.5; the public dep stays a range until the update stage's ncu run bumps it. The transcript shows only what the author wrote; the ledger shows what actually happened:

After - the ledger line for the same pass

[2026-10-03T09:15:22.411Z] activated (anywhere mode, logFile=~/.dsh/hook-dsh-governor-package.log, updateMode=programmatic, ncuBin=/usr/local/bin/ncu, exclude=[node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app], policyFile=(discovery)) [2026-10-03T09:16:01.880Z] governed <project>/acme/tool/package.json → 42 [2026-10-03T09:16:34.120Z] update stage dispatched for <project>/acme/tool (mode=programmatic, ncu via /usr/local/bin/ncu, built-in default policy) [2026-10-03T09:16:41.502Z] update: DONE — pins bumped per policy

Read the file back and @acme/chain-core is ^0.4.5 (or already bumped by ncu if the policy allowed it); write it again immediately and nothing complains - no FS_STALE_VERSION, the re-emit made the second pass a no-op. Write inside node_modules/.git/ .dsh and the ledger gains one line, skipped (excluded) ... , while the file stays untouched.

The Ledger

ONE GLOBAL LOG

One global log (logFile, default ~/.dsh/hook-dsh-governor-package.log) records activation, exclusions, chain-pass results, and every update-stage dispatch. The strings this module composes (the hook-dsh-governor-package: logger prefix is the factory's Append; each line is [<ISO>] <message> in the file):

activated (anywhere mode, logFile=..., updateMode=..., ncuBin=..., exclude=[...], policyFile=(discovery)) skipped (excluded) <path> no registry.json found for <path> governed <path> → <version> update stage dispatched for <dir> (mode=..., ncu via ..., policy ...) update: DONE / update: FAILED

The registry-miss line ends with an em dash followed by chain pass skipped; the DONE/FAILED lines are update: DONE + em dash + reason and update: FAILED + em dash + reason - byte-exact forms are in the In Action excerpt above. Those em dashes are part of the literal ledger strings, so they live only in the example block. 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 gates, the discovery, the guarded write, the refresh, the continuation, the effects and the schema come from it.

The sibling pin pass on the same seam: pins ^0.3.4 → 0.3.4; a fully pinned manifest leaves this module's update stage nothing to do.

The Rust-sided sibling: same architecture for Cargo.toml, with the update stage driven by cargo upgrade.

License: MIT. Full method contract: SCHEME.md. TypeScript-first, built with @playform build (ESBuild + tsc type-check); the published artifact contains only the built output.