@ssheleg/agent-sync 1.18.5 → 1.18.7

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,72 @@
1
+ ## v1.18.7 — the evals stop being a claim, and the manifests say what shapes them
2
+
3
+ Wave-3 close-out of the 2026-08-29 family audit (ASY-03, ASY-09). No behavior
4
+ changes; a measurement that did not exist now does, and two manifests declare
5
+ their schemas.
6
+
7
+ - **ASY-03 — the behavioral evals were executed, dated, and the numbers are worse
8
+ than silence pretended.** `test/evals/RESULTS.md` had one row: "no run yet".
9
+ It now carries two dated rows (2026-08-31, haiku and sonnet): 24 blind
10
+ fresh-context trigger probes — one per query per model, the query verbatim plus
11
+ the installed family list, nothing else — and all six scenario lines driven as
12
+ real agent sessions against scratch repositories, end states verified on disk.
13
+ Both models score 11/12 on triggers (train 6/6, validation 5/6) and fail the
14
+ SAME query: **q06, the submodule-finish positive, 0/3 samples on each model** —
15
+ the description has no submodule vocabulary to catch it. That gap is filed as
16
+ ASY-10 (a description change is a minor release, not this patch). The Method
17
+ section states the harness limits out loud: n=1 sampling except the failing
18
+ query, the operator's routing doctrine present in probe context, fs-backend
19
+ `reserve` refusing by design in s02, model aliases rather than pinned snapshot
20
+ ids. SKILL-CARD.md's "never executed" line now points at the dated rows.
21
+ - **ASY-09 — both plugin manifests declare `$schema`.**
22
+ `.claude-plugin/marketplace.json` names
23
+ `https://json.schemastore.org/claude-code-marketplace.json` and
24
+ `plugins/agent-sync/.claude-plugin/plugin.json` names
25
+ `https://json.schemastore.org/claude-code-plugin-manifest.json`; both files
26
+ validate against the schemas those addresses serve (fetched and checked at
27
+ edit time), and `claude plugin validate --strict` stays green on both. The
28
+ repo's own no-host-address rule refused the plugin manifest — correctly, by
29
+ its letter — so `json.schemastore.org` joined `ALLOWED_HOSTS` beside
30
+ `json-schema.org`: a vendor-neutral public schema registry, not an instance
31
+ address. The planted `leaked host` self-test still proves the check can fail.
32
+
33
+ ## v1.18.6 — the guard keeps its own header's promise, and /clear gets its own identity
34
+
35
+ Wave-2 hardening from the 2026-08-29 family audit (ASY-07, ASY-08, ASY-06, ASY-01).
36
+ Same shape as v1.18.5: text that claimed more than the code did.
37
+
38
+ - **ASY-07 — a machine without python3 had no guard, and said nothing.** `guard.sh`'s
39
+ header promises that every internal failure exits 2, because any other outcome is
40
+ non-blocking in Claude Code — and the python3-absent path broke it: both parser
41
+ substitutions failed under `2>/dev/null`, `path` came back empty, `is_commit`
42
+ defaulted to 0 and the hook exited 0. Fail OPEN, exactly where the header says it
43
+ must not, and nothing upstream compensates — hooks.json's `if` filter is best-effort
44
+ by doctrine, so the parser IS the guard. Both substitutions now report through
45
+ `parser_or_die`, which exits 2 and names the remedy (install python3 / fix PATH, or
46
+ remove `.claude/agent-sync.json` to switch coordination off).
47
+ `check_guard_fails_closed_without_python3` drives the real hook through a shim PATH
48
+ holding bash and cat but no interpreter — watched failing against the shipped guard
49
+ (`the guard exited 0`) — and the self-test plants the fail-open back
50
+ (`an absent interpreter fails open`).
51
+ - **ASY-08 — a post-/clear session inherited the ended run's identity.** The
52
+ SessionStart matcher read `startup|resume`, and `/clear` ends the run while keeping
53
+ the CLI process — so the stamp keyed by that process survived into the next session:
54
+ "two sessions, one identity", the case SKILL.md itself names, and `release` then
55
+ takes a lease the caller never had. The matcher now carries `clear`; `compact` stays
56
+ excluded deliberately, because a compaction continues the SAME session.
57
+ `hooks_session_test.py` gained the case (matcher parts asserted both directions, and
58
+ the hook re-stamps on a clear payload) — watched failing against the shipped matcher.
59
+ - **ASY-06 — the guard's internal-failure refusal named no next step.** A refusal with
60
+ no remedy is how an operator learns to switch a hook off. The message now says:
61
+ diagnose with `python3 <script> check`, or acquire a lease first.
62
+ - **ASY-01 — `scripts/__pycache__` regenerated forever.** Hooks now export
63
+ `PYTHONDONTWRITEBYTECODE=1` in `_lib.sh` (sourced by all four), and the test files
64
+ that import `agent_sync.py` set `sys.dont_write_bytecode` — the shipped `scripts/`
65
+ directory stays source, not build products.
66
+ - ASY-04 (this test class missing from CI) was already closed in v1.18.5:
67
+ `validate.yml` runs `hooks_session_test.py` since commit `5446990`. Verified, not
68
+ re-done. ASY-02 (command rename) closed by operator decision 2026-08-30: names stay.
69
+
1
70
  ## v1.18.5 — a pipe reaches the guard, and the installers stop deleting blind
2
71
 
3
72
  Two enforcement holes, each of the same shape: a mechanism whose own text claimed more
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssheleg/agent-sync",
3
- "version": "1.18.5",
3
+ "version": "1.18.7",
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"
@@ -1,7 +1,8 @@
1
1
  {
2
+ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
2
3
  "name": "agent-sync",
3
4
  "displayName": "Agent Sync",
4
- "version": "1.18.5",
5
+ "version": "1.18.7",
5
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.",
6
7
  "author": {
7
8
  "name": "ssheleg",
@@ -1,6 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  # Shared helpers for the agent-sync hooks. Sourced, never executed.
3
3
 
4
+ # Every hook runs python3 against the shipped scripts/ directory, which people read
5
+ # as source, not as a build product. Bytecode caching buys nothing at this call rate
6
+ # and left scripts/__pycache__ regenerating forever (ASY-01).
7
+ export PYTHONDONTWRITEBYTECODE=1
8
+
4
9
  # Run a command under a time limit, portably.
5
10
  #
6
11
  # `timeout` is GNU coreutils and is NOT on a stock macOS. Calling it directly made
@@ -8,6 +8,17 @@ S="$AGENT_SYNC_PY"
8
8
  agent_sync_configured || exit 0
9
9
  input=$(cat)
10
10
 
11
+ # The parser IS the guard: hooks.json's `if` filter is best-effort and fails open by
12
+ # doctrine, so nothing upstream compensates for a parser that cannot run. A python3
13
+ # missing from PATH used to leave `path` empty, default `is_commit` to 0 and exit 0 —
14
+ # the one machine state that disables the parser silently disabled the guard (ASY-07).
15
+ parser_or_die() {
16
+ local rc="$1"
17
+ [ "$rc" -eq 0 ] && return 0
18
+ echo "agent-sync: the guard could not run its parser (python3 exit $rc — likely missing from PATH). Failing closed. Install python3 or fix PATH and retry, or remove .claude/agent-sync.json to switch coordination off here." >&2
19
+ exit 2
20
+ }
21
+
11
22
  path=$(python3 -c '
12
23
  import json,sys
13
24
  try:
@@ -17,6 +28,7 @@ except Exception:
17
28
  ti=d.get("tool_input") or {}
18
29
  print(ti.get("file_path") or ti.get("path") or ti.get("notebook_path") or "")
19
30
  ' <<<"$input" 2>/dev/null)
31
+ parser_or_die $?
20
32
 
21
33
  # git commit: check every staged path instead of a single file argument.
22
34
  if [ -z "$path" ]; then
@@ -30,7 +42,7 @@ if [ -z "$path" ]; then
30
42
  #
31
43
  # Tokenised in python rather than globbed in shell: `git log --grep=commit` must not match, and
32
44
  # `git -c user.name=x -C dir commit` must.
33
- read -r is_commit repo <<<"$(python3 -c '
45
+ parsed=$(python3 -c '
34
46
  import json, shlex, sys
35
47
  try:
36
48
  d = json.load(sys.stdin)
@@ -71,7 +83,9 @@ for seg in cmd.replace("&&", "\n").replace("||", "\n").replace("|&", "\n").repla
71
83
  repo = r
72
84
  break
73
85
  print(is_commit, repo)
74
- ' <<<"$input" 2>/dev/null)"
86
+ ' <<<"$input" 2>/dev/null)
87
+ parser_or_die $?
88
+ read -r is_commit repo <<<"$parsed"
75
89
  [ -n "${is_commit:-}" ] || is_commit=0
76
90
  [ -n "${repo:-}" ] || repo="."
77
91
  [ -d "$repo" ] || repo="${CLAUDE_PROJECT_DIR:-$PWD}"
@@ -100,6 +114,6 @@ else
100
114
  echo "$out" >&2
101
115
  exit 2
102
116
  fi
103
- echo "agent-sync guard failed to run ($code): $out" >&2
117
+ echo "agent-sync guard failed to run ($code): $out — diagnose with \`python3 $S check\`, or acquire a lease first: \`python3 $S acquire <TASK-ID>\`" >&2
104
118
  exit 2
105
119
  fi
@@ -3,7 +3,7 @@
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
6
- "matcher": "startup|resume",
6
+ "matcher": "startup|resume|clear",
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
@@ -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.18.5"
7
+ version: "1.18.7"
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.18.5"
36
+ VERSION = "1.18.7"
37
37
 
38
38
  CONFIG_PATH = Path(".claude/agent-sync.json")
39
39
  ENV_FILE = Path(".env.agent-sync")