Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@
"name": "work-system",
"source": "./plugins/work-system",
"description": "Generic task and worktree workflow system for Claude Code. Keep tasks as GitHub issues or local markdown files, run them in isolated git worktrees with a choice of worker agent (Claude, codex, grok, kimi, or a PATH-detected cc-harness agent), and track progress across the define/kickoff/continue/status/close lifecycle.",
"version": "1.28.4"
"version": "1.29.0"
},
{
"name": "pr-flow",
"source": "./plugins/pr-flow",
"description": "PR review feedback loop for Claude Code. Create PRs with readiness checks, commit + push + trigger @claude review, inspect status, and work through review issues interactively.",
"version": "1.8.2"
"version": "1.9.0"
},
{
"name": "swarm",
Expand Down
2 changes: 1 addition & 1 deletion .claude/knowledge/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
- `features/github-issues-task-backend.md` — `task-store.py` as the one backlog path: `tasks.backend` auto|github|local (local files win until migrated; an installed-but-failing gh is exit 4, never a silent local fallback), label + body-marker identity, copy-not-link TASK.md, network-free statusline counts lanes
- `features/task-archiving-on-close.md` — `/close` archives (not deletes) the task file; adaptive commit + ff-push to main; per-repo `.claude/work-system-close-autocommit` opt-in skips the ask; why the archive takes no caller-supplied note
- `features/swarm-backend-adapter.md` — External adapter contract: jailed read+web, egress guard, out-of-band transport, schema normalization, readiness, telemetry, adapter-owned config; per-run Grok selection with a measured, cached schema probe (no version allowlist); Kimi uses ACP with denied permissions + local fail-closed validation
- `features/worker-autonomy-mandate.md` — `MANDATE.md` as the lane's authorization record: frontmatter-only consent (task prose never grants), the 0/1/3 `allows` contract (denied vs. never-asked), a review budget counted in-file so it survives a resume, pr-flow's soft-coupled shim answering "unknown" not "no", plus bot-detection before recommending a review; the `reviewed`/`fixed`/`covered` local-review merge gate (recorded on a finished review, normalized diff hash not patch-id, masked manifest versions, user-only `acknowledged` delta, why the classifier needs it)
- `features/worker-autonomy-mandate.md` — `MANDATE.md` as the lane's authorization record: frontmatter-only consent (task prose never grants), the 0/1/3 `allows` contract (denied vs. never-asked), a review budget counted in-file so it survives a resume, pr-flow's soft-coupled shim answering "unknown" not "no", plus bot-detection before recommending a review; the `reviewed`/`fixed`/`covered` local-review merge gate (recorded on a finished review, normalized diff hash not patch-id, masked manifest versions, user-only `acknowledged` delta (from a per-file summary), why the classifier needs it)
- `features/swarm-review-pipeline.md` — `/swarm:review`: 11 lenses/5 clusters across Claude + codex/grok/kimi; four-family consensus; verify, `--fix`/`--loop`, deterministic `--pr` publishing; fail-closed voice accounting (resolved != reviewed) + the marker/mutation test seam
- `features/insights-lifecycle-producers.md` — work-system's `/continue` + `/land` handoff and `/close` reports: absent vs. installed-but-unusable as distinct exit codes, report between merge gate and removal and never a gate, idempotency from the store itself, why the archived-task-file fallback was struck before merge, trigger ≠ task_status, contract path from the helper, prose ordering guarded by an assertion test; plus three shell traps (subshell array append, `${arr[@]/pat}`, `--date=format:` timezone); a review run that committed to the branch
- `features/insights-report-store.md` — insights.py as sole report write/read path: main-checkout identity, strict Fact shapes, link()-based no-overwrite publish, refuse-not-chmod store dirs, file drafts not heredocs, fixpoint redaction exempting identifiers, anchored URL parse, skeleton that fails until filled
Expand Down
48 changes: 46 additions & 2 deletions .claude/knowledge/features/worker-autonomy-mandate.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: "Worker Autonomy Mandate (MANDATE.md)"
createdAt: 2026-09-07
updatedAt: 2026-10-22
updatedAt: 2026-10-28
createdFrom: "branch: task/extend-worker-autonomy"
updatedFrom: "branch: task/harden-mandate-diff-id-and-round-lock"
updatedFrom: "branch: task/summarize-merge-delta-ack"
pluginVersion: 1.9.0
prime: false
---
Expand Down Expand Up @@ -512,6 +512,50 @@ covers. Why it is built this way:
(checked on a temp file with `tr -d '\000' | cmp`). The script cannot
see who asks, so "interactive user only" lives in the callers' prose; like
`reviewed`, it is not a boundary against a worker determined to bypass it.
- **The ack is decided from a summary, not a raw diff** (2026-10-26, after
#91's ~278-line delta was acked unread: "I am not a robot"). An ack
text that claims "I have read this delta" records something that did not
happen, so the prompt is `[a]cknowledge — merge this change without a
re-review / [d]iff / [r]e-review / [s]top`, and the model reads the full
delta itself and summarizes it per file. The summary stands on mechanical
facts, not on hunk reading: a diff-of-diffs hunk often has its file header
outside its 3 context lines, so `delta` prints `delta_file=` lines computed
per file from the two raw lane diffs — `lane=` (delta lines whose inner
prefix is the lane's own `+`/`-`) vs. `context=` (main's lines, headers), so
`lane=0` provably means "only moved with main", and `versions_only=` (equal
after the version mask). The kind labels are gated on those facts ("from
main" only at `lane=0`, "agreed fix" only with `includes_agreed_fixes=yes`
and only for the hunks shown to be one); everything else is `new code` or
`unclear`. The list sits above `delta_begin`, outside the hashed text, so
the `ack_id` and every refusal are unchanged. Review round 1 (swarm) found
three traps: `awk -v h="$header"` expands the backslashes of a C-quoted
name (`"a/\303\244.sh"`), so the lookup missed and new lane code read
`new lane=0` — join both indexes in one awk pass instead, never pass
lane-derived text through `-v`; `versions_only` must follow the gate's
`version_paths` policy, or a dependency downgrade elsewhere reads as a
renumbering; and a lane-chosen path printed raw can forge `key=value`
fields, so it goes last, `%q`-quoted. The model reads the delta from a
file (`--max-lines all`, Read in ranges to the last line): a single Bash
result is capped by the harness while the header still says
`truncated=no`. Kind labels other than the two mechanical ones are the
model's reading of the code, never of what the lane text calls itself.
Round 2: an unbounded `--max-lines all` made `ack_id` prove only that the
text was printed, so a delta over `ACK_MAX_LINES` (2000) — or, round 3,
with a line over 1000 bytes, which the Read tool cuts — gets none and
`acknowledged` refuses it (`too-large`); `%q` alone leaves multibyte bidi
characters raw under `core.quotePath=false`, so the path is `cat -v`-escaped
first; `FNR == NR` joins break when the first file is empty (use
`FILENAME == ARGV[1]`). Round 4: a compat fallback must keep every
guarantee, not just the one in view — rewriting `all` to 2000 for a
pre-1.29 work-system kept the line ceiling but not the byte ceiling, so an
old `acknowledged` would ack one long line no summary shows; the shim now
answers `old-work-system` there and `/merge` stops. And `lane=0` is not
"only main moved" when the lane moves its own lines across a context line:
`diff -U0` may show that as the context line leaving and returning (two lane
lines past one context line make that the cheaper side), with no hunk header
change inside a function — a context line that leaves and returns verbatim
counts as lane. Reproduce such a case before trusting a fix: a one-line
move is a tie and passed the old code too.
- **`fixed` is an attestation, so once per review, bound by `--after <sha>`**
to the review the caller ran. Repeatable or unbound, it would let any later
edit be stamped as covered.
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ entries are grouped per plugin, newest first.

## work-system

### 1.29.0 — 2026-10-28
- `/land` decides a post-review delta from a plain-language summary, not a raw diff: per file the kind of change, its size and whether it touches the reviewed logic, then `[a]cknowledge` (merge without a re-review) / `[d]iff` / `[r]e-review` / `[s]top`. The ack wording no longer claims the diff was read; `acknowledged --ack-id` and every refusal are unchanged.
- `mandate.sh delta` prints `delta_files=` and one `delta_file=<new|gone|changed> lane= context= versions_only= path=` line per file, computed from the two raw lane diffs (lane vs. main context lines; `versions_only` only for files on `version_paths`; the path shell-quoted), outside the acked text. `--max-lines all` prints the delta uncut. A delta over 2000 lines, or with a line over 1000 bytes, is `too_large=yes`: no `ack_id`, and `acknowledged` refuses it (`reason=too-large`) — it needs a review.

### 1.28.4 — 2026-10-27
- `mandate.sh` locks the record writers (`round`, `reviewed`, `fixed`, `acknowledged`) with `flock(2)` on `.MANDATE.lock` instead of a `mkdir` lock directory. Breaking an "abandoned" `mkdir` lock raced: a breaker read the dead owner's pid, the lock was released and taken by a live booker meanwhile, and the breaker deleted the live lock, so two writers ran and a concurrent `round` booking was lost (CI flake in `test_mandate.py`; 1 of 40 runs of 16 concurrent bookings on 1.28.2, 0 of 160 now). The kernel frees a flock when its owner dies, so the staleness rule (pid check, 5-minute age bound) is gone. The lock file stays beside the record (covered by the `.MANDATE.*` exclude); git calls close the lock descriptor so no daemon can inherit it. Taking the lock needs python3 (macOS has no `flock(1)`); without it a write is refused with exit 4, never done unlocked. A leftover lock directory from 1.28.2 or older is refused with the `rm -rf` to run. Reload every session working a lane (`/sessions`) before it books again: a 1.28.2 session refuses the new lock file and advises an `rm -f` that would unlink a held lock. The lock file counts as a lane file for the Manager's zero-loss close, every writer re-ensures the `.MANDATE.*` exclude, and a filesystem that refuses flock is named as such instead of reported as contention.

Expand Down Expand Up @@ -336,6 +340,9 @@ entries are grouped per plugin, newest first.

## pr-flow

### 1.9.0 — 2026-10-28
- `/merge` step 6b: on `changed-since-review` the delta is read in full from a file and summarized per file (kind, size, reviewed-logic impact) instead of printed; the prompt is `[a]cknowledge — merge this change without a re-review / [d]iff / [r]e-review / [s]top`. Kind labels other than version renumbering and from-main are the model's reading of the code, never taken from what the lane text says about itself. With a work-system older than 1.29 (no ack ceiling) the shim answers `delta`/`acknowledged` with `verdict=old-work-system`, so `/merge` stops instead of offering an ack: an older `acknowledged` would accept a short delta holding one long line whose payload no summary shows.

### 1.8.2 — 2026-10-28
- `REVIEW-ROUTING.md` describes `mandate.sh round` exit 4 as work-system 1.28.4 reports it (another session writing the record, no python3 for the lock, a read-only tree); the advice to clear an abandoned lock is gone, since a flock is never abandoned. Docs only.

Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ This is a **Claude Code plugin marketplace** (monorepo) containing plugins that
## Current Plugins

- **knowledge-system** (v1.9.x) — Knowledge management with three layers: Rules, Knowledge, Memory. Skills: `/init`, `/query`, `/curate`, `/reindex`, `/backfill-knowledge`, `/migrate`, `/statusline`
- **work-system** (v1.28.x) — Task and worktree workflow; backlog in GitHub Issues or `tasks/` files via `task-store.py` (`tasks.backend`); workers: Claude/codex/grok/kimi or PATH-detected cc-harness; `/kickoff` records the lane's autonomy mandate in `MANDATE.md`, `/continue` works from it; a worktree `/close` can delegate its teardown to the Manager session, and `/continue` pings it at milestones; `/land` composes `/pr-flow:merge` + `/close --delegate`; the Manager runs, asks or records (ROADMAP) a closed lane's follow-ups and owns deploys; with the optional `insights` plugin, `/continue` reports at its terminal gate, `/land` when landing, and `/close` before teardown; `/sessions` reloads idle herdr sessions running stale plugin code; `herdr-shell.sh` drives interactive programs in temp herdr tabs. Skills: `/define`, `/kickoff`, `/adopt`, `/continue`, `/status`, `/close`, `/land`, `/list`, `/sessions`, `/statusline`
- **pr-flow** (v1.8.x) — PR review feedback loop; mandate-aware and review-bot-aware (route remembered per repo); defers deploys to the Manager inside a lane unless `deploy.by = "worker"`. Skills: `/open`, `/cycle`, `/check`, `/fix`, `/rebase`, `/merge`
- **work-system** (v1.29.x) — Task and worktree workflow; backlog in GitHub Issues or `tasks/` files via `task-store.py` (`tasks.backend`); workers: Claude/codex/grok/kimi or PATH-detected cc-harness; `/kickoff` records the lane's autonomy mandate in `MANDATE.md`, `/continue` works from it; a worktree `/close` can delegate its teardown to the Manager session, and `/continue` pings it at milestones; `/land` composes `/pr-flow:merge` + `/close --delegate`; the Manager runs, asks or records (ROADMAP) a closed lane's follow-ups and owns deploys; with the optional `insights` plugin, `/continue` reports at its terminal gate, `/land` when landing, and `/close` before teardown; `/sessions` reloads idle herdr sessions running stale plugin code; `herdr-shell.sh` drives interactive programs in temp herdr tabs. Skills: `/define`, `/kickoff`, `/adopt`, `/continue`, `/status`, `/close`, `/land`, `/list`, `/sessions`, `/statusline`
- **pr-flow** (v1.9.x) — PR review feedback loop; mandate-aware and review-bot-aware (route remembered per repo); defers deploys to the Manager inside a lane unless `deploy.by = "worker"`. Skills: `/open`, `/cycle`, `/check`, `/fix`, `/rebase`, `/merge`
- **swarm** (v0.16.x) — Local mixture-of-agents code review (external `codex`/`grok` CLIs, `kimi` opt-in, plus Claude lenses: 11 in 5 clusters). Profile-based cluster/lens fan-out; externals get read + web research under an OS secret-jail. P2: `/swarm:review` pipeline (scope→fan-out→merge→verify); P5: `--fix`/`--loop` apply the findings you agreed with. Skills: `/swarm:review`, `/swarm:agents`
- **settings** (v0.1.x) — Per-plugin TOML config resolved over schema defaults; each plugin owns its `schema/settings.schema.json`. Skill: `/settings` (list/show/get/set/validate). First consumer: pr-flow `review.route`.
- **insights** (v0.1.x) — Private local task/plugin insight reports (`insights.report/v1`, one helper for write/read/list). Skill: `/insights:report [text]`. work-system's `/continue`, `/land` and `/close` are its lifecycle producers.
Expand Down
Loading
Loading