@sabaiway/agent-workflow-kit 14.3.0 → 14.4.0
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 +25 -0
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/procedures.md +1 -1
- package/references/modes/review-state.md +1 -1
- package/tools/procedures.mjs +6 -11
- package/tools/review-state.mjs +11 -11
- package/tools/task-brief.mjs +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,31 @@ Semantically versioned ([semver](https://semver.org)), newest first. The `versio
|
|
|
4
4
|
is the current release. `upgrade` mode reads a project's `docs/ai/.workflow-version` and applies
|
|
5
5
|
every `migrations/<version>-<slug>.md` newer than it, in semver order.
|
|
6
6
|
|
|
7
|
+
## 14.4.0 — a delegated task engages the held session: `review-state --check` reads a configured `task.execute`, the fold lane follows a resolved one, and `task-brief check` admits a lone suite (AD-148)
|
|
8
|
+
|
|
9
|
+
**A task set to `delegated` is now gated and advised on its own.** In 14.3.0 the held-session gate and the fold lane
|
|
10
|
+
keyed only on `plan-execution.execute`, so a delegated task was covered only when the rows not carried as tasks were
|
|
11
|
+
delegated as well. 14.4.0 reads the task's own slot. Under a config that leaves `task.execute` unset or not
|
|
12
|
+
delegated, the gate and the lane behave exactly as before, except that `procedures task --override execute=delegated`
|
|
13
|
+
now renders the lane when the override resolves to delegated. Story S10 of the epic `EPICS-STORIES-TASKS-AS-THE-UNIT-OF-WORK`; contracts
|
|
14
|
+
`docs/ai/specs/kit/carriers/` revision 7, `held-session` revision 4, `checkpoint/` revision 4.
|
|
15
|
+
|
|
16
|
+
- **The gate (`tools/review-state.mjs`).** `--check` audits the delegation ledger when the configured
|
|
17
|
+
`plan-execution.execute` or the configured `task.execute` is `delegated`, each read on its own: a solo or subagent
|
|
18
|
+
value in one slot never masks the other. It reads the configuration, never a degraded recipe or a per-run override.
|
|
19
|
+
The judge, the refusal and the report line are unchanged. The tool header, `--help` and `modes/review-state.md` say so.
|
|
20
|
+
- **The fold lane (`tools/procedures.mjs`).** `procedures plan-execution` renders the lane once when its own `execute`
|
|
21
|
+
or `task.execute` resolves to delegated; a per-run `--override` moves only the rendered activity's own slot.
|
|
22
|
+
`procedures task` renders it when `task.execute` resolves to delegated. `task.author` never counts, and `foldLane`
|
|
23
|
+
stays `[]` for every other activity. The lane's lines and caveats are unchanged; `modes/procedures.md` says so.
|
|
24
|
+
- **A lone suite is briefable (`tools/task-brief.mjs`).** A slice with no `impl` line may carry the co-located test of
|
|
25
|
+
the row's path or of one of its sweep expansions. A slice with `impl` lines still admits only tests of its own `impl`
|
|
26
|
+
paths, and a test of any other path still refuses `row`.
|
|
27
|
+
- **Tests.** `review-state-task-execute.test.mjs` (new; carriers S19), `procedures-fold-lane.test.mjs` (S20) and
|
|
28
|
+
`task-brief.test.mjs` (checkpoint S11). The pinned cells of `review-state-held-session.test.mjs` pass unedited.
|
|
29
|
+
|
|
30
|
+
Published with engine 5.9.0, whose `task` step 5 states the same rule.
|
|
31
|
+
|
|
7
32
|
## 14.3.0 — the activity table gains its tier rows: `epic` (author, review) and `task` (author, execute) are configured, resolved and rendered like every other activity (AD-147)
|
|
8
33
|
|
|
9
34
|
**Epics and tasks now have recipe slots.** Until 14.2.0 the kit had an epic file and a task brief, but no slot said who
|
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: agent-workflow-kit
|
|
|
3
3
|
description: Deploy or upgrade a portable AI-agent memory-and-workflow system in any project. Use when the user wants to bootstrap `docs/ai/` + an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) + cap/archive/index enforcement in a new or existing repo, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-kit` / `/agent-workflow-kit upgrade`. Triggers on phrases like "set up the memory system", "deploy the AI workflow here", "bootstrap docs/ai", "upgrade the workflow".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
metadata:
|
|
6
|
-
version: '14.
|
|
6
|
+
version: '14.4.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-kit
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-kit",
|
|
3
|
-
"version": "14.
|
|
3
|
+
"version": "14.4.0",
|
|
4
4
|
"description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|
|
@@ -23,7 +23,7 @@ Run **`node ${CLAUDE_SKILL_DIR}/tools/procedures.mjs <activity> [--override <slo
|
|
|
23
23
|
|
|
24
24
|
**The dispatch form (a slot resolved to `subagent`).** A carrier that is only NAMED is a name, not an instruction, so after such a slot's resolved-recipe line the advisor prints — indented under it, in place of the one-line vehicle mention — the form the dispatch must take, in five parts: (1) what a SLICE is for THIS slot — for `plan-execution.execute`, a set of file-disjoint ledger rows, wording copied verbatim where wording is a red line; for `plan-authoring.author`, a brief naming the goal, the governing spec(s) and the ledger constraints, whose returned draft the orchestrator reviews as its own; for `plan-authoring.fold`, the round's findings with their dispositions, whose edits the orchestrator follows with the self-consistency read; for `routine.carrier`, a bounded mechanical task, where a read-only chore (a sweep, gate triage) rides its placed read-only vehicle — or is carried solo with a stated reason when that vehicle is absent — a write-capable one (a regeneration, a fixture build) rides the executor, and the changelog stays the orchestrator's; for `epic.author`, a brief naming the intent, the value, the non-goals and the stories in order, from which the subagent drafts the epic file and the orchestrator runs the shape check on the draft as its own; for `task.author`, one ledger row of the plan with the contracts it reads, from which the subagent writes the task brief and the orchestrator stamps and checks it; for `task.execute`, one stamped brief, run inside its Files with the changed paths reported and the Acceptance commands run by the orchestrator; (2) the vehicle and its surveyed state — `.claude/agents/executor.md` (`placed` / `customized` / `unusable` / `missing`, from the same survey `status` and the advisor read) — dispatched **in the background**; (3) that the orchestrator **verifies every returned slice by running its suites itself**; (4) that the subagent is **never told to commit, never a review backend, never a bridge substitute**; (5) the **honest limit** — a Claude Code lane: on a host that cannot dispatch the vehicle, follow this form BY HAND and say so, never report it as a subagent dispatch. The wording is the kit's own (`tools/carriers.mjs`) and is printed, never re-worded. The dispatch-form branch is unchanged for a slot resolved to `solo` or `delegated`.
|
|
25
25
|
|
|
26
|
-
**Fold lane (an execute slot resolved to `delegated`).**
|
|
26
|
+
**Fold lane (an execute slot the activity reads resolved to `delegated`).** `procedures plan-execution` reads `plan-execution.execute` and `task.execute`, `procedures task` reads `task.execute` alone, and `task.author` never counts; a per-run `--override` moves only the rendered activity's own slot. The lane renders once, immediately after the rendered activity's `execute` slot block (after its driving contract when that slot itself resolves to `delegated`), as `codex-exec --resume <held id> --nonce <nonce> <fold-brief>` under the populated `run:` prefix, with the audited ledger's held id shell-quoted. `<nonce>` and `<fold-brief>` stay placeholders. One `caveat:` line replaces the id in each no-id case: no ledger; no folded code session in the commit epoch; a recorded substitution; an unresolvable, unreadable, malformed or audit-refused ledger (the reader reason verbatim); a failed HEAD read; or an id a one-line render cannot carry. An unborn branch runs the judge and is not a caveat. The block is advisory and never changes the exit code; `foldLane` carries the identical lines under `--json`, and is `[]` for every other activity and whenever no slot the activity reads resolves to `delegated`. The sidecar is never read.
|
|
27
27
|
|
|
28
28
|
**`routine`'s `parallel` switch renders against the EFFECTIVE carrier.** The flag always renders with its source (`parallel: on` / `parallel: off`); the concurrency claim — *file-disjoint slices dispatch concurrently* — renders **only** while the resolved `carrier` is `subagent`. Under a `solo` carrier the line states its own inertness instead, `(no effect while the carrier is solo)`: a switch never degrades, so the flag keeps the value you set and the render simply stops promising a concurrency nothing can perform. That makes four cells (`on`/`off` x `subagent`/`solo`), and only the `subagent`+`on` cell claims concurrency.
|
|
29
29
|
|
|
@@ -7,7 +7,7 @@ Read-only **review-receipt checker** (AD-038) — it makes *"reviewed ≠ shippe
|
|
|
7
7
|
Run `node ${CLAUDE_SKILL_DIR}/tools/review-state.mjs [--check] [--json]`:
|
|
8
8
|
|
|
9
9
|
1. Plain run → the human report: resolved recipe + source, plan-in-flight, tree fingerprint, the `held session: <id> — <n> fold(s) rode it` / `none` / substitution line when configured delegated execution arms it (with a task `code` thread of backend `codex` in the commit epoch and no unresolved substitution, `held sessions: <k> chain(s) — <label>: <id> (<n>) · …` is printed instead, one entry per chain, labelled by its brief or `untasked`, with `none` for a chain with no held session), and per-backend receipt state (current / stale / ungrounded / probe / rejected / missing) with verdict + grounding + timestamp.
|
|
10
|
-
2. **`--check`** → the gate exit code. Before the review-recipe early return, a configured `plan-execution.execute
|
|
10
|
+
2. **`--check`** → the gate exit code. Before the review-recipe early return, a configured `plan-execution.execute` or `task.execute` of `delegated` (each read on its own) with a plan in flight and a dirty, fingerprintable tree audits the delegation ledger and refuses a substituted held session; an absent ledger is inert, while an unreadable, malformed, foreign, audit-refused ledger or failed HEAD read fails closed with its own reason. For a head-base thread, the accepted-replacement escape is a `codex-exec` degrade at the substituted return's tree, standing after every later fold, never the review receipt id `codex`. A checkpoint-based thread's substitution closes only by its own ledger `degrade` (which also withdraws the expectation, so the next fold establishes HELD) — no core-evidence degrade lifts it, and the report's recovery hint says so. The remaining **normative exit contract lives in the tool header** (the single home — do not re-enumerate it elsewhere): exit 0 for a CONFIGURED solo recipe (after the held-session arm above) (a computed readiness-degrade NEVER silently becomes solo — it needs the explicit degrade record below), no plan in flight (the `docs/plans` naming convention: `queue.md` and `EXECUTE-`/`FEEDBACK-`/`TASK-`-prefixed or `PROMPT`/`prompt`/`handoff`-carrying names are scratch), a clean tree, a cwd where git ITSELF answers not-a-repository under both discoveries (the ONLY non-work-tree location that passes — `error`, `redirected`, `no-work-tree` and `env-only` refuse by their state name once a receipt could be required), or every recipe-named backend SATISFIED for the current tree. **A clean-tree PASS under a NON-SOLO review obligation is never silent about a latent arm:** when a `reviewed` or `council` recipe is configured, it NAMES every plan in flight and states that this gate arms as soon as the tree is dirty — the condition is discoverable BEFORE it blocks, instead of surfacing at the worst moment (a pending commit, or the landing of a feature worktree when main first turns dirty). Nothing is announced under a configured `solo` recipe or a not-a-repository cwd, where the gate can never arm. **Satisfaction is ship-class-only on the LATEST NORMAL receipt (D3(b)):** per backend, the latest probe-free current-fingerprint receipt is selected FIRST and THEN verdict-checked — only the recognized ship-class vocabulary (`ship` / `ship with nits`) satisfies; a recognized NEGATIVE (`revise` / `rethink` / `rework`) is an authoritative VETO (an earlier ship never survives a later revise); an UNRECOGNIZED verdict (e.g. `unknown` from a dead run) fails CLOSED unconditionally — a later `unknown` never lets an earlier SHIP stand, and a fresh normal re-run supersedes it. **The ONLY review-backend escape is an explicit degrade RECORD** (`core-evidence.mjs degrade --backend <name> --reason "…"`, fingerprint-bound to the current tree) — and never all backends: ≥1 non-degraded ship-class receipt is required whenever ≥1 backend is configured; a malformed evidence store denies the escape fail-closed but never fails an independently-satisfied tree. Exit 1 otherwise — missing, **stale** (every edit the payload can see moves the fingerprint, and no more), ungrounded, vetoed, or unrecognized. Plan/diff receipts and continuations (`agy-review --continue`) are **informational-only**: after a fold, only a **fresh grounded re-run** (`codex-review code`; `agy-review code --facts @f`) restores green. **Probe receipts never attest either:** a `CODEX_PROBE=1` / `AGY_PROBE=1` review runs with the frontier-model/max-effort guard OFF, so the wrapper stamps `probe:true` and this checker excludes it — per receipt, so a real review at the same fingerprint still satisfies. **Silence is not a declaration:** a malformed *or* absent probe marker is rejected fail-closed and stated in the check line. Honest bound: receipts are **not authenticated** (a forger could write `probe:false` as easily as any other field) — like the rest of the receipt this is a self-discipline mechanism, not a security boundary.
|
|
11
11
|
3. **Wire it as a gate by hand OR via the explicit-consent init preview — never without consent (AD-021/D9).** The candidate line for your own `docs/ai/gates.json`: `{ "id": "review-state", "title": "Review receipts current for the uncommitted tree", "cmd": "node <path-to-this-skill>/tools/review-state.mjs --check" }` — with the path your project actually reaches the kit by, QUOTED so a path with spaces survives, executable from the project root. The consent-gated gates-init preview — `node ${CLAUDE_SKILL_DIR}/tools/gates-init.mjs --cwd <project>` (dry-run: it prints the derived entries and writes NOTHING; `--apply --only review-state` writes exactly this one on your explicit yes) — offers exactly this entry, path resolved and quoted, ONLY when your `docs/ai/orchestration.json` declares `reviewed`/`council` on `plan-execution.review` (the slot this checker enforces). The full consent-fill contract is in `${CLAUDE_SKILL_DIR}/references/modes/gates.md`. Once declared, the opt-in `${CLAUDE_SKILL_DIR}/references/modes/hook.md` auto-approves it like any other declared gate — and `run-gates --final` requires this check among its canonical core gates.
|
|
12
12
|
4. **`--await [--timeout <s>]`** (BUGFREE-3 / AD-049) → BLOCK until every recipe-named backend is SATISFIED for the current tree (i.e. until `--check` would PASS), or the bounded timeout elapses (a loud exit 1; default 900s). Run it after dispatching the review bridges to WAIT for their receipts to land instead of hand-polling a pid: the durable completion signal is the **receipt**, never a process event (a harness "completed" notification fires early; a bridge's output late-flushes). It inherits the `--check` decision whole — a backend with a current-tree degrade RECORD stops being waited on (the shared decideCheck), so you never hand-`--await` around a known degrade. A held-session refusal and an **AUTHORITATIVE veto terminate the wait loudly BEFORE the deadline**: neither can be moved by waiting; only the held-session retry/degrade lane or a fresh review can move its corresponding refusal. Still read-only (it re-reads the receipts + the evidence store); solo / no-plan / clean-tree resolve instantly. For waiting on **one dispatch's arrival** (never obligation satisfaction) use the receipt-arrival deadline runner, `${CLAUDE_SKILL_DIR}/references/modes/receipt-deadline.md`.
|
|
13
13
|
|
package/tools/procedures.mjs
CHANGED
|
@@ -629,17 +629,10 @@ const contractLines = ({ cmd, contract, settings }) => {
|
|
|
629
629
|
return lines;
|
|
630
630
|
};
|
|
631
631
|
|
|
632
|
-
const foldLaneAdvice = ({
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
env,
|
|
637
|
-
resolveStore,
|
|
638
|
-
readStore,
|
|
639
|
-
audit,
|
|
640
|
-
readHead,
|
|
641
|
-
}) => {
|
|
642
|
-
if (activity !== 'plan-execution' || slots.find((slot) => slot.slot === 'execute')?.recipe !== 'delegated') return [];
|
|
632
|
+
const foldLaneAdvice = ({ activity, slots, config, detection, cwd, env, resolveStore, readStore, audit, readHead }) => {
|
|
633
|
+
const own = slots.find((slot) => slot.slot === 'execute')?.recipe;
|
|
634
|
+
const task = activity === 'plan-execution' ? resolveActivityRecipe({ config: config ?? {}, readiness: detection, activity: 'task', slot: 'execute' }).recipe : null;
|
|
635
|
+
if (!['plan-execution', 'task'].includes(activity) || ![own, task].includes('delegated')) return [];
|
|
643
636
|
const ledger = readDelegationLedger(cwd, env, { resolveStore, readStore, audit, readHead });
|
|
644
637
|
return foldLaneLines(judgeLedger(ledger, { backend: HELD_RECEIPT_BACKEND, degrades: [] }));
|
|
645
638
|
};
|
|
@@ -809,6 +802,8 @@ export const main = (argv, ctx = {}) => {
|
|
|
809
802
|
const foldLane = foldLaneAdvice({
|
|
810
803
|
activity,
|
|
811
804
|
slots,
|
|
805
|
+
config,
|
|
806
|
+
detection,
|
|
812
807
|
cwd,
|
|
813
808
|
env,
|
|
814
809
|
resolveStore: ctx.resolveDelegationStorePath,
|
package/tools/review-state.mjs
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
// `--check` turns the report into a gate exit code (declare it in docs/ai/gates.json).
|
|
10
10
|
//
|
|
11
11
|
// Normative `--check` exit contract (the single home of this list — SKILL.md points here):
|
|
12
|
-
// Before the review-recipe exits, configured plan-execution.execute
|
|
13
|
-
// dirty, fingerprintable tree audits the delegation ledger.
|
|
14
|
-
// unavailable ledger or a HEAD error refuses. A SUBSTITUTED held session
|
|
15
|
-
// a codex-exec degrade at the substituted return's tree, standing after
|
|
16
|
-
// or the fold of that thread's retry lifts it (codex never grants it); a
|
|
17
|
-
// substitution closes only by its own ledger degrade.
|
|
12
|
+
// Before the review-recipe exits, a configured plan-execution.execute or task.execute of delegated
|
|
13
|
+
// (each read on its own) with a plan and a dirty, fingerprintable tree audits the delegation ledger.
|
|
14
|
+
// An absent ledger is inert; an unavailable ledger or a HEAD error refuses. A SUBSTITUTED held session
|
|
15
|
+
// refuses: for a head-base thread, a codex-exec degrade at the substituted return's tree, standing after
|
|
16
|
+
// every later fold, accepts it, or the fold of that thread's retry lifts it (codex never grants it); a
|
|
17
|
+
// checkpoint-based thread's substitution closes only by its own ledger degrade.
|
|
18
18
|
// exit 0 when the CONFIGURED plan-execution.review recipe is solo (or the computed default is —
|
|
19
19
|
// absent config with no reviewer backend ready); when no plan is in flight (docs/plans/
|
|
20
20
|
// holds no top-level .md that is not queue.md and not scratch by the naming convention:
|
|
@@ -382,7 +382,7 @@ export const buildState = ({ cwd, env = process.env, detect = detectBackends, su
|
|
|
382
382
|
const heldSession = buildHeldSessionState({
|
|
383
383
|
cwd,
|
|
384
384
|
env,
|
|
385
|
-
configuredExecute: config?.[ACTIVITY]?.execute,
|
|
385
|
+
configuredExecute: [config?.[ACTIVITY]?.execute, config?.task?.execute].find((recipe) => recipe === 'delegated'),
|
|
386
386
|
plans,
|
|
387
387
|
fingerprint,
|
|
388
388
|
clean,
|
|
@@ -786,10 +786,10 @@ The ONLY escape for an unavailable backend under council is an explicit current-
|
|
|
786
786
|
record (node core-evidence.mjs degrade) — and never all backends.
|
|
787
787
|
|
|
788
788
|
--check exits 0/1 per the normative contract in the tool header. FIRST, before any of the arms
|
|
789
|
-
below, a configured plan-execution.execute
|
|
790
|
-
fingerprintable tree audits the delegation ledger: an absent
|
|
791
|
-
malformed, foreign or audit-refused ledger, a failed HEAD read,
|
|
792
|
-
exits 1. THEN: 0 for solo / no plan in flight /
|
|
789
|
+
below, a configured plan-execution.execute or task.execute of delegated (each read on its own)
|
|
790
|
+
with a plan in flight and a dirty, fingerprintable tree audits the delegation ledger: an absent
|
|
791
|
+
ledger is inert; an unreadable, malformed, foreign or audit-refused ledger, a failed HEAD read,
|
|
792
|
+
or a SUBSTITUTED held session exits 1. THEN: 0 for solo / no plan in flight /
|
|
793
793
|
a clean tree (under a non-solo review obligation the PASS names every plan in flight and its arm) /
|
|
794
794
|
a cwd where git ITSELF answers not-a-repository (the only passing non-work-tree state; the four others exit 1 by name) / obligations satisfied (reviewed: >=1 ship-class attestation;
|
|
795
795
|
council: every backend ship-class or degrade-recorded, >=1 real ship); 1 on a veto, an
|
package/tools/task-brief.mjs
CHANGED
|
@@ -167,7 +167,8 @@ const checkRow = (context, brief, row) => {
|
|
|
167
167
|
const allowed = isSweep(row.path) ? expandSweepPaths(context.top, [row.path])[row.path] : [row.path];
|
|
168
168
|
if (implementations.some((path) => !allowed.includes(path))) reject(NAMES.row, row.id);
|
|
169
169
|
const tests = files.filter(({ tag }) => tag === TEST);
|
|
170
|
-
|
|
170
|
+
const bases = implementations.length > START ? implementations : allowed;
|
|
171
|
+
if (tests.some(({ path }) => TEST_RE.test(row.path) ? path !== row.path : !bases.some((impl) => {
|
|
171
172
|
const stem = impl.replace(EXTENSION_RE, EMPTY);
|
|
172
173
|
return path === `${stem}${TEST_SUFFIX}` || path.startsWith(`${stem}${TEST_DIRECTORY}`);
|
|
173
174
|
}))) reject(NAMES.row, row.id);
|