PLUGIN DETAIL ROLE: FACTORY SERVICE PROVIDER

plugin-dsh-factory

Plugin @ DSH @ Factory • _The DeepSeek Harness Plugin Family for PlayForm._ The furnace of the DSH governance family - the first service-provider bundle in the DeepSeek Harness plugin family. Loading it registers one class plugin (export default class PluginFactory extends Service, super(ctx, "pluginFactory"), static inject = ["fs"]) that exposes ctx.pluginFactory: a single service holding every piece of common machinery the family's hooks used to duplicate. One service. Nineteen methods (the direct-govern pair RegisterGovern + Govern and the GovernSteps registry among them). The hooks bring their own metal; the factory pours the mold.

CLI INSTALL COPIED
$ pnpm add @playform/plugin-dsh-factory
Namespace: @playform/plugin-dsh-factory Release: v0.0.1
Archetype: Service Injects: fs Exposes: ctx.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: THE PARENT SERVICE

Family position (the @-sentence Plugin @ DSH @ Factory): the family's parent service - every other package consumes it (the three governance hooks inject the service; the six normalize flavors consume State/Append and the Schema helper); its own parent is the filesystem service (inject: ["fs"]).

ConsumerInjectsUses
hook-dsh-governor-package["fs", "pluginFactory"]Gate, Discover/Parse, Continue, GuardedWrite, Refresh, State, Wire, Attach, Journal, Append, UpdateKey
hook-dsh-pinner-package["fs", "pluginFactory"]Gate, Discover, Continue (pin pass), State, Wire, Attach, Journal, Append
hook-dsh-governor-cargo["fs", "pluginFactory"]Gate, Discover/Parse, Continue (TOML surgery), State, Wire, Attach, Journal, Append, UpdateKey, Seam
hook-dsh-normalize-dash + the five hook-dsh-normalize-* flavors["pluginFactory"]State, Append - plus the named Schema export (shared: false) for their config

The pure layer it deliberately does not re-export is hook-dsh-core (its service surface stays stable); the hooks import the core's helpers directly. The DSH plugin family is the DeepSeek Harness plugin layer of the PlayForm ecosystem: TypeScript-first Source/ → Target/, the deterministic @playform build, prepublishOnly-only - the same conventions as every other @playform package.

In the DeepSeek Harness

THE SERVICE BEHIND THE SEAMS

The factory is the family's bridge into the harness internals - it sits under every consumer and drives the actual facilities:

SeamWhat the plugin does thereWhat you can observe
The tool layer (write / edit / str_replace_editor)The fs/observed events the governance hooks react to are fired by the tool layer only, for every harness write or edit; GuardedWrite and Refresh keep the author's next guarded write from ever failing a stale-version check.One trigger law for the whole machine, any agent, any thread.
ctx.fs - the filesystem service (dsh-fs)Every write of a GOVERNED file goes through ctx.fs (the fs/write-intent waterfall, the standing sandbox policy, the fs/observed emit); only the ledger append and auxiliary discovery reads are plain-fs exemptions, documented.Writes that the observation policy and the sandbox see like built-in writes.
ctx.jobs - the jobs facilityAttach registers the root's job controller (which serves every owner) and runs the update stages inside the jobs envelope (kind governor-update, unowned), with the detached contained continuation as the probed fallback.Update stages that never block or abort the author's session.
The storage domainJournal writes the package_governance v2 records when the storage facility is present (probed, graceful): the queue drains, or the records buffer/drop and the human ledger stays the complete record.A machine-readable governance history beside the human ledger.
Sessions / the ledgerAppend composes the two wrappers - <State.Module>: <message> on the logger and [<ISO>] <message> in the durable ledger file - around every consumer message."Did it activate / what happened" is answerable from the ledger alone.

The Problem

THREE CONSUMERS, ONE MACHINE

Three independent consumers of the same machinery already existed. The governor, the pinner and the cargo governor each maintained their own copy of: the ledger (Append), the exclusion match (Match), the registry / policy walk-up discovery (Discover/Parse), the union keep-list resolution with the P3 chain-keys interlock, the g1 gate set, the version-guarded write with the P4 sandbox fence, the P3/U2 observation-policy refresh, the detached contained continuation scaffolding, the State construction with defensive cell unwrap, the ctx.on wiring, the P1/P2/P5 lifecycle effects, the Schemastery schema shape, and the probe-once optional-service accessor. Every bug fix or hardening pass - the sandbox fence, the stale-version leak, the in-flight disposal - had to be applied three times. The factory makes it apply once.

How It Works

THE FURNACE
THE CONSUMERS BRING THEIR OWN METAL THE FACTORY POURS THE MOLD (model logic: a Config extension, (19 methods on ctx.pluginFactory, a pure transform, an update engine inject: ["fs"] - it calls ctx.fs) and every ledger string) hook-dsh-governor-package ────►┐ hook-dsh-pinner-package ───────┤ ctx.pluginFactory (Service) hook-dsh-governor-cargo ───────┤ │ hook-dsh-normalize-dash ───────┤ ├─ Append(state, msg) ──► "<Module>: <msg>" on the logger hook-dsh-normalize-quotes ─────┤ │ + "[<ISO>] <msg>" appended to the ledger file hook-dsh-normalize-ellipsis ───┤ ├─ Match(path, list) ───► the exclusion-first segment match hook-dsh-normalize-spaces ─────┤ ├─ Discover(dir) ──────► nearest registry.json walk-up hook-dsh-normalize-invisible ──┤ ├─ Parse(path) ─────────► contained JSON read (null on any failure) hook-dsh-normalize-fullwidth ──┘ ├─ ResolvePolicy(...) ──► union keep-list: global policy → <dir>/ │ registry-adjacent → + P3 chain keys ├─ Gate(...) ───────────► g1 gates: target → actor → kind → │ Stash idempotence → basename → excluded ├─ GuardedWrite(...) ──► replaceIfVersion + the P4 sandbox fence ├─ Refresh(...) ───────► Stash seed + fs/observed re-emit (same actor) ├─ Continue(..., tfm) ─► Inflight → readText → keep-list → │ the model's transform → GuardedWrite → │ message → Refresh → contained throws ├─ State(ctx, cfg, s) ─► cell unwrap + shared fields + extras ├─ Wire(ctx, st, obs) ─► the fiber-owned fs/observed registration ├─ Attach(ctx, {...}) ─► P1 jobs controller / P2 inflight disposal / │ P5 storage domain (probed, graceful) ├─ Journal(state, ...) ─► the P5 package_governance writer ├─ Schema(shared?, m) ─► the Schemastery schema factory ├─ RegisterGovern(...) ─► the direct-govern registry (per basename) ├─ Govern(target, ...) ─► the direct-govern executor ├─ GovernSteps(...) ───► the registered steps (the registry view) ├─ UpdateKey(target) ──► the namespaced Inflight key (update stages) └─ Seam(state, name) ──► probe-once optional-service accessor

A consumer never re-implements the mold - it injects the service (inject: ["pluginFactory"], the loader holds it PENDING until the factory exists) and supplies only its own three parts:

1. A Config extension CONFIG

export const Config = Schema({ logFile: ... }, { myField: ... }) via the standalone named Schema export - usable at module-evaluation time, before any service instance exists; shared: false emits the minimal block for non-manifest modules.

2. A transform TRANSFORM

The pure (current, section, keep) => { next, count, message? } | null function. current is the RAW file text (null when the read failed); the consumer decodes, applies its logic, logs its own no-op/refusal lines and returns null for every no-op path.

3. An update engine (optional) UPDATE ENGINE

ncu/cargo dispatch, cooldowns, circuit breakers, the first-wins update-policy path pick: model logic, built on GuardedWrite/Refresh/Seam/Journal/UpdateKey.

What the factory deliberately does NOT own: the consumer ledger strings (every message a hook logs - "skipped (excluded) ...", "observed non-JSON package.json ... - skipped", "REFUSED rewrite ...", "pinned ...", "governed ... → ...", the activated (...) proof - is composed and logged by the hook through the factory's Append), the transforms (the consumer supplies the pure function; the factory only drives it inside Continue), and the update engines (the factory gives them GuardedWrite/Refresh/Seam/Journal/UpdateKey to build with). Full contract: SCHEME.md.

Verification. tsc --noEmit - zero errors; pnpm run prepublishOnly - minified Target/ (+ .d.ts twins); node factory-smoke.mjs (in the family's smokes/ directory) - 30 checks, ALL PASS, covering every primitive, the Gate matrix, both GuardedWrite postures, the full Continue lifecycle, State defaults + cell unwrap, Attach's three effects, Journal's queue/drain/silent-skip, Schema's volatile cells and Seam's probe-once caching.

The Config

NONE - IT IS THE CONFIG MACHINERY

The factory registers no Config schema of its own - it is the config machinery. Its Schema(shared?, m) instance method is the Schemastery schema factory every consumer builds its schema with, and the standalone named Schema export makes the same factory usable at module-evaluation time, before any service instance exists (the loader needs Config at load). shared: false emits the minimal block (no logFile/updateCooldownMs/ mutationTools/policyFile/exclude) for non-manifest modules like the normalize flavors; the shared defaults carry the family's block. Volatile cells (log, logFile and any consumer's own hot fields) commit without remounting the plugin; the factory's State builder unwraps them defensively.

In Action

A GOVERNED WRITE, END TO END

The consumer supplies the transform - the only code in the chain it writes

// Source/Function/Transform.ts of a consumer (condensed to its shape): export default (Current, Section, Keep) => { const Document = JSON.parse(Current); // Current: the RAW file text const Outcome = Pin(Document, Section, Keep); // the module's own logic switch (true) { case !Outcome: // nothing to pin return null; // every no-op path logs itself, then null default: return { next: Outcome.Next, // written as JSON.stringify(next, null, 2) + "\n" count: Outcome.Pinned, message: `pinned ${Path} (${Outcome.Pinned} versions)`, }; // Continue: GuardedWrite → Refresh → Append → journal } }; transcript: the write tool result shows exactly what the author wrote pinner.log: [2026-10-03T09:16:01.880Z] pinned <project>/acme/tool/package.json (7 versions)

After - the write the factory performed, as the author and the ledger see it

The factory's part of that one pass: Gate decided the actor and the path, Discover found the nearest registry.json, Continue ran the transform detached and contained, GuardedWrite carried replaceIfVersion past the P4 sandbox fence, Refresh re-emitted the fresh version with the same actor, and Append composed the logger's line from the module's message. The consumer composed the only string in the chain.

The Ledger

TWO WRAPPERS, CONSUMER'S MESSAGE

The factory registers no activation line and owns no ledger strings - it is a service, not a module. Its Append(state, message) is the family's one ledger mechanism, and it composes exactly two wrappers around the CONSUMER's message:

logger: <State.Module>: <message> (State.Module = the consumer's historical prefix) ledger: [<ISO timestamp>] <message> (appendFileSync, best-effort, when log is enabled)

Every string a hook logs - "skipped (excluded) ...", "governed <path> → <version>", "pinned <path> (N versions)", the activated (...) proof - is composed and logged by the hook, through the factory's Append. The factory's own composed strings are parameterized machinery (the unreadable-policy line takes the policy file name; the suppressed-error lines take state.Module as the prefix) and stay byte-identical when a consumer keeps its historical name.

Related plugins

12 TOTAL
hook-dsh-core DSH FAMILY

The pure layer the factory deliberately does not re-export - the hooks import the core's helpers directly.

A governance hook child: injects the service and supplies its own transform, update engine and ledger strings.

The factory's first NON-MANIFEST module: injects only the service and uses State/Append and the named Schema export.

License: MIT. The published artifact contains only the built output - files whitelists Target/, cordis.patch.yml and the docs; Source/ never ships. There are no build/watch npm scripts: the only npm script is prepublishOnly.