agents-can-communicate 0.1.18 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -69
- package/SECURITY.md +31 -0
- package/bin/acc-bootstrap.mjs +56 -0
- package/bin/acc-claude-channel.mjs +177 -0
- package/bin/acc-hook.mjs +94 -12
- package/bin/acc-mcp.mjs +6 -2
- package/bin/acc.mjs +13 -3
- package/docs/ADAPTER_AUTHORING.md +204 -0
- package/docs/ARCHITECTURE.md +131 -0
- package/docs/CAPABILITIES.md +117 -214
- package/docs/CLI.md +164 -0
- package/docs/CONCEPTS.md +134 -0
- package/docs/CONFIGURATION.md +147 -0
- package/docs/DESIGN_DECISIONS.md +89 -0
- package/docs/GETTING_STARTED.md +145 -0
- package/docs/GLOSSARY.md +26 -0
- package/docs/HOW_IT_WORKS.md +277 -0
- package/docs/MCP.md +94 -0
- package/docs/PROTOCOL.md +200 -0
- package/docs/RELEASING.md +115 -0
- package/docs/SECURITY_MODEL.md +131 -0
- package/docs/TROUBLESHOOTING.md +108 -0
- package/docs/WHY_ACC.md +61 -0
- package/docs/index.md +44 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/certification.json +228 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/PreToolUse-Edit.json +19 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/PreToolUse.json +17 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/SessionEnd.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/SessionStart.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/UserPromptSubmit.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/certification-provenance.json +269 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.252.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.258.json +23 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/fixtures/delivery/claude-code-2.1.260.json +23 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/package.json +13 -2
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/.mcp.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/adapter.mjs +45 -5
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/channel.mjs +377 -0
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/install.mjs +27 -7
- package/node_modules/@agents-can-communicate/adapter-claude-code/src/native-delivery.mjs +229 -0
- package/node_modules/@agents-can-communicate/adapter-codex/certification.json +150 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/PreToolUse.json +14 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/SessionEnd.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/SessionStart.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/UserPromptSubmit.json +10 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/certification-provenance.json +199 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.0.json +21 -0
- package/node_modules/@agents-can-communicate/adapter-codex/fixtures/delivery/codex-cli-0.152.1-remote-workspace.json +25 -0
- package/node_modules/@agents-can-communicate/adapter-codex/package.json +11 -2
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-codex/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-codex/src/adapter.mjs +54 -12
- package/node_modules/@agents-can-communicate/adapter-codex/src/app-server-client.mjs +121 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/native-delivery.mjs +151 -0
- package/node_modules/@agents-can-communicate/adapter-codex/src/ws-json-rpc.mjs +192 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/certification.json +68 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/gemini-extension.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/extension/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeAgent-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeTool-0.57.0.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/BeforeTool-shell-0.57.0.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/SessionEnd-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/SessionStart-0.57.0.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/fixtures/certification-provenance.json +293 -0
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/package.json +8 -1
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/adapter.mjs +31 -13
- package/node_modules/@agents-can-communicate/adapter-gemini-cli/src/install.mjs +4 -2
- package/node_modules/@agents-can-communicate/adapter-grok/certification.json +3 -0
- package/node_modules/@agents-can-communicate/adapter-grok/package.json +2 -1
- package/node_modules/@agents-can-communicate/adapter-grok/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-grok/src/adapter.mjs +11 -9
- package/node_modules/@agents-can-communicate/adapter-kimi/certification.json +52 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/PreToolUse-Bash.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/PreToolUse-Write.json +12 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/SessionHeartbeat.json +7 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/SessionStart.json +9 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/UserPromptSubmit.json +8 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/fixtures/certification-provenance.json +66 -0
- package/node_modules/@agents-can-communicate/adapter-kimi/package.json +8 -1
- package/node_modules/@agents-can-communicate/adapter-kimi/plugin/skills/acc/SKILL.md +22 -22
- package/node_modules/@agents-can-communicate/adapter-kimi/src/adapter.mjs +7 -3
- package/node_modules/@agents-can-communicate/adapter-sdk/package.json +1 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/capabilities.mjs +52 -18
- package/node_modules/@agents-can-communicate/adapter-sdk/src/certification.mjs +158 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/context-projector.mjs +36 -17
- package/node_modules/@agents-can-communicate/adapter-sdk/src/hook-shim.mjs +9 -1
- package/node_modules/@agents-can-communicate/adapter-sdk/src/index.mjs +7 -2
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-activation.mjs +76 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-delivery.mjs +202 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/native-vocabulary.mjs +101 -0
- package/node_modules/@agents-can-communicate/adapter-sdk/src/session-binding.mjs +28 -4
- package/node_modules/@agents-can-communicate/cli/package.json +1 -1
- package/node_modules/@agents-can-communicate/cli/src/args.mjs +12 -31
- package/node_modules/@agents-can-communicate/cli/src/doctor-command.mjs +70 -5
- package/node_modules/@agents-can-communicate/cli/src/help.mjs +2 -5
- package/node_modules/@agents-can-communicate/cli/src/install-command.mjs +111 -12
- package/node_modules/@agents-can-communicate/cli/src/main.mjs +100 -121
- package/node_modules/@agents-can-communicate/core/package.json +1 -1
- package/node_modules/@agents-can-communicate/core/src/attention.mjs +106 -0
- package/node_modules/@agents-can-communicate/core/src/conversations.mjs +276 -0
- package/node_modules/@agents-can-communicate/core/src/delivery-bindings.mjs +131 -0
- package/node_modules/@agents-can-communicate/core/src/finish-retries.mjs +97 -0
- package/node_modules/@agents-can-communicate/core/src/inbox.mjs +91 -107
- package/node_modules/@agents-can-communicate/core/src/index.mjs +3 -3
- package/node_modules/@agents-can-communicate/core/src/intents.mjs +0 -1
- package/node_modules/@agents-can-communicate/core/src/ports.mjs +2 -1
- package/node_modules/@agents-can-communicate/core/src/receipts.mjs +109 -0
- package/node_modules/@agents-can-communicate/core/src/service.mjs +21 -10
- package/node_modules/@agents-can-communicate/core/src/sessions.mjs +22 -20
- package/node_modules/@agents-can-communicate/core/src/status.mjs +11 -9
- package/node_modules/@agents-can-communicate/core/src/sync.mjs +3 -294
- package/node_modules/@agents-can-communicate/delivery-router/package.json +12 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/index.mjs +1 -0
- package/node_modules/@agents-can-communicate/delivery-router/src/router.mjs +131 -0
- package/node_modules/@agents-can-communicate/hook-runner/package.json +4 -2
- package/node_modules/@agents-can-communicate/hook-runner/src/client-version.mjs +20 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/native-binding.mjs +90 -0
- package/node_modules/@agents-can-communicate/hook-runner/src/runner.mjs +190 -105
- package/node_modules/@agents-can-communicate/installer/package.json +1 -1
- package/node_modules/@agents-can-communicate/installer/src/apply.mjs +70 -10
- package/node_modules/@agents-can-communicate/installer/src/bootstrap-runtime.mjs +144 -0
- package/node_modules/@agents-can-communicate/installer/src/detect.mjs +89 -5
- package/node_modules/@agents-can-communicate/installer/src/index.mjs +10 -2
- package/node_modules/@agents-can-communicate/installer/src/native-activation.mjs +161 -0
- package/node_modules/@agents-can-communicate/installer/src/ownership.mjs +112 -12
- package/node_modules/@agents-can-communicate/installer/src/plan.mjs +54 -2
- package/node_modules/@agents-can-communicate/installer/src/shell-bootstrap.mjs +210 -0
- package/node_modules/@agents-can-communicate/mcp-server/package.json +1 -1
- package/node_modules/@agents-can-communicate/mcp-server/src/input-validator.mjs +79 -0
- package/node_modules/@agents-can-communicate/mcp-server/src/resources.mjs +23 -28
- package/node_modules/@agents-can-communicate/mcp-server/src/server.mjs +102 -72
- package/node_modules/@agents-can-communicate/mcp-server/src/tools.mjs +54 -97
- package/node_modules/@agents-can-communicate/protocol/package.json +1 -1
- package/node_modules/@agents-can-communicate/protocol/src/config.mjs +1 -1
- package/node_modules/@agents-can-communicate/protocol/src/conversations.mjs +64 -0
- package/node_modules/@agents-can-communicate/protocol/src/fields.mjs +17 -0
- package/node_modules/@agents-can-communicate/protocol/src/index.mjs +4 -1
- package/node_modules/@agents-can-communicate/protocol/src/schema.mjs +64 -90
- package/node_modules/@agents-can-communicate/protocol/src/states.mjs +13 -40
- package/node_modules/@agents-can-communicate/storage-filesystem/package.json +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/active-journal.mjs +230 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/atomic-json.mjs +77 -28
- package/node_modules/@agents-can-communicate/storage-filesystem/src/identity.mjs +1 -1
- package/node_modules/@agents-can-communicate/storage-filesystem/src/journal.mjs +83 -35
- package/node_modules/@agents-can-communicate/storage-filesystem/src/retention.mjs +112 -0
- package/node_modules/@agents-can-communicate/storage-filesystem/src/safe-file.mjs +18 -8
- package/node_modules/@agents-can-communicate/storage-filesystem/src/store.mjs +68 -26
- package/node_modules/@agents-can-communicate/storage-filesystem/src/writer-mutex.mjs +113 -35
- package/package.json +20 -1
- package/node_modules/@agents-can-communicate/core/src/communication.mjs +0 -334
- package/node_modules/@agents-can-communicate/core/src/message-signals.mjs +0 -41
- package/node_modules/@agents-can-communicate/core/src/notify.mjs +0 -95
- package/node_modules/@agents-can-communicate/core/src/tasks.mjs +0 -244
- package/node_modules/@agents-can-communicate/core/src/workstreams.mjs +0 -109
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# How ACC works
|
|
2
|
+
|
|
3
|
+
ACC is a local communication layer around AI sessions that the user opened independently.
|
|
4
|
+
It gives those sessions a shared set of coordination facts without giving ACC ownership of
|
|
5
|
+
their prompts, permissions, processes, or work.
|
|
6
|
+
|
|
7
|
+
This page follows one interaction from client startup to an acknowledged answer. It is the
|
|
8
|
+
engineering tour; [Protocol](PROTOCOL.md) is the normative record contract and
|
|
9
|
+
[Architecture](ARCHITECTURE.md) is the package-level reference.
|
|
10
|
+
|
|
11
|
+
## The whole system in one picture
|
|
12
|
+
|
|
13
|
+
```mermaid
|
|
14
|
+
flowchart LR
|
|
15
|
+
U["human direction and authority"] --> A["independent session A"]
|
|
16
|
+
U --> B["independent session B"]
|
|
17
|
+
A --> X["CLI, MCP, or adapter hook"]
|
|
18
|
+
X --> C["vendor-neutral core"]
|
|
19
|
+
C <--> S[("filesystem store outside the repository")]
|
|
20
|
+
C --> Y["B adapter hook or acc inbox"]
|
|
21
|
+
Y --> B
|
|
22
|
+
C --> R["optional live-delivery router"]
|
|
23
|
+
R -.-> B
|
|
24
|
+
B --> Z["CLI, MCP, or adapter hook"]
|
|
25
|
+
Z --> C
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
There is no required ACC daemon and no coordinator model. CLI commands and native hooks
|
|
29
|
+
are short-lived local processes that open the same workspace store when needed. An MCP
|
|
30
|
+
client may keep its own `acc-mcp` stdio child running, but that child is a tool boundary for
|
|
31
|
+
one participant, not a scheduler or owner of the room.
|
|
32
|
+
|
|
33
|
+
## 1. A client session joins a workspace
|
|
34
|
+
|
|
35
|
+
For a native client, an installed startup hook sends the client event to `acc-hook`. The
|
|
36
|
+
vendor adapter normalizes that event, then the hook runner:
|
|
37
|
+
|
|
38
|
+
1. discovers the workspace;
|
|
39
|
+
2. opens its filesystem store;
|
|
40
|
+
3. resolves the exact client version and its certified capabilities;
|
|
41
|
+
4. opens or resumes one ACC session generation; and
|
|
42
|
+
5. stores a small binding from the client's session id to that ACC generation.
|
|
43
|
+
|
|
44
|
+
Workspace discovery uses an explicit `acc.workspace.json` first, the Git common directory
|
|
45
|
+
second, and the canonical plain directory otherwise. Git worktrees therefore share one
|
|
46
|
+
room but keep separate checkout and branch facts. Git itself is optional.
|
|
47
|
+
|
|
48
|
+
A participant is the address messages target. A session is one current opening of that
|
|
49
|
+
participant, and its unguessable generation token prevents an old process from mutating a
|
|
50
|
+
replacement session. The default participant name is derived from the client session. Set
|
|
51
|
+
`ACC_PARTICIPANT` in the client launch environment when an address must survive a client
|
|
52
|
+
restart and recover messages sent while it was away.
|
|
53
|
+
|
|
54
|
+
Attaching one session does not create durable workspace history. Its presence and intent
|
|
55
|
+
can remain ephemeral until a second live session appears or someone creates the first
|
|
56
|
+
claim, message, or handoff. This is how ACC stays silent when a session is alone without
|
|
57
|
+
making discovery unreliable when a peer arrives.
|
|
58
|
+
|
|
59
|
+
## 2. Sessions publish awareness, not assignments
|
|
60
|
+
|
|
61
|
+
An intent records a short summary, mode, and resource hints. A claim adds a leased
|
|
62
|
+
reservation for a canonical resource such as `file:src/item.mjs` or `file:src/**`.
|
|
63
|
+
Neither creates a task or grants one session authority over another.
|
|
64
|
+
|
|
65
|
+
A claim is `guarded` only when every live client path involved has a captured pre-write
|
|
66
|
+
guard. Otherwise it is `advisory`: visible coordination that peers should respect, but not
|
|
67
|
+
an operating-system lock. Even a guarded claim cannot stop unrelated programs or a write
|
|
68
|
+
path the client never exposes to its adapter.
|
|
69
|
+
|
|
70
|
+
## 3. Sending commits the durable truth first
|
|
71
|
+
|
|
72
|
+
When session A sends an addressed question, the CLI or MCP boundary validates the closed
|
|
73
|
+
protocol shape and resolves A's current session generation. Core then performs one
|
|
74
|
+
filesystem transaction that creates:
|
|
75
|
+
|
|
76
|
+
- the immutable logical message, including sender, recipients, kind, obligation, thread,
|
|
77
|
+
and explicit body;
|
|
78
|
+
- one `queued` receipt for each recipient; and
|
|
79
|
+
- a `message.recorded` event.
|
|
80
|
+
|
|
81
|
+
The first message in a thread uses its own `messageId` as `threadId`. A caller-supplied
|
|
82
|
+
`clientMessageId` is a retry key: identical retries return the original message, while the
|
|
83
|
+
same key with different content is rejected.
|
|
84
|
+
|
|
85
|
+
For a room message, recipients are the known peer participants with open sessions at
|
|
86
|
+
commit time. Participants arriving later can find the room record through full forensic
|
|
87
|
+
sync, but they do not receive retroactive receipts.
|
|
88
|
+
|
|
89
|
+
Only after the transaction commits may ACC try to make the message arrive sooner. Thus a
|
|
90
|
+
crashed, unsupported, or unreachable adapter cannot erase a successfully recorded
|
|
91
|
+
question.
|
|
92
|
+
|
|
93
|
+
## 4. Delivery has three paths
|
|
94
|
+
|
|
95
|
+
The paths share one durable record but prove different facts.
|
|
96
|
+
|
|
97
|
+
### Durable inbox
|
|
98
|
+
|
|
99
|
+
`acc inbox` is the universal recovery path. It returns only messages addressed to the
|
|
100
|
+
calling participant and atomically advances that participant's receipt to `retrieved`.
|
|
101
|
+
An exact `acc inbox --message message_x` remains usable after context compaction or when a
|
|
102
|
+
message was too large to project safely.
|
|
103
|
+
|
|
104
|
+
### Certified next-turn projection
|
|
105
|
+
|
|
106
|
+
On an exact client version and platform with passing evidence, a before-turn hook asks core
|
|
107
|
+
for queued messages just before the client's next normal turn. The adapter projects each
|
|
108
|
+
complete body inside an attributed `untrusted peer message` frame. If the complete frame
|
|
109
|
+
does not fit the configured byte budget, the hook keeps the message id and the exact inbox
|
|
110
|
+
recovery command instead of silently truncating peer text.
|
|
111
|
+
|
|
112
|
+
The hook entry point records `offered` only after its stdout transport reports that the
|
|
113
|
+
bytes crossed the boundary. The state is not `retrieved`: ACC still has no observation
|
|
114
|
+
that the model attended to those bytes.
|
|
115
|
+
|
|
116
|
+
### Native live push
|
|
117
|
+
|
|
118
|
+
The delivery router offers an actionable message to an already-running session when all of
|
|
119
|
+
these hold at once:
|
|
120
|
+
|
|
121
|
+
- exactly one live generation for the recipient;
|
|
122
|
+
- an unexpired generation-bound delivery binding published by that session's own start;
|
|
123
|
+
- a recorded policy permitting this message kind (`actionable` covers question, request,
|
|
124
|
+
answer, decision, and handoff; `all` adds note; room messages are never live);
|
|
125
|
+
- an adapter that declares `delivery.livePush` and a native contract; and
|
|
126
|
+
- adapter acceptance of the bytes.
|
|
127
|
+
|
|
128
|
+
Any missing condition leaves the receipt `queued` and returns a safe fallback reason. Two
|
|
129
|
+
adapters ship a native transport, both experimental and off until a per-client opt-in:
|
|
130
|
+
|
|
131
|
+
```text
|
|
132
|
+
sender -> durable ACC record -> exact live binding -> vendor transport -> receiver
|
|
133
|
+
\-> queued inbox on every failure
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
- **Claude Code 2.1.258** uses a vendor Channel: an ACC-owned MCP child, started only when
|
|
137
|
+
the user's ordinary `claude` launch carries the captured development-channel flag, offers
|
|
138
|
+
the message as a native notification and routes the model's explicit `acc_reply` back as a
|
|
139
|
+
real ACC answer. Claude's development-channel warning is vendor-owned and stays visible.
|
|
140
|
+
- **Codex 0.152.1** adds the message to the App Server thread queue over the vendor daemon's
|
|
141
|
+
control socket; it is presented on the idle thread or after the current turn. Codex answers
|
|
142
|
+
through the ordinary `acc reply` command, so its reply route is not native.
|
|
143
|
+
|
|
144
|
+
Compatibility is decided at the launch-time bootstrap and again by a per-session handshake
|
|
145
|
+
bound to the exact client process; there is no maximum client version, but a newer stable
|
|
146
|
+
release must pass a current probe and handshake for the captured protocol, and an older,
|
|
147
|
+
prerelease, known-bad, or uncaptured client stays durable-only. ACC is never the parent of
|
|
148
|
+
a model session after the shell `exec`, and `ACC_BYPASS=1` starts the unmodified client.
|
|
149
|
+
|
|
150
|
+
## 5. Reply closes the communication obligation
|
|
151
|
+
|
|
152
|
+
The complete question-and-answer path is:
|
|
153
|
+
|
|
154
|
+
```mermaid
|
|
155
|
+
sequenceDiagram
|
|
156
|
+
participant A as Session A
|
|
157
|
+
participant Core
|
|
158
|
+
participant Store
|
|
159
|
+
participant B as Session B
|
|
160
|
+
A->>Core: addressed question for B
|
|
161
|
+
Core->>Store: message + queued receipt + event
|
|
162
|
+
Store-->>Core: transaction committed
|
|
163
|
+
Core-->>A: recorded with delivery outcome
|
|
164
|
+
alt certified next-turn
|
|
165
|
+
B->>Core: normal turn hook
|
|
166
|
+
Core-->>B: attributed untrusted envelope
|
|
167
|
+
Core->>Store: receipt offered after transport accepts bytes
|
|
168
|
+
else durable inbox
|
|
169
|
+
B->>Core: acc inbox for the message
|
|
170
|
+
Core->>Store: advance B receipt to retrieved
|
|
171
|
+
Core-->>B: attributed message and receipt
|
|
172
|
+
end
|
|
173
|
+
B->>Core: reply to the question id
|
|
174
|
+
Core->>Store: answer + A receipt + acknowledge B receipt
|
|
175
|
+
Core-->>A: answer through next-turn or inbox
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`reply` verifies that B owns the original receipt, writes an `answer` in the same thread,
|
|
179
|
+
creates the answer's receipt for A, and advances B's original receipt to `acknowledged` in
|
|
180
|
+
one transaction. A transport failure after that cannot undo the reply. `ack` performs the
|
|
181
|
+
last transition without creating an answer when the obligation only asks for
|
|
182
|
+
acknowledgement.
|
|
183
|
+
|
|
184
|
+
Receipts are per recipient and monotonic:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
queued -> offered -> retrieved -> acknowledged
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
`recorded` is the successful send boundary, not a receipt state. `offered` does not mean
|
|
191
|
+
read, `retrieved` does not prove model attention, and `acknowledged` resolves communication
|
|
192
|
+
only. A reply saying “I will do it” is not evidence that the requested work finished; ACC
|
|
193
|
+
does not have accepted, running, or done task states.
|
|
194
|
+
|
|
195
|
+
## 6. The filesystem is the control plane
|
|
196
|
+
|
|
197
|
+
Each workspace lives below the platform data home, conceptually:
|
|
198
|
+
|
|
199
|
+
```text
|
|
200
|
+
<platform data home>/acc/workspaces/workspace_x/
|
|
201
|
+
├── protocol.json store version and workspace identity
|
|
202
|
+
├── state/<kind>/<id>.json materialised current records
|
|
203
|
+
├── events/<sequence>.json immutable semantic history
|
|
204
|
+
├── journal/ crash-recovery authority
|
|
205
|
+
├── locks/ one cross-process writer mutex
|
|
206
|
+
├── ephemeral/ non-durable presence, intent, and delivery bindings
|
|
207
|
+
├── bindings/ client-session to ACC-generation mappings
|
|
208
|
+
├── retained/ logical deletion and retired evidence
|
|
209
|
+
└── tmp/ staged atomic publications
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
This layout is diagnostic, not a public mutation API. Clients write through the protocol
|
|
213
|
+
and core rather than editing these files.
|
|
214
|
+
|
|
215
|
+
On macOS the platform data home is `~/Library/Application Support`; on Linux it is
|
|
216
|
+
`$XDG_DATA_HOME` or `~/.local/share`. `ACC_DATA_HOME` replaces that base. Runtime paths are
|
|
217
|
+
checked against workspace roots so ACC state cannot be placed inside the project.
|
|
218
|
+
|
|
219
|
+
Every durable mutation holds the same writer mutex, loads only its declared record kinds,
|
|
220
|
+
checks state generations, stages the full result, and writes a recovery journal before
|
|
221
|
+
publishing. Events are immutable no-replace files; current state is a materialised view
|
|
222
|
+
replaced atomically. While publication is incomplete, the active journal bounds event
|
|
223
|
+
cursors below the transaction's first sequence; the next store opener rolls a decided
|
|
224
|
+
transaction forward before serving a read. The store prefers a recoverable duplicate offer
|
|
225
|
+
after a crash to an unearned delivery claim.
|
|
226
|
+
|
|
227
|
+
Paths are opened with containment and no-follow checks. Corrupt records, a foreign
|
|
228
|
+
workspace identity, and unknown store versions fail before mutation rather than being
|
|
229
|
+
guessed into a compatible shape.
|
|
230
|
+
|
|
231
|
+
## 7. Adapters translate evidence, not product semantics
|
|
232
|
+
|
|
233
|
+
The vendor-neutral layers know nothing about Codex, Claude Code, Gemini CLI, Grok, or Kimi
|
|
234
|
+
Code. `protocol` owns record shapes and state transitions; `core` owns coordination rules;
|
|
235
|
+
`storage-filesystem` owns persistence. Vendor adapters own hook payloads, installation,
|
|
236
|
+
client-specific response shapes, and captured capability evidence. `hook-runner` applies
|
|
237
|
+
the common bounded fail-open lifecycle, while `delivery-router` evaluates optional live
|
|
238
|
+
delivery.
|
|
239
|
+
|
|
240
|
+
Unknown client versions and platforms inherit no capability. A method in an adapter or a
|
|
241
|
+
vendor documentation example is not enough: every `true` capability needs a retained
|
|
242
|
+
real-client fixture. This is why the same product can degrade visibly from next-turn
|
|
243
|
+
projection to inbox polling without changing its message or receipt semantics.
|
|
244
|
+
|
|
245
|
+
## 8. What crosses the trust boundary
|
|
246
|
+
|
|
247
|
+
ACC stores only explicit coordination data: identity, presence, one-line intent, claims,
|
|
248
|
+
messages the sender chose to send, structured handoffs, receipts, artifacts, and events.
|
|
249
|
+
It does not collect raw prompts, assistant responses, transcripts, environment variables,
|
|
250
|
+
credentials, or permission approvals.
|
|
251
|
+
|
|
252
|
+
Peer bodies remain untrusted data. The projector attributes the sender, escapes framing
|
|
253
|
+
and terminal control sequences, and never promotes peer text to system authority. The
|
|
254
|
+
receiving session still evaluates it under its own instructions and permissions.
|
|
255
|
+
|
|
256
|
+
Every native hook has a five-second ceiling and fails open: if ACC cannot read the store or
|
|
257
|
+
decide safely, the client's action continues. Coordination may become less effective, but
|
|
258
|
+
ACC itself must not stop a session from working.
|
|
259
|
+
|
|
260
|
+
## Trace the implementation
|
|
261
|
+
|
|
262
|
+
The shortest source-code path is:
|
|
263
|
+
|
|
264
|
+
1. `packages/hook-runner/src/runner.mjs` — attach, before-turn projection, guards, and the
|
|
265
|
+
fail-open boundary;
|
|
266
|
+
2. `packages/core/src/conversations.mjs` — record-first messages, threads, receipts, and
|
|
267
|
+
handoffs;
|
|
268
|
+
3. `packages/core/src/inbox.mjs` — recipient-owned retrieval, reply, and acknowledgement;
|
|
269
|
+
4. `packages/delivery-router/src/router.mjs` — policy, reachability, certification, and live
|
|
270
|
+
fallback;
|
|
271
|
+
5. `packages/storage-filesystem/src/store.mjs` — transactions, snapshots, ephemeral state,
|
|
272
|
+
and recovery; and
|
|
273
|
+
6. `packages/adapter-sdk/src/context-projector.mjs` — bounded untrusted peer framing.
|
|
274
|
+
|
|
275
|
+
Continue with [Capabilities](CAPABILITIES.md) for what each shipped client actually proves,
|
|
276
|
+
[Security model](SECURITY_MODEL.md) for trust boundaries and attack tests, and
|
|
277
|
+
[Adapter authoring](ADAPTER_AUTHORING.md) for the integration contract.
|
package/docs/MCP.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# MCP
|
|
2
|
+
|
|
3
|
+
`acc-mcp` lets a client with no native ACC adapter participate over stdio. It exposes the
|
|
4
|
+
same durable messages, threads, receipts, intent, claims, and handoffs, but it cannot infer
|
|
5
|
+
the client's lifecycle, intercept writes, inject a normal turn, or push a message. The
|
|
6
|
+
client polls tools under its own control.
|
|
7
|
+
|
|
8
|
+
```mermaid
|
|
9
|
+
graph LR
|
|
10
|
+
C["independently opened MCP client"] -->|"stdio JSON-RPC"| M["acc-mcp"]
|
|
11
|
+
M --> S[("ACC durable store")]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Register
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"command": "acc-mcp",
|
|
19
|
+
"env": {
|
|
20
|
+
"ACC_MCP_PARTICIPANT": "research",
|
|
21
|
+
"ACC_MCP_WORKSPACE": "/absolute/path/to/project"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`acc-mcp` accepts no command-line arguments. `ACC_MCP_PARTICIPANT` is the stable recipient
|
|
27
|
+
identity for this server. It comes from user-owned launch configuration, never from MCP
|
|
28
|
+
`initialize` or `clientInfo`. `ACC_MCP_WORKSPACE` should be absolute; without it, the
|
|
29
|
+
server uses its launch directory, which may be a different workspace from the other
|
|
30
|
+
sessions.
|
|
31
|
+
|
|
32
|
+
The server implements MCP protocol revision `2026-07-28` over newline-delimited JSON-RPC
|
|
33
|
+
stdio. Tool input schemas are closed: unknown fields and invalid conditional shapes are
|
|
34
|
+
rejected before a session is resolved.
|
|
35
|
+
|
|
36
|
+
## Tools
|
|
37
|
+
|
|
38
|
+
| Tool | Required input | Optional input |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `acc_status` | — | — |
|
|
41
|
+
| `acc_sync` | — | `cursor`, `scope: delta|full`, `limit: 1..500` |
|
|
42
|
+
| `acc_work` | `summary` and `mode`, or `clear: true` | `state`, `resourceHints` |
|
|
43
|
+
| `acc_claim` | `action`; `resource` for acquire, `claimId` for renew | `mode`, `reason`, `leaseSeconds` where valid |
|
|
44
|
+
| `acc_release` | `claimId` | — |
|
|
45
|
+
| `acc_message` | `to`, `subject`, `body` | `kind`, `obligation`, `clientMessageId` |
|
|
46
|
+
| `acc_request` | `toParticipantId`, `title` | `detail`, `clientMessageId` |
|
|
47
|
+
| `acc_inbox` | — | `messageId` |
|
|
48
|
+
| `acc_reply` | `messageId`, `body` | `subject`, `clientMessageId` |
|
|
49
|
+
| `acc_ack` | `messageId` | — |
|
|
50
|
+
| `acc_finish` | `goal` | `status`, `completed`, `remaining`, `blockers`, `toParticipantId`, `clientMessageId` |
|
|
51
|
+
|
|
52
|
+
All tool names above are the complete model-facing surface. There are no execution or
|
|
53
|
+
client-control tools.
|
|
54
|
+
|
|
55
|
+
Send-like tools return a raw structured object with `{ message, delivery }`; their text
|
|
56
|
+
content is the JSON serialization of the same value. `acc_inbox` returns message/receipt
|
|
57
|
+
pairs and advances only this participant's receipts to `retrieved`. `acc_reply` writes an
|
|
58
|
+
`answer` and acknowledges the original atomically. `acc_ack` exposes no receipt-state
|
|
59
|
+
parameter.
|
|
60
|
+
|
|
61
|
+
Resources are `acc://snapshot`, `acc://roster`, and `acc://inbox`. Reading `acc://inbox`
|
|
62
|
+
resolves the configured MCP participant and advances only the returned receipts to
|
|
63
|
+
`retrieved`, just like the inbox tool. Snapshot and roster reads do not advance receipts.
|
|
64
|
+
A full snapshot is for explicit workspace forensics.
|
|
65
|
+
|
|
66
|
+
## Capability floor
|
|
67
|
+
|
|
68
|
+
The generic MCP capability declaration is all false:
|
|
69
|
+
|
|
70
|
+
| Group | Effective behavior |
|
|
71
|
+
|---|---|
|
|
72
|
+
| lifecycle | no automatic session-start, resume, or end signal |
|
|
73
|
+
| context | no startup, before-turn, or safe-point injection |
|
|
74
|
+
| guards | no before-read, before-write, or before-shell interception |
|
|
75
|
+
| delivery | no `nextTurn`, `livePush`, or native `replyRoute` |
|
|
76
|
+
|
|
77
|
+
An MCP participant therefore reports `advisory` enforcement and `manual` lifecycle.
|
|
78
|
+
`manual` describes ACC presence reporting, not ownership of the external client. Because
|
|
79
|
+
workspace protection is the weakest live participant's real guarantee, one MCP session
|
|
80
|
+
makes guarded claims advisory for the room.
|
|
81
|
+
|
|
82
|
+
## Durable polling semantics
|
|
83
|
+
|
|
84
|
+
Every outgoing message commits first. `acc_message`, `acc_request`, `acc_reply`, and
|
|
85
|
+
`acc_finish` cannot promise push; delivery results remain queued with a durable diagnostic.
|
|
86
|
+
The recipient calls `acc_inbox` to retrieve the body. Being returned by a tool is
|
|
87
|
+
`retrieved`, not proof that a model attended to or obeyed it. A reply or explicit ack is
|
|
88
|
+
`acknowledged`.
|
|
89
|
+
|
|
90
|
+
MCP is therefore a complete communication participant with higher latency, not a fake
|
|
91
|
+
native adapter. Use it when a client can call tools but exposes no measured hook boundary.
|
|
92
|
+
|
|
93
|
+
Next: [Protocol](PROTOCOL.md) · [Capabilities](CAPABILITIES.md) ·
|
|
94
|
+
[Security model](SECURITY_MODEL.md)
|
package/docs/PROTOCOL.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Protocol
|
|
2
|
+
|
|
3
|
+
The protocol is the vendor-neutral contract shared by core, CLI, MCP, storage, and every
|
|
4
|
+
adapter. Version 0.2 uses store schema version `3` and deliberately rejects v0.1 state.
|
|
5
|
+
There is no compatibility reader, conversion, archive path, or automatic deletion.
|
|
6
|
+
|
|
7
|
+
## Durable records
|
|
8
|
+
|
|
9
|
+
The store accepts only these durable kinds:
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
workspace · participant · session · intent · claim · message · receipt · event
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`deliveryBinding` is validated but ephemeral. It is tied to one open session generation
|
|
16
|
+
and never written inside a repository.
|
|
17
|
+
|
|
18
|
+
## Identity
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
Workspace
|
|
22
|
+
└── Participant
|
|
23
|
+
└── Session generation
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- A workspace is one local coordination room.
|
|
27
|
+
- A participant is the stable recipient of a message.
|
|
28
|
+
- A session is one client conversation. Its unguessable generation token proves that a
|
|
29
|
+
later mutation still belongs to the current opening.
|
|
30
|
+
|
|
31
|
+
Sessions carry harness, checkout, branch, optional pid, enforcement, and lifecycle facts.
|
|
32
|
+
The `managed` lifecycle value means ACC hooks can report presence changes; it does not mean
|
|
33
|
+
ACC owns or controls the external client.
|
|
34
|
+
|
|
35
|
+
## Intent and claims
|
|
36
|
+
|
|
37
|
+
Intent contains `summary`, `mode`, `resourceHints`, `state`, and `updatedAt`. It is
|
|
38
|
+
awareness only.
|
|
39
|
+
|
|
40
|
+
Claims contain the owner session and generation, canonical resource URI, shared or
|
|
41
|
+
exclusive mode, advisory or guarded enforcement, reason, and lease timestamps. Claim
|
|
42
|
+
acquisition and conflict detection are atomic. Presence becoming stale never releases a
|
|
43
|
+
claim; expiry or an explicit release does.
|
|
44
|
+
|
|
45
|
+
## Message envelope
|
|
46
|
+
|
|
47
|
+
Every message contains:
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
messageId
|
|
51
|
+
threadId
|
|
52
|
+
clientMessageId
|
|
53
|
+
workspaceId
|
|
54
|
+
fromParticipantId
|
|
55
|
+
fromSessionId
|
|
56
|
+
toParticipantIds
|
|
57
|
+
kind
|
|
58
|
+
obligation
|
|
59
|
+
subject
|
|
60
|
+
body
|
|
61
|
+
inReplyTo
|
|
62
|
+
artifacts
|
|
63
|
+
handoff
|
|
64
|
+
sentAt
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`clientMessageId` is an idempotency key scoped to workspace plus sender participant. A
|
|
68
|
+
retry with the same logical content returns the original message. Reusing the key with
|
|
69
|
+
different content is a data error. CLI and MCP generate a key when omitted and return it
|
|
70
|
+
inside the message so an uncertain caller can retry explicitly.
|
|
71
|
+
|
|
72
|
+
An empty `toParticipantIds` creates a room record. At commit time, core resolves every
|
|
73
|
+
known peer participant with an open session and creates a receipt for each. Participants
|
|
74
|
+
that arrive later can inspect room history through a full sync but do not receive
|
|
75
|
+
retroactive receipts. Those already-present recipients get the normal inbox and certified
|
|
76
|
+
next-turn path; a successful next-turn write advances their room receipt to `offered`.
|
|
77
|
+
Room records are never eligible for native live push.
|
|
78
|
+
|
|
79
|
+
## Kinds and obligations
|
|
80
|
+
|
|
81
|
+
| Kind | Valid obligation | Addressing |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `note` | `none` | addressed or room |
|
|
84
|
+
| `question` | `reply` | addressed only |
|
|
85
|
+
| `request` | `reply` | addressed only |
|
|
86
|
+
| `answer` | `none` | addressed reply only |
|
|
87
|
+
| `decision` | `none`, or `acknowledge` | addressed or room; room must use `none` |
|
|
88
|
+
| `handoff` | `acknowledge`, or `none` | addressed must acknowledge; room must use `none` |
|
|
89
|
+
|
|
90
|
+
The generic `message` boundary accepts only `note`, `question`, `request`, and `decision`.
|
|
91
|
+
An `answer` must be made through `reply`, which supplies the thread link. A `handoff` must
|
|
92
|
+
be made through `finish`, which supplies the structured payload.
|
|
93
|
+
|
|
94
|
+
A request has no accepted, running, or done state. The reply resolves its communication
|
|
95
|
+
obligation; execution evidence belongs in the answer or handoff.
|
|
96
|
+
|
|
97
|
+
## Threads
|
|
98
|
+
|
|
99
|
+
The root message uses its own id as the thread id:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
threadId = messageId
|
|
103
|
+
inReplyTo = null
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
An answer carries the same `threadId` and the original message id in `inReplyTo`. There is
|
|
107
|
+
no mutable thread record or hidden thread status.
|
|
108
|
+
|
|
109
|
+
## Receipt lifecycle
|
|
110
|
+
|
|
111
|
+
Every resolved recipient gets an independent receipt:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
queued -> offered -> retrieved -> acknowledged
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`recorded` is the send boundary's success result: the message is durable. It is not a
|
|
118
|
+
receipt state. `queued` is the distinct per-recipient fact created in the same transaction,
|
|
119
|
+
so one recorded room or multi-recipient message can have zero or several queued receipts.
|
|
120
|
+
|
|
121
|
+
- `queued` proves the durable message and receipt committed.
|
|
122
|
+
- `offered` proves bytes crossed ACC's transport boundary or the target client accepted a
|
|
123
|
+
certified native call.
|
|
124
|
+
- `retrieved` proves the participant explicitly received the body through inbox or an
|
|
125
|
+
equally strong certified adapter signal.
|
|
126
|
+
- `acknowledged` proves that participant acknowledged or replied.
|
|
127
|
+
|
|
128
|
+
Offered is not read. Retrieved is not model attention. Reply is not task completion.
|
|
129
|
+
|
|
130
|
+
Forward skips are allowed when the stronger observation implies the weaker ones. Repeating
|
|
131
|
+
a state is idempotent; moving backward is rejected. There is no `seen` state because ACC
|
|
132
|
+
cannot inspect model attention, and no terminal delivery `failed` state because the
|
|
133
|
+
durable path remains available.
|
|
134
|
+
|
|
135
|
+
## Offer attempts
|
|
136
|
+
|
|
137
|
+
Delivery attempts are immutable events, not receipt states:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
message.offer_succeeded
|
|
141
|
+
message.offer_failed
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Every attempt identifies `messageId`, `recipientParticipantId`, `targetSessionId`, and
|
|
145
|
+
`targetGeneration`, followed by the transport name, adapter, client version, and timestamp.
|
|
146
|
+
A failed attempt additionally carries a safe closed error code. Core validates the target
|
|
147
|
+
as that recipient's recorded session generation and derives event attribution from it; the
|
|
148
|
+
router cannot substitute the sender or omit the selected binding. Attempts never copy the
|
|
149
|
+
peer body into diagnostics.
|
|
150
|
+
Receipt `offered` is committed only after the transport accepts bytes. A failed attempt
|
|
151
|
+
leaves the receipt queued.
|
|
152
|
+
|
|
153
|
+
## Inbox, reply, and acknowledgement
|
|
154
|
+
|
|
155
|
+
`inbox` returns unresolved messages owned by the calling participant and advances only
|
|
156
|
+
that participant's receipt to `retrieved`. An exact message id is the recovery path after
|
|
157
|
+
compaction or an over-budget projection.
|
|
158
|
+
|
|
159
|
+
`reply` verifies that ownership, records an `answer` in the original thread, and advances
|
|
160
|
+
the original receipt to `acknowledged` in one transaction. Only after that durable commit
|
|
161
|
+
may the answer be offered to the original author. A transport error cannot roll back it.
|
|
162
|
+
|
|
163
|
+
`ack` advances the caller's receipt without creating a reply. It exposes no state override;
|
|
164
|
+
callers cannot claim that a transport offered or a participant retrieved a message.
|
|
165
|
+
|
|
166
|
+
## Handoff
|
|
167
|
+
|
|
168
|
+
`finish` creates a `handoff` with structured `status`, `completed`, `remaining`,
|
|
169
|
+
`blockers`, and `verification`, releases the sender session's claims, and ends its ACC
|
|
170
|
+
presence. An addressed handoff requires acknowledgement. A room handoff does not. Neither
|
|
171
|
+
form closes or otherwise controls the external AI client.
|
|
172
|
+
|
|
173
|
+
## Delivery binding and recipient policy
|
|
174
|
+
|
|
175
|
+
A live-capable adapter may publish one ephemeral binding for its exact session generation:
|
|
176
|
+
|
|
177
|
+
```text
|
|
178
|
+
sessionId · generation · adapterId · clientVersion · availableModes
|
|
179
|
+
livePolicy · opaqueEndpointRef · leaseUntil
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The recipient owns `livePolicy` because native push may start a model turn:
|
|
183
|
+
|
|
184
|
+
- `off`: inbox and normal next-turn paths only;
|
|
185
|
+
- `actionable`: questions, requests, answers, decisions, and addressed handoffs may use live push;
|
|
186
|
+
- `all`: every addressed kind may use live push.
|
|
187
|
+
|
|
188
|
+
Default is `off`. Policy never creates a capability. The router still requires a current
|
|
189
|
+
reachable binding, one unambiguous recipient generation, a passing exact-version
|
|
190
|
+
certification, and adapter acceptance. The current Codex and Claude captures do not meet
|
|
191
|
+
those requirements; all shipped native live routes therefore fall back durably.
|
|
192
|
+
|
|
193
|
+
## Attention and sync
|
|
194
|
+
|
|
195
|
+
Bounded sync returns events after a cursor plus explicit attention. Full sync is a
|
|
196
|
+
forensic workspace snapshot, not the normal way to recover one message. Attention is
|
|
197
|
+
limited to six explicit rules: `reply_required`, `acknowledgement_required`,
|
|
198
|
+
`recipient_unavailable`, `claim_conflict`, `claim_contended`, and `claim_expired`.
|
|
199
|
+
|
|
200
|
+
Next: [CLI](CLI.md) · [MCP](MCP.md) · [Architecture](ARCHITECTURE.md)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
```mermaid
|
|
4
|
+
graph LR
|
|
5
|
+
A[npm ci] --> B[npm run check] --> C[npm test] --> D[clean candidate commit] --> E[npm pack] --> F[verify-package] --> G{approved?}
|
|
6
|
+
G -->|yes| H[tag · publish · release]
|
|
7
|
+
G -->|no| I[stop]
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
## Build the candidate
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm ci
|
|
14
|
+
npm run check
|
|
15
|
+
env npm_config_cache=/private/tmp/acc-npm-cache-v02 npm test
|
|
16
|
+
git status --short
|
|
17
|
+
git commit -m "release: prepare ACC v0.2.0"
|
|
18
|
+
candidate_dir="$(mktemp -d /private/tmp/acc-v0.2.XXXXXX)"
|
|
19
|
+
env npm_config_cache=/private/tmp/acc-npm-cache-v02 npm pack --pack-destination "$candidate_dir"
|
|
20
|
+
env npm_config_cache=/private/tmp/acc-npm-cache-v02 node scripts/verify-package.mjs \
|
|
21
|
+
"$candidate_dir/agents-can-communicate-0.2.0.tgz"
|
|
22
|
+
git diff --check
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`verify-package.mjs` is the gate that matters. It installs the tarball into a
|
|
26
|
+
clean directory with **no workspace anywhere** and then drives the product. The
|
|
27
|
+
acceptance suite additionally opens independent Claude/Codex hook sessions from
|
|
28
|
+
the installed artifact, completes both message directions, proves downgrade and
|
|
29
|
+
restart behavior, exercises the installed MCP binary, and removes client wiring twice.
|
|
30
|
+
|
|
31
|
+
Development cannot catch what this catches. Workspace symlinks are always
|
|
32
|
+
present there, so an unbundled package imports fine right up until somebody
|
|
33
|
+
else installs it.
|
|
34
|
+
|
|
35
|
+
## What it refuses
|
|
36
|
+
|
|
37
|
+
| Refused | Why |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `tests/`, `test/` | test suite |
|
|
40
|
+
| unreferenced `fixtures/` | material that is not exact redacted certification evidence |
|
|
41
|
+
| `scripts/spikes/`, `*.sock` | development probes and local endpoints |
|
|
42
|
+
| runtime, transcript, or secret directories | machine state and private material |
|
|
43
|
+
| `.github/`, `.githooks/`, `.agents/` | local configuration |
|
|
44
|
+
| `*.jsonl` | looks like a transcript |
|
|
45
|
+
| Workspaces missing from `node_modules/` | every internal import would fail |
|
|
46
|
+
|
|
47
|
+
## Record the evidence
|
|
48
|
+
|
|
49
|
+
Put the tarball name, sha256, **the commit it was built from**, exact platform/client
|
|
50
|
+
facts, fallback result, and known limitations in `docs/release-evidence/v0.3.0.md` and
|
|
51
|
+
the current `CHANGELOG.md` release table. A release without them is a release nobody can
|
|
52
|
+
audit later. Test count is deliberately left out: nothing verifies it, so it only
|
|
53
|
+
decorates or, when it drifts, misleads.
|
|
54
|
+
|
|
55
|
+
The commit matters because every workspace travels inside the tarball, so any
|
|
56
|
+
change to shipped code changes the digest. A digest recorded alone goes stale
|
|
57
|
+
on the next merge and then reads as a false claim about the current tree
|
|
58
|
+
rather than a true one about an older commit. `verify-package.mjs` prints
|
|
59
|
+
both on one line for exactly this reason, and refuses to imply
|
|
60
|
+
reproducibility when the working tree is dirty:
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
PASS agents-can-communicate-0.0.0.tgz sha256 a5c8bb1d… built from 39d0dcf
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
It went stale four times in a row anyway, each caught by hand and only
|
|
67
|
+
because someone happened to run the script. So `npm test` now checks it:
|
|
68
|
+
`tests/acceptance/recorded-candidate.test.mjs` fails when shipped code has
|
|
69
|
+
changed since the recorded commit, and names the files. A shallow clone that
|
|
70
|
+
does not have the commit reports that instead of failing — the check is of
|
|
71
|
+
the record, not of the checkout depth.
|
|
72
|
+
|
|
73
|
+
The candidate therefore uses two commits. Commit A contains every packed file and every
|
|
74
|
+
release gate. Pack and verify clean commit A, then commit B records its digest and evidence
|
|
75
|
+
only in files that are not packed. Never record a dirty-tree digest: changing a packed file
|
|
76
|
+
after commit A means making a new candidate commit and packing again.
|
|
77
|
+
|
|
78
|
+
Native real-client tests run only where the retained capture proved that native boundary.
|
|
79
|
+
For v0.2.0 neither Codex 0.152.0 nor Claude Code 2.1.252 did: the Codex control socket was
|
|
80
|
+
absent and Claude stopped at the development-channel warning. Record those exact failed
|
|
81
|
+
captures and run the packed inbox/next-turn fallback instead of promoting an unobserved
|
|
82
|
+
native path. Windows is an explicit unsupported-platform skip, not a passing capture.
|
|
83
|
+
|
|
84
|
+
By v0.3.0 that had gone both ways, which is the point of doing it per release rather than
|
|
85
|
+
once. Claude Code passed and ships a live Channel; Codex's queue capture passed and the
|
|
86
|
+
capability was **withdrawn anyway**, because the mode it needs hides which workspace the
|
|
87
|
+
session belongs to, and a session that cannot be placed must not be addressed. A capture
|
|
88
|
+
that works is not the same claim as a capability that is safe to ship.
|
|
89
|
+
|
|
90
|
+
A published version's record is history and is not rewritten; later changes
|
|
91
|
+
get a new `## Unreleased` entry at the top, checked the same way against the
|
|
92
|
+
current tree.
|
|
93
|
+
|
|
94
|
+
## Then stop
|
|
95
|
+
|
|
96
|
+
Publishing, tagging, and cutting a GitHub release are **external mutations**.
|
|
97
|
+
They need explicit approval from the maintainer, every time.
|
|
98
|
+
|
|
99
|
+
With two-factor authentication set to `auth-and-writes` — which is what `npm
|
|
100
|
+
profile get` reports for this account — every publish needs a code, and npm
|
|
101
|
+
does not prompt for one:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm publish --otp=123456
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
An npm *Automation* token, or a granular token with write access, publishes
|
|
108
|
+
without a code. A granular token scoped to selected packages cannot create a
|
|
109
|
+
package that does not exist yet, which is the case exactly once per package.
|
|
110
|
+
The `Release` workflow builds and verifies a candidate and uploads it as an
|
|
111
|
+
artifact; it does not publish.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
See also: [README](index.md) for navigation and [Glossary](GLOSSARY.md) for terms.
|