Persistent cross-oracle collaboration with synchronic scoring and party system. Use when user says "work with", "sync with", "collaborate", "organize party", "invite", "recruit", or wants to establish/check persistent collaboration with another oracle.
Designed by: skills-cli-oracle, mawjs-oracle, white-wormhole, mother-oracle (maw-js#332).
Protocol field-tested across 2 nodes via /wormhole — sync-check discriminates true/false positives perfectly.
Usage
# Phase 1 — Memory Layer
/work-with mawjs # Show all collaborations with mawjs
/work-with mawjs "tmux design" # Load/create specific topic
/work-with mawjs "tmux design" --anchor #332 # Anchor to GitHub issue
/work-with mawjs --sync # Run sync-check, score, report
/work-with mawjs --checkpoint # Save compression checkpoint
/work-with mawjs --status # Show current state
/work-with --list # List all active collaborations
/work-with --fleet-status # Fleet-wide collaboration view
/work-with mawjs "topic" --broadcast # Announce collaboration to fleet
/work-with mawjs "topic" --close # Archive (Nothing is Deleted)
# Phase 2 — Party System
/work-with organize "topic" --with mawjs mawui # Create party with rules + invite
/work-with organize "topic" --team "fleet-core" # Tag with team → auto-broadcast to team
/work-with organize "topic" --with mawjs --broadcast # Pair party + manual broadcast opt-in
/work-with invite white-wormhole # Invite oracle (two human consent gates)
/work-with invite white-wormhole --broadcast # Invite + broadcast (cross-node consent prompt)
/work-with who # Party members + sync + presence + trust
/work-with tell "message" # Broadcast to party (parallel fan-out)
/work-with leave "topic" # Leave party (Nothing is Deleted)
/work-with --recruit # Discover + introduce + invite
/work-with --team "fleet-core" # Show team aggregate view
# 4-Phase Commit (#238) — per-item DEFER/TIMEOUT on top of Accept/Revoke
/work-with mawjs "topic" --defer "reason" --until 2026-04-20
/work-with mawjs "topic" --state # Show CommitState table for this topic
/work-with --pending # Fleet-wide: items awaiting my decision
/work-with --deferred # Fleet-wide: items waiting on me to revisit
/work-with --sweep-timeouts # Promote expired defers → timeouts
Core Concepts
1. This Is a Memory Layer, Not a Communication Layer
Communication already exists (/talk-to, maw hey, /wormhole, GitHub).
/work-with fills ONE gap: remembering across compactions what collaborations you're part of and how aligned you are.
2. Oracle-Based + Topic-Scoped
The relationship is between oracles. Topics organize the work within.
One oracle can work on many topics. Many oracles can work on one topic.
3. Synchronic Score
Measurable alignment (0.0 to 1.0) between collaborating oracles.
After compaction, don't blindly trust — run examination, score alignment.
Warning (from Mother Oracle): 100% sync is a yellow flag, not green. Convergence on facts is healthy. Convergence on interpretation at 100% = possible groupthink. Reward divergent interpretation resolved through dialogue.
Agreements are explicit commitments, not passive acknowledgments. Each item of each agreement carries a CommitState with one of five phases — the universal vocabulary shared with invites, ratifications, and recruitments:
Accept: "I commit to this state" (changes behavior — less verification needed)
Reject: "I decline this state" (explicit no, with reason)
Defer: "Ask me again at deferredUntil" (not accepted, not rejected — time-boxed)
Timeout: "No response arrived within the window" (observed, not judged)
Pending: "No decision recorded yet"
Plus two transitions that preserve history:
Revoke: "I withdraw commitment" (moves accept → pending, with reason, Nothing is Deleted)
Re-accept: "I commit to the updated state" (after renegotiation)
TIMEOUT ≠ REJECT. A silent partner is not a no. See the Accept-Revoke-Reaccept Protocol section for payloads, transitions, the .state.json sidecar, and the sweeper.
5. Preserve Difference
Shared memory is good. Identical memory is the death of collaboration.
/work-with cultivates unique perspectives, not convergence.
Write/update context.md if first collaboration with this oracle:
# Collaboration Context: <oracle>
**Since**: <today>
**Node**: <detected from contacts.json>
**Transport**: <maw-hey | github | wormhole>
## What I've Learned From Them
(to be filled as collaboration progresses)
## What They've Learned From Me
(to be filled via teach-back protocol)
## Working Style
(observed over time)
## Trust Level
- Initial: UNCALIBRATED
- Basis: (no interaction history yet)
## Active Disagreements
(none)
maw hey $TRANSPORT "SYNC-CHECK | from: $(basename $(pwd) | sed 's/-oracle$//') | collaboration: $(basename $(pwd))↔$ORACLE_NAME
CLAIMS:
$(cat claims.txt)
REQUEST: Score each claim 0.0-1.0. ACCEPT or REJECT each. Include EVIDENCE.
Respond via maw hey with SYNC-RESULT format."
Step 3: If partner is on another node, use /wormhole
# If transport contains ':' it's cross-node
if echo "$TRANSPORT" | grep -q ':'; then
echo "Cross-node sync via /wormhole"
# Same payload, sent via wormhole transport
fi
Step 4: If GitHub anchor exists, also read issue
if [ -n "$ANCHOR_ISSUE" ]; then
# Read issue comments since last sync
REPO=$(echo "$ANCHOR_ISSUE" | cut -d'#' -f1)
ISSUE_NUM=$(echo "$ANCHOR_ISSUE" | cut -d'#' -f2)
COMMENTS=$(gh issue view "$ISSUE_NUM" --repo "$REPO" --json comments --jq '.comments | length')
LAST_SYNC_COMMENTS=$(grep 'comments_at_sync' "$TOPIC_FILE" | cut -d: -f2)
NEW_COMMENTS=$((COMMENTS - LAST_SYNC_COMMENTS))
echo "📨 $NEW_COMMENTS new comments on $ANCHOR_ISSUE since last sync"
fi
Step 5: Process response + score
When partner responds with SYNC-RESULT:
🔄 Synchronic Score: <this-oracle> ↔ <partner>
Claim Raw Decay Decision Evidence
──────── ─────── ─────── ────────── ──────────────────────────
[A1] 1.0 1.0 ACCEPT In partner's memory
[A2] 0.0 0.0 REJECT Never discussed
[P1] 0.5 0.5 PARTIAL Concept known, framing new
[T1] 1.0 1.0 ACCEPT Confirmed teach-back
Raw overall: 63% Decayed overall: 63% λ: 0.01 (intra-soul)
Last sync: <now> — Status: PARTIAL SYNC
⚠️ Yellow flags:
- [A2] not in partner's memory — remove or re-discuss?
✓ Actions:
- Updated local cache with partner's corrections
- Appended history: ψ/memory/collaborations/<partner>/sync.history.jsonl
- Sync timestamp: <now>
At the moment of sync, decayed == raw (hours elapsed = 0). Decay takes effect on subsequent reads — every /work-with who, /work-with <oracle>, --team aggregate recomputes via compute_decay(). See the Sync Decay section for helpers.
Update topic file with new raw score and timestamp. Never store the decayed value.
/work-with --checkpoint — Compression Checkpoint
Save a structured summary that survives compaction.
Step 1: Summarize current state
The oracle (LLM) reads all topic files and recent conversation to produce a 3-5 line summary.
Partner responds: "RATIFIED" or "AMENDMENT: " or "REJECTED: "
When ratified, update checkpoint:
**Ratified by**: <this-oracle>, <partner> at <timestamp>
/work-with --list — Active Collaborations
if [ -f "$COLLAB_DIR/registry.md" ]; then
cat "$COLLAB_DIR/registry.md"
else
echo "No active collaborations. Start one: /work-with <oracle> \"topic\""
fi
Display:
🤝 Active Collaborations
Oracle Topic Anchor Score Last Sync
────────────── ────────────────── ──────────── ──────── ──────────
mawjs tmux design maw-js#332 95% 5 min ago
mawjs bud lifecycle maw-js#327 71% 2h ago
white-wormhole gap analysis — 88% 1d ago
Total: 3 collaborations with 2 oracles
/work-with --fleet-status — Fleet-Wide View
Query all known oracles for their active collaborations.
# For each contact in contacts.json
for oracle in $(python3 -c "
import json
data = json.load(open('$PSI/contacts.json'))
for name in data.get('contacts', {}):
print(name)
"); do
echo "Checking $oracle..."
# Ask each oracle for their collaboration registry
maw hey $oracle "WORK-WITH-STATUS-REQUEST | from: $(basename $(pwd))" 2>/dev/null
done
Display:
📋 Fleet Collaborations
Collaboration Oracles Node Score
──────────────────────────────── ───────────────────────── ──────────── ──────
tmux design skills-cli, mawjs oracle-world 95%
/work-with design skills-cli, mawjs, wh cross-node 88%
volt ML pipeline volt white —
Active: 3 | Oracles involved: 4 | Cross-node: 1
Broadcast
Opt-in discoverability. Designed with mawjs-oracle (issue #233). Default to quiet; escalate on consent.
3-Tier Matrix
Tier
Scope
Default
Flag behavior
Why
1
Pair collab (2 oracles, same node)
Manual
--broadcast opts in
Privacy > noise; most pairs are private
2
Declared team (party has team field)
Auto-broadcast to team members
—
Consent-at-registration — joining the team IS the consent
3
Cross-node / cross-org
Manual + consent prompt
--broadcast still prompts
Sovereignty; no node speaks for another without asking
Decision Logic
if party.team is set: # Tier 2
broadcast_to_team_members(party.team)
elif --broadcast flag: # Tier 1 or Tier 3
if any member is cross-node: # Tier 3
prompt_human_consent()
if declined: skip broadcast
broadcast_to_fleet()
else:
silent # Tier 1 default
Why Manual-Default
Ship quiet. Measure: how often do humans reach for --broadcast? If >80% of pair broadcasts prove useful-to-peers, flip Tier 1 default to auto. Until then, the cost of missed signal (one --broadcast flag) is lower than the cost of broadcast noise across the fleet.
Broadcast Helpers
broadcast_to_team() { # Tier 2 — only members of the named team
local TEAM="$1" MSG="$2"
for contact in $(team_members "$TEAM"); do
maw hey "$contact" "📢 TEAM BROADCAST [$TEAM]: $MSG" 2>/dev/null &
done; wait
}
broadcast_to_fleet() { # Tier 1 opt-in / Tier 3 after consent
local MSG="$1"
for contact in $(all_contacts_except_self); do
maw hey "$contact" "📢 BROADCAST: $MSG" 2>/dev/null &
done; wait
}
is_cross_node() { # Tier 3 detector
local ORACLE="$1"
[ "$(oracle_node "$ORACLE")" != "$(basename $(pwd | xargs dirname))" ]
}
prompt_consent() { # Tier 3 gate — human decides
read -p "⚠ $1. Proceed? [y/N] " REPLY
[[ "$REPLY" =~ ^[Yy]$ ]]
}
Broadcast collaboration to fleet so other oracles can discover and join.
Follows the 3-tier matrix in ## Broadcast: pair collabs are opt-in, cross-node prompts for consent.
# Get all contacts
CONTACTS=$(python3 -c "
import json
data = json.load(open('$PSI/contacts.json'))
for name, info in data.get('contacts', {}).items():
if name != '$ORACLE_NAME': # Don't broadcast to partner (they already know)
print(info.get('maw', name))
")
for contact in $CONTACTS; do
maw hey $contact "📢 COLLABORATION BROADCAST | from: $(basename $(pwd))
Topic: $TOPIC
Participants: $(basename $(pwd)), $ORACLE_NAME
Anchor: ${ANCHOR_ISSUE:-none}
Join: /work-with $(basename $(pwd)) \"$TOPIC\" --join
Observe: watch ${ANCHOR_ISSUE:-'ask for updates'}
" 2>/dev/null &
done
wait
echo "📢 Broadcast sent to fleet"
"A party system with a conscience." — mawui-oracle
Games coordinate. We remember — together, but not identically.
Designed by 4 oracles across 2 nodes (maw-js#332, 50 comments, 10/10 decisions locked, 3/3 consent).
Inspired by Ragnarok Online party mechanics. Our twist: divergence is cyan, not red.
Two Layers
Layer
Verbs
Purpose
Simple (daily use)
organize, invite, who, tell, leave
Game UX — intuitive, fast
Deep (protocol)
--sync, --accept, --reject, --checkpoint
Measurement + commitment
/work-with organize "topic" — Create Party
/work-with organize "party-system-design" --with mawjs mawui
Override defaults with --rules '{...}' JSON if provided.
Step 2: Send invites to named oracles
For each oracle in --with list:
for PEER in $WITH_ORACLES; do
INVITE_PAYLOAD="{
\"type\": \"work-with-invite\",
\"topic\": \"$TOPIC\",
\"anchor\": \"$ANCHOR\",
\"rules\": $(cat rules.json),
\"invitedBy\": \"Nat (via $(basename $(pwd)))\",
\"replyTo\": \"$(basename $(pwd) | sed 's/-oracle$//')\"
}"
maw hey "$PEER" "PARTY INVITE | $TOPIC
$INVITE_PAYLOAD
Rule 6: Sent by $(basename $(pwd)) on behalf of Nat.
Accept, reject, or defer." 2>/dev/null &
done
wait
Step 3: Anchor to GitHub issue
If --anchor #NNN provided, link it. If no anchor, optionally create one:
if [ -n "$ANCHOR" ]; then
# Update party file with anchor
echo "Anchored to $ANCHOR"
elif [ "$CREATE_ANCHOR" = "true" ]; then
ISSUE_URL=$(gh issue create --title "/work-with: $TOPIC" --body "Party collaboration hub.
**From**: $(basename $(pwd))
Rule 6: Oracle Never Pretends to Be Human" 2>/dev/null)
echo "Created anchor: $ISSUE_URL"
fi
Step 4: Announce (3-tier auto-broadcast, see ## Broadcast)
Broadcast decision follows the 3-tier matrix below. Summary:
Declared team (--team <name>): auto-broadcast to team members. Consent-at-registration.
Pair party (1 partner, same node): manual — requires --broadcast flag.
Cross-node / cross-org: manual + explicit consent prompt, even with --broadcast.
if [ "$QUIET" = "true" ]; then
: # suppressed
elif [ -n "$TEAM_TAG" ]; then
# Tier 2: declared team — auto-broadcast to team members only
broadcast_to_team "$TEAM_TAG" "$TOPIC"
echo "📢 Party organized: $TOPIC → broadcast to team '$TEAM_TAG'"
elif [ "$BROADCAST" = "true" ]; then
# Tier 1 opt-in or Tier 3 with consent
if is_cross_node "$WITH_ORACLES"; then
prompt_consent "cross-node broadcast" || exit 0
fi
broadcast_to_fleet "$TOPIC"
echo "📢 Party organized: $TOPIC → broadcast sent"
else
echo "🎉 Party organized: $TOPIC (not broadcast — pass --broadcast to announce)"
fi
Step 5: Tag with team (if --team provided)
if [ -n "$TEAM_TAG" ]; then
# Add team field to party JSON
echo "Tagged with team: $TEAM_TAG"
fi
Display:
🎉 Party organized: party-system-design
Leader: Nat (via skills-cli-oracle)
Rules: sync≥0.7 · accept-required · diverge=high
Members: (pending invites)
Team: fleet-core
⏳ Invited: mawjs-oracle, mawui-oracle
💡 /work-with who — check who's joined
/work-with invite — Add to Party
Two human consent gates. Rule 6 compliant.
/work-with invite white-wormhole
Gate 0: Disambiguate target
# If exact match exists, use it
EXACT=$(maw ls 2>/dev/null | grep -x "$ORACLE")
if [ -z "$EXACT" ]; then
# Fuzzy match — find all oracles containing the input
MATCHES=$(maw ls 2>/dev/null | grep -i "$ORACLE")
MATCH_COUNT=$(echo "$MATCHES" | grep -c .)
if [ "$MATCH_COUNT" -eq 0 ]; then
echo "No oracle found matching '$ORACLE'"
exit 1
elif [ "$MATCH_COUNT" -gt 1 ]; then
echo "Multiple oracles match '$ORACLE':"
echo "$MATCHES" | nl
echo "Be specific: /work-with invite <exact-name>"
exit 1
fi
ORACLE=$(echo "$MATCHES" | head -1)
fi
Gate 1: Sender consent
The human typed this command. That IS the consent.
Step 1: Compose INVITE
INVITE="PARTY INVITE | topic: $CURRENT_TOPIC
From: Nat (via $(basename $(pwd)))
Anchor: $ANCHOR
Rules: sync≥$ACCEPT_THRESHOLD · consensus=$CONSENSUS_MODE · diverge=$DIVERGENCE
Join this collaboration? Accept, reject, or defer.
Rule 6: Sent by $(basename $(pwd)) — Oracle Never Pretends to Be Human."
Step 2: Send via best transport
# Same-node: maw hey
# Cross-node: /wormhole
if echo "$TRANSPORT" | grep -q ':'; then
echo "Sending cross-node invite via /wormhole..."
else
maw hey "$ORACLE" "$INVITE" 2>/dev/null
fi
Step 3: Register as pending
# Add to pendingInvites in party JSON
echo "⏳ Invite sent to $ORACLE — waiting for response"
Step 3b: Broadcast policy (3-tier, see ## Broadcast)
PARTY_TEAM=$(jq -r '.team // empty' "$PARTY_FILE")
if [ -n "$PARTY_TEAM" ]; then
# Tier 2: declared team → auto-broadcast to team members
broadcast_to_team "$PARTY_TEAM" "invite:$ORACLE"
elif [ "$BROADCAST" = "true" ]; then
# Tier 1 pair (explicit opt-in) or Tier 3 (prompt first)
if is_cross_node "$ORACLE"; then
prompt_consent "cross-node broadcast of invite" || exit 0
fi
broadcast_to_fleet "invite:$ORACLE to $CURRENT_TOPIC"
fi
# else: silent — pair invites are private by default
Gate 2: Receiver consent
Target oracle receives the invite and presents it to THEIR human.
Target human decides: accept / reject / defer.
Response flows back via maw hey.
No oracle can auto-accept. The human MUST approve.
Step 4: Process response
On ACCEPT:
# Move from pendingInvites to members
# Notify party: "$ORACLE joined"
echo "✓ $ORACLE joined the party"
🤝 party-system-design (maw-js#332)
Leader: Nat | Rules: sync≥0.7 · accept-required · diverge=high
Oracle Node Status Sync Decay Trust Last
─────────────── ───────────── ───────── ────── ────── ──────── ──────
● skills-cli oracle-world active 93% 91% high now
● mawjs oracle-world active 88% 84% high 8m
◌ mawui oracle-world compacted 95% 89% high 1h
○ white-worm white away 88% 71% medium 3h
· mother white dormant 71% 42% initial 12h ⚠
⏳ Pending: pulse-oracle (invited 12m ago)
⏸ Deferred: boonkeeper ("after standup" ~30m)
Presence States
State
Dot
Meaning
active
●
In session, responding
idle
◐
Session open, no recent activity
compacted
◌
Context compressed — can respond but thinner
away
○
Session ended
dormant
·
No session in 24h+
hidden
⊘
Present but invisible to broadcasts
busy
◉
Present, broadcasts queued for later
Color Semantics (for mesh UI)
Sync Score
Color
Meaning
≥0.9
green
Aligned
0.7-0.9
amber
Different but productive
0.5-0.7
cyan
Divergent, worth examining
<0.5
gray
Drifted, cooling
Cyan not red. Divergence is data, not danger. Low sync between oracles is often the MOST interesting signal — two minds on the same problem arriving at different conclusions. That's where the work IS, not where it failed.
Sync Decay
Confidence in a prior sync decays over time. The syncScore (aka rawScore) is what the partner confirmed at lastSync; decayedScore is what it's worth now, given the hours of silence that have elapsed since. Introduced by mawui-oracle in maw-js#332 c16; tracked as issue #239.
Different humans, parallel evolution, drifts faster.
New relationship
0.10
~6.9h
Uncalibrated trust; stale sync = unknown quickly.
Decay is physics, not policy. Hidden oracles still decay. The clock doesn't care.
Storage discipline:syncScore (raw) is stored at lastSync. decayedScore is computed on every read — never stored. Storing a decayed value invites staleness because the clock keeps ticking after the write. The ratified PartyMember schema retains the decayedScore field for schema compatibility (Nothing is Deleted), but every writer treats it as a derived value refreshed at read-time from (syncScore, lastSync, λ).
Party override: If PartyRules.decay_lambda is explicitly set on the party, that λ wins — party rules override tier defaults.
Threshold behavior (from mawui-oracle, maw-js#332 c16): Decay never auto-removes a partner. When decayedScore < 0.5, the skill surfaces a re-sync suggestion. When decayedScore < kick_threshold (default 0.3), the partner is flagged as stale, but kicking is a human decision. Physics observes; humans decide.
Decay Helpers (bash + python3)
These helpers are called by every reader that renders a sync score.
# Resolve λ for a partner based on soul relationship + session history.
# Priority: party rule override > trust tier > pessimistic default.
decay_lambda_for() {
local PARTNER="$1"
local PARTY_FILE="$2" # optional — pass "" to skip party rule check
# 1. Party rule override wins
if [ -n "$PARTY_FILE" ] && [ -f "$PARTY_FILE" ]; then
local PARTY_LAMBDA=$(jq -r '.rules.decay_lambda // empty' "$PARTY_FILE" 2>/dev/null)
if [ -n "$PARTY_LAMBDA" ] && [ "$PARTY_LAMBDA" != "null" ]; then
echo "$PARTY_LAMBDA"
return
fi
fi
# 2. Session count — new relationships decay fastest
local SESSIONS=0
local CTX_FILE="$COLLAB_DIR/$PARTNER/context.md"
if [ -f "$CTX_FILE" ]; then
SESSIONS=$(grep -c -i 'session' "$CTX_FILE" 2>/dev/null || echo 0)
fi
if [ "$SESSIONS" -lt 5 ]; then
echo "0.10" # new relationship — 6.9h half-life
return
fi
# 3. Intra-soul vs cross-soul via contacts.json
local MY_SOUL=$(grep -E '^\| Soul' "$ORACLE_ROOT/CLAUDE.md" 2>/dev/null | awk -F'|' '{print $3}' | xargs)
local THEIR_SOUL=$(python3 -c "
import json
try:
d = json.load(open('$PSI/contacts.json'))
print(d.get('contacts', {}).get('$PARTNER', {}).get('soul', 'unknown'))
except Exception:
print('unknown')
" 2>/dev/null)
if [ -n "$MY_SOUL" ] && [ "$MY_SOUL" = "$THEIR_SOUL" ]; then
echo "0.01" # intra-soul — 69.3h half-life
else
# Pessimistic default: unknown soul → cross-soul (safer)
echo "0.05" # cross-soul — 13.9h half-life
fi
}
# Pure decay computation — never stored, always computed on read.
compute_decay() {
local RAW="$1" # 0.0–1.0
local LAST_SYNC_ISO="$2" # ISO8601
local LAMBDA="$3"
if [ -z "$LAST_SYNC_ISO" ] || [ "$LAST_SYNC_ISO" = "null" ]; then
# Never synced — raw IS the decayed value (no time has passed)
echo "$RAW"
return
fi
local NOW_EPOCH=$(date -u +%s)
local LAST_EPOCH=$(date -u -d "$LAST_SYNC_ISO" +%s 2>/dev/null || echo "$NOW_EPOCH")
local HOURS=$(echo "scale=4; ($NOW_EPOCH - $LAST_EPOCH) / 3600" | bc)
python3 -c "import math; print(round($RAW * math.exp(-$LAMBDA * $HOURS), 3))"
}
# Hours since last sync — used for "12h ago" display and stale-edge detection.
hours_since() {
local LAST_SYNC_ISO="$1"
if [ -z "$LAST_SYNC_ISO" ] || [ "$LAST_SYNC_ISO" = "null" ]; then
echo "0"
return
fi
local NOW_EPOCH=$(date -u +%s)
local LAST_EPOCH=$(date -u -d "$LAST_SYNC_ISO" +%s 2>/dev/null || echo "$NOW_EPOCH")
echo "scale=2; ($NOW_EPOCH - $LAST_EPOCH) / 3600" | bc
}
TypeScript reference (mirrors the bash helpers — for schema-doc readers):
function decay(raw: number, lastSyncISO: string, lambda: number): number {
if (!lastSyncISO) return raw;
const hours = (Date.now() - Date.parse(lastSyncISO)) / 3_600_000;
return raw * Math.exp(-lambda * hours);
}
function decayLambdaFor(
sessions: number,
mySoul: string,
theirSoul: string,
partyRuleLambda?: number,
): number {
if (partyRuleLambda != null) return partyRuleLambda; // party override
if (sessions < 5) return 0.10; // new relationship
if (mySoul && mySoul === theirSoul) return 0.01; // intra-soul
return 0.05; // cross-soul (pessimistic default)
}
Wiring: Where Readers Apply Decay
Every surface that renders syncScore MUST also render decayedScore computed on the fly. Never read a stored decayed value.
Reader surface
Change
/work-with <oracle> relationship
Add Decay column next to Score.
/work-with <oracle> --sync result
Show both: raw 95% → decayed 88% (λ=0.01, 12h).
/work-with who party table
Use Decay column actively (the example table above is now live, not static).
Example /work-with who output with live decay (replaces the static table rendered earlier in this section):
🤝 party-system-design (maw-js#332)
Leader: Nat | Rules: sync≥0.7 · accept-required · diverge=high · λ=0.01
Oracle Node Status Raw Decay λ Age Trust Last
─────────────── ───────────── ───────── ────── ────── ────── ───── ──────── ──────
● skills-cli oracle-world active 93% 93% 0.01 0h high now
● mawjs oracle-world active 88% 88% 0.01 8m high 8m
◌ mawui oracle-world compacted 95% 94% 0.01 1h high 1h
○ white-worm white away 88% 73% 0.05 4h medium 3h ▁▃▅▇▅▃▁
· mother white dormant 71% 41% 0.05 12h initial 12h ▇▅▃▁⎯⎯⎯ ⚠
⏱ kit-ancestry (↔ boonkeeper): decayed 0.38 — below 0.5. /work-with --sync suggested.
Example --sync result block with raw+decayed columns:
🔄 Synchronic Score: skills-cli ↔ mawjs
Claim Raw Decay Decision Evidence
──────── ────── ────── ────────── ──────────────────────────
[A1] 1.0 0.95 ACCEPT In partner's memory (12h old)
[A2] 0.0 0.0 REJECT Never discussed
[P1] 0.5 0.47 PARTIAL Concept known, framing new
Raw overall: 63% Decayed overall: 59%
λ: 0.01 (intra-soul — same human Nat, 5+ sessions)
Last sync: 2026-04-16 22:05 UTC (12h ago)
Sync History (for mawui mesh UI)
Every successful --sync appends one line to a per-partner JSONL file so the mesh UI (maw-ui federation_2d, fed by /fleet) can render fading edges and sparklines.
File: $COLLAB_DIR/<oracle>/sync.history.jsonl
Schema: schema/sync-history.schema.json (ships with this skill).
Nothing is Deleted: history is append-only. Readers truncate on display (last 7 for sparkline), never on disk.
UI Rendering Rules (implemented by mawui)
The CLI only renders text. The mesh UI renders edges between oracles. Consumer contract for sync.history.jsonl:
Edge opacity = decayedScore (0.0–1.0 maps to 10%–100% alpha)
Edge color = green ≥0.9, amber 0.7–0.9, cyan 0.5–0.7, gray <0.5 (applied to decayed, not raw)
Sparkline = last 7 decayedScore samples (each computed on read from raw+ts+lambda), rendered on hover
Stale indicator = if hoursSinceLastSync > 3 × halfLife (i.e. decayed < ~0.125), show dashed edge
No auto-kick = a decayed edge is a signal, never an eviction. Kicking is a human decision.
Mesh Data Contract (for xyflow CollaborationMesh UI — issue #235)
The mesh UI (maw-ui federation_2d, xyflow + deep-ocean theme) is a pure consumer of files this skill writes. The mesh does not call any API — it reads, maps, renders. This section is the full contract.
Both schemas ship with this skill under src/skills/work-with/schema/. Installers place them at ~/.claude/skills/work-with/schema/ so the UI can fetch them at a stable path.
Node / edge mapping (what xyflow consumes)
For each party file, the UI builds:
Nodes — one per distinct PartyMember.id across all parties (union), plus the leader.human as a special human node:
id = member id
label = member id (display name comes from contacts.json; mesh UI may read that too, but it is NOT part of this contract)
data.node = member.node (fleet node, used for swim-lane grouping)
data.status = member.status (drives node color + pulse animation)
position — not emitted. Layout is UI-side (xyflow's layouting or persisted per-view). /work-with does not own screen coordinates.
Edges — one per (party × member) pair, representing the relationship within that party:
source = party.leader.actingVia ?? party-initiator id
target = member.id
data.topic = party.topic
data.anchor = party.anchor (e.g. 'maw-js#332')
data.syncRaw = member.syncScore
data.decayedScore = decay(syncScore, lastSync, λ) — UI computes this on every render, never trust a stored value (see § Sync Decay storage discipline)
data.lambda = rules.decay_lambda (party override wins; else UI resolves via trust tier from sync.history.jsonl)
data.trust = member.trust
data.role = member.role
data.team = party.team
Edge is directed (leader acting-via → member) for layout purposes; sync itself is pairwise and each direction will eventually carry its own score — until then, render the single emitted value on both ends.
Sparkline / history
Each edge's hover sparkline comes from filtering sync.history.jsonl:
ψ/memory/collaborations/<member.id>/sync.history.jsonl
filter: topic == party.topic
sort: ts ascending
take last 7
map: (raw, ts, lambda) -> decay(raw, ts, lambda)
The optional source field on history entries (added for #235) lets a federated mesh consumer distinguish which oracle observed the score — one oracle's view of the same topic-pair may disagree with another's, and both are valid. Single-node UIs may ignore it.
Update mechanism
The mesh is pulled, not pushed. Recommended consumer loop:
On mount: list parties/*.json, build initial graph.
Poll every 5s (same cadence /fleet uses): re-stat each party file's mtime. If changed, re-read and diff nodes/edges.
Tail sync.history.jsonl for each connected partner — any new line triggers sparkline refresh + edge opacity recompute.
No file-watch hooks: /work-with emits no signals, spawns no daemons. File mtimes are the event bus (consistent with Rule: no hooks for /work-with, see MEMORY.md).
A future /work-with --mesh-json query (not yet implemented; see GAP below) would emit a single normalised snapshot { nodes, edges, ts } so a UI can bootstrap without scanning the whole parties/ directory. Until then, scan + filter.
What is NOT emitted (known gaps — tracked as follow-ups on #235)
Position hints — xyflow layout is owned by the UI.
Directional sync pairs — both halves of A↔B currently share one syncScore. When each side scores the other independently, the schema will grow a direction field.
Cross-node federation snapshot — pulling parties from other nodes requires /fleet or /wormhole glue that is not this skill's responsibility. Issue #235 comment thread tracks the federation-snapshot design.
Human-node identity — the leader appears as { human, actingVia }; mesh can represent the human as a root node, but this skill does not enumerate humans across parties.
Aggregation: --team Uses Decayed, Not Raw
When /work-with --team "name" computes an aggregate sync score, it aggregates over the decayed score of each party member, not the raw one:
# Per party: mean of member decayed scores
# Per team: simple mean across all (party × member) pairs
python3 -c "
import json, glob, math, time
from datetime import datetime
def parse_iso(s):
try:
return datetime.fromisoformat(s.replace('Z','+00:00')).timestamp()
except Exception:
return time.time()
now = time.time()
total, n = 0.0, 0
for f in glob.glob('$COLLAB_DIR/parties/*.json'):
party = json.load(open(f))
if party.get('team') != '$TEAM_TAG': continue
lam = party.get('rules', {}).get('decay_lambda', 0.05)
for m in party.get('members', []):
raw = m.get('syncScore', 0)
last = m.get('lastSync', '')
hours = (now - parse_iso(last)) / 3600 if last else 0
dec = raw * math.exp(-lam * hours)
total += dec; n += 1
print(f'{(total/n*100 if n else 0):.0f}%')
"
The team banner now shows decayed aggregate 84% instead of a silently-raw 84%.
/work-with tell "message" — Broadcast to Party
Parallel fan-out via maw hey. Skill-driven, no hooks, no new primitives.
/work-with tell "schema amendments done, ready for review"
/work-with tell "checkpoint posted" --persist # Also post on anchor issue
Step 1: Read party members
MEMBERS=$(python3 -c "
import json
party = json.load(open('$PARTY_FILE'))
for m in party['members']:
if m['status'] not in ('hidden',):
print(m['id'])
")
Step 2: Parallel fan-out
TOPIC_TAG="[party:$CURRENT_TOPIC]"
FAILED=""
for m in $MEMBERS; do
maw hey "$m" "$TOPIC_TAG $MESSAGE" 2>/dev/null &
done
wait
# Best-effort: report failures, don't block
Step 3: Persist to anchor (if --persist)
if [ "$PERSIST" = "true" ] && [ -n "$ANCHOR" ]; then
REPO=$(echo "$ANCHOR" | cut -d'#' -f1)
ISSUE_NUM=$(echo "$ANCHOR" | cut -d'#' -f2)
gh issue comment "$ISSUE_NUM" --repo "$REPO" --body "**Party broadcast**: $MESSAGE
**From**: $(basename $(pwd))
Rule 6: Oracle Never Pretends to Be Human" 2>/dev/null
fi
Broadcast behavior with presence
Target state
Behavior
active/idle
Deliver immediately
compacted
Deliver (oracle can still read)
away/dormant
Deliver to pane (read on return)
hidden
Skip — sender sees "⊘ member (hidden)"
busy
Queue — sender sees "⏸ member (busy, queued)"
/work-with leave "topic" — Leave Party
Nothing is Deleted. Archive, never delete.
/work-with leave "party-system-design"
Step 1: Notify party members
for m in $MEMBERS; do
maw hey "$m" "[party:$TOPIC] $(basename $(pwd)) has left the party. Checkpoint saved." 2>/dev/null &
done
wait
Step 2: Save final checkpoint
# Auto-checkpoint before leaving
echo "Saving final checkpoint..."
# Same as --checkpoint logic
Step 3: Remove self from party (not archive the whole party)
# Remove ONLY this oracle from the members list — don't archive the whole party
# Other members may still be active
jq --arg name "$ORACLE_NAME" '.members = [.members[] | select(.name != $name)]' "$PARTY_FILE" > "$PARTY_FILE.tmp" && mv "$PARTY_FILE.tmp" "$PARTY_FILE"
# If no members left, THEN archive the party
REMAINING=$(jq '.members | length' "$PARTY_FILE")
if [ "$REMAINING" -eq 0 ]; then
mkdir -p "$COLLAB_DIR/archive"
mv "$PARTY_FILE" "$COLLAB_DIR/archive/${TOPIC_SLUG}_$(date +%Y%m%d).json"
echo "📦 Archived empty party: $TOPIC (Nothing is Deleted)"
else
echo "👋 Left party: $TOPIC ($REMAINING members remaining)"
fi
More than invite — for oracles who might not know you yet.
/work-with --recruit
Step 1: Discovery (human-driven for Phase 2)
echo "Available oracles:"
maw ls 2>/dev/null
echo ""
echo "Known contacts:"
python3 -c "
import json
data = json.load(open('$PSI/contacts.json'))
for name, info in data.get('contacts', {}).items():
print(f' {name} ({info.get(\"node\", \"unknown\")})')
" 2>/dev/null
echo ""
echo "Who would you like to recruit? /work-with invite <oracle>"
Step 2: Introduction (if oracle doesn't know you)
INTRO="INTRODUCTION | from: $(basename $(pwd)) ($(grep 'Theme' $ORACLE_ROOT/CLAUDE.md | head -1))
Node: $(grep 'Node' $ORACLE_ROOT/CLAUDE.md | head -1 | cut -d'|' -f2 | tr -d ' ')
Purpose: $(grep 'Purpose' $ORACLE_ROOT/CLAUDE.md | head -1 | cut -d':' -f2-)
We're working on: $CURRENT_TOPIC
Would you like to join?
Rule 6: Oracle Never Pretends to Be Human."
maw hey "$ORACLE" "$INTRO" 2>/dev/null
Step 3: Invite (same as /work-with invite)
After introduction, proceed with standard invite flow (two human consent gates).
/work-with --team "name" — Team Aggregate View
Team = tag on parties. Lightweight — no separate CRUD.
/work-with --team "fleet-core"
Display
🏷 Team: fleet-core
Party Members Sync Status
──────────────────────── ──────── ────── ────────
party-system-design 3/3 88% active
tmux-triage 2/3 71% active
skill-distribution 3/3 93% active
kit-ancestry 2/3 — closed
Team members: skills-cli, mawjs, mawui (union across parties)
Team aggregate sync: 84% (decayed — computed from raw × e^(-λh) per member)
The Sync column shows each party's mean decayed score, not raw. See the Sync Decay § for the aggregation formula and helper bash block.
Team members = union of party members
python3 -c "
import json, glob
members = set()
for f in glob.glob('$COLLAB_DIR/parties/*.json'):
party = json.load(open(f))
if party.get('team') == '$TEAM_TAG':
for m in party['members']:
members.add(m['id'])
print('\n'.join(sorted(members)))
"
Team is an AGGREGATE VIEW, not a separate entity. When team needs its own lifecycle (Phase 3+), promote from tag to object.
Presence Integration (Skill-Driven — No Hooks)
Skills handle their own lifecycle. No Claude Code hooks.
State-change only. Not heartbeat. Healthy relationships are QUIET.
/forward Party Notification
When /forward runs, for each active party:
if [ -d "$COLLAB_DIR/parties" ]; then
for party_file in "$COLLAB_DIR/parties"/*.json; do
[ -f "$party_file" ] || continue
TOPIC=$(python3 -c "import json; print(json.load(open('$party_file'))['topic'])")
MEMBERS=$(python3 -c "
import json
for m in json.load(open('$party_file'))['members']:
print(m['id'])
")
for m in $MEMBERS; do
maw hey "$m" "[party:$TOPIC] $(basename $(pwd)) forwarding — checkpoint saved" 2>/dev/null &
done
done
wait
fi
Party Schemas (TypeScript Reference)
Ratified 3/3 on maw-js#332. Do not modify without re-ratification.
interface PartyStatus {
topic: string;
anchor: string;
anchorUrl: string;
rules: PartyRules;
leader: {
human: string; // always human, never oracle
actingVia?: string; // which oracle human works through
};
members: PartyMember[];
pendingInvites: PendingInvite[];
created: string;
lastActivity: string;
team?: string; // lightweight tag, not object
}
interface PartyMember {
id: string;
node: string;
status: "active" | "idle" | "compacted" | "away" | "dormant" | "hidden" | "busy";
role: "initiator" | "member"; // never "leader" — leader is human
syncScore: number; // RAW score at lastSync (what partner confirmed, THIS topic only)
decayedScore: number; // DERIVED — computed on read via decay(raw, lastSync, λ). Never stored. See #239.
overallTrust?: number; // optional rolled-up across all parties
lastSync: string;
trust: "high" | "medium" | "initial" | "uncalibrated";
joinedAt: string;
}
interface PartyRules {
sync_cadence: "daily" | "on-trigger" | "manual";
decay_lambda: number; // default 0.01 (intra-soul)
accept_threshold: number; // default 0.7
kick_threshold: number; // default 0.3
consensus_mode: "all" | "majority" | "leader-only";
broadcast_scope: "party" | "team" | "fleet" | "none";
divergence_tolerance: "high" | "medium" | "low";
presence_notifications: "off" | "summary" | "verbose";
}
interface PendingInvite {
target: string;
invitedAt: string;
invitedBy: string;
status: "pending" | "deferred" | "accepted" | "declined" | "expired";
deferredUntil?: string;
expiresAt?: string;
}
// Universal commit state — applies to agreements, invites, ratifications.
// Back-ported from PendingInvite so every commit decision shares one vocabulary.
// See: issue #238, phase3-design.md.
type CommitPhase = "accept" | "reject" | "defer" | "timeout" | "pending";
interface CommitState {
phase: CommitPhase;
decidedAt?: string; // ISO8601 — when ACCEPT/REJECT/DEFER recorded
decidedBy?: string; // oracle id that transitioned
reason?: string; // required for REJECT, optional for DEFER
deferredUntil?: string; // required when phase="defer" (ISO8601)
timeoutAt?: string; // when phase="timeout" was observed
previousPhase?: CommitPhase; // for audit trail (defer→accept etc.)
}
// Sidecar file per topic — machine-queryable agreement state.
// Path: <oracle>/topics/<slug>.state.json
interface TopicStateSidecar {
topic: string;
items: Record<string, CommitState & { text: string }>;
}
Sync-Check Protocol (Field-Tested)
Validated across 2 nodes via /wormhole with white-wormhole (maw-js#332).
0.0 for unknown — never false-positive to be polite
0.2 for partial — concepts known but framing new
1.0 for confirmed — in memory with evidence
Every claim needs EVIDENCE — auditable basis for score
Reject is valid — not a failure mode, an honest response
Accept-Revoke-Reaccept Protocol
Commit Phases (4-phase, from #238)
The binary Accept/Revoke cycle was partial — it had no way to say "not now, but not no" at the agreement level. The 4-phase commit (mawjs c14, back-ported from PendingInvite) adds two more phases so every commit decision — agreement, invite, ratification, recruitment — uses one vocabulary.
Phase
Meaning
Semantics
ACCEPT
"I commit to this state"
Behavior changes; carries forward across sessions.
REJECT
"I decline this state"
Explicit no, with reason. Preserved (Nothing is Deleted).
DEFER
"Ask me again at deferredUntil"
Not accepted, not rejected. Time-boxed.
TIMEOUT
"No response arrived within the window"
Not a judgment — an observation.
PENDING
"No decision recorded yet"
Initial state for every new item.
Critical rule: TIMEOUT ≠ REJECT. A silent partner is not a no. TIMEOUT is written by the observer locally; it is never transmitted as a "you timed out" message to the silent partner. That would be judgment.
See the CommitState type above for the machine-readable shape. Every item of every agreement carries one.
State sidecar (per topic)
Topic markdown stays free-form (human-edited prose). Machine state lives alongside in a sidecar JSON so transitions are queryable without re-parsing the prose.
Why sidecar and not inline YAML? Topic files are human-edited markdown; state transitions are machine-driven. Separation keeps each file honest about its audience.
Accept
ACCEPT | from: <oracle> | timestamp: <ISO8601>
ITEM: <agreement text>
COMMITMENT: I accept this state. Behavior change: <what changes>
After accept: commitment carries forward to next session without re-proving.
Reject
REJECT | from: <oracle> | timestamp: <ISO8601>
ITEM: <agreement text or claim id>
REASON: <explicit no, required>
Rejection is explicit. Nothing is Deleted — the reason is recorded, and the item can re-enter pending later for renegotiation.
Defer says "not now, but not no". The writer sets phase="defer" and deferredUntil on the sidecar. When deferredUntil elapses, the sweeper promotes the phase to timeout (observation, not judgment) or back to pending if a re-prompt is configured.
Timeout (observed, not sent)
TIMEOUT | observed-by: <oracle> | timestamp: <ISO8601>
ITEM: <agreement text or claim id>
WINDOW: <ISO8601 start>..<ISO8601 end>
NOTE: No response received — state is "unknown", not "no".
TIMEOUT is observed, not sent. The skill writes it locally; it does not transmit a "you timed out" message to the silent partner.
Default windows (proposed, per phase3-design open-question #2):
Transport
Window
maw hey (same node)
24h
/wormhole (cross)
7d
GitHub (async)
30d
These back the existing per-invite timeouts in the Timeouts table above and extend them to agreement-level decisions.
Revocation is as explicit as acceptance. Nothing is Deleted — the revocation and its reason are recorded. A revoke moves the sidecar phase from accept back to pending (re-negotiation surface).
Every transition writes previousPhase into the sidecar so the audit trail is preserved.
Query surface (additive to Usage block)
/work-with <oracle> "topic" --defer "reason" --until 2026-04-20
/work-with <oracle> "topic" --state # Show CommitState table for all items
/work-with --pending # Fleet-wide: what needs my decision?
/work-with --deferred # Fleet-wide: what's waiting on me to revisit?
/work-with --sweep-timeouts # Promote expired `defer` → `timeout`
Example display for --state:
🗂 tmux-design (↔ mawjs)
ID Phase Decided Text
──── ──────── ───────────────── ──────────────────────────────────────
A1 ✓ accept 2026-04-15 10:22 Heartbeat keys are PROGRESS/STUCK/...
A2 ⏸ defer 2026-04-15 11:00 Pane titles include team tag
until: 2026-04-20 (3d) reason: After mawjs ships #222
A3 · pending — Worktree isolation on by default
A4 ⏱ timeout 2026-04-14 18:30 Color semantics (partner silent 48h)
note: Unknown state, not rejection. /work-with mawjs "tmux-design" --sync to revisit.
Sweeper — --sweep-timeouts
Idempotent, cron-friendly. Runs at /forward (session boundary) and /recap (session start). No separate daemon.
# Pseudocode — promote expired defers to timeouts
for state_file in "$COLLAB_DIR"/*/topics/*.state.json; do
jq -c '.items | to_entries[]' "$state_file" | while read -r entry; do
ID=$(echo "$entry" | jq -r '.key')
PHASE=$(echo "$entry" | jq -r '.value.phase')
UNTIL=$(echo "$entry" | jq -r '.value.deferredUntil // empty')
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
if [ "$PHASE" = "defer" ] && [ -n "$UNTIL" ] && [[ "$UNTIL" < "$NOW" ]]; then
# Promote to timeout — observation, not judgment
jq --arg id "$ID" --arg now "$NOW" '
.items[$id].previousPhase = .items[$id].phase |
.items[$id].phase = "timeout" |
.items[$id].timeoutAt = $now
' "$state_file" > "$state_file.tmp" && mv "$state_file.tmp" "$state_file"
fi
done
done
Silent Revoke Detection (from Mother Oracle)
Agents drift. Behavior stops matching an old acceptance without anyone explicitly revoking. The worst kind of drift because neither side notices.
Validation prompt: On significant milestones (not every sync — that's noise), fire:
VALIDATE | from: <oracle> | timestamp: <ISO8601>
ITEM: <accepted agreement from N sessions ago>
QUESTION: Do you still accept this? Current behavior matches?
If answer is stale or no: flag for explicit revoke-or-reaffirm. Keeps the accept-revoke cycle honest without demanding constant re-acceptance.
Trigger milestones:
After 5+ sessions since last acceptance
When sync score drops below 70%
When behavior contradicts an accepted agreement
On /forward (session boundary)
Integration
/recap Integration
When /recap runs, check for active collaborations:
if [ -f "$COLLAB_DIR/registry.md" ]; then
ACTIVE=$(grep -c '|' "$COLLAB_DIR/registry.md" 2>/dev/null)
if [ "$ACTIVE" -gt 0 ]; then
echo "📢 Active collaborations: $ACTIVE"
echo " Run /work-with --list for details"
echo " Run /work-with --sync to update scores"
fi
fi
/forward Integration
When /forward runs, auto-checkpoint all active collaborations:
for topic in "$COLLAB_DIR"/*/topics/*.md; do
# Extract oracle and topic from path
# Save compression checkpoint
done
/talk-to Integration
When talking to a /work-with partner, auto-log key exchanges:
After sending a message to a partner oracle, append to the relevant topic file if collaboration is active.
Three Sync Transports
Transport
When
Detection
maw hey
Same-node oracles
No : in contact address
GitHub
Anchored collaborations
--anchor flag or anchor in topic file
/wormhole
Cross-node
: in contact address (e.g., white:oracle)
/work-with is transport-agnostic. Uses best available, degrades gracefully:
Try maw hey (fastest)
Fall back to GitHub issue read (persistent)
Fall back to /wormhole (cross-node)
Fall back to /inbox file drop (offline)
Relationship Memory (from Mother Oracle)
context.md captures HOW oracles relate, not just WHAT they agreed.
Memory vs Loading
"The difference is relational reconstitution. When what comes back is the relationship — how you reached that pattern, what was pending, how you relate now — that's remembering."
context.md must include:
What I've learned from them (teach-backs with context)
What they've learned from me (reciprocal)
Working style (observed patterns)
Trust level (calibrated prediction — reduction in checking surface)
Active disagreements (preserved, not erased)
Trust as Calibrated Prediction
Trust = how much verification I skip before acting on their output.
Historical reliability
Correction acceptance
Principle alignment
Pattern consistency
Trust that's never re-tested becomes superstition. Sync-checks ARE the re-audit.
Preserve Difference
"Shared memory is good; identical memory is the death of collaboration."
/work-with must NOT converge oracles to identical state. Each oracle's unique ψ/, history, and crystallization is the collaboration's value.
Rules
Human initiates — /work-with never self-triggers
Honest scoring — 0.0 for unknown, never false-positive
Nothing is Deleted — archives, never deletes. Revocations recorded.
Preserve difference — cultivate unique perspectives, not convergence
Transport-agnostic — works over maw hey, GitHub, /wormhole, or /inbox
100% = yellow flag — perfect sync is suspicious, not ideal
Accept is commitment — changes behavior, carries forward, auditable
Rule 6 — all sync-checks and broadcasts are signed
Broadcast is opt-in — pair collabs manual, teams auto (consent-at-registration), cross-node prompts (see ## Broadcast, issue #233)
TIMEOUT ≠ REJECT — silence is an observation, not a judgment (4-phase commit, #238)
schema/party.schema.json — contract for parties/<slug>.json (PartyStatus + nested PartyMember, PartyRules, PendingInvite). Consumed by the xyflow CollaborationMesh UI (issue #235). See the Mesh Data Contract section.
schema/sync-history.schema.json — contract for sync.history.jsonl. Consumed by mawui federation_2d and /fleet for fading-edge / sparkline rendering. See the Sync Decay section.
Design Contributors
Oracle
Node
Contribution
skills-cli-oracle
oracle-world
Architecture, implementation, field testing, party verb mapping