Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/skills/sym/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Every change you make to SYM.BOT code MUST be production quality:
- **Fix what you find.** If you spot design issues, missing spec comments, or messy logic — fix them in the same change. No "TODO" or "FIXME" left behind.
- **Spec compliance.** Code must match the MMP spec. Reference section numbers in comments where relevant.
- **Clean architecture.** Each module has one responsibility. Proper error handling — not swallowed.
- **No tech debt.** The founder is solo. Sloppy code today is a blocker tomorrow. The protocol is the product — the implementation must be as rigorous as the spec.
- **No tech debt.** Sloppy code today is a blocker tomorrow, and the project has no spare capacity to pay it back. The protocol is the product — the implementation must be as rigorous as the spec.

## Behaviour

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/sym/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Every change you make to SYM.BOT code MUST be production quality:
- **Fix what you find.** If you spot design issues, missing spec comments, or messy logic — fix them in the same change. No "TODO" or "FIXME" left behind.
- **Spec compliance.** Code must match the MMP spec. Reference section numbers in comments where relevant.
- **Clean architecture.** Each module has one responsibility. Proper error handling — not swallowed.
- **No tech debt.** The founder is solo. Sloppy code today is a blocker tomorrow. The protocol is the product — the implementation must be as rigorous as the spec.
- **No tech debt.** Sloppy code today is a blocker tomorrow, and the project has no spare capacity to pay it back. The protocol is the product — the implementation must be as rigorous as the spec.

## Behaviour

Expand Down
16 changes: 8 additions & 8 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ carried (cutoffs, ratification, budgets per delegation subtree, `role-chain-fetc

### MMP 2.0 update 1: the drafts this release implements, as the spec now states them

The founder ruled that the drafts sym 0.14.0 implements land in the published spec before it ships.
The drafts sym 0.14.0 implements land in the published spec before it ships.
They are folded into one update, MMP 2.0 update 1 (sym-bot/meshcognition-website PR #43, merged to main at
c07ff20: #24, #25, #17, #22, #26, #30, #23, #31, #37, #34, #35, #27 and #28 on the §6.6 errata), and sym is aligned with its text, its reference
construction (scripts/mmp/lib.mjs) and its schemas and vectors, vendored in tests/fixtures with their
Expand All @@ -653,7 +653,7 @@ sym-attest-v1 pass case by case, and every frame sym builds validates against th
`detail` names this node's session or a probe ping is outstanding, while no newer session is
waiting to supersede, and no faster than the retry backoff (fifty forged 1011s with no detail
bought three re-handshakes in five seconds; now none). A sealed ping is answered with a pong.
- **The mood frame (founder ruling).** `{ type, mood, context, timestamp }`: mood 1 to 1,024
- **The mood frame.** `{ type, mood, context, timestamp }`: mood 1 to 1,024
characters, context at most 4,096 or null; no `from` or `fromName` (the receiver labelled the event
with the payload's name); a frame carrying them is refused. `broadcastMood` throws `EMOODFRAME`
rather than send one out of bounds.
Expand Down Expand Up @@ -720,7 +720,7 @@ sym-attest-v1 pass case by case, and every frame sym builds validates against th

### Breaking: `remix()` is asynchronous, and §15.8 runs on what it produces

By the founder's ruling, §15.8 is enforced where the spec puts it, on the remixing node, in 0.14.0.
§15.8 is enforced where the spec puts it, on the remixing node, in 0.14.0.
`node.remix(categories, opts)` now evaluates the remix against its nearest verified lineage root
(content-only, one kernel, α-weighted drift against Tguarded) before it is minted: a faithful remix
keeps its lineage; one drifted past the floor is minted as a fresh root, with no parent from the
Expand Down Expand Up @@ -774,8 +774,8 @@ key (below), whose records are quarantined. Upgrade every node of a room togethe
peer sent it, under the key that session proved (the frame is sealed on the session); it is not a
signed record. Nothing about it can be verified again later, relayed with its author's signature,
or attributed to anyone but that peer. A host shows it as the peer's word, not as a verified record.
The mood frame is `{ type, mood, context, timestamp }` (MMP 2.0 update 1, by the founder's
ruling): it names no sender, and `mood-delivered.from` is the session's proven name.
The mood frame is `{ type, mood, context, timestamp }` (MMP 2.0 update 1): it names no
sender, and `mood-delivered.from` is the session's proven name.

### The loaded version, and hot-swap

Expand All @@ -788,7 +788,7 @@ key (below), whose records are quarantined. Upgrade every node of a room togethe
own, redelivers nothing the peer re-pushes, keeps every earlier id fetching its record, and logs no
null dereference (on 0.13 this broke as "Cannot read properties of null (reading 'close')").

### Redelivery and inbox ids (founder's bug report, 2026-10)
### Redelivery and inbox ids (bug report, 2026-10)

- **A delivery is surfaced once for the life of the store.** After a hot-swap (a new SymNode for the
same identity, as mesh-channel's `sym_join_room` builds) or a reconnect, records already received
Expand All @@ -812,7 +812,7 @@ key (below), whose records are quarantined. Upgrade every node of a room togethe

### The final re-review of f0d936a (BLOCK) — what changed

The founder ruled three design changes. Rulings A and B (what grants are kept, and what a revoked
Three design changes were settled (rulings A, B and C). A and B (what grants are kept, and what a revoked
key may still sign) are met by MMP §6.6 itself (above): authority is a function of the statements
held, and no statement carries a time that could place it inside a period of trust.

Expand Down Expand Up @@ -2350,7 +2350,7 @@ disconnected.

### Changed

- **Room names must be canonical** (founder ruling 2026-08-27): one name per
- **Room names must be canonical** (decision 2026-08-27): one name per
room, one room per name. `isValidRoom` now requires the round trip through
the service type to be identity, so **`sym` is refused**. It satisfies the
kebab grammar, maps to `_sym._tcp`, and comes back as `default` — a second
Expand Down
4 changes: 2 additions & 2 deletions docs/DESIGN-room-join-authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
> key, a gated room admits only its owner. Nothing in the estate gates a room today — no owner pins
> on any node — so this closed a hole rather than breaking a user.

**Date:** 2026-08-26 · **Author:** dev1 (sym runtime lane) · **Status:** v2 — mesh review SHIP-WITH-FIXES folded (x-review cmb-3e58586f…, 12 findings, all applied); FOUNDER RULED 2026-08-26: (1) grammar fix NOW, ahead of the gate; (2) ADOPT B AS FOLDED (owner-only, 24h lifetime, join-time expiry, handshake enforcement, default+sym public). Implementation = ordinary mesh-reviewed sym-lane work
**Prior:** the C+D standing-rooms ruling (xmesh docs/DESIGN-tenancy-standing-rooms.md, founder
**Date:** 2026-08-26 · **Author:** dev1 (sym runtime lane) · **Status:** v2 — mesh review SHIP-WITH-FIXES folded (x-review cmb-3e58586f…, 12 findings, all applied); DECIDED 2026-08-26: (1) grammar fix NOW, ahead of the gate; (2) ADOPT B AS FOLDED (owner-only, 24h lifetime, join-time expiry, handshake enforcement, default+sym public). Implementation = ordinary mesh-reviewed sym-lane work
**Prior:** the C+D standing-rooms ruling (xmesh docs/DESIGN-tenancy-standing-rooms.md,
2026-08-26) names this as its residual: "sym resolves rooms by string … full closure needs
sym-side join authorization." This is that design.

Expand Down
10 changes: 5 additions & 5 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ const { SYM_STATE_DIR: SYM_DIR } = require('./core/state-root');
/** DER/PKCS8 Ed25519 private-key header — the 16 bytes preceding the raw 32-byte key. */
const ED25519_PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');

/** The founder's ruling, carried verbatim into the error an operator actually sees. */
/** The rule behind the halt, stated in the error an operator actually sees. */
const IDENTITY_RULING =
'An agent may lose its machine and recover its data, but if it loses its private key, it ' +
'cannot prove that it is the same agent. Therefore the key must be preserved, and the ' +
'system must refuse to start rather than silently create a replacement identity.';
'An agent can lose its machine and still recover its data, but an agent that loses its ' +
'private key can no longer prove it is the same agent. The key must therefore be kept, and ' +
'the system refuses to start rather than silently create a replacement identity.';

/**
* Raised instead of minting a replacement identity.
Expand Down Expand Up @@ -2064,7 +2064,7 @@ function acquireLockAt(lockPath, meta = {}, depth = 0) {
}

/**
* NAME-SUFFIXING IS DELETED (founder ruling, agent id = node id).
* NAME-SUFFIXING IS DELETED (agent id = node id).
*
* The name-suffixing resolver used to answer a same-host collision by returning
* `<base>-2`, `-3`, … That was never collision handling. An agent id names an AGENT, and
Expand Down
2 changes: 1 addition & 1 deletion lib/core/cmb-encoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function categoriesPreimage(categories) {
}

/**
* The prefix every key is minted with, FROZEN (founder canon §17). Post-cutover there is one
* The prefix every key is minted with, FROZEN (canon §17). Post-cutover there is one
* form — bare `cmb-` + 64-hex — and no dual-emit window (§19.1/§20): the freeze window plus the
* fleet census make an emission flag unnecessary, and a flag IS the backcompat §19 forbids. The
* prefix says only "this is a CMB"; the scheme lives inside the signed payload, and any future
Expand Down
2 changes: 1 addition & 1 deletion lib/core/cmb-signing.js
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ function verifyCMB(cmb, publicKeyB64url) {
: { signed: true, valid: false, error: 'legacy-key-rejected' };
}
// UNREACHABLE BY MEASUREMENT — the legacy verification arm that stood here is DELETED
// (CTO ruling, 2026-08-07). Every record with `.metadata` returns from the v2 branch above;
// (decision 2026-08-07). Every record with `.metadata` returns from the v2 branch above;
// every record with a top-level `.sig` and no `.metadata` returns `unverified-legacy` from
// the grandfather branch. Nothing could reach what followed, so nothing behind this line
// ever ran.
Expand Down
4 changes: 2 additions & 2 deletions lib/core/mood-frame.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

/**
* @module sym/core/mood-frame
* @description The `mood` control frame (MMP §9.3; control-frame.schema.json, MMP 2.0 update 1 by the
* founder's ruling): `{ type, mood, context, timestamp }`, sealed on a confirmed Core Secure session.
* @description The `mood` control frame (MMP §9.3; control-frame.schema.json, MMP 2.0 update 1):
* `{ type, mood, context, timestamp }`, sealed on a confirmed Core Secure session.
*
* mood 1 to 1,024 characters
* context at most 4,096 characters, or null
Expand Down
12 changes: 6 additions & 6 deletions lib/core/record-shape.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@
function resolveCategories(record) {
if (!record) return null;
// ONE NAME. `categories` is the container and the only container. This used to walk a candidate
// list so a record could carry it under either name — that is the name-check logic the founder
// ruled out (2026-08-09, "remove all name check logic"), and removing it is what makes this
// module's promise true rather than aspirational. With one name there is no precedence to get
// wrong, and no second reader that can disagree with this one.
// list so a record could carry it under either name — that is the name-check logic removed on
// 2026-08-09, and removing it is what makes this module's promise true rather than
// aspirational. With one name there is no precedence to get wrong, and no second reader that
// can disagree with this one.
//
// A record carrying only the retired name does not resolve, and that refusal is the point: it
// is legible at the boundary rather than half-read further in.
Expand All @@ -76,7 +76,7 @@ function requireCategories(record, where) {
* requireCategories so that "I can cope with absence" is stated at the call site rather than
* implied by a truthiness test.
*
* USAGE RULE — BINDING (CTO ruling, 2026-08-07). `hasCategories` MAY ONLY BE USED WHERE THE
* USAGE RULE — BINDING (decision 2026-08-07). `hasCategories` MAY ONLY BE USED WHERE THE
* FALSE BRANCH RETURNS A VERDICT. It must NEVER guard a block that control can fall out of.
*
* if (!hasCategories(r)) return { valid: false, error: 'cannot-verify: …' }; // CORRECT
Expand All @@ -94,7 +94,7 @@ function hasCategories(record) {

/** WHY the categories could not be read, as three distinct states rather than one falsy.
*
* The distinction the CTO measured (2026-08-07): an E2E record verified BEFORE decryption
* The distinction measured on 2026-08-07: an E2E record verified BEFORE decryption
* carries its container as a base64 CIPHERTEXT STRING with `_e2e` attached. `hasCategories`
* correctly reports false — you must not be able to content-verify ciphertext — but reporting
* that as "record carries no CAT7 container" MISDESCRIBES it. The container is right there; it
Expand Down
2 changes: 1 addition & 1 deletion lib/core/room-grant.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* @module sym/core/room-grant
* @description Ed25519-signed ROOM-JOIN grants — membership as a right, not as
* knowledge of a string (docs/DESIGN-room-join-authorization.md; founder ruling
* knowledge of a string (docs/DESIGN-room-join-authorization.md; decision
* 2026-08-26, option B as folded).
*
* WHAT THIS EXISTS TO STOP, measured rather than imagined: a room is a NAME, and
Expand Down
2 changes: 1 addition & 1 deletion lib/frame-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -1998,7 +1998,7 @@ class FrameHandler {
*
* Identified 2026-07-05 in the liquid-substrate scope note, which ranked it bug-grade and
* independent of any learning work; still present in published 0.12.1 when re-checked
* 2026-08-24. Founder decision D5: ship this fix on its own, and HOLD the recurrent-cell
* 2026-08-24. Decision D5: ship this fix on its own, and HOLD the recurrent-cell
* dimension until the learning testbed says what shape it should be.
*
* WHAT THIS IS NOT, and the honesty matters more than the fix. This is still a STATELESS
Expand Down
2 changes: 1 addition & 1 deletion lib/interior.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
* deliveries that verified — a record signed to this node, or a room broadcast it admitted — never
* a quarantined one. A recall item is { key, record, verified, storedAt, author }.
*
* THE READ SIDE IS SCOPED TO THE MIND'S MISSION (final re-review, Finding 6; the founder's ruling C).
* THE READ SIDE IS SCOPED TO THE MIND'S MISSION (final re-review, Finding 6; ruling C).
* A mind is the node's reasoning for ONE mission, and must not read what the node received for
* another:
* - each mind has its own READ VIEW: the deliveries that arrived while it runs (after its start),
Expand Down
10 changes: 5 additions & 5 deletions lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ class SymNode extends EventEmitter {

// Retention — how long to keep CMBs in local storage.
//
// DEFAULT: UNLIMITED (founder ruling 2026-07-20). Accumulated cognition is the
// DEFAULT: UNLIMITED (decision 2026-07-20). Accumulated cognition is the
// most valuable asset a node holds; deleting it on a timer destroys the very
// thing the system exists to build. The previous 86400s (24h) default was
// silently pruning authority-bearing history — observed live: a deputy store
Expand Down Expand Up @@ -679,7 +679,7 @@ class SymNode extends EventEmitter {
this._inboxSeq = 0;
this._inboxCursor = 0;
this._inboxMax = opts.inboxMax || 500;
// DURABLE (founder ruling 2026-08-04): communication is addressed to the
// DURABLE (decision 2026-08-04): communication is addressed to the
// NODE, and a new session RELINKS to it — including what was delivered
// while no session was attached. The inbox was process memory only, so a
// restart wiped the delivery feed while every sender believed it had
Expand Down Expand Up @@ -2932,7 +2932,7 @@ class SymNode extends EventEmitter {
oldestUndrainedAt: undrained.length ? (undrained[0].receivedAt ?? null) : null,
// Distinct from "has a backlog": a node that has NEVER been drained is
// not behind, it is unattended — the condition that hid five directed
// CMBs, two of them founder words, for three days.
// CMBs for three days.
neverDrained: this._inboxCursor === 0 && this._inboxSeq > 0
&& !(this._inbox.length > 0 && this._inbox.every((m) => m.acked)),
dropped: this._metrics.inboxDropped ?? 0,
Expand Down Expand Up @@ -3219,7 +3219,7 @@ class SymNode extends EventEmitter {
// was not connected wrote the CMB locally, logged a single line, and returned
// an entry INDISTINGUISHABLE from a delivered one; every layer above reported
// success for sends that reached nobody. Measured cost (bl-a6e63608c8c): five
// directed CMBs to one seat — including a founder authorization and a founder
// directed CMBs to one seat — including an authorization and a
// question — undelivered for three days while both senders believed otherwise,
// each building a prose relay workaround around a channel reporting success.
//
Expand All @@ -3237,7 +3237,7 @@ class SymNode extends EventEmitter {
// so it counts frames handed to a transport, not frames any peer received.
// Naming that "delivered" would be this exact class of overclaim one layer
// up: a count bound to something other than what its name asserts. The word
// `delivered` stays reserved until an ack earns it (CTO gate on a371021).
// `delivered` stays reserved until an ack earns it (gate on a371021).
//
// `undelivered` is still sound as written, and is the load-bearing category:
// never-dispatched does entail never-delivered, even though the converse
Expand Down
2 changes: 1 addition & 1 deletion lib/room-ownership.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* @module sym/room-ownership
* @description Which key owns which room — the receiver's local answer to
* "may this peer be here?" (docs/DESIGN-room-join-authorization.md, founder ruling
* "may this peer be here?" (docs/DESIGN-room-join-authorization.md, decision
* 2026-08-26).
*
* WHAT OWNERSHIP IS FOR. Without an owner, a room's membership is knowledge of its
Expand Down
7 changes: 3 additions & 4 deletions tests/core-secure-final-review.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@
require('./_isolate-home'); // redirect $HOME before lib/config loads

/**
* The final re-review of f0d936a (BLOCK): the founder's three rulings and the findings beside them,
* each as a regression test that fails on f0d936a. The reviewer's repros are in
* /private/tmp/claude-501/-Users-hongwei-sym-agent-a/sym-014-final-review/ (grant-probes.js P1-P4,
* node-probes.js Q1, digest-churn.js, fork-a, fork-b).
* The final re-review of f0d936a (BLOCK): the three design changes (rulings A, B and C) and the findings
* beside them, each as a regression test that fails on f0d936a. The reviewer's repros (grant-probes.js
* P1-P4, node-probes.js Q1, digest-churn.js, fork-a, fork-b) were written outside this repository.
*
* A, B retired with the time-replay grant rule (MMP §6.6; see below)
* C the interior read side is scoped to the mind's mission (Finding 6)
Expand Down
2 changes: 1 addition & 1 deletion tests/delivery-ledger.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require('./_isolate-home'); // redirect $HOME before lib/config loads

/**
* The redelivery and inbox-id bugs (founder, via agent-b, 2026-10; mesh-channel 0.10.1 on sym 0.13.16,
* The redelivery and inbox-id bugs (reported via agent-b, 2026-10; mesh-channel 0.10.1 on sym 0.13.16,
* and reproduced on 0.14.0 at 6510bac):
*
* Bug 1: after a hot-swap (a new SymNode for the same identity) or a reconnect, CMBs already
Expand Down
2 changes: 1 addition & 1 deletion tests/directed-delivery-accountability.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require('./_isolate-home'); // redirect $HOME to a temp sandbox before lib/confi
* the seat) therefore reported success for sends that reached no one.
*
* Live consequence: five directed CMBs addressed to the dev seat — including
* a founder authorization and a founder question — sat undelivered for three
* an authorization and a question — sat undelivered for three
* days while every sender believed it had delivered. Two seats independently
* built prose workarounds ("RELAY FOR DEV") around a channel that was
* reporting success.
Expand Down
2 changes: 1 addition & 1 deletion tests/durable-inbox.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

/**
* Durable delivery inbox (founder ruling 2026-08-04): communication is
* Durable delivery inbox (decision 2026-08-04): communication is
* addressed to the NODE, and a new session RELINKS to it — including what
* was delivered while no session was attached. Before this, the inbox was
* process memory only: a restart wiped the delivery feed while every sender
Expand Down
Loading
Loading