@warpgogol/forge 4.0.0 → 4.1.2

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.
Files changed (46) hide show
  1. package/AGENTS.md +18 -3
  2. package/bin/cli.ts +15 -8
  3. package/os/adr/adr.module.ts +24 -23
  4. package/os/adr/index.ts +1 -1
  5. package/os/audit/audit.module.ts +13 -8
  6. package/os/audit/index.ts +1 -1
  7. package/os/compass/compass.module.ts +22 -19
  8. package/os/core/core.module.ts +842 -864
  9. package/os/core/handlers/package-health.ts +20 -0
  10. package/os/core/index.ts +1 -1
  11. package/os/exploration/exploration.module.ts +19 -16
  12. package/os/exploration/index.ts +2 -2
  13. package/os/mission/index.ts +1 -1
  14. package/os/mission/mission.module.ts +13 -8
  15. package/os/naming/index.ts +1 -1
  16. package/os/naming/naming-convention.ts +1 -0
  17. package/os/naming/naming.module.ts +13 -7
  18. package/os/notes/index.ts +2 -2
  19. package/os/notes/notes.module.ts +20 -17
  20. package/os/plan/index.ts +1 -1
  21. package/os/plan/plan.module.ts +13 -8
  22. package/os/plugin/plugin.module.ts +10 -8
  23. package/os/program/program.module.ts +22 -20
  24. package/os/rfc/handlers/implement-stamp.ts +17 -1
  25. package/os/rfc/index.ts +1 -1
  26. package/os/rfc/rfc-0000-template.md +3 -2
  27. package/os/rfc/rfc.module.ts +54 -84
  28. package/os/rfc/types.ts +1 -0
  29. package/os/session/handlers/metrics-aggregate.ts +208 -0
  30. package/os/session/handlers/metrics-rfc.ts +339 -0
  31. package/os/session/handlers/metrics-session.ts +209 -0
  32. package/os/session/handlers/save.ts +19 -0
  33. package/os/session/index.ts +16 -1
  34. package/os/session/session.module.ts +133 -107
  35. package/os/session/types.ts +99 -0
  36. package/os/spec/spec.module.ts +28 -29
  37. package/os/werkstatt/werkstatt.module.ts +12 -9
  38. package/os/workflow/index.ts +1 -1
  39. package/os/workflow/workflow.module.ts +18 -12
  40. package/package.json +3 -1
  41. package/src/forge-module.ts +24 -10
  42. package/src/index.ts +12 -12
  43. package/src/onboarding/scaffold.ts +6 -4
  44. package/src/tests/metrics-rfc-1053.test.ts +355 -0
  45. package/src/types/werkstatt-engine-shims.d.ts +126 -48
  46. package/src/types/werkstatt-shared-shims.d.ts +6 -0
@@ -8,6 +8,7 @@
8
8
  </MODULE_CONTRACT>
9
9
  <CHANGE_SUMMARY>
10
10
  <item>Initial: forge.package.health validator — engines, CI workflow, extract config, devDeps completeness.</item>
11
+ <item>PKG-HEALTH-05: reject any versionBump in extract.config.yaml — version in package.json is source of truth.</item>
11
12
  </CHANGE_SUMMARY>
12
13
  */
13
14
 
@@ -156,6 +157,25 @@ export async function runPackageHealth(
156
157
  file: pkgDir,
157
158
  fixHint: "Create extract.config.yaml with git remote and extraction settings.",
158
159
  });
160
+ } else {
161
+ // CHECK 3b: versionBump must be absent — version in package.json is source of truth
162
+ try {
163
+ const configRaw = fs.readFileSync(extractConfigPath, "utf8");
164
+ const versionBumpMatch = configRaw.match(/^versionBump:\s*(\S+)/m);
165
+ if (versionBumpMatch) {
166
+ violations.push({
167
+ ruleId: "PKG-HEALTH-05",
168
+ packageName,
169
+ severity: "error",
170
+ message: `extract.config.yaml has versionBump: ${versionBumpMatch[1]} — this auto-increments the version on every extract. The version in package.json is the source of truth.`,
171
+ file: extractConfigPath,
172
+ fixHint:
173
+ "Remove the versionBump line. Manually bump package.json version to the target before extracting.",
174
+ });
175
+ }
176
+ } catch {
177
+ // Read error — skip this check
178
+ }
159
179
  }
160
180
 
161
181
  // CHECK 4: devDependencies completeness — tools used in scripts must be declared
package/os/core/index.ts CHANGED
@@ -10,4 +10,4 @@
10
10
  </CHANGE_SUMMARY>
11
11
  */
12
12
 
13
- export { forgeCoreModule } from "./core.module.ts";
13
+ export { createForgeCoreModule } from "./core.module.ts";
@@ -12,16 +12,17 @@
12
12
 
13
13
  import type { ForgeModule } from "../../src/forge-module.ts";
14
14
 
15
- export const forgeExplorationModule: ForgeModule = {
15
+ export async function createForgeExplorationModule(): Promise<ForgeModule> {
16
+ const { runExplorationList } = await import("./handlers/list.ts");
17
+ const { runExplorationShow } = await import("./handlers/show.ts");
18
+ const { runExplorationArchive } = await import("./handlers/archive.ts");
19
+ return {
16
20
  name: "forge-exploration",
17
21
  version: "0.1.0",
18
22
  runtime: "autonomous",
19
- async register(registry) {
20
- const { runExplorationList } = await import("./handlers/list.ts");
21
- const { runExplorationShow } = await import("./handlers/show.ts");
22
- const { runExplorationArchive } = await import("./handlers/archive.ts");
23
-
24
- registry.registerCommand({
23
+ declarations: [],
24
+ commands: [
25
+ {
25
26
  name: "exploration.list",
26
27
  description:
27
28
  "List all exploration notes in docs/explorations/. Returns id, title, status, and createdAt for each note. " +
@@ -39,9 +40,8 @@ export const forgeExplorationModule: ForgeModule = {
39
40
  },
40
41
  },
41
42
  execute: runExplorationList,
42
- });
43
-
44
- registry.registerCommand({
43
+ },
44
+ {
45
45
  name: "exploration.show",
46
46
  description:
47
47
  "Show the full content of a single exploration note. Use --id <slug> to specify the note slug " +
@@ -60,9 +60,8 @@ export const forgeExplorationModule: ForgeModule = {
60
60
  },
61
61
  },
62
62
  execute: runExplorationShow,
63
- });
64
-
65
- registry.registerCommand({
63
+ },
64
+ {
66
65
  name: "exploration.archive",
67
66
  description:
68
67
  "Archive an exploration note by setting its status to 'archived'. Use --id <slug> to specify the note. " +
@@ -86,6 +85,10 @@ export const forgeExplorationModule: ForgeModule = {
86
85
  },
87
86
  },
88
87
  execute: runExplorationArchive,
89
- });
90
- },
91
- };
88
+ }
89
+ ],
90
+ pipelines: [
91
+
92
+ ]};
93
+ }
94
+ ;
@@ -6,11 +6,11 @@
6
6
  </non-goals>
7
7
  </MODULE_CONTRACT>
8
8
  <CHANGE_SUMMARY>
9
- <item>RFC-0710: expose forgeExplorationModule and exploration types from the exploration domain.</item>
9
+ <item>RFC-0710: expose createForgeExplorationModule and exploration types from the exploration domain.</item>
10
10
  </CHANGE_SUMMARY>
11
11
  */
12
12
 
13
- export { forgeExplorationModule } from "./exploration.module.ts";
13
+ export { createForgeExplorationModule } from "./exploration.module.ts";
14
14
  export { runExplorationList } from "./handlers/list.ts";
15
15
  export { runExplorationShow } from "./handlers/show.ts";
16
16
  export { runExplorationArchive } from "./handlers/archive.ts";
@@ -1 +1 @@
1
- export { forgeMissionModule } from "./mission.module.ts";
1
+ export { createForgeMissionModule } from "./mission.module.ts";
@@ -13,14 +13,15 @@
13
13
 
14
14
  import type { ForgeModule } from "../../src/forge-module.ts";
15
15
 
16
- export const forgeMissionModule: ForgeModule = {
16
+ export async function createForgeMissionModule(): Promise<ForgeModule> {
17
+ const { runMissionArchive } = await import("./handlers/archive.ts");
18
+ return {
17
19
  name: "forge-mission",
18
20
  version: "0.1.0",
19
21
  runtime: "autonomous",
20
- async register(registry) {
21
- const { runMissionArchive } = await import("./handlers/archive.ts");
22
-
23
- registry.registerCommand({
22
+ declarations: [],
23
+ commands: [
24
+ {
24
25
  name: "mission.archive",
25
26
  description:
26
27
  "Move terminal-state mission directories (state: closed or aborted in " +
@@ -50,6 +51,10 @@ export const forgeMissionModule: ForgeModule = {
50
51
  },
51
52
  },
52
53
  execute: runMissionArchive,
53
- });
54
- },
55
- };
54
+ }
55
+ ],
56
+ pipelines: [
57
+
58
+ ]};
59
+ }
60
+ ;
@@ -10,5 +10,5 @@
10
10
  </CHANGE_SUMMARY>
11
11
  */
12
12
 
13
- export { forgeNamingModule } from "./naming.module.ts";
13
+ export { createForgeNamingModule } from "./naming.module.ts";
14
14
  export { runNamingConventionLint } from "./naming-convention.ts";
@@ -102,6 +102,7 @@ const NAMING_CONVENTION_EXEMPT_DIRS = new Set([
102
102
  "docs/performance", // Tool-generated screenshots and Lighthouse reports with timestamps
103
103
  "docs/specs", // Imported specification documents with their own naming convention
104
104
  "docs/rfcs/archive", // Archived RFCs — historical artifacts, cannot be renamed
105
+ "docs/metrics", // RFC-1053: generated metrics files with RFC/session IDs in filenames
105
106
  ]);
106
107
 
107
108
  /**
@@ -12,13 +12,15 @@
12
12
 
13
13
  import type { ForgeModule } from "../../src/forge-module.ts";
14
14
 
15
- export const forgeNamingModule: ForgeModule = {
15
+ export async function createForgeNamingModule(): Promise<ForgeModule> {
16
+ const { runNamingConventionLint } = await import("./naming-convention.ts");
17
+ return {
16
18
  name: "forge-naming",
17
19
  version: "0.1.0",
18
20
  runtime: "autonomous",
19
- async register(registry) {
20
- const { runNamingConventionLint } = await import("./naming-convention.ts");
21
- registry.registerCommand({
21
+ declarations: [],
22
+ commands: [
23
+ {
22
24
  name: "naming.convention.lint",
23
25
  contract: "naming",
24
26
  rules: [],
@@ -34,6 +36,10 @@ export const forgeNamingModule: ForgeModule = {
34
36
  },
35
37
  reads: ["packages/**/*.{ts,tsx}", "apps/**/*.{ts,tsx}", "services/**/*.{ts,tsx}"],
36
38
  execute: runNamingConventionLint,
37
- });
38
- },
39
- };
39
+ }
40
+ ],
41
+ pipelines: [
42
+
43
+ ]};
44
+ }
45
+ ;
package/os/notes/index.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /*
2
2
  <MODULE_CONTRACT>
3
- <purpose>Re-export forgeNotesModule and note validator handlers.</purpose>
3
+ <purpose>Re-export createForgeNotesModule and note validator handlers.</purpose>
4
4
  </MODULE_CONTRACT>
5
5
  <CHANGE_SUMMARY>
6
6
  <item>RFC-0808: initial notes module index.</item>
7
7
  </CHANGE_SUMMARY>
8
8
  */
9
9
 
10
- export { forgeNotesModule } from "./notes.module.ts";
10
+ export { createForgeNotesModule } from "./notes.module.ts";
@@ -17,12 +17,8 @@ import type {
17
17
  ForgeRuntimeContext,
18
18
  } from "../../src/types.ts";
19
19
 
20
- export const forgeNotesModule: ForgeModule = {
21
- name: "forge-notes",
22
- version: "0.1.0",
23
- runtime: "autonomous",
24
- async register(registry) {
25
- const { runNoteLinkValidate } = await import("../../src/validators/note-link-validate.ts");
20
+ export async function createForgeNotesModule(): Promise<ForgeModule> {
21
+ const { runNoteLinkValidate } = await import("../../src/validators/note-link-validate.ts");
26
22
  const { runNoteFrontmatterValidate } =
27
23
  await import("../../src/validators/note-frontmatter-validate.ts");
28
24
  const { runNoteOrphanDetect } = await import("../../src/validators/note-orphan-detect.ts");
@@ -47,8 +43,13 @@ export const forgeNotesModule: ForgeModule = {
47
43
  ): Promise<ForgeCommandResult> => {
48
44
  return runNoteOrphanDetect(input, context);
49
45
  };
50
-
51
- registry.registerCommand({
46
+ return {
47
+ name: "forge-notes",
48
+ version: "0.1.0",
49
+ runtime: "autonomous",
50
+ declarations: [],
51
+ commands: [
52
+ {
52
53
  name: "note.link.validate",
53
54
  contract: "note",
54
55
  rules: [],
@@ -69,9 +70,8 @@ export const forgeNotesModule: ForgeModule = {
69
70
  reads: ["vault/**/*.md"],
70
71
  cacheable: false,
71
72
  execute: noteLinkValidateWrapper,
72
- });
73
-
74
- registry.registerCommand({
73
+ },
74
+ {
75
75
  name: "note.frontmatter.validate",
76
76
  contract: "note",
77
77
  rules: [],
@@ -92,9 +92,8 @@ export const forgeNotesModule: ForgeModule = {
92
92
  reads: ["vault/**/*.md"],
93
93
  cacheable: false,
94
94
  execute: noteFrontmatterValidateWrapper,
95
- });
96
-
97
- registry.registerCommand({
95
+ },
96
+ {
98
97
  name: "note.orphan.detect",
99
98
  description:
100
99
  "Detect orphan notes in a markdown note vault — notes with zero inbound wikilinks. Always exits zero (warnings, not errors).",
@@ -109,6 +108,10 @@ export const forgeNotesModule: ForgeModule = {
109
108
  reads: ["vault/**/*.md"],
110
109
  cacheable: false,
111
110
  execute: noteOrphanDetectWrapper,
112
- });
113
- },
114
- };
111
+ }
112
+ ],
113
+ pipelines: [
114
+
115
+ ]};
116
+ }
117
+ ;
package/os/plan/index.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  </CHANGE_SUMMARY>
11
11
  */
12
12
 
13
- export { forgePlanModule } from "./plan.module.ts";
13
+ export { createForgePlanModule } from "./plan.module.ts";
14
14
  export { runPlanArchive } from "./handlers/archive.ts";
15
15
  export {
16
16
  listPlanFiles,
@@ -12,14 +12,15 @@
12
12
 
13
13
  import type { ForgeModule } from "../../src/forge-module.ts";
14
14
 
15
- export const forgePlanModule: ForgeModule = {
15
+ export async function createForgePlanModule(): Promise<ForgeModule> {
16
+ const { runPlanArchive } = await import("./handlers/archive.ts");
17
+ return {
16
18
  name: "forge-plan",
17
19
  version: "0.1.0",
18
20
  runtime: "autonomous",
19
- async register(registry) {
20
- const { runPlanArchive } = await import("./handlers/archive.ts");
21
-
22
- registry.registerCommand({
21
+ declarations: [],
22
+ commands: [
23
+ {
23
24
  name: "plan.archive",
24
25
  description:
25
26
  "Move plan files whose parent RFC has terminal status " +
@@ -44,6 +45,10 @@ export const forgePlanModule: ForgeModule = {
44
45
  },
45
46
  },
46
47
  execute: runPlanArchive,
47
- });
48
- },
49
- };
48
+ }
49
+ ],
50
+ pipelines: [
51
+
52
+ ]};
53
+ }
54
+ ;
@@ -233,8 +233,9 @@ export const forgePluginModule: ForgeModule = {
233
233
  name: "forge-plugin",
234
234
  version: "0.1.0",
235
235
  runtime: "autonomous",
236
- async register(registry) {
237
- registry.registerCommand({
236
+ declarations: [],
237
+ commands: [
238
+ {
238
239
  name: "forge.plugin.validate",
239
240
  contract: "forge",
240
241
  rules: [],
@@ -246,9 +247,8 @@ export const forgePluginModule: ForgeModule = {
246
247
  reads: ["forge.yaml", "**/forge.plugin.yaml"],
247
248
  cacheable: false,
248
249
  execute: runPluginValidate,
249
- });
250
-
251
- registry.registerCommand({
250
+ },
251
+ {
252
252
  name: "forge.plugin.discover",
253
253
  description:
254
254
  "Enumerate all project-declared skill packs with valid forge.plugin.yaml manifests (RFC-0941). Returns pack id, version, prefix, and directory.",
@@ -258,6 +258,8 @@ export const forgePluginModule: ForgeModule = {
258
258
  reads: ["forge.yaml", "**/forge.plugin.yaml"],
259
259
  cacheable: false,
260
260
  execute: runPluginDiscover,
261
- });
262
- },
263
- };
261
+ }
262
+ ],
263
+ pipelines: [
264
+
265
+ ]};
@@ -13,18 +13,19 @@ kernel registry (RFC-0856).</purpose>
13
13
 
14
14
  import type { ForgeModule } from "../../src/forge-module.ts";
15
15
 
16
- export const forgeProgramModule: ForgeModule = {
17
- name: "forge-program",
18
- version: "0.1.0",
19
- runtime: "autonomous",
20
-
21
- async register(registry) {
22
- const { runValidate } = await import("./handlers/validate.ts");
16
+ export async function createForgeProgramModule(): Promise<ForgeModule> {
17
+ const { runValidate } = await import("./handlers/validate.ts");
23
18
  const { runSeal } = await import("./handlers/seal.ts");
24
19
  const { runLease } = await import("./handlers/lease.ts");
25
20
  const { runComplete } = await import("./handlers/complete.ts");
21
+ return {
22
+ name: "forge-program",
23
+ version: "0.1.0",
24
+ runtime: "autonomous",
26
25
 
27
- registry.registerCommand({
26
+ declarations: [],
27
+ commands: [
28
+ {
28
29
  name: "program.packet.validate",
29
30
  contract: "program",
30
31
  rules: [],
@@ -57,9 +58,8 @@ export const forgeProgramModule: ForgeModule = {
57
58
  reads: ["docs/plans/**/program.yaml", "docs/plans/**/*.md"],
58
59
  cacheable: false,
59
60
  execute: runValidate,
60
- });
61
-
62
- registry.registerCommand({
61
+ },
62
+ {
63
63
  name: "program.packet.seal",
64
64
  description:
65
65
  "Steward finalizes a packet against the predecessor's completion commit, " +
@@ -99,9 +99,8 @@ export const forgeProgramModule: ForgeModule = {
99
99
  reads: ["docs/plans/**/program.yaml", "docs/plans/**/*.md"],
100
100
  cacheable: false,
101
101
  execute: runSeal,
102
- });
103
-
104
- registry.registerCommand({
102
+ },
103
+ {
105
104
  name: "program.packet.lease",
106
105
  description:
107
106
  "Manage the exclusive local executor lease for a sealed packet. " +
@@ -152,9 +151,8 @@ export const forgeProgramModule: ForgeModule = {
152
151
  reads: ["docs/plans/**/program.yaml", "docs/plans/**/*.md"],
153
152
  cacheable: false,
154
153
  execute: runLease,
155
- });
156
-
157
- registry.registerCommand({
154
+ },
155
+ {
158
156
  name: "program.packet.complete",
159
157
  description:
160
158
  "Steward validates the implementation range, writes the completion report, " +
@@ -210,6 +208,10 @@ export const forgeProgramModule: ForgeModule = {
210
208
  reads: ["docs/plans/**/program.yaml", "docs/plans/**/*.md"],
211
209
  cacheable: false,
212
210
  execute: runComplete,
213
- });
214
- },
215
- };
211
+ }
212
+ ],
213
+ pipelines: [
214
+
215
+ ]};
216
+ }
217
+ ;
@@ -12,10 +12,12 @@ verification evidence, and atomically mutates RFC frontmatter.
12
12
  </non-goals>
13
13
  </MODULE_CONTRACT>
14
14
  <CHANGE_SUMMARY>
15
- <item>RFC-0476: initial implementation.</item>
15
+ <item>RFC-0476: initial rfc.implement.stamp handler with acceptance criteria evaluation, evidence checks, and atomic status transition.</item>
16
+ <item>RFC-0268: acceptance probe evidence check before stamping.</item>
16
17
  <item>RFC-0756: auto-detect implementation commit when --implementation-commit is omitted.</item>
17
18
  <item>RFC-0795: add RFC-IMP-07 dependsOn dependency gate — blocks stamping when any dependsOn entry is not implemented.</item>
18
19
  <item>RFC-0997: add RFC-IMP-08 minimum-one-probe gate — blocks stamping for post-cutoff architecture/contract/command RFCs with no acceptance probes.</item>
20
+ <item>RFC-1053: integrate generateRfcMetrics after successful stamp (non-fatal, guarded by !isDryRun).</item>
19
21
  </CHANGE_SUMMARY>
20
22
  */
21
23
 
@@ -25,6 +27,7 @@ import { join, dirname } from "node:path";
25
27
 
26
28
  import { writeFileAtomic } from "../../../src/utils/fs-atomic.ts";
27
29
  import { parse as yamlParse } from "yaml";
30
+ import { generateRfcMetrics } from "../../session/handlers/metrics-rfc.ts";
28
31
 
29
32
  import {
30
33
  listRfcFiles,
@@ -466,6 +469,18 @@ export async function runRfcImplementStamp(
466
469
  // Write the mutated RFC file atomically
467
470
  await writeFileAtomic(rfcFilePath, mutatedSource);
468
471
 
472
+ // ── RFC-1053: Generate per-RFC metrics (non-fatal, guarded by !isDryRun) ─
473
+ let metricsPath: string | undefined;
474
+ if (!isDryRun) {
475
+ try {
476
+ metricsPath = await generateRfcMetrics(workspaceRoot, targetId, logger);
477
+ } catch (metricsErr) {
478
+ logger.warn(
479
+ `[metrics] Failed to generate metrics for ${targetId}: ${(metricsErr as Error).message}`,
480
+ );
481
+ }
482
+ }
483
+
469
484
  // ── Re-emit evidence if probes exist (to update with new status) ─────────
470
485
  if (hasProbes && requiresEvidence && evidenceRelPath) {
471
486
  // Evidence already exists and passed — no need to re-emit on stamp
@@ -493,6 +508,7 @@ export async function runRfcImplementStamp(
493
508
  stampedAt,
494
509
  criteriaChecked: criteriaEval.totalChecked,
495
510
  ...(evidenceRelPath ? { evidencePath: evidenceRelPath } : {}),
511
+ ...(metricsPath ? { metricsPath } : {}),
496
512
  },
497
513
  violations: [],
498
514
  },
package/os/rfc/index.ts CHANGED
@@ -10,7 +10,7 @@
10
10
  </CHANGE_SUMMARY>
11
11
  */
12
12
 
13
- export { forgeRfcModule } from "./rfc.module.ts";
13
+ export { createForgeRfcModule } from "./rfc.module.ts";
14
14
  export {
15
15
  listRfcFiles,
16
16
  parseRfcFile,
@@ -218,8 +218,9 @@ interface ExampleResult {
218
218
 
219
219
  - [ ] AC-1: WHEN `<command> --json` is invoked, THE command SHALL return a JSON object matching the documented output schema (evidence: probe:AC-1 or test: <path>)
220
220
  - [ ] AC-2: THE `<command>` SHALL be registered in the kernel module with the correct name and scope (evidence: file: <module-path>)
221
- - [ ] AC-3: IF `<command>` receives invalid input, THEN THE command SHALL report a blocking error and exit non-zero (evidence: test: <path/to/test>)
222
- - [ ] AC-4: THE relevant `AGENTS.md` SHALL reference this RFC where agent behavior rules changed (evidence: file: <path:line>)
221
+ - [ ] AC-3: IF `<command>` receives invalid input, THEN THE command SHALL report a blocking error (evidence: test: <path/to/test>)
222
+ - [ ] AC-4: IF `<command>` receives invalid input, THEN THE command SHALL exit non-zero (evidence: test: <path/to/test>)
223
+ - [ ] AC-5: THE relevant `AGENTS.md` SHALL reference this RFC where agent behavior rules changed (evidence: file: <path:line>)
223
224
 
224
225
  ## Implementation notes for agents
225
226