@entelligentsia/forgecli 0.2.0 → 0.3.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 +86 -0
- package/README.md +132 -5
- package/dist/bin/argv.js +5 -0
- package/dist/bin/argv.js.map +1 -1
- package/dist/bin/forge.js +1 -0
- package/dist/bin/forge.js.map +1 -1
- package/dist/extensions/forgecli/ask-user-tool.d.ts +17 -0
- package/dist/extensions/forgecli/ask-user-tool.js +139 -0
- package/dist/extensions/forgecli/ask-user-tool.js.map +1 -0
- package/dist/extensions/forgecli/forge-init.js +48 -16
- package/dist/extensions/forgecli/forge-init.js.map +1 -1
- package/dist/extensions/forgecli/hook-dispatcher.d.ts +34 -1
- package/dist/extensions/forgecli/hook-dispatcher.js +237 -3
- package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
- package/dist/extensions/forgecli/index.js +6 -2
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/store-validator.d.ts +13 -0
- package/dist/extensions/forgecli/store-validator.js +35 -0
- package/dist/extensions/forgecli/store-validator.js.map +1 -0
- package/dist/extensions/forgecli/transition-guard.d.ts +20 -0
- package/dist/extensions/forgecli/transition-guard.js +125 -0
- package/dist/extensions/forgecli/transition-guard.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// Status-transition guard — FORGE-S18-T03
|
|
2
|
+
//
|
|
3
|
+
// Encodes the legal status-transition table for each entity (task, sprint, bug)
|
|
4
|
+
// and checks whether a proposed transition is allowed.
|
|
5
|
+
//
|
|
6
|
+
// The guard reads the current status from disk via `store-cli read` (spawnSync).
|
|
7
|
+
// Fail-open: if the current-status lookup fails for any reason, the guard returns
|
|
8
|
+
// { allowed: true } and sets reason="lookup-failed". The caller (hook-dispatcher)
|
|
9
|
+
// logs this as "lookup-failed" under FORGE_HOOK_AUDIT=1 but never blocks the
|
|
10
|
+
// operation — a lookup failure must not block a valid operation.
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import * as path from "node:path";
|
|
13
|
+
// ── Legal transition tables ───────────────────────────────────────────────────
|
|
14
|
+
//
|
|
15
|
+
// Derived from task.schema.json, sprint.schema.json, bug.schema.json status enums.
|
|
16
|
+
// Each entry: fromStatus → Set<toStatus>
|
|
17
|
+
const TASK_TRANSITIONS = {
|
|
18
|
+
draft: new Set(["planned", "blocked", "escalated", "abandoned"]),
|
|
19
|
+
planned: new Set(["plan-approved", "plan-revision-required", "blocked", "escalated", "abandoned"]),
|
|
20
|
+
"plan-approved": new Set(["implementing", "blocked", "escalated", "abandoned"]),
|
|
21
|
+
implementing: new Set(["implemented", "code-revision-required", "blocked", "escalated", "abandoned"]),
|
|
22
|
+
implemented: new Set(["review-approved", "blocked", "escalated", "abandoned"]),
|
|
23
|
+
"review-approved": new Set(["approved", "blocked", "escalated", "abandoned"]),
|
|
24
|
+
approved: new Set(["committed", "blocked", "escalated", "abandoned"]),
|
|
25
|
+
"plan-revision-required": new Set(["planned", "blocked", "escalated", "abandoned"]),
|
|
26
|
+
"code-revision-required": new Set(["implementing", "blocked", "escalated", "abandoned"]),
|
|
27
|
+
// Terminal / sink states — can only be re-opened by --force.
|
|
28
|
+
blocked: new Set(["blocked", "escalated", "abandoned"]),
|
|
29
|
+
escalated: new Set(["blocked", "escalated", "abandoned"]),
|
|
30
|
+
abandoned: new Set(["blocked", "escalated", "abandoned"]),
|
|
31
|
+
committed: new Set(["blocked", "escalated", "abandoned"]),
|
|
32
|
+
};
|
|
33
|
+
const SPRINT_TRANSITIONS = {
|
|
34
|
+
planning: new Set(["active", "abandoned"]),
|
|
35
|
+
active: new Set(["completed", "partially-completed", "blocked", "abandoned"]),
|
|
36
|
+
completed: new Set(["retrospective-done"]),
|
|
37
|
+
"partially-completed": new Set(["retrospective-done"]),
|
|
38
|
+
"retrospective-done": new Set([]),
|
|
39
|
+
blocked: new Set(["active", "abandoned"]),
|
|
40
|
+
abandoned: new Set([]),
|
|
41
|
+
};
|
|
42
|
+
const BUG_TRANSITIONS = {
|
|
43
|
+
reported: new Set(["triaged", "abandoned"]),
|
|
44
|
+
triaged: new Set(["in-progress", "abandoned"]),
|
|
45
|
+
"in-progress": new Set(["fixed", "abandoned"]),
|
|
46
|
+
fixed: new Set(["verified"]),
|
|
47
|
+
verified: new Set([]),
|
|
48
|
+
abandoned: new Set([]),
|
|
49
|
+
};
|
|
50
|
+
const ENTITY_TABLES = {
|
|
51
|
+
task: TASK_TRANSITIONS,
|
|
52
|
+
sprint: SPRINT_TRANSITIONS,
|
|
53
|
+
bug: BUG_TRANSITIONS,
|
|
54
|
+
};
|
|
55
|
+
function legalNextStates(entity, fromStatus) {
|
|
56
|
+
const table = ENTITY_TABLES[entity];
|
|
57
|
+
if (!table)
|
|
58
|
+
return [];
|
|
59
|
+
const allowed = table[fromStatus];
|
|
60
|
+
return allowed ? [...allowed] : [];
|
|
61
|
+
}
|
|
62
|
+
// ── Current-status lookup (fail-open) ────────────────────────────────────────
|
|
63
|
+
/**
|
|
64
|
+
* Read the current status of an entity from the store via `store-cli read`.
|
|
65
|
+
* Returns the status string on success, or null on any failure (fail-open).
|
|
66
|
+
*/
|
|
67
|
+
function readCurrentStatus(entity, entityId, forgeRoot) {
|
|
68
|
+
const storeCliPath = path.join(forgeRoot, "tools", "store-cli.cjs");
|
|
69
|
+
try {
|
|
70
|
+
const result = spawnSync(process.execPath, [storeCliPath, "read", entity, entityId], {
|
|
71
|
+
encoding: "utf8",
|
|
72
|
+
timeout: 10_000,
|
|
73
|
+
});
|
|
74
|
+
if (result.status !== 0 || result.error)
|
|
75
|
+
return null;
|
|
76
|
+
const stdout = result.stdout?.trim();
|
|
77
|
+
if (!stdout)
|
|
78
|
+
return null;
|
|
79
|
+
// store-cli read emits JSON; parse and extract the status field.
|
|
80
|
+
const record = JSON.parse(stdout);
|
|
81
|
+
const status = record.status;
|
|
82
|
+
return typeof status === "string" ? status : null;
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Any parse error or subprocess error → fail-open.
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check whether a status transition is legal.
|
|
91
|
+
*
|
|
92
|
+
* Fail-open: if the current status cannot be read, returns
|
|
93
|
+
* `{ allowed: true, reason: "lookup-failed" }`. The caller
|
|
94
|
+
* should audit-log this outcome but MUST NOT block the operation.
|
|
95
|
+
*
|
|
96
|
+
* @param input Entity, entity ID, and target status.
|
|
97
|
+
* @param forgeRoot Absolute path to the Forge plugin root.
|
|
98
|
+
*/
|
|
99
|
+
export function checkTransition(input, forgeRoot) {
|
|
100
|
+
const { entity, entityId, toStatus } = input;
|
|
101
|
+
// Lookup current status — fail-open on any error.
|
|
102
|
+
const fromStatus = readCurrentStatus(entity, entityId, forgeRoot);
|
|
103
|
+
if (fromStatus === null) {
|
|
104
|
+
return {
|
|
105
|
+
allowed: true,
|
|
106
|
+
reason: "lookup-failed",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const table = ENTITY_TABLES[entity];
|
|
110
|
+
if (!table) {
|
|
111
|
+
// Unknown entity type — allow through (future-proofing).
|
|
112
|
+
return { allowed: true, reason: "" };
|
|
113
|
+
}
|
|
114
|
+
const allowed = table[fromStatus]?.has(toStatus) ?? false;
|
|
115
|
+
if (allowed) {
|
|
116
|
+
return { allowed: true, reason: "" };
|
|
117
|
+
}
|
|
118
|
+
const legal = legalNextStates(entity, fromStatus);
|
|
119
|
+
const legalStr = legal.length > 0 ? legal.join(", ") : "(none)";
|
|
120
|
+
return {
|
|
121
|
+
allowed: false,
|
|
122
|
+
reason: `${fromStatus} → ${toStatus} is not a legal transition for ${entity}. Legal next states from ${fromStatus}: ${legalStr}.`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=transition-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transition-guard.js","sourceRoot":"","sources":["../../../src/extensions/forgecli/transition-guard.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entelligentsia/forgecli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Forge SDLC ported onto @earendil-works/pi-coding-agent — production launcher with three bin aliases (forge/forgecli/4ge).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Entelligentsia",
|