@sabaiway/agent-workflow-kit 5.11.1 → 6.0.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 +96 -0
- package/README.md +2 -2
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/modes/grounding.md +1 -1
- package/references/modes/procedures.md +3 -3
- package/references/templates/agent_rules.md +4 -5
- package/tools/dispatch-record.mjs +1 -1
- package/tools/flow-finding-manifest.mjs +70 -0
- package/tools/flow-legality.mjs +248 -0
- package/tools/flow-record-identity.mjs +115 -0
- package/tools/flow-record-shape.mjs +283 -0
- package/tools/flow-record.mjs +49 -789
- package/tools/flow-vocabulary.mjs +96 -0
- package/tools/grounding.mjs +10 -20
- package/tools/inject-methodology.mjs +2 -0
- package/tools/procedures.mjs +7 -8
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// flow-vocabulary.mjs — the CLOSED flow-record vocabulary (kinds, purposes, terminal lanes, the
|
|
2
|
+
// design seed assignment, the allowed-transition table) plus the five shared form bindings every
|
|
3
|
+
// validator states its refusals in: HEX64_RE, isPlainObject, isNonEmptyString, isHex64 and refuse.
|
|
4
|
+
// Split out of flow-record.mjs unchanged (baseline-practices tranche 3), which now re-exports the
|
|
5
|
+
// ten public names here; the five shared bindings stay OFF that surface (plan D3) — the leaves that
|
|
6
|
+
// need them import this module, so the record family's named grammars have exactly one home and no
|
|
7
|
+
// copy can drift.
|
|
8
|
+
//
|
|
9
|
+
// The LOWEST leaf of the family and pure form: no filesystem, no git, no CLI, no side effects on
|
|
10
|
+
// import, and orchestration-config.mjs (the schema version) is its only tools sibling. Imports run
|
|
11
|
+
// ONE way — the shape, identity, legality and manifest leaves compose this module; nothing here
|
|
12
|
+
// reaches back up to the facade.
|
|
13
|
+
|
|
14
|
+
import { FLOW_SCHEMA_VERSION } from './orchestration-config.mjs';
|
|
15
|
+
|
|
16
|
+
export { FLOW_SCHEMA_VERSION };
|
|
17
|
+
|
|
18
|
+
const deepFreeze = (value) => {
|
|
19
|
+
if (value !== null && typeof value === 'object') {
|
|
20
|
+
Object.values(value).forEach(deepFreeze);
|
|
21
|
+
Object.freeze(value);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ── the closed vocabulary ─────────────────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
export const CHAIN_KIND = 'chain';
|
|
29
|
+
export const CHAIN_PURPOSES = deepFreeze(['adoption', 'round', 'refresh', 're-baseline', 'freeze', 'unfreeze', 'park', 'resume', 'converged', 'complete']);
|
|
30
|
+
export const STEP_SCOPED_PURPOSES = deepFreeze(['round', 'refresh', 're-baseline', 'freeze', 'unfreeze', 'converged']);
|
|
31
|
+
export const PLAN_LANE_PURPOSES = deepFreeze(['adoption', 'park', 'resume', 'complete']);
|
|
32
|
+
export const GLOBAL_KINDS = deepFreeze(['internal-attestation', 'down-mark', 'down-mark-up', 'down-mark-clear', 'degrade-justification', 'rerun-cause', 'bookkeeping-delta', 'maintainer-override', 'consult-attestation', 'subset-attempt']);
|
|
33
|
+
export const FLOW_KINDS = deepFreeze([CHAIN_KIND, ...GLOBAL_KINDS]);
|
|
34
|
+
|
|
35
|
+
// Reserved lane-typed terminals (#16): converged terminates a CYCLE (its step's sequence), complete
|
|
36
|
+
// terminates the PLAN. Park is a resumable suspension, never a terminal (#59).
|
|
37
|
+
export const TERMINAL_LANES = deepFreeze({ converged: 'cycle', complete: 'plan' });
|
|
38
|
+
|
|
39
|
+
// Design §5 seed member → family assignment; the drift-guard test binds this map to the verbatim
|
|
40
|
+
// seed list on one side and to the shipped CHAIN_PURPOSES/GLOBAL_KINDS on the other.
|
|
41
|
+
export const DESIGN_SEED_ASSIGNMENT = deepFreeze({
|
|
42
|
+
adoption: { family: 'chain', purpose: 'adoption' },
|
|
43
|
+
'round-chain': { family: 'chain', purpose: 'round' },
|
|
44
|
+
refresh: { family: 'chain', purpose: 'refresh' },
|
|
45
|
+
're-baseline': { family: 'chain', purpose: 're-baseline' },
|
|
46
|
+
unfreeze: { family: 'chain', purpose: 'unfreeze' },
|
|
47
|
+
freeze: { family: 'chain', purpose: 'freeze' },
|
|
48
|
+
converged: { family: 'chain', purpose: 'converged' },
|
|
49
|
+
park: { family: 'chain', purpose: 'park' },
|
|
50
|
+
resume: { family: 'chain', purpose: 'resume' },
|
|
51
|
+
complete: { family: 'chain', purpose: 'complete' },
|
|
52
|
+
'internal-attestation': { family: 'global', kind: 'internal-attestation' },
|
|
53
|
+
'down-mark': { family: 'global', kind: 'down-mark' },
|
|
54
|
+
'down-mark up': { family: 'global', kind: 'down-mark-up' },
|
|
55
|
+
'down-mark clear': { family: 'global', kind: 'down-mark-clear' },
|
|
56
|
+
'degrade-justification': { family: 'global', kind: 'degrade-justification' },
|
|
57
|
+
'rerun-cause': { family: 'global', kind: 'rerun-cause' },
|
|
58
|
+
'bookkeeping-delta': { family: 'global', kind: 'bookkeeping-delta' },
|
|
59
|
+
'maintainer-override': { family: 'global', kind: 'maintainer-override' },
|
|
60
|
+
'consult-attestation': { family: 'global', kind: 'consult-attestation' },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// The allowed-transition table — an exported frozen structure, never prose. Within a step:
|
|
64
|
+
// converged ends the sequence (only the unfreeze lane reopens it, and only in its own cycle);
|
|
65
|
+
// freeze admits only unfreeze/converged. Plan lane: park admits only resume (and both preserve the
|
|
66
|
+
// pre-park cycle/round); complete admits nothing; adoption is only ever the chain's first record.
|
|
67
|
+
// The boundary lane (between steps): the opener round, the unfreeze reopen, and a re-baseline for
|
|
68
|
+
// disjoint base motion that reopens nothing. The cross-step edge (the opener's prior-terminal
|
|
69
|
+
// reference) is enforced by validateChainSequence, distinct from the within-step successor rule.
|
|
70
|
+
export const ALLOWED_TRANSITIONS = deepFreeze({
|
|
71
|
+
stepOpening: 'round',
|
|
72
|
+
withinStep: {
|
|
73
|
+
round: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
74
|
+
refresh: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
75
|
+
're-baseline': ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
76
|
+
freeze: ['unfreeze', 'converged'],
|
|
77
|
+
unfreeze: ['round', 'refresh', 're-baseline', 'freeze', 'converged'],
|
|
78
|
+
converged: ['unfreeze'],
|
|
79
|
+
},
|
|
80
|
+
planLane: {
|
|
81
|
+
adoption: ['round', 'park', 'complete'],
|
|
82
|
+
park: ['resume'],
|
|
83
|
+
resume: ['round', 'park', 'complete'],
|
|
84
|
+
complete: [],
|
|
85
|
+
},
|
|
86
|
+
boundary: ['round', 'unfreeze', 're-baseline'],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// ── the shared form bindings (D3) — the record family's named grammars, off the public surface ────
|
|
90
|
+
|
|
91
|
+
export const HEX64_RE = /^[0-9a-f]{64}$/;
|
|
92
|
+
export const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
93
|
+
export const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;
|
|
94
|
+
export const isHex64 = (v) => typeof v === 'string' && HEX64_RE.test(v);
|
|
95
|
+
|
|
96
|
+
export const refuse = (reason) => ({ ok: false, reason });
|
package/tools/grounding.mjs
CHANGED
|
@@ -6,11 +6,9 @@
|
|
|
6
6
|
//
|
|
7
7
|
// --constraints slice the root AGENTS.md `## 🚫 Hard Constraints` section, verbatim
|
|
8
8
|
// (exactly-one-match — 0 or >1 headings is a loud STOP, never a guess);
|
|
9
|
-
// --plan <path> extract the plan's
|
|
10
|
-
// `##
|
|
11
|
-
//
|
|
12
|
-
// missing), plus `## Decisions (locked)` (optional-if-absent, the engine §7
|
|
13
|
-
// heading this release adds); a DUPLICATE heading is always a STOP.
|
|
9
|
+
// --plan <path> extract the plan's three canon sections, verbatim + whole — `## Goal and
|
|
10
|
+
// boundary`, `## Module ledger`, `## Verification` (the engine planning.md
|
|
11
|
+
// literal headings; each REQUIRED — a missing or DUPLICATE heading is a STOP).
|
|
14
12
|
// --extra <text|@file> append orchestrator-supplied facts verbatim AFTER the mechanical halves
|
|
15
13
|
// (repeatable; @file reads are confined to the work tree + the system temp
|
|
16
14
|
// surface — the merge happens INSIDE the tool, corpus #88/#95).
|
|
@@ -46,18 +44,14 @@ export const DEFAULT_MAX_PROMPT_BYTES = 120000;
|
|
|
46
44
|
export const ARGV_HARD_MAX = 131000;
|
|
47
45
|
|
|
48
46
|
export const CONSTRAINTS_HEADING = /^## .*Hard Constraints$/;
|
|
49
|
-
export const PLAN_SECTIONS = [
|
|
50
|
-
{ heading: '## Approach', optional: false },
|
|
51
|
-
{ heading: '## Verification', optional: false },
|
|
52
|
-
{ heading: '## Decisions (locked)', optional: true },
|
|
53
|
-
];
|
|
47
|
+
export const PLAN_SECTIONS = ['## Goal and boundary', '## Module ledger', '## Verification'];
|
|
54
48
|
|
|
55
49
|
// ── pure section slicing (exactly-one-match; the inject-methodology discipline) ────────
|
|
56
50
|
|
|
57
51
|
// Slice ONE `## `-level section (its heading line through the line before the next `## ` heading),
|
|
58
52
|
// verbatim. `heading` is a string (trimmed-line equality) or a RegExp over the trimmed line.
|
|
59
|
-
// 0 matches →
|
|
60
|
-
export const sliceSection = (text, heading, {
|
|
53
|
+
// 0 matches → STOP; >1 matches → STOP (never guess which).
|
|
54
|
+
export const sliceSection = (text, heading, { label = 'document' } = {}) => {
|
|
61
55
|
const lines = text.split('\n');
|
|
62
56
|
const matchesAt = [];
|
|
63
57
|
for (let i = 0; i < lines.length; i += 1) {
|
|
@@ -66,7 +60,6 @@ export const sliceSection = (text, heading, { optional = false, label = 'documen
|
|
|
66
60
|
}
|
|
67
61
|
const shown = typeof heading === 'string' ? heading : String(heading);
|
|
68
62
|
if (matchesAt.length === 0) {
|
|
69
|
-
if (optional) return null;
|
|
70
63
|
throw fail(1, `${label}: required section "${shown}" not found — STOP (nothing sliced)`);
|
|
71
64
|
}
|
|
72
65
|
if (matchesAt.length > 1) {
|
|
@@ -95,10 +88,7 @@ export const assembleGrounding = ({ constraintsText = null, autonomyText = null,
|
|
|
95
88
|
// how autonomous this session is → what this plan decides).
|
|
96
89
|
if (autonomyText != null) parts.push(autonomyText);
|
|
97
90
|
if (planText != null) {
|
|
98
|
-
for (const
|
|
99
|
-
const section = sliceSection(planText, heading, { optional, label: planLabel });
|
|
100
|
-
if (section != null) parts.push(section);
|
|
101
|
-
}
|
|
91
|
+
for (const heading of PLAN_SECTIONS) parts.push(sliceSection(planText, heading, { label: planLabel }));
|
|
102
92
|
}
|
|
103
93
|
// Orchestrator extras ride LAST, verbatim in argv order — live judgment facts read after the
|
|
104
94
|
// mechanical slices, and the merge happens INSIDE the tool (corpus #88/#95: a shell append onto
|
|
@@ -268,9 +258,9 @@ Usage:
|
|
|
268
258
|
stated source line; absent file → the computed defaults ARE the policy
|
|
269
259
|
(exit 0); malformed/unreadable → fail-closed STOP (exit 1); informational —
|
|
270
260
|
enforcement stays the sandbox + the orchestrator
|
|
271
|
-
--plan <path> extract the plan's
|
|
272
|
-
"
|
|
273
|
-
|
|
261
|
+
--plan <path> extract the plan's three canon sections verbatim + whole: "## Goal and
|
|
262
|
+
boundary", "## Module ledger", "## Verification" (each REQUIRED — a
|
|
263
|
+
missing or duplicate heading is a STOP)
|
|
274
264
|
--extra <text|@file> append orchestrator-supplied extra facts byte-verbatim AFTER the
|
|
275
265
|
mechanical sections (repeatable, argv order; the agy-review --facts
|
|
276
266
|
convention: literal text, or @path read whole through a race-free
|
|
@@ -84,6 +84,8 @@ export const AUTONOMY_EMPTY_SLOT = `${AUTONOMY_START_MARKER}\n${AUTONOMY_END_MAR
|
|
|
84
84
|
export const KNOWN_PRIOR_METHODOLOGY_SLOT = [
|
|
85
85
|
// v1.3.0 — pre-communication-contract methodology pointer (procedures route, no §1.9 clause).
|
|
86
86
|
'> **Workflow methodology** — plan → execute → review. Plans are ephemeral `docs/plans/*.md` (gitignored, **never committed**); every Plan ends with a mandatory **Phase: Cleanup**; series order lives in `docs/plans/queue.md`. Full vocabulary, lifecycle, and the plan-then-execute split live in the project\'s **planning skill** (it overrides the generic `writing-plans`); summary in `docs/ai/agent_rules.md` §5. Named activities (plan-authoring, plan-execution) have procedures — see `/agent-workflow-kit procedures <activity>` for the steps + resolved recipe.',
|
|
87
|
+
// engine 2.1.0 — the pre-canon-rewrite pointer (vocabulary + plan-then-execute wording, with the communication contract).
|
|
88
|
+
'> **Workflow methodology** — plan → execute → review. Plans are ephemeral `docs/plans/*.md` (gitignored, **never committed**); every Plan ends with a mandatory **Phase: Cleanup**; series order lives in `docs/plans/queue.md`. Full vocabulary, lifecycle, and the plan-then-execute split live in the project\'s **planning skill** (it overrides the generic `writing-plans`); summary in `docs/ai/agent_rules.md` §5. Named activities (plan-authoring, plan-execution) have procedures — see `/agent-workflow-kit procedures <activity>` for the steps + resolved recipe. **Communication:** user-facing messages deliver the artifact inline (paste the prompt / diff / command — never "see §X" as a substitute), lead with the result, show exactly what was asked, and never read as mockery (a large artifact: a real summary inline + a link).',
|
|
87
89
|
];
|
|
88
90
|
export const KNOWN_PRIOR_ORCH_SLOT = [
|
|
89
91
|
// v1.3.0 — pre-read-at-start orchestration pointer (recipes vocabulary, no orchestration.json clause).
|
package/tools/procedures.mjs
CHANGED
|
@@ -208,8 +208,8 @@ const backendSetLabel = (backends) =>
|
|
|
208
208
|
: ` → ${backends[0]}`;
|
|
209
209
|
|
|
210
210
|
// The review-loop economics block (M1 + M6's firing half) — printed when the activity engages a review
|
|
211
|
-
// backend (a slot resolving reviewed | council) and OMITTED for solo. It paraphrases the
|
|
212
|
-
// orchestration §4 canon (no rival rule): the ≤2-round architecture cap, the bar met by RAISING a
|
|
211
|
+
// backend (a slot resolving reviewed | council) and OMITTED for solo. It paraphrases the procedures.md
|
|
212
|
+
// Fold + loop step + orchestration §4 canon (no rival rule): the ≤2-round architecture cap, the bar met by RAISING a
|
|
213
213
|
// surviving major to an acceptance invariant (not exhausting prose), backend divergence = the crossover
|
|
214
214
|
// stop, the thin-plan/diff-review carve-out, a self-consistency read before every re-review, and the
|
|
215
215
|
// REQUIRED per-round structured emission {round N · finding-origin tally · per-backend verdict}. Only a
|
|
@@ -221,7 +221,7 @@ const REVIEW_RECIPES = new Set(['reviewed', 'council']);
|
|
|
221
221
|
const reviewLoopAdvice = (slots, activity) =>
|
|
222
222
|
slots.some((s) => REVIEW_RECIPES.has(s.recipe))
|
|
223
223
|
? [
|
|
224
|
-
'Review-loop economics (
|
|
224
|
+
'Review-loop economics (procedures.md Fold + loop · orchestration.md §4) — the review this recipe runs:',
|
|
225
225
|
' • Cap architecture plan-review at ≤2 rounds; the bar is met by RAISING a surviving major to an acceptance invariant (or handing it to Execute/diff-review), never by exhausting the strictest backend.',
|
|
226
226
|
' • Backend divergence (one backend grounded-ships while another keeps revising mechanics) IS the crossover stop.',
|
|
227
227
|
' • Route an all-mechanics/CI or prose-only artifact to a thin plan + diff-review; run a self-consistency read before every re-review.',
|
|
@@ -299,7 +299,7 @@ const autonomyAdvice = (activity, facts) => {
|
|
|
299
299
|
};
|
|
300
300
|
|
|
301
301
|
// The cost-lane advisory block (cost-tiered execution — orchestration.md §5 canon, paraphrased
|
|
302
|
-
// at the point of use like reviewLoopAdvice paraphrases §
|
|
302
|
+
// at the point of use like reviewLoopAdvice paraphrases procedures.md Fold + loop / orchestration §4). Rendered UNCONDITIONALLY for
|
|
303
303
|
// every activity — the lanes route EVERY step, review-backed or not (unlike reviewLoopAdvice,
|
|
304
304
|
// which fires only when a review backend engages). It may name the kit's own GENERIC L0
|
|
305
305
|
// surfaces (the gate runner, the rotation checks, the cheap-agents vehicles) — point-of-use
|
|
@@ -368,9 +368,9 @@ const flowHalvesAdvice = (flow, probe) => {
|
|
|
368
368
|
};
|
|
369
369
|
|
|
370
370
|
// ── the declared source-size practice (D-17 U1) ────────────────────────────────────
|
|
371
|
-
// A practice the agent meets only when a gate refuses is a practice learned too late: the caps
|
|
372
|
-
// reason
|
|
373
|
-
//
|
|
371
|
+
// A practice the agent meets only when a gate refuses is a practice learned too late: the caps and
|
|
372
|
+
// their reason ride EVERY named-activity render, so the plan's Module ledger is cut to them while the
|
|
373
|
+
// plan is being written. Composed from the project's live declaration, never from constants here.
|
|
374
374
|
// Each config state speaks as itself: ABSENT renders NOTHING (a project that declares no practice must
|
|
375
375
|
// not be handed invented limits); AUTHORED and INCOMPLETE render the declared caps plus the honest
|
|
376
376
|
// "nothing is recorded yet" line — both are pre-mint states, and treating INCOMPLETE as MINTED would
|
|
@@ -406,7 +406,6 @@ const declaredPracticeAdvice = (cwd, readFile, lstat) => {
|
|
|
406
406
|
? ` recorded: ${facts.recordedFiles} file(s) carry a recorded size (debt, not permission) · aggregate ${facts.aggregateLines} line(s), EXACT — growth takes a reasoned bump, never free headroom.`
|
|
407
407
|
: unmintedRecord,
|
|
408
408
|
` why: ${SOURCE_SIZE_WHY}`,
|
|
409
|
-
' at plan time: every Step that CREATES a file names the file and its single responsibility, and the planned layout fits these caps — the gate is the backstop, never the teacher.',
|
|
410
409
|
];
|
|
411
410
|
};
|
|
412
411
|
|