@sabaiway/agent-workflow-kit 5.0.0 → 5.1.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,35 @@ 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
+ ## 5.1.0 — the config learns to carry the flow block before anything writes one (AD-085)
8
+
9
+ **Upgrade this before any flow feature arrives — that ordering IS the release.** The shared
10
+ `docs/ai/orchestration.json` is read by every collaborator's kit, and the validator is strict on
11
+ purpose: an unknown top-level key fails the config load loudly (exit 1) and reddens that machine's
12
+ entire gate matrix. The upcoming orchestration-flow feature adds a `flow` block to that file, so
13
+ the fleet has to tolerate the block before the first writer exists. This release is that
14
+ tolerance, shipped first and alone.
15
+
16
+ - `orchestration.json` may now carry a reserved `"flow"` object whose `"schema"` is the NUMBER
17
+ `1` (`FLOW_SCHEMA_VERSION`, exported — flow-aware releases import it, never re-type it). Every
18
+ other byte of the block is deliberately uninterpreted: nothing in 5.1.0 reads it, nothing
19
+ writes it (the writer arrives with the flow store).
20
+ - Refusals stay loud and name the contract: a non-object `flow`, an absent or unknown `schema`,
21
+ and the STRING form `"1"` each fail with the accepted version in the message; every other
22
+ unknown top-level key refuses exactly as before.
23
+ - No writer change was needed for safety: `set-recipe` runs and the `_README` refresh carry a
24
+ present `flow` block through JSON-value-equal (content-preserving, not byte-preserving —
25
+ characterized for no-op runs, real slot changes, and all three README-refresh arms).
26
+ - Honest limit, doc-parity-bound so it cannot be reworded away: this release enforces NO version
27
+ floor against a pre-flow reader — a kit older than 5.1.0 that meets a `flow` block still fails
28
+ loudly. Tolerate-first ordering is the only mitigation until enforcement arms on the
29
+ `set-flow` path (a later release); `references/modes/procedures.md` states both halves on its
30
+ contract lines, pinned by two new doc-parity bindings.
31
+ - Groundwork pinned green: the floor mechanics the rollout leans on (the four `semver-lite`
32
+ consumers, including the installer's never-downgrade lane) are characterized, and the
33
+ characterization surfaced a `null >= 0` coercion trap the future arming floor must guard —
34
+ queued, with the guarded comparison shape documented in the test.
35
+
7
36
  ## 5.0.0 — the deployed rotation gates fail closed (memory 4.0.0 mirrored; AD-084)
8
37
 
9
38
  > ### ⚠ BREAKING — inherited from memory 4.0.0
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: '5.0.0'
6
+ version: '5.1.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": "5.0.0",
6
+ "version": "5.1.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": "5.0.0",
3
+ "version": "5.1.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",
@@ -11,9 +11,9 @@ The two v1 activities (canon in the **installed engine**, `references/procedures
11
11
 
12
12
  Run **`node ${CLAUDE_SKILL_DIR}/tools/procedures.mjs <activity> [--override <slot>=<recipe>]… [--json]`**. It reads the activity's steps live from the engine and prints them **verbatim**, then the **resolved effective recipe per slot** from the per-project config + the read-only backend detector:
13
13
 
14
- 1. **Config = `docs/ai/orchestration.json`** — strict JSON, **agent-writable via `/agent-workflow-kit set-recipe` (`${CLAUDE_SKILL_DIR}/references/modes/set-recipe.md`) OR hand-edited** (the kit reads + validates it; `procedures`/`recipes` stay read-only — the writer is `set-recipe`). Shape: `{ "<activity>": { "<slot>": "<recipe>" } }`; all slots optional (an absent slot → its computed default, stated); an optional `"_README"` string is allowed + ignored. `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated`. Seeded by `init` (a user-editable template) — see `${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`.
14
+ 1. **Config = `docs/ai/orchestration.json`** — strict JSON, **agent-writable via `/agent-workflow-kit set-recipe` (`${CLAUDE_SKILL_DIR}/references/modes/set-recipe.md`) OR hand-edited** (the kit reads + validates it; `procedures`/`recipes` stay read-only — the writer is `set-recipe`). Shape: `{ "<activity>": { "<slot>": "<recipe>" } }`; all slots optional (an absent slot → its computed default, stated); an optional `"_README"` string is allowed + ignored; a reserved `"flow"` object is tolerated + carried uninterpreted when it carries the NUMERIC `"schema": 1` (the kit's accepted flow schema version — any other form or value refuses loudly). `review` accepts `solo|reviewed|council`; `execute` accepts `solo|delegated`. Seeded by `init` (a user-editable template) — see `${CLAUDE_SKILL_DIR}/references/modes/bootstrap.md`.
15
15
  2. **Default resolution (config silent):** `review` → Reviewed if any review-capable backend is `ready`, else Solo (never Council by default); `execute` → Solo (Delegated is opt-in). **Degradation:** a config/computed default degrades **gracefully with a stated reason** (Council → Reviewed → Solo; Delegated → Solo); a per-run **`--override <slot>=<recipe>`** that can't be satisfied degrades **loudly** (a flagged warning, so you tell the user) — but is **still exit 0** (a valid request that gracefully degraded).
16
- 3. **Exit codes:** `0` success; `2` usage (unknown `<activity>` / bad `--override` — a bare `--override <recipe>`, an unknown slot, an invalid recipe-for-slot, or a duplicate slot); `1` config error (malformed / schema-invalid / unreadable `orchestration.json`) **or** engine error (the installed engine is absent / invalid / **too old** to ship `references/procedures.md` — upgrade it with `npx @sabaiway/agent-workflow-engine@latest init`). A `1`/`2` failure is loud (`path: reason`), never a silent fallback.
16
+ 3. **Exit codes:** `0` success; `2` usage (unknown `<activity>` / bad `--override` — a bare `--override <recipe>`, an unknown slot, an invalid recipe-for-slot, or a duplicate slot); `1` config error (malformed / schema-invalid / unreadable `orchestration.json`) **or** engine error (the installed engine is absent / invalid / **too old** to ship `references/procedures.md` — upgrade it with `npx @sabaiway/agent-workflow-engine@latest init`). A `1`/`2` failure is loud (`path: reason`), never a silent fallback. **Lagging-kit honesty:** a kit predating the `"flow"` key that reads a config carrying one fails this config load loudly (exit `1`, reddening its full gate matrix); this kit release enforces NO version floor against such a pre-flow reader — tolerate-first ordering is the only mitigation until a flow-aware release arms enforcement on the `set-flow` path.
17
17
 
18
18
  **Cap-soft-skip degradation (the feature's only AUTO route).** The activity procedures are auto-discoverable only through the one-line **`workflow:methodology`** pointer (this kit + the engine carry `disable-model-invocation:true`, so NL like "write a plan" does **not** auto-load this skill). On a deployment whose methodology pointer was cap-soft-skipped — or whose pre-existing customized pointer lacks the procedures clause — the procedures are still reachable by **explicitly** invoking `/agent-workflow-kit procedures`; surface that plainly rather than treating it as a gap.
19
19
 
@@ -35,6 +35,9 @@ import {
35
35
  import { SKIPPED_READONLY } from './setup-backends.mjs';
36
36
  import { LATENT_ARM_NOTICE } from './review-state.mjs';
37
37
  import { QUEUE_SHARED_RULE, LANDING_FROM_MAIN, NO_DEPENDENCIES_POSTURE, CLEANUP_OWNERSHIP_RULE, INCLUDE_IDENTITY_RULE, RESUME_VERIFY_RULE } from './worktrees.mjs';
38
+ // The flow tolerate contract (tolerate-only release): the accepted schema version + the honest
39
+ // lagging-kit sentence, both owned by the config validator and pinned into procedures.md.
40
+ import { FLOW_SCHEMA_VERSION, FLOW_LAGGING_KIT_CONTRACT } from './orchestration-config.mjs';
38
41
 
39
42
  const KIT_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
40
43
 
@@ -45,6 +48,7 @@ const VELOCITY_DOC = 'references/modes/velocity.md';
45
48
  const SETUP_DOC = 'references/modes/setup.md';
46
49
  const REVIEW_STATE_DOC = 'references/modes/review-state.md';
47
50
  const WORKTREES_DOC = 'references/modes/worktrees.md';
51
+ const PROCEDURES_DOC = 'references/modes/procedures.md';
48
52
 
49
53
  // A typed usage failure (exit 2) for the CLI parser — the codebase's typed-error idiom (no classes).
50
54
  const usageFail = (message) => Object.assign(new Error(message), { exitCode: 2 });
@@ -116,6 +120,13 @@ export const BINDINGS = Object.freeze([
116
120
  // a reworded mode doc dropping the per-owned-path × session-never-probed contract fails this pin
117
121
  // plus the gate.
118
122
  valueBinding('resume-verify-rule', RESUME_VERIFY_RULE, RESUME_VERIFY_RULE, [WORKTREES_DOC]),
123
+ // The flow tolerate contract: (a) the accepted NUMERIC `flow` schema version renders into the
124
+ // procedures.md allowed-shape contract line — a bumped constant with an unchanged doc fails this
125
+ // pin plus the gate; (b) the honest lagging-kit sentence — what a pre-flow kit does on meeting a
126
+ // `flow` block, and that this release enforces nothing against such a reader — renders as the
127
+ // exact exported sentence into the exit-1 contract line, so the admission cannot be reworded away.
128
+ valueBinding('flow-schema-version', FLOW_SCHEMA_VERSION, `\`"schema": ${FLOW_SCHEMA_VERSION}\``, [PROCEDURES_DOC]),
129
+ valueBinding('flow-lagging-kit', FLOW_LAGGING_KIT_CONTRACT, FLOW_LAGGING_KIT_CONTRACT, [PROCEDURES_DOC]),
119
130
  ].map((b) => Object.freeze(b)));
120
131
 
121
132
  // ── the pure checker (readText is injectable for hermetic tests) ────────────────────────
@@ -164,8 +175,9 @@ table, the status tokens, the trusted-dir allowlist), the recommendations/upgrad
164
175
  contract (section header, empty line, verdict templates), the acks-store path, the setup refresh
165
176
  degrade token, the review-state clean-tree latent-arm notice, the worktrees provision-record
166
177
  orientation contract (shared-queue rule, landing-from-main, no-dependencies install posture), the
167
- worktrees cleanup-ownership rule, the worktrees include-identity rule, and the worktrees
168
- resume-verify rule to
178
+ worktrees cleanup-ownership rule, the worktrees include-identity rule, the worktrees
179
+ resume-verify rule, and the flow tolerate contract (the accepted flow schema version + the
180
+ lagging-kit sentence, procedures.md) — to
169
181
  the exact token its references/modes/*.md contract must carry, and
170
182
  asserts the CURRENT value renders into every bound file. A drifted doc, an unreadable bound file,
171
183
  or an absent token FAILS CLOSED.
@@ -113,10 +113,24 @@ export const parseOp = (kind, token) => {
113
113
 
114
114
  // ── config validation (config errors → exit 1) ──────────────────────────────────────
115
115
 
116
+ // The accepted `flow` schema version — the SINGLE source both the acceptance check and the refusal
117
+ // message use; future flow-aware releases IMPORT this constant, never re-type it. The wire value is
118
+ // pinned NUMERIC (the string form is a named refusal case).
119
+ export const FLOW_SCHEMA_VERSION = 1;
120
+
121
+ // The honest lagging-kit contract sentence (tolerate-first ordering): what a kit WITHOUT the flow
122
+ // branch does when it meets a `flow` block, and that this release enforces nothing against such a
123
+ // reader. doc-parity binds it VERBATIM into the procedures mode doc's exit-1 contract line, so the
124
+ // doc can never soften the admission while the gap is real (enforcement arms with `set-flow`).
125
+ export const FLOW_LAGGING_KIT_CONTRACT =
126
+ 'a kit predating the `"flow"` key that reads a config carrying one fails this config load loudly (exit `1`, reddening its full gate matrix); this kit release enforces NO version floor against such a pre-flow reader — tolerate-first ordering is the only mitigation until a flow-aware release arms enforcement on the `set-flow` path';
127
+
116
128
  // Validate a parsed orchestration.json object against the schema. Strict: an unknown top-level
117
129
  // activity, an unknown slot for an activity, or a recipe invalid-for-slot is an error. All slots are
118
- // optional. An optional "_README" string key is allowed + ignored (self-documentation). Never a silent
119
- // fallback every rejection is a loud `path: reason` (exit 1). Returns the config on success.
130
+ // optional. An optional "_README" string key is allowed + ignored (self-documentation). A versioned
131
+ // "flow" object key is TOLERATED when its `schema` strict-equals FLOW_SCHEMA_VERSION every other
132
+ // byte of the block is deliberately uninterpreted here (nothing in this kit reads or writes it yet).
133
+ // Never a silent fallback — every rejection is a loud `path: reason` (exit 1). Returns the config on success.
120
134
  export const validateConfig = (config) => {
121
135
  if (config === null || typeof config !== 'object' || Array.isArray(config)) {
122
136
  throw fail(1, `${CONFIG_REL}: must be a JSON object of activity → { slot: recipe }`);
@@ -126,6 +140,16 @@ export const validateConfig = (config) => {
126
140
  if (typeof val !== 'string') throw fail(1, `${CONFIG_REL}: "_README" must be a string`);
127
141
  continue;
128
142
  }
143
+ if (key === 'flow') {
144
+ if (val === null || typeof val !== 'object' || Array.isArray(val)) {
145
+ throw fail(1, `${CONFIG_REL}: "flow" must be a JSON object carrying { "schema": ${FLOW_SCHEMA_VERSION} }`);
146
+ }
147
+ if (val.schema !== FLOW_SCHEMA_VERSION) {
148
+ const got = 'schema' in val ? JSON.stringify(val.schema) : 'absent';
149
+ throw fail(1, `${CONFIG_REL}: "flow".schema must be the number ${FLOW_SCHEMA_VERSION} (got ${got})`);
150
+ }
151
+ continue;
152
+ }
129
153
  const activityDef = ACTIVITIES[key];
130
154
  if (!activityDef) {
131
155
  throw fail(1, `${CONFIG_REL}: unknown activity "${key}" (known: ${KNOWN_ACTIVITIES()})`);