@ssheleg/agent-sync 1.18.7 → 1.19.1
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
CHANGED
|
@@ -1,3 +1,88 @@
|
|
|
1
|
+
## v1.19.1 — re-acquiring your own lease now refreshes it
|
|
2
|
+
|
|
3
|
+
**`renew` was fixed for exactly this and `acquire` was not, in the same file.**
|
|
4
|
+
`_refresh_lease`'s docstring records the bug it exists to have fixed: *"`renew`
|
|
5
|
+
appended `op=renew` to the RECORD plane — which has not decided a lease since 1.0.0 —
|
|
6
|
+
and touched a throttle file. The lock's own `ts` was written once, by `acquire`. So a
|
|
7
|
+
run holding a lease lost it at TTL while still working."* The repair landed in `renew`.
|
|
8
|
+
The own-lock branch of `acquire` kept doing precisely what that paragraph describes.
|
|
9
|
+
|
|
10
|
+
**Two harms, and the second is the one that bites.** The lock stayed expired, so
|
|
11
|
+
`classify_lock` read it as spent and another run's `acquire` took it over while this one
|
|
12
|
+
believed it held — measured 2026-09-01: a foreign expired lock IS taken over, timestamp
|
|
13
|
+
and host rewritten. And `_touch_renew` resets the marker `renew()` throttles on, so a
|
|
14
|
+
re-acquire **actively suppressed the next real refresh** while refreshing nothing
|
|
15
|
+
itself. That is the mechanism behind a lease expiring three times in one run against a
|
|
16
|
+
450-step CI job.
|
|
17
|
+
|
|
18
|
+
Measured before and after, on a lock aged past its own TTL:
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
old: won mykey (…) ts after re-acquire: 2026-08-29T20:59:04Z still expired
|
|
22
|
+
new: won mykey (…) ts after re-acquire: 2026-09-01T12:38:48Z
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`check_acquire_refreshes_its_own_lease` is beside `check_renew_extends_the_lease`,
|
|
26
|
+
watched failing against the previous commit with both of its assertions.
|
|
27
|
+
|
|
28
|
+
**One finding retracted in the same run, and it belongs here.** `reap` refuses to clear
|
|
29
|
+
a lock three days past a forty-five-minute TTL when this run's identity is the shared
|
|
30
|
+
fallback, and that reads like a deadlock. It is not: `acquire` takes over an expired
|
|
31
|
+
lock regardless of who wrote it — measured, timestamp and host rewritten — so the
|
|
32
|
+
expired lock blocks nothing, and `reap`'s stricter bar for a *tidying* operation is
|
|
33
|
+
correct as designed. The claim that expiry alone should license a delete was wrong
|
|
34
|
+
about which operation matters.
|
|
35
|
+
|
|
36
|
+
## v1.19.0 — the skill now promises the finish it has always performed
|
|
37
|
+
|
|
38
|
+
Wave-4 close-out of the 2026-08-29 family audit (ASY-10), filed by this
|
|
39
|
+
repository's own first eval run. One front-matter line changes; no behaviour
|
|
40
|
+
does.
|
|
41
|
+
|
|
42
|
+
- **ASY-10 — the submodule-finish request reached nothing, and now reaches this
|
|
43
|
+
skill on every sample.** `finish` exists for exactly one silent failure — a
|
|
44
|
+
submodule pushed while the parent still points at the commit before it — and
|
|
45
|
+
the description advertised none of its words, so "Finish a submodule change so
|
|
46
|
+
the parent points at the pushed commit" (eval q06) routed past the skill on
|
|
47
|
+
every probe of the v1.18.7 run. The *Use when* clause now names it, in the
|
|
48
|
+
words `finish` itself prints (`finishing work across a parent repo and its
|
|
49
|
+
submodules - clean, pushed, and pointed at`), and the trigger list carries the
|
|
50
|
+
symptom in both languages: `'the submodule is pushed, the parent points at the
|
|
51
|
+
old commit' / 'сабмодуль запушен, родитель на старом коммите'`. The
|
|
52
|
+
description is **951 characters — against the 970 house working limit, not the
|
|
53
|
+
1024 spec cap**: a first draft at 1017 passed `test/validate.py` and was
|
|
54
|
+
refused by the family's pinned auditor (`GAP DESC_HEADROOM`, exit 1, a CI
|
|
55
|
+
failure), so it was rewritten rather than shipped. `yaml.safe_load` reads the
|
|
56
|
+
front matter — the two things this family has shipped broken before.
|
|
57
|
+
- **The fix is measured, not asserted, and both arms ran in this session.** Two
|
|
58
|
+
corpus files differing in exactly one line (`- agent-sync: …`, old vs new),
|
|
59
|
+
one enforced protocol, fresh context-free subagents: **q06 went 0/3 → 3/3 on
|
|
60
|
+
haiku and 0/3 → 3/3 on sonnet**, and every number in the results file comes
|
|
61
|
+
from the text that ships — the discarded 1017-char draft was re-measured from
|
|
62
|
+
scratch after the rewrite rather than having its numbers carried over. The
|
|
63
|
+
pilot probes are reported too, because
|
|
64
|
+
they changed the protocol: offered the corpus rather than required to read it,
|
|
65
|
+
all three sonnet probes answered with no tool call at all, from the machine's
|
|
66
|
+
own installed listing — so the read was made mandatory and provable, and both
|
|
67
|
+
arms re-run under it.
|
|
68
|
+
- **Every other query was re-probed, because a widened trigger steals a
|
|
69
|
+
neighbour's.** All twelve queries × two models: **12/12 on both, train 6/6 and
|
|
70
|
+
validation 6/6**, up from 11/12. Nothing moved except q06 — including q07
|
|
71
|
+
(solo git branching) and q08 (the full delivery cycle), the two negatives with
|
|
72
|
+
the most to lose from submodule and finish vocabulary. Tables, the 68-probe
|
|
73
|
+
accounting across three arms and the limits are in `test/evals/RESULTS.md`;
|
|
74
|
+
the v1.18.7 rows are left standing beside the new ones rather than rewritten.
|
|
75
|
+
- Ten routed triggers still advertised — the umbrella's own
|
|
76
|
+
`advertised_check.js` was run against this checkout (`ok: agent-sync
|
|
77
|
+
advertises all 10 routed trigger(s)`), not a copy of its table.
|
|
78
|
+
- **Filed, not fixed: AS-10.** `npm test` accepts a description up to 1024
|
|
79
|
+
characters and CI's pinned auditor refuses one past 970, so a description in
|
|
80
|
+
that band goes green locally and red on the runner. That is AS-06's shape a
|
|
81
|
+
second time — the same auditor, the other half of the same budget — and the
|
|
82
|
+
fix belongs in `test/validate.py` with a planted self-test, not inside a
|
|
83
|
+
release whose subject is one description line. The retro entry records what
|
|
84
|
+
the divergence cost here.
|
|
85
|
+
|
|
1
86
|
## v1.18.7 — the evals stop being a claim, and the manifests say what shapes them
|
|
2
87
|
|
|
3
88
|
Wave-3 close-out of the 2026-08-29 family audit (ASY-03, ASY-09). No behavior
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/agent-sync",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.19.1",
|
|
4
4
|
"description": "Let concurrent coding agents share one project without colliding — leases with TTL, race-free id reservation, a run journal and a generated board, over a pluggable knowledge cloud.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-sync": "bin/agent-sync.js"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
|
|
3
3
|
"name": "agent-sync",
|
|
4
4
|
"displayName": "Agent Sync",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.19.1",
|
|
6
6
|
"description": "Coordination layer for multi-agent repositories — leases with TTL, race-free ID reservation, a run journal, a cross-repo signal feed and a generated board, over a pluggable knowledge cloud.",
|
|
7
7
|
"author": {
|
|
8
8
|
"name": "ssheleg",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-sync
|
|
3
|
-
description: "Use when several coding agents work one repository at the same time and must not collide - claiming a task, reserving the next decision/question/ticket id, journaling a run,
|
|
3
|
+
description: "Use when several coding agents work one repository at the same time and must not collide - claiming a task, reserving the next decision/question/ticket id, journaling a run, moving a cross-repo dependency, regenerating the board, or finishing work across a parent repo and its submodules - clean, pushed, and pointed at. Triggers - 'claim this task' / 'возьми задачу', 'who is working on' / 'кто сейчас делает', 'reserve an id' / 'зарезервируй id', 'sync the board' / 'обнови доску', 'set up agent coordination' / 'настрой координацию агентов', 'the submodule is pushed, the parent points at the old commit' / 'сабмодуль запушен, родитель на старом коммите', /agent-sync. Use it BEFORE editing any shared registry file (decisions, open questions, roadmap, workstreams, dependencies) in a project that has .claude/agent-sync.json, even when the user never mentions coordination - an unclaimed edit to those files is how two agents overwrite each other."
|
|
4
4
|
compatibility: "Requires the task-pipeline skill for its stages (npx sshlg-skills install). Needs python3 3.9+ (stdlib only, HTTP included - nothing to pip install) and bash for the hooks. The knowledge backend is configured per project; with none configured it degrades to git-file leases. Enforcement hooks are Claude Code only - on other agents the same checks run as a self-check."
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.19.1"
|
|
8
8
|
author: ssheleg
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -33,7 +33,7 @@ from datetime import datetime, timezone
|
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
from typing import Any
|
|
35
35
|
|
|
36
|
-
VERSION = "1.
|
|
36
|
+
VERSION = "1.19.1"
|
|
37
37
|
|
|
38
38
|
CONFIG_PATH = Path(".claude/agent-sync.json")
|
|
39
39
|
ENV_FILE = Path(".env.agent-sync")
|
|
@@ -1646,6 +1646,21 @@ class Sync:
|
|
|
1646
1646
|
except (json.JSONDecodeError, OSError):
|
|
1647
1647
|
held = {}
|
|
1648
1648
|
if held.get("run") == self.rid:
|
|
1649
|
+
# MOVE THE LOCK'S OWN `ts`, not just the throttle marker. This branch did
|
|
1650
|
+
# exactly what `_refresh_lease`'s docstring describes as the bug it exists
|
|
1651
|
+
# to have fixed — touch the throttle file and leave the timestamp the lease
|
|
1652
|
+
# is arbitrated by — and the repair landed in `renew` and not here, in the
|
|
1653
|
+
# same file, four minor versions ago.
|
|
1654
|
+
#
|
|
1655
|
+
# Two harms, and the second is the one that bites. The lock stayed expired,
|
|
1656
|
+
# so `classify_lock` read it as spent and another run's `acquire` took it
|
|
1657
|
+
# over while this one believed it held (measured 2026-09-01: a foreign
|
|
1658
|
+
# expired lock IS taken over, timestamp and host rewritten). And
|
|
1659
|
+
# `_touch_renew` resets the marker `renew()` throttles on — so a re-acquire
|
|
1660
|
+
# actively SUPPRESSED the next real refresh, refreshing nothing itself.
|
|
1661
|
+
# That is the mechanism behind a lease expiring three times in one run
|
|
1662
|
+
# against a 450-step CI job on 2026-09-01.
|
|
1663
|
+
self._refresh_lease(key)
|
|
1649
1664
|
self._touch_renew()
|
|
1650
1665
|
return True, self.rid
|
|
1651
1666
|
if time.time() <= parse_iso(held.get("ts", "")) + int(held.get("ttl", self.ttl)):
|