@ssheleg/agent-sync 1.2.2

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/CHANGELOG.md ADDED
@@ -0,0 +1,332 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+ This project adheres to [Semantic Versioning](https://semver.org/).
5
+
6
+ ## 1.2.2 — 2026-07-29
7
+
8
+ ### Changed
9
+ - **The npm package is `@ssheleg/agent-sync`.** Unscoped `agent-sync` was rejected on publish
10
+ with a 403: npm's name-similarity policy fires only on `PUT`, so `npm view` reporting E404
11
+ ("free") predicts nothing — the collision was with an existing `agentsync`. Scoped names are
12
+ exempt from that policy, which is the documented fix.
13
+ - **The command is still `agent-sync`.** A package's `bin` name is independent of its package
14
+ name, so nothing about daily use changes; only the install line grows a scope.
15
+ - GitHub install (`npx github:appvillis-com/agent-sync`) and the Claude Code plugin are
16
+ unaffected — the registry only ever bought the short name.
17
+
18
+ ## 1.2.1 — 2026-07-29
19
+
20
+ ### Fixed — documentation that contradicted the code
21
+ Compressing the skill surfaced three statements that measurement had already disproved and
22
+ that nobody had gone back to correct. This is the drift the tool exists to catch, in the
23
+ tool's own documentation.
24
+
25
+ - `lease-protocol.md` opened by declaring that *no backend offers compare-and-swap, so both
26
+ leases and id reservations are decided by replaying one append-only log*. Half of that is
27
+ still true — id allocation is positional over the log — and half has been false since
28
+ 1.0.0. It now separates the two mechanisms, because confusing them is how this went wrong
29
+ twice.
30
+ - The same file still said the **run** writes the claim tag and the tool only verifies. As
31
+ of 1.2.0 the tool writes it through.
32
+ - `backend-fs.md` described a "git-file lease" — commit the lock, push it, read the
33
+ rejection — a design that was never built. Leases have never depended on which knowledge
34
+ backend is configured, and the file now says so.
35
+
36
+ ### Changed
37
+ - `SKILL.md` trimmed from 4779 to 4325 tokens (13% headroom under the 5000 cap), with the
38
+ full measurement history left in `CHANGELOG.md` and `lease-protocol.md` where it belongs.
39
+ - `lease-protocol.md` gains the cross-machine section it was missing.
40
+
41
+ ## 1.2.0 — 2026-07-29
42
+
43
+ ### Added — the claim is written through to the roadmap again
44
+ Demoted to a check in 1.0.0 because an unattended process rewriting a shared registry is
45
+ the collision a lease exists to prevent. It is back, with that objection engineered out:
46
+
47
+ - **One row.** The single table row containing the task id as a whole word. Zero rows →
48
+ nothing happens. Two or more → **refused**, with the reason. It never guesses.
49
+ - **One cell.** Only the configured cell changes; links, notes and every other column are
50
+ untouched byte for byte. `cell` is 0-based, negative counts from the end.
51
+ - **Reversible.** The previous text is stored in `.agent-sync/claims.json` and restored
52
+ verbatim on release — not a default, what was actually there. After acquire→release,
53
+ `git diff` on the roadmap is **empty**.
54
+ - **Atomic.** Written to a temp file and moved into place, so a crash cannot leave the
55
+ register half-edited.
56
+
57
+ Closing a task is still yours: the tool refuses to write `done` on your behalf, because a
58
+ status a machine sets is a status nobody checked. `references/roadmap.md` documents the
59
+ whole cycle — claiming, closing, re-planning, and what to do when the claim cannot be
60
+ written.
61
+
62
+ ### Added — cross-machine leases
63
+ `leaseBackend: "git"` pushes a commit to `refs/agent-sync/leases/<key>`, and the remote's
64
+ non-fast-forward rejection **is** a compare-and-swap. Verified against a hosted remote
65
+ before being written: A created the ref, B pushed a different commit without force and was
66
+ rejected, the ref still held A. Then eight parallel processes against the real remote —
67
+ **one winner, seven losers all naming it**.
68
+
69
+ Expired leases are stolen with `--force-with-lease` against the exact object seen, so a
70
+ steal cannot clobber a holder who renewed in between. `local` remains the default and is
71
+ still exclusive between processes on one filesystem; `acquire` and `check` now say which
72
+ guarantee you actually have instead of implying the stronger one.
73
+
74
+ ## 1.1.0 — 2026-07-29
75
+
76
+ The skill can now take **any** project from nothing to a validated setup on its own.
77
+
78
+ ### Added
79
+ - **`check`** — validates the whole setup and refuses to call a broken one healthy. It fails
80
+ on a register whose allocation pattern matches nothing, a guard glob that matches no file
81
+ (a rule protecting nothing), a gate whose script is missing, a mirror source that is not
82
+ there, empty credentials, a `.gitignore` that misses the env file — or that file being
83
+ **tracked by git**, the one unrecoverable mistake here — a hand-edited or stale snapshot,
84
+ **a snapshot no agent instruction file links**, and a register with no baseline. Every one
85
+ of those failed for real during this tool's own adoption.
86
+ - **`scaffold`** — creates the documentation architecture where it is absent: a decision
87
+ register with an allocation line and an `AGENTS.md` pointing at the generated snapshot. It
88
+ never touches an existing file. A tool that rewrites a project's conventions on adoption is
89
+ worse than one that does nothing.
90
+ - The snapshot is stamped with a **hash of the configuration** it describes, so staleness
91
+ means the configuration moved on — not that a commit happened. Comparing commits was wrong
92
+ at both boundaries: a snapshot is generated before the commit that carries it, and the
93
+ config is usually added in that same commit, so the very first adoption always looked stale.
94
+
95
+ ## 1.0.1 — 2026-07-29
96
+
97
+ ### Fixed
98
+ - **A rate-limited knowledge base stopped the work, not just the record.** `journal`, `record`
99
+ and `signal` raised when the store was unreachable or throttling, so a burst of shard creation
100
+ could fail a run outright. The plane carries visibility, not correctness: publishing now
101
+ reports the gap loudly and lets the caller continue. Swallowing it would hide a hole in the
102
+ record; raising made an availability dependency out of a notebook.
103
+ - Retries widened to seven attempts for `429` and transient `5xx`, which is what a burst of
104
+ document creation actually needs.
105
+ - Run journals moved to the shard naming scheme (`20 Runs — <run>`) so they are enumerated like
106
+ every other log.
107
+
108
+ ## 1.0.0 — 2026-07-29
109
+
110
+ A full audit of the running system against its own promises. Three surfaces were
111
+ configured and unimplemented, and the central safety claim was false. All measured, none
112
+ inferred.
113
+
114
+ ### Fixed — the lease was not exclusive
115
+ - **A shared append-only document loses writes.** Twelve concurrent appends to one Outline
116
+ document returned twelve successes and left **three** lines: `editMode: append` reads,
117
+ appends and writes back, so simultaneous writers clobber each other and each is told it
118
+ succeeded. A lease decided on that can be held by two runs, each with proof.
119
+ - **Sharding per writer fixes the loss and breaks the decision.** 12/12 land, but without
120
+ compare-and-swap nothing can answer "is a contender still writing?", so eight parallel
121
+ processes each read only their own shard and **eight won one key**. A three-second settle
122
+ window took it to five. It cannot reach one.
123
+ - **Exclusion now comes from `os.open(O_EXCL)`** — an atomic create is the decision, and the
124
+ plane carries the record. Twelve parallel processes, **one winner, eleven losers all naming
125
+ the same holder**. Publishing to the plane can fail without affecting correctness, so it is
126
+ reported rather than raised.
127
+ - The limit is stated instead of implied: a lock file is exclusive between processes on one
128
+ filesystem, advisory across machines. `exclusiveLease` joins the capability set and defaults
129
+ to false, because declaring it without compare-and-swap is the most damaging lie an adapter
130
+ can tell.
131
+
132
+ ### Fixed — configured but not implemented
133
+ - **`claimTags`** appeared in the schema, in every config and in DEC-0216, and was read
134
+ nowhere. `status` now reports where a held lease and the git claim tag disagree — and says
135
+ plainly when the configured mapping *cannot be verified at all*, which is the case in the
136
+ project that shipped it. The tool verifies; the run writes. A process that rewrites a shared
137
+ registry unattended from a hook is the mechanism that clobbers other agents' work.
138
+ - **Mirror drift detection** was asserted in a docstring beside code that never checked it.
139
+ `status` now reports pages whose stamped commit is not HEAD.
140
+ - Transient `5xx` from the knowledge base are retried like `429`; twelve concurrent document
141
+ creations had been failing outright.
142
+
143
+ ## 0.6.0 — 2026-07-29
144
+
145
+ ### Fixed
146
+ - **The mirror was configured, documented and not implemented.** `mirror.enabled` and
147
+ `mirror.sources` existed in the schema, the config and the generated setup snapshot, and
148
+ nothing read them — a surface with nothing behind it, which is the failure mode that reads as
149
+ finished. `board --mirror` now renders each configured document into the plane, stamped with
150
+ the commit it was made from, refusing any page whose generated marker a human removed. A cap
151
+ on the number of files is reported rather than applied silently: a quiet truncation reads as
152
+ "everything is mirrored" when it is not.
153
+
154
+ ## 0.5.0 — 2026-07-29
155
+
156
+ ### Added
157
+ - **`adopt`** — inspect an existing project and *propose* a configuration. Adoption is where a
158
+ coordination tool most easily starts lying: guess a register wrong and every later check is
159
+ confidently about the wrong file. So it reads the repository, prints what it found, prints the
160
+ decisions it **refuses to make for you** (a registry file carrying ids with no "next free id"
161
+ line cannot have allocation reserved safely), and writes nothing. In a submodule it proposes no
162
+ registers at all, because decisions belong to the parent repository.
163
+
164
+ ## 0.4.0 — 2026-07-29
165
+
166
+ Found by simulating three agents working three repositories at once, entered from one
167
+ umbrella — the arrangement this tool is for. Every defect below was invisible from inside
168
+ a single checkout.
169
+
170
+ ### Fixed
171
+ - **Every submodule agent ran isolated, in degraded mode, seeing nobody.** A submodule is
172
+ its own git repository, so the project root is the submodule and `.env.agent-sync` — which
173
+ lives in the superproject — was never found. Three agents entered from one umbrella and
174
+ coordinated with nothing, each reporting `ungated` while believing it was configured. The
175
+ env file is now located from the superproject and parent directories, so one credential
176
+ file serves the whole tree.
177
+ - **A submodule's `reconcile` reported every umbrella decision as an orphan.** The as-built
178
+ log is shared by all repositories; id registers are per-repository, and a service repo
179
+ declares none because decisions live in the parent. Comparing the shared log against a
180
+ local register produced a wall of false findings — the loudest possible way to teach
181
+ people to ignore a check. Register checks are now scoped to what the checkout can judge,
182
+ and say plainly when they are not evaluated here.
183
+ - **Regenerating the board from a submodule replaced the shared view with a narrower one.**
184
+ Four repositories wrote one page; last writer won. The board now carries only facts true
185
+ from every checkout, and repo-local findings moved to their own generated page.
186
+
187
+ ### Added
188
+ - **`setup`** writes a generated snapshot of how *this* project is wired — registers,
189
+ guarded files, gates, the two documentation sources, what is written where, and what is
190
+ never deleted. Commit it and link it from the project's agent instructions so every agent
191
+ reads the same description of the pipeline instead of inferring it from behaviour. It is
192
+ generated rather than hand-written, because a hand-written description of a configuration
193
+ drifts from it, which is the exact failure this tool exists to surface.
194
+ - **The lifetime and deletion protocol is now stated.** Nothing in a log is edited or
195
+ deleted: the logs are replayed in order, so removing a line silently rewrites a
196
+ conclusion other agents already acted on. Correct by appending. Generated pages are the
197
+ narrow exception, and one that has lost its marker is refused, not overwritten.
198
+
199
+ ## 0.3.3 — 2026-07-29
200
+
201
+ ### Fixed
202
+ - **The enforcement hook ran in a different mode from the agent it was guarding.** A hook is
203
+ spawned with a bare environment and never inherits the operator's
204
+ `set -a && . ./.env.agent-sync`, so every hook silently fell back to the `fs` backend while
205
+ the agent's own commands used the cloud. Consequences, both invisible: the guard **denied
206
+ edits whose lease was properly held**, and it recorded runs as `ungated` while the agent had
207
+ been told `gated`. The gate was structurally broken in exactly the scenario it exists for.
208
+ The tool now loads `.env.agent-sync` itself — the path is deterministic, so correctness must
209
+ not depend on how the process was invoked. An already-set variable still wins.
210
+
211
+ ## 0.3.2 — 2026-07-29
212
+
213
+ ### Fixed
214
+ - **`reconcile` demanded an as-built record for an id nobody had taken.** The id scraper
215
+ matched every `DEC-\d+` token in a register, including the "Next free ID" pointer — the one
216
+ number that by definition is *not* allocated. Two symptoms, one cause: a permanent false
217
+ finding on the unallocated id, and a baseline stamped one higher than reality, which quietly
218
+ excused the newest real decision from ever being checked. The register's own
219
+ `nextFreeIdPattern` now identifies that pointer and subtracts it.
220
+ Found by running the new duty against this project rather than by reading the code.
221
+
222
+ ## 0.3.1 — 2026-07-29
223
+
224
+ ### Fixed
225
+ - **The guard blocked the lease holder.** A hook runs with `CLAUDE_SESSION_ID` in its
226
+ environment and a plain shell command usually does not, so the run id was derived two
227
+ different ways for one session: the agent acquired a lease as `r-f49d900b9` and was then
228
+ denied by its own `PreToolUse` guard as `r-5ef2554fe611`. The primary flow — acquire,
229
+ then edit a guarded register — could not complete. Found when the gate refused the very
230
+ commit that was writing its decision record.
231
+ The marker file is now authoritative for the checkout, with the session name recorded
232
+ beside it: a different session rotates the id, while a run that merely *learns* its
233
+ session name adopts it instead of rotating.
234
+
235
+ ## 0.3.0 — 2026-07-29
236
+
237
+ ### Fixed
238
+ - **Three of the four hooks were dead on macOS.** They called `timeout`, which is GNU
239
+ coreutils and absent from a stock macOS, so `session-start`, `renew` and `session-end`
240
+ all died with "command not found" — leases were never renewed and never released
241
+ there, the exact abandoned-lease failure this tool exists to prevent. A portable
242
+ `run_limited` helper now uses `timeout`, then `gtimeout`, then a plain-bash watchdog.
243
+ `guard` was unaffected, so enforcement itself never lapsed.
244
+
245
+ ### Added
246
+ - **The as-built record, and the duty to reconcile it against git.** Git documents say
247
+ how it *should* be — written before the code, often without it. `70 As-built` says how
248
+ it *actually is*, derived from what agents really wrote. Two source-of-truths answering
249
+ two different questions; the gap between them is the finding, not a defect. New
250
+ `record` and `reconcile` commands, wired into the pipeline's docs-study stage (resolve
251
+ divergence before writing code) and docs stage (update both sides, then re-check).
252
+ - **`reconcile` is a ratchet, not a flood.** `--set-baseline` stamps today's ids as a
253
+ counted backlog that may only shrink; ids written after it must carry an as-built
254
+ record. A check that fails on all of history is a check that gets switched off.
255
+ - **Awareness names the repository.** Work spans several repos entered from one umbrella,
256
+ and "r-alpha holds ASC-072" is only actionable once you know which checkout it is in.
257
+ - **`npx agent-sync update`** — updates every channel and prunes the shadow copy in the
258
+ same step, because `npx skills update --global` recreates it on its own even when
259
+ claude-code was never targeted.
260
+ - **`install.sh`** POSIX fallback and a **Cursor rule** (`cursor/rules/agent-sync.mdc`,
261
+ no relative links, since the file gets copied into foreign projects).
262
+
263
+ ## 0.2.0 — 2026-07-29
264
+
265
+ Coordination is not only mutual exclusion. An audit against the stated purpose — *agents
266
+ see what each other are doing and pick up important changes in time* — found the tool
267
+ enforced exclusion and delivered neither half of the awareness.
268
+
269
+ ### Fixed
270
+ - **`status` shows what other runs are doing.** It reported only the caller's own leases,
271
+ so an agent learned a task was taken and nothing about who held it or what they were
272
+ touching. A lease you cannot see makes you blocked; a lease you can see makes you
273
+ coordinated.
274
+ - **Cross-repo signals were write-only.** `signal` appended and nothing ever read the log,
275
+ so a producer was still never told a dependency had been filed against them — the exact
276
+ failure the feature exists to prevent. `status` now surfaces what landed since this run
277
+ last looked, watermarked per run so it stays quiet until something actually changes.
278
+ - **The board renders recent signals** alongside live leases.
279
+
280
+ ### Notes
281
+ - Verified with three concurrent runs across two repositories: an agent working inside a
282
+ submodule alone sees the leases and signals of agents in the parent repository, because
283
+ both read one coordination plane.
284
+
285
+ ## 0.1.0 — 2026-07-29
286
+
287
+ First release.
288
+
289
+ ### Added
290
+ - **Lease authority with TTL** — `acquire` / `renew` / `release`, decided by replaying
291
+ one append-only log. Document order is authoritative; timestamps only expire a lease,
292
+ because agents' clocks differ and the protocol must not depend on them.
293
+ - **Race-free id reservation** — positional allocation over the same log, so two agents
294
+ cannot be handed one number. An id reserved and never written to git is reported as a
295
+ leak rather than silently reclaimed.
296
+ - **Pluggable adapter contract** — six primitives, three declared capabilities
297
+ (`atomicAppend`, `totalOrderRead`, `search`), and a mandatory honest-degradation path:
298
+ a backend that cannot arbitrate exclusively must refuse lease authority and say so.
299
+ - **Backends** — `outline` (hosted or self-hosted; server-side append gives a total order
300
+ without compare-and-swap) and `fs` (local, degraded, `ungated`).
301
+ - **Run journal and cross-repo signal feed** — `filed → accepted → delivered → closed`,
302
+ so a producer learns a dependency was filed against them.
303
+ - **Generated board** — commit-stamped, and it refuses to overwrite a page that lacks the
304
+ generated marker, so a page a human took over is reported instead of clobbered.
305
+ - **Claude Code hooks** — `SessionStart`, `PreToolUse` (deny a guarded edit or a commit
306
+ staging one without a live lease), throttled `PostToolUse` renew, `SessionEnd` release.
307
+ The guard exits 2 on its own internal failures, because any other code fails open.
308
+ - **`init` as the first command** — it asks where coordination state lives instead of
309
+ guessing, writes committed shape and a gitignored mode-600 env file, and leaves the
310
+ token line empty for the operator to fill.
311
+ - **Validator with a negative self-test**, plus CI.
312
+
313
+ ### Verified against a live instance
314
+ Built and then exercised end to end against a real Outline deployment, which surfaced three
315
+ defects the unit-level work had not:
316
+ - **Markdown normalisation.** The store rewrites a `- ` bullet to `* `, so the log parser now
317
+ emits `- ` and accepts `-`/`*`/`+`. Anchoring to the character written rejected every line
318
+ the server returned.
319
+ - **A silent pre-filter hid malformed lines from the counter meant to expose them**, so the
320
+ unparseable ratio read 0% while nothing parsed. Anything entry-shaped now reaches the pattern
321
+ and is counted.
322
+ - **An unreadable log reported as a lost race.** `acquire` now raises above a 2% unparseable
323
+ ratio rather than naming a holder who does not exist.
324
+ - HTTP error bodies are surfaced instead of dropped — a bare `400` cost a debugging round when
325
+ the response said `collectionId: Invalid UUID`.
326
+ - The collection may be given as a UUID, a `urlId`, or the whole `name-urlId` slug from the
327
+ address bar, because that is what a person actually copies.
328
+
329
+ ### Notes
330
+ - Hooks exist only in Claude Code. Elsewhere the same checks run as a self-check and the
331
+ run is recorded `ungated` — a documented limit, surfaced rather than hidden.
332
+ - Requires [task-pipeline](https://github.com/ssheleg/task-pipeline) for its stages.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Appvillis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.