Skip to content

0.14.3 records: actions by assertion, per-assertion summaries, well-formed text, in-memory recall (S3, B2, M2, M1) - #63

Open
sym-bot wants to merge 3 commits into
release/0.14.3from
fix/0.14.3-records
Open

sym-bot wants to merge 3 commits into
release/0.14.3from
fix/0.14.3-records

Conversation

@sym-bot

@sym-bot sym-bot commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Four defects the independent review of release/0.14.3 head 9f6847c found (S3, B2, M2, M1), reproduced against 0.14.2 and the candidate, each fixed at the design assumption that made it. Into release/0.14.3; not merged, tagged or published.

S3 (R2-6): actionable records are messages, not memories

An actionable record (non-null application) is identified by its assertion everywhere: memory keeps one record per cognition key (§8.8.2), and the action is sent, de-duplicated and delivered by its assertion identity.

  • Receiver: the one delivery decision (_surfaceUnstored) now runs on every branch where memory stored nothing, the SVAF admit branch included. 9f6847c surfaced only a directed record there, so an actionable room record SVAF admitted under a key the store held reached nobody.
  • Sender: remember() sends an action whose cognition memory holds (HEAD's, an earlier action's, a peer's) by its own assertion (_sendAction: nothing stored again, HEAD unmoved, cognitionHeld: true, a delivery report), and an action whose store write failed. The same assertion given twice is a duplicate; a true replay is delivered once.

B2 (R2-8): attestation summaries are per assertion

aggregateAttestations summarises one named assertion: given, held, or the sole one its attestations name (a node without the record: XMesh's cliHostMode observers, 9f6847c total 0). Several: no single verdict, byAssertion has each. Adds basis, assertions, byAssertion; old fields unchanged. R2-8 kept: an attestation about another assertion is never a vote on this one.

M2 (R2-13): well-formed text is normalised, not refused

The signed object is UTF-8 bytes, where a lone surrogate can only be U+FFFD. Every string a record signs is brought to its well-formed spelling at each boundary (createCMB, a caller's record in remember(), the canonical projection, the Legacy Import route, the public verifier, the store's read). A 0.14.2 author's text cut mid-emoji verifies and is held as one string. The signing step still refuses a string no boundary normalised. sym's own cuts fall on code point boundaries (lib/core/text.js).

M1 (R2-3): recall is not disk-bound

recall() matches on a bounded search index kept apart from the entry cache, and reads only the entries it returns. recall(query, { limit, where }), total. Scans do not fill the LRU; the cache starts with the newest entries; the ratchet and the lifecycle are read from the index.

Tests

  • tests/security-0.14.3-records.test.js: 13 tests, with real nodes on the wire paths. All 13 fail on 9f6847c and pass here.
  • tests/integration/mixed-0142-records.js: 4 tests against a real 0.14.2 tree (SYM_0142_DIR). All 4 fail on 9f6847c and pass here.
  • Mutation: 34 mutants, one per new guard; all 34 caught; the no-mutation control passes.
  • Full suite on the final commit (macOS, concurrency 1): 1664 tests, 1660 pass, 0 fail, 4 skipped. The first run's one failure, daemon-relay-only (a non-string recall query must throw), is fixed in 6d8ebb8, which has its own mutant.
  • Linux docker, node:22-slim (22.23.3) and node:20-slim (20.20.2), on the new and changed test files plus the mixed 0.14.2 test: 90/90 each. GitHub CI, Node 20 and 22: green.
  • Integration 10/10. Legacy-0.13 skipped: no 0.13.17 checkout.
  • gate-two-stock-nodes passed. npm audit: 0 vulnerabilities.

Recall, measured (one call, macOS, short entries)

store query 9f6847c this branch 0.14.2
6,000 miss 74-103 ms 1-2 ms 2-4 ms
6,000 1,111 matches 74-86 ms 28-38 ms 3-5 ms
6,000 limit: 10 n/a 2 ms n/a
50,000 miss 5.0-5.7 s 9-13 ms 20-26 ms
50,000 11,111 matches 5.0 s 0.3-0.7 s 32 ms
50,000 recall(''), no limit 5.1 s 2.9 s 21 ms

Heap after load: 8.4 → 10.4 MB at 6,000 entries, and 28.9 → 50.7 MB at 50,000 (0.14.2: 18 and 147 MB). The search index holds 2.4 M and 20.1 M characters.

Dependents

  • XMesh 0.12.1: no change needed. Its observers now get the summary (total 1, basis: 'sole') instead of 0.
  • mesh-channel 0.11.1: no change required. emitOutcome reads an action whose cognition memory holds as published / sent, and remixed: false room deliveries as "delivered only". Recommended: node-host.recall should pass { limit } and use total, so a broad query on a large store does not read every match.

Known

  • 0.14.2 receivers still de-duplicate actionable room records by cognition key, so they deliver a 0.14.3 author's second action under the same categories only once. That is their defect; the mixed test shows the session stays clean.
  • An unlimited broad recall still reads what it returns.

…ormed text, in-memory recall

Four defects the review of 9f6847c found, each fixed at the design assumption that made it.

S3 (R2-6). An actionable record (a signed application section) is a message as well as a
memory: memory keeps one record per cognition key, and the action is identified by its
assertion identity wherever it is sent, de-duplicated and delivered. The frame handler's one
delivery decision (_surfaceUnstored) is now taken on every branch where memory stored nothing,
the admit branch included (a record SVAF admitted under a key the store held reached nobody).
remember() sends an action whose cognition memory holds (HEAD's, an earlier action's, a peer's)
by its own assertion (_sendAction: nothing stored again, HEAD unmoved, `cognitionHeld: true`
and a `delivery` report), and an action whose store write failed; the same assertion given
twice is a duplicate. lib/record.js: recordActionable, recordAssertion.

B2 (R2-8). aggregateAttestations summarises one named assertion: given, held, or the sole one
the attestations under the key name (a node without the record: XMesh's observers); several
give no single verdict and byAssertion has each. Adds basis, assertions, byAssertion; the old
fields stay.

M2 (R2-13). The signed object is UTF-8 bytes, where a lone surrogate can only be U+FFFD: every
string a record signs is brought to its well-formed spelling at each boundary (createCMB, a
caller's record in remember(), the canonical projection, the Legacy Import route, the public
verifier, the store's read) instead of refusing it, so a 0.14.2 author's text cut inside a pair
verifies and is held as one string. The signing step still refuses a string no boundary
normalised. sym's own cuts (message preview, wire names, SVAF text, context, error texts,
decision labels) fall on code point boundaries (lib/core/text.js: cutText).

M1 (R2-3). recall() matches on a bounded search index kept apart from the entry cache (each
entry's lowercased text, 4,096 characters at most, 32 M in all, the oldest let go first) and
reads only the entries it returns; recall(query, { limit, where }) and `total`. A scan reads
through the cache, never into it; the cache starts with the newest entries; the timestamp
ratchet and the lifecycle are read from the index. The primer, daemon, interior and mesh agent
pass limits.

Tests: tests/security-0.14.3-records.test.js (13, real nodes on the wire paths) and
tests/integration/mixed-0142-records.js (4, against a real 0.14.2 tree, SYM_0142_DIR) fail on
9f6847c and pass here. R2-13's tests and remember-payload's collapse test now state the design
as built.
The search-index recall read a non-string query as none and returned every entry; the
store's old scan threw on it by accident (query.trim), which tests/daemon-relay-only relies on
to see a throwing handler answered. A TypeError now, said on purpose; null, undefined and ''
are still no query.
…the review of 9f6847c

Each text states the design as built: actionable records sent and delivered by assertion with
memory keeping one record per cognition key; attestation summaries about one named assertion;
well-formed text normalised at every boundary, not refused; recall matched on a bounded index
apart from the entry cache, reading only what it returns, with the numbers measured at 6,000
and 50,000 entries and the memory used.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-14T18:47:54.081223Z 3cfbaf8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3cfbaf8cde

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/memory-store.js
if (t && !seen.has(t) && !content.includes(t)) { seen.add(t); parts.push(t); }
}
}
return parts.join('\n').toLowerCase();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve spaces between indexed search fields

When a multi-word query spans adjacent fields, such as an entry tagged with ['alpha', 'beta'], the previous search matched alpha beta because it combined fields with spaces, while this newline separator makes that query return no results. This silently changes recall() semantics for phrases spanning tags, content, keys, or category fields; retain the prior space separator or normalize whitespace consistently in both indexed text and queries.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant