@gtrabanco/pi-agentic-workflow 4.10.0 → 5.0.0-canary.20261102.1
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/README.md +2 -2
- package/dist/config/defaults.js +0 -8
- package/dist/config/merge.js +0 -20
- package/dist/config/schema.js +1 -103
- package/dist/config/types.d.ts +0 -60
- package/dist/extension/factory.d.ts +3 -13
- package/dist/extension/factory.js +1 -5
- package/dist/extension/index.js +0 -24
- package/package.json +4 -4
- package/skills/execute-phase/SKILL.md +2 -2
- package/skills/execute-phase/references/PREFLIGHT.md +2 -2
- package/skills/init-workspace/SKILL.md +15 -8
- package/skills/init-workspace/references/BOOTSTRAP_WRITE.md +13 -7
- package/skills/init-workspace/references/UPGRADE.md +34 -14
- package/skills/phase-contract/SKILL.md +1 -1
- package/skills/pre-execution-review/references/LEDGERS.md +1 -1
- package/skills/pre-execution-review/references/POLICY.md +2 -2
- package/skills/product-audit/SKILL.md +6 -1
- package/skills/product-audit/references/AUDIT_DIMENSIONS.md +2 -0
- package/skills/product-audit/references/AUDIT_PROCESS.md +4 -1
- package/skills/review-code/SKILL.md +16 -1
- package/skills/review-implementation/references/CLASSIFY.md +1 -1
- package/skills/triage-issue/SKILL.md +3 -2
- package/skills/unit-lane/SKILL.md +3 -3
- package/skills/unit-lane/references/RELEASE.md +4 -4
- package/skills/workflow-status/SKILL.md +2 -2
- package/skills/workflow-status/references/SENSOR_CORE.md +4 -3
- package/dist/extension/ci-guard.d.ts +0 -29
- package/dist/extension/ci-guard.js +0 -39
- package/dist/extension/cost-guard.d.ts +0 -30
- package/dist/extension/cost-guard.js +0 -50
- package/dist/extension/guard-util.d.ts +0 -26
- package/dist/extension/guard-util.js +0 -35
- package/dist/extension/history-guard.d.ts +0 -59
- package/dist/extension/history-guard.js +0 -132
- package/dist/extension/merge-guard.d.ts +0 -36
- package/dist/extension/merge-guard.js +0 -79
package/README.md
CHANGED
|
@@ -326,9 +326,9 @@ dialog.
|
|
|
326
326
|
|
|
327
327
|
- Verified against the peer floor Pi 0.99.1 (2026-09-29) (`pi install`, package skills, friendly
|
|
328
328
|
command registration, routed set/clear, settings console round-trip, `sendUserMessage`
|
|
329
|
-
with prompt template expansion). Also verified against Pi 1.
|
|
329
|
+
with prompt template expansion). Also verified against Pi 1.5.0 (2026-11-02) via a local
|
|
330
330
|
two-leg `tsc --noEmit` run (legs `0.99.1` + `latest`, the `latest` leg resolving to pi
|
|
331
|
-
1.
|
|
331
|
+
1.5.0; COMPAT-OK); continuously against the
|
|
332
332
|
`latest` dist-tag by the `pi-compat-guard` PR gate (the leg is a moving tripwire,
|
|
333
333
|
not a pinned version — a breaking release shows red here first).
|
|
334
334
|
- The package declares Pi as a peer dependency; it bundles no copy of Pi.
|
package/dist/config/defaults.js
CHANGED
|
@@ -20,14 +20,6 @@ export const DEFAULT_CONFIG = {
|
|
|
20
20
|
// Feature 66 — the memory substrate is ask-first: nothing is provisioned
|
|
21
21
|
// until the operator opts in.
|
|
22
22
|
memory: { engram: false },
|
|
23
|
-
// Tool-call guards ship all-off: they are this repository's working rules,
|
|
24
|
-
// not a package opinion — a project opts in per guard in its config file.
|
|
25
|
-
guards: {
|
|
26
|
-
merge: false,
|
|
27
|
-
history: false,
|
|
28
|
-
ci: { create: false, run: false },
|
|
29
|
-
expensiveTests: { maxTokens: 0, patterns: [] },
|
|
30
|
-
},
|
|
31
23
|
profiles: {},
|
|
32
24
|
profileOrder: [],
|
|
33
25
|
profileFallback: { ...DEFAULT_PROFILE_FALLBACK },
|
package/dist/config/merge.js
CHANGED
|
@@ -132,9 +132,6 @@ export function mergeConfigs(globalFile = {}, projectFile = {}) {
|
|
|
132
132
|
memory: {
|
|
133
133
|
engram: pick(false, projectFile.memory?.engram, globalFile.memory?.engram) === true,
|
|
134
134
|
},
|
|
135
|
-
// Tool-call guards resolve like every other knob: project wins, then
|
|
136
|
-
// global, then the shipped OFF. A guard no scope mentions stays disabled.
|
|
137
|
-
guards: mergeGuards(globalFile.guards, projectFile.guards),
|
|
138
135
|
profiles: mergeProfiles(globalFile.profiles, projectFile.profiles),
|
|
139
136
|
profileOrder: pick([], projectFile.profileOrder, globalFile.profileOrder),
|
|
140
137
|
profileFallback: mergeProfileFallback(globalFile.profileFallback, projectFile.profileFallback),
|
|
@@ -147,23 +144,6 @@ export function mergeConfigs(globalFile = {}, projectFile = {}) {
|
|
|
147
144
|
merged.advance = advance;
|
|
148
145
|
return merged;
|
|
149
146
|
}
|
|
150
|
-
/** Guards merge whole-knob: a boolean takes project-then-global, and the
|
|
151
|
-
* expensive-tests block resolves field-by-field (patterns are replaced, not
|
|
152
|
-
* concatenated, so a project override is predictable — no hidden union). */
|
|
153
|
-
function mergeGuards(globalGuards, projectGuards) {
|
|
154
|
-
return {
|
|
155
|
-
merge: pick(DEFAULT_CONFIG.guards.merge, projectGuards?.merge, globalGuards?.merge),
|
|
156
|
-
history: pick(DEFAULT_CONFIG.guards.history, projectGuards?.history, globalGuards?.history),
|
|
157
|
-
ci: {
|
|
158
|
-
create: pick(DEFAULT_CONFIG.guards.ci.create, projectGuards?.ci?.create, globalGuards?.ci?.create),
|
|
159
|
-
run: pick(DEFAULT_CONFIG.guards.ci.run, projectGuards?.ci?.run, globalGuards?.ci?.run),
|
|
160
|
-
},
|
|
161
|
-
expensiveTests: {
|
|
162
|
-
maxTokens: pick(DEFAULT_CONFIG.guards.expensiveTests.maxTokens, projectGuards?.expensiveTests?.maxTokens, globalGuards?.expensiveTests?.maxTokens),
|
|
163
|
-
patterns: pick(DEFAULT_CONFIG.guards.expensiveTests.patterns, projectGuards?.expensiveTests?.patterns, globalGuards?.expensiveTests?.patterns),
|
|
164
|
-
},
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
147
|
/** Project-over-global merge of the advance (conductor) knobs (feature 62).
|
|
168
148
|
* Absent from the result when neither scope declares any — the resolved
|
|
169
149
|
* shape of pre-62 configs is byte-stable. */
|
package/dist/config/schema.js
CHANGED
|
@@ -21,7 +21,6 @@ const ROOT_KEYS = new Set([
|
|
|
21
21
|
"profileOrder",
|
|
22
22
|
"profileFallback",
|
|
23
23
|
"memory",
|
|
24
|
-
"guards",
|
|
25
24
|
]);
|
|
26
25
|
const ROUTE_KEYS = new Set(["model", "thinking"]);
|
|
27
26
|
const COMMAND_NAME = /^[a-z0-9][a-z0-9._-]*$/u;
|
|
@@ -333,7 +332,7 @@ function validateConfig(value) {
|
|
|
333
332
|
if (!ROOT_KEYS.has(key)) {
|
|
334
333
|
issues.push({
|
|
335
334
|
path: `$.${displayKey(key)}`,
|
|
336
|
-
message: `unknown config key "${key}" (allowed: default, commands, onUnavailableRoute, onSettle, pathProtection, advance, recommendedModels, profiles, profileOrder, profileFallback, memory
|
|
335
|
+
message: `unknown config key "${key}" (allowed: default, commands, onUnavailableRoute, onSettle, pathProtection, advance, recommendedModels, profiles, profileOrder, profileFallback, memory)`,
|
|
337
336
|
});
|
|
338
337
|
}
|
|
339
338
|
}
|
|
@@ -408,11 +407,6 @@ function validateConfig(value) {
|
|
|
408
407
|
if (memory)
|
|
409
408
|
config.memory = memory;
|
|
410
409
|
}
|
|
411
|
-
if (value.guards !== undefined) {
|
|
412
|
-
const guards = checkGuards(value.guards, "$.guards", issues);
|
|
413
|
-
if (guards)
|
|
414
|
-
config.guards = guards;
|
|
415
|
-
}
|
|
416
410
|
if (value.profiles !== undefined) {
|
|
417
411
|
if (!isRecord(value.profiles)) {
|
|
418
412
|
issues.push({ path: "$.profiles", message: "must be an object mapping profile names to profile definitions" });
|
|
@@ -472,102 +466,6 @@ function checkMemory(value, path, issues) {
|
|
|
472
466
|
}
|
|
473
467
|
return unknown ? undefined : out;
|
|
474
468
|
}
|
|
475
|
-
/** Strict validator for the `guards` block. Everything absent stays off. */
|
|
476
|
-
function checkGuards(value, path, issues) {
|
|
477
|
-
if (!isRecord(value)) {
|
|
478
|
-
issues.push({ path, message: `must be a JSON object, got ${describe(value)}` });
|
|
479
|
-
return undefined;
|
|
480
|
-
}
|
|
481
|
-
const out = {};
|
|
482
|
-
for (const key of ["merge", "history"]) {
|
|
483
|
-
if (value[key] !== undefined) {
|
|
484
|
-
if (typeof value[key] !== "boolean") {
|
|
485
|
-
issues.push({ path: `${path}.${key}`, message: `must be a boolean, got ${describe(value[key])}` });
|
|
486
|
-
}
|
|
487
|
-
else {
|
|
488
|
-
out[key] = value[key];
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
if (value.ci !== undefined) {
|
|
493
|
-
const ciPath = `${path}.ci`;
|
|
494
|
-
if (!isRecord(value.ci)) {
|
|
495
|
-
issues.push({ path: ciPath, message: `must be a JSON object, got ${describe(value.ci)}` });
|
|
496
|
-
}
|
|
497
|
-
else {
|
|
498
|
-
const ci = {};
|
|
499
|
-
for (const key of ["create", "run"]) {
|
|
500
|
-
if (value.ci[key] !== undefined) {
|
|
501
|
-
if (typeof value.ci[key] !== "boolean") {
|
|
502
|
-
issues.push({ path: `${ciPath}.${key}`, message: `must be a boolean, got ${describe(value.ci[key])}` });
|
|
503
|
-
}
|
|
504
|
-
else {
|
|
505
|
-
ci[key] = value.ci[key];
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
for (const key of Object.keys(value.ci)) {
|
|
510
|
-
if (key !== "create" && key !== "run") {
|
|
511
|
-
issues.push({ path: `${ciPath}.${displayKey(key)}`, message: `unknown ci key "${key}" (allowed: create, run)` });
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
out.ci = ci;
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
if (value.expensiveTests !== undefined) {
|
|
518
|
-
out.expensiveTests = checkExpensiveTests(value.expensiveTests, `${path}.expensiveTests`, issues);
|
|
519
|
-
}
|
|
520
|
-
for (const key of Object.keys(value)) {
|
|
521
|
-
if (key !== "merge" && key !== "history" && key !== "ci" && key !== "expensiveTests") {
|
|
522
|
-
issues.push({ path: `${path}.${displayKey(key)}`, message: `unknown guards key "${key}" (allowed: merge, history, ci, expensiveTests)` });
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
return out;
|
|
526
|
-
}
|
|
527
|
-
function checkExpensiveTests(value, path, issues) {
|
|
528
|
-
if (!isRecord(value)) {
|
|
529
|
-
issues.push({ path, message: `must be a JSON object, got ${describe(value)}` });
|
|
530
|
-
return undefined;
|
|
531
|
-
}
|
|
532
|
-
const out = {};
|
|
533
|
-
if (value.maxTokens !== undefined) {
|
|
534
|
-
if (typeof value.maxTokens !== "number" || !Number.isInteger(value.maxTokens) || value.maxTokens < 0) {
|
|
535
|
-
issues.push({ path: `${path}.maxTokens`, message: `must be a non-negative integer, got ${describe(value.maxTokens)}` });
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
out.maxTokens = value.maxTokens;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
if (value.patterns !== undefined) {
|
|
542
|
-
if (!Array.isArray(value.patterns)) {
|
|
543
|
-
issues.push({ path: `${path}.patterns`, message: `must be an array, got ${describe(value.patterns)}` });
|
|
544
|
-
}
|
|
545
|
-
else {
|
|
546
|
-
const patterns = [];
|
|
547
|
-
value.patterns.forEach((entry, index) => {
|
|
548
|
-
const entryPath = `${path}.patterns[${index}]`;
|
|
549
|
-
if (!isRecord(entry) || typeof entry.match !== "string" || entry.match === "") {
|
|
550
|
-
issues.push({ path: entryPath, message: `must be an object with a non-empty string "match", got ${describe(entry)}` });
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
if (typeof entry.tokens !== "number" || !Number.isInteger(entry.tokens) || entry.tokens < 0) {
|
|
554
|
-
issues.push({ path: `${entryPath}.tokens`, message: `must be a non-negative integer, got ${describe(entry.tokens)}` });
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
try {
|
|
558
|
-
new RegExp(entry.match, "u");
|
|
559
|
-
}
|
|
560
|
-
catch {
|
|
561
|
-
issues.push({ path: `${entryPath}.match`, message: `not a valid regular expression: ${entry.match}` });
|
|
562
|
-
return;
|
|
563
|
-
}
|
|
564
|
-
patterns.push({ match: entry.match, tokens: entry.tokens });
|
|
565
|
-
});
|
|
566
|
-
out.patterns = patterns;
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
return out;
|
|
570
|
-
}
|
|
571
469
|
export function parseConfigFile(text) {
|
|
572
470
|
if (text.trim() === "")
|
|
573
471
|
return { ok: true, config: {} };
|
package/dist/config/types.d.ts
CHANGED
|
@@ -78,9 +78,6 @@ export interface ConfigFile {
|
|
|
78
78
|
/** Feature 66 (AC3/D3): the memory-substrate consent surface — Engram's MCP
|
|
79
79
|
* registration is ask-first, so nothing is provisioned unless this says so. */
|
|
80
80
|
memory?: MemoryConfigFile;
|
|
81
|
-
/** Tool-call guards (owner-gated merges, history, CI, expensive tests).
|
|
82
|
-
* Everything absent stays off. */
|
|
83
|
-
guards?: GuardsConfigFile;
|
|
84
81
|
/** Named user profiles keyed by profile name (feature 63, AC5). */
|
|
85
82
|
profiles?: Record<string, ProfileFile>;
|
|
86
83
|
/** Ordered list of profile names to probe; empty means ["default"] only
|
|
@@ -108,61 +105,6 @@ export interface MemoryConfigFile {
|
|
|
108
105
|
export interface MemoryConfig {
|
|
109
106
|
engram: boolean;
|
|
110
107
|
}
|
|
111
|
-
/**
|
|
112
|
-
* One tool-call guard, as an operator WRITES it (this repository's working
|
|
113
|
-
* rules, made mechanical; every other project gets none of it unless it
|
|
114
|
-
* configures it). Each guard ships disabled — an absent key is OFF.
|
|
115
|
-
*
|
|
116
|
-
* merge every direct merge route is owner-only: `git merge`,
|
|
117
|
-
* `gh pr merge`, and `git pull`'s merge form.
|
|
118
|
-
* history pushes are decided by asking git (`git push --dry-run`):
|
|
119
|
-
* a forced update, a ref delete or a push to `main` is
|
|
120
|
-
* declined — one mechanism covering every flag spelling and
|
|
121
|
-
* git alias.
|
|
122
|
-
* ci CI is owner-gated: creating workflow files and triggering
|
|
123
|
-
* workflow runs are blocked.
|
|
124
|
-
* expensiveTests commands matching a configured pattern are estimated in
|
|
125
|
-
* tokens; above `maxTokens` they are blocked. `tokens: 0`
|
|
126
|
-
* blocks every match regardless of estimate.
|
|
127
|
-
*
|
|
128
|
-
* The only way through a guard is the explicit per-command token — the
|
|
129
|
-
* command's first word — after an owner authorization in the same turn:
|
|
130
|
-
* `AW_ALLOW=merge`, `AW_ALLOW=force`, `AW_ALLOW=ci`, `AW_ALLOW=cost`.
|
|
131
|
-
*/
|
|
132
|
-
export interface GuardsConfigFile {
|
|
133
|
-
merge?: boolean;
|
|
134
|
-
history?: boolean;
|
|
135
|
-
ci?: GuardsCiFile;
|
|
136
|
-
expensiveTests?: GuardsExpensiveTestsFile;
|
|
137
|
-
}
|
|
138
|
-
export interface GuardsCiFile {
|
|
139
|
-
create?: boolean;
|
|
140
|
-
run?: boolean;
|
|
141
|
-
}
|
|
142
|
-
export interface GuardsExpensiveTestsFile {
|
|
143
|
-
/** Estimated-token ceiling; a match above it is blocked. `0` (default)
|
|
144
|
-
* blocks every match regardless of estimate. */
|
|
145
|
-
maxTokens?: number;
|
|
146
|
-
/** Command patterns (regex source, `u` flag) and their estimated cost. */
|
|
147
|
-
patterns?: GuardsCostPattern[];
|
|
148
|
-
}
|
|
149
|
-
export interface GuardsCostPattern {
|
|
150
|
-
match: string;
|
|
151
|
-
tokens: number;
|
|
152
|
-
}
|
|
153
|
-
/** What the extension READS: every guard knob resolved. */
|
|
154
|
-
export interface GuardsConfig {
|
|
155
|
-
merge: boolean;
|
|
156
|
-
history: boolean;
|
|
157
|
-
ci: {
|
|
158
|
-
create: boolean;
|
|
159
|
-
run: boolean;
|
|
160
|
-
};
|
|
161
|
-
expensiveTests: {
|
|
162
|
-
maxTokens: number;
|
|
163
|
-
patterns: GuardsCostPattern[];
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
108
|
export interface ConfigIssue {
|
|
167
109
|
path: string;
|
|
168
110
|
message: string;
|
|
@@ -191,8 +133,6 @@ export interface EffectiveConfig {
|
|
|
191
133
|
profileFallback: ProfileFallbackConfig;
|
|
192
134
|
/** Feature 66: the memory-substrate consent surface. */
|
|
193
135
|
memory: MemoryConfig;
|
|
194
|
-
/** The effective tool-call guards (all off unless a config file enables them). */
|
|
195
|
-
guards: GuardsConfig;
|
|
196
136
|
/** Feature 63: declaredness of the implicit `default` profile. */
|
|
197
137
|
declared: {
|
|
198
138
|
default: RouteDeclaration;
|
|
@@ -2,10 +2,6 @@ import type { LoadedConfig } from "../config/load.js";
|
|
|
2
2
|
import type { Catalogue } from "../routing/catalogue.js";
|
|
3
3
|
import type { Router } from "../routing/dispatch.js";
|
|
4
4
|
import { dirtyWorktreeWarning, receiptGuard } from "./receipt-guard.js";
|
|
5
|
-
import { mergeGuard } from "./merge-guard.js";
|
|
6
|
-
import { historyGuard } from "./history-guard.js";
|
|
7
|
-
import { ciGuard } from "./ci-guard.js";
|
|
8
|
-
import { costGuard } from "./cost-guard.js";
|
|
9
5
|
import type { ExtensionSurface, InvocationContext, ModelRef, RoutingControls } from "../routing/types.js";
|
|
10
6
|
import type { HintStore } from "../routing/state.js";
|
|
11
7
|
/**
|
|
@@ -58,18 +54,12 @@ export interface ExtensionHandle<M extends ModelRef = ModelRef> {
|
|
|
58
54
|
catalogue: Catalogue;
|
|
59
55
|
/**
|
|
60
56
|
* The Pi-free predicates the adapter (`index.ts`) binds to Pi's lifecycle
|
|
61
|
-
* events: the inline-receipt block guard
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* entry stays a thin translation layer and the decisions are unit-tested with
|
|
65
|
-
* no session (issues #182, #278).
|
|
57
|
+
* events: the inline-receipt block guard and the dirty-worktree notice. They
|
|
58
|
+
* travel with the handle so the entry stays a thin translation layer and the
|
|
59
|
+
* decisions are unit-tested with no session (issue #182).
|
|
66
60
|
*/
|
|
67
61
|
guards: {
|
|
68
62
|
receiptGuard: typeof receiptGuard;
|
|
69
|
-
mergeGuard: typeof mergeGuard;
|
|
70
|
-
historyGuard: typeof historyGuard;
|
|
71
|
-
ciGuard: typeof ciGuard;
|
|
72
|
-
costGuard: typeof costGuard;
|
|
73
63
|
dirtyWorktreeWarning: typeof dirtyWorktreeWarning;
|
|
74
64
|
};
|
|
75
65
|
}
|
|
@@ -2,10 +2,6 @@ import { loadConfig } from "../config/load.js";
|
|
|
2
2
|
import { readCatalogue } from "../routing/catalogue.js";
|
|
3
3
|
import { createRouter } from "../routing/dispatch.js";
|
|
4
4
|
import { dirtyWorktreeWarning, receiptGuard } from "./receipt-guard.js";
|
|
5
|
-
import { mergeGuard } from "./merge-guard.js";
|
|
6
|
-
import { historyGuard } from "./history-guard.js";
|
|
7
|
-
import { ciGuard } from "./ci-guard.js";
|
|
8
|
-
import { costGuard } from "./cost-guard.js";
|
|
9
5
|
import { ADVANCE_COMMAND, SETTINGS_COMMAND, SETTINGS_COMMAND_ALIAS } from "../routing/types.js";
|
|
10
6
|
import { registerAdvanceCommand } from "./conductor-command.js";
|
|
11
7
|
import { createProfileStateStore, stateFilePath } from "../routing/state.js";
|
|
@@ -82,5 +78,5 @@ export function createExtension(deps) {
|
|
|
82
78
|
description: "Show and configure per-command model routing",
|
|
83
79
|
handler: settingsHandler,
|
|
84
80
|
});
|
|
85
|
-
return { router, catalogue, guards: { receiptGuard,
|
|
81
|
+
return { router, catalogue, guards: { receiptGuard, dirtyWorktreeWarning } };
|
|
86
82
|
}
|
package/dist/extension/index.js
CHANGED
|
@@ -296,32 +296,8 @@ export default function extension(pi) {
|
|
|
296
296
|
// names the script that proves the receipt landed (issue #182). The guard
|
|
297
297
|
// itself filters to `bash`, so every tool call is checked — nesting it under
|
|
298
298
|
// one tool name would make the block unreachable for the calls it exists for.
|
|
299
|
-
//
|
|
300
|
-
// Owner-gated tool guards, all config-driven (`guards` in the config files;
|
|
301
|
-
// every guard ships OFF — a project opts in per guard):
|
|
302
|
-
// merge `git merge`, `gh pr merge`, `git pull`'s merge form (issue #278).
|
|
303
|
-
// history pushes decided by git itself (`git push --dry-run`): a forced
|
|
304
|
-
// update, a ref delete or a push to `main` is declined.
|
|
305
|
-
// ci writing workflow definitions and triggering workflow runs.
|
|
306
|
-
// cost commands estimated above the configured token budget.
|
|
307
|
-
// The only way through any of them is the explicit per-command token
|
|
308
|
-
// (`AW_ALLOW=merge|force|ci|cost`) after an owner instruction in the turn.
|
|
309
299
|
pi.on("tool_call", (event, ctx) => {
|
|
310
300
|
const command = "command" in event.input && typeof event.input.command === "string" ? event.input.command : undefined;
|
|
311
|
-
const inputPath = "path" in event.input && typeof event.input.path === "string" ? event.input.path : undefined;
|
|
312
|
-
const { guards: policy } = loadConfig({ agentDir, cwd: ctx.cwd, projectTrusted: ctx.isProjectTrusted() }).config;
|
|
313
|
-
const merge = guards.mergeGuard({ toolName: event.toolName, command }, policy.merge);
|
|
314
|
-
if (merge.block)
|
|
315
|
-
return merge;
|
|
316
|
-
const history = guards.historyGuard({ toolName: event.toolName, command }, policy.history, { cwd: ctx.cwd });
|
|
317
|
-
if (history.block)
|
|
318
|
-
return history;
|
|
319
|
-
const ci = guards.ciGuard({ toolName: event.toolName, command, path: inputPath }, policy.ci);
|
|
320
|
-
if (ci.block)
|
|
321
|
-
return ci;
|
|
322
|
-
const cost = guards.costGuard({ toolName: event.toolName, command }, policy.expensiveTests);
|
|
323
|
-
if (cost.block)
|
|
324
|
-
return cost;
|
|
325
301
|
const receipt = guards.receiptGuard({ toolName: event.toolName, command });
|
|
326
302
|
if (receipt.block)
|
|
327
303
|
return receipt;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtrabanco/pi-agentic-workflow",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-canary.20261102.1",
|
|
4
4
|
"description": "Pi package: canonical agentic-workflow skills, friendly slash commands, and per-command model routing.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
"@earendil-works/pi-tui": "*"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@gtrabanco/agentic-workflow": "
|
|
62
|
-
"@gtrabanco/agentic-workflow-schema": "
|
|
61
|
+
"@gtrabanco/agentic-workflow": "5.0.0-canary.20261102.1",
|
|
62
|
+
"@gtrabanco/agentic-workflow-schema": "5.0.0-canary.20261102.1"
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
|
-
"@types/node": "
|
|
65
|
+
"@types/node": "26.6.6",
|
|
66
66
|
"typescript": "7"
|
|
67
67
|
}
|
|
68
68
|
}
|
|
@@ -227,8 +227,8 @@ the full accumulated diff. On gate red at any step: repair that step (up to
|
|
|
227
227
|
`--max-attempts`), then continue the loop. The close-out is part of the last
|
|
228
228
|
step's completion: open the PR on `main` with `Closes #N`, write `- PR: <url>`
|
|
229
229
|
into References, and settle the index row with `agentic-workflow roadmap
|
|
230
|
-
<index> rowUpsert '<json>'` (`docs/
|
|
231
|
-
`
|
|
230
|
+
<index> rowUpsert '<json>'` (`docs/ROADMAP.md`; a fix unit edits its row in that
|
|
231
|
+
document's `## Fixes` table — no `NN` column, no `rowUpsert`).
|
|
232
232
|
`bun scripts/roadmap-settle.mjs` exits 0 (#316).
|
|
233
233
|
Terminal:
|
|
234
234
|
|
|
@@ -69,8 +69,8 @@ appending to the unit doc's Evidence section.
|
|
|
69
69
|
|
|
70
70
|
### Own-status precondition (runs after the dependency closure is met, still before any edit)
|
|
71
71
|
|
|
72
|
-
Read the unit doc's status from the
|
|
73
|
-
`docs/
|
|
72
|
+
Read the unit doc's status from the location index (the five-state machine —
|
|
73
|
+
`docs/ROADMAP.md` → Status legend):
|
|
74
74
|
|
|
75
75
|
1. **`idea`** (or no triage block in the unit doc) → STOP, before any edit:
|
|
76
76
|
|
|
@@ -65,7 +65,8 @@ Inspect the target dir (`[target-dir]`, default cwd) before touching anything:
|
|
|
65
65
|
- An existing **agent guide** (`AGENTS.md`, or a legacy `CLAUDE.md`), `docs/`,
|
|
66
66
|
`.github/`? If so, **do not clobber** — check first whether it's an
|
|
67
67
|
**agentic-workflow scaffold** (marker: an agent guide present *and* either
|
|
68
|
-
`docs/
|
|
68
|
+
`docs/ROADMAP.md` — or the pre-319 `docs/features/ROADMAP.md` — or a
|
|
69
|
+
`docs/workflow/` dir). If both markers are
|
|
69
70
|
present, offer **upgrade** as the default action, alongside merge /
|
|
70
71
|
adapt-in-place / abort — see **Upgrade mode** below. If an agent guide or
|
|
71
72
|
`docs/` exists but the markers are absent (a foreign scaffold), stay in
|
|
@@ -166,11 +167,17 @@ loads exactly upgrade and skips bootstrap plus portability.
|
|
|
166
167
|
|
|
167
168
|
## Unit document conventions (the adaptive lane)
|
|
168
169
|
|
|
169
|
-
The adaptive lane
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
The adaptive lane replaces the fixed pipeline with a single-unit-document model.
|
|
171
|
+
Every NEW unit produces one `SPEC.md` under `docs/features/<NN>-<slug>/`, cut in
|
|
172
|
+
the **`spec-by-reference`** shape the template ships: `Specs` (numbered `S#`, each
|
|
173
|
+
quoting the requester's words verbatim), Impact, Non-goals, Future cost,
|
|
174
|
+
Applicable tests, Known pre-existing issues, `Tasks` (each bullet linked to the
|
|
175
|
+
`S#` it delivers), `Findings` (OPEN rows only), `Log` (append-only; `L1` is the
|
|
176
|
+
request verbatim), Next, References. A unit opened before this shape keeps the
|
|
177
|
+
legacy 14-section shape — Objective, Why, User outcome, Acceptance criteria,
|
|
178
|
+
Impact, Non-goals, Future cost, Applicable tests, Known pre-existing issues,
|
|
179
|
+
Tasks, Evidence, Progress log, Next, References — and is never rewritten to
|
|
180
|
+
migrate.
|
|
174
181
|
|
|
175
182
|
Conventions written into the project's AGENTS.md:
|
|
176
183
|
|
|
@@ -188,8 +195,8 @@ Conventions written into the project's AGENTS.md:
|
|
|
188
195
|
|
|
189
196
|
These are written by bootstrap (fresh) and upgrade mode (additive-only for pre-
|
|
190
197
|
lane installs). A pre-lane project gets only: the lane conventions section in
|
|
191
|
-
AGENTS.md, the unit doc
|
|
192
|
-
docs-map
|
|
198
|
+
AGENTS.md, the unit doc templates under `docs/features/_TEMPLATE/` and
|
|
199
|
+
`docs/fix/_TEMPLATE/`, and the docs-map rows for them.
|
|
193
200
|
|
|
194
201
|
## Normalized Repository State
|
|
195
202
|
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
the documentation map rows, architecture); write the **adaptive lane
|
|
5
5
|
conventions** (unit document, triage, catalog steps, guards, evidence rows,
|
|
6
6
|
dated progress log, closed commit format `type(scope): description`); keep
|
|
7
|
-
the
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
the documentation templates it ships — `features/_TEMPLATE` beside
|
|
8
|
+
`features/ROADMAP.md`, and `fix/_TEMPLATE` beside `fix/README.md` — and the
|
|
9
|
+
`.github/` templates; keep `.agentic-workflow/hooks/` and activate only the
|
|
10
|
+
adapters explicitly accepted in the interview; prune unused doc folders and
|
|
11
|
+
map rows. Leave honest placeholders where the user hasn't decided — never
|
|
11
12
|
invent values.
|
|
12
13
|
5. **Seed Normalized Repository State.** Copy
|
|
13
14
|
`template/docs/workflow/REPOSITORY_STATE.md` to
|
|
@@ -21,9 +22,14 @@
|
|
|
21
22
|
optional: an absent document means no project invariants are declared, not
|
|
22
23
|
that the scaffold failed. If retained, add its documentation-map row and
|
|
23
24
|
name the project's explicit architectural-decision authority.
|
|
24
|
-
7. **Propagate the unit doc
|
|
25
|
-
`
|
|
26
|
-
|
|
25
|
+
7. **Propagate the unit doc templates.** Ensure both template folders exist and
|
|
26
|
+
are cut in the `spec-by-reference` shape the scaffold ships —
|
|
27
|
+
`docs/features/_TEMPLATE/SPEC.md` and `docs/fix/_TEMPLATE/SPEC.md` (`Specs`
|
|
28
|
+
with `S#` requirements, Impact, Non-goals, Future cost, Applicable tests,
|
|
29
|
+
Known pre-existing issues, `Tasks`, `Findings`, `Log`, Next, References).
|
|
30
|
+
These are the templates used when creating new unit documents; a unit opened
|
|
31
|
+
before this shape keeps the legacy 14-section shape and is never rewritten to
|
|
32
|
+
migrate.
|
|
27
33
|
8. **Provision Serena — one consented question, then write.** Load
|
|
28
34
|
[SERENA](SERENA.md) and ask its single consent question; one yes covers the
|
|
29
35
|
tool install, the detected platform's MCP registration and the global trust
|
|
@@ -4,7 +4,7 @@ Entered when Step 0 finds an existing **agentic-workflow scaffold** (not a
|
|
|
4
4
|
bare or foreign repo). Bootstrap mode (the Process section above) never
|
|
5
5
|
engages here — upgrade mode reuses the same discovery + interview machinery,
|
|
6
6
|
scoped to **only the blocks the current template has that this project
|
|
7
|
-
lacks**.
|
|
7
|
+
lacks**. Ten ordered steps:
|
|
8
8
|
|
|
9
9
|
1. **Locate the current template.** Fetch the current `template/` the same
|
|
10
10
|
way bootstrap does — `npx degit gtrabanco/agentic-workflow/template <temp-dir>`
|
|
@@ -33,14 +33,34 @@ lacks**. Nine ordered steps:
|
|
|
33
33
|
dated migration note so the proposal explains *why* the block exists and
|
|
34
34
|
*what* it migrates. If `MIGRATION.md` is absent, proceed on the template
|
|
35
35
|
diff alone and say so in the report — never block on a missing note.
|
|
36
|
-
4. **Propose the
|
|
36
|
+
4. **Propose the consolidated location index (issue #319).** A project whose
|
|
37
|
+
roadmap still lives at `docs/features/ROADMAP.md`, or whose fix index is still
|
|
38
|
+
a standalone `docs/fix/README.md`, is offered the move **additively**:
|
|
39
|
+
`docs/ROADMAP.md` gained as a new file (the project's feature table plus a
|
|
40
|
+
`## Fixes` section built from its own fix index), the old path removed **only
|
|
41
|
+
after the new file is in place**, and the fix README reduced to the one-line
|
|
42
|
+
pointer stub the template ships. Never rewrite an edited roadmap: if the
|
|
43
|
+
project's table differs from the template's, carry the project's rows
|
|
44
|
+
verbatim and list the differences as residuals. **The read window is closed**
|
|
45
|
+
(`docs/workflow/MIGRATION.md`, 2026-10-30, decision D4): the old path is only
|
|
46
|
+
*detected* so this step can offer the move — a repository that still runs the
|
|
47
|
+
lane against it is refused with a `roadmap-moved` pointer, so propose the move
|
|
48
|
+
as the first thing to fix, never as an optional cleanup. A half-migrated tree
|
|
49
|
+
(canonical roadmap without a `## Fixes` table) still reads its standalone fix
|
|
50
|
+
index, so the fix table can be folded in a second pass.
|
|
51
|
+
|
|
52
|
+
5. **Propose the adaptive lane conventions (feature 61 P11).** For pre-lane
|
|
37
53
|
installs (before the fixed pipeline was retired), propose these missing
|
|
38
54
|
blocks additively — **never clobber existing content**:
|
|
39
|
-
- The **unit document conventions** section in AGENTS.md (the
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
- The **unit document conventions** section in AGENTS.md (the
|
|
56
|
+
`spec-by-reference` unit doc the template ships — `Specs` with `S#`
|
|
57
|
+
requirements, `Tasks`, `Findings`, `Log` — triage-driven step selection,
|
|
58
|
+
diff-size guard, evidence rows, dated progress log, closed commit format
|
|
59
|
+
`type(scope): description`).
|
|
60
|
+
- The **unit doc templates** under `docs/features/_TEMPLATE/SPEC.md` and
|
|
61
|
+
`docs/fix/_TEMPLATE/SPEC.md` if absent — the re-cut `spec-by-reference`
|
|
62
|
+
templates the scaffold ships. A pre-existing unit keeps the legacy
|
|
63
|
+
14-section shape and is never rewritten to migrate.
|
|
44
64
|
- The **lane workflow doc** under `docs/workflow/` if absent: `FEATURE_WORKFLOW.md`
|
|
45
65
|
(unit doc → triage → catalog steps → evidence → review → release),
|
|
46
66
|
`ISSUE_WORKFLOW.md` (issues route through triage to the lane).
|
|
@@ -50,7 +70,7 @@ lacks**. Nine ordered steps:
|
|
|
50
70
|
the project already answered** — a block that's already filled is skipped,
|
|
51
71
|
not re-interviewed. The lane conventions are additive only: existing content
|
|
52
72
|
is never rewritten, only new blocks are inserted where missing.
|
|
53
|
-
|
|
73
|
+
6. **Write additively.** Insert accepted blocks, including a missing
|
|
54
74
|
`docs/workflow/REPOSITORY_STATE.md`; fill raw, still-placeholder
|
|
55
75
|
blocks with the confirmed values. **Never rewrite a block the project has
|
|
56
76
|
already tailored, and never delete anything** — a tailored block the
|
|
@@ -61,7 +81,7 @@ lacks**. Nine ordered steps:
|
|
|
61
81
|
`path-protection.md` page are proposed from the template; an existing
|
|
62
82
|
owner-written `path-policy.json` is never clobbered and is listed as a
|
|
63
83
|
residual instead. Leave honest placeholders where the user skipped.
|
|
64
|
-
|
|
84
|
+
7. **Retire the legacy `CLAUDE.md` — only with an explicit yes, never as a side
|
|
65
85
|
effect.** Older releases of this workflow wrote the guide as `CLAUDE.md`;
|
|
66
86
|
current ones write `AGENTS.md`, and current Claude Code reads `AGENTS.md`.
|
|
67
87
|
When the project has a `CLAUDE.md`, three ordered moves — never skip (a):
|
|
@@ -83,7 +103,7 @@ lacks**. Nine ordered steps:
|
|
|
83
103
|
|
|
84
104
|
This step is the only writer of `CLAUDE.md` in upgrade mode, and it is never
|
|
85
105
|
reached without the answer from (b).
|
|
86
|
-
|
|
106
|
+
8. **Seed missing urgency labels, additively (feature 15).** Independent of
|
|
87
107
|
the agent-guide/`docs/` block diff above (this is forge-repo state, not a
|
|
88
108
|
doc block): check whether the target repo already has the `urgent` and
|
|
89
109
|
`fix-next` labels (`gh label list`); create whichever is missing with the
|
|
@@ -92,7 +112,7 @@ lacks**. Nine ordered steps:
|
|
|
92
112
|
rule as the doc blocks; a pre-existing `urgent`/`fix-next` label the
|
|
93
113
|
project recolored or redescribed is left exactly as-is).
|
|
94
114
|
|
|
95
|
-
|
|
115
|
+
9. **Provision Serena, additively only (Refs #219).** Load
|
|
96
116
|
[SERENA](SERENA.md) and ask its single consent question — the tool install,
|
|
97
117
|
the detected platform's MCP registration and the global trust append all ride
|
|
98
118
|
on that one yes, and a refusal writes nothing. Then append the `.gitignore`
|
|
@@ -101,7 +121,7 @@ lacks**. Nine ordered steps:
|
|
|
101
121
|
the health check, and render the fixed provisioning report. This step is
|
|
102
122
|
additive-only; it never rewrites or deletes, consistent with upgrade mode's
|
|
103
123
|
rule.
|
|
104
|
-
|
|
124
|
+
10. **Report + hand off.** Summarize blocks added, filled, and skipped
|
|
105
125
|
(residuals), the legacy `CLAUDE.md` outcome (folded items and removed, or kept
|
|
106
126
|
with the reason), the urgency labels seeded (or already present), then print the
|
|
107
127
|
recommendation to run `product-audit` next to see which newly-available
|
|
@@ -121,12 +141,12 @@ lacks**. Nine ordered steps:
|
|
|
121
141
|
- **A block the project already tailored.** If the current template also
|
|
122
142
|
changed a block the project customized, **do not merge, diff-patch, or
|
|
123
143
|
overwrite it** — leave it exactly as the project has it and list it as a
|
|
124
|
-
residual in the report (step
|
|
144
|
+
residual in the report (step 6's never-clobber invariant). The user decides
|
|
125
145
|
separately, via an explicit bootstrap adapt-in-place run, whether to
|
|
126
146
|
re-tailor it.
|
|
127
147
|
- **Bootstrap stays unchanged on a bare or foreign repo.** If Step 0's
|
|
128
148
|
scaffold markers (an agent guide — `AGENTS.md` or a legacy `CLAUDE.md` — plus
|
|
129
|
-
`docs/features/ROADMAP.md
|
|
149
|
+
`docs/ROADMAP.md`, a pre-319 `docs/features/ROADMAP.md`, or
|
|
130
150
|
`docs/workflow/`) are absent — no repo, an empty repo, or a guide
|
|
131
151
|
that isn't this workflow's — upgrade mode never engages; the existing
|
|
132
152
|
bootstrap Process (merge/adapt/abort) runs exactly as before this mode was
|
|
@@ -72,7 +72,7 @@ split and never carries a second copy of the semantics:
|
|
|
72
72
|
| grammar | where | shape |
|
|
73
73
|
|---|---|---|
|
|
74
74
|
| **plan** | a plan file, or any `##/###/#### P<n> — <Title>` heading | heading + `Layer: <enum>` + `- [ ]` tasks + `Done-when:` |
|
|
75
|
-
| **unit doc** | the unit doc's `## Tasks` section | one `P<n> — <task> (validator: <command> <outcome>)` bullet per phase, wrapped lines joining the bullet, plus an optional nested `- Relevant files: <path, path…>` line |
|
|
75
|
+
| **unit doc** | the unit doc's `## Tasks` section | one `P<n> — <task> (validator: <command> <outcome>)` bullet per phase, optionally carrying the executed-phase tick mark (`- [x] P<n> — …`; the tick is state, never a second phase), wrapped lines joining the bullet, plus an optional nested `- Relevant files: <path, path…>` line |
|
|
76
76
|
|
|
77
77
|
Precedence is fail-closed and one-way: **if a document carries any plan-style
|
|
78
78
|
phase heading, the plan grammar governs it**; only when it carries none does
|
|
@@ -170,7 +170,7 @@ planning-findings | docs/features/<NN>-<slug>/planning-findings.md · docs/fix/<
|
|
|
170
170
|
progress | docs/features/<NN>-<slug>/SPEC.md · docs/fix/<issue>-<topic>/SPEC.md | unit-lane:progress-entries + execute-phase:phase-entries + execute-phase:gate-rejection-traces | none | none | bun test scripts/pre-execution-sensor.test.mjs
|
|
171
171
|
known-issues | docs/features/<NN>-<slug>/known-issues.md · docs/fix/<issue>-<topic>/known-issues.md | unit-lane:known-entries + execute-phase:blocker-entries-and-status | none | none | bun test scripts/ledger-ownership.test.mjs
|
|
172
172
|
decisions | docs/features/<NN>-<slug>/decisions.md · docs/fix/<issue>-<topic>/decisions.md | unit-lane:decisions + execute-phase:phase-decisions + human-owner:ratified-verdicts | none | none | bun test scripts/ledger-ownership.test.mjs
|
|
173
|
-
roadmap | docs/
|
|
173
|
+
roadmap | docs/ROADMAP.md | unit-lane:row-registration + execute-phase:status-and-pr-link + audit-docs:low-risk-row-repair | none | none | bun test scripts/bounded-delivery-loops.test.mjs
|
|
174
174
|
acceptance-manifest | docs/features/<NN>-<slug>/ACCEPTANCE.md · docs/fix/<issue>-<topic>/ACCEPTANCE.md | unit-lane:acceptance-freeze + human-owner:approved-amendment | none | none | git hash-object docs/features/<NN>-<slug>/ACCEPTANCE.md (legacy pre-feature 61) · sed -n '/^## Acceptance criteria/,/^## /p' <unit>/SPEC.md then git hash-object --stdin (unit doc era, post-feature 61)
|
|
175
175
|
# no-script-writer: SPEC.md · PLAN.md · TASKS.md · CHECKLIST.md · testing.md · architecture-notes.md · planning-evidence.md · planning-obligations.md · delegated-evidence.md
|
|
176
176
|
```
|
|
@@ -136,8 +136,8 @@ a count and never waives a finding.
|
|
|
136
136
|
`execute-phase`, and a `PLAN-REVIEW-PASS` never certifies the Product half.
|
|
137
137
|
- A gate answer from a roadmap row's status. Gates read receipts only;
|
|
138
138
|
roadmap rows are labels and never answer a gate. `done` means built + PR open
|
|
139
|
-
with the human merge pending (`docs/
|
|
140
|
-
|
|
139
|
+
with the human merge pending (`docs/ROADMAP.md` § Status legend and § Fix
|
|
140
|
+
status legend), so it never satisfies or suppresses a
|
|
141
141
|
pre-execution/verification gate, never retires the obligation to re-derive a
|
|
142
142
|
receipt, and never lets a reviewer or consumer report a unit closed while its
|
|
143
143
|
PR is unmerged. The complement of §5's "no verdict *from* a status" — and the
|