@ssheleg/agent-sync 1.19.1 → 1.19.3
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,56 @@
|
|
|
1
|
+
## v1.19.3 — the boundary ships with the skill, and two descriptions stop looking like drift
|
|
2
|
+
|
|
3
|
+
Close-out of the 2026-09-06 family audit (wave AUDIT-WAVE-0906). One bullet
|
|
4
|
+
enters the skill body, one decision gets a written home; no behaviour changes.
|
|
5
|
+
|
|
6
|
+
- **The refusal the operator's routing block promises now travels with the
|
|
7
|
+
skill.** The routing doctrine names this router's boundary — a file in a
|
|
8
|
+
project where coordination is on — and a waiver, **"no coordination" /
|
|
9
|
+
«без координации»**, and neither phrase appeared anywhere in the shipped
|
|
10
|
+
`SKILL.md`: a standalone install advertised no refusal at all. One bullet in
|
|
11
|
+
Non-negotiables now carries both. The body had **3 tokens of headroom**
|
|
12
|
+
(~4747/4750 against the house working limit), so the line displaced prose
|
|
13
|
+
rather than joining it — standing instruction 7 — and the auditor was watched
|
|
14
|
+
refusing both oversteps on the way: a first draft measured 4753
|
|
15
|
+
(`GAP BODY_HEADROOM`), the second exactly 4750 and still refused, the shipped
|
|
16
|
+
text ~4746/4750 after trimming one motivational sentence from the `status`
|
|
17
|
+
section and three words from the `$SKILL_DIR` paragraph. `audit_skill.py
|
|
18
|
+
--house`: **0 GAP, 15 PASS**.
|
|
19
|
+
- **`plugin.json` and `marketplace.json` describe this plugin in two entirely
|
|
20
|
+
different texts — now recorded as deliberate, which is what makes it
|
|
21
|
+
distinguishable from drift.** The audit's finding was not that the texts
|
|
22
|
+
differ but that nothing RECORDED whether they should, and a sibling shipped
|
|
23
|
+
accidental manifest drift this same week. They should: the marketplace entry
|
|
24
|
+
is the storefront read before install and carries the lease doctrine in full —
|
|
25
|
+
that surface is where the 1.5.2 audit caught this repo selling the refuted
|
|
26
|
+
no-CAS design, and `check_doctrine_is_current` still watches it — while
|
|
27
|
+
`plugin.json` is the installed listing's one-line layer summary. JSON takes no
|
|
28
|
+
comment, so the decision lives where this board's decisions live
|
|
29
|
+
(`docs/evidence/backlog.md`, beside B-002 and B-004), naming both texts and
|
|
30
|
+
which surface reads which.
|
|
31
|
+
|
|
32
|
+
## [1.19.2] - 2026-09-04
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- **An expired foreign lease printed at every SessionStart for ever, and the block it sat
|
|
37
|
+
in stopped being read.** Measured on the machine that filed the issue: sixteen expired
|
|
38
|
+
leases, none newer than two days, the oldest **thirteen days old** — six printed in full
|
|
39
|
+
plus *"… and 11 more"*, identically, every session for a fortnight. None of it was
|
|
40
|
+
actionable: a foreign lease is reported and deliberately never touched, so the operator
|
|
41
|
+
could not clear a single line of it. A block reprinted verbatim is a block an operator
|
|
42
|
+
learns to skim, **including the lines that would have mattered**.
|
|
43
|
+
|
|
44
|
+
Age splits the list now. A lease expired inside `STALE_DETAIL_SECONDS` (24h) may still
|
|
45
|
+
mean a run is in trouble and keeps its own line; anything older collapses into **one**
|
|
46
|
+
summary — which still **names the keys**, because a summary that hides which leases are
|
|
47
|
+
stuck replaces noise with a different uselessness. Sixteen lines became one.
|
|
48
|
+
|
|
49
|
+
Watched failing before it shipped: with the split removed the new check reports
|
|
50
|
+
*"6 ancient lease(s) still print a line each"* and *"nine ancient leases produced no
|
|
51
|
+
summary line at all, so they vanished"* — the second assertion exists because collapsing
|
|
52
|
+
and hiding are one edit apart.
|
|
53
|
+
|
|
1
54
|
## v1.19.1 — re-acquiring your own lease now refreshes it
|
|
2
55
|
|
|
3
56
|
**`renew` was fixed for exactly this and `acquire` was not, in the same file.**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ssheleg/agent-sync",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.3",
|
|
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.19.
|
|
5
|
+
"version": "1.19.3",
|
|
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",
|
|
@@ -4,7 +4,7 @@ description: "Use when several coding agents work one repository at the same tim
|
|
|
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.19.
|
|
7
|
+
version: "1.19.3"
|
|
8
8
|
author: ssheleg
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -145,9 +145,6 @@ cannot give two answers about one project.
|
|
|
145
145
|
away — watermarked per run, so it stays quiet until something changes. A dependency that
|
|
146
146
|
moved may unblock what you planned, or invalidate it.
|
|
147
147
|
|
|
148
|
-
An agent that skips this block will re-derive work someone else is doing and act on a
|
|
149
|
-
dependency state that changed an hour ago.
|
|
150
|
-
|
|
151
148
|
What else `status` decides: no credentials → degraded mode, reported, and it continues;
|
|
152
149
|
`task-pipeline` absent → it prints the install line and stops. Do not improvise a substitute
|
|
153
150
|
flow — without those stages there is nothing to bind to.
|
|
@@ -187,7 +184,7 @@ npx sshlg-skills install
|
|
|
187
184
|
|
|
188
185
|
`$SKILL_DIR` is this skill's own directory: `${CLAUDE_PLUGIN_ROOT}/skills/agent-sync` under
|
|
189
186
|
the Claude Code plugin, `~/.agents/skills/agent-sync` elsewhere. Resolve it once per session
|
|
190
|
-
and reuse it
|
|
187
|
+
and reuse it. Every command reads `.claude/agent-sync.json` from the project
|
|
191
188
|
root and needs no arguments beyond those listed.
|
|
192
189
|
|
|
193
190
|
## One identity per session, and how it is decided
|
|
@@ -326,6 +323,8 @@ the parent; a service repo listing the parent's decision register is a config de
|
|
|
326
323
|
- Two agents in one checkout are two identities, or the lease is decoration.
|
|
327
324
|
- A submodule commit is unfinished until the parent points at it — run `finish`.
|
|
328
325
|
- Everything the cloud holds about a durable fact is a link to git, never a substitute.
|
|
326
|
+
- Boundary: `.claude/agent-sync.json`. None, or one agent alone — route past. The
|
|
327
|
+
operator's waiver: "no coordination" / «без координации».
|
|
329
328
|
|
|
330
329
|
## References
|
|
331
330
|
|
|
@@ -33,11 +33,16 @@ from datetime import datetime, timezone
|
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
from typing import Any
|
|
35
35
|
|
|
36
|
-
VERSION = "1.19.
|
|
36
|
+
VERSION = "1.19.3"
|
|
37
37
|
|
|
38
38
|
CONFIG_PATH = Path(".claude/agent-sync.json")
|
|
39
39
|
ENV_FILE = Path(".env.agent-sync")
|
|
40
40
|
STATE_DIR = Path(".agent-sync")
|
|
41
|
+
# How recent an expired lease has to be to earn a line of its own. Anything older
|
|
42
|
+
# collapses into one summary that still names the keys. Twenty-four hours is chosen
|
|
43
|
+
# because it is the span in which an expired lease can still mean "a run is in
|
|
44
|
+
# trouble"; beyond it, it means "somebody's machine was closed".
|
|
45
|
+
STALE_DETAIL_SECONDS = 24 * 60 * 60
|
|
41
46
|
GENERATED_MARKER = "<!-- agent-sync:generated"
|
|
42
47
|
MERGE_LOG_MARKER = "<!-- agent-sync:merge-log -->"
|
|
43
48
|
DEFAULT_MERGE_LOG = "docs/MERGES.md"
|
|
@@ -3392,10 +3397,29 @@ def cmd_status(_args: argparse.Namespace) -> int:
|
|
|
3392
3397
|
f"{len(left_alone)} left alone")
|
|
3393
3398
|
print("\n Expired leases still on disk. Nobody holds these: the TTL ended the "
|
|
3394
3399
|
"lease\n and left the file.")
|
|
3395
|
-
|
|
3400
|
+
# Age splits the list, because age is what decides whether a line is news.
|
|
3401
|
+
# A lease that expired an hour ago may still be a run in trouble; one that
|
|
3402
|
+
# expired thirteen days ago is residue nobody will ever clear, and printing
|
|
3403
|
+
# it in full at every SessionStart is how an operator learns to skim the
|
|
3404
|
+
# whole block — including the recent lines that mattered. Measured on the
|
|
3405
|
+
# machine that filed this: sixteen entries, none newer than two days, the
|
|
3406
|
+
# oldest thirteen days old, printed identically every session for a fortnight.
|
|
3407
|
+
fresh = [e for e in stale
|
|
3408
|
+
if e.get("expired_for") is not None and e["expired_for"] < STALE_DETAIL_SECONDS]
|
|
3409
|
+
old_ones = [e for e in stale if e not in fresh]
|
|
3410
|
+
for e in fresh[:6]:
|
|
3396
3411
|
print(f" · {e['key']} [{e['state']}] {e['why']} ({spent(e)})")
|
|
3397
|
-
if len(
|
|
3398
|
-
print(f" · … and {len(
|
|
3412
|
+
if len(fresh) > 6:
|
|
3413
|
+
print(f" · … and {len(fresh) - 6} more expired in the last "
|
|
3414
|
+
f"{since(STALE_DETAIL_SECONDS)}")
|
|
3415
|
+
if old_ones:
|
|
3416
|
+
# One line, and it still names them: a summary that hides WHICH keys
|
|
3417
|
+
# cannot be acted on, and the point of collapsing is to keep the block
|
|
3418
|
+
# readable rather than to stop reporting.
|
|
3419
|
+
keys = ", ".join(e["key"] for e in old_ones[:8])
|
|
3420
|
+
more = f", +{len(old_ones) - 8} more" if len(old_ones) > 8 else ""
|
|
3421
|
+
print(f" · {len(old_ones)} expired over {since(STALE_DETAIL_SECONDS)} ago "
|
|
3422
|
+
f"— {keys}{more}")
|
|
3399
3423
|
if reapable:
|
|
3400
3424
|
print(" Clear what this run owns: agent_sync.py reap")
|
|
3401
3425
|
if left_alone:
|