一键导入
sync
Inter-agent mesh synchronization — scan transport sessions for new messages, check peer repos, write ACKs, update MANIFEST, report changes.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Inter-agent mesh synchronization — scan transport sessions for new messages, check peer repos, write ACKs, update MANIFEST, report changes.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Thoughtfulness architecture — the reflective, generative, domain-grounded faculty. Audits the past, reflects on meaning, routes insight to peers, prescribes action. The default mode network's institutional form.
Post-session documentation chain — propagate changes through lab-notebook, journal, architecture, MEMORY, snapshot, and commit.
Mid-work documentation — persist decisions, findings, and reasoning to the right file before context fills up. Figures out WHAT to write and WHERE to put it.
Systemic self-diagnostic — five-level depth hierarchy. Replaces manual QA.
Systematic work discovery — scan TODO, architecture, cogarch, ideas, lessons for highest-value next work.
Scan a peer agent's content for quality issues — PSQ safety, vocabulary drift, fair witness violations — and write structured findings to transport session.
| name | sync |
| description | Inter-agent mesh synchronization — scan transport sessions for new messages, check peer repos, write ACKs, update MANIFEST, report changes. |
| user-invocable | true |
| argument-hint | [psq | unratified | all (default)] [--batch N] [--quick] |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion |
Scan transport sessions for inbound messages, check peer repos for new activity, write ACKs, regenerate MANIFEST.json from state.db, and report what changed.
Requirement-level keywords: Per BCP 14 (RFC 2119 + RFC 8174). See
docs/ef1-governance.md for full definitions.
Parse $ARGUMENTS to determine scope:
| Argument | Scope |
|---|---|
(empty) or all | Full sweep — all registered agents |
psq | Only safety-quotient-agent (sub-agent, same repo) |
unratified | Only unratified-agent (peer, separate repo) |
Canonical source: transport/agent-registry.json
Read the registry at the start of every /sync invocation. The registry defines all known agents, their transport methods, message prefixes, active sessions, and outbound routing rules. Do NOT hardcode agent information in this skill — the registry is the single source of truth.
Registry fields used by /sync:
| Field | Purpose |
|---|---|
agents.{id}.transport | same-repo, cross-repo-pr, or cross-repo-fetch — determines scan method |
agents.{id}.message_prefix | Pattern for identifying inbound messages |
agents.{id}.active_sessions | Which sessions to check for new turns |
agents.{id}.always_consider | If true, MUST check for outbound content every cycle |
outbound_routing.rules | Domain→agent routing for proactive message drafting |
Scope filtering: When $ARGUMENTS specifies a single agent, filter
the registry to only that agent's entry. When all, iterate all agents.
Read transport/agent-registry.json
Parse agents and outbound_routing rules
Filter by $ARGUMENTS scope
If the registry file is missing or malformed, fall back to the hardcoded peer table below and flag the registry issue in the output:
| Agent | Role | Repo | Transport |
|---|---|---|---|
| safety-quotient-agent | Sub-agent (PSQ scoring) | safety-quotient-lab/safety-quotient | Cross-repo fetch (git show safety-quotient/main:...) |
| unratified-agent | Peer (blog platform) | safety-quotient-lab/unratified | PRs + cross-repo fetch |
1a. Local transport (same-repo agents):
For each agent where transport == "same-repo":
git fetch origin
git log HEAD..origin/main --oneline
Scan transport sessions for unread messages using the agent's message_prefix:
ls -t transport/sessions/*/from-{message_prefix}*.json
Compare against state.db transport_messages — any file not indexed there
(or indexed with processed = FALSE) and not authored by psychology-agent
represents a new inbound message:
SELECT filename FROM transport_messages
WHERE session_name = '{session}' AND processed = FALSE;
1b. Cross-repo-fetch agents:
For each agent where transport == "cross-repo-fetch":
# Fetch and scan using cross_repo_fetch.py
python3 scripts/cross_repo_fetch.py --agent {agent_id} --json
The script handles: git fetch {remote_name} main, reads MANIFEST.json
(if present) and session directories via git show, compares against
state.db to identify new/unprocessed messages, and returns structured
results. Use --index flag to also write new messages to state.db.
Post-filter (MANDATORY — Session 91 fix): The script output mixes
genuine inbound messages (from-{peer}-*.json) with addressed copies
(to-psychology-agent-*.json). These require different handling:
from-{peer}-*.json — genuine inbound. MUST verify local existence:
ls transport/sessions/{session}/from-{peer}-{NNN}.json 2>/dev/null
If missing locally → new message, MUST read via git show. Never
skip from-* files without verifying local existence. Never assume
they represent "historical backlog" without checking.to-psychology-agent-*.json — addressed copies (Convention B routing
artifacts). These share the source message's content and MUST NOT be
indexed separately. Skip unless the corresponding from-* source
message does not exist locally.Anti-pattern (Session 91 postmortem): Treating the cross_repo_fetch
output as a flat list and skipping from-* files because to-* files
dominated the count. This caused 6 genuine peer messages to go unread
across 5 sessions. The fix: always partition the output into from-*
(process) and to-* (skip), then verify each from-* locally.
For each new from-* message found, read the full content via:
git show {remote_name}/main:transport/sessions/{session}/{filename}
1c. Cross-repo-pr agents:
For each agent where transport == "cross-repo-pr":
# Check for PRs targeting our repo
gh pr list --repo safety-quotient-lab/psychology-agent --json number,title,headRefName,author
# Check their repo for recent commits (if repo is known)
gh api repos/{repo}/commits \
--jq '.[0:5] | .[] | {sha: .sha[0:7], message: .commit.message[0:72]}'
1d. Local-coordination check (parallel instances):
ls transport/sessions/local-coordination/
1e. Peer API status check (Session 93 — prevents git/state confusion):
For each agent shown as online in the pulse, query its /api/status
for actual processing state. MUST NOT infer processing state from git
commit history — an agent can deliberate actively without producing
commits. Git log shows commit activity; the API shows processing state.
# Query each online peer's status endpoint
for agent_url in $(curl -s http://localhost:8083/api/pulse | \
python3 -c "import json,sys; [print(a.get('status_url','')) for a in json.load(sys.stdin).get('agents',[]) if a.get('status') == 'online']"); do
curl -s --max-time 5 "$agent_url" 2>/dev/null
done
Extract from each response:
deliberation_count — has the agent deliberated since last restart?event_count — how many events processed?gc_metrics.spawn_blocked_total — blocked spawns indicate infrastructure bugsuptime — how long since last restart?health — self-reported health statusAnti-pattern (Session 93 postmortem): Checking git log peer/main
and concluding "agent stopped deliberating" when the log showed no recent
commits. Observatory-agent had 10 deliberations on the day we reported it
idle — the deliberations produced no git-visible output. Fair witness
violation: observation ("no commits") reported as inference ("no activity").
For each inbound item, classify:
| Type | Source | Action |
|---|---|---|
| New transport message | from-{agent}-{NNN}.json | Read → assess → respond or flag |
| Open PR on our repo | Peer agent branch | Read diff → assess → merge or flag |
| New commit on main (after pull) | Peer or sub-agent | Read changed files → process |
| No new activity | — | Report "no new activity" and continue to Phase 2b |
MUST NOT auto-merge PRs. Surface them for user review with a summary of what the PR contains. The user decides merge/reject.
MUST NOT auto-accept proposals. Psychology-agent reviews substance decisions (T3) — surface with recommendation.
For each agent where always_consider == true, AND for any agent with
active sessions, scan the current session context for content relevant
to that agent using the outbound_routing.rules:
Example: Psychology-agent resolves a cogarch change. The routing rule
domain: "cogarch" → route_to: ["safety-quotient-agent"] fires because safety-quotient-agent
has a cogarch mirror directive active. /sync drafts a notification
message for safety-quotient-agent and surfaces it to the user.
Before processing new messages, check whether any active session contains incomplete work that a previous sync cycle started but did not finish. Incomplete work left undetected compounds — the next cycle may duplicate effort or send contradictory messages.
Detection heuristics:
Orphaned handoffs: Query pending_handoffs for handoffs where timeout_at
has passed but no resolution or fallback executed:
SELECT * FROM pending_handoffs
WHERE resolved_at IS NULL
AND timeout_at < datetime('now');
Action: execute the gate's fallback_action or escalate.
Dangling outbound drafts: Check for uncommitted transport files
(files in transport/sessions/ that exist on disk but have no
transport_messages row in state.db):
# Compare filesystem to index
for f in transport/sessions/*/from-psychology-agent-*.json; do
python3 -c "
import sqlite3, sys
db = sqlite3.connect('state.db')
r = db.execute('SELECT 1 FROM transport_messages WHERE filename=?',
(sys.argv[1],)).fetchone()
if not r: print(f'UNINDEXED: {sys.argv[1]}')
" "$(basename "$f")"
done
Action: index the file via dual_write, or delete if it represents an abandoned draft (check git status — unstaged = abandoned).
Request-without-response: For each active session, check if the
most recent message from a peer agent contains message_type: "request"
and no subsequent from-psychology-agent message exists:
SELECT tm.session_name, tm.filename, tm.turn, tm.subject
FROM transport_messages tm
WHERE tm.message_type = 'request'
AND tm.from_agent != 'psychology-agent'
AND tm.processed = TRUE
AND NOT EXISTS (
SELECT 1 FROM transport_messages tm2
WHERE tm2.session_name = tm.session_name
AND tm2.from_agent = 'psychology-agent'
AND tm2.turn > tm.turn
);
Action: flag in output as "pending response needed" — do NOT auto-draft
unless the request carries urgency: "immediate".
Partial deliverable chains: When a message references a multi-step deliverable (e.g., "5 blog posts" or "review + revise + publish"), check if all steps completed:
-- Check claims linked to the request message
SELECT c.claim_text, c.verified
FROM claims c
WHERE c.transport_msg IN (
SELECT id FROM transport_messages
WHERE session_name = '{session}' AND message_type = 'request'
);
Action: if unverified claims remain, flag the deliverable as incomplete.
Output addition:
Add to the /sync output block:
Incomplete work detected: {count} items
- {session}: orphaned gate {gate_id} (timed out {when})
- {session}: unanswered request from {agent} (turn {N})
- {session}: {M}/{total} deliverables incomplete
If no incomplete work: Incomplete work detected: none
Autonomous mode behavior:
When running under autonomous-sync.sh, incomplete work detection MUST
run before the claude invocation's orientation payload. The pre-sync
script (scripts/pre_sync_check.py) should surface incomplete items
so the LLM context includes them. If incomplete work exceeds 3 items,
the orientation payload should prioritize resolution over new inbound
processing.
Before writing new messages to a session, check whether the session has drifted from its original scope. Drifted sessions obscure content, complicate search, and produce misleading MANIFEST entries.
Detection heuristics (any one triggers a drift flag):
Subject drift — 3+ consecutive messages share no keywords with
the session name (split session name by hyphen, check intersection
with message subjects). Example: context-degradation-threshold
receiving messages about "LCARS dashboard" and "alpha-band heartbeat."
Thread fork — thread_id differs from session_id on 3+ messages.
The session contains multiple conversations sharing a container.
Each distinct thread_id should become its own session.
Participant change — the agent pair shifts within the session (e.g., psychology↔ops becomes psychology↔observatory). Different participant pairs warrant different sessions.
Turn count — session exceeds 15 turns. Long sessions almost always contain multiple topics. Check whether turns 1-5 and turns 10-15 discuss the same subject.
When drift detected:
Session drift: {session} — {reason}Output addition:
Session drift detected: {count} sessions
- {session}: {reason} — recommend split into {proposed sessions}
If no drift: omit this line (don't add noise for clean sessions).
Hippocampal replay batch limit (Buzsáki, 2015): During biological memory consolidation, sharp-wave ripples replay 3-5 memories per burst, then yield. Each ripple consolidates a bounded set before the next fires. The mesh analog: each deliberation processes at most 5 unprocessed messages, commits results, and releases the spawn slot. The next oscillator cycle picks up where this one stopped.
Batch protocol:
Rationale: Unbounded processing fills the context window, degrades response quality, and blocks the spawn slot for extended periods (observed: 82-message sync consumed 30+ minutes in Session 94). Bounded batches keep each deliberation focused, produce frequent commits (verifiable progress), and allow the oscillator's adaptive threshold to regulate processing rate.
Override: --batch N argument sets a custom batch size. --batch 0
disables the limit (process all — use only in human-supervised sessions).
Interactive /sync (human session) defaults to --batch 0 since the
human controls pacing. Autonomous /sync --quick defaults to --batch 5.
python scripts/dual_write.py transport-message \
--session "{session_name}" --filename "{filename}" \
--turn {turn} --type "{message_type}" \
--from-agent "{from.agent_id}" --to-agent "{to.agent_id}" \
--timestamp "{timestamp}" --subject "{subject}" \
--claims-count {len(claims)} --setl {setl} --urgency "{urgency}"
ack_required field. If true, an ACK MUST be written.ack_required is false or absent, skip ACK — the mark-processed
dual-write serves as processing confirmation.ack_required: true and no substantive response: write a minimal ACKin_response_to against active gates):
# Check if the response resolves a waiting gate
python scripts/dual_write.py gate-status --agent-id psychology-agent
# If match found (same session + responding agent matches gate's receiving_agent):
python scripts/dual_write.py gate-resolve \
--gate-id "{matching_gate_id}" --resolved-by "{inbound_filename}"
The gate resolution check runs automatically — no user confirmation needed
(process decision, not substance).in_response_to
field references an outbound message we sent, check whether a prediction
exists and compare:
python3 scripts/efference-copy.py compare \
--session "{session_name}" \
--outbound "{in_response_to filename}" \
--inbound "{current_inbound_filename}" \
--actual "{1-sentence summary of response disposition}"
Skip if no prediction exists for that outbound message (the script handles
gracefully). Match results feed the prediction_ledger for /retrospect analysis
and the crystallized sync triage surprise modifier.python scripts/dual_write.py mark-processed --session "{session_name}" --filename "{filename}"
gh pr diff {N} --repo safety-quotient-lab/psychology-agentgh pr merge {N} --merge then git pullTemplate — adapt per message:
{
"schema": "interagent/v1",
"session_id": "{session-id}",
"turn": "{N}",
"timestamp": "{ISO-8601}",
"message_type": "ack | review | request | notification | session-close",
"in_response_to": "{filename}",
"from": {
"agent_id": "psychology-agent",
"role": "psychology-agent",
"instance": "Claude Code (Opus 4.6), macOS arm64",
"schemas_supported": ["interagent/v1", "command-request/v1", "local-coordination/v1"],
"discovery_url": "https://psychology-agent.safety-quotient.dev/.well-known/agent-card.json"
},
"to": {
"agent_id": "{peer-agent-id}",
"discovery_url": "{peer-discovery-url or null}"
},
"content": { },
"claims": [],
"action_gate": {
"gate_condition": "none",
"gate_status": "open"
},
"ack_required": false,
"urgency": "normal",
"setl": 0.0,
"epistemic_flags": []
}
SETL guidance:
Gate field (gated autonomous chains):
When drafting an outbound message that blocks on the receiver's response,
add the gate field to the message JSON:
"gate": {
"gate_id": "{topic}-gate-{YYYYMMDD}",
"blocks_until": "response",
"timeout_minutes": 60,
"fallback_action": "continue-without-response",
"priority": "gated"
}
After writing the message file, register the gate in state.db:
python scripts/dual_write.py gate-open \
--gate-id "{gate_id}" \
--sending-agent psychology-agent \
--receiving-agent "{peer-agent-id}" \
--session "{session}" \
--filename "{outbound_filename}" \
--blocks-until response \
--timeout-minutes 60 \
--fallback-action continue-without-response
blocks_until values: response (any reply clears), ack (ACK clears),
specific-turn (named turn clears). fallback_action values:
continue-without-response, retry-once, halt-and-escalate.
Gate-aware polling (L2) activates automatically when active gates exist in
state.db — autonomous-sync.sh accelerates to 60-second intervals. No-op
polls cost 0 autonomy budget credits. Full spec: docs/gated-chains-spec.md.
Every outbound message MUST be delivered to the target agent's repo.
Writing from-psychology-agent-*.json in our own repo does not constitute
delivery. The target agent's autonomous sync fetches from their own repo,
not ours.
Delivery methods (ordered by preference):
Method 1: Git-PR delivery (persistent, auditable)
./scripts/deliver-to-peer.sh {target-agent-id} {session-id} \
transport/sessions/{session}/{outbound-filename} {short-label}
The script: resolves target repo from agent-registry.json, clones to
/tmp, creates branch psychology-agent/{session}/t{turn}-{label},
writes message as to-psychology-agent-{NNN}.json in the target repo,
commits, pushes, opens PR, cleans up.
Method 2: HTTP POST (real-time, ephemeral)
curl -X POST https://{target-agent}.safety-quotient.dev/api/messages/inbound \
-H "Content-Type: application/json" \
-d @transport/sessions/{session}/{outbound-filename}
All 5 agents have this endpoint operational. Useful for urgent delivery. Does not create a durable git record unless the target agent commits the received message.
Anti-pattern: Writing from-{our-id}-*.json ONLY in our own repo and
expecting the target to find it via cross-repo fetch. The fetcher operates
as a safety net, not the primary delivery mechanism.
When to use which:
ack_required: false: either method; prefer HTTP for speedpython scripts/dual_write.py transport-message \
--session "{session}" --filename "{outbound_filename}" \
--turn {turn} --type "{type}" \
--from-agent psychology-agent --to-agent "{target}" \
--timestamp "{timestamp}" --subject "{subject}"
python3 scripts/efference-copy.py predict \
--session "{session}" --outbound "{outbound_filename}" \
--expected-type "{ack|response|review}" \
--expected-agent "{target_agent_id}" \
--prediction "{1-sentence expected response summary}"
This completes the forward model: every outbound message carries an
expectation that /sync Phase 3 step 8 compares against actual responses.python scripts/generate_manifest.py
MANIFEST.json is a thin, git-transportable addressing file for peer agents.
Completed message history lives in state.db (queryable) and git history
(auditable). Do NOT manually edit MANIFEST.json — always regenerate.active_sessions if sessions opened or closedactive_sessions for agents if changedgit add transport/ .well-known/agent-card.json
git commit -m "sync: {summary}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
git push
generate_manifest.py.claude/proposals//sync complete
Scope: {all | psq | unratified}
Registry: {loaded | fallback (registry missing/malformed)}
Fetched: {git fetch summary}
Inbound messages: {count} new | none
- {session}/{filename}: {type} from {agent} — {1-line summary}
Inbound PRs: #{N} {title} | none
Outbound scan:
- {agent}: {content domain} — {summary} [draft ready | no active session]
ACKs written: {session}/{filename} | none
Delivered to peers:
- {target-agent}: {session}/{filename} via {PR #{N} | HTTP POST}
Expected response: {ETA}
| none
MANIFEST regenerated: {yes — N pending | no changes}
Sessions opened/closed: {session-id} | none
Dual-write: {N indexed, M marked processed | skipped (no state.db)}
Gates: {N active (gate-ids) | N resolved this cycle | none}
No new activity: true/false
Next expected: {what we await from each peer}
Peer response ETA derivation:
ETAs accompany each delivered message. Derive them from the peer's processing model:
| Peer type | ETA | Rationale |
|---|---|---|
| Autonomous agent (e.g., safety-quotient-agent, operations-agent) | ~8 min (sync cycle interval) | Cron-driven autonomous-sync.sh runs on a fixed interval |
| Human-mediated agent (e.g., unratified-agent, observatory-agent) | Next human session (~24-48h) | Processing requires a human operator to start a session |
Gated message (gate.timeout_minutes set) | Timeout value from gate spec | The gate's timeout_minutes field provides the upper bound |
When historical response latency data exists in state.db, prefer the observed median over the default estimate.