20 Commits

Author SHA1 Message Date
daymade 9c9e561313 fix(peer-message): prefer native communication across hosts (#530)
* fix(peer-message): prefer native communication across hosts

* docs(peer-message): distinguish native multi-target sends
2026-09-13 11:47:24 +08:00
daymade 1d1a476c9b docs(peer-message): verify local read status from recipient transcripts (#523) 2026-09-11 14:52:54 +08:00
daymade 0d4b7d5f03 feat(skills): complete reply lookup and recovery cleanup workflows (#501)
* feat(skills): complete reply lookup and recovery cleanup workflows

* fix(peer-message): ignore commented reply metadata
2026-09-08 14:46:29 +08:00
daymade 903090b27f Improve peer-message inbound triage and stop acknowledgement loops (#498) 2026-09-08 14:36:59 +08:00
daymade a0af111d0c feat(peer-message): verify, then ask the owner when another session's in-flight work blocks you (v1.6.0) (#491)
* feat(peer-message): verify, then ask the owner when another session's in-flight work blocks you (v1.6.0)

Every existing contract started after a message already existed (§4 verifies an
inbound assertion, §5 reads a set of denials); nothing told a session to send the
first message when it ran into someone else's uncommitted edits on a shared checkout,
so it stopped and reported "someone's WIP". §5 becomes an umbrella: 5.1 verify the
trace is live against the artifact's own authority (`git diff <immutable ref>` empty =
landed residue), message candidate owners one at a time, wait a bounded window, and
continue on an isolated copy without touching their files when nobody claims it;
5.2 is the former §5 verbatim. SKILL.md carries the rule as the outbound mirror of
「收到 peer 消息」; routing table, §6 owner table, description (1019/1024, all user
phrases kept), READMEs and CHANGELOG follow. No script change; 33 tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(peer-message): close the 13 findings a fresh-context executability review raised on v1.6.0

BLOCKER: the residue branch defined a state and no action. `git diff` empty meant
"not WIP", 「真在飞才去问」 then closed the asking branch, the isolated-copy exit was
gated on having waited, and the residue-clearing step was assigned to whoever advanced
main — four exits, all shut. It now says outright that residue is nobody's work in
progress and does not block you.

MAJOR: the payload pointer sent a cold outbound question at §2's reply structure, whose
`in_reply_to` and `result` cannot be filled; 「逐个单发(不广播)」 contradicted the routing
table's broadcast row, when the actual prohibition is inferring a machine-wide broadcast
from one send; the four-item report frame is written against `peer.py list` fields that do
not exist on the official tool the same step permits; the bounded window named no duration
and no mechanism beyond a subscription only the main conversation may use; and a subagent
running this could never receive the reply it was told to wait for, because sends go out
under the parent session's address. The alignment paragraph told you to align the paths you
touched and to leave other people's dirty files alone — a shared registry file is routinely
both, so the comparison now happens before the merge, when "differs because of my merge" and
"differs because someone else is mid-flight" are still distinguishable.

MINOR: a non-empty diff proves not-residue, not that anyone is editing; `git log`/`status`
pick candidates rather than classify; the alignment step gained its command, chosen so it
writes the working tree and not the shared index; 「四步」 named a six-step list; step 1 sent
every reader to `peer.py --help` including those on the official route; and a pointer read
as if §5.2 authorizes disposal when its own Stop forbids it.

quick_validate passes, 33 tests pass, regression audit: 11 candidates classified, 0 retired.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(peer-message): the alignment step was an unauthorized write obligation; gate it and demote it

A fresh-context fidelity review, anchored on the pre-change ref and on the user's own
sentence, found that the residue-clearing paragraph had turned a permission to talk
into a duty to write. The user said the sessions can communicate with each other; the
paragraph read "whoever advanced main does this step", assigned that duty over a shared
working tree, pre-emptively rejected the conservative option, and never routed back to
its own Stop — which requires the current user's confirmation before overwriting someone
else's changes. The obligation itself came from a peer's proposal, and a peer cannot
authorize. It is now explicitly optional, the asymmetry is stated (leaving residue costs
one wasted round; a wrong write destroys work), and the only branch that actually writes
is gated: re-verify the file still equals the snapshot value immediately before writing,
and stop if it moved. SKILL.md's trust boundary gains the missing category, and its
condensed section gains the two safety clauses the reference had but it did not.

Also: the war story keeps only what the anchors can verify — the user's sentence — and
says outright that it authorizes communication, not writes; the report frame now applies
whether or not someone claims the work, so a claimed path leaves a trace too; the
description recovers the receiver-side delivery trigger it had lost, paid for by
compressing two clauses (1012 of 1024); and the changelog's "verbatim" claim is corrected
to name the one cross-reference that had to change.

quick_validate passes, 33 tests pass, regression audit: 13 candidates classified, 0 retired.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(peer-message): six factual claims the fidelity review measured; one was false in this skill's own main scenario

A fresh-context reviewer calibrated every git and tool claim in the new section against a
throwaway repository and against this repo's own references.

"Check whether the pid holding the lock is still alive" had no basis in either reference and
is false for the lock this skill's main scenario produces: `.git/index.lock` is zero bytes and
carries no pid — reproduced here, and git's own error can only say a process "may have crashed"
and left it. A lock now tells you its owner only if it carries one, git's is named as one that
does not, and an unreadable lock routes straight to asking.

The idle-notice subscription kept its pointer but lost all three qualifiers its reference
states — same-machine Claude sessions only, both sides 2.1.236+, and this skill's own UDS
fallback and Codex route do not implement it — while the same step sends readers to a `list`
that returns Codex rows by default. With the tool contract's main-conversation-only rule that
is four conditions, and the common case is that one fails, so the self-set deadline is now the
normal path rather than the fallback.

`origin/main` was called an immutable ref while being used as one; it moves on fetch, so a
report naming it does not resolve to the same commit later. Baselines are pinned to a resolved
SHA and the report writes the SHA.

Two claims were stronger than their evidence. An empty diff means the working tree matches that
ref — residue only follows because the ref is merged main, which the text now says. And a diff
separates landed from not-landed, not residue from someone's work in progress: not-landed also
covers your own stale edits and work sitting on another remote branch, so a non-empty result
routes to asking rather than to a classification.

quick_validate passes, 33 tests pass, regression audit: 13 candidates classified, 0 retired.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(peer-message): settle the jurisdiction the new rule collided with, and drop three persisted counts

The optional-alignment step contradicted a standing safety rule elsewhere in this
operator's environment, which requires the same alignment as part of delivery rather
than as an optional tidy-up. Both are right about different cases, so §5.1 now names
the one that outranks it: when the release path transformed the content — sanitizing,
renaming, reformatting — what stays in the shared working tree is the pre-transform
draft, carrying exactly what the release removed (credentials, real names, internal
paths). That is a security matter for the environment's own gate, not an optional
tidy-up, and the rest stays optional.

Three counts were persisted values that any reader could compute from the enumeration
sitting next to them: the report frame's 「四项」 (stated twice, once in SKILL.md's pointer),
the delegation payload's 「四项」, and the subscription qualifiers' 「三个限定 / 四条里有一条」.
A count next to its own list is a number that silently goes wrong the first time the list
changes, so all four sites now let the enumeration speak.

The repository ownership map still described `coordination-and-learning-loop.md` as owning
reply addressing, payload structure, delivery-status language, and the two verification
contracts — the same drift v1.5.1 fixed in the READMEs, now that the file owns a third
contract. No version bump: v1.6.0 is unmerged and unconsumed, so this collapses into it.

quick_validate passes, 33 tests pass, check_marketplace passes, regression audit: 14
candidates classified, 0 retired.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-07 15:39:24 +08:00
daymade 6cec2a9b1b docs(peer-message): repair the paragraph v1.5.0 shipped broken, realign the README ownership clause (#485)
Two documentation defects, both found by a bounded inventory of what v1.5.0 touched.

1. The §5 explanatory paragraph was mangled by v1.5.0's own fix. The war story's
   two-conjunct filter got spliced into the middle of an existing sentence, which
   left three consecutive em-dashes, a dangling clause ("原来的三项一个都没承载"),
   and two mentions of "three items" whose antecedents read as different sets. It
   is now three sentences: the conjuncts are stated once, each half is mapped to
   the error it names, and the reference to the old three-item frame is explicit
   about which frame it means. No rule changes -- the instruction the paragraph
   carries is the same one, it is now readable.

2. README.md and README.zh-CN.md described `coordination-and-learning-loop.md` as
   owning only "parent/worker handoff language and evidence-gated Skill
   improvement". That was accurate at v1.2.0 and has been stale since v1.3.0 gave
   that file the inbound-assertion contract and v1.4.0 the denial contract. The
   clause now matches the scope the file declares in its own frontmatter, which is
   also what CLAUDE.md's ownership map already stated correctly -- the drift was
   in the README copy, not in the map.

Checked and deliberately not changed: `.claude-plugin/marketplace.json`'s
description is still byte-identical to SKILL.md's frontmatter (1023 chars);
CLAUDE.md's peer-message ownership map already names all four owners with the
current scope; `~/.claude/references/irreversible-gates.md` mentions peer-message
only as a historical incident record, which is not a restatement of the procedure.
README's "matrix of 19 skills" is a third-party vendor's bundle size, not a
derived count of this repo (which has 56 plugins), so it stays.

quick_validate passes, 33 tests pass.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:33:49 +08:00
daymade 99fa2c770f feat(peer-message): calibrate the discriminator, and put the filter into the report frame (#483)
v1.4.0's review closed with an unchecked item: the poll-and-report-frame rules had
never been executed end-to-end in a real multi-session ownership investigation, so
their usability was only established down to "the text agrees with the tool". That
run has now happened, and it produced one new rule and one repair.

§4 gains the case where the premise is true but the discriminator under it does not
discriminate. A peer classifying a shared artifact usually holds one binary marker --
a record carries a trailer or not, a file is in a manifest or not, a field is empty
or not -- and reads absence as "anomaly", then anomaly as "from a writer I cannot
enumerate". Answering only "not mine" is correct and still harmful: it confirms the
one link in that chain the peer could not check for itself, and it walks away with an
uncalibrated criterion. The action is not to prove authorship but to run the same
marker across comparable neighbours -- adjacent commits on the branch, sibling files
in the directory, records from the same tool in the same window.

What the rule asks for is a COUNT, not a verdict: how many were read, how many are
missing the marker, and a value distribution rather than present/absent once the
marker turns out to be multi-valued. Whether that count means "baseline" is the
asker's call, so `Stop` is unconditional and says so. An earlier draft gated it on
"the criterion is calibrated", and two independent reviewers showed the same hole
from different directions: the text defined only the un-calibrated case (no
neighbours available), so reading one neighbour that happened to carry the marker
satisfied the gate and released it for exactly the move the section exists to
prevent. There is deliberately no sample-size threshold -- a threshold becomes a pass
you issue to yourself.

§5's report frame gains the item its own prose already demanded and its checklist
never carried. The section says outright that writing the frame IS stating the
default filter you applied, and then requires three items (targets polled, provider
and whether `--limit` was hit, unverified saved-catalog rows), none of which reveals
it. A narrowed investigation fills all three truthfully and reads as compliant while
the exact gap the war story teaches is the one thing absent from the table. Four
items now, the filter first, with the boundary stated: item one is the cut YOU made,
items three and four are the coverage the TOOL gave you, and one fact appearing in
both is not double-counting.

Both examples now name their own filter. Review found the pre-existing one had
quietly narrowed by provider while being presented as the un-narrowed case -- `list`
defaults to `--provider all`, so "ask only the Claude side" is already a cut. The
narrowed example states what it gave up, because no narrowing axis is safe: `cwd`,
liveness and provider each drop precisely the rows this section names as the lead.
The war story's filter is now quoted as the conjunction it is ("in this repo's
working directory" AND "all live"), each half mapping to one of the two errors the
section teaches; the previous text quoted half of it and called that verbatim.

SKILL.md carries the calibration rule inline, warns that neighbours drawn only from
your own history calibrate nothing, and its frame pointer says four.

Three reviewer dispatches across two axes produced eight adopted findings. The first
attempt at §5 was deleted outright rather than softened, after the fidelity axis
showed it inverted the section's own war story: it declared correct the very filter
that story was written about, cited the routing table's authorization rule as if it
were a width constraint, and named a first round without ever defining a second.

The fact this ships (absence of a session trailer is the branch baseline, not a
signal) was verified with two different predicates: a line-anchored grep and git's own
trailer parser, with a second trailer as the known-answer control proving the
instrument could report both values.

Regression audit: 2 candidates, both the three-item frame becoming four, classified
preserved_or_moved with the original items and example substance kept; 333 exact
preservations. 33 tests pass. Description unchanged at 1023 of 1024 characters.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 21:40:23 +08:00
daymade 6483fc8dc4 fix(peer-message): put the Codex reply address back in from (#482)
The previous release removed it, reasoning that the field's contract is "copy
this into `to`" and no Codex address satisfies it for the official Claude tools.

That conflated two things. The official tools cannot reach a Codex thread — true
— but `codex:<uuid>` is a working reply address: `codex queue --thread` documents
its argument as "Session UUID or exact session name", and `resolve_codex`
resolves it. The address was never the problem; only its resolvability by one
particular route was, and only some recipients are limited to that route.

Dropping it spared those recipients a single recoverable failed attempt — one the
body line already explained — and charged for it by taking a usable handle away
from every recipient that does have this Skill. It also left the route travelling
without the thing it routes, which inverts the rule this repo published three
commits ago: Case 23's T2 says that when the two spaces do not intersect, the
identifier and its route travel together.

`protocol-and-discovery.md` §2 had said `from` stays put. The previous release
contradicted it in code without updating it, so the implementation and its own
specification disagreed. Both are corrected here, and §2 now states why the
identifier is good and only the official-resolvable form is missing.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-06 20:34:55 +08:00
daymade 90ffc68f88 fix(peer-message,skill-creator): normalize the reply address however it arrived (#471)
#458 fixed the address `auto_reply_address` computes. That covered only the case
where that function is what supplies it.

The asymmetry underneath: `send_claude` normalizes the TARGET through
`resolve_claude` but never the REPLY address — and the reply address is the one
the recipient is told to copy into `to`. Three paths therefore still shipped an
envelope no official tool can resolve:

- a caller passing `whoami` output through `--reply-to`, which this Skill's own
  §1 explicitly instructed them to do
- the messaging-socket variable being absent, falling back to the session id
- only a session name being set

The intersection was derivable the whole time: `resolve_claude` matches a needle
against {pid, name, sessionId, messagingSocketPath}, so a `claude:<uuid>` finds
its own registry row, and both the socket and the bare name are on it.
`send_claude` now puts the reply address through that same resolution, emitting
`uds:<socket>` in `from` and the bare name in `from-name`, and leaves genuinely
unresolvable addresses to the body-line route hint the Codex branch already uses.

A Codex sender now gets no `from` at all rather than one that looks addressable
and is not. That field's contract is "copy this into `to`", no Codex address
satisfies it, 21% of real envelopes already carry no `from`, and the thread id
stays in `from-name` and in the body.

Three statements in §1 that contradicted each other collapse to one, and the
"fall back to `from-name`" advice in SKILL.md and protocol §1 is corrected: it
cannot help for envelopes this Skill produces, where both attributes share a
source and fail together.

The methodology's Case 23 also narrows an overstated claim: a recipient can
still identify the sender from the body and reply to its listed name, or install
the Skill. What does not exist is recovery from the handle alone.

33 tests. The unit tests stayed green with the normalization call removed, which
is the shape of a check that cannot fail; the added wiring test goes through the
real socket and reads the address off the frame that left the process.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 04:24:27 +08:00
daymade 5860d26b25 docs(peer-message): 把第四个 owner 写进 CLAUDE.md,并对自己的例证执行自己的新鲜度规则 (v1.4.1) (#470)
两处都是 v1.4.0 自己留下的:

CLAUDE.md 的 peer-message owner 指针表列了三个 owner,漏了
references/coordination-and-learning-loop.md —— 它拥有回传地址、正文结构、
状态语言,以及两条判定合同(收到断言怎么核、一组否认值多少)。缺口在
v1.4.0 之前就有,但 v1.4.0 往这个未登记的 owner 里加了两条承重规则,
把缺口扩大了。

§5 的例证数字(行数、工作目录数)是一个分钟级换血的集合的观测——两个
审阅者在同一小时内量到了不同的值。旁边那句警告原本只说「不要抄行数」,
现在覆盖本节全部数字,并说明承重的是每个数字旁边的结构性事实:有没有
过滤开关、打印哪些字段、默认值是多少。

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 04:05:15 +08:00
daymade 592aae0914 feat(peer-message): a set of peer denials is not a conclusion (v1.4.0) (#469)
v1.3.0 taught a session not to believe what a peer asserts. This closes the
sender-side mirror: not over-believing what peers deny.

The set you can enumerate is not the set that can affect the artifact. Write
access to a path belongs to anything that has it — a writer that never
registered, a scheduled job, a person, and the exited sessions that do not
surface. A round of denials narrows the candidate set and decides nothing.

coordination-and-learning-loop.md §5 first pins down what `list` actually
returns, because the frame cannot be written without it: it does not partition
by working directory (no cwd filter exists, every row prints `cwd=`), a row
does not mean "running" and the two providers differ (Claude rows come from a
registry a process drops out of on exit; Codex rows are a saved catalog whose
entries are mostly finished sessions, which makes an exited Codex session a
clue carrying its own cwd rather than noise), and `--limit` defaults to 30 on
the Codex half alone while `--help` shows neither default nor description.

§2 gains the freshness clause its evidence field was missing: when the subject
is mutable, one observation is not a fact. SKILL.md's trust boundary gains the
inverse of its existing rule — a conclusion derived from peer replies must not
enter a user-facing report as established fact.

Distilled from a real cross-session ownership misjudgement where every polled
session truthfully denied authorship and the actual writer was never in the
polled frame; §5 also names the second half of that error, converting "I cannot
find the owner" into "the owner is dead, so this is disposable".

Two rounds of independent fresh-context review: the first found the mandatory
frame-reporting instruction unexecutable because the text described `list` in
ways three live runs contradicted; the second caught the changelog restating
the corrected claim, an "exited sessions are unreachable" line the Codex saved
catalog falsifies, and an example row count this section's own freshness rule
would reject.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 03:40:41 +08:00
daymade e0cae20405 fix(skill-creator,peer-message): bound Case 23, then apply it to its own source (#465)
Case 23 shipped the three-tier rule but not the boundary work that keeps it from
becoming a tax. Two corrections:

**It is an error-interpretation rule, not a pre-call gate.** On the happy path it
must cost nothing — no check, no conversion, no suspicion — and engage only once
a not-found-shaped error actually returns. Written as a pre-call action, the rule
is itself the source of misfires. Its trigger is now a conjunction of three
conditions, all decidable from context with no probing, and it is phrased as a
producer-side question ("can your consumer resolve what you emit?"). The
consumer-side phrasing of the same rule fires everywhere.

**The not-found clause needed somewhere to stop.** It now carries three terminal
exits, none of which is trying another address, and a third verdict —
`unresolved` — distinct from absent. With only "exists" and "does not exist", an
agent keeps searching to decide which; the third word is what lets it stop.

T1 also gains its three preconditions. The load-bearing one: a handle destined
for storage must not be cast, because names are mutable and collidable while
canonical ids are not. Cast for use, store the canonical, carry both when the
envelope has room.

Applying that precondition back to peer-message found the reference
implementation breaking it in one branch: when a session name is set, `from` is a
pid-derived socket locator (pids are reused) and `from-name` is a mutable display
name, so an archived envelope carries no stable route back to its sender. The
canonical session id now rides the body's first line whenever neither attribute
already carries it — the same escape hatch the fixed attribute set already forces
for the message id.

29/29 tests pass; removing the new line turns one red.


Claude-Session: https://claude.ai/code/session_019MoXJU8BzjoKZYTFHEkhCe

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 02:52:15 +08:00
daymade 71cdb26521 feat(peer-message): verify inbound peer assertions before acting or replying (v1.3.0) (#440)
An inbound assertion about your session or shared state is the sender's observation, not evidence about you: it can see a shared artifact changed but not who changed it. SKILL.md gains a 收到 peer 消息 section; coordination-and-learning-loop.md gains §4 with the six-field rule (check the premise against its own authority, answer premise + blocked need together, quote the read-back, unknown when undecidable, never act on a false premise). Rebased onto v1.2.0 with the description carrying both trigger sets; CHANGELOG and README completed to repo convention.

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-04 21:00:55 +08:00
daymade fafed0b6bd fix(peer-message): tell a Claude recipient how to reply to a Codex sender (v1.2.2) (#460)
v1.2.1 fixed the Claude→Claude case by emitting `uds:<socket>` as the envelope
`from`, the one form both routes resolve. The Codex→Claude direction has no
such form: official Claude tools cannot reach a Codex thread by any address.
That is a fact about the two products, not a defect.

The consequence is the same shape as the bug v1.2.1 fixed. The receiving Claude
session is handed an instruction by its own host — copy `from` into `to` —
that cannot work, and nothing tells it why. It may never have loaded this
Skill, so no documentation reaches it.

The host parser fixes the attribute set, so the only channel left is the body's
first line, where the message-id already rides. Codex-sender envelopes now
carry `[reply: peer.py send codex:<id>]` there.

Claude senders are deliberately unchanged: their `from` is directly usable by
the official tools, and adding a hint would push work away from the official
channel for no reason.

Two deterministic tests cover both branches; removing the hint turns one red.


Claude-Session: https://claude.ai/code/session_019MoXJU8BzjoKZYTFHEkhCe

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 20:46:56 +08:00
daymade 0d990b40d9 fix(peer-message): emit an officially-resolvable reply address (v1.2.1) (#458)
v1.2.0 shipped a reply address-space contract built on a wrong premise and
fixed it in the wrong layer.

The premise: it asserted the envelope `from` is a `claude:<session-uuid>` form
and that the official peer tools accept only bare names. A sweep of the local
transcript corpus shows `from` is `uds:<socket-path>` in the overwhelming
majority of real envelopes and a session-UUID in a negligible minority — the
generalization came from that minority. Live testing confirms the official
tools accept `uds:<socket-path>` (the form the host itself emits) and bare
names, and reject `claude:<uuid>`.

The layer: documentation cannot fix this at all. The receiving session may
never have loaded this Skill. It has only the host's own instruction to copy
`from` into `to`, and the official tools; the official listing's `[ref]` is not
a UUID prefix, so it cannot map the address back by eye either. No
recipient-side recovery path exists, so the envelope producer has to be right.

peer.py now emits `uds:<socket>` as the envelope `from` — the one form both
routes resolve — falling back to previous behaviour when the socket variable is
absent, and leaving `codex:` senders untouched since official Claude tools
cannot reach Codex by any address.

v1.2.0's guidance also carried three defects of its own, all removed here:
its fallback ("on a lookup miss, use peer.py") was a dead end because `list`
and `send` read the same registry; its acceptance check (`reachable` is true)
still passed when `list` yields `claude:<pid>` for an unnamed session; and it
never covered the reverse gap, that the official `name [ref]` form fails
`resolve_claude`'s whole-string match.

Four deterministic tests cover the socket preference, the round trip back
through `resolve_claude`, the no-socket fallback, and the Codex case; reverting
the fix turns two of them red. Existing 21 tests unchanged and passing.


Claude-Session: https://claude.ai/code/session_019MoXJU8BzjoKZYTFHEkhCe

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-04 20:37:36 +08:00
daymade d0575b4f9d feat(peer-message): held-inbound fix path + reply address-space contract (v1.2.0) (#456)
Two operator-path pillars from one live cross-session incident, combined
into a single delivery with the user's explicit in-task approval (the
address-space edits were left complete-but-uncommitted in the shared
checkout by a concurrent session):

- official-feature.md §3 owns the held-message fix flow: held is not a
  transport failure (never resend), locate the settings file the receiver
  actually loads, write crossSessionInbound=accept only with the current
  user's confirmation, converge via the master settings file where a
  profile-sync SSOT exists, read back independently, and note running
  sessions apply it on next launch.
- protocol-and-discovery.md §1 owns the two address spaces: peer.py
  resolves prefixed names/pids/session-UUIDs while official peer tools
  accept only bare names; envelope `from` and `whoami` output are UUID
  forms that official tools reject with the existence-shaped
  "No agent named" error. Reply via peer.py send <from> directly, or map
  UUID→bare name via list --json first; switch routes after two official
  resolution failures instead of enumerating address forms.
- SKILL.md gains both routing rows and execution steps; description and
  README gain the held-message trigger signal; the no-forged-attestation
  red line is restated.

Validation: quick_validate, 21 unittest cases, check_marketplace,
check_version_progression, and the skill regression audit
(pre-edit snapshot, 4 modified-guidance candidates dispositioned
preserved_or_moved) all pass.

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-09-04 20:20:03 +08:00
daymade fd68770d44 fix(peer-message): fail loudly on unreadable Claude evidence (#434) 2026-09-01 10:18:03 +08:00
daymade 13aa494c42 feat(peer-message): add evidence-gated coordination loop (#433)
* docs(peer-message): add evidence-gated coordination loop

* fix(peer-message): make reply handoffs executable

* fix(peer-message): bound identity and wait inputs
2026-09-01 09:19:56 +08:00
daymade d57b231911 docs(peer-message): enforce operational SSOT ownership (#428)
* docs(peer-message): centralize operational truth

* docs(peer-message): separate stable and volatile boundaries

* docs(peer-message): point each contract to its owner

* fix(peer-message): make runtime requirements discoverable

* docs(peer-message): enforce canonical contract owners

* docs(peer-message): document default receipt semantics
2026-08-31 20:00:13 +08:00
daymade 9ebf92cc11 feat(peer-message): coordinate local Claude and Codex sessions (#426)
* feat(peer-message): coordinate Claude and Codex sessions

* fix(peer-message): preserve provenance and exact routing

* fix(peer-message): discover isolated Claude profiles
2026-08-31 07:49:00 +08:00