Repository navigation
v0.10.0
Added
-
post participant describe [--model <text>] [--effort <text>] [--cwd <abs-path>] [--clear]
lets a session record the model, reasoning effort, and working directory it
runs under. The values are stored as an optionalruntimeobject on the
participant record and shown inparticipant show,participant list, and
who. It is silent (no channel event, no mail), counts as activity like
participant touch, and older post binaries read records that carry it. -
participant describealso records--repo,--branch,--title,
--role interactive|child|headless,--parent <participant-id>,
--state working|idle,--pane, and--harness-session.--unset <field>
(repeatable) removes one field, and--endedends the session and drops the
runtime.participant endalso drops the runtime, so an ended session is no
longer a peer to find. -
post who --live [--role <role>] [--repo <basename-or-path>]lists the peers
you can message now: a live watch or armed doorbell and activity within the
last 10 minutes. Text is one line each (<pfp> <name|id> · repo@branch · title · state · age);--jsongives the full records.whoparticipants
now carry the profilenameandpfp. -
post send --toaccepts a live participant's profile name or
repo:<basename-or-path>. An exact id,participant:<id>, a room, and a
lineage keep winning over a name. Zero live matches isunknown_recipient
and several isambiguous_recipientwithdetails.candidates; both exit 65
and send nothing. The receipt carriesresolved {id, name?, via}.
chat --emote --attakes the same names andrepo:among live members. -
post profile set --namerefuses a name a live participant already holds. -
The doorbell supervisor can ring a headless resident.
post-doorbell resident add --room <room> -- <command>stores$POST_MAIL_ROOT/doorbell/residents/<room>.json(room,argv). With no Herdr pane and no live participant, the supervisor still runspost watch --snapshotfor that room and execs the command plus--reason mention|mail|channel(mention, then mail, then channel). The command gets no subject, sender, body, or preview. Exit 0 acknowledges the batch. Exit 75 leaves it pending and retries after 30 seconds without counting a failure. Any other exit, or a run past 30 seconds, backs off like a failed pane ring.enable,disable,subscribe,unsubscribe,mute,unmute, andstatustake--roomor--resident, and with neither they use the bound participant or the single room that contains the current directory.statusshows each resident's armed state, last ring, last exit, and pending count. -
The doorbell supervisor and its installer run on a host with no
herdrbinary. Such a host has no panes, residents still ring, andstatusreports herdr as not installed instead of failing. -
post-doorbell mute --channel <name>andunmute --channel <name>apply to panes and residents. A muted channel rings for nothing, mentions included, and membership is unchanged. Subscribed means everything, the default means mentions only, and muted means nothing. Mute wins over subscribe. A prefs file with nomutedfield means nothing is muted, andstatuslists the muted channels. -
post rooms rename <old> <new> [--dry-run]renames a local room and keeps
its mail: the mailbox directory moves, every live reference to the name is
rewritten (participant workspace fields, cursor keys, channel members, bare
profile keys, and the address each of the room's routing receipts binds), androoms.jsoncommits last.
Any failure through that commit rolls back. A crash leaves
rename-journal.json, whichpost doctorreports and which blocks every
other rename until the same rename is rerun to finish it. While it
stands, a send to either name refuses with the resume command rather than
recreate the old mailbox, anddoctor --fixskips both rooms. The resume
refuses, rather than merges, if the old mailbox was recreated anyway.
History keeps the old name. It holds.rename.lockexclusively, and
send,catchup, and writingread/chathold it shared, so they wait
for a running rename.sendreads its body before taking the lock, so a
stalled stdin cannot hold up a rename. On a bridged host it requires a fresh
bridge/health.jsonwith zeroedlocal_heldcounters and a local-held
record for every letter to the old name the bridge would export; the
refusal is the retryablebridge_guard_unavailable. Separately,post rooms addrefusing
a name that is a remote placeholder now names the owning host and suggests
a runnable<name>-<suffix>registration instead of aset-pathhint that
could never work. -
One doorbell supervisor per host (
skills/post/hooks/doorbell-supervisor.mjs,
run aspost-doorbell) replaces the per-agentcodex-notify-monitortimers.
It finds each post participant's herdr pane by exact session digest, scans
each enabled participant withpost watch --snapshotwhen its mail or
channels change (with a periodic reconciliation pass as the backstop), and
rings the pane with a[post-doorbell:v2]notice. A snapshot is accepted
whole or not at all, and a failed scan never advances state. Per-generation
state means a moved or resumed session rings again. Failures back off
exponentially and are reported as broken after five in a row. A python3
fcntl.flockhelper guarantees one supervisor per mail root, and
health.jsonplus the heartbeat givepost-doorbell statusa liveness
answer (running, stale, or dead) separate from scan health. It rings every
bound session by default;post-doorbell disableopts out andenable [--focused] [--desktop]re-arms, whilesubscribe --channel <name>,
unsubscribe --channel <name>, andselect --pane <id>manage channel
rings and pane choice. -
skills/post/hooks/install-doorbell-supervisor.mjsinstalls the supervisor
as a launchd LaunchAgent (macOS) or systemd user service (Linux), and waits
for the lock and a healthy first tick. It moves an old
~/.local/bin/post-doorbellaside with its hash and migrates old timers one
at a time: carry over the settings, wait for a healthy subscription, then
disable that timer. Every step is recorded in
$POST_MAIL_ROOT/doorbell/install-receipt.json, so an interrupted run
resumes where it stopped.--uninstallremoves only the supervisor;
--restore-legacyre-enables the timers whose unit files are unchanged. -
Participant DM across hosts, post side (design rev 3.1).
post send --to participant:<id>@<host>queues a letter for a participant on an enrolled
peer host. The letter is written only toarchive/, withto_host, and
the receipt saysdelivery.state: queued. The send refuses before writing
anything when the sender has no local room, when the host is unknown or
the topology unreadable, or whenbridge/health.jsondoes not show a fresh
bridge withtyped-outbound-exclusionandparticipant-mail-v1. New error
codes:topology_unavailableandbridge_status_unavailable(both
retryable), plusunknown_host,no_bridge,remote_sender_unroutable, and
bridge_unsupported. -
post delivery <mail-id>reports a sent letter's state (queued,
published,received,rejected, orunknownon corrupt evidence) from
the bridge's evidence files. It is visible only to the sender. -
post bridge deliver, the bridge-only import command, has a frozen JSON
contract (post.bridge-deliver.v1, with samples incontract/samples/). It
writes the admission record before the inbox file, so a crash between
them converges on rerun, and a replay skips the mutable admission checks. -
An imported letter's origin comes from its admission record at every call
site (inbox, read, search, catchup, watch, routing). Its reply address is
participant:<sender>@<source-host>. An imported sender whose id matches
a local participant's is never that participant's own mail. -
post contract samples [--dir <path>]prints, or writes, the output
samples built into the binary: watch snapshots (plain, digest, and with an
unusable cursor), inbox, chat, who, profile show and list, channels,
participant show and bind, rooms, version, and doctor. The samples come from
the real commands run against a temporary store (tests/contract_samples.rs;
POST_UPDATE_CONTRACT=1regenerates them, otherwise drift fails the suite).
Consumer tests in the doorbell, the skill hooks, and Porch read them from the
binary they will run, viaPOST_BIN. -
post contract skill-manifest [--verify <path>]prints the sha256 of every
file in the skill bundle (SKILL.md,references/,hooks/,agents/) as
built, or checks a served skill path against it. A symlinked path is checked
through what it resolves to; a real directory is treated as a rendered copy,
where a file with skill-render fence markers that differs is listed as
rendered_unverifiedand gives verdictunverified: not drift, and not a
match. Drift and unverified exit 1. -
scripts/install-smoke.sh [--results FILE] <post-bin>runs the doorbell
parsers, the doorbell contract suite, and Porch's launch check against one
binary in a throwaway store. A check that cannot run fails the smoke unless
the operator allows its skip (POST_SMOKE_ALLOW_SKIP=porch), which ends in
PASS_WITH_SKIPS;--resultswrites one JSON line per check.
scripts/install-post.sh <commit>builds that commit with--locked,
smokes it before installing, refuses a symlinkedpost, backs up the old
binary by build sha (a copy checked against the live file's sha256; an
existing backup of other bytes is never trusted), installs atomically,
restores the backup if the installed bytes are wrong, verifies the served
skill, and writes~/.local/share/post/install-receipt.jsonwith the backup,
the smoke verdict (passonly when every check ran), and each check's
result. Its header lists every exit code.--dry-runchanges nothing. -
POST_WATCH_PROFILE=1makespost watchprint one diagnostic stderr line
per target scan: mail-snapshot time, channel-enumeration time, channel scan
time, and file counts. Nothing else changes. An ignored bench test,
watch_projection_cost_bench, reports each watch projection's cost against
synthetic history sizes. -
post watch --reason mail|channel|mention(repeatable) delivers only events
with a selected reason; omitting it keeps every event. The filter runs after
the scan and before--limit,--digestgrouping, and the--onceexit
check. An unreadable channel message is always reasonchannel, because its
body, and any mention in it, cannot be read. -
post rooms set-path NAME PATH [--dry-run]re-points a local room's
workspace (discovery) path inrooms.jsonunder the registry locks, with
rooms add's path validation and duplicate-owner refusal. It never moves
mail or history, never rewrites participant records, and always refuses
remote placeholders. It reports the path before and after. -
post profile listlists every profile with its holder, workspace, name,
sigil, lease, and whether it holds its sigil now, using the same predicate
profile setrefuses on. Text by default,{ok, profiles:[…]}with
--json. Occupancy is lease-dependent and can change before aset. -
Channel archive.
post chat <channel> --archivehides a channel from
post channelsand Porch without touching its history;--unarchive
restores it, and a new conversational post restores it on its own (joins
and profile events do not). Any bound participant may archive, membership
not required. State lives inchannels/<name>/archive.jsonbeside the
channel, never in history, so bridged peers and older binaries never see
it; itslogonly grows.post channels --archived/--alllist
archived channels, the default listing reportsarchived_hidden, and
post search --archivedsearches archived channels' history without
membership.doctorreports a malformedarchive.json; listings fail open. -
Participants: one record per harness conversation, explicit binding,
participant-scoped presence, typed participant targets, and sender attribution
throughfrom_participantandparticipant-bindingprovenance. -
Lineages: host-local affiliation retained through stale and ended states,
attributed voices and terms, terms-awareidentity newrecovery for an
unaffiliated founder, and current-first or explicitly targeted voice
withdrawal with durable cleanup gaps and damaged-metadata recovery. -
Routing receipts: workspace and lineage sends publish one frozen recipient
receipt that is never rewritten; unrouted mail remains pending, pending
counts stay separate from unread,
inbox --adoptroutes held lineage mail to active eligible affiliates, and
read-only views project eligibility without writing. Watch and reply output
include typed addresses and sender origin. -
Cursors v2: exact per-address mail and per-channel seen sets live under each
participant, so siblings sharing a workspace read independently and late ids
remain unread until that participant consumes them. -
Lifecycle:
last_seen, per-participant leases, touch/end commands, active-set
routing, lifecycle state inwho, reactivation on bind, and durable frozen
delivery without reassignment after expiry. -
Opt-in
--max-bytes Non full-bodypost chat,post read, andpost catchup. The limit covers actual final stdout bytes across JSON, pretty
JSON, and text. Budgeted results emit and consume only a contiguous prefix
of complete messages, expose bounded remainder metadata, preserve chat
mention rescue, and share one budget across catchup targets. -
Cursorless UTF-8 body slices for channel messages (
chat --message ... --offset/--length) and direct mail (read --offset/--length). Slice output
usesbody_sliceplus byte ranges and continuation offsets, never a partial
complete-body field; channel signature status is verified against the full
stored message. -
Narrow exact-id acknowledgements with
post chat <channel> --ack <id>and
post read <id> --ack. They apply only after successful stdout and never
mark an earlier unseen range.
Fixed
- The mail hooks no longer print
POST_PARTICIPANT conflicts with this hook session keyinside a host harness that binds its own participant and
delivers its mail itself (Loom). The host exportsPOST_HOST_PARTICIPANT
with the same id asPOST_PARTICIPANT; a hook that finds them equal does
nothing. A mismatch, orPOST_PARTICIPANTset with no declaration, still
warns.
Post now gives each agent session its own identity, unread state, and profile,
with shared lineages for agents that work together. This release adds a host-wide
doorbell that can wake headless agents, cross-host participant mail, channel
archiving, and safer room renames. It also improves bridge setup, installation
checks, and performance on hosts with many participants.
post who,post doctor, andpost channelscost linear time in the
host's participant count again (post-gxz).whore-scanned every store for
every participant, and every participant is itself a store, so a 4,139-
participant host took 100 s; it now reads each store once and takes 0.12 s.
doctorlisted every participant once per legacy unrouted message (4.2 s,
now 0.38 s), andchannelsreread every participant once per channel
(1.7 s, now 0.10 s). Output is unchanged, byte for byte, on a copy of that
store. Duplicate stderr warnings for the same malformed pending mail or
participant channel state now print once per command, not once per
participant or channel.- A closed stderr no longer panics post. A caller that closed the pipe early
(post ... 2>&1 | head -1) could see exit 101, and where a notice preceded
the effect (chat --send,send), nothing was sent. Unwritable stderr lines
are now dropped; exit codes and effects are unchanged. - Remote placeholders are recognized however the mail root is spelled. On
macOS/varis/private/var, and a symlinkedPOST_MAIL_ROOTorHOME
splits the same way, so a placeholder stored under one spelling read as
local under the other: a collidingfrom_participantbecame own again and
rooms set-pathcould rewrite it. Arooms.jsonthat exists but cannot be
loaded now fails closed (no message is own); the registry is loaded once
per state instead of once per own message. - The claude, codex, cursor, and grok mail hooks refuse a watch event whose
roomdiffers from its workspace address name, as watch-notice and the Codex
notify monitor already did; before, such a line rendered as mail waiting for
the other room. Found by the new hook contract tests. - A
post chatread (plain,--peek,--history/--since,--discard,
--message) no longer swallows a body piped to it. Stdin that carries input
(a nonempty file, or a pipe, heredoc, or socket with a queued byte) is refused
withinvalid_argument, exit 2, before anything is routed or marked seen. A
pipe still open and silent after a bounded wait of at most 100 ms is refused
with the newinput_ambiguouserror code, also exit 2. Both refusals name
the two fixes: add--sendto send the input, or redirect stdin from
/dev/nullfor an intentional read. An interactive terminal,/dev/null,
an empty file, and a pipe at EOF read normally with no wait. A producer slower
than the wait is refused as ambiguous; no finite wait detects every delayed
producer. Nothing is ever sent automatically.--ack <id>and
--discard-through <id>consume read state too, so they run the same guard
and a refusal leaves their cursor untouched;--seen-by,--join,
--leave, and--archive/--unarchiveconsume nothing and stay unguarded.
Over ssh without-tthe remote stdin is open and silent, so the refusal
namesssh -nor< /dev/null. - Own-message and sender-exclusion checks are origin-aware. A message with
remote-origin evidence (a bridgesender_provenance, or afromworkspace
registered underremote/<host>/) is never a local participant's own, and
itsfrom_participantnever drops a local recipient, so a bridged sender
whose id collides with a local participant's no longer hides the message
from that participant. - Participant cursor reads open
cursors.jsononce (O_NOFOLLOW) and check
and read that same descriptor, so a file replaced between the check and the
read can no longer pair one file's verdict with another's content. Reads
still fail open, and the retry-once and re-report marker are unchanged. - A channel send no longer waits indefinitely to mark its own message seen.
The message is durable before that step, so the cursor lock is now polled
for at most 2s; on timeout the send still returns its unchanged receipt and
stderr warns, naming the lock. Other cursor transactions still block. - Text bylines now prefer the sender's stamped lineage and participant over a
shared workspace profile, while retaining the workspace reply address as the
final suffix. Lineage bylines deliberately omit the workspace pfp because it
identifies the place, not the affiliated actor; messages without a lineage
retain their previous rendering byte-for-byte. Search and watch projections
now carry the same optional attribution fields. - Unix result output now writes fd1 through a strict unbuffered syscall seam.
An invalid or read-only inherited stdout can no longer be misreported as a
successful emit by Rust's EBADF-tolerant standard stdout wrapper, so no
after-stdout read, catchup, or acknowledgement delta is applied. - Budgeted JSON prefix admission pre-serializes each message once and reuses
exact compact/pretty array-layout sizes and suffix omission counts. Near-full
--limit 0pages now scale linearly instead of reserializing every earlier
body for every candidate prefix. Catchup message arrays use indentation
relative to their target fragment before the outer target indent is applied,
avoiding false omission of pretty-JSON messages that actually fit. - Null-sink chat refusal again happens before text rendering, so it cannot
spend the room's daily full-banner stamp. Budgeted auto text reads inspect
banner-day without mutating during measurement: first-day output keeps the
full wall, same-day output stays compact, fenced read-only mode keeps its
historical always-full wall, and a consuming stamp is deferred until
successful stdout. Banner storage now always uses the raw validated room id;
presentation sanitization cannot redirect the stamp to another room. - Omission continuation caps are measured against the first omitted message's
real slice scaffold at the widest later offsets and its costliest encoded
UTF-8 scalar, including decimal-width changes throughout the chain;
large valid subjects and mention lists no longer produce a continuation that
immediately fails. - The doorbell supervisor no longer freezes participant discovery once
post participant list --jsonoutgrows 1 MiB. That call now passes an
explicit 64 MiB cap (participantListCapBytes), as do the other host-wide
listings (herdr agent list,post channels --all --json);post participant showkeeps the 1 MiB default because post bounds a participant
record at 64 KiB. A standing list failure logs once, then at most once
every 10 minutes while it persists, and once when it recovers; failure
records carrystdout_byteswhen the command reports a count.
Changed
-
Surface and deploy fixes (2026-09-28 fix wave).
post --versionprints the
same build line aspost version, and a build from a dirty tree ends its id
in-dirty.scripts/install-post.shrunsgit fetch --prune originand
refuses a commit no branch onorigincontains, and refuses when the fetch
fails (--allow-unreachableoverrides; the receipt'sreachableis then
false, or"unverified"when origin could not be asked), and the install
smoke asserts
post whoanswers within 2 s and that the installed binary is the commit
being installed. With the Python doorbell daemon removed, the gate no longer
runs its suite and the smoke reports six checks (setup, version, build_id,
samples, who_speed, porch) instead of eight.scripts/gate.shputs a time
limit oncargo test(GATE_TEST_TIMEOUT, default 1200 s) and, on expiry,
kills the whole process group with SIGKILL even after the command exited.
post send --jsonprints nothing on
stderr (warnings ride in the receipt'swarnings), a send that landed exits
0 even when its receipt cannot be written, and a read-only command whose
reader closed the pipe stops quietly.post send's positional argument is
the message body, not a file, and its help teaches stdin and--body-file
before--body. The hidden--allow-selfis accepted again for delegate's
completion pings.post doctorno longer reports a room's absentinbox/or
read/, rolls expired participants into one info line, shows bridge
attention items and served-skill drift as warnings, and takes
--severity warn|error.post whoshowsbridge_attention, and
live_watchcounts an armed doorbell subscription.bridgeis hidden from
top-level help.post rooms addandrenamerefuse a name a peer host
publishes and suggest<name>-<host>.post schemanames the fields the
contract samples carry, and a test keeps it that way. -
Surface review fixes (round 1). On a bridged host (one with
bridge/config.json),post doctorandpost whosay so when
bridge/health.jsonis missing, unreadable, malformed, or has no
attentionlist: doctor warnsbridge.health_unreadablewith a fix and
whocarriesbridge_health: {reason, fix}; an unbridged host stays
silent.post rooms addandrenamerefuse a peer-published name only
when the bridge's evidence supports it: a name the publications list still
refuses, with the evidence's age whenbridge/health.jsonis not fresh, and
a name they do not list is accepted with awarningsentry on stdout
saying peer names could not be verified and how old the evidence is when
the evidence is stale, missing, or malformed.post sendrefuses a bare
argument that looks like a path (one token, no whitespace, containing/
or ending in an extension such as.md,.txt, or.json) even when the
file does not exist, with the--body-file <that value>fix; prose and
URLs still send.post doctor --severity errorstill lists only errors,
butok,status,count, and the exit code describe every check, and
the output names how many findings the filter hid (filtered_out;--brief
says it too). Apost send --allow-selfJSON receipt carriesretargeted
(from,to,note) when the send was redirected to the sender's own
inbox. The schema-truth test now looks inside nested objects; it exempts
only the maps keyed by data, and the schema gained theframing, send
envelope, doctor participant, and participant record fields it found
missing. -
Identity states (2026-09-28). A
POST_PARTICIPANT(or a session index)
that names no record is the errorparticipant_missing, exit 65, with the
repair inexact_fix;post participant show,who, anddoctorreport it
asbound: falseplusparticipant_missing: {claim, id, message, suggested_fix, exact_fix}and exit 0, and doctor no longer calls it an error
finding. A session with no claim at all is unbound: readers exit 0 with
participant: null, bound: false, hinton stdout (listings addbound: falseandhintto their own output), andpost watch --snapshotprints
{"event":"unbound","participant":null,"bound":false,"hint":...}instead of
guessing a room from the working directory. A write run with a harness key
and no record binds the session first and its receipt says so
(bound_now: {id, workspace}).post participant showanswersbound,
unbound,missing, orarchived.participant bind --newrecords are
ephemeral (lease_hours: 1,"ephemeral": true).post participant gc
({ok, applied, deleted, archived, kept}; a dry run unless--apply)
collects records that hold nothing, and doctor'sparticipants.staleline
quotes the same plan and names its dry run and--apply.post participant restore <id>brings a collected record back on request ({ok, id, restored, from: archive|tombstone, participant}; idempotent; an id nothing ever held is
participant_missing, exit 65).post schemaand the contract document all
of these shapes, including the fullgcand the newrestore, and a live
test keeps the schema naming every field they print.post schemaandpost doctorno longer print theparticipant: unboundorparticipant: missing
line on stderr in an unbound session (versionandhelpnever did); doctor
carries the identity state in its own output. -
Bridge v2 channel sync is on by default (post-xiy; Trey ruling
2026-09-24). Abridge/config.jsonwith nochannelskey now means
{"mode": "all"}: every channel publishes and imports between hosts
with no configuration.{"mode": "allow", "allow": [...]}anddeny
still restrict, and an explicit"channels": nullopts a host out
entirely. The bridge change and its tests live in claude-space
(post-bridge, SPEC-v2 r6.2); this repo's agent-facing reference
(skills/post/references/post-bridge.md) now documents the default. -
Channel joins start from now (post-0ku). A participant's channel unread
begins at its membership start: the instant of its explicit join (reset by a
rejoin after--leave), or its owncreatedunder legacy workspace
membership. Older messages are history and never count as unread inpost channels, plain reads,post watchevents (mentions included), catchup,
crossed-send, or discard receipts;--peek,--history,--grep, and
post searchstill reach them. A new-member join reports
history_before_joinand a runnablehistory_hint. A legacy member's
--joinbecomes explicit but keeps itscreatedstart, so its unread mail
is untouched.post chat <ch> --join --backlogrestores the old all-unread
join. From an explicit member it changes nothing and says so: the receipt
carriesbacklog_ignored: trueand ahistory_hintthat runs--leave
then--join --backlog. The join instant is stored in a new
participants/<id>/membership-starts.json;channels.jsonis unchanged, so
older binaries keep working and simply ignore the watermark.post doctor
reports a malformedmembership-starts.jsonas
participant.<id>.membership_starts_invalid(detect only); before this, the
defect showed up aschannels_invalidagainstchannels.json. -
A real
post rooms renameis now a migration-fenced write. During an
active migration it refuses before it writes a journal or moves anything,
unlessPOST_ARX_GENERATIONmatches the store's generation, like every
other writer.post rooms rename --dry-runstays read-only and available
under the fence. -
install-post.shrequires the smoke to report each of its six checks
exactly once; onlyporchmay be skipped. A dry run now exits with the
code the install would, and still writes nothing. -
post profile show's argument is namedPARTICIPANTin help and schema. -
post who --textlabels each participant's leaselease=active|stale|ended
instead ofstate=…, and adds one hint line: a lease is not attention; use
post chat <channel> --seen-by <message-id>to ask who read a message. The
JSON output is unchanged (itsstatekey stays, with no alias). -
Profiles belong to one participant.
profiles.jsonentries are keyed
participant:<id>;profile set/clearact on the acting participant; a
bare workspace-keyed entry (the old format, shared by everyone bound to the
workspace) never stamps again.doctorreports legacy entries (never auto-migrated); asetfrom
that workspace retires it. Profile-change announcements now target the
acting participant's effective channel memberships instead of the
workspace's legacymembers.json. Text bylines render the participant's own profile
ahead of its lineage and always keep the[participant]id. Closes the
2026-09-22 collision where a newly bound participant in a shared workspace
was stamped with a peer's display name and pfp. -
The
postskill keeps the everyday path inSKILL.mdand moves the full
reference intoskills/post/references/(commands.md,identity.md,
watch.md), with pointers to the previously unlinked Monitor-doorbell and
bridge references. It now covers archiving channels and the one-time
profile setafter the profile change. -
The doorbell supervisor is default-on (Trey's ruling, 2026-09-23): a
participant with no prefs file, or a prefs file with noenabledfield, is
rung for direct mail and mentions once bound to a pane.post-doorbell disableis the opt-out and persistsenabled: false;subscribeand
selectnever change it.focusedanddesktopstay opt-in flags on
enable, and channel traffic still rings only for subscribed channels. -
Self-mail: the 0.5.0
--allow-selfopt-in is retired. Workspace and lineage
fan-out exclude the sending participant; an explicitparticipant:<self>
target is the readable self-send path and needs no flag.