@tacuchi/agent-workflow-cli 20.26.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/adapters/node-file-system.js +3 -0
- package/dist/adapters/node-file-system.js.map +1 -1
- package/dist/application/design/design-gate-service.js +246 -0
- package/dist/application/design/design-gate-service.js.map +1 -0
- package/dist/application/design/design-graph-service.js +85 -0
- package/dist/application/design/design-graph-service.js.map +1 -0
- package/dist/application/design/design-index-service.js +132 -0
- package/dist/application/design/design-index-service.js.map +1 -0
- package/dist/application/design/design-publish-service.js +488 -0
- package/dist/application/design/design-publish-service.js.map +1 -0
- package/dist/application/design/design-resolver-service.js +165 -0
- package/dist/application/design/design-resolver-service.js.map +1 -0
- package/dist/application/resume-service.js +23 -4
- package/dist/application/resume-service.js.map +1 -1
- package/dist/application/semantic-operation/protocol.js +18 -12
- package/dist/application/semantic-operation/protocol.js.map +1 -1
- package/dist/application/semantic-operation/publish.js +56 -9
- package/dist/application/semantic-operation/publish.js.map +1 -1
- package/dist/application/skills-resolver-service.js +19 -4
- package/dist/application/skills-resolver-service.js.map +1 -1
- package/dist/application/status-service.js +1 -0
- package/dist/application/status-service.js.map +1 -1
- package/dist/application/workline-index-service.js +6 -0
- package/dist/application/workline-index-service.js.map +1 -1
- package/dist/application/workspace-init-service.js +9 -1
- package/dist/application/workspace-init-service.js.map +1 -1
- package/dist/cli/commands/designs.js +110 -0
- package/dist/cli/commands/designs.js.map +1 -0
- package/dist/cli/commands/index.js +2 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/resume.js +8 -0
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/status.js +45 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/help-groups.js +3 -0
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/cli/parser.js +1 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/domain/design/artifact-body.js +223 -0
- package/dist/domain/design/artifact-body.js.map +1 -0
- package/dist/domain/design/artifact.js +606 -0
- package/dist/domain/design/artifact.js.map +1 -0
- package/dist/domain/design/baseline.js +359 -0
- package/dist/domain/design/baseline.js.map +1 -0
- package/dist/domain/design/capability.js +51 -0
- package/dist/domain/design/capability.js.map +1 -0
- package/dist/domain/design/closure.js +161 -0
- package/dist/domain/design/closure.js.map +1 -0
- package/dist/domain/design/governance.js +298 -0
- package/dist/domain/design/governance.js.map +1 -0
- package/dist/domain/design/identity.js +145 -0
- package/dist/domain/design/identity.js.map +1 -0
- package/dist/domain/design/manifest.js +552 -0
- package/dist/domain/design/manifest.js.map +1 -0
- package/dist/domain/design/maturity.js +234 -0
- package/dist/domain/design/maturity.js.map +1 -0
- package/dist/domain/design/naming.js +117 -0
- package/dist/domain/design/naming.js.map +1 -0
- package/dist/domain/design/projections.js +165 -0
- package/dist/domain/design/projections.js.map +1 -0
- package/dist/domain/design/reference.js +234 -0
- package/dist/domain/design/reference.js.map +1 -0
- package/dist/domain/design/retired.js +61 -0
- package/dist/domain/design/retired.js.map +1 -0
- package/dist/domain/design/revision.js +219 -0
- package/dist/domain/design/revision.js.map +1 -0
- package/dist/domain/design/secrets.js +86 -0
- package/dist/domain/design/secrets.js.map +1 -0
- package/dist/domain/design/validation.js +75 -0
- package/dist/domain/design/validation.js.map +1 -0
- package/dist/domain/design/yaml-subset.js +390 -0
- package/dist/domain/design/yaml-subset.js.map +1 -0
- package/dist/domain/safe-path.js +23 -0
- package/dist/domain/safe-path.js.map +1 -0
- package/dist/domain/skills.js +27 -3
- package/dist/domain/skills.js.map +1 -1
- package/package.json +1 -1
- package/skills/w/SKILL.md +10 -8
- package/skills/w/artifacts/README.md +3 -4
- package/skills/w/artifacts/artifacts-core/CHECKPOINT.md +2 -2
- package/skills/w/commands/plan-exec.md +11 -5
- package/skills/w/commands/plan-new.md +6 -2
- package/skills/w/commands/plan-refine.md +6 -3
- package/skills/w/commands/quick.md +2 -2
- package/skills/w/commands/spec-new.md +2 -2
- package/skills/w/commands/spec-refine.md +1 -0
- package/skills/w/context/MANIFEST.json +19 -8
- package/skills/w/loops/CHASSIS.md +2 -2
- package/skills/w/loops/CODE-POLICIES.md +22 -7
- package/skills/w/loops/README.md +1 -1
- package/skills/w/loops/plan-exec-loop/LOOP.md +127 -61
- package/skills/w/loops/plan-new-loop/LOOP.md +66 -70
- package/skills/w/loops/plan-refine-loop/LOOP.md +15 -7
- package/skills/w/loops/spec-refine-loop/LOOP.md +14 -12
- package/skills/w/modules/DESIGN-REFERENCES.md +109 -0
- package/skills/w/modules/PERSIST-ROUTING.md +1 -0
- package/skills/w/modules/PLAN-EXECUTION-BATCHES.md +84 -0
- package/skills/w/roles/README.md +7 -5
- package/skills/w/roles/design/ROLE.md +217 -0
- package/skills/w/schemas/design/design-baseline.v1.schema.json +71 -0
- package/skills/w/schemas/design/design-manifest.v1.schema.json +322 -0
- package/skills/w/schemas/design/design-review.v1.schema.json +72 -0
- package/skills/w/schemas/design/design-revocation.v1.schema.json +54 -0
- package/skills/w/schemas/design/ui-flow.v1.schema.json +211 -0
- package/skills/w/schemas/design/ui-screen.v1.schema.json +214 -0
- package/skills/w/artifacts/artifacts-design/SPEC.md +0 -42
- package/skills/w/modules/PLAN-DESIGN-SPECS.md +0 -12
- package/skills/w/modules/PLAN-REFINE-DESIGN-SPECS.md +0 -7
- package/skills/w/roles/ui-spec/ROLE.md +0 -147
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { checkSafeRelativePath } from "../safe-path.js";
|
|
2
|
+
import { isDigest, parseArtifactRef, parseBaselineRef, } from "./identity.js";
|
|
3
|
+
const SPEC_SECTION = "## Design references";
|
|
4
|
+
/**
|
|
5
|
+
* Forms that look like a reference and are not. Each one is recognized on
|
|
6
|
+
* purpose: telling an author "'latest' no fija una revisión" is worth far more
|
|
7
|
+
* than telling them the value did not match a pattern.
|
|
8
|
+
*/
|
|
9
|
+
const APPROXIMATE = [
|
|
10
|
+
{
|
|
11
|
+
test: /@latest\b/i,
|
|
12
|
+
why: "'latest' no fija ninguna revisión: mañana señala otra cosa",
|
|
13
|
+
action: "escribí la revisión exacta, por ejemplo DES-001@r4",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
test: /@(head|current|latest|último|ultimo)\b/i,
|
|
17
|
+
why: "es un alias móvil, no una revisión",
|
|
18
|
+
action: "escribí la revisión exacta, por ejemplo DES-001@r4",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
test: /^docs\/designs\//,
|
|
22
|
+
why: "es un path, y el path es un hint de ubicación, nunca la identidad",
|
|
23
|
+
action: "referenciá el package por su id y revisión, por ejemplo DES-001@r4",
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
test: /^\d{3}-design-/,
|
|
27
|
+
why: "es el slug de una carpeta, y la identidad no deriva del slug",
|
|
28
|
+
action: "referenciá el package por su id y revisión, por ejemplo DES-001@r4",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
test: /^DES-\d{3,}$/,
|
|
32
|
+
why: "nombra el package pero no fija la revisión",
|
|
33
|
+
action: "agregá la revisión, por ejemplo DES-001@r4",
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
function approximate(value, artifact, field) {
|
|
37
|
+
for (const form of APPROXIMATE) {
|
|
38
|
+
if (!form.test.test(value))
|
|
39
|
+
continue;
|
|
40
|
+
return {
|
|
41
|
+
code: "DESIGN_REFERENCE_APPROXIMATE",
|
|
42
|
+
artifact,
|
|
43
|
+
message: `${field}: '${value}' ${form.why}`,
|
|
44
|
+
action: form.action,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Read the `## Design references` section of a spec. Absent section = no
|
|
51
|
+
* references, which is the normal state of a spec without UI — not an error.
|
|
52
|
+
*/
|
|
53
|
+
export function parseSpecDesignReferences(markdown, artifact) {
|
|
54
|
+
const references = [];
|
|
55
|
+
const failures = [];
|
|
56
|
+
const section = sectionBody(markdown, SPEC_SECTION);
|
|
57
|
+
if (section === null)
|
|
58
|
+
return { references, failures };
|
|
59
|
+
const blocks = splitBlocks(section);
|
|
60
|
+
// The section exists and says nothing parseable. Returning silently would let
|
|
61
|
+
// a spec claim it references a design while referencing none — the failure
|
|
62
|
+
// mode this whole contract removes.
|
|
63
|
+
if (blocks.length === 0) {
|
|
64
|
+
if (section.trim().length > 0) {
|
|
65
|
+
failures.push({
|
|
66
|
+
code: "DESIGN_REFERENCE_INCOMPLETE",
|
|
67
|
+
artifact,
|
|
68
|
+
message: `'${SPEC_SECTION}' tiene contenido y ninguna referencia legible`,
|
|
69
|
+
action: "escribí un bloque con 'package:', 'baseline_hint:' y 'digest:', o quitá la sección",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return { references, failures };
|
|
73
|
+
}
|
|
74
|
+
for (const block of blocks) {
|
|
75
|
+
const parsed = readSpecReference(block, artifact);
|
|
76
|
+
if ("code" in parsed)
|
|
77
|
+
failures.push(parsed);
|
|
78
|
+
else
|
|
79
|
+
references.push(parsed);
|
|
80
|
+
}
|
|
81
|
+
return { references, failures };
|
|
82
|
+
}
|
|
83
|
+
function readSpecReference(fields, artifact) {
|
|
84
|
+
const raw = fields.get("package") ?? "";
|
|
85
|
+
const missing = (field) => ({
|
|
86
|
+
code: "DESIGN_REFERENCE_INCOMPLETE",
|
|
87
|
+
artifact,
|
|
88
|
+
message: `la referencia de diseño no declara '${field}'`,
|
|
89
|
+
action: "una referencia publicada lleva package, baseline_hint y digest",
|
|
90
|
+
});
|
|
91
|
+
if (raw.length === 0)
|
|
92
|
+
return missing("package");
|
|
93
|
+
const near = approximate(raw, artifact, "package");
|
|
94
|
+
if (near !== null)
|
|
95
|
+
return near;
|
|
96
|
+
const baseline = parseBaselineRef(raw);
|
|
97
|
+
if (baseline === null) {
|
|
98
|
+
return {
|
|
99
|
+
code: "DESIGN_REFERENCE_APPROXIMATE",
|
|
100
|
+
artifact,
|
|
101
|
+
message: `package: '${raw}' no es una referencia exacta`,
|
|
102
|
+
action: "usá la forma DES-NNN@rN",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const hint = fields.get("baseline_hint") ?? "";
|
|
106
|
+
if (hint.length === 0)
|
|
107
|
+
return missing("baseline_hint");
|
|
108
|
+
const safe = checkSafeRelativePath(hint);
|
|
109
|
+
if (!safe.ok) {
|
|
110
|
+
return {
|
|
111
|
+
code: "DESIGN_PATH_UNSAFE",
|
|
112
|
+
artifact,
|
|
113
|
+
message: `baseline_hint: '${hint}' ${safe.why}`,
|
|
114
|
+
action: "el hint es un path relativo al workspace, bajo docs/designs/",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const digest = fields.get("digest") ?? "";
|
|
118
|
+
if (digest.length === 0)
|
|
119
|
+
return missing("digest");
|
|
120
|
+
if (!isDigest(digest)) {
|
|
121
|
+
return {
|
|
122
|
+
code: "DESIGN_REFERENCE_INCOMPLETE",
|
|
123
|
+
artifact,
|
|
124
|
+
message: `digest: '${digest}' no es 'sha256:' + 64 hex`,
|
|
125
|
+
action: "copiá el digest del baseline que estás fijando",
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return { baseline, baseline_hint: safe.path, digest };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The shape of a task reference, matched LOOSELY on purpose: anything that
|
|
132
|
+
* looks like the two-part form is captured here and then judged. Matching only
|
|
133
|
+
* the canonical form would make `DES-001@latest / SCR-002@r2` invisible — and a
|
|
134
|
+
* reference that is silently ignored is worse than one that is rejected.
|
|
135
|
+
*/
|
|
136
|
+
const TASK_REFERENCE_SHAPE = /(DES-[0-9]+(?:@[A-Za-z0-9]+)?)\s*\/\s*((?:FLW|SCR|RUL|TOK|VIS)-[0-9]+(?:@[A-Za-z0-9]+)?(?:#[A-Za-z0-9][A-Za-z0-9_-]*)?)/g;
|
|
137
|
+
/**
|
|
138
|
+
* Read the design references a plan phase or task declares. Only the exact
|
|
139
|
+
* two-part form resolves; anything shaped like one but written approximately is
|
|
140
|
+
* REPORTED, never skipped.
|
|
141
|
+
*/
|
|
142
|
+
export function parseTaskDesignReferences(text, artifact) {
|
|
143
|
+
const references = [];
|
|
144
|
+
const failures = [];
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
const pinned = new Set();
|
|
147
|
+
for (const match of text.matchAll(TASK_REFERENCE_SHAPE)) {
|
|
148
|
+
const raw = match[0];
|
|
149
|
+
if (seen.has(raw))
|
|
150
|
+
continue;
|
|
151
|
+
seen.add(raw);
|
|
152
|
+
const left = match[1];
|
|
153
|
+
const right = match[2];
|
|
154
|
+
const baseline = parseBaselineRef(left);
|
|
155
|
+
if (baseline === null) {
|
|
156
|
+
const near = approximate(left, artifact, `referencia de tarea '${raw}'`);
|
|
157
|
+
failures.push(near ?? malformed(raw, left, artifact));
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const artifactRef = parseArtifactRef(`${baseline.package}/${right}`);
|
|
161
|
+
if (artifactRef === null) {
|
|
162
|
+
failures.push(malformed(raw, right, artifact));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
pinned.add(baseline.package);
|
|
166
|
+
references.push({ baseline, artifact: artifactRef, raw });
|
|
167
|
+
}
|
|
168
|
+
// A package named without being pinned is the exact failure this contract
|
|
169
|
+
// removes — unless the same text pins it, where the bare mention is just prose.
|
|
170
|
+
for (const match of text.matchAll(/DES-[0-9]+(?:@[A-Za-z]+)?(?![@/\w-])/g)) {
|
|
171
|
+
const value = match[0];
|
|
172
|
+
const id = value.split("@")[0];
|
|
173
|
+
if (pinned.has(id))
|
|
174
|
+
continue;
|
|
175
|
+
const near = approximate(value, artifact, "referencia de tarea");
|
|
176
|
+
failures.push(near ?? malformed(value, value, artifact));
|
|
177
|
+
}
|
|
178
|
+
return { references, failures };
|
|
179
|
+
}
|
|
180
|
+
function malformed(raw, part, artifact) {
|
|
181
|
+
return {
|
|
182
|
+
code: "DESIGN_REFERENCE_APPROXIMATE",
|
|
183
|
+
artifact,
|
|
184
|
+
message: `referencia de tarea '${raw}': '${part}' no es una referencia exacta`,
|
|
185
|
+
action: "usá la forma DES-NNN@rN / XXX-NNN@rN[#estado], con la revisión sin ceros de más",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/** Body of a `## Heading` section, or null when the section is absent. */
|
|
189
|
+
function sectionBody(markdown, heading) {
|
|
190
|
+
const lines = markdown.split(/\r?\n/);
|
|
191
|
+
const start = lines.findIndex((l) => l.trim() === heading);
|
|
192
|
+
if (start === -1)
|
|
193
|
+
return null;
|
|
194
|
+
const rest = lines.slice(start + 1);
|
|
195
|
+
const end = rest.findIndex((l) => /^##\s/.test(l));
|
|
196
|
+
return (end === -1 ? rest : rest.slice(0, end)).join("\n");
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* One block per reference, and a block STARTS at `package:`.
|
|
200
|
+
*
|
|
201
|
+
* Two reasons. A contiguous list of three-line references is the natural way to
|
|
202
|
+
* write several, and splitting only on blank lines silently kept the last. And
|
|
203
|
+
* prose in the section — "nota: ver el paquete de abajo" — parses as a
|
|
204
|
+
* `key: value` line, so anything before the first `package:` is commentary and
|
|
205
|
+
* must not become a half-built reference nobody wrote.
|
|
206
|
+
*/
|
|
207
|
+
function splitBlocks(section) {
|
|
208
|
+
const blocks = [];
|
|
209
|
+
let current = null;
|
|
210
|
+
for (const line of section.split(/\r?\n/)) {
|
|
211
|
+
const match = /^\s*[-*]?\s*([a-z_]+):\s*(.+?)\s*$/.exec(line);
|
|
212
|
+
if (match === null) {
|
|
213
|
+
if (line.trim().length === 0)
|
|
214
|
+
current = null;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
const key = match[1];
|
|
218
|
+
const value = match[2].replace(/^`|`$/g, "");
|
|
219
|
+
if (key === "package") {
|
|
220
|
+
current = new Map();
|
|
221
|
+
blocks.push(current);
|
|
222
|
+
}
|
|
223
|
+
if (current === null)
|
|
224
|
+
continue; // prosa antes de la primera referencia
|
|
225
|
+
if (current.has(key)) {
|
|
226
|
+
current = new Map([[key, value]]);
|
|
227
|
+
blocks.push(current);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
current.set(key, value);
|
|
231
|
+
}
|
|
232
|
+
return blocks;
|
|
233
|
+
}
|
|
234
|
+
//# sourceMappingURL=reference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reference.js","sourceRoot":"","sources":["../../../src/domain/design/reference.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The retired design path, reported instead of read (AC-COMP-01/02).
|
|
3
|
+
*
|
|
4
|
+
* A document that still carries the legacy render is not an input this contract
|
|
5
|
+
* accepts and not evidence any gate honors. It is REPORTED — never parsed as a
|
|
6
|
+
* contract, never imported, converted, migrated on touch or bulk-migrated. The
|
|
7
|
+
* asymmetry is deliberate: reading it would make the old format a second source
|
|
8
|
+
* of truth, and the day the two disagree nothing could say which one governs.
|
|
9
|
+
*
|
|
10
|
+
* Retirement is not destruction. Nothing here writes, moves or rewrites a file:
|
|
11
|
+
* a `## UI spec` section written last year stays exactly where it is, byte for
|
|
12
|
+
* byte. What changes is that Workline answers `retired/unsupported` and asks for
|
|
13
|
+
* the result to be recreated over the UI Design Package.
|
|
14
|
+
*/
|
|
15
|
+
export const RETIRED_CODE = "DESIGN_RETIRED_UNSUPPORTED";
|
|
16
|
+
const RECREATE = "recreá ese diseño sobre el UI Design Package: no hay importador, conversión ni migración";
|
|
17
|
+
/**
|
|
18
|
+
* Each form is recognized on purpose rather than by one loose pattern: telling
|
|
19
|
+
* an author WHICH retired surface they presented is the difference between a
|
|
20
|
+
* diagnostic and a rejection.
|
|
21
|
+
*/
|
|
22
|
+
const FORMS = [
|
|
23
|
+
{
|
|
24
|
+
// A heading of its own, so prose that merely mentions the old name is not a
|
|
25
|
+
// false positive — a plan explaining the retirement is not carrying one.
|
|
26
|
+
test: /^\s*#{2,}\s+UI spec\s*$/m,
|
|
27
|
+
what: "una sección '## UI spec'",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
// A real filename, so the literal placeholder `NNN-SPEC-<SLUG>.md` that the
|
|
31
|
+
// doctrine uses to NAME the retired shape is not itself an offender.
|
|
32
|
+
test: /\b\d{3}-SPEC-[A-Z0-9-]+\.md\b/,
|
|
33
|
+
what: "un design SPEC de sesión (NNN-SPEC-<SLUG>.md)",
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
/**
|
|
37
|
+
* Legacy surfaces this text presents. Empty for a document that carries none —
|
|
38
|
+
* which is every current document, so the check costs nothing to keep on.
|
|
39
|
+
*/
|
|
40
|
+
export function reportRetiredDesign(text, artifact) {
|
|
41
|
+
return FORMS.filter((form) => form.test.test(text)).map((form) => ({
|
|
42
|
+
code: RETIRED_CODE,
|
|
43
|
+
artifact,
|
|
44
|
+
message: `'${artifact}' presenta ${form.what}: es un camino retirado y no se lee como contrato ni cuenta como evidencia`,
|
|
45
|
+
action: RECREATE,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A binding or invocation naming a retired identity is refused one layer up, by
|
|
50
|
+
* `RETIRED_SKILL_IDENTITIES` in the skills resolver: the line is ignored with a
|
|
51
|
+
* warning and the role keeps its built-in default, so the capability is never
|
|
52
|
+
* left mute. Nothing is duplicated here — one rule, one home.
|
|
53
|
+
*
|
|
54
|
+
* An **output of `ui-spec-generator`** is retired too, and is stated in the
|
|
55
|
+
* doctrine rather than pattern-matched: that repository is outside this topology
|
|
56
|
+
* and its output format is not ours to recognize. A detector keyed on the tool's
|
|
57
|
+
* NAME would fire on every document that explains the retirement, which is the
|
|
58
|
+
* opposite of a useful guard. What a document can actually carry — a `## UI spec`
|
|
59
|
+
* section or a session design SPEC — is what the forms above catch.
|
|
60
|
+
*/
|
|
61
|
+
//# sourceMappingURL=retired.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retired.js","sourceRoot":"","sources":["../../../src/domain/design/retired.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,4BAA4B,CAAC;AAEzD,MAAM,QAAQ,GACZ,0FAA0F,CAAC;AAQ7F;;;;GAIG;AACH,MAAM,KAAK,GAA2B;IACpC;QACE,4EAA4E;QAC5E,yEAAyE;QACzE,IAAI,EAAE,0BAA0B;QAChC,IAAI,EAAE,0BAA0B;KACjC;IACD;QACE,4EAA4E;QAC5E,qEAAqE;QACrE,IAAI,EAAE,+BAA+B;QACrC,IAAI,EAAE,+CAA+C;KACtD;CACF,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY,EAAE,QAAgB;IAChE,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,IAAI,EAAE,YAAY;QAClB,QAAQ;QACR,OAAO,EAAE,IAAI,QAAQ,cAAc,IAAI,CAAC,IAAI,4EAA4E;QACxH,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;GAYG"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { ARTIFACT_PREFIX, isArtifactId } from "./identity.js";
|
|
2
|
+
import { NAMING, revisionSegment } from "./naming.js";
|
|
3
|
+
/**
|
|
4
|
+
* Changing published normative content.
|
|
5
|
+
*
|
|
6
|
+
* The rule has one shape: you never edit a published revision, you mint the
|
|
7
|
+
* next one. Everything else follows — the previous file keeps its name, every
|
|
8
|
+
* reference to it keeps resolving, and `supersedes` records the succession
|
|
9
|
+
* without rewriting what came before.
|
|
10
|
+
*
|
|
11
|
+
* This module answers the two questions that operation asks: *what is the next
|
|
12
|
+
* revision and where does it go*, and afterwards *did anything published
|
|
13
|
+
* disappear*. Writing the files is publication's job (F5); this is the arithmetic
|
|
14
|
+
* and the proof.
|
|
15
|
+
*/
|
|
16
|
+
/** Where each artifact kind is catalogued. One rule, one place. */
|
|
17
|
+
export const KIND_LIST = {
|
|
18
|
+
flow: "flows",
|
|
19
|
+
screen: "screens",
|
|
20
|
+
rule: "rules",
|
|
21
|
+
token: "tokens",
|
|
22
|
+
rendition: "renditions",
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* The next revision of `id`, or the first one when the artifact is new.
|
|
26
|
+
* `slug` only shapes the file name; identity never derives from it.
|
|
27
|
+
*/
|
|
28
|
+
export function planNextRevision(manifest, kind, id, slug, artifact) {
|
|
29
|
+
// The id shapes the PATH this function returns, so validating the cosmetic
|
|
30
|
+
// slug and not the identity would let `../../evil` out of here as a path.
|
|
31
|
+
if (!isArtifactId(id, kind)) {
|
|
32
|
+
return {
|
|
33
|
+
ok: false,
|
|
34
|
+
failure: {
|
|
35
|
+
code: "DESIGN_FIELD_INVALID",
|
|
36
|
+
artifact,
|
|
37
|
+
message: `'${id}' no es un id de ${kind}`,
|
|
38
|
+
action: `usá ${ARTIFACT_PREFIX[kind]}-NNN`,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(slug)) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
failure: {
|
|
46
|
+
code: "DESIGN_FIELD_INVALID",
|
|
47
|
+
artifact,
|
|
48
|
+
message: `'${slug}' no es un slug: solo minúsculas, números y guiones`,
|
|
49
|
+
action: "usá algo como 'alta-miembro'",
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const existing = manifest.catalog[KIND_LIST[kind]].filter((e) => e.id === id);
|
|
54
|
+
const highest = existing.reduce((max, e) => Math.max(max, e.revision), 0);
|
|
55
|
+
const revision = highest + 1;
|
|
56
|
+
const rule = NAMING[kind];
|
|
57
|
+
const path = `${rule.dir}/${id}-${revisionSegment(revision)}-${slug}${rule.suffix}`;
|
|
58
|
+
// No collision check here on purpose: `revision` is always max+1 and the
|
|
59
|
+
// revision is part of the path, so the new file CANNOT be one the catalog
|
|
60
|
+
// already holds. The impossibility is structural — an `if` would only be
|
|
61
|
+
// dead code pretending to be a safeguard.
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
value: {
|
|
65
|
+
id,
|
|
66
|
+
revision,
|
|
67
|
+
path,
|
|
68
|
+
supersedes: highest === 0 ? null : `${manifest.id}/${id}@r${highest}`,
|
|
69
|
+
preserves: existing.map((e) => e.path).sort(),
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* What a publication lost, comparing the manifest before and after.
|
|
75
|
+
*
|
|
76
|
+
* A published revision may gain nothing and lose nothing: not its path, and —
|
|
77
|
+
* for a baseline — not its digest. Anything else means a reference somebody
|
|
78
|
+
* already wrote now resolves to different bytes, which is the one thing the
|
|
79
|
+
* whole revision model exists to prevent.
|
|
80
|
+
*/
|
|
81
|
+
export function findHistoryLoss(before, after) {
|
|
82
|
+
return [
|
|
83
|
+
...artifactLosses(before, after),
|
|
84
|
+
...baselineLosses(before, after),
|
|
85
|
+
...addressedLosses(before, after),
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Assets and governance records are addressed by content and by id: an asset
|
|
90
|
+
* that leaves the catalog breaks whatever depended on that digest, and a review
|
|
91
|
+
* that changes its target changes what it approved.
|
|
92
|
+
*/
|
|
93
|
+
function artifactLosses(before, after) {
|
|
94
|
+
const losses = [];
|
|
95
|
+
const current = publishedArtifacts(after);
|
|
96
|
+
for (const [ref, entry] of publishedArtifacts(before)) {
|
|
97
|
+
const now = current.get(ref);
|
|
98
|
+
if (now === undefined) {
|
|
99
|
+
losses.push({ ref, kind: "removed", before: entry.path, after: null });
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (now.path !== entry.path) {
|
|
103
|
+
losses.push({ ref, kind: "moved", before: entry.path, after: now.path });
|
|
104
|
+
}
|
|
105
|
+
// Promoting a published revision in place is a mutation of what someone
|
|
106
|
+
// already pinned: `outline` and `handoff` mean different things to a gate,
|
|
107
|
+
// so the promotion is the NEXT revision, never an edit of this one.
|
|
108
|
+
for (const field of ["maturity", "supersedes"]) {
|
|
109
|
+
const was = entry[field] ?? null;
|
|
110
|
+
const is = now[field] ?? null;
|
|
111
|
+
if (was === is)
|
|
112
|
+
continue;
|
|
113
|
+
losses.push({
|
|
114
|
+
ref,
|
|
115
|
+
kind: "rewritten",
|
|
116
|
+
field,
|
|
117
|
+
before: String(was),
|
|
118
|
+
after: is === null ? null : String(is),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return losses;
|
|
123
|
+
}
|
|
124
|
+
function baselineLosses(before, after) {
|
|
125
|
+
const losses = [];
|
|
126
|
+
// Changing a package's identity orphans every reference ever written to it —
|
|
127
|
+
// the largest history loss there is, and the one nobody would think to look for.
|
|
128
|
+
if (before.id !== after.id) {
|
|
129
|
+
losses.push({
|
|
130
|
+
ref: before.id,
|
|
131
|
+
kind: "rewritten",
|
|
132
|
+
field: "id",
|
|
133
|
+
before: before.id,
|
|
134
|
+
after: after.id,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
for (const baseline of before.baselines) {
|
|
138
|
+
const ref = `${before.id}@r${baseline.revision}`;
|
|
139
|
+
const now = after.baselines.find((b) => b.revision === baseline.revision);
|
|
140
|
+
if (now === undefined) {
|
|
141
|
+
losses.push({ ref, kind: "removed", before: baseline.path, after: null });
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (now.path !== baseline.path) {
|
|
145
|
+
losses.push({ ref, kind: "moved", before: baseline.path, after: now.path });
|
|
146
|
+
}
|
|
147
|
+
if (now.digest !== baseline.digest) {
|
|
148
|
+
losses.push({ ref, kind: "rewritten", before: baseline.digest, after: now.digest });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return losses;
|
|
152
|
+
}
|
|
153
|
+
function addressedLosses(before, after) {
|
|
154
|
+
return [...assetLosses(before, after), ...governanceLosses(before, after)];
|
|
155
|
+
}
|
|
156
|
+
/** An asset that leaves the catalog breaks whatever depended on that digest. */
|
|
157
|
+
function assetLosses(before, after) {
|
|
158
|
+
const losses = [];
|
|
159
|
+
for (const asset of before.catalog.assets) {
|
|
160
|
+
const now = after.catalog.assets.find((a) => a.digest === asset.digest);
|
|
161
|
+
if (now === undefined) {
|
|
162
|
+
losses.push({ ref: asset.digest, kind: "removed", before: asset.path, after: null });
|
|
163
|
+
}
|
|
164
|
+
else if (now.path !== asset.path) {
|
|
165
|
+
losses.push({ ref: asset.digest, kind: "moved", before: asset.path, after: now.path });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return losses;
|
|
169
|
+
}
|
|
170
|
+
/** A review that changes its target changes what it approved. */
|
|
171
|
+
function governanceLosses(before, after) {
|
|
172
|
+
const losses = [];
|
|
173
|
+
for (const key of ["reviews", "revocations"]) {
|
|
174
|
+
for (const record of before.governance[key]) {
|
|
175
|
+
const now = after.governance[key].find((g) => g.id === record.id);
|
|
176
|
+
if (now === undefined) {
|
|
177
|
+
losses.push({ ref: record.id, kind: "removed", before: record.path, after: null });
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
for (const field of ["digest", "target"]) {
|
|
181
|
+
if (now[field] === record[field])
|
|
182
|
+
continue;
|
|
183
|
+
losses.push({
|
|
184
|
+
ref: record.id,
|
|
185
|
+
kind: "rewritten",
|
|
186
|
+
field,
|
|
187
|
+
before: record[field],
|
|
188
|
+
after: now[field],
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return losses;
|
|
194
|
+
}
|
|
195
|
+
/** Turn a loss into the diagnostic a publication should refuse with. */
|
|
196
|
+
export function historyLossFailure(loss, artifact) {
|
|
197
|
+
const what = loss.field ?? "digest";
|
|
198
|
+
const message = {
|
|
199
|
+
removed: `${loss.ref} estaba publicada y ya no está en el catálogo`,
|
|
200
|
+
moved: `${loss.ref} cambió de path: '${loss.before}' → '${loss.after}'`,
|
|
201
|
+
rewritten: `${loss.ref} cambió de ${what}: '${loss.before}' → '${String(loss.after)}'`,
|
|
202
|
+
}[loss.kind];
|
|
203
|
+
return {
|
|
204
|
+
code: "DESIGN_HISTORY_MUTATED",
|
|
205
|
+
artifact,
|
|
206
|
+
message,
|
|
207
|
+
action: "una revisión publicada no se toca: creá la siguiente y dejá la anterior como está",
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function publishedArtifacts(manifest) {
|
|
211
|
+
const out = new Map();
|
|
212
|
+
for (const key of Object.values(KIND_LIST)) {
|
|
213
|
+
for (const entry of manifest.catalog[key]) {
|
|
214
|
+
out.set(`${manifest.id}/${entry.id}@r${entry.revision}`, entry);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return out;
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=revision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"revision.js","sourceRoot":"","sources":["../../../src/domain/design/revision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credentials never enter a package.
|
|
3
|
+
*
|
|
4
|
+
* A package records an ACCESS CLASSIFICATION — `private`, `team`, `public` —
|
|
5
|
+
* and never the thing that grants access. The distinction matters because a
|
|
6
|
+
* package is a durable, committed, shareable dossier: a token that lands in one
|
|
7
|
+
* is a token in every clone of the repo, forever, and rotating it does not
|
|
8
|
+
* remove it from history.
|
|
9
|
+
*
|
|
10
|
+
* The repo's `code-scan-service` greps source trees with regexes; this walks an
|
|
11
|
+
* already-parsed document instead, which is a different shape — the values are
|
|
12
|
+
* structured, so the key is as much a signal as the value.
|
|
13
|
+
*/
|
|
14
|
+
/** Keys that hold a secret by definition. Their presence is the finding. */
|
|
15
|
+
const SECRET_KEYS = /^(password|passwd|pwd|secret|client_secret|token|access_token|refresh_token|id_token|api_?key|apikey|private_?key|secret_?key|access_?key|credential|credentials|auth_?token|bearer|session_?id|cookie)$/i;
|
|
16
|
+
/** Shapes that are a credential wherever they appear, whatever the key. */
|
|
17
|
+
const SECRET_VALUES = [
|
|
18
|
+
{ test: /-----BEGIN [A-Z ]*PRIVATE KEY-----/, what: "una clave privada PEM" },
|
|
19
|
+
{ test: /\bgh[pousr]_[A-Za-z0-9]{16,}\b/, what: "un token de GitHub" },
|
|
20
|
+
{ test: /\bgithub_pat_[A-Za-z0-9_]{20,}\b/, what: "un token de GitHub" },
|
|
21
|
+
{ test: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, what: "un token de Slack" },
|
|
22
|
+
{ test: /\bAKIA[0-9A-Z]{16}\b/, what: "una access key de AWS" },
|
|
23
|
+
{ test: /\bsk-[A-Za-z0-9]{20,}\b/, what: "una API key" },
|
|
24
|
+
// Los proveedores que la spec declara obligatorios para v1 tienen su propio
|
|
25
|
+
// prefijo, y no reconocerlos dejaba a AC-SEC-02 sin cubrir justo donde importa.
|
|
26
|
+
{ test: /\bfigd_[A-Za-z0-9_-]{20,}\b/, what: "un token de Figma" },
|
|
27
|
+
{ test: /\bfigu_[A-Za-z0-9_-]{20,}\b/, what: "un token de Figma" },
|
|
28
|
+
{ test: /\bAIza[0-9A-Za-z_-]{35}\b/, what: "una API key de Google" },
|
|
29
|
+
{ test: /\bya29\.[0-9A-Za-z_-]{20,}\b/, what: "un token OAuth de Google" },
|
|
30
|
+
{ test: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/, what: "una API key de Anthropic" },
|
|
31
|
+
{ test: /\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/, what: "un JWT" },
|
|
32
|
+
{ test: /\b(?:Bearer|Basic)\s+[A-Za-z0-9._~+/=-]{16,}/, what: "un header de autorización" },
|
|
33
|
+
// `https://user:password@host` — the one credential form that hides in a URL.
|
|
34
|
+
{
|
|
35
|
+
test: /\b[a-z][a-z0-9+.-]*:\/\/[^/\s:@]+:[^/\s@]+@/i,
|
|
36
|
+
what: "una URL con usuario y contraseña",
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
/**
|
|
40
|
+
* Walk a parsed document for credentials. Returns every finding: a document
|
|
41
|
+
* with two leaked tokens should report two, not stop at the first.
|
|
42
|
+
*/
|
|
43
|
+
export function findSecrets(document, at = "") {
|
|
44
|
+
if (typeof document === "string") {
|
|
45
|
+
const shape = SECRET_VALUES.find((s) => s.test.test(document));
|
|
46
|
+
return shape === undefined ? [] : [{ at, what: shape.what }];
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(document)) {
|
|
49
|
+
return document.flatMap((v, i) => findSecrets(v, `${at}[${i}]`));
|
|
50
|
+
}
|
|
51
|
+
if (typeof document !== "object" || document === null)
|
|
52
|
+
return [];
|
|
53
|
+
const findings = [];
|
|
54
|
+
for (const [key, value] of Object.entries(document)) {
|
|
55
|
+
const path = at.length === 0 ? key : `${at}.${key}`;
|
|
56
|
+
// A key named `token` with ANY content is the finding — there is no value of
|
|
57
|
+
// that field a package may carry, and wrapping it in an object
|
|
58
|
+
// (`token: { value: … }`) does not make it a different field.
|
|
59
|
+
if (SECRET_KEYS.test(key) && !isEmpty(value)) {
|
|
60
|
+
findings.push({ at: path, what: `un campo '${key}'` });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
findings.push(...findSecrets(value, path));
|
|
64
|
+
}
|
|
65
|
+
return findings;
|
|
66
|
+
}
|
|
67
|
+
export function secretFailures(document, artifact) {
|
|
68
|
+
return findSecrets(document).map((finding) => ({
|
|
69
|
+
code: "DESIGN_SECRET_PRESENT",
|
|
70
|
+
artifact,
|
|
71
|
+
message: `'${finding.at}' parece contener ${finding.what}`,
|
|
72
|
+
action: "un package registra la CLASIFICACIÓN de acceso, nunca la credencial: quitala y rotala, ya está en el historial de git",
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
function isEmpty(value) {
|
|
76
|
+
if (value === null || value === undefined)
|
|
77
|
+
return true;
|
|
78
|
+
if (typeof value === "string")
|
|
79
|
+
return value.trim().length === 0;
|
|
80
|
+
if (Array.isArray(value))
|
|
81
|
+
return value.length === 0;
|
|
82
|
+
if (typeof value === "object")
|
|
83
|
+
return Object.keys(value).length === 0;
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=secrets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.js","sourceRoot":"","sources":["../../../src/domain/design/secrets.ts"],"names":[],"mappings":"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"}
|