@sabaiway/agent-workflow-kit 5.5.0 → 5.7.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 +122 -0
- package/README.md +1 -1
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/hooks/gate-approve.mjs +7 -1
- package/references/modes/doc-parity.md +1 -1
- package/references/modes/gates.md +20 -4
- package/references/modes/procedures.md +2 -0
- package/references/modes/recommendations.md +4 -1
- package/references/modes/review-state.md +1 -1
- package/references/modes/setup.md +18 -2
- package/references/modes/upgrade.md +38 -18
- package/references/modes/velocity.md +1 -0
- package/references/scripts/migrate-gates-branches.test.mjs +146 -1
- package/references/scripts/migrate-gates.mjs +295 -60
- package/references/scripts/migrate-gates.test.mjs +206 -14
- package/references/shared/deploy-tail.md +1 -1
- package/references/templates/gates.json +1 -1
- package/tools/ack-write.mjs +20 -11
- package/tools/atomic-write.mjs +71 -18
- package/tools/checker-claim.mjs +100 -0
- package/tools/coverage-producer.mjs +43 -6
- package/tools/direct-run.mjs +76 -0
- package/tools/doc-parity.mjs +34 -3
- package/tools/engine-source.mjs +12 -8
- package/tools/ensure-configs.mjs +141 -0
- package/tools/ensure-ops.mjs +284 -0
- package/tools/ensure-vocabulary.mjs +71 -0
- package/tools/flow-check-cores.mjs +253 -0
- package/tools/flow-check-git-lane.mjs +56 -0
- package/tools/flow-check-rungs.mjs +330 -0
- package/tools/flow-check.mjs +23 -611
- package/tools/gates-declaration.mjs +36 -11
- package/tools/gates-init.mjs +140 -25
- package/tools/hide-footprint.mjs +21 -3
- package/tools/lens-region.mjs +74 -23
- package/tools/orchestration-config.mjs +5 -3
- package/tools/orchestration-write.mjs +7 -0
- package/tools/procedures.mjs +64 -5
- package/tools/recommendations.mjs +384 -34
- package/tools/refresh-parity.mjs +263 -0
- package/tools/run-gates.mjs +8 -5
- package/tools/setup-backends.mjs +88 -77
- package/tools/source-size-check.mjs +310 -0
- package/tools/source-size-config.mjs +244 -0
- package/tools/source-size-core.mjs +59 -0
- package/tools/source-size-gate-cmd.mjs +27 -0
- package/tools/source-size-judge.mjs +114 -0
- package/tools/source-size-refusal.mjs +70 -0
- package/tools/source-size-report.mjs +254 -0
- package/tools/source-size-scope.mjs +145 -0
- package/tools/tracked-tree-census.mjs +102 -0
- package/tools/upgrade-runlist.mjs +92 -0
- package/tools/velocity-profile.mjs +24 -3
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
// orchestration-config.mjs — the schema / read / pure-transform core for the per-project
|
|
3
2
|
// orchestration config (docs/ai/orchestration.json). It is the SINGLE source of the config contract:
|
|
4
3
|
//
|
|
@@ -16,12 +15,13 @@
|
|
|
16
15
|
//
|
|
17
16
|
// This module performs NO filesystem WRITES — only reads (loadConfig). The single fs-writer lives in
|
|
18
17
|
// orchestration-write.mjs, which procedures.mjs never imports DIRECTLY (the pinned import-split
|
|
19
|
-
// rule).
|
|
20
|
-
//
|
|
18
|
+
// rule). It has NO CLI while upgrade.md names it — hence the registered refusal at the foot of the
|
|
19
|
+
// file (direct-run.mjs), and no shebang. Fs-injectable, dependency-free, Node >= 22; nothing on import.
|
|
21
20
|
|
|
22
21
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
23
22
|
import { join } from 'node:path';
|
|
24
23
|
import { ACTIVITIES, SLOT_RECIPES } from './recipes.mjs';
|
|
24
|
+
import { refuseDirectRun } from './direct-run.mjs';
|
|
25
25
|
|
|
26
26
|
// The hand-editable / agent-writable, per-project config (strict JSON). cwd-relative — the error prefix
|
|
27
27
|
// uses this rel path so a user sees a path they can open, never an absolute temp/host path.
|
|
@@ -396,3 +396,5 @@ export const refreshReadme = (config) => {
|
|
|
396
396
|
|
|
397
397
|
// The canonical seed file body (what `init` deploys + what serializeConfig round-trips byte-identically).
|
|
398
398
|
export const SEED_CONFIG = { _README: CANON_README, 'plan-authoring': { review: 'solo' }, 'plan-execution': { execute: 'solo', review: 'solo' } };
|
|
399
|
+
|
|
400
|
+
refuseDirectRun(import.meta.url);
|
|
@@ -25,3 +25,10 @@ const stop = (message) => Object.assign(new Error(`[agent-workflow-kit] ${messag
|
|
|
25
25
|
// creation. config is serialized canonically (serializeConfig: 2-space, _README-first, trailing NL).
|
|
26
26
|
export const writeConfig = (cwd, config, deps = {}) =>
|
|
27
27
|
writeDocsAiFileAtomic(cwd, CONFIG_REL, serializeConfig(config), deps, { stop, noun: 'a config' });
|
|
28
|
+
|
|
29
|
+
// seedConfig(cwd, config, deps) → { writtenPath, created }. Same writer, CREATE-ONLY: it is the
|
|
30
|
+
// seed-if-missing arm the ensure CLI runs, where a config that appeared between the probe and the
|
|
31
|
+
// write must survive untouched (`created: false` says it did). writeConfig stays the arm for a
|
|
32
|
+
// content update of a file the caller has just read.
|
|
33
|
+
export const seedConfig = (cwd, config, deps = {}) =>
|
|
34
|
+
writeDocsAiFileAtomic(cwd, CONFIG_REL, serializeConfig(config), deps, { stop, noun: 'a config', createOnly: true });
|
package/tools/procedures.mjs
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
// steps LIVE from the installed agent-workflow-engine (references/procedures.md — AD-016 live read, no
|
|
6
6
|
// bundled mirror), reads the per-project, hand-edited config (docs/ai/orchestration.json), runs the
|
|
7
7
|
// read-only backend detector, and prints the activity's steps VERBATIM + the resolved effective recipe
|
|
8
|
-
// per slot (default = Reviewed-when-a-backend-is-ready, Council on request, slot-aware incl. Delegated)
|
|
8
|
+
// per slot (default = Reviewed-when-a-backend-is-ready, Council on request, slot-aware incl. Delegated),
|
|
9
|
+
// plus the project's DECLARED source-size practice when it declares one (D-17 U1).
|
|
9
10
|
//
|
|
10
11
|
// Invariants (mirror recipes.mjs): pure-where-possible, READ-ONLY (never writes, never commits, never
|
|
11
12
|
// runs a subscription CLI). The deterministic resolution lives in the kit (resolveActivityRecipe), not
|
|
@@ -44,6 +45,10 @@ import { AUTONOMY_REL, loadAutonomy, resolveAutonomy, isSparseSeedConfig } from
|
|
|
44
45
|
// acyclic — pinned by test/read-graph-purity.test.mjs (FLOW-READ-GRAPH-PURITY).
|
|
45
46
|
import { resolveFlowStorePath, readFlowStore } from './flow-store-read.mjs';
|
|
46
47
|
import { CHAIN_KIND } from './flow-record.mjs';
|
|
48
|
+
// The declared source-size practice (D-17 U1), read through the practice's PURE READ core — never
|
|
49
|
+
// source-size-check.mjs, which owns the writer half: this advisor is a read root of
|
|
50
|
+
// test/read-graph-purity.test.mjs, and the core exists so a surface can ask without reaching a writer.
|
|
51
|
+
import { SOURCE_SIZE_CONFIG_REL, SOURCE_SIZE_WHY, loadSourceSizeConfig, practiceFacts } from './source-size-core.mjs';
|
|
47
52
|
export { CONFIG_REL };
|
|
48
53
|
|
|
49
54
|
// ── argument + override parsing (usage errors → exit 2) ─────────────────────────────
|
|
@@ -361,6 +366,49 @@ const flowHalvesAdvice = (flow, probe) => {
|
|
|
361
366
|
];
|
|
362
367
|
};
|
|
363
368
|
|
|
369
|
+
// ── the declared source-size practice (D-17 U1) ────────────────────────────────────
|
|
370
|
+
// A practice the agent meets only when a gate refuses is a practice learned too late: the caps, their
|
|
371
|
+
// reason and the plan-time rung ride EVERY named-activity render, so the layout is cut to them while
|
|
372
|
+
// the plan is being written. Composed from the project's live declaration, never from constants here.
|
|
373
|
+
// Each config state speaks as itself: ABSENT renders NOTHING (a project that declares no practice must
|
|
374
|
+
// not be handed invented limits); AUTHORED and INCOMPLETE render the declared caps plus the honest
|
|
375
|
+
// "nothing is recorded yet" line — both are pre-mint states, and treating INCOMPLETE as MINTED would
|
|
376
|
+
// report a half record as the whole tree's debt; MINTED renders the recorded counts too.
|
|
377
|
+
// A config that cannot be read renders ONE loud line carrying the reader's own message and the render
|
|
378
|
+
// still completes: the exit code for a broken source-size config belongs to the practice's own
|
|
379
|
+
// checker (exit 2 there, and its declared gate reds the matrix on it), while THIS tool's exit
|
|
380
|
+
// contract is about its own config and the engine.
|
|
381
|
+
|
|
382
|
+
export const DECLARED_PRACTICE_HEADER = `Declared source-size practice (${SOURCE_SIZE_CONFIG_REL}) — known BEFORE the code is written:`;
|
|
383
|
+
|
|
384
|
+
const declaredPracticeAdvice = (cwd, readFile, lstat) => {
|
|
385
|
+
let declaration;
|
|
386
|
+
try {
|
|
387
|
+
declaration = loadSourceSizeConfig(cwd, { readFile, lstat });
|
|
388
|
+
} catch (err) {
|
|
389
|
+
return [`Declared source-size practice: UNREADABLE — ${(err && err.message) || err} — fix ${SOURCE_SIZE_CONFIG_REL} by hand; a declared practice is never guessed around.`];
|
|
390
|
+
}
|
|
391
|
+
if (declaration.state === 'absent') return [];
|
|
392
|
+
const facts = practiceFacts(declaration.config);
|
|
393
|
+
// The two pre-mint states share a LANE (the ratchet holds nothing, the mint step is next) but not a
|
|
394
|
+
// FACT: an incomplete file carries half the machine record, so "no size is recorded" would be a
|
|
395
|
+
// plain untruth about it — it names the missing half instead. Neither ever prints minted counts.
|
|
396
|
+
// The mint step is named, not rendered: on a project path that does not survive double-quoting the
|
|
397
|
+
// checker deliberately withholds a paste-ready command, and this advisor never re-decides that.
|
|
398
|
+
const unmintedRecord = declaration.state === 'incomplete'
|
|
399
|
+
? ` recorded: PARTIAL — the machine record is half-written (missing ${declaration.missingMachineKeys.map((key) => `"${key}"`).join(', ')}), so the ratchet holds nothing yet; run \`source-size-check.mjs --check\` for the mint step.`
|
|
400
|
+
: ' recorded: NOTHING YET — the caps are declared but no size is recorded, so the ratchet holds nothing; run `source-size-check.mjs --check` for the mint step.';
|
|
401
|
+
return [
|
|
402
|
+
DECLARED_PRACTICE_HEADER,
|
|
403
|
+
` caps: ${facts.maxLines} lines · ${facts.maxLineBytes} bytes per line, over ${facts.roots} declared root(s).`,
|
|
404
|
+
declaration.state === 'minted'
|
|
405
|
+
? ` 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.`
|
|
406
|
+
: unmintedRecord,
|
|
407
|
+
` why: ${SOURCE_SIZE_WHY}`,
|
|
408
|
+
' 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.',
|
|
409
|
+
];
|
|
410
|
+
};
|
|
411
|
+
|
|
364
412
|
// The verbatim per-backend DRIVING CONTRACT block (M-contract): the exact invocation descriptor(s),
|
|
365
413
|
// the closed flag set, the grounding note, the round-2/continue delta, and the guarded passthrough
|
|
366
414
|
// tiers — every descriptor printed VERBATIM from the registry mirror of the bridge manifest
|
|
@@ -397,7 +445,7 @@ const contractLines = ({ cmd, contract, settings }) => {
|
|
|
397
445
|
return lines;
|
|
398
446
|
};
|
|
399
447
|
|
|
400
|
-
const formatHuman = ({ activity, section, slots, warnings, plans, autonomy, flowHalves }) => {
|
|
448
|
+
const formatHuman = ({ activity, section, slots, warnings, plans, autonomy, flowHalves, declaredPractice }) => {
|
|
401
449
|
const lines = [
|
|
402
450
|
section,
|
|
403
451
|
'',
|
|
@@ -417,6 +465,7 @@ const formatHuman = ({ activity, section, slots, warnings, plans, autonomy, flow
|
|
|
417
465
|
const advice = reviewLoopAdvice(slots, activity);
|
|
418
466
|
if (advice.length) lines.push('', ...advice);
|
|
419
467
|
lines.push('', ...costLanesAdvice());
|
|
468
|
+
if (declaredPractice.length) lines.push('', ...declaredPractice);
|
|
420
469
|
if (warnings.length) {
|
|
421
470
|
lines.push('', 'warnings:');
|
|
422
471
|
for (const w of warnings) lines.push(` ⚠ ${w}`);
|
|
@@ -424,7 +473,7 @@ const formatHuman = ({ activity, section, slots, warnings, plans, autonomy, flow
|
|
|
424
473
|
return lines.join('\n');
|
|
425
474
|
};
|
|
426
475
|
|
|
427
|
-
const buildJson = ({ activity, section, slots, configSource, warnings, plans, autonomy, flowHalves }) => ({
|
|
476
|
+
const buildJson = ({ activity, section, slots, configSource, warnings, plans, autonomy, flowHalves, declaredPractice }) => ({
|
|
428
477
|
activity,
|
|
429
478
|
section,
|
|
430
479
|
slots: Object.fromEntries(
|
|
@@ -439,6 +488,9 @@ const buildJson = ({ activity, section, slots, configSource, warnings, plans, au
|
|
|
439
488
|
costLanes: costLanesAdvice(),
|
|
440
489
|
// ADDITIVE (AD-044 Plan 4): the per-activity autonomy block, structured (empty when unresolvable).
|
|
441
490
|
autonomy: autonomyAdvice(activity, autonomy),
|
|
491
|
+
// ADDITIVE (D-17 U1): the SAME composed lines the human render prints — one array, two renders, so
|
|
492
|
+
// a scripted reader and a human can never be told different things about the declared practice.
|
|
493
|
+
declaredPractice,
|
|
442
494
|
// CONDITIONAL (flow P8): the armed-halves block rides ONLY a flow-carrying config — the unarmed
|
|
443
495
|
// JSON key set stays byte-exact (unarmed neutrality outranks the additive-key precedent).
|
|
444
496
|
...(flowHalves == null ? {} : { flowHalves }),
|
|
@@ -461,6 +513,12 @@ ${CONFIG_REL} + the read-only backend detector, and prints both. A per-run
|
|
|
461
513
|
--override <slot>=<recipe> (repeatable) overrides the configured/default recipe for that slot.
|
|
462
514
|
Read-only: never writes, never commits, never runs a subscription CLI.
|
|
463
515
|
|
|
516
|
+
Also prints the project's DECLARED source-size practice (${SOURCE_SIZE_CONFIG_REL}) when it declares
|
|
517
|
+
one — the caps, what is recorded, why the practice exists, and the plan-time rung — as the
|
|
518
|
+
declaredPractice block (--json: the same lines under "declaredPractice"). A project with no such
|
|
519
|
+
file renders nothing; a file that cannot be read renders ONE loud UNREADABLE line and still exits 0
|
|
520
|
+
(the practice's own checker owns the exit code for its config).
|
|
521
|
+
|
|
464
522
|
Exit codes: 0 success (an unsatisfiable override degrades loudly, still 0);
|
|
465
523
|
2 usage (unknown activity / bad --override); 1 config or engine error
|
|
466
524
|
(incl. a malformed ${AUTONOMY_REL} — the advisory still renders, the exit flips).`;
|
|
@@ -515,9 +573,10 @@ export const main = (argv, ctx = {}) => {
|
|
|
515
573
|
// unarmed project keeps byte-identical output (human AND JSON) and never pays the store probe.
|
|
516
574
|
const flowProbe = ctx.flowProbe ?? defaultFlowProbe;
|
|
517
575
|
const flowHalves = config?.flow == null ? null : flowHalvesAdvice(config.flow, flowProbe(cwd));
|
|
576
|
+
const declaredPractice = declaredPracticeAdvice(cwd, readFile, lstat);
|
|
518
577
|
const stdout = json
|
|
519
|
-
? JSON.stringify(buildJson({ activity, section, slots, configSource, warnings, plans, autonomy, flowHalves }), null, 2)
|
|
520
|
-
: formatHuman({ activity, section, slots, warnings, plans, autonomy, flowHalves });
|
|
578
|
+
? JSON.stringify(buildJson({ activity, section, slots, configSource, warnings, plans, autonomy, flowHalves, declaredPractice }), null, 2)
|
|
579
|
+
: formatHuman({ activity, section, slots, warnings, plans, autonomy, flowHalves, declaredPractice });
|
|
521
580
|
if (autonomy?.error) {
|
|
522
581
|
return { code: 1, stdout, stderr: `procedures: malformed ${AUTONOMY_REL} — ${autonomy.error}` };
|
|
523
582
|
}
|