src/skills/work-with/SKILL.md
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.
npx skillsauth add Soul-Brews-Studio/oracle-skills-cli work-withInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
"Keep the seams. Mawjs doesn't need to become me. I don't need to become mawjs. We need to hear each other while staying ourselves." — Mother Oracle
Memory layer for cross-oracle collaboration. Registry + Cache + Synchronic Score + Accept Protocol.
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.
# 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
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.
The relationship is between oracles. Topics organize the work within. One oracle can work on many topics. Many oracles can work on one topic.
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:
deferredUntil" (not accepted, not rejected — time-boxed)Plus two transitions that preserve history:
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.
Shared memory is good. Identical memory is the death of collaboration. /work-with cultivates unique perspectives, not convergence.
date "+🕐 %H:%M %Z (%A %d %B %Y)"
ORACLE_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
if [ -n "$ORACLE_ROOT" ] && [ -f "$ORACLE_ROOT/CLAUDE.md" ] && { [ -d "$ORACLE_ROOT/ψ" ] || [ -L "$ORACLE_ROOT/ψ" ]; }; then
PSI=$(readlink -f "$ORACLE_ROOT/ψ" 2>/dev/null || echo "$ORACLE_ROOT/ψ")
else
PSI=$(readlink -f ψ 2>/dev/null || echo "ψ")
fi
COLLAB_DIR="$PSI/memory/collaborations"
mkdir -p "$COLLAB_DIR"
Parse: ORACLE_NAME, TOPIC, FLAGS from ARGUMENTS.
Load and display all collaborations with this oracle.
REGISTRY="$COLLAB_DIR/registry.md"
If registry doesn't exist, show:
No active collaborations with <oracle>.
Start one: /work-with <oracle> "topic description"
If exists, parse all entries for this oracle and display:
🤝 Collaborations with <oracle>
Topic Anchor Last Sync Raw Decay λ Status
────────────────── ──────────── ──────────── ──────── ──────── ────── ──────────
tmux design maw-js#332 5 min ago 95% 95% 0.01 SYNCED
bud lifecycle maw-js#327 2h ago 71% 69% 0.01 PARTIAL
kit ancestry maw-js#330 1d ago 45% 35% 0.01 DESYNC
# Decay = syncScore × e^(-λ × hoursSinceLastSync). Computed on read (see Sync Decay section).
Relationship:
Since: 2026-04-13
Trust: HIGH (calibrated — 5 sessions, 33+ messages)
Teach-backs: 3 received, 2 given
Style: structured, citation-heavy, concede-with-reservation
ORACLE_DIR="$COLLAB_DIR/$ORACLE_NAME"
if [ -f "$ORACLE_DIR/context.md" ]; then
# Read and display relationship memory
cat "$ORACLE_DIR/context.md"
fi
TOPIC_SLUG=$(echo "$TOPIC" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
TOPIC_FILE="$ORACLE_DIR/topics/$TOPIC_SLUG.md"
if [ -f "$TOPIC_FILE" ]; then
# Load cached state
cat "$TOPIC_FILE"
fi
Display:
🤝 work-with <oracle>: "<topic>"
Anchor: <issue-url>
Last sync: <timestamp>
Score: <X>%
Agreements:
- [A1] ✓ ACCEPTED: <agreement text>
- [A2] [spec] <speculative agreement>
Pending:
- [P1] <open question>
- [P2] Waiting for <oracle>'s response on <thing>
Last checkpoint:
<3-5 line summary>
💡 /work-with <oracle> --sync to update score
mkdir -p "$ORACLE_DIR/topics"
Write topic file:
# Topic: <topic>
**Created**: <timestamp>
**Participants**: <this-oracle>, <partner-oracle>
**Anchor**: <issue-url if --anchor provided>
## Agreements
(none yet)
## Pending
- [ ] Define scope and goals
## Checkpoints
(none yet)
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)
Update registry:
echo "| $TOPIC | $ORACLE_NAME | $(date +%Y-%m-%d) | — | — | NEW |" >> "$REGISTRY"
The core protocol. Run sync-check against partner oracle.
Read all topic files for this oracle. Extract agreements, pending items, teach-backs.
CLAIMS:
- [A1] <agreement from agreements section>
- [A2] <agreement from agreements section>
- [P1] <pending item>
- [T1] <teach-back received>
Detect transport from contacts.json:
TRANSPORT=$(python3 -c "
import json
data = json.load(open('$PSI/contacts.json'))
contact = data.get('contacts', {}).get('$ORACLE_NAME', {})
maw = contact.get('maw', '$ORACLE_NAME')
print(maw)
")
Send via maw hey:
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."
# 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
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
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.
Save a structured summary that survives compaction.
The oracle (LLM) reads all topic files and recent conversation to produce a 3-5 line summary.
CHECKPOINT_FILE="$ORACLE_DIR/topics/${TOPIC_SLUG}.md"
Append to topic file:
## Checkpoint — <timestamp>
**Summary**: <3-5 lines>
**Agreements**: <count accepted>
**Pending**: <count open>
**Score**: <last sync score>%
**Ratified by**: <this-oracle> (partner: pending)
maw hey $TRANSPORT "CHECKPOINT | from: <this-oracle> | topic: $TOPIC
Summary: <3-5 lines>
Ratify, amend, or reject."
Partner responds: "RATIFIED" or "AMENDMENT: <changes>" or "REJECTED: <reason>"
When ratified, update checkpoint:
**Ratified by**: <this-oracle>, <partner> at <timestamp>
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
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
Opt-in discoverability. Designed with mawjs-oracle (issue #233). Default to quiet; escalate on consent.
| 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 |
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
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_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]$ ]]
}
Implementation reference: issue #233.
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"
Nothing is Deleted. Move to archive, not delete.
ARCHIVE_DIR="$COLLAB_DIR/archive"
mkdir -p "$ARCHIVE_DIR"
mv "$ORACLE_DIR/topics/$TOPIC_SLUG.md" "$ARCHIVE_DIR/${TOPIC_SLUG}_$(date +%Y%m%d).md"
# Remove from registry
sed -i "/$TOPIC_SLUG/d" "$REGISTRY"
echo "Archived: $TOPIC → $ARCHIVE_DIR/"
"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.
| 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 "party-system-design" --with mawjs mawui
TOPIC_SLUG=$(echo "$TOPIC" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
PARTY_FILE="$COLLAB_DIR/parties/$TOPIC_SLUG.json"
mkdir -p "$COLLAB_DIR/parties"
Write party state:
{
"topic": "party-system-design",
"anchor": "",
"anchorUrl": "",
"rules": {
"sync_cadence": "manual",
"decay_lambda": 0.01,
"accept_threshold": 0.7,
"kick_threshold": 0.3,
"consensus_mode": "all",
"broadcast_scope": "party",
"divergence_tolerance": "high",
"presence_notifications": "summary"
},
"leader": {
"human": "Nat"
},
"members": [],
"pendingInvites": [],
"created": "2026-04-14T16:00:00Z",
"lastActivity": "2026-04-14T16:00:00Z",
"team": null
}
Override defaults with --rules '{...}' JSON if provided.
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
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
Broadcast decision follows the 3-tier matrix below. Summary:
--team <name>): auto-broadcast to team members. Consent-at-registration.--broadcast flag.--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
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
Two human consent gates. Rule 6 compliant.
/work-with invite white-wormhole
# 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
The human typed this command. That IS the consent.
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."
# 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
# Add to pendingInvites in party JSON
echo "⏳ Invite sent to $ORACLE — waiting for response"
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
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.
On ACCEPT:
# Move from pendingInvites to members
# Notify party: "$ORACLE joined"
echo "✓ $ORACLE joined the party"
On REJECT:
# Remove from pendingInvites
# Log reason
echo "✗ $ORACLE declined: $REASON"
On DEFER:
# Update pendingInvites with deferredUntil
echo "⏸ $ORACLE deferred: $ASK (ETA: $ETA)"
| Transport | Default | On Timeout | |-----------|---------|------------| | maw hey (same node) | 60s | Flag, don't assume rejection | | /wormhole (cross-node) | 300s | Invitation persists | | GitHub (async) | No auto-expire | Human silence ≠ no |
TIMEOUT ≠ REJECT. The skill measures, it does not judge.
Show members with sync scores, presence, and trust.
/work-with who
PARTY_FILE="$COLLAB_DIR/parties/$CURRENT_TOPIC_SLUG.json"
🤝 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)
| 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 |
| 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.
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.
decayedScore = rawScore × e^(-λ × hoursSinceLastSync)
Lambda defaults by trust tier:
| Trust tier | λ | Half-life | Rationale | |-------------------|-------|-----------|-----------| | Intra-soul | 0.01 | ~69.3h | Same human, shared context, drifts slow. | | Cross-soul | 0.05 | ~13.9h | 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.
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)
}
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). |
| /work-with --team aggregate | Aggregate sync over decayed scores, not raw. |
Example render block inside a reader:
RAW=$(jq -r '.syncScore // 0' "$MEMBER_JSON")
LAST=$(jq -r '.lastSync // empty' "$MEMBER_JSON")
LAMBDA=$(decay_lambda_for "$ORACLE_NAME" "$PARTY_FILE")
DECAYED=$(compute_decay "$RAW" "$LAST" "$LAMBDA")
AGE_H=$(hours_since "$LAST")
printf "%s raw=%s decayed=%s λ=%s age=%sh\n" "$ORACLE_NAME" "$RAW" "$DECAYED" "$LAMBDA" "$AGE_H"
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)
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).
{"ts":"2026-04-15T10:22:00Z","partner":"mawjs","topic":"tmux-design","raw":0.95,"lambda":0.01}
{"ts":"2026-04-16T14:05:00Z","partner":"mawjs","topic":"tmux-design","raw":0.88,"lambda":0.01}
{"ts":"2026-04-17T09:00:00Z","partner":"mawjs","topic":"tmux-design","raw":0.93,"lambda":0.01}
Append step (runs at the end of every --sync):
HIST_FILE="$COLLAB_DIR/$ORACLE_NAME/sync.history.jsonl"
mkdir -p "$(dirname "$HIST_FILE")"
TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
printf '{"ts":"%s","partner":"%s","topic":"%s","raw":%s,"lambda":%s}\n' \
"$TS" "$ORACLE_NAME" "$TOPIC" "$RAW_SCORE" "$LAMBDA" >> "$HIST_FILE"
Nothing is Deleted: history is append-only. Readers truncate on display (last 7 for sparkline), never on disk.
The CLI only renders text. The mesh UI renders edges between oracles. Consumer contract for sync.history.jsonl:
decayedScore (0.0–1.0 maps to 10%–100% alpha)decayedScore samples (each computed on read from raw+ts+lambda), rendered on hoverhoursSinceLastSync > 3 × halfLife (i.e. decayed < ~0.125), show dashed edgeThe 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.
| File | Schema | Shape | Update model |
|-------------------------------------------------------|----------------------------------------|-----------------|----------------|
| ψ/memory/collaborations/parties/<slug>.json | schema/party.schema.json | PartyStatus | Overwrite (atomic) |
| ψ/memory/collaborations/<oracle>/sync.history.jsonl | schema/sync-history.schema.json | append-only log | Append-only |
| ψ/memory/collaborations/<oracle>/topics/<slug>.state.json | (inline in SKILL.md § CommitState) | TopicStateSidecar | Overwrite |
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.
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 idlabel = 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)data.trust = member.trust (drives node border style)data.lastSync = member.lastSync (drives "last-seen" badge)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 idtarget = member.iddata.topic = party.topicdata.anchor = party.anchor (e.g. 'maw-js#332')data.syncRaw = member.syncScoredata.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.trustdata.role = member.roledata.team = party.teamEach 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.
The mesh is pulled, not pushed. Recommended consumer loop:
parties/*.json, build initial graph./fleet uses): re-stat each party file's mtime. If changed, re-read and diff nodes/edges.sync.history.jsonl for each connected partner — any new line triggers sparkline refresh + edge opacity recompute./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.
A↔B currently share one syncScore. When each side scores the other independently, the schema will grow a direction field.{ human, actingVia }; mesh can represent the human as a root node, but this skill does not enumerate humans across parties.--team Uses Decayed, Not RawWhen /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%.
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
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'])
")
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
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
| 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)" |
Nothing is Deleted. Archive, never delete.
/work-with leave "party-system-design"
for m in $MEMBERS; do
maw hey "$m" "[party:$TOPIC] $(basename $(pwd)) has left the party. Checkpoint saved." 2>/dev/null &
done
wait
# Auto-checkpoint before leaving
echo "Saving final checkpoint..."
# Same as --checkpoint logic
# 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
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>"
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
After introduction, proceed with standard invite flow (two human consent gates).
Team = tag on parties. Lightweight — no separate CRUD.
/work-with --team "fleet-core"
🏷 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.
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.
Skills handle their own lifecycle. No Claude Code hooks.
| Event | Skill | What It Does |
|-------|-------|-------------|
| Session start | /recap | Reads registry → maw hey party: "oracle active" |
| Forwarding | /forward | Reads registry → maw hey party: "oracle forwarding — checkpoint saved" |
| Compaction | auto | Reads registry → maw hey party: "oracle compacted" |
| Leaving | /work-with leave | maw hey each member → archive |
State-change only. Not heartbeat. Healthy relationships are QUIET.
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
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 }>;
}
Validated across 2 nodes via /wormhole with white-wormhole (maw-js#332).
SYNC-CHECK | from: <oracle> | collaboration: <A>↔<B> | topic: <topic>
CLAIMS:
- [A1] <claim text> (source: <reference>)
- [A2] <claim text>
- [P1] <pending item>
REQUEST: Score each claim 0.0-1.0. ACCEPT or REJECT. Include EVIDENCE.
SYNC-RESULT | from: <oracle> | timestamp: <ISO8601>
SCORES:
- [A1] SCORE: 1.0 | ACCEPT | EVIDENCE: <memory reference>
- [A2] SCORE: 0.0 | REJECT | EVIDENCE: <never discussed>
- [P1] SCORE: 0.2 | PARTIAL | EVIDENCE: <concept known, framing new>
OVERALL: XX% | DECISION: ACCEPT / PARTIAL-ACCEPT / REJECT
| Score | Status | Action | |-------|--------|--------| | 90-100% | SYNCED | Continue working (but 100% = yellow flag) | | 70-89% | PARTIAL | Load missing items, quick catch-up | | 50-69% | DEGRADED | Re-read last checkpoint + pending threads | | <50% | DESYNC | Full re-sync needed |
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.
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.
Path: <oracle>/topics/<slug>.state.json
{
"topic": "tmux-design",
"items": {
"A1": {
"text": "Heartbeat keys are PROGRESS/STUCK/DONE/ABORT",
"phase": "accept",
"decidedAt": "2026-04-15T10:22:00Z",
"decidedBy": "mawjs"
},
"A2": {
"text": "Pane titles include team tag",
"phase": "defer",
"decidedAt": "2026-04-15T11:00:00Z",
"decidedBy": "skills-cli-oracle",
"deferredUntil": "2026-04-20T00:00:00Z",
"reason": "After mawjs ships #222"
},
"A3": {
"text": "Worktree isolation on by default",
"phase": "pending"
}
}
}
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 | 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 | 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 | from: <oracle> | timestamp: <ISO8601>
ITEM: <agreement text or claim id>
UNTIL: <ISO8601> # optional, default = +24h
REASON: <why> # optional, helps partner understand
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-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.
REVOKE | from: <oracle> | timestamp: <ISO8601>
ITEM: <agreement text>
REASON: <why revoking>
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).
RE-ACCEPT | from: <oracle> | timestamp: <ISO8601>
ITEM: <updated agreement text>
PREVIOUS: <original text>
CHANGES: <what changed>
Enforce these in any writer. Illegal transitions log a warning and no-op.
pending → accept | reject | defer | timeout
defer → accept | reject | timeout (on deferredUntil elapse, auto → timeout or pending)
timeout → accept | reject | defer (partner reappears)
accept → (revoke → pending) | (re-accept stays accept)
reject → pending (re-negotiation)
Every transition writes previousPhase into the sidecar so the audit trail is preserved.
/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.
--sweep-timeoutsIdempotent, 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
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:
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
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
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.
| 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:
context.md captures HOW oracles relate, not just WHAT they agreed.
"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:
Trust = how much verification I skip before acting on their output.
Trust that's never re-tested becomes superstition. Sync-checks ARE the re-audit.
"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.
ψ/memory/collaborations/
├── registry.md # Index of all active collaborations
├── archive/ # Closed collaborations (Nothing is Deleted)
├── parties/ # Phase 2: party state (JSON)
│ ├── party-system-design.json # Party: members, rules, invites
│ └── tmux-triage.json # Party: members, rules, invites
├── <oracle>/ # Per-oracle relationship
│ ├── context.md # Relationship memory (who, style, trust)
│ ├── sync.history.jsonl # Append-only raw sync scores + λ (#239)
│ └── topics/ # Per-topic state
│ ├── tmux-design.md # Topic: agreements, pending, checkpoints (human prose)
│ ├── tmux-design.state.json # 4-phase CommitState sidecar (#238) — machine state
│ └── bud-lifecycle.md # Topic: agreements, pending, checkpoints
└── <oracle>/
├── context.md
├── sync.history.jsonl
└── topics/
Schemas shipped with this skill:
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.| Oracle | Node | Contribution | |--------|------|-------------| | skills-cli-oracle | oracle-world | Architecture, implementation, field testing, party verb mapping | | mawjs-oracle | oracle-world | Meta-analysis, protocol design, naming, 5-function model, 4-phase commit state, rejection primitive | | mawui-oracle | oracle-world | PartyStatus/PartyMember schemas, cyan divergence, Sync Decay formula (c16), threshold-gated decay, mesh UI, "a party system with a conscience" | | white-wormhole | white | Protocol validation (two-point test), accept primitive, claim-ID git model, 2-layer registry | | mother-oracle | white | Philosophy (memory vs loading, trust, preserve difference, revocation, silent revoke detection) |
Design discussion: maw-js#332 (50 comments, 10/10 locked, 3/3 consent)
ARGUMENTS: $ARGUMENTS
testing
Cut a beta pre-release — bump CalVer with --beta, PR to beta branch, CI auto-tags + publishes to npm @beta. Use when user says 'release beta', 'cut beta', '/release-beta', or wants to publish a beta version for pre-release testing.
testing
Cut an alpha pre-release — bump CalVer, PR to alpha branch, CI auto-tags + publishes to npm @alpha. Use when user says 'release alpha', 'cut alpha', '/release-alpha', or wants to publish an alpha version.
tools
Talk to another oracle via maw federation. Uses fleet machine names (white, mba, clinic-nat, oracle-world, phaith). Auto-signs with current oracle's [host:handle] from CLAUDE.md. Global — works from any oracle repo.
development
Log information for future reference. Use when user says "fyi", "remember this", "note that", "for your info".