gennady 0.8.4-draft.2 → 0.8.4-draft.3
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/ai/directives/sdd-v2/root.directive.xml +36 -3
- package/ai/directives/sdd-v2/router.directive.xml +22 -1
- package/ai/kit/templates/sdd-v2/root.directive.hbs +11 -3
- package/ai/kit/templates/sdd-v2/router.directive.hbs +22 -1
- package/ai/skills/agent-inbox/SKILL.md +1 -1
- package/ai/skills/sdd/SKILL.md +5 -3
- package/ai/skills/sdd-audit/SKILL.md +5 -2
- package/ai/skills/sdd-check/SKILL.md +2 -2
- package/ai/skills/sdd-code-review/SKILL.md +4 -1
- package/ai/skills/sdd-critic/SKILL.md +5 -2
- package/ai/skills/sdd-execute/SKILL.md +5 -2
- package/ai/skills/sdd-reconcile/SKILL.md +5 -2
- package/ai/skills/sdd-scaffold/SKILL.md +5 -2
- package/dist/ai/directives/sdd-v2/root.directive.xml +36 -3
- package/dist/ai/directives/sdd-v2/router.directive.xml +22 -1
- package/dist/ai/kit/templates/sdd-v2/root.directive.hbs +11 -3
- package/dist/ai/kit/templates/sdd-v2/router.directive.hbs +22 -1
- package/dist/ai/skills/agent-inbox/SKILL.md +1 -1
- package/dist/ai/skills/sdd/SKILL.md +5 -3
- package/dist/ai/skills/sdd-audit/SKILL.md +5 -2
- package/dist/ai/skills/sdd-check/SKILL.md +2 -2
- package/dist/ai/skills/sdd-code-review/SKILL.md +4 -1
- package/dist/ai/skills/sdd-critic/SKILL.md +5 -2
- package/dist/ai/skills/sdd-execute/SKILL.md +5 -2
- package/dist/ai/skills/sdd-reconcile/SKILL.md +5 -2
- package/dist/ai/skills/sdd-scaffold/SKILL.md +5 -2
- package/dist/chunks/{classify-mr-stage.logic-BiCHTmPp.js → classify-mr-stage.logic-CazmQDwZ.js} +2 -2
- package/dist/chunks/{config-index-9VHWSDQD.js → config-index-DU8-FEjq.js} +1 -1
- package/dist/chunks/{devtools-b0DwO1x_.js → devtools-1JmXJjUG.js} +1 -1
- package/dist/chunks/{eval.cmd-CwNPaNJd.js → eval.cmd-CBANnOFG.js} +1 -1
- package/dist/chunks/{help-D5cXtdMI.js → help-DVloaFF5.js} +1 -1
- package/dist/chunks/{index-BVnJEutp.js → index-2xhpqJtd.js} +2 -2
- package/dist/chunks/{index-RR5OUxje.js → index-AxsLBUAx.js} +14 -14
- package/dist/chunks/{index-CUkeTGqM.js → index-B7sydB0W.js} +2 -2
- package/dist/chunks/{index-CMCn9ErF.js → index-BKxk4zOz.js} +2 -2
- package/dist/chunks/{index-Db13NlHh.js → index-BNMmX_Mv.js} +21 -21
- package/dist/chunks/{index-CmQO-RmM.js → index-BPzldG3a.js} +2 -2
- package/dist/chunks/{index-BOQl7F_Z.js → index-BQlFDvKW.js} +2 -2
- package/dist/chunks/{index-RzkvIfJQ.js → index-Baq7RL1L.js} +3 -3
- package/dist/chunks/index-BbPiwvY9.js +2 -0
- package/dist/chunks/{index-ClJLPEUB.js → index-BreGNLJ_.js} +1 -1
- package/dist/chunks/{index-DjSafAkN.js → index-BslPwGtM.js} +2 -2
- package/dist/chunks/{index-DiMFxS0z.js → index-BtCUN2fZ.js} +16 -16
- package/dist/chunks/{index-CS0VbiLD.js → index-C10-EOdB.js} +1 -1
- package/dist/chunks/{index-2BOEcJIa.js → index-C4JmZLaj.js} +2 -2
- package/dist/chunks/index-CA-JKLr4.js +2 -0
- package/dist/chunks/{index-43GiH--T.js → index-CG_jJZVu.js} +2 -2
- package/dist/chunks/{index-BdkFKsMm.js → index-CGyP2uDE.js} +3 -3
- package/dist/chunks/{index-BOKv0b01.js → index-CPl1s9XJ.js} +2 -2
- package/dist/chunks/{index-DIPrBH-4.js → index-CSxE4cEm.js} +9 -9
- package/dist/chunks/{index-Cm7U1-fU.js → index-CWR5EzXx.js} +2 -2
- package/dist/chunks/{index-BwN5llCR.js → index-Camql5kL.js} +2 -2
- package/dist/chunks/{index-Drh-J9O7.js → index-CdUiTipO.js} +7 -7
- package/dist/chunks/{index-Cn029KTN.js → index-Crg61pK5.js} +2 -2
- package/dist/chunks/{index-Dja2O-hV.js → index-Cv9ZYXso.js} +1 -1
- package/dist/chunks/{index-Ck8v_Gv7.js → index-D7ADMlG3.js} +1 -1
- package/dist/chunks/index-DNl2gC8l.js +158 -0
- package/dist/chunks/{index-BYIzl-Ut.js → index-DVJA0qVm.js} +36 -36
- package/dist/chunks/{index-BAEIZVx0.js → index-DcFJhkY1.js} +2 -2
- package/dist/chunks/{index-Dx5_CB2E.js → index-DdEd-L3x.js} +2 -2
- package/dist/chunks/{index-DVmqMmgb.js → index-DkGA1eW_.js} +32 -32
- package/dist/chunks/{index-3k7mPX1e.js → index-Dnwxlti3.js} +1 -1
- package/dist/chunks/{index-CrizvxOJ.js → index-DqdEJDi2.js} +2 -2
- package/dist/chunks/{index-BIPebtDl.js → index-DqiwmBt4.js} +1 -1
- package/dist/chunks/{index-DS4MhAw-.js → index-PfuqBWjY.js} +2 -2
- package/dist/chunks/{index-BmFT1Kx2.js → index-SVP6RfVl.js} +2 -2
- package/dist/chunks/{index-CDxV4b81.js → index-Y8wu48Q0.js} +1 -1
- package/dist/chunks/index-_ewSKppm.js +2 -0
- package/dist/chunks/{index-Ctcr_Qeu.js → index-aBVtCLIJ.js} +2 -2
- package/dist/chunks/index-dNdz9j6d.js +2 -0
- package/dist/chunks/{index-BFUkVkSo.js → index-gskOpu3H.js} +16 -16
- package/dist/chunks/{index-CXpkT25O.js → index-pBSLMn5Q.js} +3 -3
- package/dist/chunks/index-sqPhX5Io.js +2 -0
- package/dist/chunks/{index-BzmR4Es2.js → index-vfrjhUD1.js} +39 -39
- package/dist/chunks/{index-J2NvjjDV.js → index-xGF5XC_i.js} +2 -2
- package/dist/chunks/{run-review-command.logic-CA2Rjymb.js → run-review-command.logic-B9GoCxh-.js} +24 -24
- package/dist/chunks/{serve.cmd-RxYS6WoN.js → serve.cmd-UXkaeaPI.js} +2 -2
- package/dist/chunks/{services-Csd556Tv.js → services-BYCeGBlO.js} +4 -4
- package/dist/chunks/{shared-B5CTGvpG.js → shared-B3cXL2Np.js} +1178 -1117
- package/dist/chunks/{stats-index-BLztvK69.js → stats-index-AIAnnp9S.js} +2 -2
- package/dist/gennady.js +46 -46
- package/dist/index.js +4 -4
- package/dist/shared/sdd/ladder.d.ts +42 -0
- package/dist/shared/sdd/module-specs.d.ts +7 -0
- package/dist/shared/sdd/tracker.d.ts +9 -0
- package/package.json +1 -1
- package/dist/chunks/classify-mr-stage.logic-ClmhzQPQ.js +0 -53
- package/dist/chunks/config-index-RFeqlDWs.js +0 -104
- package/dist/chunks/devtools-p8tmPuLn.js +0 -79
- package/dist/chunks/eval.cmd-ewU5lcUY.js +0 -34
- package/dist/chunks/help-DzGnMNi2.js +0 -7
- package/dist/chunks/index--AXFi0ty.js +0 -89
- package/dist/chunks/index-64TSesTc.js +0 -186
- package/dist/chunks/index-9Gv5trS_.js +0 -125
- package/dist/chunks/index-B1DQIryA.js +0 -2
- package/dist/chunks/index-B1PiBeQ3.js +0 -2
- package/dist/chunks/index-B4rzuhkt.js +0 -183
- package/dist/chunks/index-B97HJjkg.js +0 -159
- package/dist/chunks/index-BF6qypm0.js +0 -131
- package/dist/chunks/index-BK84ZQsh.js +0 -128
- package/dist/chunks/index-BRVIirjj.js +0 -141
- package/dist/chunks/index-BXnryCGW.js +0 -96
- package/dist/chunks/index-BaZm7Otb.js +0 -2
- package/dist/chunks/index-Bhk746A2.js +0 -129
- package/dist/chunks/index-Bi-eY_dY.js +0 -164
- package/dist/chunks/index-BjumU69g.js +0 -2
- package/dist/chunks/index-BkWXcpXU.js +0 -16979
- package/dist/chunks/index-BlY4jQIA.js +0 -168
- package/dist/chunks/index-C0iy7GxD.js +0 -140
- package/dist/chunks/index-CCBTXczb.js +0 -56
- package/dist/chunks/index-CDAfkSrr.js +0 -2
- package/dist/chunks/index-CDHymWzC.js +0 -102
- package/dist/chunks/index-CNfXJAao.js +0 -2
- package/dist/chunks/index-CQsae1nq.js +0 -207
- package/dist/chunks/index-CVmd0prf.js +0 -2
- package/dist/chunks/index-CZuTNeNm.js +0 -2
- package/dist/chunks/index-CasNhhrL.js +0 -96
- package/dist/chunks/index-Ce7GWENF.js +0 -377
- package/dist/chunks/index-Cf-02TAg.js +0 -265
- package/dist/chunks/index-CfjQWSrh.js +0 -52
- package/dist/chunks/index-Chr7gBBz.js +0 -443
- package/dist/chunks/index-Ci875Zlg.js +0 -346
- package/dist/chunks/index-CwA6RzIB.js +0 -3793
- package/dist/chunks/index-D1dI63ej.js +0 -46
- package/dist/chunks/index-DCS32A2i.js +0 -663
- package/dist/chunks/index-DOFoNxvb.js +0 -131
- package/dist/chunks/index-DVkCEuws.js +0 -136
- package/dist/chunks/index-DkadaaXq.js +0 -658
- package/dist/chunks/index-Dn11CLtk.js +0 -7
- package/dist/chunks/index-Do3spFJT.js +0 -340
- package/dist/chunks/index-DrFRZ0Yg.js +0 -2
- package/dist/chunks/index-DvEQhXi2.js +0 -1054
- package/dist/chunks/index-GiS0x6jg.js +0 -90
- package/dist/chunks/index-NacvqeQ5.js +0 -69
- package/dist/chunks/index-RNJhFzdr.js +0 -227
- package/dist/chunks/index-R_vUB6CL.js +0 -2
- package/dist/chunks/index-TQft1s5H.js +0 -20
- package/dist/chunks/index-ZmQRP38W.js +0 -26969
- package/dist/chunks/index-jdOZ4AkD.js +0 -143
- package/dist/chunks/index-kNa21kUI.js +0 -279
- package/dist/chunks/index-v4AV9kAc.js +0 -108
- package/dist/chunks/run-review-command.logic-CU1Slmc_.js +0 -331
- package/dist/chunks/serve.cmd-dWu_k_G_.js +0 -162
- package/dist/chunks/services-CwvXl2MB.js +0 -46832
- package/dist/chunks/shared-DSZTUx5b.js +0 -4509
- package/dist/chunks/stats-index-H6ALcjxy.js +0 -66
|
@@ -1,4509 +0,0 @@
|
|
|
1
|
-
import { spawn as gt, execSync as Ge, execFile as It, execFileSync as Ot } from "node:child_process";
|
|
2
|
-
import v, { mkdirSync as Fe, readFileSync as D, existsSync as R, readdirSync as $, statSync as oe, writeFileSync as q, rmSync as Se, renameSync as Tt } from "node:fs";
|
|
3
|
-
import k, { resolve as y, dirname as w, join as h, basename as G, relative as x, sep as b, extname as _t, posix as B } from "node:path";
|
|
4
|
-
import { platform as ge, homedir as te } from "node:os";
|
|
5
|
-
import { fileURLToPath as fe } from "node:url";
|
|
6
|
-
import { l as O, V as Nt, a as Ct } from "./services-CwvXl2MB.js";
|
|
7
|
-
import { promisify as Dt } from "node:util";
|
|
8
|
-
const At = 864e5, yt = 3e3, Rt = "update-check-worker.ts", kt = "update-check-cache.json";
|
|
9
|
-
function vt(e) {
|
|
10
|
-
return ge() === "win32" ? y(process.env.LOCALAPPDATA ?? y(te(), "AppData", "Local"), "gennady") : ge() === "darwin" ? y(te(), "Library", "Caches", "gennady") : y(te(), ".cache", "gennady");
|
|
11
|
-
}
|
|
12
|
-
function wt(e) {
|
|
13
|
-
try {
|
|
14
|
-
const t = D(e, "utf-8"), s = JSON.parse(t);
|
|
15
|
-
return typeof s?.lastCheck != "string" || typeof s?.latestVersion != "string" ? null : s;
|
|
16
|
-
} catch {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function Pt(e, t) {
|
|
21
|
-
return Date.now() - new Date(e.lastCheck).getTime() < t;
|
|
22
|
-
}
|
|
23
|
-
function Ie(e, t) {
|
|
24
|
-
const s = (r) => r.split(".").map((i) => parseInt(i, 10)), n = s(e), o = s(t);
|
|
25
|
-
if (n.some(isNaN) || o.some(isNaN)) return !1;
|
|
26
|
-
for (let r = 0; r < Math.max(n.length, o.length); r++) {
|
|
27
|
-
const i = n[r] ?? 0, a = o[r] ?? 0;
|
|
28
|
-
if (a > i) return !0;
|
|
29
|
-
if (a < i) return !1;
|
|
30
|
-
}
|
|
31
|
-
return !1;
|
|
32
|
-
}
|
|
33
|
-
function Oe(e, t) {
|
|
34
|
-
let s = !1;
|
|
35
|
-
process.on("beforeExit", () => {
|
|
36
|
-
s || (s = !0, process.stderr.write(`
|
|
37
|
-
Update available: ${e} → ${t}
|
|
38
|
-
`), process.stderr.write(`Run: npm i -g gennady@latest
|
|
39
|
-
|
|
40
|
-
`));
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
function oo(e, t) {
|
|
44
|
-
if (process.env.GENNADY_NO_UPDATE_CHECK === "1" || process.argv.includes("--no-update-check")) {
|
|
45
|
-
process.env.GENNADY_DEBUG === "1" && process.stderr.write(
|
|
46
|
-
`[update-check] skipped: opt-out (GENNADY_NO_UPDATE_CHECK or --no-update-check)
|
|
47
|
-
`
|
|
48
|
-
);
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
if (process.env.CI === "true" || process.env.CONTINUOUS_INTEGRATION === "true" || process.env.NODE_ENV === "test") {
|
|
52
|
-
process.env.GENNADY_DEBUG === "1" && process.stderr.write(`[update-check] skipped: CI/test environment
|
|
53
|
-
`);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const s = Number(process.env.GENNADY_UPDATE_CHECK_INTERVAL) || At, n = vt(), o = y(n, kt);
|
|
57
|
-
Fe(n, { recursive: !0 });
|
|
58
|
-
const r = wt(o);
|
|
59
|
-
if (r && Pt(r, s)) {
|
|
60
|
-
r.latestVersion && Ie(e.version, r.latestVersion) && (process.stderr.isTTY || t?.skipNotificationIfNoTty === !1) && Oe(e.version, r.latestVersion);
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const i = y(w(fe(import.meta.url)), Rt);
|
|
64
|
-
gt(
|
|
65
|
-
process.execPath,
|
|
66
|
-
[i, e.name, e.version, o, String(yt)],
|
|
67
|
-
{
|
|
68
|
-
stdio: "ignore"
|
|
69
|
-
}
|
|
70
|
-
).unref(), r?.latestVersion && Ie(e.version, r.latestVersion) && (process.stderr.isTTY || t?.skipNotificationIfNoTty === !1) && Oe(e.version, r.latestVersion);
|
|
71
|
-
}
|
|
72
|
-
const j = ["plain", "nocolor", "noColor", "no-color", "color=no", "color=never"].some(
|
|
73
|
-
(e) => process.argv.includes(`-${e}`) || process.argv.includes(`--${e}`)
|
|
74
|
-
), U = {
|
|
75
|
-
// Modifiers
|
|
76
|
-
reset: "\x1B[0m",
|
|
77
|
-
bold: "\x1B[1m",
|
|
78
|
-
dim: "\x1B[2m",
|
|
79
|
-
italic: "\x1B[3m",
|
|
80
|
-
underline: "\x1B[4m",
|
|
81
|
-
overline: "\x1B[53m",
|
|
82
|
-
inverse: "\x1B[7m",
|
|
83
|
-
hidden: "\x1B[8m",
|
|
84
|
-
strikethrough: "\x1B[9m",
|
|
85
|
-
// Colors (foreground)
|
|
86
|
-
black: "\x1B[30m",
|
|
87
|
-
red: "\x1B[31m",
|
|
88
|
-
green: "\x1B[32m",
|
|
89
|
-
yellow: "\x1B[33m",
|
|
90
|
-
blue: "\x1B[34m",
|
|
91
|
-
magenta: "\x1B[35m",
|
|
92
|
-
cyan: "\x1B[36m",
|
|
93
|
-
white: "\x1B[37m",
|
|
94
|
-
gray: "\x1B[90m",
|
|
95
|
-
redBright: "\x1B[91m",
|
|
96
|
-
greenBright: "\x1B[92m",
|
|
97
|
-
yellowBright: "\x1B[93m",
|
|
98
|
-
blueBright: "\x1B[94m",
|
|
99
|
-
magentaBright: "\x1B[95m",
|
|
100
|
-
cyanBright: "\x1B[96m",
|
|
101
|
-
whiteBright: "\x1B[97m",
|
|
102
|
-
// Background colors
|
|
103
|
-
bgBlack: "\x1B[40m",
|
|
104
|
-
bgRed: "\x1B[41m",
|
|
105
|
-
bgGreen: "\x1B[42m",
|
|
106
|
-
bgYellow: "\x1B[43m",
|
|
107
|
-
bgBlue: "\x1B[44m",
|
|
108
|
-
bgMagenta: "\x1B[45m",
|
|
109
|
-
bgCyan: "\x1B[46m",
|
|
110
|
-
bgWhite: "\x1B[47m",
|
|
111
|
-
bgGray: "\x1B[100m",
|
|
112
|
-
bgRedBright: "\x1B[101m",
|
|
113
|
-
bgGreenBright: "\x1B[102m",
|
|
114
|
-
bgYellowBright: "\x1B[103m",
|
|
115
|
-
bgBlueBright: "\x1B[104m",
|
|
116
|
-
bgMagentaBright: "\x1B[105m",
|
|
117
|
-
bgCyanBright: "\x1B[106m",
|
|
118
|
-
bgWhiteBright: "\x1B[107m"
|
|
119
|
-
};
|
|
120
|
-
function Ve(e = []) {
|
|
121
|
-
const t = (s) => j ? s : e.join("") + s + U.reset;
|
|
122
|
-
return new Proxy(t, {
|
|
123
|
-
get(s, n) {
|
|
124
|
-
if (n === "toString")
|
|
125
|
-
return () => j ? "%s" : e.join("") + "%s" + U.reset;
|
|
126
|
-
if (n in U) {
|
|
127
|
-
const o = U[n];
|
|
128
|
-
return Ve(j ? [] : [...e, o].filter(Boolean));
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
apply(s, n, o) {
|
|
132
|
-
const r = o[0] ?? "";
|
|
133
|
-
return j ? r : e.join("") + r + U.reset;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
const ro = Ve(), io = (e, t = {}, s = {}) => {
|
|
138
|
-
const n = { _: [] }, o = e.slice(2);
|
|
139
|
-
for (let r = 0; r < o.length; r++) {
|
|
140
|
-
const i = o[r];
|
|
141
|
-
if (i.startsWith("-")) {
|
|
142
|
-
const a = i.replace(/^-+/, ""), [c, l] = a.split("=");
|
|
143
|
-
let f = !1;
|
|
144
|
-
for (const [d, u] of Object.entries(t)) {
|
|
145
|
-
const p = Array.isArray(u) ? u : u.aliases ?? [d];
|
|
146
|
-
if (p && c && p.includes(c)) {
|
|
147
|
-
f = !0;
|
|
148
|
-
const m = typeof u == "object" && !Array.isArray(u) ? u.takesValue === !0 : !1;
|
|
149
|
-
let E;
|
|
150
|
-
l !== void 0 ? E = l.replace(/^"|"$/g, "") : m && r + 1 < o.length && !o[r + 1].startsWith("-") ? (E = o[r + 1], r++) : E = !0, d in n ? (Array.isArray(n[d]) || (n[d] = [n[d]]), n[d].push(E)) : n[d] = E;
|
|
151
|
-
break;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
if (!f && s.strict)
|
|
155
|
-
throw new Error(`Unknown flag: -${a}`);
|
|
156
|
-
} else
|
|
157
|
-
n._.push(i);
|
|
158
|
-
}
|
|
159
|
-
return n;
|
|
160
|
-
}, Lt = {
|
|
161
|
-
js: "JavaScript",
|
|
162
|
-
jsx: "JavaScript",
|
|
163
|
-
ts: "TypeScript",
|
|
164
|
-
tsx: "TypeScript",
|
|
165
|
-
java: "Java",
|
|
166
|
-
py: "Python",
|
|
167
|
-
c: "C",
|
|
168
|
-
cpp: "C++",
|
|
169
|
-
cs: "C#",
|
|
170
|
-
rb: "Ruby",
|
|
171
|
-
php: "PHP",
|
|
172
|
-
go: "Go",
|
|
173
|
-
swift: "Swift",
|
|
174
|
-
m: "Objective-C",
|
|
175
|
-
mm: "Objective-C++",
|
|
176
|
-
kt: "Kotlin",
|
|
177
|
-
sh: "Shell",
|
|
178
|
-
bash: "Shell",
|
|
179
|
-
html: "HTML",
|
|
180
|
-
css: "CSS",
|
|
181
|
-
less: "Less",
|
|
182
|
-
scss: "SCSS",
|
|
183
|
-
sass: "Sass",
|
|
184
|
-
md: "Markdown",
|
|
185
|
-
mdc: "Markdown"
|
|
186
|
-
}, Te = (e) => Lt[e] ?? void 0, ao = () => {
|
|
187
|
-
try {
|
|
188
|
-
return Ge("osascript -e 'user locale of (get system info)'").toString().trim().toLowerCase().split("_").filter((s) => s !== "en" && s !== "us")[0] ?? "en";
|
|
189
|
-
} catch {
|
|
190
|
-
return "en";
|
|
191
|
-
}
|
|
192
|
-
}, _e = (e) => {
|
|
193
|
-
const t = e.match(/[\p{L}\p{N}_]+|[^\s\p{L}\p{N}_]/gu);
|
|
194
|
-
return t ? t.length : 0;
|
|
195
|
-
}, bt = [
|
|
196
|
-
"^package\\.json",
|
|
197
|
-
"^tsconfig\\.json",
|
|
198
|
-
"^babel\\.config\\.json",
|
|
199
|
-
"^\\.pnpmfile\\.cjs",
|
|
200
|
-
"^\\.yarnrc(?:\\.(?:yml|yaml))?",
|
|
201
|
-
"^go\\.(?:mod|sum)",
|
|
202
|
-
"^\\.env(?:\\.[\\w]+)?",
|
|
203
|
-
"^\\..+",
|
|
204
|
-
".*\\.(?:yml|yaml|rc|ini|conf)"
|
|
205
|
-
], Mt = [
|
|
206
|
-
"^package-lock\\.json",
|
|
207
|
-
"^npm-shrinkwrap\\.json",
|
|
208
|
-
"^yarn(?:-lock)?\\.(?:yaml|yml|toml)",
|
|
209
|
-
"^pnpm-lock\\.yaml",
|
|
210
|
-
"^composer\\.lock",
|
|
211
|
-
"^podfile\\.lock",
|
|
212
|
-
"^go\\.sum",
|
|
213
|
-
"^gemfile\\.lock"
|
|
214
|
-
], xt = {
|
|
215
|
-
doc: /\.(md|markdown|txt|rst)$/i,
|
|
216
|
-
cfg: new RegExp(bt.join("|"), "i"),
|
|
217
|
-
img: /\.(png|jpe?g|gif|svg|bmp|tiff|ico)$/i,
|
|
218
|
-
css: /\.(css|less|scss|sass|styl)$/i,
|
|
219
|
-
html: /\.(html?)$/i,
|
|
220
|
-
code: /\.(js|jsx|ts|tsx|java|py|c|cpp|cs|rb|php|go|swift|m|mm|kt|sh|bash)$/i,
|
|
221
|
-
bin: /^(exe|dll|so|bin)\b/i,
|
|
222
|
-
lock: new RegExp(Mt.join("|"), "i"),
|
|
223
|
-
json: /\.json$/i
|
|
224
|
-
}, Ne = (e, t) => {
|
|
225
|
-
if (t?.extra && Array.isArray(t.extra)) {
|
|
226
|
-
for (const n of t.extra)
|
|
227
|
-
if (/^Binary files? /i.test(n) || /GIT binary patch/i.test(n))
|
|
228
|
-
return "bin";
|
|
229
|
-
}
|
|
230
|
-
return Object.entries(xt).find(([, n]) => n.test(e))?.[0] ?? "other";
|
|
231
|
-
}, $t = (e) => {
|
|
232
|
-
const t = e.split(`
|
|
233
|
-
`), s = [];
|
|
234
|
-
let n = null, o = null;
|
|
235
|
-
if (t.forEach((r) => {
|
|
236
|
-
if (r.startsWith("diff --git")) {
|
|
237
|
-
if (n) {
|
|
238
|
-
n.diff.tokens = n.diff.hunks.reduce(
|
|
239
|
-
(f, d) => f + d.tokens,
|
|
240
|
-
0
|
|
241
|
-
), n.tokens = n.diff.tokens;
|
|
242
|
-
const l = n.filename.match(/\.([^.]+)$/);
|
|
243
|
-
n.ext = l?.[1]?.toLowerCase() ?? "", n.category = Ne(
|
|
244
|
-
n.filename,
|
|
245
|
-
n.metadata
|
|
246
|
-
), n.programmingLanguage = Te(n.ext), s.push(n);
|
|
247
|
-
}
|
|
248
|
-
const i = r.match(/^diff --git a\/(.+?) b\/(.+)$/), a = i?.[1] ?? null;
|
|
249
|
-
n = {
|
|
250
|
-
isNew: !1,
|
|
251
|
-
isDeleted: !1,
|
|
252
|
-
isRenamed: !1,
|
|
253
|
-
filename: i?.[2] ?? null ?? "",
|
|
254
|
-
oldFilename: a ?? null,
|
|
255
|
-
newFileMode: null,
|
|
256
|
-
deletedFileMode: null,
|
|
257
|
-
diff: {
|
|
258
|
-
tokens: 0,
|
|
259
|
-
hunks: []
|
|
260
|
-
},
|
|
261
|
-
tokens: 0,
|
|
262
|
-
metadata: {},
|
|
263
|
-
ext: "",
|
|
264
|
-
category: "other",
|
|
265
|
-
programmingLanguage: void 0
|
|
266
|
-
}, o = null;
|
|
267
|
-
} else if (n && r.startsWith("new file mode")) {
|
|
268
|
-
const i = r.split(" ");
|
|
269
|
-
n.isNew = !0, n.newFileMode = i[3] ?? null, n.metadata.newFileMode = n.newFileMode;
|
|
270
|
-
} else if (n && r.startsWith("deleted file mode")) {
|
|
271
|
-
const i = r.split(" ");
|
|
272
|
-
n.isDeleted = !0, n.deletedFileMode = i[3] ?? null, n.metadata.deletedFileMode = n.deletedFileMode;
|
|
273
|
-
} else if (n && r.startsWith("old mode")) {
|
|
274
|
-
const i = r.split(" ");
|
|
275
|
-
n.metadata.oldMode = i[2] ?? null;
|
|
276
|
-
} else if (n && r.startsWith("new mode")) {
|
|
277
|
-
const i = r.split(" ");
|
|
278
|
-
n.metadata.newMode = i[2] ?? null;
|
|
279
|
-
} else if (n && r.startsWith("similarity index")) {
|
|
280
|
-
const i = r.split(" ");
|
|
281
|
-
n.metadata.similarityIndex = i[2] ?? null;
|
|
282
|
-
} else if (n && r.startsWith("rename from")) {
|
|
283
|
-
const i = r.substring(11).trim();
|
|
284
|
-
n.isRenamed = !0, n.oldFilename = i, n.metadata.renameFrom = i;
|
|
285
|
-
} else if (n && r.startsWith("rename to")) {
|
|
286
|
-
const i = r.substring(9).trim();
|
|
287
|
-
n.isRenamed = !0, n.filename = i, n.metadata.renameTo = i;
|
|
288
|
-
} else n && r.startsWith("index") ? n.metadata.index = r.substring(5).trim() : n && (r.startsWith("--- ") || r.startsWith("+++ ")) ? r.startsWith("--- ") ? n.metadata.oldFileMarker = r : n.metadata.newFileMarker = r : n && r.startsWith("@@") ? (o = {
|
|
289
|
-
header: r,
|
|
290
|
-
changes: [r],
|
|
291
|
-
tokens: _e(r)
|
|
292
|
-
}, n.diff.hunks.push(o)) : o && (r.startsWith("+") || r.startsWith("-") || r.startsWith(" ")) ? (o.changes.push(r), o.tokens += _e(r)) : n && (Array.isArray(n.metadata.extra) || (n.metadata.extra = []), n.metadata.extra.push(r));
|
|
293
|
-
}), n) {
|
|
294
|
-
const r = n, i = r.filename.match(/\.([^.]+)$/);
|
|
295
|
-
r.diff.tokens = r.diff.hunks.reduce(
|
|
296
|
-
(a, c) => a + c.tokens,
|
|
297
|
-
0
|
|
298
|
-
), r.tokens = r.diff.tokens, r.ext = i?.[1]?.toLowerCase() ?? "", r.category = Ne(r.filename, r.metadata), r.programmingLanguage = Te(r.ext), s.push(r);
|
|
299
|
-
}
|
|
300
|
-
return s;
|
|
301
|
-
}, P = (e, t) => {
|
|
302
|
-
try {
|
|
303
|
-
return Ge(e, { encoding: "utf-8" });
|
|
304
|
-
} catch (s) {
|
|
305
|
-
const n = s.status;
|
|
306
|
-
return n !== void 0 && (t?.expectedExitCodes ?? []).includes(n) || O.error("[execSyncSafe] [running → failed] Command failed", { cause: s }), "";
|
|
307
|
-
}
|
|
308
|
-
}, Ce = (e) => /\.(test|spec)s?\./.test(e), Bt = () => P("git branch --list 2>/dev/null")?.match(/\s*\*?\s*(master|main)$/m)?.[1] ?? "master", co = () => P("git rev-parse --abbrev-ref HEAD 2>/dev/null").trim() || "HEAD", lo = () => {
|
|
309
|
-
const t = (P("git config --get remote.origin.url 2>/dev/null").trim() || P("git remote get-url origin 2>/dev/null").trim() || "").trim();
|
|
310
|
-
if (!t) return null;
|
|
311
|
-
if (/^[a-z]+:\/\//i.test(t))
|
|
312
|
-
try {
|
|
313
|
-
const n = new URL(t), o = (n.hostname || "").toLowerCase(), r = (n.protocol || "").replace(/:$/, "").toLowerCase(), i = (n.pathname || "").replace(/^\/+/, "").replace(/\.git$/i, "");
|
|
314
|
-
return !o || !i ? null : { host: o, project: i, scheme: r };
|
|
315
|
-
} catch (n) {
|
|
316
|
-
O.debug("[getGitRemote] [parsing → skip] URL parse failed", { cause: n });
|
|
317
|
-
}
|
|
318
|
-
const s = t.match(/^[\w.-]+@([^:\/]+)[:\/](.+)$/);
|
|
319
|
-
if (s) {
|
|
320
|
-
const n = (s[1] || "").toLowerCase(), o = (s[2] || "").replace(/^\/+/, "").replace(/\.git$/i, ""), r = "ssh";
|
|
321
|
-
return !n || !o ? null : { host: n, project: o, scheme: r };
|
|
322
|
-
}
|
|
323
|
-
return null;
|
|
324
|
-
}, Ut = () => {
|
|
325
|
-
try {
|
|
326
|
-
const e = P(`git rev-list --count HEAD ^${Bt()} 2>/dev/null`);
|
|
327
|
-
return parseInt(e, 10) || 0;
|
|
328
|
-
} catch (e) {
|
|
329
|
-
return O.debug("[getGitCommitCount] [counting → fallback] Using 0", { cause: e }), 0;
|
|
330
|
-
}
|
|
331
|
-
}, Gt = (e) => P(e ? `git diff ${e}` : "git diff HEAD"), uo = (e) => {
|
|
332
|
-
const t = Gt(e), s = $t(t).sort((a, c) => a.tokens - c.tokens), n = s.filter(
|
|
333
|
-
(a) => !a.isDeleted && !a.isRenamed && !Ce(a.filename) && (a.category === "config" || a.programmingLanguage)
|
|
334
|
-
);
|
|
335
|
-
n.length || n.push(
|
|
336
|
-
...s.filter(
|
|
337
|
-
(a) => !a.isDeleted && !a.isRenamed && (a.category === "doc" || Ce(a.filename))
|
|
338
|
-
)
|
|
339
|
-
);
|
|
340
|
-
const o = n.reduce((a, c) => a + c.tokens, 0), r = n.at(-1)?.tokens ?? 0, i = [
|
|
341
|
-
...new Set(n.map((a) => a.programmingLanguage).filter(Boolean))
|
|
342
|
-
];
|
|
343
|
-
return {
|
|
344
|
-
diff: t,
|
|
345
|
-
parsedDiff: s,
|
|
346
|
-
parsedCodeDiff: n,
|
|
347
|
-
parsedCodeTokens: o,
|
|
348
|
-
parsedCodeChunkMaxTokens: r,
|
|
349
|
-
programmingLanguages: i,
|
|
350
|
-
commitCount: Ut()
|
|
351
|
-
};
|
|
352
|
-
}, Ft = (e) => {
|
|
353
|
-
if (Array.isArray(e) && e.length === 2) {
|
|
354
|
-
const t = e[1];
|
|
355
|
-
if (t === null)
|
|
356
|
-
return e[0];
|
|
357
|
-
throw t instanceof Error ? t : new TypeError(`[UNGUARD_ERROR_TYPE] Error "${t}"`, { cause: t });
|
|
358
|
-
}
|
|
359
|
-
throw new SyntaxError(
|
|
360
|
-
"[UNGUARD_ERROR_SYNTAX] Invalid input: not a valid [result, error] tuple.",
|
|
361
|
-
{ cause: e }
|
|
362
|
-
);
|
|
363
|
-
}, fo = async (e) => {
|
|
364
|
-
const t = await e;
|
|
365
|
-
return Ft(t);
|
|
366
|
-
}, De = "</think>", po = (e) => {
|
|
367
|
-
const t = String(e ?? "").split(De).slice(-1).join(De).trim();
|
|
368
|
-
return /^(Хорошо,|Okay,)/.test(t) ? t.split(`
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
`).slice(-1).join(`
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
`).trim() : t;
|
|
375
|
-
};
|
|
376
|
-
class re {
|
|
377
|
-
/** @purpose Default configuration filename. */
|
|
378
|
-
static DEFAULT_FILENAME = ".gennadyrc";
|
|
379
|
-
/**
|
|
380
|
-
* @purpose Create GennadyRc instances for current directory and HOME (lookup priority).
|
|
381
|
-
* @returns Array of two instances: [cwd, HOME].
|
|
382
|
-
* @sideEffect IO: file read at constructor.
|
|
383
|
-
*/
|
|
384
|
-
static getDefaults() {
|
|
385
|
-
return [process.cwd(), process.env.HOME].map((t) => new re(t));
|
|
386
|
-
}
|
|
387
|
-
/** @purpose Full path to the loaded rc config file. */
|
|
388
|
-
_filename = "";
|
|
389
|
-
/** @purpose Parsed configuration data with model list. */
|
|
390
|
-
_data = {
|
|
391
|
-
models: []
|
|
392
|
-
};
|
|
393
|
-
/** @purpose Load/parse error if config is invalid, null otherwise. */
|
|
394
|
-
_error = null;
|
|
395
|
-
/**
|
|
396
|
-
* @purpose Load and parse Gennady RC config file from a directory.
|
|
397
|
-
* @param [dir] Directory to look for the rc file in (defaults to cwd).
|
|
398
|
-
* @param [name] Config filename (defaults to .gennadyrc).
|
|
399
|
-
*/
|
|
400
|
-
constructor(t = process.cwd(), s = re.DEFAULT_FILENAME) {
|
|
401
|
-
this._filename = h(t, s);
|
|
402
|
-
try {
|
|
403
|
-
if (R(this._filename)) {
|
|
404
|
-
const n = JSON.parse(D(this._filename).toString());
|
|
405
|
-
Array.isArray(n) ? this._data.models = n : n && typeof n == "object" && Array.isArray(n.models) ? this._data = {
|
|
406
|
-
...this._data,
|
|
407
|
-
...n
|
|
408
|
-
} : this._error = new Error(
|
|
409
|
-
`[GENNADY_RC_ERROR_CONFIG] Invalid "${this._filename}" config data`,
|
|
410
|
-
{ cause: n }
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
} catch (n) {
|
|
414
|
-
this._error = new Error(`[GENNADY_RC_ERROR_PARSE] Read "${this._filename}" config failed`, {
|
|
415
|
-
cause: n
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
/**
|
|
420
|
-
* @purpose Check that configuration was successfully loaded and recognized.
|
|
421
|
-
* @returns true if no error; false on parse error or invalid data.
|
|
422
|
-
*/
|
|
423
|
-
isValid() {
|
|
424
|
-
return !this._error;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* @purpose Get list of models from loaded rc.
|
|
428
|
-
* @returns Array of RcModel (empty on error or missing file).
|
|
429
|
-
*/
|
|
430
|
-
getModels() {
|
|
431
|
-
return this._data.models;
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* @purpose Get load/parse error if one occurred.
|
|
435
|
-
* @returns Error or null on successful load.
|
|
436
|
-
*/
|
|
437
|
-
getError() {
|
|
438
|
-
return this._error;
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
const Vt = Dt(It);
|
|
442
|
-
class A extends Error {
|
|
443
|
-
/**
|
|
444
|
-
* @purpose Create a VCS resolution error with an optional root cause.
|
|
445
|
-
* @param message Human-readable error description.
|
|
446
|
-
* @param [cause] Original error that triggered this failure.
|
|
447
|
-
*/
|
|
448
|
-
constructor(t, s) {
|
|
449
|
-
super(t, { cause: s }), this.name = "VcsResolveError";
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
function jt() {
|
|
453
|
-
return {
|
|
454
|
-
git: async (e) => {
|
|
455
|
-
const { stdout: t } = await Vt("git", e, { encoding: "utf-8" });
|
|
456
|
-
return String(t ?? "").trim();
|
|
457
|
-
},
|
|
458
|
-
env: (e) => process.env[e]
|
|
459
|
-
};
|
|
460
|
-
}
|
|
461
|
-
function Yt(e) {
|
|
462
|
-
const t = e.lastIndexOf("!");
|
|
463
|
-
if (t === -1) return null;
|
|
464
|
-
const s = e.slice(0, t), n = e.slice(t + 1), o = Number(n);
|
|
465
|
-
return !s || !Number.isInteger(o) || o <= 0 ? null : { project: s, iid: o };
|
|
466
|
-
}
|
|
467
|
-
function Ht(e) {
|
|
468
|
-
const t = e.match(/^git@([^:]+):(.+)\.git$/);
|
|
469
|
-
if (t)
|
|
470
|
-
return { host: t[1], project: t[2] };
|
|
471
|
-
const s = e.match(/^https?:\/\/([^/]+)\/(.+)\.git$/);
|
|
472
|
-
return s ? { host: s[1], project: s[2] } : null;
|
|
473
|
-
}
|
|
474
|
-
function qt(e) {
|
|
475
|
-
const t = e.match(/^https?:\/\/([^/]+)\/(.+)\/-\/merge_requests\/(\d+)\/?$/);
|
|
476
|
-
return t ? { host: t[1], project: t[2], iid: Number(t[3]) } : null;
|
|
477
|
-
}
|
|
478
|
-
async function Wt(e, t = jt()) {
|
|
479
|
-
if (O.debug("[resolveVcsContext] [idle → resolving]"), typeof e.url == "string") {
|
|
480
|
-
O.debug("[resolveVcsContext] [resolving → parsing-url]");
|
|
481
|
-
const d = qt(e.url);
|
|
482
|
-
if (!d) {
|
|
483
|
-
const N = `[resolveVcsContext] Некорректный URL: ${e.url}`;
|
|
484
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${N}`), new A(`Некорректный URL: ${e.url}`);
|
|
485
|
-
}
|
|
486
|
-
const u = e.host ?? d.host, p = d.project, m = d.iid, E = /github/i.test(u) ? "github" : "gitlab", S = E === "github" ? t.env("GITHUB_PERSONAL_TOKEN") ?? t.env("GITHUB_TOKEN") : t.env("GITLAB_PERSONAL_TOKEN");
|
|
487
|
-
if (!S) {
|
|
488
|
-
const N = E === "github" ? "GITHUB_PERSONAL_TOKEN or GITHUB_TOKEN" : "GITLAB_PERSONAL_TOKEN", I = `[resolveVcsContext] Не найден токен доступа. Установите ${N}.`;
|
|
489
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${I}`), new A(`Не найден токен доступа. Установите ${N}.`);
|
|
490
|
-
}
|
|
491
|
-
const _ = { provider: E, host: u, project: p, iid: m, token: S };
|
|
492
|
-
return O.info(`[resolveVcsContext] [resolving → resolved] ${u}/${p}!${m}`), _;
|
|
493
|
-
}
|
|
494
|
-
if (typeof e.ref == "string" && e.branch)
|
|
495
|
-
throw O.error("[resolveVcsContext] [resolving → failed] [resolveVcsContext] ref и branch не могут быть указаны одновременно"), new A("ref и branch не могут быть указаны одновременно");
|
|
496
|
-
const s = typeof e.ref == "string" ? Yt(e.ref) : null;
|
|
497
|
-
if (typeof e.ref == "string" && !s) {
|
|
498
|
-
const d = `[resolveVcsContext] Некорректный формат ref: ${e.ref}`;
|
|
499
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${d}`), new A(`Некорректный формат ref: ${e.ref}`);
|
|
500
|
-
}
|
|
501
|
-
const n = s?.iid ?? e.iid, o = s?.project ?? e.project;
|
|
502
|
-
let r = e.host, i = o;
|
|
503
|
-
if (!r || !i)
|
|
504
|
-
try {
|
|
505
|
-
O.debug("[resolveVcsContext] [resolving → fetching-remote]");
|
|
506
|
-
const d = await t.git(["config", "remote.origin.url"]), u = Ht(d);
|
|
507
|
-
if (!u) {
|
|
508
|
-
const p = `[resolveVcsContext] Не удалось разобрать URL удалённого репозитория: ${d}`;
|
|
509
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${p}`), new A(`Не удалось разобрать URL удалённого репозитория: ${d}`);
|
|
510
|
-
}
|
|
511
|
-
r ??= u.host, i ??= u.project;
|
|
512
|
-
} catch (d) {
|
|
513
|
-
if (d instanceof A) throw d;
|
|
514
|
-
if (!o) {
|
|
515
|
-
const u = "[resolveVcsContext] Не найден удалённый репозиторий origin", p = new A("Не найден удалённый репозиторий origin", d);
|
|
516
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${u}`, { error: p }), p;
|
|
517
|
-
}
|
|
518
|
-
O.debug(
|
|
519
|
-
"[resolveVcsContext] [resolving → remote-unreachable] project explicit, skipping host auto-detect"
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
if (!r || !i)
|
|
523
|
-
throw O.error("[resolveVcsContext] [resolving → failed] [resolveVcsContext] Не удалось определить host и project"), new A("Не удалось определить host и project");
|
|
524
|
-
const a = /github/i.test(r) ? "github" : "gitlab";
|
|
525
|
-
let c = e.branch;
|
|
526
|
-
if (!s && n === void 0)
|
|
527
|
-
try {
|
|
528
|
-
O.debug("[resolveVcsContext] [resolving → fetching-branch]"), c ??= await t.git(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
529
|
-
} catch (d) {
|
|
530
|
-
const u = "[resolveVcsContext] Не удалось определить текущую ветку без явного ref или project+iid", p = new A(
|
|
531
|
-
"Не удалось определить текущую ветку без явного ref или project+iid",
|
|
532
|
-
d
|
|
533
|
-
);
|
|
534
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${u}`, { error: p }), p;
|
|
535
|
-
}
|
|
536
|
-
const l = a === "github" ? t.env("GITHUB_PERSONAL_TOKEN") ?? t.env("GITHUB_TOKEN") : t.env("GITLAB_PERSONAL_TOKEN");
|
|
537
|
-
if (!l) {
|
|
538
|
-
const d = a === "github" ? "GITHUB_PERSONAL_TOKEN or GITHUB_TOKEN" : "GITLAB_PERSONAL_TOKEN", u = `[resolveVcsContext] Не найден токен доступа. Установите ${d}.`;
|
|
539
|
-
throw O.error(`[resolveVcsContext] [resolving → failed] ${u}`), new A(`Не найден токен доступа. Установите ${d}.`);
|
|
540
|
-
}
|
|
541
|
-
const f = {
|
|
542
|
-
provider: a,
|
|
543
|
-
host: r,
|
|
544
|
-
project: i,
|
|
545
|
-
iid: n,
|
|
546
|
-
branch: c,
|
|
547
|
-
token: l
|
|
548
|
-
};
|
|
549
|
-
return O.info(
|
|
550
|
-
`[resolveVcsContext] [resolving → resolved] ${r}/${i}${n ? `!${n}` : ""}`
|
|
551
|
-
), f;
|
|
552
|
-
}
|
|
553
|
-
const mo = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
554
|
-
__proto__: null,
|
|
555
|
-
VcsResolveError: A,
|
|
556
|
-
resolveVcsContext: Wt
|
|
557
|
-
}, Symbol.toStringTag, { value: "Module" }));
|
|
558
|
-
function Eo(e) {
|
|
559
|
-
const t = String(e).match(/<message[\s\S]*?>[\s\S]*?<\/message>/)?.[0];
|
|
560
|
-
if (!t)
|
|
561
|
-
return null;
|
|
562
|
-
const s = (a) => t.match(a)?.[1] ?? "", n = s(/type="([^"]+)"/), o = s(/icon="([^"]+)"/), r = s(/<subject>([\s\S]*?)<\/subject>/), i = s(/<description>([\s\S]*?)<\/description>/).replace(/\n\s+-/g, `
|
|
563
|
-
-`);
|
|
564
|
-
return {
|
|
565
|
-
type: n,
|
|
566
|
-
icon: o,
|
|
567
|
-
subject: r?.trim() || null,
|
|
568
|
-
description: i?.trim() || null
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
function se(e) {
|
|
572
|
-
return e ? String(e).replace(/[<>&'"]/g, (t) => {
|
|
573
|
-
switch (t) {
|
|
574
|
-
case "<":
|
|
575
|
-
return "<";
|
|
576
|
-
case ">":
|
|
577
|
-
return ">";
|
|
578
|
-
case "&":
|
|
579
|
-
return "&";
|
|
580
|
-
case "'":
|
|
581
|
-
return "'";
|
|
582
|
-
case '"':
|
|
583
|
-
return """;
|
|
584
|
-
default:
|
|
585
|
-
return t;
|
|
586
|
-
}
|
|
587
|
-
}) : "";
|
|
588
|
-
}
|
|
589
|
-
function Ae(e, t = 0) {
|
|
590
|
-
const s = " ".repeat(t), { tag: n, attrs: o, children: r } = e;
|
|
591
|
-
let i = "";
|
|
592
|
-
if (o && (i = Object.entries(o).filter(([, c]) => c != null).map(([c, l]) => ` ${c}="${se(String(l))}"`).join("")), r == null)
|
|
593
|
-
return `${s}<${n}${i} />`;
|
|
594
|
-
if (Array.isArray(r) && r.length === 0)
|
|
595
|
-
return `${s}<${n}${i} />`;
|
|
596
|
-
let a = "";
|
|
597
|
-
return Array.isArray(r) ? r.length > 0 && typeof r[0] == "string" ? a = r.map((l) => se(l)).join(`
|
|
598
|
-
`) : a = `
|
|
599
|
-
` + r.map((l) => Ae(l, t + 1)).join(`
|
|
600
|
-
`) + `
|
|
601
|
-
` + s : typeof r == "string" ? a = se(r) : "cdata" in r ? a = `<![CDATA[${String(r.cdata).replaceAll("]]>", "]]]]><![CDATA[>")}]]>` : a = `
|
|
602
|
-
` + Ae(r, t + 1) + `
|
|
603
|
-
` + s, `${s}<${n}${i}>${a}</${n}>`;
|
|
604
|
-
}
|
|
605
|
-
function ho(e) {
|
|
606
|
-
const t = [
|
|
607
|
-
"ai.knowledge.md",
|
|
608
|
-
"ai/ai.knowledge.md"
|
|
609
|
-
], s = [];
|
|
610
|
-
for (const n of t) {
|
|
611
|
-
const o = k.join(e, n);
|
|
612
|
-
try {
|
|
613
|
-
v.existsSync(o) && v.statSync(o).isFile() && s.push(n);
|
|
614
|
-
} catch {
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
return s.length === 0 ? "" : `**MUST READ PROJECT KNOWLEDGE FILES**: ${s.join(", ")}.`;
|
|
618
|
-
}
|
|
619
|
-
const Kt = [
|
|
620
|
-
{
|
|
621
|
-
kind: "marker",
|
|
622
|
-
relativePath: "go.mod",
|
|
623
|
-
commands: ["go test ./...", "go vet ./...", "go fmt ./..."]
|
|
624
|
-
},
|
|
625
|
-
{
|
|
626
|
-
kind: "npm-package-json",
|
|
627
|
-
scriptGroups: [
|
|
628
|
-
{
|
|
629
|
-
preferredNames: [
|
|
630
|
-
"test",
|
|
631
|
-
"test:run",
|
|
632
|
-
"test:ci",
|
|
633
|
-
"test:app",
|
|
634
|
-
"mc:test",
|
|
635
|
-
"unit",
|
|
636
|
-
"jest",
|
|
637
|
-
"vitest"
|
|
638
|
-
],
|
|
639
|
-
fallbackPatterns: [/(^|:)test($|:)/i, /(^|:)unit($|:)/i, /(^|:)spec($|:)/i]
|
|
640
|
-
},
|
|
641
|
-
{
|
|
642
|
-
preferredNames: ["lint:ci", "lint", "eslint", "stylelint", "format:check"],
|
|
643
|
-
fallbackPatterns: [/(^|:)(lint|eslint|stylelint)($|:)/i]
|
|
644
|
-
},
|
|
645
|
-
{
|
|
646
|
-
preferredNames: ["type-check", "typecheck", "check"],
|
|
647
|
-
fallbackPatterns: [/(^|:)(typecheck|type-check|check)($|:)/i]
|
|
648
|
-
}
|
|
649
|
-
],
|
|
650
|
-
maxCommands: 5
|
|
651
|
-
},
|
|
652
|
-
{
|
|
653
|
-
kind: "marker",
|
|
654
|
-
relativePath: "Cargo.toml",
|
|
655
|
-
commands: ["cargo test", "cargo clippy"]
|
|
656
|
-
}
|
|
657
|
-
];
|
|
658
|
-
function Xt(e, t) {
|
|
659
|
-
const s = k.join(e, t);
|
|
660
|
-
try {
|
|
661
|
-
return v.existsSync(s) && v.statSync(s).isFile();
|
|
662
|
-
} catch {
|
|
663
|
-
return !1;
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
function Zt(e, t) {
|
|
667
|
-
const s = e.toLowerCase(), n = t.toLowerCase();
|
|
668
|
-
return /(^|:|-)watch($|:|-)/i.test(s) ? !0 : [
|
|
669
|
-
/(^|\s)--watch(?:[=\s]|$)/i,
|
|
670
|
-
/(^|\s)--watchall(?:[=\s]|$)/i,
|
|
671
|
-
/(^|\s)watch(?:\s|$)/i,
|
|
672
|
-
/(^|\s)nodemon(?:\s|$)/i
|
|
673
|
-
].some((r) => r.test(n));
|
|
674
|
-
}
|
|
675
|
-
function zt(e, t, s) {
|
|
676
|
-
const n = Object.entries(e).filter(
|
|
677
|
-
([o, r]) => !s.has(o) && !Zt(o, r)
|
|
678
|
-
);
|
|
679
|
-
for (const o of t.preferredNames)
|
|
680
|
-
if (n.some(([r]) => r === o))
|
|
681
|
-
return o;
|
|
682
|
-
for (const [o] of n)
|
|
683
|
-
if (t.fallbackPatterns.some((r) => r.test(o)))
|
|
684
|
-
return o;
|
|
685
|
-
return null;
|
|
686
|
-
}
|
|
687
|
-
function Qt(e, t) {
|
|
688
|
-
const s = k.join(e, "package.json");
|
|
689
|
-
let n;
|
|
690
|
-
try {
|
|
691
|
-
if (!v.existsSync(s) || !v.statSync(s).isFile())
|
|
692
|
-
return null;
|
|
693
|
-
n = v.readFileSync(s, "utf-8");
|
|
694
|
-
} catch {
|
|
695
|
-
return null;
|
|
696
|
-
}
|
|
697
|
-
let o;
|
|
698
|
-
try {
|
|
699
|
-
o = JSON.parse(n);
|
|
700
|
-
} catch {
|
|
701
|
-
return null;
|
|
702
|
-
}
|
|
703
|
-
const r = o.scripts;
|
|
704
|
-
if (!r || typeof r != "object")
|
|
705
|
-
return null;
|
|
706
|
-
const i = [], a = /* @__PURE__ */ new Set();
|
|
707
|
-
for (const c of t.scriptGroups) {
|
|
708
|
-
const l = zt(r, c, a);
|
|
709
|
-
if (l && (i.push(`npm run ${l}`), a.add(l)), i.length >= t.maxCommands)
|
|
710
|
-
break;
|
|
711
|
-
}
|
|
712
|
-
return i.length === 0 ? null : i.slice(0, t.maxCommands);
|
|
713
|
-
}
|
|
714
|
-
function Jt(e) {
|
|
715
|
-
for (const t of Kt) {
|
|
716
|
-
if (t.kind === "marker") {
|
|
717
|
-
if (Xt(e, t.relativePath))
|
|
718
|
-
return [...t.commands];
|
|
719
|
-
continue;
|
|
720
|
-
}
|
|
721
|
-
if (t.kind === "npm-package-json") {
|
|
722
|
-
const s = Qt(e, t);
|
|
723
|
-
if (s != null && s.length > 0)
|
|
724
|
-
return s;
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
return [];
|
|
728
|
-
}
|
|
729
|
-
function es(e) {
|
|
730
|
-
return e.map((t) => `\`${t}\``).join(", ");
|
|
731
|
-
}
|
|
732
|
-
function So(e) {
|
|
733
|
-
const t = Jt(e);
|
|
734
|
-
if (t.length === 0)
|
|
735
|
-
return {
|
|
736
|
-
axiomHint: "как в README или CI принято проверять код (тесты, линтер, типы). Не подменяй это тяжёлой production-сборкой или публикацией, если это явно не описано как проверка",
|
|
737
|
-
toolsExample: "команды из `package.json` → `scripts`, из Makefile или из CI-шагов, которые относятся к качеству кода; сначала прочитай README",
|
|
738
|
-
verifyStep: "Прочитай README и при наличии — конфиг CI: какие команды запускают тесты, линтер и проверку типов. Выполни их из корня репозитория. Не запускай `build`/`publish`/полный `ci`, если не уверен, что это нужно для проверки изменений; при сомнении спроси пользователя."
|
|
739
|
-
};
|
|
740
|
-
const s = es(t);
|
|
741
|
-
return {
|
|
742
|
-
axiomHint: `\`${t[0]}\` или следующую команду из того же набора проверок`,
|
|
743
|
-
toolsExample: s,
|
|
744
|
-
verifyStep: `Из корня репозитория выполни: ${s}. Не добавляй \`build\`, полный \`ci\` или публикацию, если они не перечислены здесь и не описаны в README как обязательная проверка.`
|
|
745
|
-
};
|
|
746
|
-
}
|
|
747
|
-
const Y = "ai/agents";
|
|
748
|
-
function ye(e) {
|
|
749
|
-
return v.existsSync(e) ? v.promises.readFile(e, "utf-8") : null;
|
|
750
|
-
}
|
|
751
|
-
function ts(e) {
|
|
752
|
-
let t = e;
|
|
753
|
-
for (; ; ) {
|
|
754
|
-
const s = k.join(t, "package.json");
|
|
755
|
-
if (v.existsSync(s))
|
|
756
|
-
return t;
|
|
757
|
-
const n = k.dirname(t);
|
|
758
|
-
if (n === t)
|
|
759
|
-
return null;
|
|
760
|
-
t = n;
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
async function go(e) {
|
|
764
|
-
const t = k.dirname(fe(import.meta.url)), s = k.join(process.cwd(), Y, e), n = ye(s);
|
|
765
|
-
if (n)
|
|
766
|
-
return n;
|
|
767
|
-
const o = ts(t), r = [];
|
|
768
|
-
o ? (r.push(k.join(o, Y, e)), r.push(k.join(o, "dist", Y, e))) : r.push(
|
|
769
|
-
k.join(t, "../../../../../../dist", Y, e)
|
|
770
|
-
);
|
|
771
|
-
for (const i of r) {
|
|
772
|
-
const a = ye(i);
|
|
773
|
-
if (a)
|
|
774
|
-
return a;
|
|
775
|
-
}
|
|
776
|
-
throw new Error(`Не найден файл шаблона ${e}.`, {
|
|
777
|
-
cause: {
|
|
778
|
-
projectOverridePath: s,
|
|
779
|
-
packageCandidates: r
|
|
780
|
-
}
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
const je = ["product", "library", "infrastructure", "interface"], z = "One-line human summary of how many decisions and what area; fold the full D-NNN entries under <details> per AX_SPEC_PROGRESSIVE_DISCLOSURE.", Q = 'One-line human summary of outstanding prerequisites; fold the full Requirement/Kind/Owner/Resolution table under <details>. Declare explicitly ("No external bootstrap required.") when the STEP_7 audit found none — do not leave the table silently empty.', F = "MANDATORY (AX_SPEC_MANDATORY_DIAGRAM): at least one fenced mermaid or ASCII diagram giving a reader the shape of this artifact at a glance, up top.", ss = "List infra-*/api/design-system-* scopes this scope depends on, and any consumer scopes it provides to.", ns = `# <scope-name>: Scope Specification
|
|
784
|
-
|
|
785
|
-
<!--SECTION:SCOPE_TYPE-->
|
|
786
|
-
## scope-type
|
|
787
|
-
product
|
|
788
|
-
<!--/SECTION:SCOPE_TYPE-->
|
|
789
|
-
|
|
790
|
-
<!--SECTION:VISION-->
|
|
791
|
-
## Vision & Primary Goal
|
|
792
|
-
<!--/SECTION:VISION-->
|
|
793
|
-
|
|
794
|
-
<!--SECTION:OVERVIEW-->
|
|
795
|
-
## Overview
|
|
796
|
-
[MANDATORY per \`AX_SPEC_MANDATORY_DIAGRAM\` — ≥1 diagram right up top so a reader grasps the scope at a glance (checked by \`SDD_NO_DIAGRAM_BLOCK\` / \`SDD_DIAGRAM_BLOCK_EMPTY\`). The floor is one system-context / high-level view; add more (user flow, data flow) by author judgment. Fenced mermaid OR ASCII; pick the diagram type per \`formats/diagram-vocabulary.xml\`. Example:]
|
|
797
|
-
|
|
798
|
-
\`\`\`mermaid
|
|
799
|
-
flowchart TD
|
|
800
|
-
user --> product
|
|
801
|
-
product --> dep1[dependency]
|
|
802
|
-
product --> dep2[dependency]
|
|
803
|
-
\`\`\`
|
|
804
|
-
<!--/SECTION:OVERVIEW-->
|
|
805
|
-
|
|
806
|
-
<!--SECTION:PROJECT_TYPE-->
|
|
807
|
-
## Project Type
|
|
808
|
-
- **Type:** app[mobile|desktop|spa] | service-module-sdk | cli-utility
|
|
809
|
-
- **Why this type:** [Краткое обоснование.]
|
|
810
|
-
<!--/SECTION:PROJECT_TYPE-->
|
|
811
|
-
|
|
812
|
-
<!--SECTION:GOLDEN_DX-->
|
|
813
|
-
## Target Experience
|
|
814
|
-
[Commentary-rich: init/setup + happy path + error/degradation path.]
|
|
815
|
-
<!--/SECTION:GOLDEN_DX-->
|
|
816
|
-
|
|
817
|
-
<!--SECTION:SCOPE_DEPENDENCIES-->
|
|
818
|
-
## Scope Dependencies
|
|
819
|
-
- **Depends on:** [infra-*, api, design-system-* scopes]
|
|
820
|
-
- **Provides to:** [consumer scopes, if any]
|
|
821
|
-
<!--/SECTION:SCOPE_DEPENDENCIES-->
|
|
822
|
-
|
|
823
|
-
<!--SECTION:REQUIREMENTS_AND_CONSTRAINTS-->
|
|
824
|
-
## Requirements & Constraints
|
|
825
|
-
|
|
826
|
-
### Functional Requirements
|
|
827
|
-
|
|
828
|
-
### Non-Functional Constraints
|
|
829
|
-
|
|
830
|
-
### Out-of-Scope
|
|
831
|
-
|
|
832
|
-
### Runtime & Deferred Scope
|
|
833
|
-
[Per \`AX_RUNTIME_BACKING_EXPLICIT\`. Backing per major capability + deferred parts + trust boundaries требующие real runtime hook.]
|
|
834
|
-
|
|
835
|
-
### Rules
|
|
836
|
-
| Rule | Category | Source |
|
|
837
|
-
|---|---|---|
|
|
838
|
-
<!--/SECTION:REQUIREMENTS_AND_CONSTRAINTS-->
|
|
839
|
-
|
|
840
|
-
<!--SECTION:ARCHITECTURE-->
|
|
841
|
-
## High-Level Architecture
|
|
842
|
-
[Выбранный вариант. Block diagram. Краткие описания.]
|
|
843
|
-
|
|
844
|
-
### Rejected Alternatives
|
|
845
|
-
<!--/SECTION:ARCHITECTURE-->
|
|
846
|
-
|
|
847
|
-
<!--SECTION:MODULE_MAP-->
|
|
848
|
-
## Module Map
|
|
849
|
-
[Appended by \`module-decomposition\`. Initially: «Modules not yet decomposed — run \`module-decomposition <scope-name>\`».]
|
|
850
|
-
<!--/SECTION:MODULE_MAP-->
|
|
851
|
-
|
|
852
|
-
<!--SECTION:DECISION_LOG-->
|
|
853
|
-
## Decision Log
|
|
854
|
-
[One-line human summary — how many decisions, what area. Full entries fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`) — history, never needed to grasp the scope NOW.]
|
|
855
|
-
|
|
856
|
-
<details>
|
|
857
|
-
<summary>Полные записи Decision Log</summary>
|
|
858
|
-
|
|
859
|
-
[D-NNN entries.]
|
|
860
|
-
|
|
861
|
-
</details>
|
|
862
|
-
<!--/SECTION:DECISION_LOG-->
|
|
863
|
-
|
|
864
|
-
<!--SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
865
|
-
## Prerequisites
|
|
866
|
-
[One-line human summary — is anything outstanding. Full table folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
867
|
-
|
|
868
|
-
<details>
|
|
869
|
-
<summary>Таблица предусловий</summary>
|
|
870
|
-
|
|
871
|
-
| Requirement | Kind | Owner | Resolution |
|
|
872
|
-
|---|---|---|---|
|
|
873
|
-
<!-- Empty list allowed only when STEP_7 audit produced zero external assumptions — declare explicitly. -->
|
|
874
|
-
|
|
875
|
-
</details>
|
|
876
|
-
<!--/SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
877
|
-
|
|
878
|
-
<!--SECTION:HANDOFF-->
|
|
879
|
-
## Handoff to Modules
|
|
880
|
-
- **Primary input:** \`specs/<scope-name>/<scope-name>.spec.md\`
|
|
881
|
-
- **Areas requiring decomposition:** [list]
|
|
882
|
-
- **Named abstractions:** [from Target Experience]
|
|
883
|
-
- **Bootstrap tickets ready for cascade:** see Prerequisites
|
|
884
|
-
- **Open risks:** [validation needs]
|
|
885
|
-
|
|
886
|
-
<!-- Pivot Invalidation List — only in pivot mode -->
|
|
887
|
-
<!--/SECTION:HANDOFF-->
|
|
888
|
-
`, os = [
|
|
889
|
-
{
|
|
890
|
-
name: "SCOPE_TYPE",
|
|
891
|
-
required: !0,
|
|
892
|
-
loadBearing: !1,
|
|
893
|
-
fold: !1,
|
|
894
|
-
fill: "Literal value `product` — identifies this spec's scope-type to REQUIRED_SECTIONS checks."
|
|
895
|
-
},
|
|
896
|
-
{
|
|
897
|
-
name: "VISION",
|
|
898
|
-
required: !0,
|
|
899
|
-
loadBearing: !0,
|
|
900
|
-
fold: !1,
|
|
901
|
-
fill: "Vision & primary goal — what this product scope is for, in a sentence or two."
|
|
902
|
-
},
|
|
903
|
-
{ name: "OVERVIEW", required: !0, loadBearing: !1, fold: !1, fill: F },
|
|
904
|
-
{
|
|
905
|
-
name: "PROJECT_TYPE",
|
|
906
|
-
required: !0,
|
|
907
|
-
loadBearing: !1,
|
|
908
|
-
fold: !1,
|
|
909
|
-
fill: "Pick the project Type (app[mobile|desktop|spa] | service-module-sdk | cli-utility) and one-line rationale."
|
|
910
|
-
},
|
|
911
|
-
{
|
|
912
|
-
name: "GOLDEN_DX",
|
|
913
|
-
required: !0,
|
|
914
|
-
loadBearing: !0,
|
|
915
|
-
fold: !1,
|
|
916
|
-
fill: "Target experience: init/setup + happy path + error/degradation path, commentary-rich."
|
|
917
|
-
},
|
|
918
|
-
{
|
|
919
|
-
name: "SCOPE_DEPENDENCIES",
|
|
920
|
-
required: !0,
|
|
921
|
-
loadBearing: !1,
|
|
922
|
-
fold: !1,
|
|
923
|
-
fill: ss
|
|
924
|
-
},
|
|
925
|
-
{
|
|
926
|
-
name: "REQUIREMENTS_AND_CONSTRAINTS",
|
|
927
|
-
required: !0,
|
|
928
|
-
loadBearing: !0,
|
|
929
|
-
fold: !1,
|
|
930
|
-
fill: "Functional Requirements, Non-Functional Constraints, Out-of-Scope, Runtime & Deferred Scope (AX_RUNTIME_BACKING_EXPLICIT), and the Rules table."
|
|
931
|
-
},
|
|
932
|
-
{
|
|
933
|
-
name: "ARCHITECTURE",
|
|
934
|
-
required: !0,
|
|
935
|
-
loadBearing: !0,
|
|
936
|
-
fold: !1,
|
|
937
|
-
fill: "Chosen high-level architecture with a block diagram and brief descriptions, plus Rejected Alternatives."
|
|
938
|
-
},
|
|
939
|
-
{
|
|
940
|
-
name: "MODULE_MAP",
|
|
941
|
-
required: !0,
|
|
942
|
-
loadBearing: !0,
|
|
943
|
-
fold: !1,
|
|
944
|
-
fill: "Appended by module-decomposition; initially a placeholder pointing at that command."
|
|
945
|
-
},
|
|
946
|
-
{ name: "DECISION_LOG", required: !0, loadBearing: !0, fold: !0, fill: z },
|
|
947
|
-
{
|
|
948
|
-
name: "BOOTSTRAP_REQUIREMENTS",
|
|
949
|
-
required: !0,
|
|
950
|
-
loadBearing: !1,
|
|
951
|
-
fold: !0,
|
|
952
|
-
fill: Q
|
|
953
|
-
},
|
|
954
|
-
{
|
|
955
|
-
name: "HANDOFF",
|
|
956
|
-
required: !0,
|
|
957
|
-
loadBearing: !1,
|
|
958
|
-
fold: !1,
|
|
959
|
-
fill: "Primary input path, areas requiring decomposition, named abstractions, bootstrap tickets, open risks."
|
|
960
|
-
}
|
|
961
|
-
], rs = `# <scope-name>: Library Specification
|
|
962
|
-
|
|
963
|
-
<!--SECTION:SCOPE_TYPE-->
|
|
964
|
-
## scope-type
|
|
965
|
-
library
|
|
966
|
-
<!--/SECTION:SCOPE_TYPE-->
|
|
967
|
-
|
|
968
|
-
<!--SECTION:VISION-->
|
|
969
|
-
## Vision & Primary Goal
|
|
970
|
-
[Что library делает; главная проблема, которую решает.]
|
|
971
|
-
<!--/SECTION:VISION-->
|
|
972
|
-
|
|
973
|
-
<!--SECTION:OVERVIEW-->
|
|
974
|
-
## Overview
|
|
975
|
-
[MANDATORY per \`AX_SPEC_MANDATORY_DIAGRAM\` — ≥1 diagram up top (checked by \`SDD_NO_DIAGRAM_BLOCK\` / \`SDD_DIAGRAM_BLOCK_EMPTY\`). The floor is one — how a consumer wires this library in; add more by author judgment. Fenced mermaid OR ASCII; pick the diagram type per \`formats/diagram-vocabulary.xml\`. Example:]
|
|
976
|
-
|
|
977
|
-
\`\`\`mermaid
|
|
978
|
-
flowchart LR
|
|
979
|
-
consumer -->|imports| library
|
|
980
|
-
library --> capability
|
|
981
|
-
\`\`\`
|
|
982
|
-
<!--/SECTION:OVERVIEW-->
|
|
983
|
-
|
|
984
|
-
<!--SECTION:GOLDEN_DX-->
|
|
985
|
-
## Target Experience
|
|
986
|
-
[Публичный API DX: init/setup + happy path + error path. Комментарии раскрывают намерение.]
|
|
987
|
-
<!--/SECTION:GOLDEN_DX-->
|
|
988
|
-
|
|
989
|
-
<!--SECTION:SCOPE_DEPENDENCIES-->
|
|
990
|
-
## Scope Dependencies
|
|
991
|
-
- **Depends on:** [infra-*, interface scopes]
|
|
992
|
-
- **Provides to:** [consumer scopes]
|
|
993
|
-
<!--/SECTION:SCOPE_DEPENDENCIES-->
|
|
994
|
-
|
|
995
|
-
<!--SECTION:REQUIREMENTS_AND_CONSTRAINTS-->
|
|
996
|
-
## Requirements & Constraints
|
|
997
|
-
|
|
998
|
-
### Functional Requirements
|
|
999
|
-
|
|
1000
|
-
### Non-Functional Constraints
|
|
1001
|
-
|
|
1002
|
-
### Out-of-Scope
|
|
1003
|
-
|
|
1004
|
-
### Runtime & Deferred Scope
|
|
1005
|
-
[Per \`AX_RUNTIME_BACKING_EXPLICIT\`. Backing per major capability + deferred parts.]
|
|
1006
|
-
|
|
1007
|
-
### Rules
|
|
1008
|
-
| Rule | Category | Source |
|
|
1009
|
-
|---|---|---|
|
|
1010
|
-
<!--/SECTION:REQUIREMENTS_AND_CONSTRAINTS-->
|
|
1011
|
-
|
|
1012
|
-
<!--SECTION:PUBLIC_API_SURFACE-->
|
|
1013
|
-
## Public API Surface
|
|
1014
|
-
[Ключевые exported interfaces, types, functions. Intent-level, без impl detail.]
|
|
1015
|
-
<!--/SECTION:PUBLIC_API_SURFACE-->
|
|
1016
|
-
|
|
1017
|
-
<!--SECTION:ARCHITECTURE-->
|
|
1018
|
-
## Architecture
|
|
1019
|
-
[Выбранный design pattern. Кратко — rejected alternatives.]
|
|
1020
|
-
<!--/SECTION:ARCHITECTURE-->
|
|
1021
|
-
|
|
1022
|
-
<!--SECTION:DECISION_LOG-->
|
|
1023
|
-
## Decision Log
|
|
1024
|
-
[One-line human summary — how many decisions, what area. Full entries fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`) — history, never needed to grasp the scope NOW.]
|
|
1025
|
-
|
|
1026
|
-
<details>
|
|
1027
|
-
<summary>Полные записи Decision Log</summary>
|
|
1028
|
-
|
|
1029
|
-
[D-NNN entries.]
|
|
1030
|
-
|
|
1031
|
-
</details>
|
|
1032
|
-
<!--/SECTION:DECISION_LOG-->
|
|
1033
|
-
|
|
1034
|
-
<!--SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1035
|
-
## Prerequisites
|
|
1036
|
-
[One-line human summary — is anything outstanding. Full table folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1037
|
-
|
|
1038
|
-
<details>
|
|
1039
|
-
<summary>Таблица предусловий</summary>
|
|
1040
|
-
|
|
1041
|
-
| Requirement | Kind | Owner | Resolution |
|
|
1042
|
-
|---|---|---|---|
|
|
1043
|
-
<!-- Empty list allowed only when STEP_7 audit produced zero external assumptions — declare explicitly. -->
|
|
1044
|
-
|
|
1045
|
-
</details>
|
|
1046
|
-
<!--/SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1047
|
-
|
|
1048
|
-
<!--SECTION:HANDOFF-->
|
|
1049
|
-
## Handoff to Modules
|
|
1050
|
-
- **Areas requiring decomposition:** [list]
|
|
1051
|
-
- **Named abstractions:** [from Target Experience]
|
|
1052
|
-
- **Bootstrap tickets ready for cascade:** see Prerequisites
|
|
1053
|
-
- **Open risks:** [validation needs]
|
|
1054
|
-
|
|
1055
|
-
<!-- Pivot Invalidation List — only in pivot mode -->
|
|
1056
|
-
<!--/SECTION:HANDOFF-->
|
|
1057
|
-
`, is = [
|
|
1058
|
-
{
|
|
1059
|
-
name: "SCOPE_TYPE",
|
|
1060
|
-
required: !0,
|
|
1061
|
-
loadBearing: !1,
|
|
1062
|
-
fold: !1,
|
|
1063
|
-
fill: "Literal value `library`."
|
|
1064
|
-
},
|
|
1065
|
-
{
|
|
1066
|
-
name: "VISION",
|
|
1067
|
-
required: !0,
|
|
1068
|
-
loadBearing: !0,
|
|
1069
|
-
fold: !1,
|
|
1070
|
-
fill: "What the library does and the main problem it solves."
|
|
1071
|
-
},
|
|
1072
|
-
{ name: "OVERVIEW", required: !0, loadBearing: !1, fold: !1, fill: F },
|
|
1073
|
-
{
|
|
1074
|
-
name: "GOLDEN_DX",
|
|
1075
|
-
required: !0,
|
|
1076
|
-
loadBearing: !0,
|
|
1077
|
-
fold: !1,
|
|
1078
|
-
fill: "Public API DX: init/setup + happy path + error path, intent commentary throughout."
|
|
1079
|
-
},
|
|
1080
|
-
{
|
|
1081
|
-
name: "SCOPE_DEPENDENCIES",
|
|
1082
|
-
required: !0,
|
|
1083
|
-
loadBearing: !1,
|
|
1084
|
-
fold: !1,
|
|
1085
|
-
fill: "List infra-*/interface scopes depended on, and consumer scopes provided to."
|
|
1086
|
-
},
|
|
1087
|
-
{
|
|
1088
|
-
name: "REQUIREMENTS_AND_CONSTRAINTS",
|
|
1089
|
-
required: !0,
|
|
1090
|
-
loadBearing: !0,
|
|
1091
|
-
fold: !1,
|
|
1092
|
-
fill: "Functional Requirements, Non-Functional Constraints, Out-of-Scope, Runtime & Deferred Scope, and the Rules table."
|
|
1093
|
-
},
|
|
1094
|
-
{
|
|
1095
|
-
name: "PUBLIC_API_SURFACE",
|
|
1096
|
-
required: !0,
|
|
1097
|
-
loadBearing: !0,
|
|
1098
|
-
fold: !1,
|
|
1099
|
-
fill: "Key exported interfaces/types/functions, intent-level — no implementation detail."
|
|
1100
|
-
},
|
|
1101
|
-
{
|
|
1102
|
-
name: "ARCHITECTURE",
|
|
1103
|
-
required: !0,
|
|
1104
|
-
loadBearing: !1,
|
|
1105
|
-
fold: !1,
|
|
1106
|
-
fill: "Chosen design pattern, briefly, plus rejected alternatives."
|
|
1107
|
-
},
|
|
1108
|
-
{ name: "DECISION_LOG", required: !0, loadBearing: !0, fold: !0, fill: z },
|
|
1109
|
-
{
|
|
1110
|
-
name: "BOOTSTRAP_REQUIREMENTS",
|
|
1111
|
-
required: !0,
|
|
1112
|
-
loadBearing: !1,
|
|
1113
|
-
fold: !0,
|
|
1114
|
-
fill: Q
|
|
1115
|
-
},
|
|
1116
|
-
{
|
|
1117
|
-
name: "HANDOFF",
|
|
1118
|
-
required: !0,
|
|
1119
|
-
loadBearing: !1,
|
|
1120
|
-
fold: !1,
|
|
1121
|
-
fill: "Areas requiring decomposition, named abstractions, bootstrap tickets, open risks."
|
|
1122
|
-
}
|
|
1123
|
-
], as = `# <scope-name>: Infrastructure Specification
|
|
1124
|
-
|
|
1125
|
-
<!--SECTION:SCOPE_TYPE-->
|
|
1126
|
-
## scope-type
|
|
1127
|
-
infrastructure
|
|
1128
|
-
<!--/SECTION:SCOPE_TYPE-->
|
|
1129
|
-
|
|
1130
|
-
<!--SECTION:VISION-->
|
|
1131
|
-
## Vision
|
|
1132
|
-
[Что включает dev/ops experience.]
|
|
1133
|
-
<!--/SECTION:VISION-->
|
|
1134
|
-
|
|
1135
|
-
<!--SECTION:OVERVIEW-->
|
|
1136
|
-
## Overview
|
|
1137
|
-
[MANDATORY per \`AX_SPEC_MANDATORY_DIAGRAM\` — ≥1 diagram up top (checked by \`SDD_NO_DIAGRAM_BLOCK\` / \`SDD_DIAGRAM_BLOCK_EMPTY\`). The floor is one — the developer workflow or the tool pipeline at a glance; add more by author judgment. Fenced mermaid OR ASCII; pick the diagram type per \`formats/diagram-vocabulary.xml\`. Example:]
|
|
1138
|
-
|
|
1139
|
-
\`\`\`
|
|
1140
|
-
edit ──► typecheck ──► lint ──► test ──► pre-commit ──► CI
|
|
1141
|
-
\`\`\`
|
|
1142
|
-
<!--/SECTION:OVERVIEW-->
|
|
1143
|
-
|
|
1144
|
-
<!--SECTION:SCOPE_DEPENDENCIES-->
|
|
1145
|
-
## Scope Dependencies
|
|
1146
|
-
- **Depends on:** None (infrastructure scopes are typically leaves)
|
|
1147
|
-
- **Provides rules to:** [list of product/library scopes that depend on this]
|
|
1148
|
-
<!--/SECTION:SCOPE_DEPENDENCIES-->
|
|
1149
|
-
|
|
1150
|
-
<!--SECTION:TOOL_STACK-->
|
|
1151
|
-
## Tool Stack
|
|
1152
|
-
|
|
1153
|
-
### Categories Covered
|
|
1154
|
-
[Список категорий + краткое обоснование исключений.]
|
|
1155
|
-
|
|
1156
|
-
### Tool Choices
|
|
1157
|
-
| Category | Tool | Rationale |
|
|
1158
|
-
|---|---|---|
|
|
1159
|
-
| <category> | <tool> | D-NNN |
|
|
1160
|
-
<!--/SECTION:TOOL_STACK-->
|
|
1161
|
-
|
|
1162
|
-
<!--SECTION:WORKFLOW_EXAMPLE-->
|
|
1163
|
-
## Developer Workflow Example
|
|
1164
|
-
[Shell-script: setup, daily flow, commit flow, debug flow.]
|
|
1165
|
-
<!--/SECTION:WORKFLOW_EXAMPLE-->
|
|
1166
|
-
|
|
1167
|
-
<!--SECTION:FILE_STRUCTURE-->
|
|
1168
|
-
## File Structure
|
|
1169
|
-
[ASCII tree + File Mapping.]
|
|
1170
|
-
<!--/SECTION:FILE_STRUCTURE-->
|
|
1171
|
-
|
|
1172
|
-
<!--SECTION:EFFECTIVE_RULES-->
|
|
1173
|
-
## Effective Rules (for cascade)
|
|
1174
|
-
[One-line human summary — how many rules, which categories. Full table folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1175
|
-
|
|
1176
|
-
<details>
|
|
1177
|
-
<summary>Таблица правил</summary>
|
|
1178
|
-
|
|
1179
|
-
| Rule | Category | Source |
|
|
1180
|
-
|---|---|---|
|
|
1181
|
-
| <rule-name> | coding | infra (D-NNN) |
|
|
1182
|
-
|
|
1183
|
-
</details>
|
|
1184
|
-
<!--/SECTION:EFFECTIVE_RULES-->
|
|
1185
|
-
|
|
1186
|
-
<!--SECTION:VERIFICATION_COMMANDS-->
|
|
1187
|
-
## Verification Commands
|
|
1188
|
-
**Mandatory for infrastructure scopes.** After tool stack is finalized, record a table:
|
|
1189
|
-
| Command Name | Invocation |
|
|
1190
|
-
| typecheck-command | <actual invocation> |
|
|
1191
|
-
| test-command | <actual invocation> |
|
|
1192
|
-
| lint-command | <actual invocation> |
|
|
1193
|
-
| format-command | <actual invocation> |
|
|
1194
|
-
| check-command | <actual invocation> |
|
|
1195
|
-
|
|
1196
|
-
Include only command names for tools present in the chosen stack.
|
|
1197
|
-
\`check-command\` is **always required** when the runtime setup rule (\`nodejs-npm-setup\` or equivalent) is active — it is the composed entry point that runs all active phases in \`CheckPhaseOrder\` order (typecheck → test → lint → format). Composition: chain invocations of each active phase command in that order. Task tickets use \`check-command\` as their single verification alias.
|
|
1198
|
-
<!--/SECTION:VERIFICATION_COMMANDS-->
|
|
1199
|
-
|
|
1200
|
-
<!--SECTION:DECISION_LOG-->
|
|
1201
|
-
## Decision Log
|
|
1202
|
-
[One-line human summary — how many decisions, what area. Full entries fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`) — history, never needed to grasp the scope NOW.]
|
|
1203
|
-
|
|
1204
|
-
<details>
|
|
1205
|
-
<summary>Полные записи Decision Log</summary>
|
|
1206
|
-
|
|
1207
|
-
[D-NNN entries.]
|
|
1208
|
-
|
|
1209
|
-
</details>
|
|
1210
|
-
<!--/SECTION:DECISION_LOG-->
|
|
1211
|
-
|
|
1212
|
-
<!--SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1213
|
-
## Prerequisites
|
|
1214
|
-
[One-line human summary — is anything outstanding. Full table folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1215
|
-
|
|
1216
|
-
<details>
|
|
1217
|
-
<summary>Таблица предусловий</summary>
|
|
1218
|
-
|
|
1219
|
-
| Requirement | Kind | Owner | Resolution |
|
|
1220
|
-
|---|---|---|---|
|
|
1221
|
-
<!-- Kind ∈ package | workspace-link | tool | file | external-type | env | service | structural -->
|
|
1222
|
-
<!-- Owner ∈ this-scope-task | external-prereq-scope | operator-action -->
|
|
1223
|
-
<!-- Empty list allowed only when STEP_7 audit produced zero external assumptions — declare it explicitly: "No external bootstrap required." -->
|
|
1224
|
-
|
|
1225
|
-
</details>
|
|
1226
|
-
<!--/SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1227
|
-
|
|
1228
|
-
<!--SECTION:HANDOFF-->
|
|
1229
|
-
## Handoff
|
|
1230
|
-
- **Setup tasks to scaffold:** [list]
|
|
1231
|
-
- **Effective rules ready for cascade:** see Effective Rules
|
|
1232
|
-
- **Verification Commands ready for cascade:** see Verification Commands
|
|
1233
|
-
- **Bootstrap tickets ready for cascade:** see Prerequisites
|
|
1234
|
-
- **Open risks:** [not-closed items]
|
|
1235
|
-
|
|
1236
|
-
<!-- Pivot Invalidation List — only in pivot mode -->
|
|
1237
|
-
<!--/SECTION:HANDOFF-->
|
|
1238
|
-
`, cs = [
|
|
1239
|
-
{
|
|
1240
|
-
name: "SCOPE_TYPE",
|
|
1241
|
-
required: !0,
|
|
1242
|
-
loadBearing: !1,
|
|
1243
|
-
fold: !1,
|
|
1244
|
-
fill: "Literal value `infrastructure`."
|
|
1245
|
-
},
|
|
1246
|
-
{
|
|
1247
|
-
name: "VISION",
|
|
1248
|
-
required: !0,
|
|
1249
|
-
loadBearing: !0,
|
|
1250
|
-
fold: !1,
|
|
1251
|
-
fill: "What dev/ops experience this scope covers."
|
|
1252
|
-
},
|
|
1253
|
-
{ name: "OVERVIEW", required: !0, loadBearing: !1, fold: !1, fill: F },
|
|
1254
|
-
{
|
|
1255
|
-
name: "SCOPE_DEPENDENCIES",
|
|
1256
|
-
required: !0,
|
|
1257
|
-
loadBearing: !1,
|
|
1258
|
-
fold: !1,
|
|
1259
|
-
fill: "Depends-on (usually None — infra scopes are leaves) and the product/library scopes this provides rules to."
|
|
1260
|
-
},
|
|
1261
|
-
{
|
|
1262
|
-
name: "TOOL_STACK",
|
|
1263
|
-
required: !0,
|
|
1264
|
-
loadBearing: !0,
|
|
1265
|
-
fold: !1,
|
|
1266
|
-
fill: "Categories covered plus rationale for exclusions, and the Category/Tool/Rationale (D-NNN) choices table."
|
|
1267
|
-
},
|
|
1268
|
-
{
|
|
1269
|
-
name: "WORKFLOW_EXAMPLE",
|
|
1270
|
-
required: !0,
|
|
1271
|
-
loadBearing: !1,
|
|
1272
|
-
fold: !1,
|
|
1273
|
-
fill: "Shell-script style walkthrough: setup, daily flow, commit flow, debug flow."
|
|
1274
|
-
},
|
|
1275
|
-
{
|
|
1276
|
-
name: "FILE_STRUCTURE",
|
|
1277
|
-
required: !0,
|
|
1278
|
-
loadBearing: !1,
|
|
1279
|
-
fold: !1,
|
|
1280
|
-
fill: "ASCII tree plus File Mapping for the tooling config files."
|
|
1281
|
-
},
|
|
1282
|
-
{
|
|
1283
|
-
name: "EFFECTIVE_RULES",
|
|
1284
|
-
required: !0,
|
|
1285
|
-
loadBearing: !1,
|
|
1286
|
-
fold: !0,
|
|
1287
|
-
fill: "One-line summary of how many rules / which categories; full Rule/Category/Source table folds under <details>."
|
|
1288
|
-
},
|
|
1289
|
-
{
|
|
1290
|
-
name: "VERIFICATION_COMMANDS",
|
|
1291
|
-
required: !0,
|
|
1292
|
-
loadBearing: !0,
|
|
1293
|
-
fold: !1,
|
|
1294
|
-
fill: "Mandatory: Command Name/Invocation table for the active phases (typecheck/test/lint/format) plus the composed check-command alias."
|
|
1295
|
-
},
|
|
1296
|
-
{ name: "DECISION_LOG", required: !0, loadBearing: !0, fold: !0, fill: z },
|
|
1297
|
-
{
|
|
1298
|
-
name: "BOOTSTRAP_REQUIREMENTS",
|
|
1299
|
-
required: !0,
|
|
1300
|
-
loadBearing: !1,
|
|
1301
|
-
fold: !0,
|
|
1302
|
-
fill: Q
|
|
1303
|
-
},
|
|
1304
|
-
{
|
|
1305
|
-
name: "HANDOFF",
|
|
1306
|
-
required: !0,
|
|
1307
|
-
loadBearing: !1,
|
|
1308
|
-
fold: !1,
|
|
1309
|
-
fill: "Setup tasks to scaffold, effective rules/verification commands/bootstrap tickets ready for cascade, open risks."
|
|
1310
|
-
}
|
|
1311
|
-
], ls = `# <scope-name>: Interface Specification
|
|
1312
|
-
|
|
1313
|
-
<!--SECTION:SCOPE_TYPE-->
|
|
1314
|
-
## scope-type
|
|
1315
|
-
interface
|
|
1316
|
-
<!--/SECTION:SCOPE_TYPE-->
|
|
1317
|
-
|
|
1318
|
-
<!--SECTION:VISION-->
|
|
1319
|
-
## Vision
|
|
1320
|
-
[Что контракт определяет и для кого.]
|
|
1321
|
-
<!--/SECTION:VISION-->
|
|
1322
|
-
|
|
1323
|
-
<!--SECTION:OVERVIEW-->
|
|
1324
|
-
## Overview
|
|
1325
|
-
[MANDATORY per \`AX_SPEC_MANDATORY_DIAGRAM\` — ≥1 diagram up top (checked by \`SDD_NO_DIAGRAM_BLOCK\` / \`SDD_DIAGRAM_BLOCK_EMPTY\`). The floor is one — who produces vs consumes this contract, or the version-transition flow; add more by author judgment. Fenced mermaid OR ASCII; pick the diagram type per \`formats/diagram-vocabulary.xml\`. Example:]
|
|
1326
|
-
|
|
1327
|
-
\`\`\`mermaid
|
|
1328
|
-
flowchart LR
|
|
1329
|
-
producer -->|implements| Contract
|
|
1330
|
-
Contract -->|consumed by| consumer
|
|
1331
|
-
\`\`\`
|
|
1332
|
-
<!--/SECTION:OVERVIEW-->
|
|
1333
|
-
|
|
1334
|
-
<!--SECTION:SCOPE_DEPENDENCIES-->
|
|
1335
|
-
## Scope Dependencies
|
|
1336
|
-
- **Depends on:** [infra scopes if any]
|
|
1337
|
-
- **Provides interface to:** [product/library scopes]
|
|
1338
|
-
<!--/SECTION:SCOPE_DEPENDENCIES-->
|
|
1339
|
-
|
|
1340
|
-
<!--SECTION:INTERFACE_DECLARATION-->
|
|
1341
|
-
## Interface Declaration
|
|
1342
|
-
- **Schema format:** OpenAPI 3.x | gRPC proto3 | JSON Schema | GraphQL SDL
|
|
1343
|
-
- **Versioning scheme:** semver | date-based (YYYY-MM-DD) | none
|
|
1344
|
-
- **Namespace:** <api prefix / package name>
|
|
1345
|
-
- **Interfaces:** [список endpoint groups / message types / operations]
|
|
1346
|
-
<!--/SECTION:INTERFACE_DECLARATION-->
|
|
1347
|
-
|
|
1348
|
-
<!--SECTION:VERSIONING_POLICY-->
|
|
1349
|
-
## Versioning Policy
|
|
1350
|
-
[Semver rules / breaking-change protocol / deprecation window.]
|
|
1351
|
-
<!--/SECTION:VERSIONING_POLICY-->
|
|
1352
|
-
|
|
1353
|
-
<!--SECTION:COMPATIBILITY_MATRIX-->
|
|
1354
|
-
## Compatibility Matrix
|
|
1355
|
-
[One-line human summary — how many consumers, any breaking changes pending. Full matrix folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1356
|
-
|
|
1357
|
-
<details>
|
|
1358
|
-
<summary>Матрица совместимости</summary>
|
|
1359
|
-
|
|
1360
|
-
| Consumer scope | Min compatible version | Breaking change protocol |
|
|
1361
|
-
|---|---|---|
|
|
1362
|
-
|
|
1363
|
-
</details>
|
|
1364
|
-
<!--/SECTION:COMPATIBILITY_MATRIX-->
|
|
1365
|
-
|
|
1366
|
-
<!--SECTION:DECISION_LOG-->
|
|
1367
|
-
## Decision Log
|
|
1368
|
-
[One-line human summary — how many decisions, what area. Full entries fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`) — history, never needed to grasp the scope NOW.]
|
|
1369
|
-
|
|
1370
|
-
<details>
|
|
1371
|
-
<summary>Полные записи Decision Log</summary>
|
|
1372
|
-
|
|
1373
|
-
[D-NNN entries.]
|
|
1374
|
-
|
|
1375
|
-
</details>
|
|
1376
|
-
<!--/SECTION:DECISION_LOG-->
|
|
1377
|
-
|
|
1378
|
-
<!--SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1379
|
-
## Prerequisites
|
|
1380
|
-
[One-line human summary — is anything outstanding. Full table folds (reference detail, checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1381
|
-
|
|
1382
|
-
<details>
|
|
1383
|
-
<summary>Таблица предусловий</summary>
|
|
1384
|
-
|
|
1385
|
-
| Requirement | Kind | Owner | Resolution |
|
|
1386
|
-
|---|---|---|---|
|
|
1387
|
-
<!-- Empty list allowed only when STEP_7 audit produced zero external assumptions — declare explicitly. -->
|
|
1388
|
-
|
|
1389
|
-
</details>
|
|
1390
|
-
<!--/SECTION:BOOTSTRAP_REQUIREMENTS-->
|
|
1391
|
-
|
|
1392
|
-
<!--SECTION:HANDOFF-->
|
|
1393
|
-
## Handoff
|
|
1394
|
-
- **Contract files to scaffold:** [schema files, codegen configs]
|
|
1395
|
-
- **Bootstrap tickets ready for cascade:** see Prerequisites
|
|
1396
|
-
- **Open risks:** [versioning gaps, unconfirmed consumers]
|
|
1397
|
-
<!--/SECTION:HANDOFF-->
|
|
1398
|
-
`, ds = [
|
|
1399
|
-
{
|
|
1400
|
-
name: "SCOPE_TYPE",
|
|
1401
|
-
required: !0,
|
|
1402
|
-
loadBearing: !1,
|
|
1403
|
-
fold: !1,
|
|
1404
|
-
fill: "Literal value `interface`."
|
|
1405
|
-
},
|
|
1406
|
-
{
|
|
1407
|
-
name: "VISION",
|
|
1408
|
-
required: !0,
|
|
1409
|
-
loadBearing: !0,
|
|
1410
|
-
fold: !1,
|
|
1411
|
-
fill: "What the contract defines, and for whom."
|
|
1412
|
-
},
|
|
1413
|
-
{ name: "OVERVIEW", required: !0, loadBearing: !1, fold: !1, fill: F },
|
|
1414
|
-
{
|
|
1415
|
-
name: "SCOPE_DEPENDENCIES",
|
|
1416
|
-
required: !0,
|
|
1417
|
-
loadBearing: !1,
|
|
1418
|
-
fold: !1,
|
|
1419
|
-
fill: "Infra scopes depended on (if any), and the product/library scopes this interface is provided to."
|
|
1420
|
-
},
|
|
1421
|
-
{
|
|
1422
|
-
name: "INTERFACE_DECLARATION",
|
|
1423
|
-
required: !0,
|
|
1424
|
-
loadBearing: !0,
|
|
1425
|
-
fold: !1,
|
|
1426
|
-
fill: "Schema format, versioning scheme, namespace, and the list of endpoint groups / message types / operations."
|
|
1427
|
-
},
|
|
1428
|
-
{
|
|
1429
|
-
name: "VERSIONING_POLICY",
|
|
1430
|
-
required: !0,
|
|
1431
|
-
loadBearing: !0,
|
|
1432
|
-
fold: !1,
|
|
1433
|
-
fill: "Semver rules, breaking-change protocol, deprecation window."
|
|
1434
|
-
},
|
|
1435
|
-
{
|
|
1436
|
-
name: "COMPATIBILITY_MATRIX",
|
|
1437
|
-
required: !0,
|
|
1438
|
-
loadBearing: !0,
|
|
1439
|
-
fold: !0,
|
|
1440
|
-
fill: "One-line summary of consumer count / pending breaking changes; full Consumer/Min-version/Breaking-change-protocol matrix folds under <details>."
|
|
1441
|
-
},
|
|
1442
|
-
{ name: "DECISION_LOG", required: !0, loadBearing: !0, fold: !0, fill: z },
|
|
1443
|
-
{
|
|
1444
|
-
name: "BOOTSTRAP_REQUIREMENTS",
|
|
1445
|
-
required: !0,
|
|
1446
|
-
loadBearing: !1,
|
|
1447
|
-
fold: !0,
|
|
1448
|
-
fill: Q
|
|
1449
|
-
},
|
|
1450
|
-
{
|
|
1451
|
-
name: "HANDOFF",
|
|
1452
|
-
required: !0,
|
|
1453
|
-
loadBearing: !1,
|
|
1454
|
-
fold: !1,
|
|
1455
|
-
fill: "Contract files to scaffold (schemas, codegen configs), bootstrap tickets, open risks."
|
|
1456
|
-
}
|
|
1457
|
-
], us = `# Module: <ModuleName>
|
|
1458
|
-
|
|
1459
|
-
<!--SECTION:MODULE_VISION-->
|
|
1460
|
-
## Module Vision
|
|
1461
|
-
[What this module owns. Link to parent scope spec → \`../../<scope>.spec.md\`. Links to parent/child modules if any.]
|
|
1462
|
-
<!--/SECTION:MODULE_VISION-->
|
|
1463
|
-
|
|
1464
|
-
<!--SECTION:OVERVIEW-->
|
|
1465
|
-
## Overview
|
|
1466
|
-
[MANDATORY per \`AX_SPEC_MANDATORY_DIAGRAM\` — ≥1 diagram, checked by \`SDD_NO_DIAGRAM_BLOCK\` / \`SDD_DIAGRAM_BLOCK_EMPTY\`. One glance beats a paragraph. The floor is one; add more (sequence, data-flow, state) whenever a second view genuinely helps a reader understand this module — that call is the author's. Fenced mermaid OR ASCII; pick the diagram type per \`formats/diagram-vocabulary.xml\`. The \`Inter-Module Dependencies\` graph is separate (machine-parsed) and does NOT satisfy this floor. Example — the module's main happy-path flow:]
|
|
1467
|
-
|
|
1468
|
-
\`\`\`mermaid
|
|
1469
|
-
flowchart LR
|
|
1470
|
-
caller -->|request| Port
|
|
1471
|
-
Port --> Service
|
|
1472
|
-
Service -->|result| caller
|
|
1473
|
-
\`\`\`
|
|
1474
|
-
<!--/SECTION:OVERVIEW-->
|
|
1475
|
-
|
|
1476
|
-
<!--SECTION:MODULE_USAGE_EXAMPLE-->
|
|
1477
|
-
## Module Usage Example
|
|
1478
|
-
[MANDATORY. Self-sufficient happy-path snippet — как потребитель использует этот модуль в изоляции. Пишется на таргет-языке проекта — единственное место для кода per \`AX_CONTRACTS_TEXTUAL_AGNOSTIC\`. Показывает публичную поверхность через реальный сценарий вызова: init / happy path / минимальный error path. Composition с соседними модулями — НЕ здесь, это уровень scope spec (link, не дублировать).]
|
|
1479
|
-
<!--/SECTION:MODULE_USAGE_EXAMPLE-->
|
|
1480
|
-
|
|
1481
|
-
<!--SECTION:INTER_MODULE_DEPENDENCIES-->
|
|
1482
|
-
## Inter-Module Dependencies
|
|
1483
|
-
- **Depends on:** <list of modules within this scope with links>
|
|
1484
|
-
- **Scope Reference (cross-scope):** <interface-type scope with link if any>
|
|
1485
|
-
- **Provides to:** <list of modules within this scope>
|
|
1486
|
-
|
|
1487
|
-
\`\`\`mermaid
|
|
1488
|
-
graph TD
|
|
1489
|
-
<this-module> --> <sibling-1>
|
|
1490
|
-
<this-module> --> <sibling-2>
|
|
1491
|
-
<this-module> -. Scope Reference .-> <contracts-scope>
|
|
1492
|
-
<consumer> --> <this-module>
|
|
1493
|
-
\`\`\`
|
|
1494
|
-
<!--/SECTION:INTER_MODULE_DEPENDENCIES-->
|
|
1495
|
-
|
|
1496
|
-
<!--SECTION:ENTITY_INVENTORY-->
|
|
1497
|
-
## Entity Inventory
|
|
1498
|
-
[Per \`ENTITY_INVENTORY_FORMAT\`.]
|
|
1499
|
-
<!--/SECTION:ENTITY_INVENTORY-->
|
|
1500
|
-
|
|
1501
|
-
<!--SECTION:ENTITY_SURFACES-->
|
|
1502
|
-
## Entity Surfaces
|
|
1503
|
-
[One-line human summary — what the surfaces cover; the reader sees this without expanding. Heavy per-entity detail folds per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1504
|
-
|
|
1505
|
-
<details>
|
|
1506
|
-
<summary>Полные поверхности сущностей</summary>
|
|
1507
|
-
|
|
1508
|
-
[Each entity per \`ENTITY_SURFACE_FORMAT\`.]
|
|
1509
|
-
|
|
1510
|
-
</details>
|
|
1511
|
-
<!--/SECTION:ENTITY_SURFACES-->
|
|
1512
|
-
|
|
1513
|
-
<!--SECTION:MODULE_CONTRACTS-->
|
|
1514
|
-
## Module Contracts
|
|
1515
|
-
[One-line human summary — which Ports / Adapters / Services this module defines. The contract bodies fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`).]
|
|
1516
|
-
|
|
1517
|
-
<!-- Subsections: any subset of Ports / Adapters / Services / Patterns / Utilities / Module-level invariants.
|
|
1518
|
-
Unnumbered \`###\` headers (e.g. \`### Ports\`, \`### OperationDef Pattern\`). -->
|
|
1519
|
-
|
|
1520
|
-
<details>
|
|
1521
|
-
<summary>Контракты DbC</summary>
|
|
1522
|
-
|
|
1523
|
-
### Ports
|
|
1524
|
-
[Per \`DBC_PORT_FORMAT\`. Omit if this module has no Ports.]
|
|
1525
|
-
|
|
1526
|
-
### Adapters
|
|
1527
|
-
[Per \`DBC_ADAPTER_FORMAT\`. Omit if this module has no Adapters.]
|
|
1528
|
-
|
|
1529
|
-
</details>
|
|
1530
|
-
<!--/SECTION:MODULE_CONTRACTS-->
|
|
1531
|
-
|
|
1532
|
-
<!--SECTION:PUBLIC_OPTIONS-->
|
|
1533
|
-
## Public Options & Policies
|
|
1534
|
-
[One-line human summary — what is configurable. Full enumeration folds (reference detail).]
|
|
1535
|
-
|
|
1536
|
-
<details>
|
|
1537
|
-
<summary>Опции и политики</summary>
|
|
1538
|
-
|
|
1539
|
-
[All publicly observable flags/options/policies, either bound to contracts above OR explicitly \`deferred / not consumed in v1\` with link to ticket/version that activates them.]
|
|
1540
|
-
|
|
1541
|
-
</details>
|
|
1542
|
-
<!--/SECTION:PUBLIC_OPTIONS-->
|
|
1543
|
-
|
|
1544
|
-
<!--SECTION:FILE_STRUCTURE-->
|
|
1545
|
-
## File Structure
|
|
1546
|
-
\`\`\`
|
|
1547
|
-
<module>/
|
|
1548
|
-
├── ports/
|
|
1549
|
-
│ └── <name>-<port>.<ext>
|
|
1550
|
-
├── adapters/
|
|
1551
|
-
│ └── <platform>-<name>-<adapter>.<ext>
|
|
1552
|
-
└── ...
|
|
1553
|
-
\`\`\`
|
|
1554
|
-
|
|
1555
|
-
**File Mapping:**
|
|
1556
|
-
- \`<path>\`: <component>
|
|
1557
|
-
<!--/SECTION:FILE_STRUCTURE-->
|
|
1558
|
-
|
|
1559
|
-
<!--SECTION:MODULE_DECISION_LOG-->
|
|
1560
|
-
## Module Decision Log
|
|
1561
|
-
[One-line human summary — which decisions are recorded, at a glance. Full entries fold per \`AX_SPEC_PROGRESSIVE_DISCLOSURE\` (checked by \`SDD_SECTION_NOT_FOLDED\`) — a decision log only grows, it is never needed to grasp the module NOW.]
|
|
1562
|
-
|
|
1563
|
-
<details>
|
|
1564
|
-
<summary>Полные записи Decision Log</summary>
|
|
1565
|
-
|
|
1566
|
-
[D-NNN entries at module level per \`DECISION_LOG_ENTRY_FORMAT\`. Cross-module or cross-scope → scope spec or contracts spec.]
|
|
1567
|
-
|
|
1568
|
-
</details>
|
|
1569
|
-
<!--/SECTION:MODULE_DECISION_LOG-->
|
|
1570
|
-
|
|
1571
|
-
<!--SECTION:HANDOFF-->
|
|
1572
|
-
## Handoff to Tasks
|
|
1573
|
-
- **Implementation files to be created:** <list from File Structure>
|
|
1574
|
-
- **Test files to be created:** <list>
|
|
1575
|
-
- **Stack dependencies:**
|
|
1576
|
-
- Language: \`<lang>\` (resolves to \`ai/directives/coding/<lang>-rules.xml\`)
|
|
1577
|
-
- Test framework: \`<framework>\` (resolves to \`ai/directives/testing/<framework>.xml\`)
|
|
1578
|
-
- **Module Rules Additions:** (additions on top of scope-wide baseline; \`None\` if no module-specific rules)
|
|
1579
|
-
|
|
1580
|
-
| Rule | Category | Source |
|
|
1581
|
-
|------|----------|--------|
|
|
1582
|
-
| <rule-name> | coding \\| testing \\| architecture \\| quality | \`ai/directives/<category>/<rule>.xml\` |
|
|
1583
|
-
|
|
1584
|
-
- **Open risks & validation needs:** <not-closed items>
|
|
1585
|
-
<!--/SECTION:HANDOFF-->
|
|
1586
|
-
|
|
1587
|
-
<!-- Optional appendix — include only when the module has non-trivial post-impl learnings worth preserving. -->
|
|
1588
|
-
<!--SECTION:IMPLEMENTATION_INSIGHTS-->
|
|
1589
|
-
## Implementation Insights
|
|
1590
|
-
[One-line human summary — what a future reader must know. Detail folds (appendix).]
|
|
1591
|
-
|
|
1592
|
-
<details>
|
|
1593
|
-
<summary>Заметки по реализации</summary>
|
|
1594
|
-
|
|
1595
|
-
[Library wrap patterns, transaction subtleties, fake-runtime pitfalls, etc. Authored after audit-cycle compaction; immutable contract for future readers.]
|
|
1596
|
-
|
|
1597
|
-
</details>
|
|
1598
|
-
<!--/SECTION:IMPLEMENTATION_INSIGHTS-->
|
|
1599
|
-
`, fs = [
|
|
1600
|
-
{
|
|
1601
|
-
name: "MODULE_VISION",
|
|
1602
|
-
required: !0,
|
|
1603
|
-
loadBearing: !0,
|
|
1604
|
-
fold: !1,
|
|
1605
|
-
fill: "What this module owns; link to the parent scope spec and to sibling/parent/child modules."
|
|
1606
|
-
},
|
|
1607
|
-
{ name: "OVERVIEW", required: !0, loadBearing: !1, fold: !1, fill: F },
|
|
1608
|
-
{
|
|
1609
|
-
name: "MODULE_USAGE_EXAMPLE",
|
|
1610
|
-
required: !0,
|
|
1611
|
-
loadBearing: !0,
|
|
1612
|
-
fold: !1,
|
|
1613
|
-
fill: "MANDATORY: self-sufficient happy-path snippet in the target language showing how a consumer uses this module in isolation (init/happy-path/minimal error path). No cross-module composition here."
|
|
1614
|
-
},
|
|
1615
|
-
{
|
|
1616
|
-
name: "INTER_MODULE_DEPENDENCIES",
|
|
1617
|
-
required: !0,
|
|
1618
|
-
loadBearing: !1,
|
|
1619
|
-
fold: !1,
|
|
1620
|
-
fill: "Depends-on / Scope Reference / Provides-to lists plus the machine-parsed dependency graph."
|
|
1621
|
-
},
|
|
1622
|
-
{
|
|
1623
|
-
name: "ENTITY_INVENTORY",
|
|
1624
|
-
required: !0,
|
|
1625
|
-
loadBearing: !0,
|
|
1626
|
-
fold: !1,
|
|
1627
|
-
fill: "The closed-world entity inventory per ENTITY_INVENTORY_FORMAT."
|
|
1628
|
-
},
|
|
1629
|
-
{
|
|
1630
|
-
name: "ENTITY_SURFACES",
|
|
1631
|
-
required: !0,
|
|
1632
|
-
loadBearing: !1,
|
|
1633
|
-
fold: !0,
|
|
1634
|
-
fill: "One-line summary of what the surfaces cover; per-entity detail per ENTITY_SURFACE_FORMAT folds under <details>."
|
|
1635
|
-
},
|
|
1636
|
-
{
|
|
1637
|
-
name: "MODULE_CONTRACTS",
|
|
1638
|
-
required: !0,
|
|
1639
|
-
loadBearing: !0,
|
|
1640
|
-
fold: !0,
|
|
1641
|
-
fill: "One-line summary of which Ports/Adapters/Services this module defines; the DbC contract bodies (Ports per DBC_PORT_FORMAT, Adapters per DBC_ADAPTER_FORMAT) fold under <details>."
|
|
1642
|
-
},
|
|
1643
|
-
{
|
|
1644
|
-
name: "PUBLIC_OPTIONS",
|
|
1645
|
-
required: !1,
|
|
1646
|
-
loadBearing: !1,
|
|
1647
|
-
fold: !1,
|
|
1648
|
-
fill: "Publicly observable flags/options/policies, bound to a contract above or explicitly deferred with a link."
|
|
1649
|
-
},
|
|
1650
|
-
{
|
|
1651
|
-
name: "FILE_STRUCTURE",
|
|
1652
|
-
required: !0,
|
|
1653
|
-
loadBearing: !1,
|
|
1654
|
-
fold: !1,
|
|
1655
|
-
fill: "ASCII tree of the module directory plus a File Mapping list."
|
|
1656
|
-
},
|
|
1657
|
-
{
|
|
1658
|
-
name: "MODULE_DECISION_LOG",
|
|
1659
|
-
required: !1,
|
|
1660
|
-
loadBearing: !1,
|
|
1661
|
-
fold: !0,
|
|
1662
|
-
fill: "One-line summary of recorded decisions; full D-NNN entries per DECISION_LOG_ENTRY_FORMAT fold under <details>."
|
|
1663
|
-
},
|
|
1664
|
-
{
|
|
1665
|
-
name: "HANDOFF",
|
|
1666
|
-
required: !0,
|
|
1667
|
-
loadBearing: !1,
|
|
1668
|
-
fold: !1,
|
|
1669
|
-
fill: "Implementation/test files to create, stack dependencies (language/test framework), module rules additions, open risks."
|
|
1670
|
-
},
|
|
1671
|
-
{
|
|
1672
|
-
name: "IMPLEMENTATION_INSIGHTS",
|
|
1673
|
-
required: !1,
|
|
1674
|
-
loadBearing: !1,
|
|
1675
|
-
fold: !1,
|
|
1676
|
-
fill: "Optional appendix — non-trivial post-implementation learnings (library wrap patterns, transaction subtleties, fake-runtime pitfalls), added after audit-cycle compaction."
|
|
1677
|
-
}
|
|
1678
|
-
], ps = `# Task: <ACRONYM>-<slug> — <Task Title>
|
|
1679
|
-
|
|
1680
|
-
<!--SECTION:META-->
|
|
1681
|
-
## Meta
|
|
1682
|
-
- **Task-ID:** <ACRONYM>-<slug> <!-- semantic slug; same slug across branches = same feature (see Slug Registry) -->
|
|
1683
|
-
- **Status:** [ ] TODO <!-- [ ] TODO | [~] IN_PROGRESS | [x] DONE | [!] BLOCKED -->
|
|
1684
|
-
- **Purpose:** <semantic goal one-liner>
|
|
1685
|
-
- **Scope:** <scope-name>
|
|
1686
|
-
- **Module:** <module-name or N/A>
|
|
1687
|
-
- **Dependencies:** <comma-separated Task-IDs or None>
|
|
1688
|
-
- **Reopens:** <count> (<YYYY-MM-DD> — <last reason>) <!-- omit when 0 -->
|
|
1689
|
-
- **Spec References:** <!-- one anchor PER contract — makes the contract set enumerable for the typing-scenario check -->
|
|
1690
|
-
- Contract: [<PortName>](<spec anchor>)
|
|
1691
|
-
- Adapter: [<AdapterName>](<spec anchor>)
|
|
1692
|
-
- Consumer: [<ConsumerName>](<spec anchor>)
|
|
1693
|
-
- Constraints (if applicable): [<scope spec §>](<path>)
|
|
1694
|
-
- **Runtime Backing:** \`not-implemented\` | \`simulation\` | \`real-runtime\`
|
|
1695
|
-
- **Verification Levels:** subset of \`contract\` | \`unit\` | \`integration\` | \`e2e\`
|
|
1696
|
-
- **Deferred Runtime Scope:** None | <description>
|
|
1697
|
-
<!--/SECTION:META-->
|
|
1698
|
-
|
|
1699
|
-
<!--SECTION:PHASES_OVERVIEW-->
|
|
1700
|
-
## Phases Overview
|
|
1701
|
-
| ID | Kind | Deps | Status |
|
|
1702
|
-
|----|------|------|--------|
|
|
1703
|
-
| P1 | <kind> | — | [ ] |
|
|
1704
|
-
| P2 | <kind> | P1 | [ ] |
|
|
1705
|
-
|
|
1706
|
-
<!-- Kind ∈ bootstrap | impl | test | config | doc | refactor (fix only on execution). impl and test are ALWAYS separate phases. Orchestrator reads this table to plan. -->
|
|
1707
|
-
<!--/SECTION:PHASES_OVERVIEW-->
|
|
1708
|
-
|
|
1709
|
-
## Phases
|
|
1710
|
-
|
|
1711
|
-
<!--SECTION:PHASE_P1-->
|
|
1712
|
-
### P1 — <kind>
|
|
1713
|
-
- **Objective:** <one-line>
|
|
1714
|
-
- **Rules:** <!-- links only, resolved from the cascade; rule content is never inlined -->
|
|
1715
|
-
- [ai/directives/<category>/<rule>.xml](<relative-path>)
|
|
1716
|
-
- **Target Files:**
|
|
1717
|
-
- <path>
|
|
1718
|
-
- **Inputs:** none <!-- or "P1 handoff" -->
|
|
1719
|
-
- **Exit:** <verifiable criterion>
|
|
1720
|
-
<!--/SECTION:PHASE_P1-->
|
|
1721
|
-
|
|
1722
|
-
<!--SECTION:PHASE_P2-->
|
|
1723
|
-
### P2 — <kind>
|
|
1724
|
-
- **Objective:** <one-line>
|
|
1725
|
-
- **Rules:**
|
|
1726
|
-
- [ai/directives/<category>/<rule>.xml](<relative-path>)
|
|
1727
|
-
- **Target Files:**
|
|
1728
|
-
- <path>
|
|
1729
|
-
- **Inputs:** P1 handoff
|
|
1730
|
-
- **Exit:** <verifiable criterion>
|
|
1731
|
-
<!--/SECTION:PHASE_P2-->
|
|
1732
|
-
|
|
1733
|
-
<!--SECTION:BDD-->
|
|
1734
|
-
## Acceptance Criteria (BDD)
|
|
1735
|
-
Each scenario is tagged with the requirement it proves and its verification level — this is the
|
|
1736
|
-
use-case → \`[<ACRONYM>-REQ-N]\` → vision chain the operator reviews at scaffold.
|
|
1737
|
-
|
|
1738
|
-
**Feature:** <component behavior>
|
|
1739
|
-
|
|
1740
|
-
**Scenario:** <name> [\`<level>\`] \`[<ACRONYM>-REQ-N]\`
|
|
1741
|
-
- **Given** <precondition>
|
|
1742
|
-
- **When** <command / query>
|
|
1743
|
-
- **Then** <postcondition>
|
|
1744
|
-
- **And** <side effect>
|
|
1745
|
-
|
|
1746
|
-
<!-- BLOCKER: every DbC contract in Spec References has at least one \`contract\`-level typing scenario; Deferred Test Ownership is forbidden for typing scenarios. -->
|
|
1747
|
-
<!--/SECTION:BDD-->
|
|
1748
|
-
|
|
1749
|
-
<!--SECTION:VERIFICATION-->
|
|
1750
|
-
## Verification
|
|
1751
|
-
| Command | Required by |
|
|
1752
|
-
|---------|-------------|
|
|
1753
|
-
| <resolved invocation> | <rule-id>, <rule-id> |
|
|
1754
|
-
|
|
1755
|
-
<!-- One row per unique check-command alias. Phase-subagent runs only rows whose Required-by overlaps its phase Rules. -->
|
|
1756
|
-
|
|
1757
|
-
- **Task-specific Completion additions:** <list or "none beyond project baseline">
|
|
1758
|
-
<!--/SECTION:VERIFICATION-->
|
|
1759
|
-
|
|
1760
|
-
<!--SECTION:TEST_COVERAGE-->
|
|
1761
|
-
## Test Scenario Coverage <!-- BLOCKER: an unmapped scenario blocks task close -->
|
|
1762
|
-
- Scenario <name> → \`<test-file>\` :: \`<canonical case name>\` <!-- or: Deferred Test Ownership: <Task-ID> -->
|
|
1763
|
-
<!--/SECTION:TEST_COVERAGE-->
|
|
1764
|
-
|
|
1765
|
-
<!--SECTION:EXECUTION_LOG-->
|
|
1766
|
-
## Execution Log
|
|
1767
|
-
*(Round = one execute-then-audit attempt; per-phase blocks within a Round. Skeleton is minimal — event lines appear only when the event happens. Token vocabulary lives in \`<module>.3-tasks.md\`. A \`[x]\` line with an unreplaced \`<…>\` placeholder is a fabricated DONE — forbidden.)*
|
|
1768
|
-
|
|
1769
|
-
### Round 1 — <YYYY-MM-DD>, initial
|
|
1770
|
-
|
|
1771
|
-
#### P1
|
|
1772
|
-
- [ ] \`<ts>\` ver \`<cmd>\` → \`<pass|fail>\` exit=\`<code>\`
|
|
1773
|
-
- [ ] \`<ts>\` DONE
|
|
1774
|
-
**Handoff →** artifacts: [...]; decisions: [...]; open: [...]
|
|
1775
|
-
|
|
1776
|
-
#### Round close
|
|
1777
|
-
- [ ] \`<ts>\` DONE
|
|
1778
|
-
<!--/SECTION:EXECUTION_LOG-->
|
|
1779
|
-
|
|
1780
|
-
<!--SECTION:DECISION_LOG-->
|
|
1781
|
-
## Decision Log
|
|
1782
|
-
<!-- local decisions taken during execution, ADR-compact (\`<ACRONYM>-D-N · <statement>\` → why ↳ rejected). Omit if none beyond the spec. -->
|
|
1783
|
-
<!--/SECTION:DECISION_LOG-->
|
|
1784
|
-
|
|
1785
|
-
<!-- AUDIT_ROUNDS appended only after the first reopen-triggering audit (per the audit directive). -->
|
|
1786
|
-
`, ms = [
|
|
1787
|
-
{
|
|
1788
|
-
name: "META",
|
|
1789
|
-
required: !0,
|
|
1790
|
-
loadBearing: !1,
|
|
1791
|
-
fold: !1,
|
|
1792
|
-
fill: "Task-ID, Status, Purpose, Scope/Module, Dependencies, Spec References (one anchor per contract), Runtime Backing, Verification Levels, Deferred Runtime Scope."
|
|
1793
|
-
},
|
|
1794
|
-
{
|
|
1795
|
-
name: "PHASES_OVERVIEW",
|
|
1796
|
-
required: !0,
|
|
1797
|
-
loadBearing: !1,
|
|
1798
|
-
fold: !1,
|
|
1799
|
-
fill: "ID/Kind/Deps/Status table for every phase; impl and test are always separate phases."
|
|
1800
|
-
},
|
|
1801
|
-
{
|
|
1802
|
-
name: "PHASE_P<N>",
|
|
1803
|
-
required: !0,
|
|
1804
|
-
loadBearing: !1,
|
|
1805
|
-
fold: !1,
|
|
1806
|
-
fill: "One anchored PHASE_P<N> section per row in Phases Overview: Objective, Rules (links only), Target Files, Inputs, Exit criterion."
|
|
1807
|
-
},
|
|
1808
|
-
{
|
|
1809
|
-
name: "BDD",
|
|
1810
|
-
required: !0,
|
|
1811
|
-
loadBearing: !1,
|
|
1812
|
-
fold: !1,
|
|
1813
|
-
fill: "Given/When/Then scenarios tagged with [<ACRONYM>-REQ-N] and a verification level. BLOCKER: every DbC contract in Spec References needs ≥1 contract-level typing scenario."
|
|
1814
|
-
},
|
|
1815
|
-
{
|
|
1816
|
-
name: "VERIFICATION",
|
|
1817
|
-
required: !0,
|
|
1818
|
-
loadBearing: !1,
|
|
1819
|
-
fold: !1,
|
|
1820
|
-
fill: "Command/Required-by table — one row per unique check-command alias — plus task-specific completion additions."
|
|
1821
|
-
},
|
|
1822
|
-
{
|
|
1823
|
-
name: "TEST_COVERAGE",
|
|
1824
|
-
required: !0,
|
|
1825
|
-
loadBearing: !1,
|
|
1826
|
-
fold: !1,
|
|
1827
|
-
fill: "BLOCKER: map every BDD scenario to a test-file + case name, or an explicit Deferred Test Ownership."
|
|
1828
|
-
},
|
|
1829
|
-
{
|
|
1830
|
-
name: "EXECUTION_LOG",
|
|
1831
|
-
required: !0,
|
|
1832
|
-
loadBearing: !1,
|
|
1833
|
-
fold: !1,
|
|
1834
|
-
fill: "Round/phase event log — timestamps, verification runs, DONE lines, handoffs. Never fabricate a [x] with a placeholder still in it."
|
|
1835
|
-
},
|
|
1836
|
-
{
|
|
1837
|
-
name: "DECISION_LOG",
|
|
1838
|
-
required: !1,
|
|
1839
|
-
loadBearing: !1,
|
|
1840
|
-
fold: !1,
|
|
1841
|
-
fill: "Local ADR-compact decisions taken during execution; omit if none beyond the spec."
|
|
1842
|
-
}
|
|
1843
|
-
], Es = `# <module> — Tasks
|
|
1844
|
-
|
|
1845
|
-
## Tracker Index
|
|
1846
|
-
| Task-ID | Title | Dependencies | Status | Reopens |
|
|
1847
|
-
|---------|-------|--------------|--------|---------|
|
|
1848
|
-
| <ACRONYM>-<slug> | <title> | <deps or —> | [ ] TODO | — |
|
|
1849
|
-
|
|
1850
|
-
## Slug Registry
|
|
1851
|
-
<!-- one slug per line; this IS the uniqueness mechanism — the same slug in two branches collides on merge here, surfacing "same feature" instead of hiding it. Append-only. -->
|
|
1852
|
-
- <slug>
|
|
1853
|
-
|
|
1854
|
-
## Intra-Module DAG
|
|
1855
|
-
\`\`\`mermaid
|
|
1856
|
-
graph TD
|
|
1857
|
-
A[<slug-a>] --> B[<slug-b>]
|
|
1858
|
-
\`\`\`
|
|
1859
|
-
<!-- edge A → B = "A depends on B". Cross-module / cross-scope edges live one level up, not here. -->
|
|
1860
|
-
|
|
1861
|
-
## Decision Log (module-task level)
|
|
1862
|
-
<!-- decomposition / planning decisions (ticket merges, deferred coverage, rule-activation overrides), ADR-compact. Local execution-time decisions stay in each ticket's own Decision Log. -->
|
|
1863
|
-
|
|
1864
|
-
## Conventions
|
|
1865
|
-
Project-wide conventions (Execution-Log token vocabulary, Baseline Completion Rule, post-task audit hook, file-header) are declared once in \`specs/3-tasks.md\` and inherited here — not repeated.
|
|
1866
|
-
`, hs = [
|
|
1867
|
-
{
|
|
1868
|
-
name: "TRACKER_INDEX",
|
|
1869
|
-
required: !0,
|
|
1870
|
-
loadBearing: !1,
|
|
1871
|
-
fold: !1,
|
|
1872
|
-
fill: "Task-ID/Title/Dependencies/Status/Reopens table — one row per ticket in this module."
|
|
1873
|
-
},
|
|
1874
|
-
{
|
|
1875
|
-
name: "SLUG_REGISTRY",
|
|
1876
|
-
required: !0,
|
|
1877
|
-
loadBearing: !1,
|
|
1878
|
-
fold: !1,
|
|
1879
|
-
fill: "Append-only list of every slug used in this module — the merge-collision uniqueness mechanism."
|
|
1880
|
-
},
|
|
1881
|
-
{
|
|
1882
|
-
name: "INTRA_MODULE_DAG",
|
|
1883
|
-
required: !0,
|
|
1884
|
-
loadBearing: !1,
|
|
1885
|
-
fold: !1,
|
|
1886
|
-
fill: 'Mermaid graph of intra-module ticket dependencies (edge A --> B = "A depends on B").'
|
|
1887
|
-
},
|
|
1888
|
-
{
|
|
1889
|
-
name: "DECISION_LOG",
|
|
1890
|
-
required: !1,
|
|
1891
|
-
loadBearing: !1,
|
|
1892
|
-
fold: !1,
|
|
1893
|
-
fill: "Decomposition/planning decisions (ticket merges, deferred coverage, rule-activation overrides), ADR-compact."
|
|
1894
|
-
},
|
|
1895
|
-
{
|
|
1896
|
-
name: "CONVENTIONS",
|
|
1897
|
-
required: !0,
|
|
1898
|
-
loadBearing: !1,
|
|
1899
|
-
fold: !1,
|
|
1900
|
-
fill: "Pointer to specs/3-tasks.md — project-wide conventions are declared once there, inherited here."
|
|
1901
|
-
}
|
|
1902
|
-
], Ss = `# Tasks: <scope>
|
|
1903
|
-
|
|
1904
|
-
## Scope Spec
|
|
1905
|
-
- [Scope spec](./<scope>.spec.md)
|
|
1906
|
-
|
|
1907
|
-
## Cascade Table
|
|
1908
|
-
Effective rules for this scope, from the Scope Graph (depends-on transitive closure). Tier order (low → high on collision): traversed-scopes → target-scope → module → phase.
|
|
1909
|
-
| Tier | coding | testing | architecture | infra |
|
|
1910
|
-
|------|--------|---------|--------------|-------|
|
|
1911
|
-
| infra-base (traversed) | typescript-rules | vitest-rules | | eslint-setup |
|
|
1912
|
-
| <scope> (target) | | | ports-adapters | |
|
|
1913
|
-
| module:<m> | | node-test | | |
|
|
1914
|
-
|
|
1915
|
-
## Inter-Module DAG
|
|
1916
|
-
\`\`\`mermaid
|
|
1917
|
-
graph TD
|
|
1918
|
-
A[<module-a>] --> B[<module-b>]
|
|
1919
|
-
\`\`\`
|
|
1920
|
-
|
|
1921
|
-
## Tracker
|
|
1922
|
-
| Task-ID | Title | Module | Dependencies | Status | Reopens |
|
|
1923
|
-
|---------|-------|--------|--------------|--------|---------|
|
|
1924
|
-
| <ACR>-<slug> | <title> | <module> | <deps> | [ ] TODO | — |
|
|
1925
|
-
|
|
1926
|
-
## Decision Log (scope task level)
|
|
1927
|
-
[D-NNN for scope-level decomposition / planning choices.]
|
|
1928
|
-
`, gs = [
|
|
1929
|
-
{
|
|
1930
|
-
name: "SCOPE_SPEC_LINK",
|
|
1931
|
-
required: !0,
|
|
1932
|
-
loadBearing: !1,
|
|
1933
|
-
fold: !1,
|
|
1934
|
-
fill: "Link to the co-located scope spec (./<scope>.spec.md)."
|
|
1935
|
-
},
|
|
1936
|
-
{
|
|
1937
|
-
name: "CASCADE_TABLE",
|
|
1938
|
-
required: !0,
|
|
1939
|
-
loadBearing: !1,
|
|
1940
|
-
fold: !1,
|
|
1941
|
-
fill: "Effective rules for this scope from the Scope Graph depends-on transitive closure, by tier (traversed-scopes → target-scope → module → phase)."
|
|
1942
|
-
},
|
|
1943
|
-
{
|
|
1944
|
-
name: "INTER_MODULE_DAG",
|
|
1945
|
-
required: !0,
|
|
1946
|
-
loadBearing: !1,
|
|
1947
|
-
fold: !1,
|
|
1948
|
-
fill: "Mermaid graph of inter-module dependencies within this scope."
|
|
1949
|
-
},
|
|
1950
|
-
{
|
|
1951
|
-
name: "TRACKER",
|
|
1952
|
-
required: !0,
|
|
1953
|
-
loadBearing: !1,
|
|
1954
|
-
fold: !1,
|
|
1955
|
-
fill: "Task-ID/Title/Module/Dependencies/Status/Reopens rollup table — module tickets plus any scope-level tickets (infra/interface scopes with no modules)."
|
|
1956
|
-
},
|
|
1957
|
-
{
|
|
1958
|
-
name: "DECISION_LOG",
|
|
1959
|
-
required: !1,
|
|
1960
|
-
loadBearing: !1,
|
|
1961
|
-
fold: !1,
|
|
1962
|
-
fill: "Scope-level decomposition/planning decisions, D-NNN, ADR-compact."
|
|
1963
|
-
}
|
|
1964
|
-
], Is = "# Project Tasks\n\n## Entry Points\n- [Specs Portal](./README.md) — Scope Graph + all scope specs.\n- Tickets execute ONLY via the `/sdd-execute` flow (one ticket) or its batch form; the orchestrator dispatches phase-workers then audit — the operator does not invoke audit by hand.\n\n## Project-Wide Conventions (declared once, inherited)\n- **File-header:** owned by the coding rule (`@file` / `@consumers` / `@tasks`), enforced by `sdd-verify`.\n- **Baseline Completion Rule:** a Round cannot go `[x] DONE` until — every phase `[x]`; every BDD scenario mapped to a test or `Deferred Test Ownership`; verification commands run with exit recorded; every entity beyond the Inventory logged `intro …`; a Handoff line closes each phase.\n- **Execution-Log token vocabulary:** `intro <Entity> ← <reason>` · `decision <key>=<value> ← <reason>` · `tried <approach> → <result>` · `discovery <fact>` · `insight <observation> → <spec-section>` · `verified <tool>@<version> <summary>` · `ver <cmd> → pass|fail exit=<N>` · `BLOCKED <cause>` · `DONE`. A `[x]` line with an unreplaced `<…>` placeholder is fabricated (BLOCKER).\n- **Post-task hook:** after a Round closes the orchestrator runs audit; until PASS the round is closed-but-unverified and dependents are blocked.\n\n## Cross-Scope DAG\nCross-scope edges + integration tickets only; intra-scope edges live in each scope index. Order follows the Portal Scope Graph.\n```mermaid\ngraph TD\n backend --> infra-base\n web --> backend\n```\n\n## Scope Tracker\n| Scope | Type | Index | Tasks | Done |\n|---|---|---|---|---|\n| infra-base | infrastructure | [3-tasks](./infra-base/infra-base.3-tasks.md) | 6 | 0/6 |\n| backend | product | [3-tasks](./backend/backend.3-tasks.md) | 12 | 0/12 |\n\n## Decision Log (project task level)\n[D-NNN when the operator made non-default cross-scope choices.]\n", Os = [
|
|
1965
|
-
{
|
|
1966
|
-
name: "ENTRY_POINTS",
|
|
1967
|
-
required: !0,
|
|
1968
|
-
loadBearing: !1,
|
|
1969
|
-
fold: !1,
|
|
1970
|
-
fill: "Link to the Specs Portal, plus the reminder that tickets execute only via /sdd-execute (single or batch)."
|
|
1971
|
-
},
|
|
1972
|
-
{
|
|
1973
|
-
name: "PROJECT_WIDE_CONVENTIONS",
|
|
1974
|
-
required: !0,
|
|
1975
|
-
loadBearing: !1,
|
|
1976
|
-
fold: !1,
|
|
1977
|
-
fill: "File-header rule, Baseline Completion Rule, Execution-Log token vocabulary, and post-task audit hook — declared once here, inherited by every scope/module/ticket."
|
|
1978
|
-
},
|
|
1979
|
-
{
|
|
1980
|
-
name: "CROSS_SCOPE_DAG",
|
|
1981
|
-
required: !0,
|
|
1982
|
-
loadBearing: !1,
|
|
1983
|
-
fold: !1,
|
|
1984
|
-
fill: "Mermaid graph of cross-scope depends-on edges + integration tickets only; intra-scope edges live in each scope index."
|
|
1985
|
-
},
|
|
1986
|
-
{
|
|
1987
|
-
name: "SCOPE_TRACKER",
|
|
1988
|
-
required: !0,
|
|
1989
|
-
loadBearing: !1,
|
|
1990
|
-
fold: !1,
|
|
1991
|
-
fill: "Scope/Type/Index/Tasks/Done rollup table — one row per scope, linking to its 3-tasks.md."
|
|
1992
|
-
},
|
|
1993
|
-
{
|
|
1994
|
-
name: "DECISION_LOG",
|
|
1995
|
-
required: !1,
|
|
1996
|
-
loadBearing: !1,
|
|
1997
|
-
fold: !1,
|
|
1998
|
-
fill: "Cross-scope decomposition/planning decisions, D-NNN, ADR-compact."
|
|
1999
|
-
}
|
|
2000
|
-
], Ts = `# <project-name>
|
|
2001
|
-
|
|
2002
|
-
## Vision
|
|
2003
|
-
<Одно предложение. Опускается для infrastructure-only проектов.>
|
|
2004
|
-
|
|
2005
|
-
## Scope Graph
|
|
2006
|
-
|
|
2007
|
-
\`\`\`mermaid
|
|
2008
|
-
graph TD
|
|
2009
|
-
backend --> infra-base
|
|
2010
|
-
backend --> api
|
|
2011
|
-
web --> infra-base
|
|
2012
|
-
web --> api
|
|
2013
|
-
web --> design-system-core
|
|
2014
|
-
mobile --> api
|
|
2015
|
-
\`\`\`
|
|
2016
|
-
|
|
2017
|
-
## Scopes
|
|
2018
|
-
|
|
2019
|
-
| Scope | Type | Spec | Description |
|
|
2020
|
-
|---|---|---|---|
|
|
2021
|
-
| [\`infra-base\`](./infra-base/infra-base.spec.md) | infrastructure | ✅ | TS + pnpm + vitest + biome |
|
|
2022
|
-
| [\`api\`](./api/api.spec.md) | interface | 🚧 | REST API v1 |
|
|
2023
|
-
| [\`backend\`](./backend/backend.spec.md) | product | ✅ | Node.js IMAP-сервис |
|
|
2024
|
-
| [\`web\`](./web/web.spec.md) | product | 🚧 | React SPA |
|
|
2025
|
-
`, _s = [
|
|
2026
|
-
{
|
|
2027
|
-
name: "VISION",
|
|
2028
|
-
required: !1,
|
|
2029
|
-
loadBearing: !1,
|
|
2030
|
-
fold: !1,
|
|
2031
|
-
fill: "One sentence for the whole project; omit for infrastructure-only projects."
|
|
2032
|
-
},
|
|
2033
|
-
{
|
|
2034
|
-
name: "SCOPE_GRAPH",
|
|
2035
|
-
required: !0,
|
|
2036
|
-
loadBearing: !1,
|
|
2037
|
-
fold: !1,
|
|
2038
|
-
fill: "A mermaid graph of scope depends-on edges."
|
|
2039
|
-
},
|
|
2040
|
-
{
|
|
2041
|
-
name: "SCOPES",
|
|
2042
|
-
required: !0,
|
|
2043
|
-
loadBearing: !1,
|
|
2044
|
-
fold: !1,
|
|
2045
|
-
fill: "Scope/Type/Spec-status/Description table — one row per scope, linking to its spec.md."
|
|
2046
|
-
}
|
|
2047
|
-
], pe = {
|
|
2048
|
-
product: {
|
|
2049
|
-
kind: "product",
|
|
2050
|
-
skeleton: ns,
|
|
2051
|
-
sections: os,
|
|
2052
|
-
pathPattern: "specs/<scope>/<scope>.spec.md"
|
|
2053
|
-
},
|
|
2054
|
-
library: {
|
|
2055
|
-
kind: "library",
|
|
2056
|
-
skeleton: rs,
|
|
2057
|
-
sections: is,
|
|
2058
|
-
pathPattern: "specs/<scope>/<scope>.spec.md"
|
|
2059
|
-
},
|
|
2060
|
-
infrastructure: {
|
|
2061
|
-
kind: "infrastructure",
|
|
2062
|
-
skeleton: as,
|
|
2063
|
-
sections: cs,
|
|
2064
|
-
pathPattern: "specs/<scope>/<scope>.spec.md"
|
|
2065
|
-
},
|
|
2066
|
-
interface: {
|
|
2067
|
-
kind: "interface",
|
|
2068
|
-
skeleton: ls,
|
|
2069
|
-
sections: ds,
|
|
2070
|
-
pathPattern: "specs/<scope>/<scope>.spec.md"
|
|
2071
|
-
},
|
|
2072
|
-
module: {
|
|
2073
|
-
kind: "module",
|
|
2074
|
-
skeleton: us,
|
|
2075
|
-
sections: fs,
|
|
2076
|
-
pathPattern: "specs/<scope>/<module>/<module>.spec.md"
|
|
2077
|
-
},
|
|
2078
|
-
task: {
|
|
2079
|
-
kind: "task",
|
|
2080
|
-
skeleton: ps,
|
|
2081
|
-
sections: ms,
|
|
2082
|
-
pathPattern: "specs/<scope>/<module>/<module>.task.<ACR>-<slug>.md"
|
|
2083
|
-
},
|
|
2084
|
-
"module-index": {
|
|
2085
|
-
kind: "module-index",
|
|
2086
|
-
skeleton: Es,
|
|
2087
|
-
sections: hs,
|
|
2088
|
-
pathPattern: "specs/<scope>/<module...>/<module>.3-tasks.md"
|
|
2089
|
-
},
|
|
2090
|
-
"scope-index": {
|
|
2091
|
-
kind: "scope-index",
|
|
2092
|
-
skeleton: Ss,
|
|
2093
|
-
sections: gs,
|
|
2094
|
-
pathPattern: "specs/<scope>/<scope>.3-tasks.md"
|
|
2095
|
-
},
|
|
2096
|
-
"project-index": {
|
|
2097
|
-
kind: "project-index",
|
|
2098
|
-
skeleton: Is,
|
|
2099
|
-
sections: Os,
|
|
2100
|
-
pathPattern: "specs/3-tasks.md"
|
|
2101
|
-
},
|
|
2102
|
-
portal: {
|
|
2103
|
-
kind: "portal",
|
|
2104
|
-
skeleton: Ts,
|
|
2105
|
-
sections: _s,
|
|
2106
|
-
pathPattern: "specs/README.md"
|
|
2107
|
-
}
|
|
2108
|
-
}, Io = Object.keys(pe);
|
|
2109
|
-
function Ye(e) {
|
|
2110
|
-
return pe[e].sections.filter((t) => t.loadBearing).map((t) => t.name);
|
|
2111
|
-
}
|
|
2112
|
-
function Ns(e) {
|
|
2113
|
-
return pe[e].sections.filter((t) => t.fold).map((t) => t.name);
|
|
2114
|
-
}
|
|
2115
|
-
function Cs(e) {
|
|
2116
|
-
return e.provider === "github" ? new Nt({ baseUrl: "https://api.github.com", token: e.token }) : new Ct({ baseUrl: `https://${e.host}/api/v4`, token: e.token });
|
|
2117
|
-
}
|
|
2118
|
-
const Oo = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
2119
|
-
__proto__: null,
|
|
2120
|
-
createVcsClient: Cs
|
|
2121
|
-
}, Symbol.toStringTag, { value: "Module" })), He = "(?<!\\p{L})", Ds = "(?!\\p{L})", L = (e) => new RegExp(`${He}${e}${Ds}`, "iu"), T = (e) => new RegExp(`${He}${e}\\p{L}*`, "iu"), As = [
|
|
2122
|
-
{ re: L("проз[аыуе]"), why: "книжное", use: "текст" },
|
|
2123
|
-
{ re: L("новь[её]"), why: "сленг", use: "новое" },
|
|
2124
|
-
{ re: T("докрут"), why: "сленг", use: "доработать" },
|
|
2125
|
-
{ re: T("обстук"), why: "сленг", use: "проверить" },
|
|
2126
|
-
{ re: T("запил"), why: "сленг", use: "сделать" },
|
|
2127
|
-
{ re: T("реифиц"), why: "калька (reify)", use: "создать объект / сохранить" },
|
|
2128
|
-
{ re: T("инстанцир"), why: "калька (instantiate)", use: "создать" },
|
|
2129
|
-
{ re: L("дабы"), why: "архаизм", use: "чтобы" },
|
|
2130
|
-
{ re: L("посему"), why: "архаизм", use: "поэтому" },
|
|
2131
|
-
{ re: L("сие"), why: "архаизм", use: "это" },
|
|
2132
|
-
{ re: L("надлежит"), why: "канцелярит", use: "нужно / следует" },
|
|
2133
|
-
{ re: L("зиждется"), why: "книжное", use: "основан на" },
|
|
2134
|
-
{ re: T("воззрени"), why: "книжное", use: "взгляд / подход" },
|
|
2135
|
-
{ re: T("лейтмотив"), why: "книжное", use: "основная мысль" },
|
|
2136
|
-
{ re: T("скаут"), why: "калька (scout)", use: "просмотреть / разведать" },
|
|
2137
|
-
{ re: L("дифф(?:а|е|у|ом|ов|ами)?"), why: "калька (diff)", use: "изменения / различия" },
|
|
2138
|
-
{ re: T("чейнджсет"), why: "калька (changeset)", use: "набор изменений" },
|
|
2139
|
-
{ re: T("триаж"), why: "калька (triage)", use: "разбор / сортировка" },
|
|
2140
|
-
{ re: T("х[эе]ндофф"), why: "калька (handoff)", use: "передача / готовое задание" },
|
|
2141
|
-
{ re: T("факт-чек"), why: "калька (fact-check)", use: "сверка / проверка фактов" },
|
|
2142
|
-
// Пойманы на описании MR!177 — оператор: «мозг плавится, это не читать». Только бесспорные случаи:
|
|
2143
|
-
// прижившиеся в русском IT слова (деградация, оркестрация, раунд) НЕ баним — точность важнее полноты.
|
|
2144
|
-
{ re: T("гейт"), why: "калька (gate)", use: "проверка / запрет / условие" },
|
|
2145
|
-
{ re: T("сентинел"), why: "калька (sentinel)", use: "служебное значение" },
|
|
2146
|
-
{ re: T("перевооруж"), why: "калька (re-arm)", use: "запустить заново / поставить заново" },
|
|
2147
|
-
{ re: T("поглощающ"), why: "калька (absorbing state)", use: "застревает навсегда" },
|
|
2148
|
-
{ re: L("фикс(?:а|у|ом|е|ы|ов|ами)?"), why: "сленг (fix)", use: "правка / исправление" },
|
|
2149
|
-
{ re: T("блокер"), why: "сленг (blocker)", use: "блокирующий дефект" },
|
|
2150
|
-
{ re: T("терминальн"), why: "калька (terminal)", use: "окончательный / конечный" }
|
|
2151
|
-
];
|
|
2152
|
-
function To(e) {
|
|
2153
|
-
const t = [], s = e.split(`
|
|
2154
|
-
`), o = e.replace(/<!--[\s\S]*?-->/g, (i) => i.replace(/[^\n]/g, " ")).split(`
|
|
2155
|
-
`);
|
|
2156
|
-
let r = !1;
|
|
2157
|
-
for (let i = 0; i < o.length; i++) {
|
|
2158
|
-
const a = o[i];
|
|
2159
|
-
if (a.trim().startsWith("```")) {
|
|
2160
|
-
r = !r;
|
|
2161
|
-
continue;
|
|
2162
|
-
}
|
|
2163
|
-
if (r || s[i].includes("<!-- stop-ok")) continue;
|
|
2164
|
-
const l = a.replace(/`[^`]*`/g, (f) => " ".repeat(f.length));
|
|
2165
|
-
for (const f of As) {
|
|
2166
|
-
const d = new RegExp(
|
|
2167
|
-
f.re.source,
|
|
2168
|
-
f.re.flags.includes("g") ? f.re.flags : f.re.flags + "g"
|
|
2169
|
-
);
|
|
2170
|
-
let u;
|
|
2171
|
-
for (; (u = d.exec(l)) !== null; )
|
|
2172
|
-
t.push({ word: u[0], line: i + 1, col: u.index + 1, why: f.why, use: f.use }), u.index === d.lastIndex && d.lastIndex++;
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
|
-
return t;
|
|
2176
|
-
}
|
|
2177
|
-
const ys = /^[A-Z][A-Z0-9_]*$/;
|
|
2178
|
-
function qe(e) {
|
|
2179
|
-
return ys.test(e);
|
|
2180
|
-
}
|
|
2181
|
-
function g(e, t) {
|
|
2182
|
-
if (!qe(t)) return { status: "invalid_name" };
|
|
2183
|
-
const s = `<!--SECTION:${t}-->`, n = `<!--/SECTION:${t}-->`, o = e.split(`
|
|
2184
|
-
`);
|
|
2185
|
-
let r = 0, i = 0;
|
|
2186
|
-
for (const f of o) {
|
|
2187
|
-
const d = f.trim();
|
|
2188
|
-
d === s ? r++ : d === n && i++;
|
|
2189
|
-
}
|
|
2190
|
-
if (r === 0 && i === 0) return { status: "not_found" };
|
|
2191
|
-
if (r !== i) return { status: "unbalanced", startCount: r, endCount: i };
|
|
2192
|
-
if (r > 1) return { status: "duplicated", count: r };
|
|
2193
|
-
const a = [];
|
|
2194
|
-
let c = !1;
|
|
2195
|
-
for (const f of o) {
|
|
2196
|
-
const d = f.trim();
|
|
2197
|
-
if (d === s) {
|
|
2198
|
-
c = !0;
|
|
2199
|
-
continue;
|
|
2200
|
-
}
|
|
2201
|
-
if (d === n) {
|
|
2202
|
-
c = !1;
|
|
2203
|
-
continue;
|
|
2204
|
-
}
|
|
2205
|
-
c && a.push(f);
|
|
2206
|
-
}
|
|
2207
|
-
const l = a.join(`
|
|
2208
|
-
`);
|
|
2209
|
-
return l.trim() === "" ? { status: "empty" } : { status: "ok", content: l };
|
|
2210
|
-
}
|
|
2211
|
-
function _o(e, t) {
|
|
2212
|
-
if (!qe(t)) return null;
|
|
2213
|
-
const s = `<!--SECTION:${t}-->`, n = `<!--/SECTION:${t}-->`, o = e.split(`
|
|
2214
|
-
`);
|
|
2215
|
-
let r = -1, i = -1, a = 0, c = 0;
|
|
2216
|
-
return o.forEach((l, f) => {
|
|
2217
|
-
const d = l.trim();
|
|
2218
|
-
d === s ? (a++, r === -1 && (r = f)) : d === n && (c++, i = f);
|
|
2219
|
-
}), a !== 1 || c !== 1 || i < r ? null : { openLine: r, closeLine: i };
|
|
2220
|
-
}
|
|
2221
|
-
function No(e) {
|
|
2222
|
-
const t = g(e, "ENTITY_INVENTORY");
|
|
2223
|
-
return t.status !== "ok" ? [] : t.content.split(`
|
|
2224
|
-
`).map((n) => n.trim()).filter((n) => n.startsWith("|") && !/^\|[\s:|-]+$/.test(n)).slice(1).map((n) => (n.split("|")[1] ?? "").replace(/`/g, "").trim()).filter(Boolean);
|
|
2225
|
-
}
|
|
2226
|
-
function Rs(e, t) {
|
|
2227
|
-
try {
|
|
2228
|
-
const s = h(e, "package.json");
|
|
2229
|
-
if (!R(s) || JSON.parse(D(s, "utf-8")).name !== "gennady") return null;
|
|
2230
|
-
const o = h(e, t);
|
|
2231
|
-
return R(o) ? o : null;
|
|
2232
|
-
} catch {
|
|
2233
|
-
return null;
|
|
2234
|
-
}
|
|
2235
|
-
}
|
|
2236
|
-
function Co(e, t) {
|
|
2237
|
-
try {
|
|
2238
|
-
const n = h(e, "node_modules", "gennady", t);
|
|
2239
|
-
if (R(n)) return n;
|
|
2240
|
-
} catch {
|
|
2241
|
-
}
|
|
2242
|
-
try {
|
|
2243
|
-
const n = import.meta.resolve("gennady"), r = fe(n).replace(/[/\\]dist[/\\].*$/, ""), i = h(r, t);
|
|
2244
|
-
if (R(i)) return i;
|
|
2245
|
-
} catch {
|
|
2246
|
-
}
|
|
2247
|
-
const s = Rs(e, t);
|
|
2248
|
-
return s || null;
|
|
2249
|
-
}
|
|
2250
|
-
function Do(e, t) {
|
|
2251
|
-
return !Buffer.isBuffer(e) || !Buffer.isBuffer(t) ? !0 : Buffer.compare(e, t) !== 0;
|
|
2252
|
-
}
|
|
2253
|
-
function Ao(e, t) {
|
|
2254
|
-
let s = e;
|
|
2255
|
-
for (const n of t)
|
|
2256
|
-
s = s.replace(n.from, n.to);
|
|
2257
|
-
return s;
|
|
2258
|
-
}
|
|
2259
|
-
const We = {
|
|
2260
|
-
from: /npx\s+tsx\s+~\/Developer\/gennady\/cli\/gennady\.ts\b/g,
|
|
2261
|
-
to: "npx gennady"
|
|
2262
|
-
}, Ke = {
|
|
2263
|
-
from: /npx tsx ~\/Developer\/gennady\/cli[ \t]+/g,
|
|
2264
|
-
to: "npx gennady "
|
|
2265
|
-
}, Xe = {
|
|
2266
|
-
from: /~\/Developer\/gennady\/cli\/gennady\.ts/g,
|
|
2267
|
-
to: "npx gennady"
|
|
2268
|
-
}, ks = {
|
|
2269
|
-
from: /~\/Developer\/gennady\/ai\/skills\//g,
|
|
2270
|
-
to: ".claude/skills/"
|
|
2271
|
-
}, Ze = {
|
|
2272
|
-
from: /~\/Developer\/gennady\/ai\/directives\//g,
|
|
2273
|
-
to: "ai/directives/"
|
|
2274
|
-
}, ze = {
|
|
2275
|
-
from: /\/Users\/k\.lebedev\/Developer\/gennady\/ai\//g,
|
|
2276
|
-
to: "ai/"
|
|
2277
|
-
}, Qe = {
|
|
2278
|
-
from: /\/Users\/k\.lebedev\/Developer\/gennady\/cli\/gennady\.ts/g,
|
|
2279
|
-
to: "npx gennady"
|
|
2280
|
-
}, Je = {
|
|
2281
|
-
from: /\$HOME\/Developer\/gennady\/cli\/gennady\.ts\b/g,
|
|
2282
|
-
to: "~/Developer/gennady/cli/gennady.ts"
|
|
2283
|
-
}, yo = [
|
|
2284
|
-
We,
|
|
2285
|
-
Ke,
|
|
2286
|
-
Xe,
|
|
2287
|
-
Ze,
|
|
2288
|
-
ze,
|
|
2289
|
-
Qe,
|
|
2290
|
-
Je
|
|
2291
|
-
], Ro = [
|
|
2292
|
-
We,
|
|
2293
|
-
Ke,
|
|
2294
|
-
Xe,
|
|
2295
|
-
ks,
|
|
2296
|
-
Ze,
|
|
2297
|
-
ze,
|
|
2298
|
-
Qe,
|
|
2299
|
-
Je
|
|
2300
|
-
], vs = "+", ws = "~", Ps = "-", Ls = "=", bs = "(unchanged)", Ms = "(would add)", xs = "(would update)", $s = "(would delete)", Bs = "(unchanged, skip)";
|
|
2301
|
-
function ko(e, t = {}) {
|
|
2302
|
-
const s = [];
|
|
2303
|
-
for (const a of e) {
|
|
2304
|
-
const { relativePath: c, status: l } = a;
|
|
2305
|
-
let f, d;
|
|
2306
|
-
l === "added" ? (f = vs, d = t.dryRun ? Ms : "") : l === "updated" ? (f = ws, d = t.dryRun ? xs : "") : l === "deleted" ? (f = Ps, d = t.dryRun ? $s : "") : (f = Ls, d = t.dryRun ? Bs : bs);
|
|
2307
|
-
const u = c.padEnd(64);
|
|
2308
|
-
d ? s.push(` ${f} ${u} ${d}`) : s.push(` ${f} ${c}`);
|
|
2309
|
-
}
|
|
2310
|
-
const n = e.filter((a) => a.status === "added").length, o = e.filter((a) => a.status === "updated").length, r = e.filter((a) => a.status === "unchanged").length, i = e.filter((a) => a.status === "deleted").length;
|
|
2311
|
-
if (t.dryRun)
|
|
2312
|
-
s.push("Dry-run: no files written.");
|
|
2313
|
-
else {
|
|
2314
|
-
let a = `Synced: ${n} added, ${o} updated, ${r} skipped (unchanged)`;
|
|
2315
|
-
i > 0 && (a += `, ${i} deleted`), s.push(a);
|
|
2316
|
-
}
|
|
2317
|
-
return s;
|
|
2318
|
-
}
|
|
2319
|
-
const Us = /[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d/#&.:=?%@~_]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PR-TZcf-ntqry=><~]))/g, Re = [
|
|
2320
|
-
/^\s*(✖|❌|⚠|🛑|× |✗ |FAIL |●|▶)/,
|
|
2321
|
-
/:\d+:\d+:\s+(error|warning)/i,
|
|
2322
|
-
/^\s+at\s+/,
|
|
2323
|
-
/ERR_[A-Z_]+/,
|
|
2324
|
-
/(Error|TypeError|SyntaxError|AssertionError|AggregateError):/,
|
|
2325
|
-
/(failed|passed|errors?|warnings?)\s*[:\d]/i,
|
|
2326
|
-
/exit (code|status)\s*[1-9]/i,
|
|
2327
|
-
/\b(tests?|specs?|suites?)\s+\d+\s+failed/i,
|
|
2328
|
-
/\d+\s+(failed|passing|pending)/i,
|
|
2329
|
-
/^\[(FAILED|ERROR|PASSED)\]/i,
|
|
2330
|
-
/^\s*\d+\)\s/,
|
|
2331
|
-
/(FAIL|PASS)\s{2,}/,
|
|
2332
|
-
/^TAP version/i,
|
|
2333
|
-
/^\s*not\s+ok\s+\d+/i
|
|
2334
|
-
];
|
|
2335
|
-
function vo(e) {
|
|
2336
|
-
const t = e.replace(Us, ""), s = t.split(`
|
|
2337
|
-
`).filter((n) => {
|
|
2338
|
-
const o = n.trim();
|
|
2339
|
-
return !o || o.length > 200 && !/:\d+/.test(o) && !Re.some((r) => r.test(o)) ? !1 : Re.some((r) => r.test(o));
|
|
2340
|
-
});
|
|
2341
|
-
return s.length === 0 ? t.slice(0, 2e3) + `
|
|
2342
|
-
... [no error lines]` : s.join(`
|
|
2343
|
-
`);
|
|
2344
|
-
}
|
|
2345
|
-
const Gs = ["typecheck", "test", "test:coverage", "lint", "format"];
|
|
2346
|
-
function Fs(e) {
|
|
2347
|
-
const t = e.lint;
|
|
2348
|
-
if (t === void 0) return !1;
|
|
2349
|
-
const s = /* @__PURE__ */ new Set(), n = [t];
|
|
2350
|
-
for (; n.length > 0; ) {
|
|
2351
|
-
const o = n.shift();
|
|
2352
|
-
if (o !== void 0) {
|
|
2353
|
-
if (/\bgennady\b/.test(o)) return !0;
|
|
2354
|
-
for (const r of o.matchAll(/npm run ([A-Za-z0-9:_-]+)/g)) {
|
|
2355
|
-
const i = r[1];
|
|
2356
|
-
i && !s.has(i) && e[i] !== void 0 && (s.add(i), n.push(e[i]));
|
|
2357
|
-
}
|
|
2358
|
-
}
|
|
2359
|
-
}
|
|
2360
|
-
return !1;
|
|
2361
|
-
}
|
|
2362
|
-
function wo(e) {
|
|
2363
|
-
const { packageJsonPresent: t, scripts: s, gennadyAvailable: n } = e, o = Gs.map((c) => ({
|
|
2364
|
-
name: c,
|
|
2365
|
-
present: s[c] !== void 0
|
|
2366
|
-
})), r = Fs(s), i = [];
|
|
2367
|
-
t || i.push("package.json"), i.push(...o.filter((c) => !c.present).map((c) => c.name)), s.lint !== void 0 && !r && i.push("lint→gennady"), n || i.push("gennady (not installed)");
|
|
2368
|
-
const a = t && o.every((c) => c.present) && r && n;
|
|
2369
|
-
return { packageJsonPresent: t, required: o, lintHasGennady: r, gennadyAvailable: n, ready: a, missing: i };
|
|
2370
|
-
}
|
|
2371
|
-
function Vs(e) {
|
|
2372
|
-
return e.includes("✅") ? "done" : e.includes("🚧") ? "wip" : "unknown";
|
|
2373
|
-
}
|
|
2374
|
-
function Po(e) {
|
|
2375
|
-
const t = e.split(`
|
|
2376
|
-
`);
|
|
2377
|
-
let s = !1;
|
|
2378
|
-
const n = [];
|
|
2379
|
-
for (const o of t) {
|
|
2380
|
-
const r = o.trim();
|
|
2381
|
-
if (/^#{1,6}\s/.test(r)) {
|
|
2382
|
-
s = /^#{1,6}\s+Scopes\s*$/.test(r);
|
|
2383
|
-
continue;
|
|
2384
|
-
}
|
|
2385
|
-
if (!s || !r.startsWith("|")) continue;
|
|
2386
|
-
const i = r.replace(/^\|/, "").replace(/\|$/, "").split("|").map((p) => p.trim());
|
|
2387
|
-
if (i.length < 3) continue;
|
|
2388
|
-
const [a, c, l, f] = i;
|
|
2389
|
-
if (a === void 0 || c === void 0 || l === void 0) continue;
|
|
2390
|
-
const d = a.match(/`([^`]+)`/);
|
|
2391
|
-
if (!d || !d[1]) continue;
|
|
2392
|
-
const u = a.match(/\]\(([^)]+)\)/);
|
|
2393
|
-
n.push({
|
|
2394
|
-
name: d[1],
|
|
2395
|
-
type: c,
|
|
2396
|
-
status: Vs(l),
|
|
2397
|
-
description: f?.trim() ?? "",
|
|
2398
|
-
specPath: u?.[1] ?? null
|
|
2399
|
-
});
|
|
2400
|
-
}
|
|
2401
|
-
return n;
|
|
2402
|
-
}
|
|
2403
|
-
function js(e) {
|
|
2404
|
-
const t = [], s = /([A-Za-z0-9_-]+)(?:\[[^\]]*\])?\s*-->(?:\s*\|[^|]*\|)?\s*([A-Za-z0-9_-]+)/;
|
|
2405
|
-
for (const n of e.split(`
|
|
2406
|
-
`)) {
|
|
2407
|
-
const o = n.trim();
|
|
2408
|
-
if (!o.includes("-->")) continue;
|
|
2409
|
-
const r = s.exec(o);
|
|
2410
|
-
r?.[1] && r[2] && t.push({ from: r[1], to: r[2] });
|
|
2411
|
-
}
|
|
2412
|
-
return t;
|
|
2413
|
-
}
|
|
2414
|
-
const Ys = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "coverage", ".next", "out"]), Hs = /\.(js|jsx|ts|tsx)$/, qs = [
|
|
2415
|
-
"tsconfig.json",
|
|
2416
|
-
"eslint.config.js",
|
|
2417
|
-
"eslint.config.mjs",
|
|
2418
|
-
"eslint.config.cjs",
|
|
2419
|
-
".eslintrc",
|
|
2420
|
-
".eslintrc.json",
|
|
2421
|
-
".eslintrc.js",
|
|
2422
|
-
".eslintrc.cjs",
|
|
2423
|
-
"prettier.config.js",
|
|
2424
|
-
"prettier.config.cjs",
|
|
2425
|
-
".prettierrc",
|
|
2426
|
-
".prettierrc.json",
|
|
2427
|
-
"vitest.config.ts",
|
|
2428
|
-
"vitest.config.js",
|
|
2429
|
-
"vite.config.ts",
|
|
2430
|
-
"vite.config.js",
|
|
2431
|
-
"jest.config.js",
|
|
2432
|
-
"jest.config.ts"
|
|
2433
|
-
];
|
|
2434
|
-
function et(e, t, s) {
|
|
2435
|
-
let n;
|
|
2436
|
-
try {
|
|
2437
|
-
n = $(e, { withFileTypes: !0 });
|
|
2438
|
-
} catch {
|
|
2439
|
-
return;
|
|
2440
|
-
}
|
|
2441
|
-
for (const o of n) {
|
|
2442
|
-
const r = o.name;
|
|
2443
|
-
if (o.isDirectory()) {
|
|
2444
|
-
if (Ys.has(r) || r.startsWith(".")) continue;
|
|
2445
|
-
et(h(e, r), t, s);
|
|
2446
|
-
} else if (Hs.test(r)) {
|
|
2447
|
-
s.count++;
|
|
2448
|
-
const i = h(e, r).slice(t.length + 1);
|
|
2449
|
-
s.dirs.add(i.includes("/") ? i.slice(0, i.indexOf("/")) : ".");
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
}
|
|
2453
|
-
function Lo(e) {
|
|
2454
|
-
const t = { count: 0, dirs: /* @__PURE__ */ new Set() };
|
|
2455
|
-
et(e, e, t);
|
|
2456
|
-
const s = qs.filter((n) => R(h(e, n)));
|
|
2457
|
-
return {
|
|
2458
|
-
codePresent: t.count > 0,
|
|
2459
|
-
codeFileCount: t.count,
|
|
2460
|
-
codeDirs: [...t.dirs].sort(),
|
|
2461
|
-
infraPresent: s.length > 0,
|
|
2462
|
-
configFiles: s
|
|
2463
|
-
};
|
|
2464
|
-
}
|
|
2465
|
-
function Ws(e) {
|
|
2466
|
-
try {
|
|
2467
|
-
return oe(h(e, "tasks")).isDirectory() ? "v1" : "v2";
|
|
2468
|
-
} catch {
|
|
2469
|
-
return "v2";
|
|
2470
|
-
}
|
|
2471
|
-
}
|
|
2472
|
-
function bo(e, t) {
|
|
2473
|
-
if (Ws(e) === "v2") return "v2";
|
|
2474
|
-
try {
|
|
2475
|
-
if (oe(h(e, "tasks", t)).isDirectory()) return "v1";
|
|
2476
|
-
} catch {
|
|
2477
|
-
}
|
|
2478
|
-
try {
|
|
2479
|
-
return oe(h(e, "specs", t, `${t}.3-tasks.md`)).isFile() ? "v2" : "v1";
|
|
2480
|
-
} catch {
|
|
2481
|
-
return "v1";
|
|
2482
|
-
}
|
|
2483
|
-
}
|
|
2484
|
-
function Mo(e) {
|
|
2485
|
-
const t = P(`git -C ${JSON.stringify(e)} diff --name-only HEAD 2>/dev/null`), s = P(
|
|
2486
|
-
`git -C ${JSON.stringify(e)} ls-files --others --exclude-standard 2>/dev/null`
|
|
2487
|
-
);
|
|
2488
|
-
return [...new Set(
|
|
2489
|
-
[...t.split(`
|
|
2490
|
-
`), ...s.split(`
|
|
2491
|
-
`)].map((o) => o.trim()).filter(Boolean)
|
|
2492
|
-
)].filter(
|
|
2493
|
-
(o) => /\.(ts|tsx|js)$/.test(o) && !/\.(test|spec)\.[jt]sx?$/.test(o) && !o.includes("node_modules/")
|
|
2494
|
-
);
|
|
2495
|
-
}
|
|
2496
|
-
function xo(e, t) {
|
|
2497
|
-
const s = P(
|
|
2498
|
-
`git -C ${JSON.stringify(e)} show HEAD:${JSON.stringify(t)} 2>/dev/null`,
|
|
2499
|
-
{ expectedExitCodes: [128] }
|
|
2500
|
-
);
|
|
2501
|
-
return s || null;
|
|
2502
|
-
}
|
|
2503
|
-
function M(e, t) {
|
|
2504
|
-
const s = new RegExp(`\\*\\*${t}:\\*\\*\\s*(.+)`);
|
|
2505
|
-
return e.match(s)?.[1]?.trim() ?? null;
|
|
2506
|
-
}
|
|
2507
|
-
function ie(e, t) {
|
|
2508
|
-
const s = e.split(`
|
|
2509
|
-
`), n = [];
|
|
2510
|
-
let o = !1;
|
|
2511
|
-
for (const r of s) {
|
|
2512
|
-
const i = /^\s*-?\s*\*\*([^:*]+):\*\*/.exec(r);
|
|
2513
|
-
if (i) {
|
|
2514
|
-
o = i[1]?.trim().toLowerCase() === t.toLowerCase();
|
|
2515
|
-
continue;
|
|
2516
|
-
}
|
|
2517
|
-
if (o && r.trim() !== "")
|
|
2518
|
-
if (/^\s*-\s+/.test(r)) n.push(r.trim().replace(/^-\s+/, "").trim());
|
|
2519
|
-
else break;
|
|
2520
|
-
}
|
|
2521
|
-
return n;
|
|
2522
|
-
}
|
|
2523
|
-
function ae(e) {
|
|
2524
|
-
const t = e.match(/\[([^\]]+)\]\(([^)]+)\)/);
|
|
2525
|
-
return t && t[1] && t[2] ? { name: t[1], anchor: t[2] } : { name: e.trim(), anchor: "" };
|
|
2526
|
-
}
|
|
2527
|
-
function me(e) {
|
|
2528
|
-
const t = e.match(/\*\*Task-ID:\*\*\s*`?([A-Za-z0-9][\w-]*)`?/)?.[1] ?? null, s = e.match(/\*\*Status:\*\*\s*(\[.\]\s*[A-Z_]+)/)?.[1] ?? null, n = M(e, "Dependencies"), o = !n || /^none$/i.test(n.trim()) ? [] : n.split(",").map((i) => i.trim()).filter(Boolean), r = ie(e, "Spec References").map((i) => {
|
|
2529
|
-
const a = i.indexOf(":"), c = a !== -1 && a < i.indexOf("["), l = c ? i.slice(0, a).trim() : "", { name: f, anchor: d } = ae(c ? i.slice(a + 1) : i);
|
|
2530
|
-
return { role: l, name: f, anchor: d };
|
|
2531
|
-
});
|
|
2532
|
-
return {
|
|
2533
|
-
taskId: t,
|
|
2534
|
-
status: s,
|
|
2535
|
-
purpose: M(e, "Purpose"),
|
|
2536
|
-
scope: M(e, "Scope"),
|
|
2537
|
-
module: M(e, "Module"),
|
|
2538
|
-
dependencies: o,
|
|
2539
|
-
specRefs: r
|
|
2540
|
-
};
|
|
2541
|
-
}
|
|
2542
|
-
function tt(e) {
|
|
2543
|
-
return e.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((t) => t.trim());
|
|
2544
|
-
}
|
|
2545
|
-
function st(e) {
|
|
2546
|
-
return /^\|?\s*:?-{2,}/.test(e.trim());
|
|
2547
|
-
}
|
|
2548
|
-
function Ks(e) {
|
|
2549
|
-
const t = [];
|
|
2550
|
-
for (const s of e.split(`
|
|
2551
|
-
`)) {
|
|
2552
|
-
if (!s.trimStart().startsWith("|") || st(s)) continue;
|
|
2553
|
-
const n = tt(s);
|
|
2554
|
-
if (n.length < 4 || n[0]?.toLowerCase() === "id") continue;
|
|
2555
|
-
const [o, r, i, a] = n;
|
|
2556
|
-
o && t.push({
|
|
2557
|
-
id: o,
|
|
2558
|
-
kind: r ?? "",
|
|
2559
|
-
deps: !i || i === "—" ? [] : i.split(",").map((c) => c.trim()).filter(Boolean),
|
|
2560
|
-
status: a ?? ""
|
|
2561
|
-
});
|
|
2562
|
-
}
|
|
2563
|
-
return t;
|
|
2564
|
-
}
|
|
2565
|
-
function $o(e) {
|
|
2566
|
-
return {
|
|
2567
|
-
objective: M(e, "Objective"),
|
|
2568
|
-
rules: ie(e, "Rules").map((t) => ae(t).anchor || ae(t).name),
|
|
2569
|
-
targetFiles: ie(e, "Target Files").map((t) => t.replace(/[`*]/g, "").trim()),
|
|
2570
|
-
inputs: M(e, "Inputs"),
|
|
2571
|
-
exit: M(e, "Exit")
|
|
2572
|
-
};
|
|
2573
|
-
}
|
|
2574
|
-
function Bo(e) {
|
|
2575
|
-
const t = [];
|
|
2576
|
-
for (const s of e.split(`
|
|
2577
|
-
`)) {
|
|
2578
|
-
if (!s.trimStart().startsWith("|") || st(s)) continue;
|
|
2579
|
-
const n = tt(s);
|
|
2580
|
-
if (n.length < 2 || n[0]?.toLowerCase() === "command") continue;
|
|
2581
|
-
const [o, r] = n;
|
|
2582
|
-
o && t.push({
|
|
2583
|
-
command: o.replace(/`/g, "").trim(),
|
|
2584
|
-
requiredBy: (r ?? "").split(",").map((i) => i.trim()).filter(Boolean)
|
|
2585
|
-
});
|
|
2586
|
-
}
|
|
2587
|
-
return t;
|
|
2588
|
-
}
|
|
2589
|
-
function Xs(e, t) {
|
|
2590
|
-
const s = t.trim(), n = /^P(\d+)(\b|_FIX\b)/i.exec(s);
|
|
2591
|
-
if (e === 3 && n) return `PHASE_P${n[1]}${/_FIX/i.test(s) ? "_FIX" : ""}`;
|
|
2592
|
-
if (e !== 2) return null;
|
|
2593
|
-
const o = s.toLowerCase();
|
|
2594
|
-
return /\bmeta\b/.test(o) ? "META" : /phases overview/.test(o) ? "PHASES_OVERVIEW" : /acceptance criteria|\bbdd\b/.test(o) ? "BDD" : /\bverification\b/.test(o) ? "VERIFICATION" : /test scenario coverage|test coverage/.test(o) ? "TEST_COVERAGE" : /execution log/.test(o) ? "EXECUTION_LOG" : /decision log/.test(o) ? "DECISION_LOG" : null;
|
|
2595
|
-
}
|
|
2596
|
-
function nt(e) {
|
|
2597
|
-
const t = [];
|
|
2598
|
-
return e.forEach((s, n) => {
|
|
2599
|
-
const o = /^(#{2,3})\s+(.+?)\s*$/.exec(s);
|
|
2600
|
-
o && o[1] && o[2] && t.push({ idx: n, level: o[1].length, name: Xs(o[1].length, o[2]) });
|
|
2601
|
-
}), t;
|
|
2602
|
-
}
|
|
2603
|
-
function W(e, t) {
|
|
2604
|
-
const s = e.split(`
|
|
2605
|
-
`), n = nt(s), o = n.find((i) => i.name === t);
|
|
2606
|
-
if (!o) return null;
|
|
2607
|
-
let r = s.length;
|
|
2608
|
-
for (const i of n)
|
|
2609
|
-
if (i.idx > o.idx && i.level <= o.level) {
|
|
2610
|
-
r = i.idx;
|
|
2611
|
-
break;
|
|
2612
|
-
}
|
|
2613
|
-
return s.slice(o.idx + 1, r).join(`
|
|
2614
|
-
`);
|
|
2615
|
-
}
|
|
2616
|
-
function Uo(e) {
|
|
2617
|
-
const t = e.split(`
|
|
2618
|
-
`), s = nt(t), n = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map(), r = [];
|
|
2619
|
-
for (let a = 0; a < s.length; a++) {
|
|
2620
|
-
const c = s[a];
|
|
2621
|
-
if (!c || c.name === null || e.includes(`<!--SECTION:${c.name}-->`)) continue;
|
|
2622
|
-
let l = t.length;
|
|
2623
|
-
for (let f = a + 1; f < s.length; f++) {
|
|
2624
|
-
const d = s[f];
|
|
2625
|
-
if (d && d.level <= c.level) {
|
|
2626
|
-
l = d.idx;
|
|
2627
|
-
break;
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
|
-
n.set(c.idx, c.name), o.set(l, c.name), r.push(c.name);
|
|
2631
|
-
}
|
|
2632
|
-
if (r.length === 0) return { text: e, injected: [] };
|
|
2633
|
-
const i = [];
|
|
2634
|
-
for (let a = 0; a <= t.length; a++) {
|
|
2635
|
-
const c = o.get(a);
|
|
2636
|
-
c && i.push(`<!--/SECTION:${c}-->`);
|
|
2637
|
-
const l = n.get(a);
|
|
2638
|
-
l && i.push(`<!--SECTION:${l}-->`), a < t.length && i.push(t[a]);
|
|
2639
|
-
}
|
|
2640
|
-
return { text: i.join(`
|
|
2641
|
-
`), injected: r };
|
|
2642
|
-
}
|
|
2643
|
-
function Go(e, t) {
|
|
2644
|
-
const s = e.includes("<!--SECTION:META-->") || W(e, "META") !== null, n = e.includes("<!--SECTION:EXECUTION_LOG-->");
|
|
2645
|
-
if (!s || n) return { text: e, scaffolded: !1 };
|
|
2646
|
-
const o = [
|
|
2647
|
-
"",
|
|
2648
|
-
"## Execution Log",
|
|
2649
|
-
"<!--SECTION:EXECUTION_LOG-->",
|
|
2650
|
-
`- ${t} migrated from v1 — no rounds/phases recorded in v1 format`,
|
|
2651
|
-
"<!--/SECTION:EXECUTION_LOG-->"
|
|
2652
|
-
].join(`
|
|
2653
|
-
`);
|
|
2654
|
-
return { text: `${e.endsWith(`
|
|
2655
|
-
`) ? e.slice(0, -1) : e}
|
|
2656
|
-
${o}
|
|
2657
|
-
`, scaffolded: !0 };
|
|
2658
|
-
}
|
|
2659
|
-
const ke = /<[A-Za-z…][^>\s]*>/, Zs = /`[^`]*`/g;
|
|
2660
|
-
function Fo(e) {
|
|
2661
|
-
return e.includes("<!--SECTION:META-->") && e.includes("<!--SECTION:EXECUTION_LOG-->");
|
|
2662
|
-
}
|
|
2663
|
-
function Vo(e) {
|
|
2664
|
-
return W(e, "META") !== null && W(e, "EXECUTION_LOG") !== null;
|
|
2665
|
-
}
|
|
2666
|
-
function ot(e) {
|
|
2667
|
-
const t = /* @__PURE__ */ new Map(), s = /* @__PURE__ */ new Map();
|
|
2668
|
-
for (const o of e.split(`
|
|
2669
|
-
`)) {
|
|
2670
|
-
const r = o.trim(), i = /^<!--SECTION:([A-Z][A-Z0-9_]*)-->$/.exec(r), a = /^<!--\/SECTION:([A-Z][A-Z0-9_]*)-->$/.exec(r);
|
|
2671
|
-
i?.[1] && t.set(i[1], (t.get(i[1]) ?? 0) + 1), a?.[1] && s.set(a[1], (s.get(a[1]) ?? 0) + 1);
|
|
2672
|
-
}
|
|
2673
|
-
return [.../* @__PURE__ */ new Set([...t.keys(), ...s.keys()])].filter((o) => (t.get(o) ?? 0) !== (s.get(o) ?? 0));
|
|
2674
|
-
}
|
|
2675
|
-
function rt(e) {
|
|
2676
|
-
const t = [], s = [];
|
|
2677
|
-
for (const n of e.split(`
|
|
2678
|
-
`)) {
|
|
2679
|
-
const o = n.trim(), r = /^<!--SECTION:([A-Z][A-Z0-9_]*)-->$/.exec(o), i = /^<!--\/SECTION:([A-Z][A-Z0-9_]*)-->$/.exec(o);
|
|
2680
|
-
if (r?.[1]) {
|
|
2681
|
-
const a = s[s.length - 1];
|
|
2682
|
-
a && t.push(`${r[1]} opens while ${a} is still open — sections must be flat, not nested`), s.push(r[1]);
|
|
2683
|
-
} else if (i?.[1]) {
|
|
2684
|
-
const a = s[s.length - 1];
|
|
2685
|
-
if (a === i[1]) s.pop();
|
|
2686
|
-
else if (s.includes(i[1])) {
|
|
2687
|
-
for (t.push(
|
|
2688
|
-
`/${i[1]} closes while ${a} is the innermost open section — interleaved sections`
|
|
2689
|
-
); s.length && s[s.length - 1] !== i[1]; ) s.pop();
|
|
2690
|
-
s.pop();
|
|
2691
|
-
}
|
|
2692
|
-
}
|
|
2693
|
-
}
|
|
2694
|
-
return t;
|
|
2695
|
-
}
|
|
2696
|
-
function zs(e) {
|
|
2697
|
-
const t = e.split(`
|
|
2698
|
-
`);
|
|
2699
|
-
let s = -1, n = -1;
|
|
2700
|
-
return t.forEach((o, r) => {
|
|
2701
|
-
/🛑|BLOCKED/.test(o) && (s = r), /✅|RESOLVED/.test(o) && (n = r);
|
|
2702
|
-
}), s !== -1 && s > n;
|
|
2703
|
-
}
|
|
2704
|
-
function jo(e, t) {
|
|
2705
|
-
const s = [], n = (l, f) => void s.push({ severity: "error", code: l, file: e, message: f }), o = (l, f) => void s.push({ severity: "warn", code: l, file: e, message: f });
|
|
2706
|
-
for (const l of ot(t))
|
|
2707
|
-
n("SDD_ANCHOR_UNBALANCED", `Section ${l}: open/close markers do not balance.`);
|
|
2708
|
-
for (const l of rt(t))
|
|
2709
|
-
n("SDD_SECTION_OVERLAP", `${l}. sdd-extract pulls one flat section — fix the marker order.`);
|
|
2710
|
-
const r = g(t, "META"), i = g(t, "EXECUTION_LOG");
|
|
2711
|
-
r.status !== "ok" && n("SDD_MISSING_META", "No usable META section."), i.status !== "ok" && n("SDD_MISSING_EXECUTION_LOG", "No usable EXECUTION_LOG section.");
|
|
2712
|
-
let a = !1;
|
|
2713
|
-
if (r.status === "ok") {
|
|
2714
|
-
const l = me(r.content);
|
|
2715
|
-
l.taskId || o("SDD_MISSING_TASK_ID", "Meta has no parseable Task-ID."), l.status || o("SDD_STATUS_UNPARSEABLE", "Meta Status is missing or not in `[x] STATE` form."), a = l.status?.includes("[x]") ?? !1;
|
|
2716
|
-
}
|
|
2717
|
-
if (i.status === "ok") {
|
|
2718
|
-
const l = i.content.split(`
|
|
2719
|
-
`);
|
|
2720
|
-
for (const f of l) {
|
|
2721
|
-
const d = f.replace(Zs, "");
|
|
2722
|
-
/\[x\]/.test(d) && ke.test(f) && n(
|
|
2723
|
-
"SDD_FABRICATED_DONE",
|
|
2724
|
-
`Checked [x] line with an unreplaced placeholder: "${f.trim()}"`
|
|
2725
|
-
);
|
|
2726
|
-
}
|
|
2727
|
-
zs(i.content) && (a ? n(
|
|
2728
|
-
"SDD_DONE_WITH_ACTIVE_BLOCKER",
|
|
2729
|
-
"Status is DONE but the Execution Log ends with an unresolved BLOCKED."
|
|
2730
|
-
) : o(
|
|
2731
|
-
"SDD_BLOCKER_OPEN",
|
|
2732
|
-
"Execution Log ends with an unresolved 🛑 BLOCKED — no later ✅ RESOLVED. Resolve or explicitly reopen before the orchestrator relies on this ticket."
|
|
2733
|
-
));
|
|
2734
|
-
}
|
|
2735
|
-
a && ke.test(t) && o(
|
|
2736
|
-
"SDD_DONE_WITH_PLACEHOLDERS",
|
|
2737
|
-
"Status is DONE but unreplaced <…> scaffold placeholders remain."
|
|
2738
|
-
);
|
|
2739
|
-
const c = g(t, "PHASES_OVERVIEW");
|
|
2740
|
-
if (c.status === "ok") {
|
|
2741
|
-
const l = Ks(c.content), f = new Set(l.map((u) => u.id));
|
|
2742
|
-
for (const u of l)
|
|
2743
|
-
for (const p of u.deps)
|
|
2744
|
-
f.has(p) || n("SDD_PHASE_DEP_UNRESOLVED", `Phase ${u.id} depends on unknown phase ${p}.`);
|
|
2745
|
-
J(l.flatMap((u) => u.deps.map((p) => ({ from: u.id, to: p })))) && n("SDD_PHASE_DAG_CYCLE", "Phase dependency graph has a cycle.");
|
|
2746
|
-
const d = /* @__PURE__ */ new Set();
|
|
2747
|
-
for (const u of t.matchAll(/<!--SECTION:PHASE_(P[0-9]+)-->/g))
|
|
2748
|
-
d.add(u[1]);
|
|
2749
|
-
for (const u of l)
|
|
2750
|
-
d.has(u.id) || n(
|
|
2751
|
-
"SDD_PHASE_SECTION_MISSING",
|
|
2752
|
-
`Phase ${u.id} in the overview has no PHASE_${u.id} section.`
|
|
2753
|
-
);
|
|
2754
|
-
for (const u of d)
|
|
2755
|
-
f.has(u) || n("SDD_PHASE_SECTION_ORPHAN", `PHASE_${u} section has no row in the Phases Overview.`);
|
|
2756
|
-
if (a)
|
|
2757
|
-
for (const u of l)
|
|
2758
|
-
u.status.includes("[x]") || n("SDD_DONE_PHASE_UNCHECKED", `Status is DONE but phase ${u.id} is not checked ([x]).`);
|
|
2759
|
-
}
|
|
2760
|
-
return s;
|
|
2761
|
-
}
|
|
2762
|
-
function J(e) {
|
|
2763
|
-
const t = /* @__PURE__ */ new Map();
|
|
2764
|
-
for (const o of e) {
|
|
2765
|
-
const r = t.get(o.from);
|
|
2766
|
-
r ? r.push(o.to) : t.set(o.from, [o.to]);
|
|
2767
|
-
}
|
|
2768
|
-
const s = /* @__PURE__ */ new Map(), n = (o) => {
|
|
2769
|
-
s.set(o, 1);
|
|
2770
|
-
for (const r of t.get(o) ?? []) {
|
|
2771
|
-
const i = s.get(r);
|
|
2772
|
-
if (i === 1 || i === void 0 && n(r)) return !0;
|
|
2773
|
-
}
|
|
2774
|
-
return s.set(o, 2), !1;
|
|
2775
|
-
};
|
|
2776
|
-
for (const o of t.keys())
|
|
2777
|
-
if (s.get(o) === void 0 && n(o)) return !0;
|
|
2778
|
-
return !1;
|
|
2779
|
-
}
|
|
2780
|
-
function Yo(e) {
|
|
2781
|
-
const { scopes: t, edges: s, specDirs: n } = e, o = "specs/README.md", r = [], i = (c, l) => void r.push({ severity: "error", code: c, file: o, message: l }), a = new Set(t.map((c) => c.name));
|
|
2782
|
-
J(s) && i("SDD_PORTAL_GRAPH_CYCLE", "Scope Graph has a dependency cycle.");
|
|
2783
|
-
for (const c of s)
|
|
2784
|
-
a.has(c.from) || i(
|
|
2785
|
-
"SDD_PORTAL_DANGLING_DEP",
|
|
2786
|
-
`Graph edge from a scope not in the table: ${c.from} --> ${c.to}`
|
|
2787
|
-
), a.has(c.to) || i(
|
|
2788
|
-
"SDD_PORTAL_DANGLING_DEP",
|
|
2789
|
-
`Graph edge to a scope not in the table: ${c.from} --> ${c.to}`
|
|
2790
|
-
);
|
|
2791
|
-
for (const c of n)
|
|
2792
|
-
a.has(c) || i("SDD_PORTAL_ORPHAN_SPEC", `Spec dir has no row in the portal Scopes table: specs/${c}/`);
|
|
2793
|
-
for (const c of t)
|
|
2794
|
-
c.status === "done" && !n.includes(c.name) && i("SDD_PORTAL_SPEC_MISSING", `Scope marked done has no spec on disk: ${c.name}`);
|
|
2795
|
-
return r;
|
|
2796
|
-
}
|
|
2797
|
-
function Ho(e, t, s) {
|
|
2798
|
-
const n = g(t, "META"), o = n.status === "ok" ? me(n.content) : null;
|
|
2799
|
-
return {
|
|
2800
|
-
file: e,
|
|
2801
|
-
taskId: o?.taskId ?? null,
|
|
2802
|
-
status: o?.status ?? null,
|
|
2803
|
-
dependencies: o?.dependencies ?? [],
|
|
2804
|
-
flowVersion: s
|
|
2805
|
-
};
|
|
2806
|
-
}
|
|
2807
|
-
function qo(e, t, s) {
|
|
2808
|
-
const n = W(t, "META"), o = n !== null ? me(n) : null;
|
|
2809
|
-
return {
|
|
2810
|
-
file: e,
|
|
2811
|
-
taskId: o?.taskId ?? null,
|
|
2812
|
-
status: o?.status ?? null,
|
|
2813
|
-
dependencies: o?.dependencies ?? [],
|
|
2814
|
-
flowVersion: s
|
|
2815
|
-
};
|
|
2816
|
-
}
|
|
2817
|
-
function Wo(e) {
|
|
2818
|
-
return [
|
|
2819
|
-
{
|
|
2820
|
-
severity: "warn",
|
|
2821
|
-
code: "SDD_LEGACY_TICKET_UNANCHORED",
|
|
2822
|
-
file: e,
|
|
2823
|
-
message: "Legacy ticket format (plain headers, no <!--SECTION--> markers) — only Task-ID/Status are checked here. Run `gennady sdd-migrate anchors` to enable the full structural check."
|
|
2824
|
-
}
|
|
2825
|
-
];
|
|
2826
|
-
}
|
|
2827
|
-
function Qs(e) {
|
|
2828
|
-
return e != null && /\bDONE\b/i.test(e);
|
|
2829
|
-
}
|
|
2830
|
-
function Ko(e) {
|
|
2831
|
-
const t = new Map(
|
|
2832
|
-
e.filter((n) => n.taskId).map((n) => [n.taskId, n.status])
|
|
2833
|
-
), s = (n) => n.filter((o) => !/^(none|n\/a|[—-])\b/i.test(o.trim()));
|
|
2834
|
-
return e.filter(
|
|
2835
|
-
(n) => n.taskId != null && /\bTODO\b/i.test(n.status ?? "") && s(n.dependencies).every((o) => Qs(t.get(o)))
|
|
2836
|
-
);
|
|
2837
|
-
}
|
|
2838
|
-
function Xo(e) {
|
|
2839
|
-
const t = [], s = /* @__PURE__ */ new Map();
|
|
2840
|
-
for (const o of e) {
|
|
2841
|
-
if (!o.taskId) continue;
|
|
2842
|
-
const r = s.get(o.taskId);
|
|
2843
|
-
r ? r.push(o.file) : s.set(o.taskId, [o.file]);
|
|
2844
|
-
}
|
|
2845
|
-
for (const [o, r] of s)
|
|
2846
|
-
r.length > 1 && t.push({
|
|
2847
|
-
severity: "error",
|
|
2848
|
-
code: "SDD_TASK_ID_COLLISION",
|
|
2849
|
-
file: r[0],
|
|
2850
|
-
message: `Task-ID ${o} is used by ${r.length} tickets: ${r.join(", ")}.`
|
|
2851
|
-
});
|
|
2852
|
-
for (const o of e)
|
|
2853
|
-
for (const r of o.dependencies)
|
|
2854
|
-
s.has(r) || t.push({
|
|
2855
|
-
severity: "error",
|
|
2856
|
-
code: "SDD_DEP_UNRESOLVED",
|
|
2857
|
-
file: o.file,
|
|
2858
|
-
message: `Dependency ${r} resolves to no ticket in the tree.`
|
|
2859
|
-
});
|
|
2860
|
-
const n = e.filter((o) => o.taskId).flatMap((o) => o.dependencies.map((r) => ({ from: o.taskId, to: r })));
|
|
2861
|
-
return J(n) && t.push({
|
|
2862
|
-
severity: "error",
|
|
2863
|
-
code: "SDD_DAG_CYCLE",
|
|
2864
|
-
file: "(task graph)",
|
|
2865
|
-
message: "Task dependency graph has a cycle."
|
|
2866
|
-
}), t;
|
|
2867
|
-
}
|
|
2868
|
-
function Zo(e, t) {
|
|
2869
|
-
const s = [], n = (a) => a.replace(/`/g, "").replace(/\s+/g, " ").trim(), o = (a) => a === "v2" ? "error" : "warn", r = new Set(e.map((a) => a.taskId).filter((a) => a !== null)), i = /* @__PURE__ */ new Map();
|
|
2870
|
-
for (const a of t) {
|
|
2871
|
-
const c = i.get(a.taskId);
|
|
2872
|
-
c ? c.push(a) : i.set(a.taskId, [a]);
|
|
2873
|
-
}
|
|
2874
|
-
for (const a of e) {
|
|
2875
|
-
if (!a.taskId) continue;
|
|
2876
|
-
const c = i.get(a.taskId);
|
|
2877
|
-
if (!c) {
|
|
2878
|
-
s.push({
|
|
2879
|
-
severity: o(a.flowVersion),
|
|
2880
|
-
code: "SDD_TRACKER_MISSING_ROW",
|
|
2881
|
-
file: a.file,
|
|
2882
|
-
message: `Ticket ${a.taskId} has no row in any Tracker Index.`
|
|
2883
|
-
});
|
|
2884
|
-
continue;
|
|
2885
|
-
}
|
|
2886
|
-
if (a.status)
|
|
2887
|
-
for (const l of c)
|
|
2888
|
-
n(l.status) !== n(a.status) && s.push({
|
|
2889
|
-
severity: "error",
|
|
2890
|
-
code: "SDD_TRACKER_STATUS_DRIFT",
|
|
2891
|
-
file: l.file,
|
|
2892
|
-
message: `Tracker row for ${a.taskId} says "${n(l.status)}" but the ticket Status is "${n(a.status)}".`
|
|
2893
|
-
});
|
|
2894
|
-
}
|
|
2895
|
-
for (const a of t)
|
|
2896
|
-
r.has(a.taskId) || s.push({
|
|
2897
|
-
severity: o(a.flowVersion),
|
|
2898
|
-
code: "SDD_TRACKER_ORPHAN_ROW",
|
|
2899
|
-
file: a.file,
|
|
2900
|
-
message: `Tracker row ${a.taskId} points to no ticket on disk.`
|
|
2901
|
-
});
|
|
2902
|
-
return s;
|
|
2903
|
-
}
|
|
2904
|
-
const ce = Object.fromEntries(
|
|
2905
|
-
je.map((e) => [e, Ye(e)])
|
|
2906
|
-
), it = Ye("module"), le = Array.from(
|
|
2907
|
-
new Set([...je, "module"].flatMap((e) => Ns(e)))
|
|
2908
|
-
), ve = 120, we = 120, Pe = 6, Js = /[.!?]\s+[A-ZА-ЯЁ]/;
|
|
2909
|
-
function en(e) {
|
|
2910
|
-
return (e.match(/^```/gm) ?? []).length >= 2;
|
|
2911
|
-
}
|
|
2912
|
-
const tn = [
|
|
2913
|
-
{ re: /аппрув[а-яё]*/giu, say: "подтверждение" },
|
|
2914
|
-
{ re: /реифицир[а-яё]*/giu, say: "вынести в данные" },
|
|
2915
|
-
{ re: /чек(ать|нуть|аем|ается)[а-яё]*/giu, say: "проверить" },
|
|
2916
|
-
{ re: /(по)?фиксить|фиксим|фиксят/giu, say: "починить" },
|
|
2917
|
-
{ re: /дроп(ать|нуть|аем|нем)[а-яё]*/giu, say: "удалить" },
|
|
2918
|
-
{ re: /юза(ть|ем|ю|ется)[а-яё]*/giu, say: "использовать" },
|
|
2919
|
-
{ re: /(за)?имплементи[а-яё]*/giu, say: "реализовать" },
|
|
2920
|
-
{ re: /фанаут[а-яё]*/giu, say: "делает fan-out / рассылает" },
|
|
2921
|
-
{ re: /зафейли?[а-яё]*/giu, say: "упасть / завершиться ошибкой" },
|
|
2922
|
-
{ re: /засабмити?[а-яё]*/giu, say: "отправить" },
|
|
2923
|
-
{ re: /линку(ет|ют|ем|я)[а-яё]*/giu, say: "связывает / сопоставляет" },
|
|
2924
|
-
{ re: /м[её]рж[а-яё]*/giu, say: "объединяет" },
|
|
2925
|
-
{ re: /(?<![а-яё])тул[аеуы](?![а-яё])/giu, say: "инструмент" },
|
|
2926
|
-
{ re: /под\s+капотом/giu, say: "внутри / как устроено" },
|
|
2927
|
-
{ re: /подня[а-яё]*\s+сервис[а-яё]*/giu, say: "запустить сервис" },
|
|
2928
|
-
{ re: /на\s+проводе/giu, say: "в ответе / запросе сервера" },
|
|
2929
|
-
{ re: /разв(о|е)д[а-яё]*\s+провод[а-яё]*/giu, say: "явно связать зависимости" }
|
|
2930
|
-
], sn = [
|
|
2931
|
-
{
|
|
2932
|
-
re: /осуществля[а-яё]*/giu,
|
|
2933
|
-
say: "подставить подлежащее и обычный глагол, напр. «модуль делает X»"
|
|
2934
|
-
},
|
|
2935
|
-
{ re: /посредством/giu, say: "через" },
|
|
2936
|
-
{
|
|
2937
|
-
re: /(?<![а-яё])производ(ится|ятся|илось)/giu,
|
|
2938
|
-
say: "подставить обычный глагол, напр. «модуль строит X»"
|
|
2939
|
-
},
|
|
2940
|
-
{ re: /имеет\s+место\s+быть/giu, say: "есть / происходит" }
|
|
2941
|
-
];
|
|
2942
|
-
function zo(e, t) {
|
|
2943
|
-
const s = [];
|
|
2944
|
-
for (const { re: n, say: o } of tn) {
|
|
2945
|
-
const r = [...t.matchAll(n)].map((i) => i[0]);
|
|
2946
|
-
r.length !== 0 && s.push({
|
|
2947
|
-
severity: "warn",
|
|
2948
|
-
code: "SDD_LANGUAGE_CALQUE",
|
|
2949
|
-
file: e,
|
|
2950
|
-
message: `«${r[0]}»${r.length > 1 ? ` (×${r.length})` : ""} — англицизм-калька; по-русски: ${o} (AX_OPERATOR_DIALOGUE_STYLE).`
|
|
2951
|
-
});
|
|
2952
|
-
}
|
|
2953
|
-
for (const { re: n, say: o } of sn) {
|
|
2954
|
-
const r = [...t.matchAll(n)].map((i) => i[0]);
|
|
2955
|
-
r.length !== 0 && s.push({
|
|
2956
|
-
severity: "warn",
|
|
2957
|
-
code: "SDD_LANGUAGE_CALQUE",
|
|
2958
|
-
file: e,
|
|
2959
|
-
message: `«${r[0]}»${r.length > 1 ? ` (×${r.length})` : ""} — канцелярит; по-русски: ${o} (AX_OPERATOR_DIALOGUE_STYLE).`
|
|
2960
|
-
});
|
|
2961
|
-
}
|
|
2962
|
-
return s;
|
|
2963
|
-
}
|
|
2964
|
-
const Le = 20, be = 750;
|
|
2965
|
-
function nn(e) {
|
|
2966
|
-
const t = e.split(`
|
|
2967
|
-
`).map((s) => s.trim()).filter((s) => s.startsWith("|") && !/^\|[\s:|-]+$/.test(s));
|
|
2968
|
-
return Math.max(0, t.length - 1);
|
|
2969
|
-
}
|
|
2970
|
-
function Qo(e, t, s = "v1") {
|
|
2971
|
-
const n = ot(t).map((a) => ({
|
|
2972
|
-
severity: "error",
|
|
2973
|
-
code: "SDD_ANCHOR_UNBALANCED",
|
|
2974
|
-
file: e,
|
|
2975
|
-
message: `Section ${a}: open/close markers do not balance.`
|
|
2976
|
-
}));
|
|
2977
|
-
for (const a of rt(t))
|
|
2978
|
-
n.push({
|
|
2979
|
-
severity: "error",
|
|
2980
|
-
code: "SDD_SECTION_OVERLAP",
|
|
2981
|
-
file: e,
|
|
2982
|
-
message: `${a}. sdd-extract pulls one flat section — fix the marker order.`
|
|
2983
|
-
});
|
|
2984
|
-
const o = /<!--SECTION:MODULE_VISION-->/.test(t), r = /<!--SECTION:SCOPE_TYPE-->/.test(t) && !o;
|
|
2985
|
-
if (r)
|
|
2986
|
-
for (const a of ["ENTITY_INVENTORY", "MODULE_CONTRACTS"])
|
|
2987
|
-
new RegExp(`<!--SECTION:${a}-->`).test(t) && n.push({
|
|
2988
|
-
severity: "warn",
|
|
2989
|
-
code: "SDD_SCOPE_BLOATED",
|
|
2990
|
-
file: e,
|
|
2991
|
-
message: `Scope spec carries module-level section ${a} — move that detail into a module spec; a scope stays a thin index of modules (AX_SCOPE_STAYS_THIN).`
|
|
2992
|
-
});
|
|
2993
|
-
if (o) {
|
|
2994
|
-
const a = g(t, "ENTITY_INVENTORY"), c = a.status === "ok" ? nn(a.content) : 0;
|
|
2995
|
-
c > Le ? n.push({
|
|
2996
|
-
severity: "warn",
|
|
2997
|
-
code: "SDD_MODULE_OVERSIZED",
|
|
2998
|
-
file: e,
|
|
2999
|
-
message: `Entity Inventory has ${c} entities (> ${Le}) — decompose into sub-modules (AX_HIERARCHICAL_SPECS).`
|
|
3000
|
-
}) : t.split(`
|
|
3001
|
-
`).length > be && n.push({
|
|
3002
|
-
severity: "warn",
|
|
3003
|
-
code: "SDD_MODULE_SPEC_VERBOSE",
|
|
3004
|
-
file: e,
|
|
3005
|
-
message: `Module spec is ${t.split(`
|
|
3006
|
-
`).length} lines (> ${be}) with a cohesive inventory — compress the spec (AX_HIERARCHICAL_SPECS).`
|
|
3007
|
-
});
|
|
3008
|
-
}
|
|
3009
|
-
const i = g(t, "SCOPE_TYPE");
|
|
3010
|
-
if (i.status === "ok" && !o) {
|
|
3011
|
-
const a = Object.keys(ce).find(
|
|
3012
|
-
(c) => new RegExp(`\\b${c}\\b`).test(i.content)
|
|
3013
|
-
);
|
|
3014
|
-
if (a) {
|
|
3015
|
-
const c = new Set(
|
|
3016
|
-
[...t.matchAll(/<!--SECTION:([A-Z_]+)-->/g)].map((l) => l[1])
|
|
3017
|
-
);
|
|
3018
|
-
for (const l of ce[a])
|
|
3019
|
-
c.has(l) || n.push({
|
|
3020
|
-
severity: "error",
|
|
3021
|
-
code: "SDD_SPEC_SECTION_MISSING",
|
|
3022
|
-
file: e,
|
|
3023
|
-
message: `${a} scope spec is missing required section ${l}.`
|
|
3024
|
-
});
|
|
3025
|
-
}
|
|
3026
|
-
}
|
|
3027
|
-
if (s === "v2") {
|
|
3028
|
-
const a = new Set(
|
|
3029
|
-
[...t.matchAll(/<!--SECTION:([A-Z_]+)-->/g)].map((c) => c[1])
|
|
3030
|
-
);
|
|
3031
|
-
if (o)
|
|
3032
|
-
for (const c of it)
|
|
3033
|
-
a.has(c) || n.push({
|
|
3034
|
-
severity: "error",
|
|
3035
|
-
code: "SDD_SPEC_SECTION_MISSING",
|
|
3036
|
-
file: e,
|
|
3037
|
-
message: `module spec is missing required section ${c}.`
|
|
3038
|
-
});
|
|
3039
|
-
if (r || o) {
|
|
3040
|
-
const c = g(t, "OVERVIEW");
|
|
3041
|
-
c.status !== "ok" ? n.push({
|
|
3042
|
-
severity: "error",
|
|
3043
|
-
code: "SDD_NO_DIAGRAM_BLOCK",
|
|
3044
|
-
file: e,
|
|
3045
|
-
message: "Spec has no Overview section — every spec opens with an Overview carrying at least one diagram (AX_SPEC_MANDATORY_DIAGRAM)."
|
|
3046
|
-
}) : en(c.content) || n.push({
|
|
3047
|
-
severity: "error",
|
|
3048
|
-
code: "SDD_DIAGRAM_BLOCK_EMPTY",
|
|
3049
|
-
file: e,
|
|
3050
|
-
message: "Overview section has no diagram — add at least one fenced mermaid or ASCII diagram (AX_SPEC_MANDATORY_DIAGRAM)."
|
|
3051
|
-
});
|
|
3052
|
-
}
|
|
3053
|
-
if (o || r)
|
|
3054
|
-
for (const c of le) {
|
|
3055
|
-
const l = g(t, c);
|
|
3056
|
-
l.status === "ok" && !/<details[\s>]/i.test(l.content) && n.push({
|
|
3057
|
-
severity: "error",
|
|
3058
|
-
code: "SDD_SECTION_NOT_FOLDED",
|
|
3059
|
-
file: e,
|
|
3060
|
-
message: `Section ${c} does not fold its detail under \`<details>\` — collapse the contract body so the summary stays readable (AX_SPEC_PROGRESSIVE_DISCLOSURE).`
|
|
3061
|
-
});
|
|
3062
|
-
}
|
|
3063
|
-
if (o || r)
|
|
3064
|
-
for (const c of a) {
|
|
3065
|
-
if (le.includes(c)) continue;
|
|
3066
|
-
const l = g(t, c);
|
|
3067
|
-
if (l.status !== "ok") continue;
|
|
3068
|
-
const f = l.content.split(`
|
|
3069
|
-
`).length;
|
|
3070
|
-
f > ve && n.push({
|
|
3071
|
-
severity: "error",
|
|
3072
|
-
code: "SDD_SECTION_TOO_LONG",
|
|
3073
|
-
file: e,
|
|
3074
|
-
message: `Section ${c} is ${f} lines (> ${ve}) — decompose (split into a module/sub-scope) or fold the reference detail into a foldable section (AX_SPEC_PROGRESSIVE_DISCLOSURE).`
|
|
3075
|
-
});
|
|
3076
|
-
}
|
|
3077
|
-
n.push(...on(e, t));
|
|
3078
|
-
}
|
|
3079
|
-
return n;
|
|
3080
|
-
}
|
|
3081
|
-
function Me(e) {
|
|
3082
|
-
return /^\|[\s:|-]+\|$/.test(e);
|
|
3083
|
-
}
|
|
3084
|
-
function xe(e) {
|
|
3085
|
-
return e.slice(1, -1).split("|").map((t) => t.trim()).filter((t) => t.length > 0);
|
|
3086
|
-
}
|
|
3087
|
-
function on(e, t) {
|
|
3088
|
-
const s = [], n = t.split(`
|
|
3089
|
-
`);
|
|
3090
|
-
let o = !1;
|
|
3091
|
-
for (let r = 0; r < n.length; r++) {
|
|
3092
|
-
const i = n[r].trim();
|
|
3093
|
-
if (i.startsWith("```")) {
|
|
3094
|
-
o = !o;
|
|
3095
|
-
continue;
|
|
3096
|
-
}
|
|
3097
|
-
if (o || !i.startsWith("|") || !i.endsWith("|") || Me(i)) continue;
|
|
3098
|
-
const a = (n[r + 1] ?? "").trim();
|
|
3099
|
-
if (Me(a)) {
|
|
3100
|
-
const c = xe(i).length;
|
|
3101
|
-
c > Pe && s.push({
|
|
3102
|
-
severity: "error",
|
|
3103
|
-
code: "SDD_TABLE_TOO_MANY_COLUMNS",
|
|
3104
|
-
file: e,
|
|
3105
|
-
message: `Table header has ${c} columns (> ${Pe}) — a table is an index; split it or move detail into a subsection (AX_SPEC_TABLE_IS_INDEX).`
|
|
3106
|
-
});
|
|
3107
|
-
}
|
|
3108
|
-
for (const c of xe(i)) {
|
|
3109
|
-
if (/<br/i.test(c)) {
|
|
3110
|
-
s.push({
|
|
3111
|
-
severity: "error",
|
|
3112
|
-
code: "SDD_TABLE_CELL_HAS_BR",
|
|
3113
|
-
file: e,
|
|
3114
|
-
message: `Table cell has a \`<br>\` line break — move the expanded content into a subsection below the table, not a multi-line cell (AX_SPEC_TABLE_IS_INDEX): "${c.slice(0, 60)}"`
|
|
3115
|
-
});
|
|
3116
|
-
continue;
|
|
3117
|
-
}
|
|
3118
|
-
c.length > we ? s.push({
|
|
3119
|
-
severity: "error",
|
|
3120
|
-
code: "SDD_TABLE_CELL_TOO_LONG",
|
|
3121
|
-
file: e,
|
|
3122
|
-
message: `Table cell is ${c.length} chars (> ${we}) — a cell is one short line; move the detail into a subsection below the table (AX_SPEC_TABLE_IS_INDEX): "${c.slice(0, 60)}..."`
|
|
3123
|
-
}) : Js.test(c) && s.push({
|
|
3124
|
-
severity: "error",
|
|
3125
|
-
code: "SDD_TABLE_CELL_MULTI_SENTENCE",
|
|
3126
|
-
file: e,
|
|
3127
|
-
message: `Table cell carries more than one sentence — a cell is one short line, not a paragraph; move the detail into a subsection below the table (AX_SPEC_TABLE_IS_INDEX): "${c.slice(0, 60)}..."`
|
|
3128
|
-
});
|
|
3129
|
-
}
|
|
3130
|
-
}
|
|
3131
|
-
return s;
|
|
3132
|
-
}
|
|
3133
|
-
function Jo(e) {
|
|
3134
|
-
const t = g(e, "INTER_MODULE_DEPENDENCIES");
|
|
3135
|
-
return t.status === "ok" ? js(t.content) : [];
|
|
3136
|
-
}
|
|
3137
|
-
function rn(e) {
|
|
3138
|
-
const t = /\*\*Depends on:\*\*\s*(.*)/i.exec(e);
|
|
3139
|
-
return t?.[1] ? [...t[1].matchAll(/[a-z][a-z0-9-]*\*?/gi)].map((s) => s[0]) : [];
|
|
3140
|
-
}
|
|
3141
|
-
function an(e, t) {
|
|
3142
|
-
return e.some((s) => s.endsWith("*") ? t.startsWith(s.slice(0, -1)) : s === t);
|
|
3143
|
-
}
|
|
3144
|
-
function er(e, t, s) {
|
|
3145
|
-
const n = g(t, "SCOPE_DEPENDENCIES");
|
|
3146
|
-
if (n.status !== "ok") return [];
|
|
3147
|
-
const o = /([^/\\]+)\.(?:spec|1-spec)\.md$/.exec(e)?.[1];
|
|
3148
|
-
if (!o) return [];
|
|
3149
|
-
const r = rn(n.content), i = [];
|
|
3150
|
-
for (const a of s)
|
|
3151
|
-
a.from === o && !an(r, a.to) && i.push({
|
|
3152
|
-
severity: "warn",
|
|
3153
|
-
code: "SDD_SCOPE_DEP_UNDECLARED",
|
|
3154
|
-
file: e,
|
|
3155
|
-
message: `Portal Scope Graph has \`${o} --> ${a.to}\` but the scope spec's Scope Dependencies does not list ${a.to} (AX_SCOPE_GRAPH_DISCIPLINE). Add it under "Depends on" (a name or a \`prefix-*\` wildcard), or fix the portal graph.`
|
|
3156
|
-
});
|
|
3157
|
-
return i;
|
|
3158
|
-
}
|
|
3159
|
-
function tr(e, t, s) {
|
|
3160
|
-
return J(s) ? [
|
|
3161
|
-
{
|
|
3162
|
-
severity: "error",
|
|
3163
|
-
code: "SDD_MODULE_DAG_CYCLE",
|
|
3164
|
-
file: t,
|
|
3165
|
-
message: `Scope ${e}: module dependency graph (## 9) has a cycle.`
|
|
3166
|
-
}
|
|
3167
|
-
] : [];
|
|
3168
|
-
}
|
|
3169
|
-
function $e(e) {
|
|
3170
|
-
const t = /<!--SECTION:MODULE_VISION-->/.test(e), s = /<!--SECTION:SCOPE_TYPE-->/.test(e) && !t;
|
|
3171
|
-
return t ? "module" : s ? "scope" : "other";
|
|
3172
|
-
}
|
|
3173
|
-
function cn(e, t) {
|
|
3174
|
-
let s = w(w(e)), n = "";
|
|
3175
|
-
for (; s !== n; ) {
|
|
3176
|
-
const o = h(s, `${G(s)}.spec.md`);
|
|
3177
|
-
if (o !== e && t.has(o)) return o;
|
|
3178
|
-
n = s, s = w(s);
|
|
3179
|
-
}
|
|
3180
|
-
return null;
|
|
3181
|
-
}
|
|
3182
|
-
function ln(e, t, s) {
|
|
3183
|
-
for (const n of e.matchAll(/\]\(([^)`#]+\.spec\.md)(?:#[^)]*)?\)/g)) {
|
|
3184
|
-
const o = n[1];
|
|
3185
|
-
if (o && y(t, o) === y(s)) return !0;
|
|
3186
|
-
}
|
|
3187
|
-
return !1;
|
|
3188
|
-
}
|
|
3189
|
-
function sr(e) {
|
|
3190
|
-
const t = [], s = new Map(e.map((a) => [a.file, a])), n = new Set(e.map((a) => a.file)), o = e.filter((a) => $e(a.content) === "module"), r = /* @__PURE__ */ new Map();
|
|
3191
|
-
for (const a of o) {
|
|
3192
|
-
const c = cn(a.file, n);
|
|
3193
|
-
c && r.set(a.file, c);
|
|
3194
|
-
}
|
|
3195
|
-
for (const a of o) {
|
|
3196
|
-
const c = r.get(a.file);
|
|
3197
|
-
if (!c) continue;
|
|
3198
|
-
const l = s.get(c);
|
|
3199
|
-
l && (ln(l.content, w(c), a.file) || t.push({
|
|
3200
|
-
severity: a.flowVersion === "v2" ? "error" : "warn",
|
|
3201
|
-
code: "SDD_MODULE_NOT_IN_INDEX",
|
|
3202
|
-
file: c,
|
|
3203
|
-
message: `Module spec ${a.file} is not linked from its parent index ${c} — every module must appear in the parent's Module Map / links (AX_HIERARCHICAL_SPECS).`
|
|
3204
|
-
}));
|
|
3205
|
-
}
|
|
3206
|
-
const i = new Set(r.values());
|
|
3207
|
-
for (const a of i) {
|
|
3208
|
-
const c = s.get(a);
|
|
3209
|
-
if (!(!c || $e(c.content) !== "module"))
|
|
3210
|
-
for (const l of ["ENTITY_INVENTORY", "MODULE_CONTRACTS"])
|
|
3211
|
-
new RegExp(`<!--SECTION:${l}-->`).test(c.content) && t.push({
|
|
3212
|
-
severity: c.flowVersion === "v2" ? "error" : "warn",
|
|
3213
|
-
code: "SDD_PARENT_MODULE_NOT_INDEX",
|
|
3214
|
-
file: a,
|
|
3215
|
-
message: `Module spec has child module specs beneath it but still carries module-level section ${l} — a parent module must become a thin index over its children (AX_SCOPE_STAYS_THIN).`
|
|
3216
|
-
});
|
|
3217
|
-
}
|
|
3218
|
-
return t;
|
|
3219
|
-
}
|
|
3220
|
-
const dn = /^[ \t]*✚ /m;
|
|
3221
|
-
function nr(e, t) {
|
|
3222
|
-
const s = [], n = g(t, "CHANGE_MANIFEST"), o = n.status === "ok";
|
|
3223
|
-
return dn.test(t) && !o && s.push({
|
|
3224
|
-
severity: "error",
|
|
3225
|
-
code: "SDD_REVIEW_INCONSISTENT",
|
|
3226
|
-
file: e,
|
|
3227
|
-
message: "Found a ✚ change-mark but no CHANGE_MANIFEST — review-state is malformed (compress half-ran, or marks added without entering review-state). Add the manifest (CHANGE_MANIFEST_FORMAT) or strip the marks. AX_SPEC_LIFECYCLE."
|
|
3228
|
-
}), o && (/ТИП ИЗМЕНЕНИЯ/.test(n.content) || s.push({
|
|
3229
|
-
severity: "error",
|
|
3230
|
-
code: "SDD_REVIEW_INCONSISTENT",
|
|
3231
|
-
file: e,
|
|
3232
|
-
message: "CHANGE_MANIFEST is missing the «ТИП ИЗМЕНЕНИЯ» field — the manifest is incomplete and cannot be reviewed or compressed. Fill the required fields (CHANGE_MANIFEST_FORMAT). AX_SPEC_LIFECYCLE."
|
|
3233
|
-
}), s.push({
|
|
3234
|
-
severity: "warn",
|
|
3235
|
-
code: "SDD_REVIEW_STATE_STUCK",
|
|
3236
|
-
file: e,
|
|
3237
|
-
message: "Spec is in review-state (CHANGE_MANIFEST present). Finalize it: once external review approves («no comments»), run compress — remove the manifest + ✚/~ marks; if the change was abandoned, remove the manifest. A spec must not linger in review-state. AX_SPEC_LIFECYCLE."
|
|
3238
|
-
})), s;
|
|
3239
|
-
}
|
|
3240
|
-
let H = null;
|
|
3241
|
-
async function un() {
|
|
3242
|
-
if (H) return H;
|
|
3243
|
-
const { JSDOM: e } = await import("jsdom"), t = new e("<!DOCTYPE html><html><body></body></html>"), s = globalThis;
|
|
3244
|
-
s.window ??= t.window, s.document ??= t.window.document, s.navigator ??= t.window.navigator;
|
|
3245
|
-
const n = (await import("mermaid")).default;
|
|
3246
|
-
return n.initialize({ startOnLoad: !1 }), H = (o) => n.parse(o), H;
|
|
3247
|
-
}
|
|
3248
|
-
function fn(e) {
|
|
3249
|
-
const t = [], s = e.split(`
|
|
3250
|
-
`);
|
|
3251
|
-
let n = !1, o = [];
|
|
3252
|
-
for (const r of s) {
|
|
3253
|
-
const i = r.trim();
|
|
3254
|
-
if (!n && i.startsWith("```mermaid")) {
|
|
3255
|
-
n = !0, o = [];
|
|
3256
|
-
continue;
|
|
3257
|
-
}
|
|
3258
|
-
if (n && i === "```") {
|
|
3259
|
-
t.push(o.join(`
|
|
3260
|
-
`)), n = !1;
|
|
3261
|
-
continue;
|
|
3262
|
-
}
|
|
3263
|
-
n && o.push(r);
|
|
3264
|
-
}
|
|
3265
|
-
return t;
|
|
3266
|
-
}
|
|
3267
|
-
async function pn(e) {
|
|
3268
|
-
const t = await un();
|
|
3269
|
-
try {
|
|
3270
|
-
return await t(e), null;
|
|
3271
|
-
} catch (s) {
|
|
3272
|
-
return s instanceof Error ? s.message.split(`
|
|
3273
|
-
`)[0] ?? "invalid" : String(s);
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
|
-
async function or(e, t) {
|
|
3277
|
-
const s = fn(t);
|
|
3278
|
-
if (s.length === 0) return [];
|
|
3279
|
-
const n = [];
|
|
3280
|
-
for (const o of s) {
|
|
3281
|
-
const r = await pn(o);
|
|
3282
|
-
r !== null && n.push({
|
|
3283
|
-
severity: "error",
|
|
3284
|
-
code: "SDD_DIAGRAM_INVALID",
|
|
3285
|
-
file: e,
|
|
3286
|
-
message: `mermaid diagram does not parse: ${r}`
|
|
3287
|
-
});
|
|
3288
|
-
}
|
|
3289
|
-
return n;
|
|
3290
|
-
}
|
|
3291
|
-
function at(e) {
|
|
3292
|
-
const t = e.match(/\*{0,2}Task-ID:\*{0,2}\s*`?([A-Za-z0-9][A-Za-z0-9_-]*)`?/), s = e.match(/\*{0,2}Status:\*{0,2}\s*(\[.\]\s*[A-Z_]+)/);
|
|
3293
|
-
return {
|
|
3294
|
-
taskId: t?.[1] ?? null,
|
|
3295
|
-
status: s?.[1] ?? null
|
|
3296
|
-
};
|
|
3297
|
-
}
|
|
3298
|
-
function ct(e) {
|
|
3299
|
-
return e.replace(/`/g, "").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1").trim();
|
|
3300
|
-
}
|
|
3301
|
-
function V(e) {
|
|
3302
|
-
return e.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((t) => t.trim());
|
|
3303
|
-
}
|
|
3304
|
-
function rr(e, t, s) {
|
|
3305
|
-
const n = e.split(`
|
|
3306
|
-
`), o = lt(n);
|
|
3307
|
-
if (!o) return { ok: !1, reason: "no_table" };
|
|
3308
|
-
const { headerIdx: r, idCol: i, statusCol: a } = o;
|
|
3309
|
-
for (let c = r + 1; c < n.length; c++) {
|
|
3310
|
-
const l = n[c];
|
|
3311
|
-
if (l === void 0 || !l.trimStart().startsWith("|")) break;
|
|
3312
|
-
const f = V(l);
|
|
3313
|
-
if (/^\|?\s*-+\s*\|/.test(l) || ct(f[i] ?? "") !== t) continue;
|
|
3314
|
-
const d = l.split("|"), u = a + 1;
|
|
3315
|
-
if (d[u] === void 0) return { ok: !1, reason: "no_table" };
|
|
3316
|
-
const p = d[u];
|
|
3317
|
-
return d[u] = ` ${s} `, d[u] === p ? { ok: !0, text: e, changed: !1 } : (n[c] = d.join("|"), { ok: !0, text: n.join(`
|
|
3318
|
-
`), changed: !0 });
|
|
3319
|
-
}
|
|
3320
|
-
return { ok: !1, reason: "task_not_found" };
|
|
3321
|
-
}
|
|
3322
|
-
function lt(e) {
|
|
3323
|
-
for (let t = 0; t < e.length; t++) {
|
|
3324
|
-
const s = e[t];
|
|
3325
|
-
if (s === void 0 || !s.trimStart().startsWith("|")) continue;
|
|
3326
|
-
const n = V(s).map((i) => i.toLowerCase()), o = n.indexOf("task-id"), r = n.indexOf("status");
|
|
3327
|
-
if (o !== -1 && r !== -1) return { headerIdx: t, idCol: o, statusCol: r };
|
|
3328
|
-
}
|
|
3329
|
-
return null;
|
|
3330
|
-
}
|
|
3331
|
-
function ir(e) {
|
|
3332
|
-
const t = e.split(`
|
|
3333
|
-
`), s = lt(t);
|
|
3334
|
-
if (!s) return [];
|
|
3335
|
-
const n = [];
|
|
3336
|
-
for (let o = s.headerIdx + 1; o < t.length; o++) {
|
|
3337
|
-
const r = t[o];
|
|
3338
|
-
if (r === void 0 || !r.trimStart().startsWith("|")) break;
|
|
3339
|
-
if (/^\|?\s*-+\s*\|/.test(r)) continue;
|
|
3340
|
-
const i = V(r), a = ct(i[s.idCol] ?? "");
|
|
3341
|
-
a && n.push({ taskId: a, status: (i[s.statusCol] ?? "").trim() });
|
|
3342
|
-
}
|
|
3343
|
-
return n;
|
|
3344
|
-
}
|
|
3345
|
-
function mn(e) {
|
|
3346
|
-
return /^\[x\]/i.test(e.trim());
|
|
3347
|
-
}
|
|
3348
|
-
function En(e) {
|
|
3349
|
-
for (let t = 0; t < e.length; t++) {
|
|
3350
|
-
const s = e[t];
|
|
3351
|
-
if (s === void 0 || !s.trimStart().startsWith("|")) continue;
|
|
3352
|
-
const n = V(s).map((a) => a.toLowerCase()), o = n.indexOf("index"), r = n.indexOf("tasks"), i = n.indexOf("done") !== -1 ? n.indexOf("done") : n.indexOf("progress");
|
|
3353
|
-
if (o !== -1 && r !== -1 && i !== -1)
|
|
3354
|
-
return { headerIdx: t, indexCol: o, tasksCol: r, doneCol: i };
|
|
3355
|
-
}
|
|
3356
|
-
return null;
|
|
3357
|
-
}
|
|
3358
|
-
function hn(e) {
|
|
3359
|
-
return e.match(/\[[^\]]*\]\(([^)]+)\)/)?.[1] ?? null;
|
|
3360
|
-
}
|
|
3361
|
-
function ar(e, t) {
|
|
3362
|
-
const s = e.split(`
|
|
3363
|
-
`), n = En(s);
|
|
3364
|
-
if (!n) return { text: e, updated: [] };
|
|
3365
|
-
const o = [];
|
|
3366
|
-
for (let r = n.headerIdx + 1; r < s.length; r++) {
|
|
3367
|
-
const i = s[r];
|
|
3368
|
-
if (i === void 0 || !i.trimStart().startsWith("|")) break;
|
|
3369
|
-
if (/^\|?\s*-+\s*\|/.test(i)) continue;
|
|
3370
|
-
const a = V(i), c = hn(a[n.indexCol] ?? "");
|
|
3371
|
-
if (!c) continue;
|
|
3372
|
-
const l = t(c);
|
|
3373
|
-
if (!l) continue;
|
|
3374
|
-
const f = l.filter((_) => mn(_.status)).length, d = l.length, u = i.split("|"), p = n.tasksCol + 1, m = n.doneCol + 1;
|
|
3375
|
-
if (u[p] === void 0 || u[m] === void 0) continue;
|
|
3376
|
-
const E = ` ${d} `, S = ` ${f}/${d} `;
|
|
3377
|
-
u[p] === E && u[m] === S || (u[p] = E, u[m] = S, s[r] = u.join("|"), o.push(c));
|
|
3378
|
-
}
|
|
3379
|
-
return { text: s.join(`
|
|
3380
|
-
`), updated: o };
|
|
3381
|
-
}
|
|
3382
|
-
function cr(e, t, s) {
|
|
3383
|
-
const n = y(w(y(e)), s);
|
|
3384
|
-
return x(y(t), n).split(b).join("/");
|
|
3385
|
-
}
|
|
3386
|
-
function lr(e) {
|
|
3387
|
-
const t = /<DependsOn>([\s\S]*?)<\/DependsOn>/.exec(e);
|
|
3388
|
-
return t?.[1] ? [...t[1].matchAll(/^\s*-\s+(\S+)/gm)].map((s) => s[1]) : [];
|
|
3389
|
-
}
|
|
3390
|
-
function dr(e, t, s, n) {
|
|
3391
|
-
if (s.length === 0) return [];
|
|
3392
|
-
const o = new Set(s), r = /* @__PURE__ */ new Set(), i = /* @__PURE__ */ new Set(), a = [...s];
|
|
3393
|
-
for (; a.length; ) {
|
|
3394
|
-
const c = a.pop();
|
|
3395
|
-
if (!r.has(c)) {
|
|
3396
|
-
r.add(c);
|
|
3397
|
-
for (const l of n.get(c) ?? [])
|
|
3398
|
-
o.has(l) || i.add(l), r.has(l) || a.push(l);
|
|
3399
|
-
}
|
|
3400
|
-
}
|
|
3401
|
-
return [...i].map((c) => ({
|
|
3402
|
-
severity: "error",
|
|
3403
|
-
code: "SDD_RULES_CASCADE_UNRESOLVED",
|
|
3404
|
-
file: e,
|
|
3405
|
-
message: `Phase ${t}: rule dependency "${c}" is required transitively but is not in the phase's Rules: list — the closure over <DependsOn> is incomplete.`
|
|
3406
|
-
}));
|
|
3407
|
-
}
|
|
3408
|
-
function Be(e) {
|
|
3409
|
-
const t = /@tasks:\s*(.+)/.exec(e);
|
|
3410
|
-
return t?.[1] ? t[1].split(",").map((s) => s.trim()).filter((s) => s.length > 0 && !/^n\/a$/i.test(s)) : [];
|
|
3411
|
-
}
|
|
3412
|
-
function ur(e, t, s) {
|
|
3413
|
-
if (s === null) return [];
|
|
3414
|
-
const n = Be(s), o = new Set(Be(t)), r = n.filter((i) => !o.has(i));
|
|
3415
|
-
return r.length === 0 ? [] : [
|
|
3416
|
-
{
|
|
3417
|
-
severity: "error",
|
|
3418
|
-
code: "SDD_TASKS_APPEND_ONLY_REGRESSION",
|
|
3419
|
-
file: e,
|
|
3420
|
-
message: `@tasks: header dropped previously-declared id(s): ${r.join(", ")} — the header is append-only, ids are never removed.`
|
|
3421
|
-
}
|
|
3422
|
-
];
|
|
3423
|
-
}
|
|
3424
|
-
function Sn(e) {
|
|
3425
|
-
const t = [];
|
|
3426
|
-
let s = 0, n = 0;
|
|
3427
|
-
for (let o = 0; o < e.length; o++) {
|
|
3428
|
-
const r = e[o];
|
|
3429
|
-
r === "(" ? s++ : r === ")" ? s = Math.max(0, s - 1) : r === "," && s === 0 && (t.push(e.slice(n, o).trim()), n = o + 1);
|
|
3430
|
-
}
|
|
3431
|
-
return t.push(e.slice(n).trim()), t.filter((o) => o.length > 0);
|
|
3432
|
-
}
|
|
3433
|
-
function fr(e) {
|
|
3434
|
-
const t = /@consumers:\s*(.+)/.exec(e);
|
|
3435
|
-
return t?.[1] ? Sn(t[1]) : [];
|
|
3436
|
-
}
|
|
3437
|
-
function pr(e) {
|
|
3438
|
-
const t = e.trim(), s = t.replace(/\s*\([^)]*\)\s*$/, "").trim(), n = s.length === 0 || /\s/.test(s) || /\bexternal\b/i.test(t);
|
|
3439
|
-
return { raw: t, name: s, external: n };
|
|
3440
|
-
}
|
|
3441
|
-
function mr(e, t, s) {
|
|
3442
|
-
const n = [];
|
|
3443
|
-
for (const o of t)
|
|
3444
|
-
o.external || s.has(o.name) || n.push({
|
|
3445
|
-
severity: "warn",
|
|
3446
|
-
code: "SDD_CONSUMERS_UNRESOLVED",
|
|
3447
|
-
file: e,
|
|
3448
|
-
message: `@consumers: entry "${o.name}" was not found referenced elsewhere in the codebase (text search) — mark it external in the description if it lives outside this repo, or fix the name.`
|
|
3449
|
-
});
|
|
3450
|
-
return n;
|
|
3451
|
-
}
|
|
3452
|
-
function dt(e) {
|
|
3453
|
-
const t = /^-\s*Deferred Test Ownership:\s*(\S+)\s*(.*)$/.exec(e), s = t?.[1] ?? null, n = t ? t[2] ?? "" : e.replace(/^-\s*/, ""), o = /^(.+?)\s*→\s*`([^`]+)`\s*::\s*(.+?)\.?\s*$/.exec(n);
|
|
3454
|
-
if (!o) return null;
|
|
3455
|
-
const r = (o[1] ?? "").replace(/`\[[^\]]+\]`|\[[^\]]+\]/g, "").trim(), i = (o[2] ?? "").trim(), a = [...(o[3] ?? "").matchAll(/`([^`]+)`/g)].map((c) => c[1]);
|
|
3456
|
-
return !i || a.length === 0 ? null : { scenario: r, testFile: i, caseNames: a, deferred: s };
|
|
3457
|
-
}
|
|
3458
|
-
function Er(e) {
|
|
3459
|
-
const t = [];
|
|
3460
|
-
for (const s of e.split(`
|
|
3461
|
-
`)) {
|
|
3462
|
-
const n = s.trim();
|
|
3463
|
-
if (!n.startsWith("-")) continue;
|
|
3464
|
-
const o = dt(n);
|
|
3465
|
-
o && t.push(o);
|
|
3466
|
-
}
|
|
3467
|
-
return t;
|
|
3468
|
-
}
|
|
3469
|
-
function gn(e) {
|
|
3470
|
-
const t = [];
|
|
3471
|
-
for (const s of e.split(`
|
|
3472
|
-
`)) {
|
|
3473
|
-
const n = s.trim();
|
|
3474
|
-
n.startsWith("-") && (dt(n) || t.push(n));
|
|
3475
|
-
}
|
|
3476
|
-
return t;
|
|
3477
|
-
}
|
|
3478
|
-
function hr(e) {
|
|
3479
|
-
const t = [], s = /\b(?:it|test)(?:\.\w+)?\s*\(\s*(['"`])((?:\\.|(?!\1).)*)\1/g;
|
|
3480
|
-
for (const n of e.matchAll(s))
|
|
3481
|
-
n[2] !== void 0 && t.push(n[2]);
|
|
3482
|
-
return t;
|
|
3483
|
-
}
|
|
3484
|
-
function Sr(e, t) {
|
|
3485
|
-
const s = t.replace(/\\/g, "/"), n = `/${s}`;
|
|
3486
|
-
return e.filter((o) => o === s || o.endsWith(n));
|
|
3487
|
-
}
|
|
3488
|
-
function gr(e, t, s) {
|
|
3489
|
-
return s.length <= 1 ? [] : [
|
|
3490
|
-
{
|
|
3491
|
-
severity: "warn",
|
|
3492
|
-
code: "SDD_BDD_TESTFILE_AMBIGUOUS",
|
|
3493
|
-
file: e,
|
|
3494
|
-
message: `Declared test-file "${t}" matches ${s.length} files on disk (${s.join(", ")}) — ambiguous; use a longer path suffix to disambiguate.`
|
|
3495
|
-
}
|
|
3496
|
-
];
|
|
3497
|
-
}
|
|
3498
|
-
function Ir(e, t, s, n = "v1", o = null) {
|
|
3499
|
-
const r = [], i = n === "v2" ? "error" : "warn";
|
|
3500
|
-
for (const a of t) {
|
|
3501
|
-
if (a.deferred !== null) {
|
|
3502
|
-
o !== null && a.deferred === o && r.push({
|
|
3503
|
-
severity: "error",
|
|
3504
|
-
code: "SDD_BDD_DEFERRED_TO_SELF",
|
|
3505
|
-
file: e,
|
|
3506
|
-
message: `Scenario "${a.scenario}" defers test ownership to this ticket's own Task-ID (${o}) — that hides missing coverage instead of delegating it to another ticket.`
|
|
3507
|
-
});
|
|
3508
|
-
continue;
|
|
3509
|
-
}
|
|
3510
|
-
const c = s.get(a.testFile) ?? [];
|
|
3511
|
-
for (const l of a.caseNames)
|
|
3512
|
-
c.includes(l) || r.push({
|
|
3513
|
-
severity: i,
|
|
3514
|
-
code: "SDD_BDD_SCENARIO_UNTESTED",
|
|
3515
|
-
file: e,
|
|
3516
|
-
message: `Scenario "${a.scenario}" claims case "${l}" in ${a.testFile}, but no it()/test() with that exact name was found there.`
|
|
3517
|
-
});
|
|
3518
|
-
}
|
|
3519
|
-
return r;
|
|
3520
|
-
}
|
|
3521
|
-
function Or(e, t) {
|
|
3522
|
-
return gn(t).map(
|
|
3523
|
-
(s) => ({
|
|
3524
|
-
severity: "warn",
|
|
3525
|
-
code: "SDD_BDD_COVERAGE_ROW_UNPARSED",
|
|
3526
|
-
file: e,
|
|
3527
|
-
message: `Test Scenario Coverage row could not be parsed — no "→ \\\`file\\\` :: \\\`case\\\`" and no valid "Deferred Test Ownership: <Task-ID>": "${s}"`
|
|
3528
|
-
})
|
|
3529
|
-
);
|
|
3530
|
-
}
|
|
3531
|
-
const Ee = ["PLANNED", "MAPPED", "APPROVED", "DONE"], de = ["keep", "rename", "merge", "split", "create", "drop"], K = "UNMAPPED", In = [
|
|
3532
|
-
{ match: /^scope-type$/i, section: "SCOPE_TYPE" },
|
|
3533
|
-
{ match: /^vision(\s*&\s*primary goal)?$/i, section: "VISION" },
|
|
3534
|
-
{ match: /^module vision$/i, section: "MODULE_VISION" },
|
|
3535
|
-
{ match: /^overview$/i, section: "OVERVIEW" },
|
|
3536
|
-
{ match: /^project type$/i, section: "PROJECT_TYPE" },
|
|
3537
|
-
{ match: /^(approved\s+)?golden (dx|ux)(\s+example)?(\s*\(.*\))?$/i, section: "GOLDEN_DX" },
|
|
3538
|
-
{ match: /^scope dependencies$/i, section: "SCOPE_DEPENDENCIES" },
|
|
3539
|
-
{ match: /^requirements(\s*&\s*constraints)?$/i, section: "REQUIREMENTS_AND_CONSTRAINTS" },
|
|
3540
|
-
{ match: /^(high-level\s+)?architecture$/i, section: "ARCHITECTURE" },
|
|
3541
|
-
{ match: /^module map(\s*\(.*\))?$/i, section: "MODULE_MAP" },
|
|
3542
|
-
{ match: /^module decision log$/i, section: "MODULE_DECISION_LOG" },
|
|
3543
|
-
{ match: /^decision log$/i, section: "DECISION_LOG" },
|
|
3544
|
-
{ match: /^(prerequisites|bootstrap requirements)$/i, section: "BOOTSTRAP_REQUIREMENTS" },
|
|
3545
|
-
{ match: /^handoff(\s+to\s+.*)?$/i, section: "HANDOFF" },
|
|
3546
|
-
{ match: /^tool stack(\s*\(.*\))?$/i, section: "TOOL_STACK" },
|
|
3547
|
-
{ match: /^developer workflow example$/i, section: "WORKFLOW_EXAMPLE" },
|
|
3548
|
-
{ match: /^file structure$/i, section: "FILE_STRUCTURE" },
|
|
3549
|
-
{ match: /^effective rules(\s*\(.*\))?$/i, section: "EFFECTIVE_RULES" },
|
|
3550
|
-
{ match: /^verification commands$/i, section: "VERIFICATION_COMMANDS" },
|
|
3551
|
-
{ match: /^interface declaration$/i, section: "INTERFACE_DECLARATION" },
|
|
3552
|
-
{ match: /^versioning policy$/i, section: "VERSIONING_POLICY" },
|
|
3553
|
-
{ match: /^compatibility matrix$/i, section: "COMPATIBILITY_MATRIX" },
|
|
3554
|
-
{ match: /^public api surface$/i, section: "PUBLIC_API_SURFACE" },
|
|
3555
|
-
{ match: /^module usage example$/i, section: "MODULE_USAGE_EXAMPLE" },
|
|
3556
|
-
{ match: /^inter-module dependencies$/i, section: "INTER_MODULE_DEPENDENCIES" },
|
|
3557
|
-
{ match: /^entity inventory(\s*\(.*\))?$/i, section: "ENTITY_INVENTORY" },
|
|
3558
|
-
{ match: /^entity surfaces$/i, section: "ENTITY_SURFACES" },
|
|
3559
|
-
{ match: /^module contracts(\s*\(.*\))?$/i, section: "MODULE_CONTRACTS" },
|
|
3560
|
-
{ match: /^public options(\s*&\s*policies)?$/i, section: "PUBLIC_OPTIONS" }
|
|
3561
|
-
];
|
|
3562
|
-
function On(e) {
|
|
3563
|
-
const t = e.replace(/^#{2,3}\s*/, "").replace(/^\d+(\.\d+)*\.?\s+/, "").trim();
|
|
3564
|
-
for (const s of In)
|
|
3565
|
-
if (s.match.test(t)) return s.section;
|
|
3566
|
-
return K;
|
|
3567
|
-
}
|
|
3568
|
-
const Tn = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "out", "coverage"]);
|
|
3569
|
-
function ue(e, t, s) {
|
|
3570
|
-
let n;
|
|
3571
|
-
try {
|
|
3572
|
-
n = $(e, { withFileTypes: !0 });
|
|
3573
|
-
} catch {
|
|
3574
|
-
return;
|
|
3575
|
-
}
|
|
3576
|
-
for (const o of n) {
|
|
3577
|
-
if (o.name.startsWith(".") || Tn.has(o.name) || o.isSymbolicLink()) continue;
|
|
3578
|
-
const r = h(e, o.name);
|
|
3579
|
-
o.isDirectory() ? ue(r, t, s) : o.isFile() && t(o.name) && s.push(r);
|
|
3580
|
-
}
|
|
3581
|
-
}
|
|
3582
|
-
function _n(e) {
|
|
3583
|
-
const t = [];
|
|
3584
|
-
let s = !1;
|
|
3585
|
-
for (const n of e.split(`
|
|
3586
|
-
`)) {
|
|
3587
|
-
if (n.trimStart().startsWith("```")) {
|
|
3588
|
-
s = !s;
|
|
3589
|
-
continue;
|
|
3590
|
-
}
|
|
3591
|
-
!s && /^##\s/.test(n) && t.push(n.trim());
|
|
3592
|
-
}
|
|
3593
|
-
return t;
|
|
3594
|
-
}
|
|
3595
|
-
function Nn(e) {
|
|
3596
|
-
const t = /* @__PURE__ */ new Set();
|
|
3597
|
-
for (const s of e.matchAll(/<!--SECTION:([A-Z][A-Z0-9_]*)-->/g))
|
|
3598
|
-
s[1] && t.add(s[1]);
|
|
3599
|
-
return [...t];
|
|
3600
|
-
}
|
|
3601
|
-
function Cn(e) {
|
|
3602
|
-
const t = g(e, "SCOPE_TYPE");
|
|
3603
|
-
return t.status !== "ok" ? null : /\b(product|library|infrastructure|interface)\b/.exec(t.content)?.[1] ?? null;
|
|
3604
|
-
}
|
|
3605
|
-
function Dn(e, t) {
|
|
3606
|
-
let s = "";
|
|
3607
|
-
try {
|
|
3608
|
-
s = D(t, "utf-8");
|
|
3609
|
-
} catch {
|
|
3610
|
-
}
|
|
3611
|
-
const n = g(s, "META"), o = n.status === "ok" ? n.content : /##[^\n]*\bMeta\b[^\n]*\n([\s\S]*?)(?=\n## |$)/.exec(s)?.[1] ?? "", r = at(o), i = /\*{0,2}Purpose:\*{0,2}\s*([^\n]+)/.exec(o)?.[1]?.trim() ?? null, a = /\*{0,2}Scope:\*{0,2}\s*`?([\w./-]+)`?/.exec(o)?.[1] ?? null, c = /\*{0,2}Module:\*{0,2}\s*`?([\w./-]+)`?/.exec(o)?.[1] ?? null;
|
|
3612
|
-
return {
|
|
3613
|
-
ticket: {
|
|
3614
|
-
file: x(e, t),
|
|
3615
|
-
taskId: r.taskId,
|
|
3616
|
-
status: r.status,
|
|
3617
|
-
purpose: i
|
|
3618
|
-
},
|
|
3619
|
-
scope: a,
|
|
3620
|
-
module: c
|
|
3621
|
-
};
|
|
3622
|
-
}
|
|
3623
|
-
function he(e) {
|
|
3624
|
-
const t = h(e, "specs"), s = [];
|
|
3625
|
-
ue(t, (i) => i.endsWith(".spec.md") || i.endsWith(".1-spec.md"), s), s.sort();
|
|
3626
|
-
const n = [];
|
|
3627
|
-
for (const i of s) {
|
|
3628
|
-
const a = x(e, i), c = a.split(b), l = c[1];
|
|
3629
|
-
if (!l || c.length < 3) continue;
|
|
3630
|
-
const f = c.length >= 4 ? c[c.length - 2] ?? null : null, d = D(i, "utf-8"), u = f !== null, p = Cn(d), m = u ? ["MODULE_VISION", "OVERVIEW", ...it.filter((E) => E !== "MODULE_VISION")] : ["OVERVIEW", ...p ? ce[p] ?? [] : []];
|
|
3631
|
-
n.push({
|
|
3632
|
-
specFile: a,
|
|
3633
|
-
scope: l,
|
|
3634
|
-
module: f,
|
|
3635
|
-
scopeType: p,
|
|
3636
|
-
lines: d.split(`
|
|
3637
|
-
`).length,
|
|
3638
|
-
mermaidCount: (d.match(/^\s*```mermaid/gm) ?? []).length,
|
|
3639
|
-
anchors: Nn(d),
|
|
3640
|
-
headings: _n(d),
|
|
3641
|
-
tickets: [],
|
|
3642
|
-
targetSections: [...new Set(m)],
|
|
3643
|
-
foldSections: u ? [...le] : []
|
|
3644
|
-
});
|
|
3645
|
-
}
|
|
3646
|
-
const o = [];
|
|
3647
|
-
ue(h(e, "tasks"), (i) => /\.task-[0-9]+\.md$/.test(i), o), o.sort();
|
|
3648
|
-
const r = [];
|
|
3649
|
-
for (const i of o) {
|
|
3650
|
-
const a = x(e, i), c = a.split(b)[1] ?? "", { ticket: l, scope: f, module: d } = Dn(e, i), u = a.split(b).length >= 4 ? a.split(b)[2] : null, p = n.filter((I) => I.scope === c), m = d ? p.find((I) => I.module === d) : void 0, E = u ? p.find((I) => I.module === u) : void 0, S = f ? p.find((I) => I.module === f) : void 0, _ = p.find((I) => I.module === null), N = m ?? E ?? S ?? _;
|
|
3651
|
-
N ? N.tickets.push(l) : r.push(l);
|
|
3652
|
-
}
|
|
3653
|
-
return { units: n, orphanTickets: r };
|
|
3654
|
-
}
|
|
3655
|
-
function ee(e) {
|
|
3656
|
-
const t = w(e.specFile).split(b).slice(1).join(b), s = G(e.specFile).replace(/\.md$/, ".migration.md");
|
|
3657
|
-
return h("migration", t, s);
|
|
3658
|
-
}
|
|
3659
|
-
function ut(e) {
|
|
3660
|
-
const t = [
|
|
3661
|
-
"## Inventory",
|
|
3662
|
-
"",
|
|
3663
|
-
"<!-- Сгенерировано `sdd-migrate plan`. Не редактировать руками — `plan --verify` сверяет этот блок с реальностью и падает при расхождении. -->",
|
|
3664
|
-
"",
|
|
3665
|
-
`- spec: \`${e.specFile}\` · строк: ${e.lines} · scope-type: ${e.scopeType ? `\`${e.scopeType}\`` : "—"} · mermaid-блоков: ${e.mermaidCount} · якорей: ${e.anchors.length}`,
|
|
3666
|
-
`- обязательные целевые секции: ${e.targetSections.map((s) => `\`${s}\``).join(", ")}`
|
|
3667
|
-
];
|
|
3668
|
-
e.foldSections.length > 0 && t.push(
|
|
3669
|
-
`- секции, чья детализация сворачивается в \`<details>\`: ${e.foldSections.map((s) => `\`${s}\``).join(", ")}`
|
|
3670
|
-
), t.push("- заголовки (level 2):");
|
|
3671
|
-
for (const s of e.headings) t.push(` - \`${s}\``);
|
|
3672
|
-
e.headings.length === 0 && t.push(" - (нет)"), t.push("- тикеты:");
|
|
3673
|
-
for (const s of e.tickets)
|
|
3674
|
-
t.push(
|
|
3675
|
-
` - \`${s.file}\` · ${s.taskId ? `\`${s.taskId}\`` : "—"} · ${s.status ?? "—"} · ${s.purpose ?? "—"}`
|
|
3676
|
-
);
|
|
3677
|
-
return e.tickets.length === 0 && t.push(" - (нет)"), t.join(`
|
|
3678
|
-
`);
|
|
3679
|
-
}
|
|
3680
|
-
function An(e) {
|
|
3681
|
-
const t = [
|
|
3682
|
-
"## Section Map",
|
|
3683
|
-
"",
|
|
3684
|
-
"<!-- Предзаполнено `sdd-migrate plan` по курируемым правилам (mapHeadingToSection). Агент правит",
|
|
3685
|
-
" только строки с целью UNMAPPED — остальные пересматривает лишь если правило ошиблось.",
|
|
3686
|
-
` Действия: ${de.join(" | ")}. Каждая обязательная целевая секция должна появиться в колонке «Цель».`,
|
|
3687
|
-
" Полный целевой порядок секций — в format-файле структуры (ai/directives/sdd-v2/formats/*-spec-structure.xml). -->",
|
|
3688
|
-
"",
|
|
3689
|
-
"| Источник | Действие | Цель (SECTION) | Комментарий |",
|
|
3690
|
-
"|---|---|---|---|"
|
|
3691
|
-
], s = /* @__PURE__ */ new Set();
|
|
3692
|
-
for (const n of e.headings) {
|
|
3693
|
-
const o = On(n);
|
|
3694
|
-
o === K ? t.push(`| \`${n}\` | ? | ${K} | не распознано правилом — решает агент |`) : (s.add(o), t.push(`| \`${n}\` | keep | ${o} | предзаполнено правилом |`));
|
|
3695
|
-
}
|
|
3696
|
-
for (const n of e.targetSections)
|
|
3697
|
-
s.has(n) || t.push(`| — | create | ${n} | если не покрыта строками выше — создать |`);
|
|
3698
|
-
return t.join(`
|
|
3699
|
-
`);
|
|
3700
|
-
}
|
|
3701
|
-
function yn(e) {
|
|
3702
|
-
const t = G(e.specFile).replace(/\.(1-spec|spec)\.md$/, ""), n = [
|
|
3703
|
-
"## Ticket Map",
|
|
3704
|
-
"",
|
|
3705
|
-
"<!-- Новый ID: `<ACR>-<slug>` — kebab-case, слаг из Meta.Purpose, уникален в рамках всего репо.",
|
|
3706
|
-
` Назначение вычисляется из ID: \`${w(e.specFile)}/${t}.task.<новый-ID>.md\`. -->`,
|
|
3707
|
-
"",
|
|
3708
|
-
"| Файл | Task-ID | Новый ID | Назначение |",
|
|
3709
|
-
"|---|---|---|---|"
|
|
3710
|
-
];
|
|
3711
|
-
for (const o of e.tickets) n.push(`| \`${o.file}\` | ${o.taskId ?? "—"} | ? | ? |`);
|
|
3712
|
-
return e.tickets.length === 0 && n.push("| (нет тикетов) | — | — | — |"), n.join(`
|
|
3713
|
-
`);
|
|
3714
|
-
}
|
|
3715
|
-
function Rn(e) {
|
|
3716
|
-
return [
|
|
3717
|
-
"## Diagram Plan",
|
|
3718
|
-
"",
|
|
3719
|
-
"<!-- Заполняет агент: какая диаграмма встанет в Overview (обязательная) и откуда её содержание",
|
|
3720
|
-
" (существующий mermaid-блок / новая — из какого текста строится). Дополнительные диаграммы",
|
|
3721
|
-
" (последовательности, потоки данных) — по решению агента, если они улучшают чтение. -->",
|
|
3722
|
-
"",
|
|
3723
|
-
`- существующих mermaid-блоков: ${e.mermaidCount}`,
|
|
3724
|
-
"- Overview-диаграмма: ?"
|
|
3725
|
-
].join(`
|
|
3726
|
-
`);
|
|
3727
|
-
}
|
|
3728
|
-
function kn(e) {
|
|
3729
|
-
return [
|
|
3730
|
-
"## Steps",
|
|
3731
|
-
"",
|
|
3732
|
-
"<!-- Работа этого юнита. Механические шаги всего репо (anchors / ids / move) живут в migration/README.md",
|
|
3733
|
-
" и выполняются централизованно — здесь только то, что делается в рамках этой спеки. -->",
|
|
3734
|
-
"",
|
|
3735
|
-
"- [ ] S1 ✍️ Изучить исходник, заполнить Section Map / Ticket Map / Diagram Plan → **Status:** MAPPED",
|
|
3736
|
-
"- [ ] S2 ✅ `npx tsx cli/gennady.ts sdd-migrate plan --verify` — карты полны, расхождений с реальностью нет",
|
|
3737
|
-
"- [ ] S3 🛑 Подтверждение оператора → **Status:** APPROVED",
|
|
3738
|
-
"- [ ] S4 ✍️ Реструктуризация спеки по Section Map: целевой порядок секций из format-файла, заголовки без номеров, тяжёлые секции — в `<details>`, Overview с диаграммой по Diagram Plan",
|
|
3739
|
-
"- [ ] S5 ✍️ Текст — плоский технический русский (без калек и метафор; код/ID/токены — English)",
|
|
3740
|
-
`- [ ] S6 ✅ \`${`npx tsx cli/gennady.ts sdd-check --all specs/${e.scope}`}\` — строгий v2-гейт (структура, фолды, разбор mermaid, язык) → **Status:** DONE`,
|
|
3741
|
-
""
|
|
3742
|
-
].join(`
|
|
3743
|
-
`);
|
|
3744
|
-
}
|
|
3745
|
-
function Tr(e) {
|
|
3746
|
-
return [
|
|
3747
|
-
`# План миграции: ${e.specFile}`,
|
|
3748
|
-
"",
|
|
3749
|
-
`- **Status:** PLANNED <!-- ${Ee.join(" → ")} -->`,
|
|
3750
|
-
`- **Scope:** ${e.scope}${e.module ? ` | **Module:** ${e.module}` : ""}`,
|
|
3751
|
-
"",
|
|
3752
|
-
"<!--SECTION:INVENTORY-->",
|
|
3753
|
-
ut(e),
|
|
3754
|
-
"<!--/SECTION:INVENTORY-->",
|
|
3755
|
-
"",
|
|
3756
|
-
"<!--SECTION:SECTION_MAP-->",
|
|
3757
|
-
An(e),
|
|
3758
|
-
"<!--/SECTION:SECTION_MAP-->",
|
|
3759
|
-
"",
|
|
3760
|
-
"<!--SECTION:TICKET_MAP-->",
|
|
3761
|
-
yn(e),
|
|
3762
|
-
"<!--/SECTION:TICKET_MAP-->",
|
|
3763
|
-
"",
|
|
3764
|
-
"<!--SECTION:DIAGRAM_PLAN-->",
|
|
3765
|
-
Rn(e),
|
|
3766
|
-
"<!--/SECTION:DIAGRAM_PLAN-->",
|
|
3767
|
-
"",
|
|
3768
|
-
"<!--SECTION:STEPS-->",
|
|
3769
|
-
kn(e),
|
|
3770
|
-
"<!--/SECTION:STEPS-->",
|
|
3771
|
-
""
|
|
3772
|
-
].join(`
|
|
3773
|
-
`);
|
|
3774
|
-
}
|
|
3775
|
-
function _r(e) {
|
|
3776
|
-
const t = [
|
|
3777
|
-
"# Миграция v1 → v2 — план",
|
|
3778
|
-
"",
|
|
3779
|
-
"Слой сгенерирован `sdd-migrate plan`. Один файл плана — на одну спеку (её секции + её задачи).",
|
|
3780
|
-
"Статусы юнитов живут в самих unit-файлах; `sdd-migrate plan --verify` проверяет весь слой.",
|
|
3781
|
-
"",
|
|
3782
|
-
"## Порядок исполнения",
|
|
3783
|
-
"",
|
|
3784
|
-
"- [ ] G1 🤖 `sdd-migrate anchors --all . --write` — якоря во все v1-тикеты (идемпотентно)",
|
|
3785
|
-
"- [ ] G2 ✍️ Заполнить unit-файлы (Section Map / Ticket Map / Diagram Plan) — раздаётся параллельно, по одному юниту на агента",
|
|
3786
|
-
"- [ ] G3 ✅ `sdd-migrate plan --verify` — весь слой полон, слаги без коллизий, расхождений нет",
|
|
3787
|
-
"- [ ] G4 🛑 Подтверждение оператора по всему плану",
|
|
3788
|
-
"- [ ] G5 🤖 `sdd-migrate ids --map migration/ids.tsv --write` — карта собирается из всех Ticket Map; замена по словогранице",
|
|
3789
|
-
"- [ ] G6 🤖 `sdd-migrate move --scope <scope> --write` — по одному scope: переезд тикетов + индексы `*.3-tasks.md`; пустой `tasks/<scope>/` удаляется → строгие v2-проверки включаются на этом scope",
|
|
3790
|
-
"- [ ] G7 ✍️ Шаги S4–S6 каждого юнита — раздаётся параллельно (юниты не пересекаются по файлам)",
|
|
3791
|
-
"- [ ] G8 ✅ Финальный гейт: `sdd-state` → `FLOW_VERSION=v2` · `sdd-check --all .` чист · ноль старых Task-ID",
|
|
3792
|
-
"",
|
|
3793
|
-
"## Юниты",
|
|
3794
|
-
"",
|
|
3795
|
-
"| План | Спека | Тикетов |",
|
|
3796
|
-
"|---|---|---|"
|
|
3797
|
-
];
|
|
3798
|
-
for (const s of e.units)
|
|
3799
|
-
t.push(`| \`${ee(s)}\` | \`${s.specFile}\` | ${s.tickets.length} |`);
|
|
3800
|
-
if (e.orphanTickets.length > 0) {
|
|
3801
|
-
t.push("", "## ⚠ Тикеты без юнита", ""), t.push("Их scope не имеет спеки — решить с оператором до старта:", "");
|
|
3802
|
-
for (const s of e.orphanTickets) t.push(`- \`${s.file}\` (${s.taskId ?? "—"})`);
|
|
3803
|
-
}
|
|
3804
|
-
return t.push(""), t.join(`
|
|
3805
|
-
`);
|
|
3806
|
-
}
|
|
3807
|
-
function vn(e) {
|
|
3808
|
-
const s = /\*\*Status:\*\*\s*([A-Z]+)/.exec(e)?.[1];
|
|
3809
|
-
return s && Ee.includes(s) ? s : null;
|
|
3810
|
-
}
|
|
3811
|
-
function wn(e) {
|
|
3812
|
-
return e.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((t) => t.trim());
|
|
3813
|
-
}
|
|
3814
|
-
function X(e) {
|
|
3815
|
-
const t = [];
|
|
3816
|
-
let s = !1;
|
|
3817
|
-
for (const n of e.split(`
|
|
3818
|
-
`))
|
|
3819
|
-
if (n.trimStart().startsWith("|")) {
|
|
3820
|
-
if (!s) {
|
|
3821
|
-
s = !0;
|
|
3822
|
-
continue;
|
|
3823
|
-
}
|
|
3824
|
-
/^\|?\s*-+\s*\|/.test(n) || t.push(wn(n));
|
|
3825
|
-
}
|
|
3826
|
-
return t;
|
|
3827
|
-
}
|
|
3828
|
-
function C(e) {
|
|
3829
|
-
return e.replace(/^`|`$/g, "").trim();
|
|
3830
|
-
}
|
|
3831
|
-
const ft = /^[A-Z][A-Z0-9]*(-[a-z0-9]+)+$/;
|
|
3832
|
-
function Ue(e) {
|
|
3833
|
-
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3834
|
-
}
|
|
3835
|
-
function Pn(e, t, s) {
|
|
3836
|
-
const n = [], o = (d, u) => {
|
|
3837
|
-
n.push({ severity: "error", code: d, file: e, message: u });
|
|
3838
|
-
}, r = vn(t);
|
|
3839
|
-
if (r === null)
|
|
3840
|
-
return o(
|
|
3841
|
-
"MIG_STATUS_INVALID",
|
|
3842
|
-
`Строка **Status:** отсутствует или несёт неизвестный токен — допустимо: ${Ee.join(" / ")}.`
|
|
3843
|
-
), n;
|
|
3844
|
-
const i = r !== "PLANNED", a = g(t, "INVENTORY");
|
|
3845
|
-
a.status !== "ok" ? o("MIG_INVENTORY_MISSING", "Секция INVENTORY отсутствует или разбалансирована.") : a.content.trim() !== ut(s).trim() && o(
|
|
3846
|
-
"MIG_INVENTORY_DRIFT",
|
|
3847
|
-
"Inventory разошёлся с реальностью (спека или тикеты изменились после генерации плана) — перегенерируй юнит: sdd-migrate plan --all --write."
|
|
3848
|
-
);
|
|
3849
|
-
const c = g(t, "SECTION_MAP");
|
|
3850
|
-
if (c.status !== "ok")
|
|
3851
|
-
o("MIG_SECTION_MAP_MISSING", "Секция SECTION_MAP отсутствует или разбалансирована.");
|
|
3852
|
-
else {
|
|
3853
|
-
const d = X(c.content), p = d.filter((m) => C(m[0] ?? "") !== "—").map((m) => C(m[0] ?? ""));
|
|
3854
|
-
for (const m of s.headings) {
|
|
3855
|
-
const E = p.filter((S) => S === m).length;
|
|
3856
|
-
E === 0 && o("MIG_SECTION_UNMAPPED", `Заголовок из Inventory без строки в Section Map: \`${m}\`.`), E > 1 && o(
|
|
3857
|
-
"MIG_SECTION_DUPLICATED",
|
|
3858
|
-
`Заголовок отображён ${E} раз(а): \`${m}\` — нужна ровно одна строка.`
|
|
3859
|
-
);
|
|
3860
|
-
}
|
|
3861
|
-
for (const m of p)
|
|
3862
|
-
s.headings.includes(m) || o(
|
|
3863
|
-
"MIG_SECTION_UNKNOWN",
|
|
3864
|
-
`Строка Section Map ссылается на несуществующий заголовок: \`${m}\`.`
|
|
3865
|
-
);
|
|
3866
|
-
for (const m of d)
|
|
3867
|
-
C(m[2] ?? "") === K && o(
|
|
3868
|
-
"MIG_SECTION_UNMAPPED_TARGET",
|
|
3869
|
-
`Заголовок «${C(m[0] ?? "")}» остался нераспознанным (UNMAPPED) — реши действие и цель вручную.`
|
|
3870
|
-
);
|
|
3871
|
-
for (const m of d) {
|
|
3872
|
-
const E = (m[1] ?? "").trim();
|
|
3873
|
-
de.includes(E) || !i && E === "?" || o(
|
|
3874
|
-
"MIG_BAD_ACTION",
|
|
3875
|
-
`Недопустимое действие «${E}» — словарь: ${de.join(" | ")}${i ? "" : " (или ? до заполнения)"}.`
|
|
3876
|
-
), i && (m[2] ?? "").trim() === "?" && o(
|
|
3877
|
-
"MIG_TARGET_UNSET",
|
|
3878
|
-
`Цель не заполнена для строки «${C(m[0] ?? "")}» при Status ≥ MAPPED.`
|
|
3879
|
-
);
|
|
3880
|
-
}
|
|
3881
|
-
if (i) {
|
|
3882
|
-
const m = d.map((E) => (E[2] ?? "").trim());
|
|
3883
|
-
for (const E of s.targetSections)
|
|
3884
|
-
m.includes(E) || o(
|
|
3885
|
-
"MIG_TARGET_MISSING",
|
|
3886
|
-
`Обязательная целевая секция не производится ни одной строкой: \`${E}\`.`
|
|
3887
|
-
);
|
|
3888
|
-
}
|
|
3889
|
-
}
|
|
3890
|
-
const l = g(t, "TICKET_MAP");
|
|
3891
|
-
if (l.status !== "ok")
|
|
3892
|
-
o("MIG_TICKET_MAP_MISSING", "Секция TICKET_MAP отсутствует или разбалансирована.");
|
|
3893
|
-
else {
|
|
3894
|
-
const d = X(l.content).filter((p) => C(p[0] ?? "") !== "(нет тикетов)"), u = d.map((p) => C(p[0] ?? ""));
|
|
3895
|
-
for (const p of s.tickets)
|
|
3896
|
-
u.includes(p.file) || o("MIG_TICKET_UNMAPPED", `Тикет из Inventory без строки в Ticket Map: \`${p.file}\`.`);
|
|
3897
|
-
for (const p of d) {
|
|
3898
|
-
const m = C(p[0] ?? "");
|
|
3899
|
-
if (!s.tickets.find((I) => I.file === m)) {
|
|
3900
|
-
o(
|
|
3901
|
-
"MIG_TICKET_UNKNOWN",
|
|
3902
|
-
`Строка Ticket Map ссылается на несуществующий тикет: \`${m}\`.`
|
|
3903
|
-
);
|
|
3904
|
-
continue;
|
|
3905
|
-
}
|
|
3906
|
-
const S = C(p[1] ?? "");
|
|
3907
|
-
(S === "" || S === "—") && o(
|
|
3908
|
-
"MIG_TICKET_ID_UNREADABLE",
|
|
3909
|
-
`Старый Task-ID не прочитан из тикета \`${m}\` (parseMeta вернул null — проверь формат строки Task-ID в Meta) — заменять нечего, sdd-migrate ids промолчит.`
|
|
3910
|
-
);
|
|
3911
|
-
const _ = C(p[2] ?? ""), N = C(p[3] ?? "");
|
|
3912
|
-
i && (ft.test(_) ? new RegExp(
|
|
3913
|
-
`^specs/${Ue(s.scope)}/.*\\.task\\.${Ue(_)}\\.md$`
|
|
3914
|
-
).test(N.split(b).join("/")) || o(
|
|
3915
|
-
"MIG_BAD_DESTINATION",
|
|
3916
|
-
`Назначение «${N}» не подходит форме \`specs/${s.scope}/**/*.task.${_}.md\` — тикет \`${m}\`.`
|
|
3917
|
-
) : o(
|
|
3918
|
-
"MIG_BAD_SLUG",
|
|
3919
|
-
`Новый ID «${_}» не соответствует грамматике \`<ACR>-<slug>\` (ACR — верхний регистр, slug — kebab-case, ≥ 2 слова) — тикет \`${m}\`.`
|
|
3920
|
-
));
|
|
3921
|
-
}
|
|
3922
|
-
}
|
|
3923
|
-
const f = g(t, "DIAGRAM_PLAN");
|
|
3924
|
-
return f.status !== "ok" ? o("MIG_DIAGRAM_PLAN_MISSING", "Секция DIAGRAM_PLAN отсутствует или разбалансирована.") : i && /Overview-диаграмма:\s*\?/.test(f.content) && o(
|
|
3925
|
-
"MIG_DIAGRAM_PLAN_EMPTY",
|
|
3926
|
-
"Diagram Plan не заполнен (Overview-диаграмма: ?) при Status ≥ MAPPED."
|
|
3927
|
-
), n;
|
|
3928
|
-
}
|
|
3929
|
-
function Nr(e) {
|
|
3930
|
-
const t = [], s = he(e), n = /* @__PURE__ */ new Map();
|
|
3931
|
-
for (const o of s.units) {
|
|
3932
|
-
const r = ee(o), i = h(e, r);
|
|
3933
|
-
if (!R(i)) {
|
|
3934
|
-
t.push({
|
|
3935
|
-
severity: "error",
|
|
3936
|
-
code: "MIG_UNIT_FILE_MISSING",
|
|
3937
|
-
file: r,
|
|
3938
|
-
message: `Нет файла плана для спеки \`${o.specFile}\` — сгенерируй: sdd-migrate plan --all --write.`
|
|
3939
|
-
});
|
|
3940
|
-
continue;
|
|
3941
|
-
}
|
|
3942
|
-
const a = D(i, "utf-8");
|
|
3943
|
-
t.push(...Pn(r, a, o));
|
|
3944
|
-
const c = g(a, "TICKET_MAP");
|
|
3945
|
-
if (c.status === "ok")
|
|
3946
|
-
for (const l of X(c.content)) {
|
|
3947
|
-
const f = C(l[2] ?? "");
|
|
3948
|
-
if (!ft.test(f)) continue;
|
|
3949
|
-
const d = n.get(f);
|
|
3950
|
-
d && d !== r ? t.push({
|
|
3951
|
-
severity: "error",
|
|
3952
|
-
code: "MIG_SLUG_COLLISION",
|
|
3953
|
-
file: r,
|
|
3954
|
-
message: `Новый ID «${f}» уже занят в \`${d}\` — слаг уникален в рамках репо.`
|
|
3955
|
-
}) : n.set(f, r);
|
|
3956
|
-
}
|
|
3957
|
-
}
|
|
3958
|
-
for (const o of s.orphanTickets)
|
|
3959
|
-
t.push({
|
|
3960
|
-
severity: "error",
|
|
3961
|
-
code: "MIG_TICKET_ORPHAN",
|
|
3962
|
-
file: o.file,
|
|
3963
|
-
message: "Тикет без юнита: его scope не имеет спеки — решить с оператором до миграции."
|
|
3964
|
-
});
|
|
3965
|
-
return t;
|
|
3966
|
-
}
|
|
3967
|
-
const pt = ["specs", "tasks", "cli", "shared", "services", "ai", "e2e"], Ln = /* @__PURE__ */ new Set([
|
|
3968
|
-
".md",
|
|
3969
|
-
".ts",
|
|
3970
|
-
".tsx",
|
|
3971
|
-
".js",
|
|
3972
|
-
".jsx",
|
|
3973
|
-
".mjs",
|
|
3974
|
-
".cjs",
|
|
3975
|
-
".json",
|
|
3976
|
-
".xml",
|
|
3977
|
-
".yml",
|
|
3978
|
-
".yaml",
|
|
3979
|
-
".sh",
|
|
3980
|
-
".hbs",
|
|
3981
|
-
".txt"
|
|
3982
|
-
]), bn = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", "out", "coverage"]), Z = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
3983
|
-
function Cr(e) {
|
|
3984
|
-
const t = [], s = [], n = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set();
|
|
3985
|
-
e.split(`
|
|
3986
|
-
`).forEach((r, i) => {
|
|
3987
|
-
const a = r.trim();
|
|
3988
|
-
if (a === "" || a.startsWith("#")) return;
|
|
3989
|
-
const c = r.split(" ").map((d) => d.trim()), [l, f] = c;
|
|
3990
|
-
if (c.length !== 2 || !l || !f) {
|
|
3991
|
-
t.push(`строка ${i + 1}: ожидаю ровно две TAB-колонки \`<old>\\t<new>\``);
|
|
3992
|
-
return;
|
|
3993
|
-
}
|
|
3994
|
-
Z.test(l) || t.push(`строка ${i + 1}: old «${l}» вне грамматики ID`), Z.test(f) || t.push(`строка ${i + 1}: new «${f}» вне грамматики ID`), l === f && t.push(`строка ${i + 1}: old и new совпадают («${l}»)`), n.has(l) && t.push(`строка ${i + 1}: old «${l}» встречается повторно`), o.has(f) && t.push(`строка ${i + 1}: new «${f}» встречается повторно`), n.add(l), o.add(f), s.push({ old: l, next: f });
|
|
3995
|
-
});
|
|
3996
|
-
for (const r of s)
|
|
3997
|
-
n.has(r.next) && t.push(
|
|
3998
|
-
`цепочка: new «${r.next}» одновременно чей-то old — переименования нельзя упорядочить безопасно`
|
|
3999
|
-
);
|
|
4000
|
-
return t.length > 0 ? { ok: !1, errors: t } : { ok: !0, map: s };
|
|
4001
|
-
}
|
|
4002
|
-
function ne(e) {
|
|
4003
|
-
return (e ?? "").replace(/`/g, "").trim();
|
|
4004
|
-
}
|
|
4005
|
-
function Dr(e) {
|
|
4006
|
-
const t = he(e), s = [], n = /* @__PURE__ */ new Set(), o = [];
|
|
4007
|
-
for (const r of t.units) {
|
|
4008
|
-
const i = ee(r);
|
|
4009
|
-
let a;
|
|
4010
|
-
try {
|
|
4011
|
-
a = D(h(e, i), "utf-8");
|
|
4012
|
-
} catch {
|
|
4013
|
-
continue;
|
|
4014
|
-
}
|
|
4015
|
-
const c = g(a, "TICKET_MAP");
|
|
4016
|
-
if (c.status === "ok")
|
|
4017
|
-
for (const l of X(c.content)) {
|
|
4018
|
-
const f = ne(l[0]);
|
|
4019
|
-
if (f === "(нет тикетов)") continue;
|
|
4020
|
-
const d = ne(l[1]), u = ne(l[2]);
|
|
4021
|
-
if (d === "?" || u === "?") continue;
|
|
4022
|
-
if (!Z.test(d) || !Z.test(u)) {
|
|
4023
|
-
o.push(
|
|
4024
|
-
`${i}: строка Ticket Map тикета \`${f}\` невалидна — old=«${d}» new=«${u}» вне грамматики ID (не «?», значит не заглушка).`
|
|
4025
|
-
);
|
|
4026
|
-
continue;
|
|
4027
|
-
}
|
|
4028
|
-
const p = `${d} ${u}`;
|
|
4029
|
-
n.has(p) || (n.add(p), s.push(p));
|
|
4030
|
-
}
|
|
4031
|
-
}
|
|
4032
|
-
return o.length > 0 ? { ok: !1, errors: o } : { ok: !0, tsv: s.sort().join(`
|
|
4033
|
-
`) + (s.length > 0 ? `
|
|
4034
|
-
` : "") };
|
|
4035
|
-
}
|
|
4036
|
-
function mt(e) {
|
|
4037
|
-
return e.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4038
|
-
}
|
|
4039
|
-
function Et(e, t) {
|
|
4040
|
-
const s = [], n = (o) => {
|
|
4041
|
-
let r;
|
|
4042
|
-
try {
|
|
4043
|
-
r = $(o, { withFileTypes: !0 });
|
|
4044
|
-
} catch {
|
|
4045
|
-
return;
|
|
4046
|
-
}
|
|
4047
|
-
for (const i of r) {
|
|
4048
|
-
if (i.name.startsWith(".") || bn.has(i.name) || i.isSymbolicLink()) continue;
|
|
4049
|
-
const a = h(o, i.name);
|
|
4050
|
-
i.isDirectory() ? n(a) : i.isFile() && Ln.has(_t(i.name)) && s.push(a);
|
|
4051
|
-
}
|
|
4052
|
-
};
|
|
4053
|
-
for (const o of t) n(h(e, o));
|
|
4054
|
-
return s.sort();
|
|
4055
|
-
}
|
|
4056
|
-
function Ar(e, t, s, n = pt) {
|
|
4057
|
-
const o = [], r = t.map((i) => ({
|
|
4058
|
-
re: new RegExp(`\\b${mt(i.old)}\\b`, "g"),
|
|
4059
|
-
next: i.next
|
|
4060
|
-
}));
|
|
4061
|
-
for (const i of Et(e, n)) {
|
|
4062
|
-
let a;
|
|
4063
|
-
try {
|
|
4064
|
-
a = D(i, "utf-8");
|
|
4065
|
-
} catch {
|
|
4066
|
-
continue;
|
|
4067
|
-
}
|
|
4068
|
-
let c = a, l = 0;
|
|
4069
|
-
for (const { re: f, next: d } of r)
|
|
4070
|
-
c = c.replace(f, () => (l++, d));
|
|
4071
|
-
l > 0 && (s && q(i, c, "utf-8"), o.push({ file: x(e, i), count: l }));
|
|
4072
|
-
}
|
|
4073
|
-
return o;
|
|
4074
|
-
}
|
|
4075
|
-
function yr(e, t, s = pt) {
|
|
4076
|
-
const n = [], o = t.map((r) => ({ re: new RegExp(`\\b${mt(r.old)}\\b`), id: r.old }));
|
|
4077
|
-
for (const r of Et(e, s)) {
|
|
4078
|
-
let i;
|
|
4079
|
-
try {
|
|
4080
|
-
i = D(r, "utf-8");
|
|
4081
|
-
} catch {
|
|
4082
|
-
continue;
|
|
4083
|
-
}
|
|
4084
|
-
for (const { re: a, id: c } of o)
|
|
4085
|
-
a.test(i) && n.push({ file: x(e, r), id: c });
|
|
4086
|
-
}
|
|
4087
|
-
return n;
|
|
4088
|
-
}
|
|
4089
|
-
function Mn(e) {
|
|
4090
|
-
const t = /\*\*Dependencies:\*\*\s*([^\n]+)/.exec(e);
|
|
4091
|
-
return !t || !t[1] || /^\s*(none|—|-)\s*$/i.test(t[1]) ? [] : [...t[1].replace(/\([^)]*\)/g, " ").matchAll(/\b([A-Za-z][A-Za-z0-9]*(?:-[A-Za-z0-9]+)+)\b/g)].map(
|
|
4092
|
-
(n) => n[1]
|
|
4093
|
-
);
|
|
4094
|
-
}
|
|
4095
|
-
function xn(e) {
|
|
4096
|
-
const t = /^#\s+Task:\s*([^\n]+)/m.exec(e);
|
|
4097
|
-
if (!t || !t[1]) return "—";
|
|
4098
|
-
const s = t[1].trim(), n = s.split("—");
|
|
4099
|
-
return (n.length > 1 ? n.slice(1).join("—") : s).trim() || "—";
|
|
4100
|
-
}
|
|
4101
|
-
function $n(e, t) {
|
|
4102
|
-
const s = [], o = he(e).units.filter((a) => a.scope === t);
|
|
4103
|
-
if (o.length === 0)
|
|
4104
|
-
return { ok: !1, errors: [`scope «${t}» не найден среди юнитов миграции`] };
|
|
4105
|
-
const r = [], i = [];
|
|
4106
|
-
for (const a of o) {
|
|
4107
|
-
const c = ee(a), l = h(e, c);
|
|
4108
|
-
if (!R(l)) {
|
|
4109
|
-
a.tickets.length > 0 && s.push(
|
|
4110
|
-
`нет файла плана ${c} — сгенерируй слой: sdd-migrate plan --all --write`
|
|
4111
|
-
), i.push({ unit: a, tickets: [] });
|
|
4112
|
-
continue;
|
|
4113
|
-
}
|
|
4114
|
-
const f = g(D(l, "utf-8"), "TICKET_MAP"), d = /* @__PURE__ */ new Map();
|
|
4115
|
-
if (f.status === "ok")
|
|
4116
|
-
for (const p of f.content.split(`
|
|
4117
|
-
`)) {
|
|
4118
|
-
if (!p.trimStart().startsWith("|")) continue;
|
|
4119
|
-
const m = p.trim().replace(/^\|/, "").replace(/\|$/, "").split("|").map((S) => S.replace(/`/g, "").trim()), E = m[0] ?? "";
|
|
4120
|
-
(E.startsWith("tasks" + b) || E.startsWith("tasks/")) && d.set(E, { newId: m[2] ?? "?", dest: m[3] ?? "?" });
|
|
4121
|
-
}
|
|
4122
|
-
const u = [];
|
|
4123
|
-
for (const p of a.tickets) {
|
|
4124
|
-
const m = d.get(p.file);
|
|
4125
|
-
if (!m || m.newId === "?" || m.dest === "?") {
|
|
4126
|
-
s.push(`тикет ${p.file}: Ticket Map не заполнен (новый ID/назначение) в ${c}`);
|
|
4127
|
-
continue;
|
|
4128
|
-
}
|
|
4129
|
-
const E = h(e, p.file);
|
|
4130
|
-
if (!R(E)) {
|
|
4131
|
-
s.push(
|
|
4132
|
-
`тикет ${p.file} отсутствует на диске (план разошёлся — sdd-migrate plan --verify)`
|
|
4133
|
-
);
|
|
4134
|
-
continue;
|
|
4135
|
-
}
|
|
4136
|
-
r.push({ from: p.file, to: m.dest, newId: m.newId });
|
|
4137
|
-
const S = D(E, "utf-8"), _ = g(S, "META"), N = _.status === "ok" ? _.content : S, I = at(N);
|
|
4138
|
-
u.push({
|
|
4139
|
-
newId: m.newId,
|
|
4140
|
-
title: xn(S),
|
|
4141
|
-
status: I.status ?? "[ ] TODO",
|
|
4142
|
-
deps: Mn(N),
|
|
4143
|
-
destDir: w(m.dest)
|
|
4144
|
-
});
|
|
4145
|
-
}
|
|
4146
|
-
i.push({ unit: a, tickets: u });
|
|
4147
|
-
}
|
|
4148
|
-
return s.length > 0 ? { ok: !1, errors: s } : { ok: !0, moves: r, units: i };
|
|
4149
|
-
}
|
|
4150
|
-
function Bn(e, t) {
|
|
4151
|
-
const s = new Set(t.map((r) => r.newId)), n = [
|
|
4152
|
-
`# ${e} — Tasks`,
|
|
4153
|
-
"",
|
|
4154
|
-
"## Tracker Index",
|
|
4155
|
-
"| Task-ID | Title | Dependencies | Status | Reopens |",
|
|
4156
|
-
"|---------|-------|--------------|--------|---------|"
|
|
4157
|
-
];
|
|
4158
|
-
for (const r of t)
|
|
4159
|
-
n.push(
|
|
4160
|
-
`| ${r.newId} | ${r.title} | ${r.deps.length > 0 ? r.deps.join(", ") : "—"} | ${r.status} | — |`
|
|
4161
|
-
);
|
|
4162
|
-
n.push("", "## Slug Registry"), n.push(
|
|
4163
|
-
"<!-- один ID на строку; уникальность держится этим списком — одинаковый ID в двух ветках сталкивается здесь при merge. Только добавление. -->"
|
|
4164
|
-
);
|
|
4165
|
-
for (const r of t) n.push(`- ${r.newId}`);
|
|
4166
|
-
n.push("", "## Intra-Module DAG", "```mermaid", "graph TD");
|
|
4167
|
-
let o = 0;
|
|
4168
|
-
for (const r of t)
|
|
4169
|
-
for (const i of r.deps)
|
|
4170
|
-
s.has(i) && (n.push(
|
|
4171
|
-
` ${r.newId.replace(/[^A-Za-z0-9]/g, "_")}[${r.newId}] --> ${i.replace(/[^A-Za-z0-9]/g, "_")}[${i}]`
|
|
4172
|
-
), o++);
|
|
4173
|
-
return o === 0 && n.push(" %% зависимостей внутри модуля нет"), n.push(
|
|
4174
|
-
"```",
|
|
4175
|
-
"<!-- ребро A → B = «A зависит от B». Кросс-модульные рёбра живут уровнем выше. -->"
|
|
4176
|
-
), n.push(
|
|
4177
|
-
"",
|
|
4178
|
-
"## Decision Log (module-task level)",
|
|
4179
|
-
"<!-- решения декомпозиции/планирования; локальные решения исполнения — в Decision Log самих тикетов. -->"
|
|
4180
|
-
), n.push(
|
|
4181
|
-
"",
|
|
4182
|
-
"## Conventions",
|
|
4183
|
-
"Проектные конвенции объявлены в `specs/3-tasks.md` и наследуются — здесь не повторяются.",
|
|
4184
|
-
""
|
|
4185
|
-
), n.join(`
|
|
4186
|
-
`);
|
|
4187
|
-
}
|
|
4188
|
-
function Un(e, t) {
|
|
4189
|
-
const s = h("specs", e), n = (c) => c === s ? e : G(c), o = /* @__PURE__ */ new Map(), r = [];
|
|
4190
|
-
for (const c of t)
|
|
4191
|
-
for (const l of c.tickets)
|
|
4192
|
-
o.set(l.newId, n(l.destDir)), r.push(l);
|
|
4193
|
-
const i = [
|
|
4194
|
-
`# Tasks: ${e}`,
|
|
4195
|
-
"",
|
|
4196
|
-
"## Scope Spec",
|
|
4197
|
-
`- [Scope spec](./${e}.spec.md)`,
|
|
4198
|
-
"",
|
|
4199
|
-
"## Cascade Table",
|
|
4200
|
-
"<!-- ЗАПОЛНЯЕТ АГЕНТ: действующие правила scope из Scope Graph (транзитивное замыкание depends-on). -->",
|
|
4201
|
-
"",
|
|
4202
|
-
"## Inter-Module DAG",
|
|
4203
|
-
"```mermaid",
|
|
4204
|
-
"graph TD"
|
|
4205
|
-
], a = /* @__PURE__ */ new Set();
|
|
4206
|
-
for (const c of r) {
|
|
4207
|
-
const l = n(c.destDir);
|
|
4208
|
-
for (const f of c.deps) {
|
|
4209
|
-
const d = o.get(f);
|
|
4210
|
-
if (d && d !== l) {
|
|
4211
|
-
const u = `${l}-->${d}`;
|
|
4212
|
-
a.has(u) || (a.add(u), i.push(
|
|
4213
|
-
` ${l.replace(/[^A-Za-z0-9]/g, "_")}[${l}] --> ${d.replace(/[^A-Za-z0-9]/g, "_")}[${d}]`
|
|
4214
|
-
));
|
|
4215
|
-
}
|
|
4216
|
-
}
|
|
4217
|
-
}
|
|
4218
|
-
a.size === 0 && i.push(" %% кросс-модульных зависимостей нет"), i.push(
|
|
4219
|
-
"```",
|
|
4220
|
-
"",
|
|
4221
|
-
"## Tracker",
|
|
4222
|
-
"| Task-ID | Title | Module | Dependencies | Status | Reopens |",
|
|
4223
|
-
"|---------|-------|--------|--------------|--------|---------|"
|
|
4224
|
-
);
|
|
4225
|
-
for (const c of r) {
|
|
4226
|
-
const l = n(c.destDir);
|
|
4227
|
-
i.push(
|
|
4228
|
-
`| ${c.newId} | ${c.title} | ${l === e ? "—" : l} | ${c.deps.length > 0 ? c.deps.join(", ") : "—"} | ${c.status} | — |`
|
|
4229
|
-
);
|
|
4230
|
-
}
|
|
4231
|
-
return i.push(
|
|
4232
|
-
"",
|
|
4233
|
-
"## Decision Log (scope task level)",
|
|
4234
|
-
"<!-- D-NNN scope-уровневых решений декомпозиции/планирования. -->",
|
|
4235
|
-
""
|
|
4236
|
-
), i.join(`
|
|
4237
|
-
`);
|
|
4238
|
-
}
|
|
4239
|
-
const Gn = ["specs", "tasks"];
|
|
4240
|
-
function Fn(e) {
|
|
4241
|
-
const t = e.indexOf("#");
|
|
4242
|
-
return t >= 0 ? { path: e.slice(0, t), fragment: e.slice(t) } : { path: e, fragment: "" };
|
|
4243
|
-
}
|
|
4244
|
-
function Vn(e, t, s, n) {
|
|
4245
|
-
if (n.size === 0) return { text: e, count: 0 };
|
|
4246
|
-
const o = B.dirname(t), r = B.dirname(s);
|
|
4247
|
-
let i = 0;
|
|
4248
|
-
return { text: e.replace(
|
|
4249
|
-
/\]\(([^)\s]+)([^)]*)\)/g,
|
|
4250
|
-
(c, l, f) => {
|
|
4251
|
-
if (l === "" || /^[a-z][a-z0-9+.-]*:\/\//i.test(l)) return c;
|
|
4252
|
-
const { path: d, fragment: u } = Fn(l);
|
|
4253
|
-
if (d === "") return c;
|
|
4254
|
-
const p = B.normalize(B.join(o, d)), m = n.get(p);
|
|
4255
|
-
if (!m) return c;
|
|
4256
|
-
i++;
|
|
4257
|
-
let E = B.relative(r, m);
|
|
4258
|
-
return E.startsWith(".") || (E = "./" + E), `](${E}${u}${f})`;
|
|
4259
|
-
}
|
|
4260
|
-
), count: i };
|
|
4261
|
-
}
|
|
4262
|
-
function jn(e, t) {
|
|
4263
|
-
const s = [], n = (o) => {
|
|
4264
|
-
let r;
|
|
4265
|
-
try {
|
|
4266
|
-
r = $(o, { withFileTypes: !0 });
|
|
4267
|
-
} catch {
|
|
4268
|
-
return;
|
|
4269
|
-
}
|
|
4270
|
-
for (const i of r) {
|
|
4271
|
-
if (i.name.startsWith(".") || i.isSymbolicLink()) continue;
|
|
4272
|
-
const a = h(o, i.name);
|
|
4273
|
-
i.isDirectory() ? n(a) : i.isFile() && i.name.endsWith(".md") && s.push(a);
|
|
4274
|
-
}
|
|
4275
|
-
};
|
|
4276
|
-
for (const o of t) n(h(e, o));
|
|
4277
|
-
return s.sort();
|
|
4278
|
-
}
|
|
4279
|
-
function Yn(e, t, s) {
|
|
4280
|
-
Fe(w(h(e, s)), { recursive: !0 });
|
|
4281
|
-
try {
|
|
4282
|
-
Ot("git", ["mv", t, s], { cwd: e, stdio: "pipe" });
|
|
4283
|
-
} catch {
|
|
4284
|
-
Tt(h(e, t), h(e, s));
|
|
4285
|
-
}
|
|
4286
|
-
}
|
|
4287
|
-
function Hn(e) {
|
|
4288
|
-
const t = [], s = (n) => {
|
|
4289
|
-
let o;
|
|
4290
|
-
try {
|
|
4291
|
-
o = $(n, { withFileTypes: !0 });
|
|
4292
|
-
} catch {
|
|
4293
|
-
return;
|
|
4294
|
-
}
|
|
4295
|
-
for (const r of o) {
|
|
4296
|
-
const i = h(n, r.name);
|
|
4297
|
-
r.isDirectory() ? s(i) : /\.task-[0-9]+\.md$/.test(r.name) && t.push(i);
|
|
4298
|
-
}
|
|
4299
|
-
};
|
|
4300
|
-
return s(e), t.length === 0;
|
|
4301
|
-
}
|
|
4302
|
-
function Rr(e, t, s) {
|
|
4303
|
-
const n = $n(e, t);
|
|
4304
|
-
if (!n.ok) return n;
|
|
4305
|
-
const o = s ? "" : "would ", r = [], i = new Map(n.moves.map((u) => [u.from, u.to]));
|
|
4306
|
-
if (i.size > 0)
|
|
4307
|
-
for (const u of jn(e, Gn)) {
|
|
4308
|
-
const p = x(e, u), m = i.get(p) ?? p, { text: E, count: S } = Vn(
|
|
4309
|
-
D(u, "utf-8"),
|
|
4310
|
-
p,
|
|
4311
|
-
m,
|
|
4312
|
-
i
|
|
4313
|
-
);
|
|
4314
|
-
S > 0 && (s && q(u, E, "utf-8"), r.push(` ${o}link ${p} — ${S} ссылк(и) на переезжающие тикеты`));
|
|
4315
|
-
}
|
|
4316
|
-
for (const u of n.moves)
|
|
4317
|
-
s && Yn(e, u.from, u.to), r.push(` ${o}mv ${u.from} → ${u.to}`);
|
|
4318
|
-
const a = h("specs", t), c = /* @__PURE__ */ new Map();
|
|
4319
|
-
for (const u of n.units)
|
|
4320
|
-
for (const p of u.tickets) {
|
|
4321
|
-
const m = c.get(p.destDir) ?? [];
|
|
4322
|
-
m.push(p), c.set(p.destDir, m);
|
|
4323
|
-
}
|
|
4324
|
-
for (const u of [...c.keys()].sort()) {
|
|
4325
|
-
if (u === a) continue;
|
|
4326
|
-
const p = c.get(u), m = G(u), E = h(u, `${m}.3-tasks.md`);
|
|
4327
|
-
s && q(h(e, E), Bn(m, p), "utf-8"), r.push(` ${o}index ${E} — ${p.length} тикет(ов)`);
|
|
4328
|
-
}
|
|
4329
|
-
const l = h("specs", t, `${t}.3-tasks.md`);
|
|
4330
|
-
s && q(h(e, l), Un(t, n.units), "utf-8"), r.push(` ${o}index ${l} — сводный трекер scope`);
|
|
4331
|
-
const f = h(e, "tasks", t);
|
|
4332
|
-
R(f) && (s ? Hn(f) ? (Se(f, { recursive: !0, force: !0 }), r.push(` rm -r tasks/${t}/ — пусто, scope переключён на v2`)) : r.push(` ⚠ tasks/${t}/ НЕ удалён — там остались *.task-*.md вне плана`) : r.push(` would rm -r tasks/${t}/ (если не останется тикетов)`));
|
|
4333
|
-
const d = h(e, "tasks");
|
|
4334
|
-
return R(d) && $(d, { withFileTypes: !0 }).filter(
|
|
4335
|
-
(p) => p.name !== "README.md"
|
|
4336
|
-
).length === 0 && (s ? (Se(d, { recursive: !0, force: !0 }), r.push(
|
|
4337
|
-
" rm -r tasks/ — последний scope мигрирован, репо переключён на v2 (FLOW_VERSION)"
|
|
4338
|
-
)) : r.push(" would rm -r tasks/ (последний scope — репо переключится на v2)")), { ok: !0, report: r };
|
|
4339
|
-
}
|
|
4340
|
-
const qn = "ERR_CLI_YAGNI_UNDERUSED", Wn = "ERR_CLI_YAGNI_WAIVER_DECISION_MISSING", Kn = 2;
|
|
4341
|
-
function kr(e) {
|
|
4342
|
-
return e.replace(/^[ \t]*export\s*(?:\*|\{[^}]*\})\s*from\s*['"][^'"]+['"];?[ \t]*$/gm, "");
|
|
4343
|
-
}
|
|
4344
|
-
const Xn = ["MODULE_CONTRACTS", "ENTITY_SURFACES", "PUBLIC_API_SURFACE"];
|
|
4345
|
-
function Zn(e, t) {
|
|
4346
|
-
const s = [];
|
|
4347
|
-
for (const n of t) {
|
|
4348
|
-
const o = new RegExp(
|
|
4349
|
-
"<!--SECTION:" + n + "-->([\\s\\S]*?)<!--/SECTION:" + n + "-->",
|
|
4350
|
-
"g"
|
|
4351
|
-
);
|
|
4352
|
-
for (const r of e.matchAll(o))
|
|
4353
|
-
s.push([r.index, r.index + r[0].length]);
|
|
4354
|
-
}
|
|
4355
|
-
return s;
|
|
4356
|
-
}
|
|
4357
|
-
function vr(e, t) {
|
|
4358
|
-
const n = /<!--SECTION:[A-Z_]+-->/.test(e) ? Zn(e, Xn) : null, o = (I) => !n || n.some(([ht, St]) => I >= ht && I < St), r = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), i = new RegExp("^(#{2,6})[ \\t]+.*`" + r + "`.*$", "gm");
|
|
4359
|
-
let a, c = null;
|
|
4360
|
-
for (; a = i.exec(e); )
|
|
4361
|
-
if (o(a.index)) {
|
|
4362
|
-
c = a;
|
|
4363
|
-
break;
|
|
4364
|
-
}
|
|
4365
|
-
if (!c) return null;
|
|
4366
|
-
const l = c[1].length, f = e.slice(c.index + c[0].length), u = new RegExp("^#{1," + l + "}[ \\t]", "m").exec(f), p = f.slice(0, u ? u.index : void 0), m = /-\s*\*\*Usage Waiver(?:\s*\(external:\s*([^)]+)\))?:\*\*\s*(?:(D-[A-Za-z0-9]+)\s*[—-]\s*)?(.+)/.exec(
|
|
4367
|
-
p
|
|
4368
|
-
);
|
|
4369
|
-
if (!m) return null;
|
|
4370
|
-
const E = (m[3] ?? "").trim();
|
|
4371
|
-
if (!E) return null;
|
|
4372
|
-
const S = m[1]?.trim(), _ = m[2], N = _ ? { decision: _, reason: E } : { reason: E };
|
|
4373
|
-
return S ? { ...N, external: S } : N;
|
|
4374
|
-
}
|
|
4375
|
-
function wr(e, t) {
|
|
4376
|
-
const s = t.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4377
|
-
return new RegExp("^###\\s*" + s + "\\b", "m").test(e);
|
|
4378
|
-
}
|
|
4379
|
-
function Pr(e, t, s, n) {
|
|
4380
|
-
const o = [];
|
|
4381
|
-
for (const r of e) {
|
|
4382
|
-
const i = t.get(r.name) ?? 0;
|
|
4383
|
-
if (i >= Kn) continue;
|
|
4384
|
-
const a = s.get(r.name);
|
|
4385
|
-
if (!a) {
|
|
4386
|
-
o.push({
|
|
4387
|
-
severity: "error",
|
|
4388
|
-
code: qn,
|
|
4389
|
-
file: r.file,
|
|
4390
|
-
symbol: r.name,
|
|
4391
|
-
message: `\`${r.name}\` (${r.kind}) has ${i} usage(s) in production code (< 2) — YAGNI suspect. Fix: remove it, or — if genuinely needed — add \`- **Usage Waiver:** <reason>\` to its contract/surface entry (cite \`D-NNN — <reason>\` instead only when a Decision Log entry actually backs the reason, and then it must exist; for a public API named to an external consumer use the \`(external: <consumer>)\` variant) and log \`yagni ${r.name} ← <reason>\` in the Execution Log.`
|
|
4392
|
-
});
|
|
4393
|
-
continue;
|
|
4394
|
-
}
|
|
4395
|
-
a.decision && !n.has(a.decision) && o.push({
|
|
4396
|
-
severity: "error",
|
|
4397
|
-
code: Wn,
|
|
4398
|
-
file: r.file,
|
|
4399
|
-
symbol: r.name,
|
|
4400
|
-
message: `\`${r.name}\` has a Usage Waiver citing ${a.decision}, but no Decision Log entry with that id was found anywhere in the repo — an unresolvable citation does not gate the finding. Fix: add the ${a.decision} entry to a Decision Log, correct the citation, or drop the citation if the reason does not need one.`
|
|
4401
|
-
});
|
|
4402
|
-
}
|
|
4403
|
-
return o;
|
|
4404
|
-
}
|
|
4405
|
-
export {
|
|
4406
|
-
fr as $,
|
|
4407
|
-
Io as A,
|
|
4408
|
-
Jo as B,
|
|
4409
|
-
ir as C,
|
|
4410
|
-
Fo as D,
|
|
4411
|
-
Ho as E,
|
|
4412
|
-
Vo as F,
|
|
4413
|
-
re as G,
|
|
4414
|
-
Wo as H,
|
|
4415
|
-
qo as I,
|
|
4416
|
-
Xo as J,
|
|
4417
|
-
Zo as K,
|
|
4418
|
-
sr as L,
|
|
4419
|
-
tr as M,
|
|
4420
|
-
g as N,
|
|
4421
|
-
Ks as O,
|
|
4422
|
-
$o as P,
|
|
4423
|
-
cr as Q,
|
|
4424
|
-
dr as R,
|
|
4425
|
-
Or as S,
|
|
4426
|
-
pe as T,
|
|
4427
|
-
Er as U,
|
|
4428
|
-
A as V,
|
|
4429
|
-
Sr as W,
|
|
4430
|
-
gr as X,
|
|
4431
|
-
me as Y,
|
|
4432
|
-
Ir as Z,
|
|
4433
|
-
bo as _,
|
|
4434
|
-
oo as a,
|
|
4435
|
-
pr as a0,
|
|
4436
|
-
P as a1,
|
|
4437
|
-
mr as a2,
|
|
4438
|
-
hr as a3,
|
|
4439
|
-
lr as a4,
|
|
4440
|
-
ys as a5,
|
|
4441
|
-
qe as a6,
|
|
4442
|
-
_o as a7,
|
|
4443
|
-
at as a8,
|
|
4444
|
-
rr as a9,
|
|
4445
|
-
co as aA,
|
|
4446
|
-
Ae as aB,
|
|
4447
|
-
go as aC,
|
|
4448
|
-
So as aD,
|
|
4449
|
-
ho as aE,
|
|
4450
|
-
No as aF,
|
|
4451
|
-
po as aG,
|
|
4452
|
-
Te as aH,
|
|
4453
|
-
ao as aI,
|
|
4454
|
-
$t as aJ,
|
|
4455
|
-
Ft as aK,
|
|
4456
|
-
mo as aL,
|
|
4457
|
-
Oo as aM,
|
|
4458
|
-
ar as aa,
|
|
4459
|
-
Bo as ab,
|
|
4460
|
-
Ko as ac,
|
|
4461
|
-
Rr as ad,
|
|
4462
|
-
Uo as ae,
|
|
4463
|
-
Go as af,
|
|
4464
|
-
Nr as ag,
|
|
4465
|
-
he as ah,
|
|
4466
|
-
ee as ai,
|
|
4467
|
-
Tr as aj,
|
|
4468
|
-
_r as ak,
|
|
4469
|
-
Dr as al,
|
|
4470
|
-
Cr as am,
|
|
4471
|
-
Ar as an,
|
|
4472
|
-
yr as ao,
|
|
4473
|
-
Pr as ap,
|
|
4474
|
-
Ce as aq,
|
|
4475
|
-
kr as ar,
|
|
4476
|
-
vr as as,
|
|
4477
|
-
wr as at,
|
|
4478
|
-
Ao as au,
|
|
4479
|
-
Do as av,
|
|
4480
|
-
Co as aw,
|
|
4481
|
-
yo as ax,
|
|
4482
|
-
ko as ay,
|
|
4483
|
-
Ro as az,
|
|
4484
|
-
lo as b,
|
|
4485
|
-
Cs as c,
|
|
4486
|
-
vo as d,
|
|
4487
|
-
Ws as e,
|
|
4488
|
-
To as f,
|
|
4489
|
-
uo as g,
|
|
4490
|
-
Po as h,
|
|
4491
|
-
wo as i,
|
|
4492
|
-
Lo as j,
|
|
4493
|
-
jo as k,
|
|
4494
|
-
or as l,
|
|
4495
|
-
zo as m,
|
|
4496
|
-
Mo as n,
|
|
4497
|
-
ur as o,
|
|
4498
|
-
io as p,
|
|
4499
|
-
xo as q,
|
|
4500
|
-
Wt as r,
|
|
4501
|
-
ro as s,
|
|
4502
|
-
js as t,
|
|
4503
|
-
fo as u,
|
|
4504
|
-
Yo as v,
|
|
4505
|
-
Qo as w,
|
|
4506
|
-
Eo as x,
|
|
4507
|
-
nr as y,
|
|
4508
|
-
er as z
|
|
4509
|
-
};
|