Skip to content

0.14.1: a small, documented public surface (exports map, dismissCMB, openStore, semanticEncoder switch) - #54

Merged
sym-bot merged 4 commits into
mainfrom
feat/0.14.1-public-surface
Oct 14, 2026
Merged

sym-bot merged 4 commits into
mainfrom
feat/0.14.1-public-surface

Conversation

@sym-bot

@sym-bot sym-bot commented Oct 13, 2026

Copy link
Copy Markdown
Owner

sym 0.14.1: a small, documented public surface

XMesh reached into lib/ by package path because 0.14.0 declared no exports map. This declares one, with only what XMesh's C0 work (sym-bot/xmesh#47, test/sym-boundary.test.ts) showed it needs, each name documented with what it promises and what it does not (README "Public API", docs/reference.md). Additive for everything that was supported; the wire is unchanged.

The surface

Subpath Exports
@sym-bot/sym unchanged: SymNode, migrateStores, version, rooms, emit, MeshAgent, llm/claude, identity, relocation, invite, platform
@sym-bot/sym/core exactly createCMB, renderContent, CAT7_CATEGORIES, FIELD_WEIGHT_PROFILES, encode, encodeCategory, encodeForSVAF, categoryKeyV1, kernelId, verifyCMB (the two constants are frozen copies; named imports work from ESM)
@sym-bot/sym/decision-log exactly DecisionLog
@sym-bot/sym/store exactly openStore(cmbsDir, name)
@sym-bot/sym/package.json the manifest

Anything else is refused with ERR_PACKAGE_PATH_NOT_EXPORTED (require, require.resolve, import). Bins unchanged.

The three things XMesh had no public API for

  • Dismiss. node.dismissCMB(key) (MMP 6.4) takes the same earned authority as validateCMB (6.5, validator or above) and returns { ok, reason? }. MemoryStore.dismissCMB takes the caller's authority the same way (it was ungated and returned nothing; nothing in sym called it).
  • Open a stopped identity's store. openStore never creates the directory (ENOSTORE) and returns a frozen handle of get / validateCMB / dismissCMB. The store is found with the main entry's identity.nodeDirById(identity.nodeIdForName(name)) + cmbs. The handle cannot check the authority its caller states (a store is files); README and SECURITY.md say so.
  • The eager semantic encoder. semanticEncoder: 'eager' | 'off' and SYM_SEMANTIC_ENCODER=off. Off: nothing loads the model, SVAF and 15.8 measure in the n-gram kernel, remix() mints with its lineage, unverified, at once (does not wait for a model that will never load).
  • svafEvaluator and cliHostMode are documented (constructor JSDoc, README, docs/reference.md) and tested.

Decisions to look at

  1. Requiring sym no longer starts the model load. It was a side effect of require, so a constructor option could never say no. The load now starts when a node is built or when the kernel is first used. Every host builds a node, so the default behaviour is unchanged in practice; a process that only requires sym (sym --version) loads nothing.
  2. The encoder is one per process, so 'off' is a process setting: any node built with it (or the env var) turns it off for every node and it stays off; a later 'eager' does not turn it back on (the node says so).
  3. dismissCMB is gated (validator or above), as the spec says (6.5), rather than exposing the old ungated store method.
  4. The package ships no .d.ts; XMesh keeps its own ambient declarations, renamed to the new subpaths.
  5. docs/reference.md now ships in the tarball, because the shipped README and CHANGELOG point at it.

Compatibility (the exports map restricts deep imports, which were never supported)

Hit by 0.14.1 when they move to it, none shipped:

  • XMesh feat/0.12.0-sym-014 (draft Release 0.13.14 — a peer's admission verdict is observable as it lands #47): the 7 blocked sites move to the subpaths above (the daemon IPC client is dropped by XMesh); its tests deep-import lib/core/index.js (signCMB, assertionIdV2_0...), lib/core/verified-receipt.js, lib/core/state-root, lib/core/context-encoder.js, lib/memory-store and need the exported package.json to find the file by path.
  • sym-014-relayfix and sym-relayfix branches: the same stock-nodes gate driver imports lib/discovery.js by package path (fixed here in main's copy).
  • xmesh-world-core and xmesh-grid-web (pinned to sym 0.13.9): proof/level1.ts and test/oracle.test.ts deep-import cmb-encoder, cmb-signing, context-encoder, lineage-tether, tether-attestation. Not shipped; hit only if they bump.
  • sym-mesh-channel main (fix/0.10.2): its tests import lib/node.js, lib/core/cmb-encoder.js, lib/core/cmb-signing.js; its server.js and bin/install.js fall back to lib/rooms.js only when sdk.rooms is missing (never, on 0.14).

Not hit: sym-mesh-channel 0.10.1 and XMesh 0.11.0 (both depend on ^0.13), the published sym-mesh-channel 0.11.0 (main export and ./package.json only), xmesh-core, xmesh-grid-api, sym-llm-sidecar, sym-core, xmesh-station, xmesh-release (^0.13.15), xmesh-agent (retired, ^0.13.16). xmesh-world-web's handover scripts load sym by absolute file path, which exports does not restrict.

Verification

  • npm test: 1472 tests, 1469 pass, 0 fail, 3 skipped (run under the shared heavy lock).
  • npm run gate:stock-nodes: passed, and now also asserts the surface on the installed tarball.
  • npm audit: 0 vulnerabilities. npm pack --dry-run: 87 files, 576 kB.
  • New tests: public-surface (packs the tarball, checks every documented subpath and its exact names, private paths refused, main entry/package.json/both bins, README names what the code exports; 11 mutations must each be reported), lifecycle-dismiss, semantic-encoder-switch (child processes with a counting stand-in for the model, so "nothing loads" is a count of zero), host-options. Mutating the code under each (load ignores off, dismiss ungated, node asserts authority, cliHostMode ignored) fails the matching test.

Not merged, published or tagged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VMk5HbMaz4oD7zoakvPyA6

sym-bot and others added 3 commits October 13, 2026 22:24
…er switch

An embedding host (XMesh) reached into lib/ by path because the package declared no
public surface. This declares a small one, and adds the three things it was missing:

- package.json "exports": ".", "./core" (ten CMB-library names, constants frozen),
  "./decision-log" (DecisionLog), "./store" (openStore), "./package.json". Every other
  path is refused with ERR_PACKAGE_PATH_NOT_EXPORTED. Bins unchanged.
- node.dismissCMB(key) (MMP 6.4), gated on the same earned authority as validateCMB
  (6.5), returning { ok, reason? }. MemoryStore.dismissCMB takes the caller's authority
  the same way and no longer returns nothing.
- openStore(cmbsDir, name): the store of a stopped identity, never created, a frozen
  handle of get / validateCMB / dismissCMB.
- semanticEncoder: 'eager' | 'off' and SYM_SEMANTIC_ENCODER=off. Requiring sym no
  longer starts the model load; a node starts it when built, unless the process is off.
  Off: nothing loads, SVAF and section 15.8 measure in the n-gram kernel, remix() mints with
  its lineage, unverified, at once.
- svafEvaluator and cliHostMode documented in the constructor JSDoc and tested.

The stock-nodes gate driver reaches NullDiscovery by its place on disk and now also
asserts the surface on the installed tarball.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMk5HbMaz4oD7zoakvPyA6
…code

README "Public API" says, for each subpath, what is promised and what is not.
docs/reference.md has the recipes, what replaces each deep import a host used, the host
options and the semantic encoder switch. CHANGELOG 0.14.1 lists the subpaths and says the
exports map refuses deep imports, which were never supported. SECURITY.md says an opened
stopped store cannot check the authority its caller states. docs/reference.md now ships,
because the shipped README and CHANGELOG point at it.

tests/public-surface.test.js packs the tarball, unpacks it with the repository's
dependencies beside it, and checks, on that copy: every documented subpath resolves and
exports exactly the documented names (also by name from ESM), no underscore name, a private
path is refused with ERR_PACKAGE_PATH_NOT_EXPORTED by require, require.resolve and import,
the main entry, ./package.json and both bins work, and the README names what the code
exports. Eleven mutations (an extra name, a missing one, an underscore name, a new subpath,
a lib/ wildcard, no exports map, no ./package.json, a dropped main name, a DecisionLog
method, an unfrozen constant, a README that drifts) must each be reported.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMk5HbMaz4oD7zoakvPyA6
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMk5HbMaz4oD7zoakvPyA6
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 13, 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-13T22:08:21.160407Z 61b7541 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: 61b7541226

ℹ️ 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 on lines +104 to +105
(function readEnvironment() {
const v = process.env.SYM_SEMANTIC_ENCODER;

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 Read the environment setting when the node is built

When a host requires the package, then sets process.env.SYM_SEMANTIC_ENCODER = 'off' before constructing its first node, this module-level IIFE has already captured the old value. The constructor consequently remains in eager mode and starts the heavy model load, despite the documented contract that the environment variable may be set before the first node is built. Re-read or otherwise apply the environment setting at node construction time.

Useful? React with 👍 / 👎.

…comparison

0.14.0 removed a stale socket file only if its inode number was still the one it had probed. Linux
reuses a removed file's inode number at once, so the guard passed for a socket another daemon had just
bound: tests/ipc-listen-exclusive failed on Linux CI (main, Node 20 and 22) and passed on macOS. Reproduced
in a Linux container; the same inode number came back after unlink and bind.

The removal and the rebind now hold a start lock beside the socket (a directory naming its owner's pid;
a dead owner's, or one older than 10 s, is broken). The test runs eight starters at one stale file, checks
that a live holder's lock is respected, and that a dead owner's and a reused-pid lock are broken. It passes
on Linux (Node 20 and 22) and on macOS; without the lock four of the tests fail.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VMk5HbMaz4oD7zoakvPyA6
@sym-bot
sym-bot merged commit 980b3b9 into main Oct 14, 2026
2 checks passed
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