@sabaiway/agent-workflow-kit 3.14.0 → 3.15.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 CHANGED
@@ -4,6 +4,29 @@ 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
+ ## 3.15.0 — a shipped opt-in now advertises itself (AD-076)
8
+
9
+ `/agent-workflow-kit upgrade` and `recommendations` now offer the closing-block detector that 3.14.0
10
+ shipped.
11
+
12
+ They did not, and that was the whole defect. 3.14.0 added the detector with a mode doc, a catalog row
13
+ and a README row — every surface an agent reads — and no advisor entry. So a user who installed the
14
+ update and ran `upgrade` was told «nothing is broken — process is optimal» while the capability from
15
+ that very version sat unwired and unmentioned. Both statements were true alone. Together they meant
16
+ the only route to the new feature was reading this file or interrogating the agent.
17
+
18
+ The new item fires when no `Stop` hook runs the detector's runtime, states what goes unseen without
19
+ it, and carries a hand-apply pointer to the mode doc — there is still no writer for this hook, and the
20
+ doc carries the exact block plus the three merge cases. It matches on the runtime FILE NAME rather
21
+ than an exact command, because with no writer every user pastes their own path: an exact comparison
22
+ would keep nagging someone who already wired it.
23
+
24
+ The reason this was structurally invited is worth stating. Every other surface of a new mode is
25
+ drift-guarded — omit the `SKILL.md` row, the catalog entry or the mode doc and a test fails. The
26
+ advisor is the one surface with no such guard, and the only one a user receives without asking. The
27
+ guard that would close it needs an accurate claim for all 28 modes, so it is the next slice's first
28
+ item rather than a hurried addition here.
29
+
7
30
  ## 3.14.0 — the closing state block gets a checker (AD-075)
8
31
 
9
32
  A new opt-in `Stop` hook reads the turn's final assistant message and warns when the closing state
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: '3.14.0'
6
+ version: '3.15.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-kit
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "3.14.0",
6
+ "version": "3.15.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "3.14.0",
3
+ "version": "3.15.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",
@@ -131,7 +131,7 @@ form this family already sanctions. When the detector has earned a writer in rea
131
131
  "hooks": [
132
132
  {
133
133
  "type": "command",
134
- "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/state-block-guard.mjs\" --require-block",
134
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/state-block-guard.mjs\"",
135
135
  "timeout": 10
136
136
  }
137
137
  ]
@@ -141,10 +141,12 @@ form this family already sanctions. When the detector has earned a writer in rea
141
141
  }
142
142
  ```
143
143
 
144
- Drop `--require-block` if your project has not adopted the three-part closing block and you only want
145
- the two lying-slot checks. `--require-block` is the **only** argument accepted: an unrecognised one
146
- makes the guard refuse to judge the turn and say so, rather than quietly running in the weaker mode
147
- you did not choose.
144
+ **Add `--require-block` to that command IF your project requires the three-part block on every
145
+ message** then a turn that drops the block entirely is reported too. It is left OFF above on
146
+ purpose: this kit does not mandate the block, so switching it on for a project that never adopted it
147
+ would warn after nearly every turn. `--require-block` is the **only** argument accepted: an
148
+ unrecognised one makes the guard refuse to judge the turn and say so, rather than quietly running in
149
+ the weaker mode you did not choose.
148
150
 
149
151
  A `Stop` hook is read at session start, so it becomes live in the **next** session, not the current
150
152
  one.
@@ -104,6 +104,7 @@ export const SEVERITIES = Object.freeze({
104
104
  'read-lane': SEVERITY_OPTIONAL,
105
105
  'read-lane.stale': SEVERITY_ATTENTION,
106
106
  'read-lane.missing': SEVERITY_ATTENTION,
107
+ 'state-block': SEVERITY_OPTIONAL,
107
108
  'family-freshness': SEVERITY_ATTENTION,
108
109
  'sandbox-masks': SEVERITY_OPTIONAL,
109
110
  'agy-adddir': SEVERITY_OPTIONAL,
@@ -161,6 +162,7 @@ export const WHATS = Object.freeze({
161
162
  'read-lane': 'the gate hook is wired but the read-only compound lane is off — pipes/chains of seeded reads still prompt one by one',
162
163
  'read-lane.stale': 'the read-lane is ON but the placed gate hook is stale — an old hook never reads lanes.json, so the lane is silently dark; reseed it',
163
164
  'read-lane.missing': 'the gate hook is wired but its placed file is missing — every Bash call errors and the read-lane is dark; re-place it',
165
+ 'state-block': 'nothing checks the closing state block — a turn that ends on «nothing needed from you», or on a promise it never started, passes unseen',
164
166
  'family-freshness': '{parts}',
165
167
  'sandbox-masks': '{n} sandbox device mask(s) clutter git status — the managed exclude block is absent or stale',
166
168
  'sandbox-masks.stale-real': '{n} sandbox device mask(s) clutter git status — the exclude block is stale; {m} fenced entr(ies) are REAL paths (a fresh apply drops them)',
@@ -214,6 +216,7 @@ export const BENEFITS = Object.freeze({
214
216
  'gate-hook': 'velocity — your own declared gate commands auto-approve byte-exactly (opt-in PreToolUse hook)',
215
217
  'commit-guard': 'integrity — commits require the ONE green --final receipt at the exact staged fingerprint (consented pre-commit arm)',
216
218
  'read-lane': 'velocity — pipes/chains of your seeded read-only commands auto-approve instead of prompting (opt-in, conservatively classified)',
219
+ 'state-block': 'no silent stalls — a turn ending on «you are not needed», or on work it never started, warns at once instead of waiting to be spotted',
217
220
  'family-freshness': 'currency — placed family members carry the latest shipped fixes and features',
218
221
  'sandbox-masks': 'zero clutter — git status shows only your changes (the review domain already ignores the masks by construction)',
219
222
  'agy-adddir': 'large reviews — an oversized agy code review offloads to a staging dir instead of refusing',
@@ -439,6 +442,26 @@ const probeReadLane = ({ root, deps, add, skip }) => {
439
442
  }
440
443
  };
441
444
 
445
+ // The state-block-guard offer (AD-075 · OPT-IN-SHIPS-INVISIBLE). This item exists because its absence
446
+ // fired: 3.14.0 shipped the detector with a mode doc, a catalog row and a README row — and no advisor
447
+ // entry — so `upgrade` reported «nothing is broken» to a user who did not have it. There is no writer
448
+ // for this hook, so the apply is a HAND-APPLY pointer at the mode doc, which carries the exact block
449
+ // and the three merge cases.
450
+ const probeStateBlockHook = ({ root, deps, add, skip }) => {
451
+ try {
452
+ const project = readSettingsFile(join(root, SETTINGS_FILE), { ...deps, cwd: root });
453
+ const local = readSettingsFile(join(root, SETTINGS_LOCAL_FILE), { ...deps, cwd: root });
454
+ if (isStateBlockGuardWired(project.data) || isStateBlockGuardWired(local.data)) return; // converged
455
+ add(
456
+ 'state-block',
457
+ fillTemplate(WHATS['state-block'], {}),
458
+ `HAND-APPLY: add a Stop hook running ${STATE_BLOCK_HOOK_COMMAND} to ${SETTINGS_FILE} — the exact block and the three merge cases are in references/modes/state-block-guard.md`,
459
+ );
460
+ } catch (err) {
461
+ skip('state-block', err);
462
+ }
463
+ };
464
+
442
465
  const probeFamilyFreshness = ({ deps, add, skip }) => {
443
466
  try {
444
467
  const survey = deps.surveyFamily ?? surveyFamily;
@@ -617,6 +640,29 @@ export const READ_LANE_KEY = 'readLane';
617
640
  const GATE_HOOK_REL = '.claude/hooks/agent-workflow-gates.mjs';
618
641
  const BUNDLED_HOOK_ABS = join(HERE, '..', 'references', 'hooks', 'gate-approve.mjs');
619
642
 
643
+ // The state-block-guard wiring the advisor looks for. Matched on the RUNTIME FILE NAME inside the
644
+ // command, not on an exact string: the hook has no writer, so every user pastes their own path —
645
+ // a copy under `.claude/hooks/`, a kit-source path, `--require-block` or not. Any Stop entry that
646
+ // runs this runtime counts as wired, which is the honest question ("is it watching?").
647
+ export const STATE_BLOCK_HOOK_RUNTIME = 'state-block-guard.mjs';
648
+ // NO `--require-block` in the offered command. That flag turns on the absent-block report, and this
649
+ // kit does not mandate the three-part closing block — recommending it to every project would hand
650
+ // them a hook that warns after nearly every turn. The mode doc explains when to add it. (Offering the
651
+ // strict flag by default would also contradict the very reason the report was made opt-in.)
652
+ export const STATE_BLOCK_HOOK_COMMAND = `node "$CLAUDE_PROJECT_DIR/.claude/hooks/${STATE_BLOCK_HOOK_RUNTIME}"`;
653
+ const runsStateBlockGuard = (hook) => typeof hook?.command === 'string' && hook.command.includes(STATE_BLOCK_HOOK_RUNTIME);
654
+ // Both entry shapes count. The matcher-group form (an object carrying its own `hooks` array) is what
655
+ // this family ships and what was observed firing; a flat entry is accepted too, because the question
656
+ // this probe answers is "is anything watching?" — and guessing NO for a wiring that works would nag
657
+ // someone who already did the work.
658
+ const isStateBlockGuardWired = (data) => {
659
+ const entries = data?.hooks?.Stop;
660
+ if (!Array.isArray(entries)) return false;
661
+ return entries.some((entry) => (Array.isArray(entry?.hooks)
662
+ ? entry.hooks.some(runsStateBlockGuard)
663
+ : runsStateBlockGuard(entry)));
664
+ };
665
+
620
666
  // Byte-compare the placed gate hook against the bundled runtime. A read error (an unreadable placed
621
667
  // hook, a broken kit bundle) propagates → the probe states a skip, never a wrong currency verdict.
622
668
  const isPlacedHookCurrent = (root, deps) => {
@@ -879,6 +925,7 @@ const PROBES = Object.freeze([
879
925
  probeGates,
880
926
  probeCommitGuard,
881
927
  probeReadLane,
928
+ probeStateBlockHook,
882
929
  probeFamilyFreshness,
883
930
  probeMasksItem,
884
931
  probeAgyAdddir,