@warpgogol/forge 2.8.0 → 2.8.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.
- package/AGENTS.md +1 -1
- package/README.md +23 -13
- package/README.uk.md +19 -12
- package/dist/os/session/handlers/validate.d.ts.map +1 -1
- package/dist/os/session/handlers/validate.js +23 -1
- package/dist/os/session/handlers/validate.js.map +1 -1
- package/dist/os/session/types.d.ts +31 -1
- package/dist/os/session/types.d.ts.map +1 -1
- package/dist/os/session/types.js +7 -0
- package/dist/os/session/types.js.map +1 -1
- package/os/adr/adr.module.ts +158 -0
- package/os/adr/frontmatter-io.ts +80 -0
- package/os/adr/handlers/archive.ts +244 -0
- package/os/adr/handlers/implement-stamp.ts +331 -0
- package/os/adr/handlers/list-create.ts +207 -0
- package/os/adr/handlers/validate.test.ts +232 -0
- package/os/adr/handlers/validate.ts +429 -0
- package/os/adr/index.ts +33 -0
- package/os/adr/types.ts +141 -0
- package/os/audit/audit.module.ts +48 -0
- package/os/audit/frontmatter-io.ts +84 -0
- package/os/audit/handlers/archive.ts +239 -0
- package/os/audit/index.ts +23 -0
- package/os/audit/types.ts +29 -0
- package/os/compass/compass.module.ts +186 -0
- package/os/compass/handlers/compass-audit-handler.ts +382 -0
- package/os/compass/handlers/compass-change-summary-handler.ts +272 -0
- package/os/compass/handlers/compass-inventory-handler.ts +294 -0
- package/os/compass/handlers/compass-inventory.ts +521 -0
- package/os/compass/handlers/git-revision.ts +137 -0
- package/os/compass/handlers/resolve-scan-root-workpiece.test.ts +65 -0
- package/os/compass/handlers/resolve-scan-root.ts +79 -0
- package/os/compass/index.ts +33 -0
- package/os/core/core.module.ts +948 -0
- package/os/core/handlers/assets-check.ts +122 -0
- package/os/core/handlers/assets-helpers.ts +174 -0
- package/os/core/handlers/assets-list.ts +99 -0
- package/os/core/handlers/build.ts +155 -0
- package/os/core/handlers/determinism-check.ts +354 -0
- package/os/core/handlers/dev.ts +127 -0
- package/os/core/handlers/invariant-engine.test.ts +681 -0
- package/os/core/handlers/knowledge-compact.ts +248 -0
- package/os/core/handlers/lifecycle-handlers.test.ts +524 -0
- package/os/core/handlers/note-frontmatter-validate.test.ts +93 -0
- package/os/core/handlers/note-link-validate.test.ts +117 -0
- package/os/core/handlers/note-orphan-detect.test.ts +94 -0
- package/os/core/handlers/package-health.test.ts +226 -0
- package/os/core/handlers/package-health.ts +231 -0
- package/os/core/handlers/pinned-check.ts +200 -0
- package/os/core/handlers/pinned-init.ts +333 -0
- package/os/core/handlers/pinned-types.ts +50 -0
- package/os/core/handlers/pinned-validate.ts +301 -0
- package/os/core/handlers/profile-resolve.ts +92 -0
- package/os/core/handlers/release-prepare.ts +294 -0
- package/os/core/handlers/release-publish.ts +211 -0
- package/os/core/handlers/validate.ts +262 -0
- package/os/core/handlers/workspace-deps.ts +55 -0
- package/os/core/index.ts +13 -0
- package/os/exploration/exploration.module.ts +90 -0
- package/os/exploration/frontmatter-io.ts +77 -0
- package/os/exploration/handlers/archive.ts +159 -0
- package/os/exploration/handlers/list.ts +77 -0
- package/os/exploration/handlers/show.ts +107 -0
- package/os/exploration/index.ts +25 -0
- package/os/exploration/types.ts +65 -0
- package/os/mission/handlers/archive.test.ts +391 -0
- package/os/mission/handlers/archive.ts +453 -0
- package/os/mission/index.ts +1 -0
- package/os/mission/mission.module.ts +48 -0
- package/os/mission/types.ts +41 -0
- package/os/naming/index.ts +14 -0
- package/os/naming/naming-convention.test.ts +163 -0
- package/os/naming/naming-convention.ts +378 -0
- package/os/naming/naming.module.ts +36 -0
- package/os/notes/index.ts +10 -0
- package/os/notes/notes.module.ts +109 -0
- package/os/plan/frontmatter-io.ts +82 -0
- package/os/plan/handlers/archive.ts +238 -0
- package/os/plan/index.ts +23 -0
- package/os/plan/plan.module.ts +48 -0
- package/os/plan/types.ts +29 -0
- package/os/program/discovery.ts +294 -0
- package/os/program/handlers/complete.ts +419 -0
- package/os/program/handlers/lease.ts +433 -0
- package/os/program/handlers/seal.ts +332 -0
- package/os/program/handlers/validate.ts +213 -0
- package/os/program/lease.ts +156 -0
- package/os/program/program.module.ts +212 -0
- package/os/program/schemas.ts +218 -0
- package/os/program/state.ts +264 -0
- package/os/rfc/acceptance.ts +344 -0
- package/os/rfc/decision-log.ts +310 -0
- package/os/rfc/dna-trace.ts +280 -0
- package/os/rfc/frontmatter-io.test.ts +132 -0
- package/os/rfc/frontmatter-io.ts +130 -0
- package/os/rfc/handlers/archive.ts +300 -0
- package/os/rfc/handlers/check.ts +238 -0
- package/os/rfc/handlers/implement-stamp.ts +515 -0
- package/os/rfc/handlers/index-graph.ts +237 -0
- package/os/rfc/handlers/lifecycle.test.ts +127 -0
- package/os/rfc/handlers/lifecycle.ts +249 -0
- package/os/rfc/handlers/list-create.ts +296 -0
- package/os/rfc/handlers/pipeline-status.ts +173 -0
- package/os/rfc/handlers/shared.ts +121 -0
- package/os/rfc/handlers/supersede-propose.ts +241 -0
- package/os/rfc/handlers/validate-rules.test.ts +891 -0
- package/os/rfc/handlers/validate-rules.ts +984 -0
- package/os/rfc/handlers/validate.ts +166 -0
- package/os/rfc/handlers.ts +19 -0
- package/os/rfc/index.ts +97 -0
- package/os/rfc/rfc.module.ts +416 -0
- package/os/rfc/types.ts +596 -0
- package/os/rfc/verification-evidence.ts +244 -0
- package/os/session/atif-parser.ts +166 -0
- package/os/session/frontmatter-io.ts +163 -0
- package/os/session/handlers/archive.ts +288 -0
- package/os/session/handlers/list.ts +174 -0
- package/os/session/handlers/save.ts +348 -0
- package/os/session/handlers/validate.ts +250 -0
- package/os/session/index.ts +50 -0
- package/os/session/session.module.ts +126 -0
- package/os/session/types.ts +203 -0
- package/os/spec/live-spec-list-show-validate.test.ts +257 -0
- package/os/spec/live-spec-list.ts +78 -0
- package/os/spec/live-spec-merge.test.ts +261 -0
- package/os/spec/live-spec-merge.ts +413 -0
- package/os/spec/live-spec-show.ts +117 -0
- package/os/spec/live-spec-types.ts +93 -0
- package/os/spec/live-spec-validate.ts +170 -0
- package/os/spec/spec-materialize.ts +390 -0
- package/os/spec/spec-schema.ts +174 -0
- package/os/spec/spec-status.ts +284 -0
- package/os/spec/spec-validate.ts +513 -0
- package/os/spec/spec.module.ts +135 -0
- package/os/werkstatt/handlers/lock.ts +168 -0
- package/os/werkstatt/handlers/schema.ts +47 -0
- package/os/werkstatt/handlers/werkstatt-lock-recover.ts +175 -0
- package/os/werkstatt/handlers/werkstatt-lock-status.ts +73 -0
- package/os/werkstatt/handlers/werkstatt-operation-validate.ts +104 -0
- package/os/werkstatt/index.ts +39 -0
- package/os/werkstatt/werkstatt.module.ts +62 -0
- package/os/workflow/handlers.ts +304 -0
- package/os/workflow/index.ts +28 -0
- package/os/workflow/types.ts +99 -0
- package/os/workflow/workflow.module.ts +54 -0
- package/package.json +3 -3
- package/profiles/godot-csharp.yaml +4 -4
- package/profiles/knowledge-typescript-turborepo.yaml +110 -0
- package/profiles/phaser-turborepo.yaml +5 -5
- package/skills/_shared/fo-session-summary.md +132 -16
- package/skills/fo/fo-add-tests/pbt-guide.md +13 -13
- package/skills/fo/fo-doc-audit/SKILL.md +6 -6
- package/skills/fo/fo-handoff/SKILL.md +5 -0
- package/skills/fo/fo-idea/SKILL.md +1 -1
- package/skills/fo/fo-review/SKILL.md +1 -1
- package/skills/fo/fo-session-retro/SKILL.md +8 -0
- package/skills/fo/fo-session-save/SKILL.md +8 -0
- package/src/cli-output.ts +59 -0
- package/src/config/__tests__/resolve-terminology.test.ts +45 -0
- package/src/config/forge-config.ts +524 -0
- package/src/forge-module.ts +39 -0
- package/src/index.ts +172 -0
- package/src/knowledge/__tests__/promote.test.ts +219 -0
- package/src/knowledge/__tests__/serialize.test.ts +192 -0
- package/src/knowledge/budgets.ts +202 -0
- package/src/knowledge/compact.ts +405 -0
- package/src/knowledge/index.ts +53 -0
- package/src/knowledge/parse.ts +227 -0
- package/src/knowledge/promote.ts +156 -0
- package/src/knowledge/schema.ts +112 -0
- package/src/knowledge/serialize.ts +72 -0
- package/src/migration-adapters/__tests__/ignored-files.test.ts +51 -0
- package/src/migration-adapters/__tests__/registry.test.ts +131 -0
- package/src/migration-adapters/__tests__/types.test.ts +86 -0
- package/src/migration-adapters/git-utils.ts +71 -0
- package/src/migration-adapters/ignored-files.ts +221 -0
- package/src/migration-adapters/index.ts +21 -0
- package/src/migration-adapters/node-typescript-pnpm/index.ts +189 -0
- package/src/migration-adapters/phaser-pnpm/index.ts +189 -0
- package/src/migration-adapters/registry.ts +61 -0
- package/src/migration-adapters/types.ts +68 -0
- package/src/onboarding/__tests__/workspace-discovery.test.ts +134 -0
- package/src/onboarding/agents-generate.ts +443 -0
- package/src/onboarding/create.ts +427 -0
- package/src/onboarding/doctor.ts +1297 -0
- package/src/onboarding/init.ts +337 -0
- package/src/onboarding/invariant-engine.ts +448 -0
- package/src/onboarding/memory-scaffold.ts +189 -0
- package/src/onboarding/nested-agents-generate.ts +93 -0
- package/src/onboarding/nested-agents-templates.ts +233 -0
- package/src/onboarding/profile-validate.ts +139 -0
- package/src/onboarding/scaffold-project.ts +299 -0
- package/src/onboarding/scaffold.ts +143 -0
- package/src/onboarding/upgrade.ts +558 -0
- package/src/onboarding/workspace-discovery.ts +167 -0
- package/src/profiles/__tests__/profile-schema.test.ts +228 -0
- package/src/profiles/__tests__/stack-profile.test.ts +190 -0
- package/src/profiles/__tests__/terminology-utils.test.ts +71 -0
- package/src/profiles/profile-schema.ts +350 -0
- package/src/profiles/stack-profile.ts +188 -0
- package/src/profiles/terminology-utils.ts +36 -0
- package/src/registry.ts +185 -0
- package/src/skill-schema.ts +37 -0
- package/src/tests/acceptance-criteria.test.ts +99 -0
- package/src/tests/adr-implement-stamp.test.ts +368 -0
- package/src/tests/agents-generate-domain.test.ts +317 -0
- package/src/tests/agents-generate.test.ts +458 -0
- package/src/tests/bindings-schema.test.ts +314 -0
- package/src/tests/budgets.test.ts +213 -0
- package/src/tests/cli-output.test.ts +196 -0
- package/src/tests/compact.test.ts +796 -0
- package/src/tests/create.test.ts +289 -0
- package/src/tests/doctor-autonomy.test.ts +98 -0
- package/src/tests/doctor-bindings.test.ts +286 -0
- package/src/tests/doctor-domain.test.ts +113 -0
- package/src/tests/exploration-handlers.test.ts +256 -0
- package/src/tests/fixtures/agents-generate-business-before.txt +288 -0
- package/src/tests/forge-config.test.ts +420 -0
- package/src/tests/fs-atomic.test.ts +96 -0
- package/src/tests/fs.test.ts +103 -0
- package/src/tests/generated-marker.test.ts +161 -0
- package/src/tests/hash.test.ts +37 -0
- package/src/tests/implement-stamp.test.ts +505 -0
- package/src/tests/init-bindings.test.ts +183 -0
- package/src/tests/knowledge-parse.test.ts +307 -0
- package/src/tests/knowledge-pbt.test.ts +164 -0
- package/src/tests/memory-scaffold.test.ts +192 -0
- package/src/tests/migration-adapters.test.ts +480 -0
- package/src/tests/package-files.test.ts +54 -0
- package/src/tests/pinned-check.test.ts +194 -0
- package/src/tests/pinned-init.test.ts +126 -0
- package/src/tests/profile-schema.test.ts +194 -0
- package/src/tests/profile-validate.test.ts +120 -0
- package/src/tests/program-lease.test.ts +245 -0
- package/src/tests/program-paths-extended.test.ts +117 -0
- package/src/tests/program-paths.test.ts +98 -0
- package/src/tests/program-property.test.ts +139 -0
- package/src/tests/program-schemas.test.ts +445 -0
- package/src/tests/program-spec-node.test.ts +250 -0
- package/src/tests/promote.test.ts +481 -0
- package/src/tests/registry.test.ts +73 -0
- package/src/tests/scaffold-project.test.ts +150 -0
- package/src/tests/session-handlers.test.ts +503 -0
- package/src/tests/session-pbt.test.ts +190 -0
- package/src/tests/skill-schema.test.ts +146 -0
- package/src/tests/skill-validate-knowledge.test.ts +26 -0
- package/src/tests/skill-validate.test.ts +254 -0
- package/src/tests/stack-profile.test.ts +221 -0
- package/src/tests/string-utils.test.ts +42 -0
- package/src/tests/upgrade.test.ts +428 -0
- package/src/tests/werkstatt-lock.test.ts +343 -0
- package/src/tests/workspace-discovery-domain.test.ts +112 -0
- package/src/tests/workspace-discovery.test.ts +135 -0
- package/src/types.ts +221 -0
- package/src/utils/__tests__/fs-idempotent.test.ts +73 -0
- package/src/utils/__tests__/fs-trash.test.ts +45 -0
- package/src/utils/fs-atomic.ts +93 -0
- package/src/utils/fs-idempotent.ts +41 -0
- package/src/utils/fs-trash-sync.ts +37 -0
- package/src/utils/fs-trash.ts +24 -0
- package/src/utils/fs.ts +74 -0
- package/src/utils/generated-marker.ts +166 -0
- package/src/utils/hash.ts +19 -0
- package/src/utils/index.ts +29 -0
- package/src/utils/string-utils.ts +19 -0
- package/src/validators/__tests__/note-orphan-detect.test.ts +94 -0
- package/src/validators/note-frontmatter-validate.ts +142 -0
- package/src/validators/note-link-validate.ts +145 -0
- package/src/validators/note-orphan-detect.ts +146 -0
- package/src/validators/port-validate.ts +97 -0
- package/src/validators/skill-validate.ts +831 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/*
|
|
2
|
+
<MODULE_CONTRACT>
|
|
3
|
+
<purpose>
|
|
4
|
+
RFC-0334: rfc.supersede.propose — escalation command for blocked implementations.
|
|
5
|
+
Scaffolds a new draft RFC with supersedes pre-linked, conflict context, and TODO sections.
|
|
6
|
+
</purpose>
|
|
7
|
+
<non-goals>
|
|
8
|
+
<item>Does not modify the blocked RFC — supersededBy is set only by humans.</item>
|
|
9
|
+
<item>No automatic conflict detection — agent judgment triggers escalation.</item>
|
|
10
|
+
</non-goals>
|
|
11
|
+
</MODULE_CONTRACT>
|
|
12
|
+
<CHANGE_SUMMARY>
|
|
13
|
+
<item>RFC-0334: initial implementation.</item>
|
|
14
|
+
<item>Post-refactor hardening: supersede proposals resolve archived RFC files by basename id.</item>
|
|
15
|
+
</CHANGE_SUMMARY>
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readFile } from "node:fs/promises";
|
|
19
|
+
import { join, basename } from "node:path";
|
|
20
|
+
|
|
21
|
+
import { toKebabCase } from "../../../src/utils/string-utils.ts";
|
|
22
|
+
import { writeFileAtomic } from "../../../src/utils/fs-atomic.ts";
|
|
23
|
+
import { listRfcFiles, readAndParseRfc, rfcFileMatchesId } from "../frontmatter-io.ts";
|
|
24
|
+
import { collectDecisionLog, scoreDecisions } from "../decision-log.ts";
|
|
25
|
+
import { RFC_DIR } from "../types.ts";
|
|
26
|
+
import { resolveRfcTemplate } from "./shared.ts";
|
|
27
|
+
import type { ConsultedDecision, RfcSupersedeProposeResult } from "../types.ts";
|
|
28
|
+
import type {
|
|
29
|
+
ForgeCommandInput,
|
|
30
|
+
ForgeCommandResult,
|
|
31
|
+
ForgeRuntimeContext,
|
|
32
|
+
} from "../../../src/types.ts";
|
|
33
|
+
import { toIsoDate } from "./shared.ts";
|
|
34
|
+
|
|
35
|
+
const DNA_REGISTRY_PATH = "docs/architecture-dna.md";
|
|
36
|
+
const DNA_HEADING_RE = /^##\s+DNA-(\d+)\b/gm;
|
|
37
|
+
|
|
38
|
+
async function validateDnaIds(workspaceRoot: string, ids: string[]): Promise<string[]> {
|
|
39
|
+
const errors: string[] = [];
|
|
40
|
+
const registrySrc = await readFile(join(workspaceRoot, DNA_REGISTRY_PATH), "utf-8");
|
|
41
|
+
const knownDna = new Set<string>();
|
|
42
|
+
for (const m of registrySrc.matchAll(DNA_HEADING_RE)) {
|
|
43
|
+
knownDna.add(`DNA-${m[1]}`);
|
|
44
|
+
}
|
|
45
|
+
for (const id of ids) {
|
|
46
|
+
if (id.startsWith("DNA-")) {
|
|
47
|
+
if (!knownDna.has(id)) {
|
|
48
|
+
errors.push(`Unknown DNA invariant: ${id} (not found in ${DNA_REGISTRY_PATH}).`);
|
|
49
|
+
}
|
|
50
|
+
} else if (id.startsWith("RFC-")) {
|
|
51
|
+
const rfcDirPath = join(workspaceRoot, RFC_DIR);
|
|
52
|
+
const files = await listRfcFiles(rfcDirPath);
|
|
53
|
+
const targetFile = files.find((f) => rfcFileMatchesId(f, id));
|
|
54
|
+
if (!targetFile) {
|
|
55
|
+
errors.push(`Unknown RFC reference: ${id} (not found in ${RFC_DIR}).`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return errors;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function runRfcSupersedePropose(
|
|
63
|
+
input: ForgeCommandInput,
|
|
64
|
+
context: ForgeRuntimeContext,
|
|
65
|
+
): Promise<ForgeCommandResult<RfcSupersedeProposeResult>> {
|
|
66
|
+
const { workspaceRoot, logger, outputFormat } = context;
|
|
67
|
+
const rfcDirPath = join(workspaceRoot, RFC_DIR);
|
|
68
|
+
|
|
69
|
+
const targetId = input.flags["id"] as string | undefined;
|
|
70
|
+
const reason = input.flags["reason"] as string | undefined;
|
|
71
|
+
const invariantStr = input.flags["invariant"] as string | undefined;
|
|
72
|
+
const customTitle = input.flags["title"] as string | undefined;
|
|
73
|
+
|
|
74
|
+
if (!targetId) throw new Error("rfc.supersede.propose requires --id flag (e.g. --id RFC-0322).");
|
|
75
|
+
if (!reason) throw new Error("rfc.supersede.propose requires --reason flag.");
|
|
76
|
+
if (!invariantStr)
|
|
77
|
+
throw new Error(
|
|
78
|
+
"rfc.supersede.propose requires --invariant flag (comma-separated DNA-N or RFC-XXXX).",
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Parse and validate invariant ids
|
|
82
|
+
const invariantIds = invariantStr
|
|
83
|
+
.split(",")
|
|
84
|
+
.map((s) => s.trim())
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
const idRe = /^(DNA-\d+|RFC-\d{4})$/;
|
|
87
|
+
for (const id of invariantIds) {
|
|
88
|
+
if (!idRe.test(id)) {
|
|
89
|
+
throw new Error(`Malformed invariant id: "${id}". Must match DNA-N or RFC-XXXX.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Validate invariants exist
|
|
94
|
+
const invariantErrors = await validateDnaIds(workspaceRoot, invariantIds);
|
|
95
|
+
if (invariantErrors.length > 0) {
|
|
96
|
+
throw new Error(invariantErrors.join(" "));
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Find and validate target RFC
|
|
100
|
+
const files = await listRfcFiles(rfcDirPath);
|
|
101
|
+
const targetFile = files.find((f) => rfcFileMatchesId(f, targetId));
|
|
102
|
+
if (!targetFile) {
|
|
103
|
+
throw new Error(`Target RFC ${targetId} not found. Use rfc.list to see available RFCs.`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const targetParsed = await readAndParseRfc(rfcDirPath, targetFile);
|
|
107
|
+
if (!targetParsed) {
|
|
108
|
+
throw new Error(`Could not parse target RFC ${targetId}.`);
|
|
109
|
+
}
|
|
110
|
+
if ("error" in targetParsed) {
|
|
111
|
+
throw new Error(`Could not parse target RFC ${targetId}: ${targetParsed.error}`);
|
|
112
|
+
}
|
|
113
|
+
const targetFm = targetParsed.parsed.frontmatter;
|
|
114
|
+
const targetStatus = String(targetFm["status"] ?? "");
|
|
115
|
+
const targetTitle = String(targetFm["title"] ?? "");
|
|
116
|
+
|
|
117
|
+
if (targetStatus !== "accepted" && targetStatus !== "implemented") {
|
|
118
|
+
if (targetStatus === "draft" || targetStatus === "reviewing") {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`Target ${targetId} has status "${targetStatus}" — it is not decided yet. Record the concern in review, not via supersession.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
throw new Error(`Target ${targetId} has status "${targetStatus}" — it is already closed.`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Determine next RFC id
|
|
127
|
+
let maxId = 0;
|
|
128
|
+
for (const f of files) {
|
|
129
|
+
const bn = basename(f);
|
|
130
|
+
const match = bn.match(/^rfc-(\d{4})/);
|
|
131
|
+
if (match) {
|
|
132
|
+
const num = parseInt(match[1]!, 10);
|
|
133
|
+
if (num > maxId) maxId = num;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const nextNum = maxId + 1;
|
|
137
|
+
const paddedNum = String(nextNum).padStart(4, "0");
|
|
138
|
+
const nextSlug = `rfc-${paddedNum}`;
|
|
139
|
+
const nextId = `RFC-${paddedNum}`;
|
|
140
|
+
|
|
141
|
+
// Read template
|
|
142
|
+
const templatePath = resolveRfcTemplate(workspaceRoot);
|
|
143
|
+
let templateContent: string;
|
|
144
|
+
try {
|
|
145
|
+
templateContent = await readFile(templatePath, "utf-8");
|
|
146
|
+
} catch {
|
|
147
|
+
throw new Error(`RFC template not found at ${templatePath}.`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const today = toIsoDate(new Date());
|
|
151
|
+
const title = customTitle ?? `Supersede ${targetId}: ${targetTitle}`;
|
|
152
|
+
const kebabTitle = toKebabCase(title);
|
|
153
|
+
const fileName = `${nextSlug}-${kebabTitle}.md`;
|
|
154
|
+
|
|
155
|
+
// Decision-log consultation (RFC-0329)
|
|
156
|
+
let consultedDecisions: ConsultedDecision[] = [];
|
|
157
|
+
try {
|
|
158
|
+
const logEntries = await collectDecisionLog(rfcDirPath);
|
|
159
|
+
consultedDecisions = scoreDecisions(title, logEntries);
|
|
160
|
+
} catch {
|
|
161
|
+
// graceful degradation
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Build consultation block
|
|
165
|
+
const invariantList = invariantIds.join(", ");
|
|
166
|
+
const targetFileRel = join(RFC_DIR, targetFile);
|
|
167
|
+
const consultationBlock = `## Context
|
|
168
|
+
|
|
169
|
+
> **Consultation request (generated by rfc.supersede.propose, ${today})**
|
|
170
|
+
>
|
|
171
|
+
> - **Blocked RFC:** ${targetId} — "${targetTitle}" (status: ${targetStatus}, ${targetFileRel})
|
|
172
|
+
> - **Violated invariant(s):** ${invariantList}
|
|
173
|
+
> - **Reason:** ${reason}
|
|
174
|
+
> - **Requested decision:** accept this replacement, reject it with clarification, or amend ${targetId}.
|
|
175
|
+
|
|
176
|
+
TODO(agent): expand — what was attempted, where exactly the conflict surfaces (file paths, contract clauses), and why no conforming implementation exists.`;
|
|
177
|
+
|
|
178
|
+
// Build frontmatter seeds
|
|
179
|
+
const supersedesYaml = `supersedes:\n - ${targetId}`;
|
|
180
|
+
const relatedYaml = `related:\n${invariantIds.map((id) => ` - ${id}`).join("\n")}`;
|
|
181
|
+
|
|
182
|
+
// Apply replacements
|
|
183
|
+
let content = templateContent;
|
|
184
|
+
content = content.replace(/^id: RFC-0000$/m, `id: ${nextId}`);
|
|
185
|
+
content = content.replace(/^title: ".*"$/m, `title: "${title}"`);
|
|
186
|
+
content = content.replace(/^kind: \w+$/m, `kind: architecture`);
|
|
187
|
+
content = content.replace(/^scope: \w+$/m, `scope: workspace`);
|
|
188
|
+
content = content.replace(/^createdAt: YYYY-MM-DD$/m, `createdAt: ${today}`);
|
|
189
|
+
content = content.replace(/^updatedAt: YYYY-MM-DD$/m, `updatedAt: ${today}`);
|
|
190
|
+
content = content.replace(/^# RFC-0000: .+$/m, `# ${nextId}: ${title}`);
|
|
191
|
+
|
|
192
|
+
// Replace supersedes
|
|
193
|
+
content = content.replace(/^supersedes: \[\]$/m, supersedesYaml);
|
|
194
|
+
|
|
195
|
+
// Replace related
|
|
196
|
+
content = content.replace(/^related:\n( # .*\n)*$/m, relatedYaml);
|
|
197
|
+
|
|
198
|
+
// Replace Context section with consultation block
|
|
199
|
+
content = content.replace(/## Context\n[\s\S]*?(?=\n## )/, consultationBlock);
|
|
200
|
+
|
|
201
|
+
// Replace Decision section with TODO
|
|
202
|
+
content = content.replace(
|
|
203
|
+
/## Decision\n[\s\S]*?(?=\n## )/,
|
|
204
|
+
`## Decision\n\nTODO(agent): describe the replacement decision that resolves the conflict without violating ${invariantList}.`,
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// Write the file
|
|
208
|
+
const targetPath = join(rfcDirPath, fileName);
|
|
209
|
+
await writeFileAtomic(targetPath, content);
|
|
210
|
+
|
|
211
|
+
const relativeFile = join(RFC_DIR, fileName);
|
|
212
|
+
|
|
213
|
+
if (outputFormat === "pretty") {
|
|
214
|
+
logger.success(`Created ${nextId}: ${title}`);
|
|
215
|
+
logger.info(`File: ${relativeFile}`);
|
|
216
|
+
logger.info("");
|
|
217
|
+
logger.warn(
|
|
218
|
+
`ESCALATION: human decision required — ${targetId} conflicts with ${invariantList}. ` +
|
|
219
|
+
`Draft created: ${relativeFile}. Implementation of ${targetId} is halted until a human accepts, rejects, or clarifies.`,
|
|
220
|
+
);
|
|
221
|
+
if (consultedDecisions.length > 0) {
|
|
222
|
+
logger.info("Prior decisions to consult:");
|
|
223
|
+
for (const d of consultedDecisions) {
|
|
224
|
+
logger.info(` ${d.rfcId} [${d.kind}] (score ${d.score}): ${d.title}`);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return {
|
|
230
|
+
data: {
|
|
231
|
+
command: "rfc.supersede.propose",
|
|
232
|
+
status: "ok",
|
|
233
|
+
file: relativeFile,
|
|
234
|
+
id: nextId,
|
|
235
|
+
supersedesTarget: targetId,
|
|
236
|
+
invariants: invariantIds,
|
|
237
|
+
consultedDecisions,
|
|
238
|
+
},
|
|
239
|
+
summary: `Created ${nextId} proposing to supersede ${targetId} (conflict: ${invariantList})`,
|
|
240
|
+
};
|
|
241
|
+
}
|