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

Decisions

Part of the demarkus-soul hub. Architecture decision records for the core project, canonical here since 2026-09-28; docs/adr/ in git is a frozen archive of 0001 to 0027 (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

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

  • 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; amended by ADR 0034

  • ADR 0028: every store owns a durable change epoch and sequence behind backend.ChangeSource, proven by one conformance suite; the file store keeps a journal

  • ADR 0029: the broker issues the federation agent's hub token from agentTokens, with a broker record as the ownership marker; the umbrella drops the bootstrap Job; superseded by ADR 0034

  • ADR 0030: the broker generates and persists its state cookie key, so the chart render is deterministic under Argo CD (proposed)

  • ADR 0031: the bucket store seals receipts into change blocks outside the commit, and the hub reads them as a cached backlog, so a knowledge cursor resumes across a restart up to the ring

  • ADR 0032: the knowledge and memory brokers run inside the knowledge server process as two gateways on one mux; in-process dispatch, no write token handoff, WATCH fan-out on the hub, resource bound tokens (proposed)

  • ADR 0033: the knowledge server's bearer listener (UDP 443, ALPN mark) takes an identity bearer in the auth field; the gateway's audience, org gate, tenancy and write predicates admit each request, watchers share the tokenless group and end at bearer expiry (proposed); superseded by ADR 0037

  • ADR 0034: the knowledge server derives the federation graph from change feeds into the hub's checkpoints and /graph.md, in process with no token; agent tokens and the umbrella's agent are removed; supersedes ADR 0029 (proposed)

  • ADR 0035: drop content-addressed fetch, the per-world body-hash index, mark_resolve and mark_index before protocol 1.0; /sha256- paths stay reserved

  • ADR 0036: the bucket store commits through create-only log slots with group commit, catalog reads may lag a second across replicas, checkpoints are derived, head.json freezes, the server holds a world's store through contract types only (backend.Follower for peer hints); supersedes ADR 0031 (proposed)

  • ADR 0037: the contract is the protocol, transports are bindings (in-process, QUIC, MCP), two doors by credential kind (capability tokens on raw mark, OAuth identity on MCP); the bearer listener is removed; supersedes ADR 0033 (proposed)

  • ADR 0038: a broker write carries the verified email as user and the MCP client name as agent; WATCH events and the commit log carry both

  • ADR 0039: drop the standalone federation agent (demarkus-agent, fedcrawl, chart, image, release steps) before 1.0; the knowledge server is the only graph producer; amends ADR 0034 (proposed)

soul.demarkus.io:6309/conventions.md draft reader meta

Conventions and Working Agreements

Hard rules for how to work on demarkus with Fritz. These are collaboration and repo/process conventions, distinct from the Go code-quality rules in /guidelines.md and the idioms in /patterns.md. Reference all three before writing code.

These were migrated from Claude Code's built-in auto-memory on 2026-06-06 when that feature was disabled in favor of demarkus-soul as the single memory store. They were reconstructed from index summaries; the original long-form rationale for each was lost in the migration, so the "why" below is brief.

Collaboration

No sycophancy

On code or ideas. Critically review before presenting: layering violations, missing edge cases, state desync, stale references, channel blocking, rune vs byte vs cell-width confusion, silent error paths, leaky abstractions, wrong architectural layer. Challenge ideas before agreeing: what's the downside? what breaks? what's the simpler alternative? is this the right problem? Disagreement backed by reasoning is expected.

No AI co-author trailer in commits

Never add Co-Authored-By: Claude ... or any AI co-author trailer to commits or PRs. Fritz is the sole author.

Never ship anything broken

Grow PR scope rather than ship a known-broken surface. "A follow-up will fix it" is not a valid mitigation. If a change leaves something broken, the fix belongs in the same PR.

Prefer OSS, non-cost-blocker tooling

When recommending infra or platform tools, default to genuinely open-source options. Flag BSL/SSPL/Elastic-licensed or paid-tier-gated tools explicitly and offer OSS alternatives.

Question opt-in knobs that come from plans

When a plan calls for an Enabled bool or an "opt-in deployment," ask whether a real "off" deployment actually exists. Default to baking the capability in rather than adding a flag nobody will turn off.

Engineering value: robustness proven under failure

Adopted at Fritz's request on 2026-09-22. Use etcd's engineering discipline as the primary quality reference for demarkus: explicit guarantees, fault injection, recorded operation histories, and validation against a small model of expected behavior. Reference: etcd robustness testing.

Correctness under crashes, cancellation, concurrency, and uncertain write outcomes is a project value. A clean package graph and green happy-path tests are useful evidence, but cannot establish those guarantees.

  • State the invariants before changing storage, write reconciliation, authorization, cache invalidation, or service lifecycle behavior.
  • Test failures at the boundaries where state changes: before and after commit, response loss, lock contention, cancellation while waiting, and removal or replacement of cached resources.
  • Preserve acknowledged durable writes across recovery, immutable version history, authorization scope, and consistency between documents and derived indexes. Represent unknown or partial outcomes explicitly.
  • Use deterministic scheduling and fault injection where possible. Collect operation histories and check them against the contract; use model-based or linearizability checks where the declared guarantee requires them. Race-detector success alone does not prove correct ordering.
  • A regression test must fail on the defective behavior and survive later refactors. Test-framework changes must preserve their ability to expose known bugs.
  • CI follows dependency direction: shared-module changes validate downstream consumers. Evaluate quality by demonstrated guarantees rather than file size, package count, or coverage percentage alone.

Apply this proportionately to demarkus's guarantees. This value does not require Raft, etcd's deployment complexity, paid testing services, or blanket fault suites for cosmetic changes. NATS Server is a secondary reference for bounded network-service lifecycles; the Go standard library is the reference for cohesive packages and precise APIs.

The initial required fixes are tracked in Refactor review debt. Future correctness work should strengthen these guarantees before adding abstraction for its own sake.

Architecture and layering

Core vs knowledge-system layering

The protocol core is permanent. Broker, universe, onboarding, gateway, MCP, and the plugins are disposable overlays. Default to layering above the core; only touch core if the feature survives throwing away every product built on top of it.

No core changes for plugin work

Plugins must reuse the existing server/client/protocol surface. Discuss before touching core for a plugin feature.

Repo conventions

Supported platforms

demarkus supports macOS, Linux, and WSL. WSL follows Linux behavior. Native Windows is not supported and must not receive Windows-specific implementation or test work unless Fritz explicitly changes scope.

Branch for every change: never commit directly to main

Every change starts on a feature branch. Never commit to main, never push to main directly. Land via PR so reviews and CI gates fire. This is true even for work that looks small or obviously correct; the value of the rule is that it is not negotiated per change. Push to main triggers auto-release (see /patterns.md §CI/CD), so a direct commit to main also skips the release sanity-check that a PR provides.

The first step of any code-touching task is git switch -c <branch> (or confirm we are already on one). When in doubt, ask before staging changes.

This rule was added 2026-06-08 after a broker change landed on main as a direct commit. See /journal/2026-06-08.md.

Utility binaries live in tools/

New sync, agent, or dev-ops binaries go in tools/, not client/cmd/.

Bump plugin pin versions on every update

plugins/claude-code/scripts/lib.sh SERVER / CLIENT / TOOLS_VERSION must move with every plugin change.

No external runtime deps in the plugin

The claude-code plugin is pure awk/bash. No jq, no python, no node at runtime; parse JSON in awk. (This governs the shipped hook/script runtime, not dev-only test helpers.)

No ${{ }} in GitHub Actions run: comments

GH Actions parses run: blocks for expressions before shell semantics. A stray ${{ inside a # comment fails the entire workflow.

No em dashes in user-facing strings

CLI flag help, error messages, prompts, and MCP tool descriptions never use em dashes. Use a semicolon, colon, or comma instead. Code comments follow the surrounding file's idiom. Added 2026-07-06 after em dashes landed in the agent's -publish-retention help text.

Code comments are terse

Added 2026-07-13 after a review pass found agent-written comments running to 10+ line essays.

A comment states only what the code cannot show: the why, an invariant, a platform caveat, a rejected alternative that will tempt the next reader. Target 1-3 lines. No narration of what the next line does, no restating the function signature, no design-document prose in doc comments; deep rationale belongs in the soul (debugging.md, plans) or the PR description, with the comment carrying the one-line conclusion.

Refactor-on-touch: when editing code whose existing comments are verbose, tighten them in the same edit. Scope this to the code being touched; no repo-wide comment churn commits.

Where architectural decisions live

Added 2026-08-17 after /soul-doctor found the soul had no ADR series for the core project. Revised 2026-09-28 at Fritz's request: the soul is the home of the series, not a mirror.

  • Protocol, spec, and repo-level decisions are ADRs in the soul at /adr/NNNN-<title>.md with type: Decision, rel-* metadata for supersedes or depends-on links, and a status line (proposed or accepted, dated) at the top. The soul copy is canonical; nothing is written to docs/adr/ in git for a new decision.
  • docs/adr/ in git is a frozen archive of ADR 0001 to 0027 as they stood when the series moved. It is not updated; the soul copy wins where they differ, and ADR 0027 is accepted in the soul while the git copy still reads proposed.
  • Code and commits reference a decision by its soul link (mark://soul.demarkus.io/adr/<file> and the https://soul.demarkus.io/t/u/... web form), which is what the commit-message rule below already asks for.
  • Sub-project decisions stay in the sub-project's own series (/demarkus-library/adr/).
  • Working notes about a decision (trail, rejected options, dated context) go in the plan or journal that produced it and link to the ADR. Do not record a decision only in a journal or in /thoughts.md.
  • The hub is /index/decisions.md; add a row when a new ADR lands and drop the (proposed) marker when it is accepted.

Added 2026-08-27 at Fritz's request. A commit message is a subject line plus a body containing only the relevant soul links (the mark://soul.demarkus.io/... URL and its https://soul.demarkus.io/t/u/... web form, typically the ADR or plan behind the change). No prose body; the rationale lives in the linked document, not the commit.

Prompt templates and tool descriptions are token budgets

Added 2026-09-04 after the terse sweep. Everything under plugins/prompt-source/ and every MCP tool description (client/cmd/demarkus-mcp, tools/internal/broker/mcp_tools_list.go) is paid in tokens: session guidance per session, command and skill bodies per invocation, tool schemas per request in hosts that eager-load them (Cursor). Rules: terse prose (fragments, no filler, no rationale a step already implies; rationale lives in the soul), behavior intact (every step, rule, path, flag, shell fence, output shape, template directive), nothing volatile (no timestamps, ids, counts; they break prompt-cache prefixes), no em dashes, regenerate and bump the affected lineage in the same PR. CodeRabbit path instructions and plugins/prompt-source/README.md carry the same rule.

Roadmap is a hub plus topic files

Added 2026-09-04 after /roadmap.md reached 55KB. /roadmap.md holds one row per item (status, link) and nothing else; entries live in /roadmap/<topic>.md as ## <Name>: <STATUS> (<dates, PRs>) sections. Append the entry to its topic file and add the hub row in the same edit. New topic file when an area has none or a file passes about 16KB; never a second catch-all. Section headings are link anchors, so a shipped entry keeps its title and gains dated lines. Full rule at /roadmap.md#format; the plugin's remember skill carries the same layout for every project.

Added 2026-09-04 after asking whether per-section hubs help or hinder the graph. They help when a hub is only links: backlinks land on the child and its section, the catalog ranks small focused documents, and mark_explore on the hub orients in one call. They hinder when a hub restates its children (status columns, summaries), because that second copy drifts, and when fan-out grows without bound, because every child becomes one hop from every other and relatedness signals wash out.

Rules for every hub (index.md, roadmap.md, any link page):

  • Links plus one line each. No content, status, dates, or summaries copied from children; the child is the single source.
  • Each child links back to its hub.
  • About 40 outbound documents, then a second-level hub. Anchored links into one document count once.
  • Real relations go in rel-* metadata, not hub adjacency.
  • Prefer generated hubs where the agent can build them, as with /graph.md and the hash indexes.

Revision to the roadmap rule above, same day: entry headings are the name alone and never change; Status: <active|planned|done|backlog> (<dates, PRs>) is the first line under the heading and is the only place status lives. The hub lists topic files and entries by name. The remember skill carries the same rules.

Promoted to mark://root/.well-known/demarkus/style.md@v1 via knowledge on 2026-09-04 (section only; the rest of this document stays personal). The shared copy is authoritative for hub and status-document structure.

Scope note, same day: the roadmap hub-plus-topic-files format above is this project's own practice. The shared rule, in the remember skill, the knowledge system's style guide and template, and the memory broker seed, is the generic hub rule only (plus "a document that outgrows one fetch splits into a hub plus topic files"); other projects lay out their roadmap as they see fit.

Edit a prompt procedure whole, never sentence by sentence

Added 2026-09-08 after PR #431, where six review rounds on one paragraph of the promote skill each fixed one sentence, and one of those fixes contradicted a rule three sentences away (incomplete-dedup named as a gate input while the same step said it stops the cascade).

A skill or command template is a procedure without a compiler; nothing checks that a rule added in one sentence agrees with the rest. Rules for any change under plugins/prompt-source/:

  • Read the whole step, and any step it hands off to, before changing it. A finding on one sentence is fixed by rewriting the step.
  • Every named state or outcome (incomplete-dedup, catalog-only, not-permitted) is defined once, at its first use, as terminal or carried forward; every later mention follows that definition. Grep the template for the name before adding a use.
  • Every call the step makes has a failure line in the same step; an added call brings its failure handling with it.
  • After the edit, read the step once more against the cascade rule at the top of the skill. Then regenerate and run pre-commit.
  • Review rounds that keep landing on the same paragraph mean the paragraph was patched, not rewritten. Stop and rewrite it.

Suggested commit messages are five words

Asked 2026-10-01: when Fritz asks for a commit message, give a conventional-commit type and scope followed by exactly five words, for example feat(knowledge): derive federation graph from changes. No body, no co-author.

One branch per piece of work, not per fix

When a feature branch is already checked out, follow-up fixes, review findings and small related changes go on that branch. Never open a new branch per fix or per finding unless the user asks for one: a pile of branches is friction, and every switch risks stranding uncommitted work. This narrows branch for every change, which is about never committing to main. Added 2026-10-14 after a suggested fix/style-size-warning branch.

trail
  1. soul.demarkus.io:6309 v30
  2. conventions