soul.demarkus.io:6309/index/decisions.md/v13 draft reader meta

Decisions

Part of the demarkus-soul hub. Architecture decision records for the core project. Canonical copies live in git at docs/adr/; these are verbatim mirrors so they are reachable by lookup, backlinks, and this hub (see where decisions live). Sub-project decisions live in their own series, for example /demarkus-library/adr/. Status and dates live in each ADR.

  • ADR 0001: broker confidential web-client registry

  • ADR 0002: align store frontmatter with the Open Knowledge Format

  • ADR 0003: default OKF type on publish

  • ADR 0004: edge semantics, provenance on every edge, typed relations via rel- metadata

  • ADR 0005: node identity omits the default port

  • ADR 0006: the Postgres backend is an optional build, not a dependency; superseded by ADR 0010

  • ADR 0007: SNI selects and pins one virtual Mark server per QUIC connection

  • ADR 0008: GCS world state commits through one root CAS

  • ADR 0009: AGPL for the core, MIT for plugins and satellites

  • ADR 0010: remove the Postgres backend; the knowledge server owns shared multi-writer state

  • ADR 0011: allowlisted private-use scheme redirects for MCP hosts, unblocking Cursor's MCP OAuth against the broker

  • ADR 0012: body match is an optional mode of LOOKUP; recall specified, ranking left to the implementation

  • ADR 0013: source revisions, extraction views, graph freshness and generated-format compatibility

  • ADR 0014: the protocol module is wire and stored format only

  • ADR 0015: the backend contract takes a context per call and reads through views

  • ADR 0016: the publish policy is enforced above the store, inside its commit

  • ADR 0017: archive preconditions, closed views, and seeding above the store

  • ADR 0018: one mark URL parser, and host case is not identity

  • ADR 0019: tool bodies and the write contract live in shared packages

  • ADR 0020: one reconcile owner in docwrite, and write tools check arguments before authorization

  • ADR 0021: a world registry that announces drops, DNS label world names, one tenant door for tools and resources

  • ADR 0022: the MCP gateway takes its dependencies from Run, not the Server; NewServer takes a deps struct

  • ADR 0023: the broker is core, oauthsrv, gateway and storage behind one Run, with brokertest for shared fixtures

  • ADR 0024: the plugin binary is provision and registry package families by concern, and a harness is a host.Host value

  • ADR 0025: WATCH is the eighth verb, the store emits hints only, and protocol 1.0 freezes after WATCH and ALPN mark/1 (proposed)

  • ADR 0026: a client SDK under MIT with its own semver, CC0 wire fixtures, the server never imports it, exact relicensed file list (proposed)

  • ADR 0027: world token files are optional at open and split into a broker-owned runtime file and an operator-owned static file; the chart's bootstrap Job becomes optional for GitOps

soul.demarkus.io:6309/adr/0024-plugin-binary-packages-by-concern-and-host-values.md draft reader meta

ADR 0024: The plugin binary is packages by concern, and a harness is a value

Status: accepted (2026-09-22). Canonical copy in git at docs/adr/0024-plugin-binary-packages-by-concern-and-host-values.md. Part of the architecture quality plan, track 3e; follows the split pattern of ADR 0023; the findings are T6 to T12, T18, T21 and T22 in the tooling ledger.

Context

tools/demarkus-plugin is the one binary every demarkus plugin adapter calls. Its lifecycle package (provision, 2,142 lines) held eight concerns and its state package (registry, 1,065 lines) seven, with a nine argument join, a mutable package global selecting the harness, three --format switch ladders in main, three semver comparators and no test over the lifecycle functions that had produced the last two regressions (findings T3 and T4). The plan asked for the split, one host package and the seams the tests need, behaviour preserved: plugin output, hook output, state files and log text byte identical, with one exception the tests forced and the consequences describe, the report of a server that dies at startup.

Decision

  • internal/provision is the orchestration and health reporting root (Provision, Init, Status, HealthWarning, VerifyAuth, DetectServers) over five packages: release pins and installs the binaries, procscan discovers processes and probes ports and binaries with every probe bounded and an unrunnable probe reported as unknown, tokens mints, migrates and verifies the plugin token, server spawns, adopts and restarts the managed server, seed publishes the first documents. progress is the one [demarkus-memory] stderr log. The dependency shape is release on procscan; tokens on procscan and the registry's statefile; server on procscan, tokens and release; seed on nothing but progress, taking its client and token as inputs; only the root on all of them. config owns the ~/.demarkus layout (BinPath, TokenPath, the memory roots, the managed log and tokens paths) and both halves of plugin-memory.conf (LoadConfig, SaveConfig).
  • internal/registry is a directory of packages: statefile is the transactional engine (lock, atomic write, prepare then apply with rollback, record field and slug validation); catalog reads and binds the memory catalog and owns the local memory's alias and the names a join may not take; knowledge, promote and broker each keep one registry or validation; join commits a memory join as one joinPlan; mcpconfig edits a harness's MCP config file. Shared test fixtures live in registrytest and provisiontest, which import only packages whose tests do not import them back.
  • The catalog record has one codec, config.MemoryRow, and one records reader, config.Records; MemoryRegister is gone, a join is the only writer.
  • internal/host holds what differs per harness at the output edge: the hook payload shapes, the project directory variable, the MCP config file and its remote server entry, the once per session nudge, the pi proxy unwrap. Claude, Cursor, Pi and OpenCode are values; ForHook, ForGate and ForMcp resolve the --format and --harness vocabularies, which do not change. mcpconfig takes the host as a parameter. The tool name grammar stays with config.
  • internal/semver is the one release version parser; each caller keeps its own leniency (update reads an unparseable version as 0.0.0, the drift probe says nothing).
  • Test seams are package values: release.BaseURL, tokens.DialProbe, statefile.Writer, the managed ports in provision. The server stub the lifecycle tests run is a compiled program, because ps reports a script's interpreter, and it answers --version, binds its -port and ignores SIGHUP as the real server does.
  • scripts/check-identical-copies.sh guards every hand copied adapter file: the eight bootstraps byte for byte, the branded hooks modulo the plugin label and surface flag, the TS runBin helpers per family.

Consequences

  • Plugin output, hook output, state files and log text are unchanged, except the startup death report below; the hook shapes are pinned by host's tests. Two edge cases moved with the dedup and are recorded in /debt.md: the memory-default listing normalizes a hand corrupted catalog row, and the update check reads a prerelease suffixed version as unparseable.
  • The lifecycle has tests for EnsureBinaries, server.Ensure, Init in all three modes, provisionLocked, VerifyAuth and HealthWarning; the T3 probe regression is pinned in procscan and server, T4 in main.
  • Writing those tests showed that a managed server which dies at startup was reported as spawned: the plugin process never reaped its detached child, so the exited process read as alive to kill 0. server.Ensure now waits on the child while the poll runs and reports the exit with the log tail.
  • The pin bump workflow reads release.go (ServerVersion, ClientVersion).
  • The lint ratchet shrinks by the join's argument and result limit entries and the paths move.
trail
  1. soul.demarkus.io:6309 v13
  2. 0024-plugin-binary-packages-by-concern-and-host-values