PLUGIN DETAIL ROLE: GOVERNOR GATE: Cargo.toml

hook-dsh-governor-cargo

Hook @ DSH @ Governor @ Cargo • _The DeepSeek Harness Plugin Family for PlayForm._ The Cargo.toml flavor of the silent package.json governor - the same architecture, the same API-awareness discipline, the same silence toward the author, but for RUST manifests, with the update stage driven by the RUST-SIDE CLI ( cargo upgrade, from cargo-edit): Rust does not co-opt into the TypeScript ecosystem, so there is no npm-library equivalent - the update stage operates at the exclusionary level through the cargo CLI. Chain canonicalization + full-version normalization + surgical TOML rewriting + cargo upgrade --exclude - comments survive. Every claim below is live-verified (2026-10-03, live).

CLI INSTALL COPIED
$ pnpm add @playform/hook-dsh-governor-cargo
Namespace: @playform/hook-dsh-governor-cargo Release: v0.0.1
Archetype: Hook Runtime dep: smol-toml 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: THE RUST-SIDED SIBLING

Family position (the @-sentence Hook @ DSH @ Governor @ Cargo): a hook child of the plugin-dsh-factory service and the hook-dsh-core helpers; the Rust-sided sibling of the two npm governance hooks on the same fs/observed seam. One of three governance hooks, each gating on its own basename and writing its own ledger: governor-package (package.json → governor.log), pinner-package (package.json → pinner.log), and this bundle (Cargo.toml → cargo-governor.log, chain pass bare caret /=exact + cargo upgrade).

The basenames are disjoint: the cargo module never sees a package.json event and the npm plugins never see a Cargo.toml event. Composition semantics: chain > strip > normalize (this module's precedence), keep-list wins over normalization - never over the chain. Machinery-wise it is a factory flavor: gates, discovery, the guarded write, the refresh, the continuation, the effects, the schema and the namespaced UpdateKey come from plugin-dsh-factory; the policy loader and the suppression composer come from hook-dsh-core. This bundle keeps the Cargo-specific residue: the TOML identification, the surgical pin, the full-version directive, and the cargo upgrade bridge. Besides the fs/observed event path, its two steps are registered with the factory's direct-govern registry at apply (Factory.RegisterGovern("Cargo.toml", "cargo" | "update", ...)), so the raw-write tool's per-call govern selection can drive the same chain + normalization pass and update stage directly through Factory.Govern.

In the DeepSeek Harness

BRIDGING THE RUST/TS BOUNDARY
SeamWhat the plugin does thereWhat you can observe
fs/observed - the file observation eventThe same seam the two npm governance hooks use, gated on the Cargo.toml basename - the basenames are disjoint, so the cargo module never sees a package.json event and the npm plugins never see a Cargo.toml event.One trigger law covers the whole machine, npm and Rust alike.
ctx.fs - the filesystem service (dsh-fs)The surgical TOML rewrite goes through the factory's guarded write (replaceIfVersion + the P4 sandbox fence); smol-toml is used for identification only, never a stringify, so comments survive byte-for-byte.Governed manifests that still read exactly like the author's file.
ctx.subprocess (dsh-subprocess) - the Rust sideThe update stage crosses the Rust/TS boundary through the subprocess seam: cargo upgrade driven with a fully-specified argv, collect-mode output piped to the job ring and the ledger.The only update tool Rust has, driven without shell PATH drift.
ctx.jobs - the jobs facilityThe update stage runs in the jobs envelope (kind governor-update, unowned) when a controller serves the context, with the detached contained continuation as the probed fallback.cargo upgrade never blocks or aborts the author's session.
The ledger / sessionA separate ledger (cargo-governor.log) records activation, exclusions, chain-pass results, refusals and every update dispatch - including the exact cargo upgrade argv and its exit code.The Rust-side pass is fully auditable from the ledger alone.

The Problem

WHY THE CARGO GOVERNOR EXISTS

Rust manifests drift the same way npm manifests do - but Cargo.toml is a TOML document people decorate with comments, and there is no in-process library path: the only update tool is the cargo CLI. A governor for Cargo.toml must therefore rewrite surgically (never a TOML stringify) and delegate its update stage across the Rust/TS boundary - while still hooking the same fs/observed event the npm hooks use, so one trigger law covers the whole machine.

How It Works

THE PIPELINE G = U ∘ P

The same G1/G2 gates and Stash seeding as the npm governor; the G3 chain pass parses with smol-toml (IDENTIFICATION ONLY) and plans: 1. CHAIN - dep in registry.effectiveLatest → the bare resolved version (Cargo's implicit caret) or =resolved with pinStyle "exact", always the FULL form (shorthand padded); 2. STRIP - strict-mode unknown deps; 3. NORMALIZE - every other simple version (full-version directive below; the keep-list wins → byte-identical). The rewrite is a surgical line-level edit on the raw text - comments, inline comments, blank lines and spacing survive byte-for-byte - then GuardedWrite → ledger governed <path> → <version> → Refresh. The G4 update stage crosses the Rust/TS boundary:

cargo upgrade --manifest-path <Cargo.toml> --exclude <crate> ... (ONE flag per crate - the comma form is silently IGNORED) [-p <crate> ...] [--compatible/--incompatible/--pinned ...] exclude = policy.reject ∪ chain deps (the cordis-trap guard: cargo upgrade must never bump a chain-governed pin)

The full-version normalization directive. No shorthand like "1.0" may remain in a governed Cargo.toml; every dependency version is expanded to its MOST SPECIFIC form:

WrittenGoverned
1.01.0.0
11.0.0
0.10.1.0
=1.0=1.0.0 (the exact form keeps its = prefix)
1.2-rc.11.2.0-rc.1 (padded BEFORE the prerelease)
1.2.3-rc.1unchanged (prerelease preserved as authored)
1.2.3+buildunchanged (build metadata preserved)
>=1.2, ~0.3, >1.0, 1.0, 2.0unchanged (complex ranges left as-is)

The TOML rewrite: why comments survive. smol-toml (the ONE runtime dependency, carried by the bundle's own node_modules) is used for PARSING ONLY - identification of the dep entries in [dependencies], [dev-dependencies], [build-dependencies], [target.'cfg(...)'.dependencies] (and its dev/build variants), and [workspace.dependencies] (handled natively). Handled entry shapes: name = "0.3.4", name = { version = "0.3.4", ... } (single line or pretty-printed), [dependencies.name] table style. Never rewritten: path = "...", git = "...", and inherited entries (workspace = true). Non-dependency sections are NEVER touched; the refusal guard refuses any plan entry outside an identified dependency table, and Pin aborts (never a partial rewrite) if a planned entry cannot be located in the text.

The Rust side (live-verified). cargo 1.100.0-nightly + cargo-edit 0.13.13, resolved through cargoBin. --exclude is ONE flag per crate - the comma form is silently IGNORED, the exact inverse of ncu's one-comma-argument reject list. There is NO --exact flag in cargo-edit 0.13.13 - exact pins are enforced at the manifest level by the chain pass (pinStyle: "exact" → =resolved). cargo upgrade preserves comments and formatting (it edits via toml_edit). If cargo-edit is NOT installed, cargo upgrade exits 101 with no such command: upgrade - the module logs update: cargo-edit unavailable: ... and fails gracefully.

The Config

SCHEMA + DEFAULTS AT LOAD

The exported Config schema validates and fills every default at load; new patch entries are declared with insert:. Full example:

- insert: - id: hook-dsh-governor-cargo name: "@playform/hook-dsh-governor-cargo" config: log: true logFile: ~/.dsh/hook-dsh-governor-cargo.log # SEPARATE ledger - never the npm ledgers updateCooldownMs: 3000 strict: false # strip unknown deps - explicit only, never a default mutationTools: [write, edit, str_replace_editor] maxUpdateFailures: 3 # circuit breaker: pause a dir's update stage cargoBin: /usr/local/bin/cargo # absolute - host PATH != shell PATH updateMode: cargo # the ONLY mode (the Rust-side CLI) policyFile: "" # optional global update-policy.json keepFile: "" # optional global pin-policy.json (the keep-list) exclude: [node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app]

Update-stage policy mapping (update-policy.json, same discovery as the npm governor): reject → --exclude <crate> (ONE flag per crate); allow → -p <crate> (repeatable); incompatible/pinned/compatible → --incompatible/--pinned/--compatible; pinStyle has NO CLI equivalent (manifest-level via the chain pass); verifyCommand runs after the upgrade via the subprocess seam, exit 127 non-fatal. The built-in default mirrors cargo upgrade's own semantics: no rejects, incompatible: "ignore", pinned: "ignore", pinStyle: "caret", no verifyCommand (never install implicitly).

In Action

ONE GOVERNED WRITE

One governed write. The author saves a Cargo.toml with comments, a shorthand version, and a chain-governed dep:

Before - the Cargo.toml as the author wrote it

[dependencies] # the engine — keep the comment alive serde = "1.0" # shorthand: must become 1.0.0 tokio = { version = "0.4.1", features = ["full"] } # chain-governed

With the nearest registry.json resolving tokio to 0.4.5, the surgical rewrite produces (same text, same comments, same inline spacing):

After - the same file, surgically rewritten

[dependencies] # the engine — keep the comment alive serde = "1.0.0" # shorthand: must become 1.0.0 tokio = { version = "0.4.5", features = ["full"] } # chain-governed

The transcript shows only what the author wrote; the ledger shows the pass:

The ledger lines for the same pass

[2026-10-03T09:15:22.411Z] activated (cargo flavor, logFile=~/.dsh/hook-dsh-governor-cargo.log, updateMode=cargo, cargoBin=/usr/local/bin/cargo, exclude=[node_modules, .git, .dsh, .pnpm, .store, DeepSeek Harness.app], policyFile=(discovery), keepFile=(discovery)) [2026-10-03T09:16:01.880Z] governed <project>/acme/engine/Cargo.toml → 42 [2026-10-03T09:16:34.120Z] update stage dispatched for <project>/acme/engine (cargo via /usr/local/bin/cargo, built-in default policy) [2026-10-03T09:16:41.502Z] update: cargo upgrade --manifest-path <project>/acme/engine/Cargo.toml --exclude serde tokio → {"exitCode":0} [2026-10-03T09:16:41.503Z] update: DONE — pins bumped per policy

Here the policy's chain deps plus rejects became one --exclude flag per crate; serde and tokio are protected from the CLI bump - tokio by the chain, serde by the normalization pass already writing its full form. Read the file back to see the governed state; write it again immediately and nothing complains - no FS_STALE_VERSION. Write inside node_modules/.git/.dsh and the ledger gains skipped (excluded) ... while the file stays untouched. Remove cargo-edit and the pass fails gracefully: update: cargo-edit unavailable: ..., the manifest untouched, the breaker counting the failure.

The Ledger

ONE GLOBAL LOG

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

activated (cargo flavor, logFile=..., updateMode=cargo, cargoBin=..., exclude=[...], policyFile=(discovery), keepFile=(discovery)) skipped (excluded) <path> no registry.json found for <path> governed <path> → <version> update stage dispatched for <dir> (cargo via ..., policy ...) update: cargo upgrade <argv> → {"exitCode":0} update: DONE / update: FAILED update: cargo-edit unavailable: <cause>

The registry-miss line ends with an em dash followed by chain pass skipped; the DONE line is update: DONE + 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 npm sibling: same architecture, same API-awareness discipline, same silence - with the update stage driven by ncu.

The keep-list sidecar (pin-policy.json) is shared: discovery global keepFile → the file's own directory → registry-adjacent, the union.

Gates, discovery, the guarded write, the refresh, the continuation, the effects, the schema and the namespaced UpdateKey come from the furnace.

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