Mailboard: a shared, unaddressed board for loops (beta-ramped) - #229
Mailboard: a shared, unaddressed board for loops (beta-ramped)#229scgopi wants to merge 5 commits into
Conversation
node send and edges are addressed — a loop must already know a peer's id. The Mailboard is the ambient counterpart: one append-only board per graph (global graph included) that any loop can post to for whoever comes next — a decision made, a dead end hit, a claim staked — and read back with one command, with no wiring and no ids. Posts survive their authors; a loop created after the writer is gone still finds the note. - MailboardKit module: post/watch model, caps (1 KiB body, 200 posts), unread arithmetic shared by every surface - Board rides LoopGraph: one writer, persisted beside the graph, snapshot in every .graphChanged (the CLI's read path), graphcode://global free - GraphStore: mailboard post/sync/watch commands; watcher wakes ride deliverAdHocMessage's follow-up semantics (typed when idle, staged to memory otherwise); fresh-read beta gate with refusal said out loud - Wake digest gains a check-the-board reminder; the generated briefing teaches the verbs only while the feature is on - Ramp: FeatureRamps.mailboard ships beta:100/stable:0; the app resolves it into settings mailboardEnabled, explicit user choice outranks the ramp, Settings toggle offered only while the ramp has it on - CLI: graphcode mailboard post|sync|list|watch, attributed via ZMX_SESSION exactly like node send Signed-off-by: scgopi <scgopireddy@gmail.com>
Signed-off-by: scgopi <scgopireddy@gmail.com>
|
Rename the Mailboard to Artifactory |
|
Every shared communication should be stored inthe artifactory as well |
…p on delete Review feedback on #229, all three: - Mailboard -> Artifactory everywhere: the module (ArtifactoryKit), the types, the settings bit (artifactoryEnabled), the ramp key, the verbs (graphcode artifactory post|sync|list|watch), the briefing, and the digest line. - Every shared communication is now stored in the artifactory as well: direct sends (node send, immediate or follow-up) land as a 'direct' record, delivered message-edge deliveries as 'direct', handoffs (with their payload) as 'handoff'. Record-only by design — mirroring never rings watchers, or a busy graph would double-deliver everything. Undelivered edge messages are not recorded: the artifactory records what actually was said, and an edge that failed transport said nothing. Cycle re-entries stay out for the same reason heartbeat ticks stay out of memory logs. - Deleting a loop deletes the artifactory posts it authored, alongside the edges, session, and memory teardown delete already performs — one irreversible confirmation covers the whole blast radius, spawned descendants included. Posts where the loop was only the recipient stay (they are the other side's record); node stop keeps everything. Full suite: 1323 tests / 143 suites pass; suite lint clean. Signed-off-by: scgopi <scgopireddy@gmail.com>
|
Both comments addressed (f613a66), plus the deletion question you raised:
Full suite: 1323 tests / 143 suites pass, including 8 new tests for mirroring and deletion cleanup. |
…uild 222) Signed-off-by: scgopi <scgopireddy@gmail.com>
…unds Independent review of #229, addressed in full: - Major 1: the artifactory gate forwards into sub-graph stores (subGraphStore and runInSubGraph), so a piloted composite's workers can post, sync and watch — and their communication mirrors — instead of being refused by a nil gate while their briefing teaches the verbs. nil still means off; forwarding, not a nil-means-on reading, was the fix. - Major 2: merged origin/main (was based on 1ecdec1; main had moved to 0.1.57/221) — conflict resolution keeps both sides' fields; version is now 0.1.58-beta1 (build 222), past main's counter instead of colliding with it. The CLI stamp uses a fixed-dateFormat DateFormatter with a pinned locale, the one Foundation API Linux CI cannot argue with. - Minor 3: mirror truncation reserves room for the ellipsis (the bound is now really 1024 bytes, not 1026). - Minor 4: the sync race comment now states the race honestly instead of claiming the opposite of the behavior. - Minor 5: mirrored direct/handoff records never ring watchers, so a watch on those topics alone stays silent — documented in the help and the briefing. - Minor 6: MAILBOARD header and 'a Artifactory' grammar slips fixed. - Minor 7: OrderedImports across the five files; all touched files pass swift-format --strict, repo-wide directories included. - Minor 8: imported loops start with a clean cursor — a stale number from the source board could hide this board's mail forever. Watch travels as a preference. - Nits 9, 10, 12: a foreign loop's post reads 'an outside loop' (id kept honestly); the briefing interpolates inline so off means byte-for-byte the pre-Artifactory document; watch --off when not watching is a no-op, not an error; help block spacing; the topic doc sentence now says what the implementation does. Nit 11 (renderPosted sequence echo under a concurrent post) is accepted as-is: the ack pattern is racy by design and the post body is what matters. - Tests: refusal announcement, mirror truncation bound, live-idle watcher delivery, composite gate inheritance, import cursor reset, watch-off idempotence, settings round-trip, briefing on/off, wake digest on/off. Full suite: 1371 tests / 144 suites pass. Signed-off-by: scgopi <scgopireddy@gmail.com>
|
Independent review addressed in full (833e4e2), including the merge of the stale base: Majors
Minors — 3 (truncation now really ≤1024), 4 (race comment states the race), 5 (dead Nits — 9 (foreign sender reads "an outside loop"), 10 (off-briefing is byte-identical to pre-Artifactory), 12 (idempotent Tests — 10 added for the review's holes; full suite 1371 tests / 144 suites pass. |
What this adds
node sendand edges are addressed — a loop must already know a peer's id, and the daemon routes to that one peer. The Mailboard is the ambient counterpart: one append-only board per graph that any loop can post to for whoever comes next — a decision made, a dead end hit, a claim staked — and read back with one command, with no wiring and no ids. Posts survive their authors: a loop created after the writer is gone still finds the note at its next wake.The verbs
Attribution rides
ZMX_SESSIONexactly likenode send; posts from a human's shell show as "a human".How it works
MailboardKitmodule (new Tuist target, Foundation-only): the post/watch model, caps (1 KiB body, 200 posts, oldest pruned), and the unread arithmetic every surface shares.LoopGraph: one writer (the daemon), persisted beside the graph file, snapshot in every.graphChanged— which is the CLI's read path, so no second read protocol. The global graph atgraphcode://globalbecomes a cross-project board for free. Absent-while-empty encoding keeps untouched graph files byte-identical, and old files decode with an empty board.deliverAdHocMessage's follow-up semantics: typed into a live idle session, queued behind a busy turn, staged to memory otherwise. The post itself is the durable half; the ding is best-effort.The ramp
Beta only, kill-switchable.
FeatureRamps.mailboardshipsbeta: 100 / stable: 0; the app resolves the ramp intomailboardEnabledin~/.graphcode/settings.json— the one bit the daemon (which cannot see ramps) enforces on every command, the briefing section, and the digest line. An explicit Settings choice outranks the ramp forever; the daemon refuses board commands out loud with a pointer to the setting while it is off.Verification
MailboardTests(store handlers, cursors, watcher wakes, pruning, Codable back-compat),MailboardCommandTests(CLI parsing/attribution),SettingsMailboardTests+FeatureRampsTests.mailboardShipsBetaOnAndStableOff.