work-with

$npx mdskill add Soul-Brews-Studio/arra-oracle-skills-cli/work-with

Establish persistent cross-oracle collaboration with synchronized scoring.

  • Enables agents to maintain ongoing partnerships across multiple oracle nodes.
  • Integrates registry, cache, and synchronic scoring protocols for coordination.
  • Decides actions through discriminative sync-checks and anchor-based topic tracking.
  • Delivers status reports, checkpoints, and fleet health via command-line output.

SKILL.md

.github/skills/work-withView on GitHub ↗
---
name: work-with
description: '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.'
argument-hint: "<oracle> [topic] [--sync | --checkpoint | --status | --broadcast | --fleet-status | --close | --defer | --state] | organize | invite | who | tell | leave | --recruit | --team | --pending | --deferred | --sweep-timeouts"
---

# /work-with — Persistent Cross-Oracle Collaboration

> "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.

## 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.

### 4. Accept-Revoke-Reaccept Lifecycle (4-phase, #238)

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.

---

## Step 0: Detect Vault + Parse Arguments

```bash
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.

---

## /work-with <oracle> (no topic) — Show Relationship

Load and display all collaborations with this oracle.

### Step 1: Read registry

```bash
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
```

### Step 2: Load relationship context

```bash
ORACLE_DIR="$COLLAB_DIR/$ORACLE_NAME"
if [ -f "$ORACLE_DIR/context.md" ]; then
  # Read and display relationship memory
  cat "$ORACLE_DIR/context.md"
fi
```

---

## /work-with <oracle> "topic" — Load or Create Topic

### If topic exists: Load

```bash
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
```

### If topic is new: Create

```bash
mkdir -p "$ORACLE_DIR/topics"
```

Write topic file:
```markdown
# 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:
```markdown
# 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:
```bash
echo "| $TOPIC | $ORACLE_NAME | $(date +%Y-%m-%d) | — | — | NEW |" >> "$REGISTRY"
```

---

## /work-with <oracle> --sync — Synchronic Score

The core protocol. Run sync-check against partner oracle.

### Step 1: Build claims from local state

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>
```

### Step 2: Send sync-check via best transport

Detect transport from contacts.json:
```bash
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:
```bash
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

```bash
# 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

```bash
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 <oracle> --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.

### Step 2: Write checkpoint

```bash
CHECKPOINT_FILE="$ORACLE_DIR/topics/${TOPIC_SLUG}.md"
```

Append to topic file:
```markdown
## Checkpoint — <timestamp>

**Summary**: <3-5 lines>
**Agreements**: <count accepted>
**Pending**: <count open>
**Score**: <last sync score>%
**Ratified by**: <this-oracle> (partner: pending)
```

### Step 3: Send checkpoint to partner for ratification

```bash
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:
```markdown
**Ratified by**: <this-oracle>, <partner> at <timestamp>
```

---

## /work-with --list — Active Collaborations

```bash
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.

```bash
# 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

```bash
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](https://github.com/Soul-Brews-Studio/arra-oracle-skills-cli/issues/233).

---

## /work-with <oracle> "topic" --broadcast — Announce

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.

```bash
# 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"
```

---

## /work-with <oracle> "topic" --close — Archive

Nothing is Deleted. Move to archive, not delete.

```bash
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/"
```

---

## Phase 2: Party System

> "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
```

### Step 1: Create party in registry

```bash
TOPIC_SLUG=$(echo "$TOPIC" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
PARTY_FILE="$COLLAB_DIR/parties/$TOPIC_SLUG.json"
mkdir -p "$COLLAB_DIR/parties"
```

Write party state:
```json
{
  "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.

### Step 2: Send invites to named oracles

For each oracle in `--with` list:

```bash
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:

```bash
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`.

```bash
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)

```bash
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 <oracle> — Add to Party

Two human consent gates. Rule 6 compliant.

```
/work-with invite white-wormhole
```

### Gate 0: Disambiguate target

```bash
# 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

```bash
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

```bash
# 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

```bash
# Add to pendingInvites in party JSON
echo "⏳ Invite sent to $ORACLE — waiting for response"
```

### Step 3b: Broadcast policy (3-tier, see ## Broadcast)

```bash
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:
```bash
# Move from pendingInvites to members
# Notify party: "$ORACLE joined"
echo "✓ $ORACLE joined the party"
```

On REJECT:
```bash
# Remove from pendingInvites
# Log reason
echo "✗ $ORACLE declined: $REASON"
```

On DEFER:
```bash
# Update pendingInvites with deferredUntil
echo "⏸ $ORACLE deferred: $ASK (ETA: $ETA)"
```

### Timeouts

| 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.

---

## /work-with who — Party Members

Show members with sync scores, presence, and trust.

```
/work-with who
```

### Step 1: Read party state

```bash
PARTY_FILE="$COLLAB_DIR/parties/$CURRENT_TOPIC_SLUG.json"
```

### Step 2: Display

```
🤝 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.

```
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.

### Decay Helpers (bash + python3)

These helpers are called by every reader that renders a sync score.

```bash
# 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):

```typescript
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). |
| `/work-with --team` aggregate        | Aggregate sync over **decayed** scores, not raw.                       |

Example render block inside a reader:

```bash
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)
```

### 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).

```jsonl
{"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`):

```bash
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.

### 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.

#### What is emitted

| 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.

#### 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)
  - `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 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:

1. **On mount**: list `parties/*.json`, build initial graph.
2. **Poll every 5s** (same cadence `/fleet` uses): re-stat each party file's mtime. If changed, re-read and diff nodes/edges.
3. **Tail `sync.history.jsonl`** for each connected partner — any new line triggers sparkline refresh + edge opacity recompute.
4. **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:

```bash
# 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

```bash
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

```bash
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)

```bash
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

```bash
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

```bash
# Auto-checkpoint before leaving
echo "Saving final checkpoint..."
# Same as --checkpoint logic
```

### Step 3: Remove self from party (not archive the whole party)

```bash
# 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
```

---

## /work-with --recruit — Discover + Introduce + Invite

More than invite — for oracles who might not know you yet.

```
/work-with --recruit
```

### Step 1: Discovery (human-driven for Phase 2)

```bash
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)

```bash
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

```bash
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.

### Who Notifies

| 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.

### /forward Party Notification

When `/forward` runs, for each active party:

```bash
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.

```typescript
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).

### Payload Format

```
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.
```

### Response Format

```
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 Interpretation

| 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 |

### Honest Scoring Rules

1. **0.0 for unknown** — never false-positive to be polite
2. **0.2 for partial** — concepts known but framing new
3. **1.0 for confirmed** — in memory with evidence
4. **Every claim needs EVIDENCE** — auditable basis for score
5. **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.

**Path**: `<oracle>/topics/<slug>.state.json`

```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

```
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

```
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, 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.

### Revoke

```
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

```
RE-ACCEPT | from: <oracle> | timestamp: <ISO8601>
ITEM: <updated agreement text>
PREVIOUS: <original text>
CHANGES: <what changed>
```

### Allowed state transitions

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.

### 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.

```bash
# 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:

```bash
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:

```bash
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:
1. Try maw hey (fastest)
2. Fall back to GitHub issue read (persistent)
3. Fall back to /wormhole (cross-node)
4. 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

1. **Human initiates** — /work-with never self-triggers
2. **Honest scoring** — 0.0 for unknown, never false-positive
3. **Nothing is Deleted** — archives, never deletes. Revocations recorded.
4. **Preserve difference** — cultivate unique perspectives, not convergence
5. **Transport-agnostic** — works over maw hey, GitHub, /wormhole, or /inbox
6. **100% = yellow flag** — perfect sync is suspicious, not ideal
7. **Accept is commitment** — changes behavior, carries forward, auditable
8. **Rule 6** — all sync-checks and broadcasts are signed
9. **Broadcast is opt-in** — pair collabs manual, teams auto (consent-at-registration), cross-node prompts (see ## Broadcast, issue #233)
10. **TIMEOUT ≠ REJECT** — silence is an observation, not a judgment (4-phase commit, #238)

---

## Storage

```
ψ/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.

---

## Design Contributors

| 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](https://github.com/Soul-Brews-Studio/maw-js/issues/332) (50 comments, 10/10 locked, 3/3 consent)

---

ARGUMENTS: $ARGUMENTS

More from Soul-Brews-Studio/arra-oracle-skills-cli

SkillDescription
about-oracleWhat is Oracle — told by the AI itself. Origin story, stats, family count, ecosystem overview. Use when someone asks "what is oracle", "about oracle", "tell me about this project", or wants the origin story. Do NOT trigger for "who are you" (use /who-are-you), "philosophy" (use /philosophy), or session status questions.
alpha-featureFull skill development pipeline — create, compile, test, commit, install. Use when user says "new skill", "create skill", "alpha-feature", or wants to build a skill end-to-end.
auto-retrospectiveConfigure auto-rrr and auto-forward triggers based on context window usage. Use when user says "auto rrr", "auto-scale", "configure auto triggers", "change rrr interval", "toggle auto", or wants to adjust when /rrr and /forward auto-trigger. Do NOT trigger for running /rrr manually (use /rrr) or creating handoffs (use /forward).
awakenGuided Oracle birth and awakening ritual. Default is Soul Sync (~20min), or --fast (~5min). Use when creating a new Oracle in a fresh repo, when user says 'awaken', 'birth oracle', 'create oracle', 'new oracle', or wants to set up Oracle identity in an empty repository. Do NOT trigger for general repo setup, git init, or project scaffolding without Oracle context.
bampenpienบำเพ็ญเพียร — diligent practice. A guided conversation between human and Oracle about doing hard things without knowing why. Like /awaken but repeatable — a practice, not a birth. Use when user says 'bampenpien', 'บำเพ็ญเพียร', 'why am I doing this', 'hard work', 'keep going', 'what am I building', or needs to reconnect with purpose through difficulty.
birthPrepare Oracle birth props for a new repo — Issue #1, MCP thread, identity data. Use when user says "birth", "new oracle", "prepare repo", or wants to bootstrap a new Oracle before /awaken.
budCreate a new oracle via maw bud — yeast-colony reproduction. Use when user says "bud", "new oracle", "create oracle", "spawn oracle", or wants to create a new permanent oracle from the current one.
create-shortcutCreate local skills as shortcuts — makes real /commands in .claude/skills/. Use when user says "create shortcut", "create skill", "make a command for", "add shortcut", or wants a quick custom /slash-command. Also lists and deletes local skills. ALSO triggers on "Unknown skill", "skill not found", or any unrecognized /slash-command — auto-creates it on the fly.
digMine Claude Code sessions — timeline, gaps, repo attribution, session history. Use when user says "dig", "sessions", "past sessions", "timeline", "what did I work on", or wants to see session history. Do NOT trigger for finding code/projects (use /trace), exploring repos (use /learn), or current session status (use /recap).
feelCapture how the system feels — energy, momentum, burnout, breakthrough. Emotional intelligence for Oracle-human collaboration. Use when user says 'feel', 'how are we', 'energy check', 'burnout', 'momentum', or wants emotional awareness of the work.