@tacuchi/agent-workflow-cli 22.0.1 → 22.2.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/dist/application/decision-note-service.js +154 -0
- package/dist/application/decision-note-service.js.map +1 -0
- package/dist/application/decision-registration-service.js +124 -0
- package/dist/application/decision-registration-service.js.map +1 -0
- package/dist/application/docs-canon-service.js +13 -2
- package/dist/application/docs-canon-service.js.map +1 -1
- package/dist/application/flow/submit.js +78 -9
- package/dist/application/flow/submit.js.map +1 -1
- package/dist/application/parsers/spec-relation.js +50 -0
- package/dist/application/parsers/spec-relation.js.map +1 -1
- package/dist/application/resume-service.js +45 -62
- package/dist/application/resume-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 +297 -32
- package/dist/application/workline-index-service.js.map +1 -1
- package/dist/cli/commands/resume.js +20 -3
- package/dist/cli/commands/resume.js.map +1 -1
- package/dist/cli/commands/status.js +57 -8
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/domain/decision-choice.js +71 -0
- package/dist/domain/decision-choice.js.map +1 -0
- package/dist/domain/decision-note.js +235 -0
- package/dist/domain/decision-note.js.map +1 -0
- package/dist/domain/decision-preview.js +159 -0
- package/dist/domain/decision-preview.js.map +1 -0
- package/dist/domain/design/governance.js +2 -4
- package/dist/domain/design/governance.js.map +1 -1
- package/dist/domain/effective-contract.js +138 -0
- package/dist/domain/effective-contract.js.map +1 -0
- package/dist/domain/flow/authority.js +113 -7
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/flow/run-state.js +66 -15
- package/dist/domain/flow/run-state.js.map +1 -1
- package/dist/domain/lineage.js +190 -0
- package/dist/domain/lineage.js.map +1 -0
- package/dist/domain/proposal.js +18 -0
- package/dist/domain/proposal.js.map +1 -1
- package/dist/domain/reconciliation.js +50 -0
- package/dist/domain/reconciliation.js.map +1 -0
- package/dist/domain/sealed-record.js +19 -0
- package/dist/domain/sealed-record.js.map +1 -0
- package/package.json +1 -1
- package/skills/w/commands/resume.md +19 -12
- package/skills/w/commands/status.md +9 -5
- package/skills/w/commands/workspace-init.md +6 -6
- package/skills/w/context/MANIFEST.json +10 -0
- package/skills/w/loops/plan-exec-loop/LOOP.md +37 -12
|
@@ -17,17 +17,28 @@ export const statusCommand = {
|
|
|
17
17
|
const data = result.data;
|
|
18
18
|
if (data === undefined)
|
|
19
19
|
return "";
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
25
|
-
lines.push(...renderPipeline(data.pipeline));
|
|
26
|
-
}
|
|
20
|
+
const header = `${data.workspace.name} · ${data.workspace.path}`;
|
|
21
|
+
const lines = [header, ""];
|
|
22
|
+
lines.push(...renderPipeline(data.pipeline));
|
|
27
23
|
// A broken design reference is PENDING work, not history: it stays in the
|
|
28
24
|
// default view for the same reason an open plan does. Valid references and
|
|
29
25
|
// orphaned packages are inventory and wait for `--detail`.
|
|
30
26
|
lines.push(...renderDesignAlerts(data, lines.at(-1)));
|
|
27
|
+
lines.push(...renderLooseSessions(data, lines.at(-1)));
|
|
28
|
+
// Nothing rendered, and the two reasons for that are not the same answer: a
|
|
29
|
+
// folder nobody initialized has an empty pipeline because no workspace is
|
|
30
|
+
// tracking it, and reporting peace there sends someone looking for work that
|
|
31
|
+
// was never registered. One line either way, and no empty section under it.
|
|
32
|
+
if (lines.length === 2) {
|
|
33
|
+
return data.workspace.initialized
|
|
34
|
+
? `${header} — sin pendientes\n`
|
|
35
|
+
: `${header} — sin workspace de Workline: creá uno con /w:workspace-init\n`;
|
|
36
|
+
}
|
|
37
|
+
// With something to show, the same fact is a notice: the documents are real
|
|
38
|
+
// and worth listing, and they are still outside a workspace.
|
|
39
|
+
if (!data.workspace.initialized) {
|
|
40
|
+
lines.push(...(lines.at(-1) === "" ? [] : [""]), "Aviso: este directorio no es un workspace de Workline — creá uno con /w:workspace-init");
|
|
41
|
+
}
|
|
31
42
|
if (context.detail)
|
|
32
43
|
lines.push("", ...renderDetail(data));
|
|
33
44
|
return `${lines.join("\n").trimEnd()}\n`;
|
|
@@ -37,8 +48,29 @@ const GROUP_TITLES = {
|
|
|
37
48
|
"spec-unrefined": "Specs sin refinar",
|
|
38
49
|
"spec-unplanned": "Specs sin plan",
|
|
39
50
|
"plan-open": "Planes abiertos",
|
|
51
|
+
// Kept because the model keeps the class: a loose checkpoint is reported as a
|
|
52
|
+
// notice now, so nothing reaches this row — and the day something does, it is
|
|
53
|
+
// titled rather than rendered as a blank group.
|
|
40
54
|
"checkpoint-orphan": "Checkpoints sueltos",
|
|
41
55
|
};
|
|
56
|
+
/** `plan` · `spec` · `sesión` — how an item names itself when its detail leads. */
|
|
57
|
+
const KIND_NOUNS = {
|
|
58
|
+
"spec-unrefined": "spec",
|
|
59
|
+
"spec-unplanned": "spec",
|
|
60
|
+
"plan-open": "plan",
|
|
61
|
+
"checkpoint-orphan": "sesión",
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Each pending item over three lines: what it is, what it still owes, and the
|
|
65
|
+
* command that continues it.
|
|
66
|
+
*
|
|
67
|
+
* The middle line is the whole point — a board that listed only the title and the
|
|
68
|
+
* command could say `plan 031 — 100%, fases 6/6` about a plan whose final
|
|
69
|
+
* validation had never run. When what it owes is an OBLIGATION that leaves it
|
|
70
|
+
* neither runnable nor closable, that obligation takes the headline and the
|
|
71
|
+
* percentage drops below it: read in the other order, the number is the part
|
|
72
|
+
* people believe.
|
|
73
|
+
*/
|
|
42
74
|
function renderPipeline(pipeline) {
|
|
43
75
|
const lines = [];
|
|
44
76
|
for (const kind of Object.keys(GROUP_TITLES)) {
|
|
@@ -47,13 +79,30 @@ function renderPipeline(pipeline) {
|
|
|
47
79
|
continue;
|
|
48
80
|
lines.push(`${GROUP_TITLES[kind]} (${items.length})`);
|
|
49
81
|
for (const item of items) {
|
|
50
|
-
|
|
82
|
+
const { next, progress, obligation } = item.detail;
|
|
83
|
+
lines.push(obligation ? ` ${KIND_NOUNS[item.kind]} ${item.number} — ${next}` : ` ${item.summary}`);
|
|
84
|
+
lines.push(` ${obligation ? progress : next}`);
|
|
51
85
|
lines.push(` ${item.command}`);
|
|
52
86
|
}
|
|
53
87
|
lines.push("");
|
|
54
88
|
}
|
|
55
89
|
return lines;
|
|
56
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Loose sessions, as a notice with its count and how to look.
|
|
93
|
+
*
|
|
94
|
+
* The folders stay out of the default view on purpose: what a person needs here
|
|
95
|
+
* is to know the work exists and that nothing on this board accounts for it.
|
|
96
|
+
* Retiring one is another job, and this read does not do it.
|
|
97
|
+
*/
|
|
98
|
+
function renderLooseSessions(data, before) {
|
|
99
|
+
const count = data.loose_sessions.length;
|
|
100
|
+
if (count === 0)
|
|
101
|
+
return [];
|
|
102
|
+
const lines = before === "" ? [] : [""];
|
|
103
|
+
lines.push(`Aviso: ${count} sesión(es) con trabajo y sin documento asociado — vela con 'aw status --detail'`);
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
57
106
|
/**
|
|
58
107
|
* Only what needs a hand: a reference that moved, and one that no longer
|
|
59
108
|
* resolves. `before` keeps the block one blank line away from whatever came
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,gBAAgB,GACjB,MAAM,qCAAqC,CAAC;AAM7C,MAAM,CAAC,MAAM,aAAa,GAA6B;IACrD,IAAI,EAAE,QAAQ;IACd,QAAQ,EACN,uGAAuG;QACvG,oDAAoD;IAEtD,KAAK,CAAC,OAAO,CAAC,KAAiB,EAAE,GAAe;QAC9C,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;QAClF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;IACzC,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,MAAmC,EAAE,OAA2B;QAC1E,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACzB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QAElC,MAAM,
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../../src/cli/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide who answers: the evidence, a note already published, or the person.
|
|
3
|
+
*
|
|
4
|
+
* `unresolvable` is a real outcome and not a defensive branch. A question whose
|
|
5
|
+
* evidence left zero behaviours has not become a preference — it has outgrown
|
|
6
|
+
* the gate, and handing it to a person as if it were a choice would offer a menu
|
|
7
|
+
* with nothing on it. It leaves through the escalation package instead.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveDecision(question, contract, chain) {
|
|
10
|
+
if (question.assertions.length === 0) {
|
|
11
|
+
return {
|
|
12
|
+
kind: "unresolvable",
|
|
13
|
+
why: "la pregunta no nombra ninguna afirmación del contrato",
|
|
14
|
+
action: "direccioná las afirmaciones en juego como S033/AC-05: una decisión que no dice sobre qué decide no se puede componer",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (question.behaviors.length === 0) {
|
|
18
|
+
return {
|
|
19
|
+
kind: "unresolvable",
|
|
20
|
+
why: "la evidencia descartó todas las conductas válidas",
|
|
21
|
+
action: "escalá con el paquete: no queda ninguna alternativa que ofrecer, así que esto ya no es una preferencia",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// Evidence first, and only over facts: one behaviour standing means there is
|
|
25
|
+
// nothing to prefer. Reaching for a prior note here would let a recorded
|
|
26
|
+
// preference override a fact the evidence just established.
|
|
27
|
+
const [only] = question.behaviors;
|
|
28
|
+
if (question.behaviors.length === 1 && only !== undefined) {
|
|
29
|
+
return { kind: "settled", behavior: only };
|
|
30
|
+
}
|
|
31
|
+
const inForce = new Set(contract.applied);
|
|
32
|
+
const asked = new Set(question.assertions);
|
|
33
|
+
const touching = chain.filter((note) => inForce.has(note.id) && note.supersedes_assertions.some((id) => asked.has(id)));
|
|
34
|
+
const [candidate] = touching;
|
|
35
|
+
if (touching.length === 1 &&
|
|
36
|
+
candidate !== undefined &&
|
|
37
|
+
coversQuestion(candidate, question.assertions)) {
|
|
38
|
+
return { kind: "reused", note: candidate.id, decision: candidate.decision };
|
|
39
|
+
}
|
|
40
|
+
return { kind: "ask", behaviors: question.behaviors, why: whyAsk(touching) };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The three different reasons a preference still belongs to the person.
|
|
44
|
+
*
|
|
45
|
+
* They are kept apart because they send whoever reads them somewhere else:
|
|
46
|
+
* nothing to reuse means decide; one partial note means decide the remainder and
|
|
47
|
+
* say so; several notes means the question spans decisions that were taken
|
|
48
|
+
* separately, and the answer has to reconcile them rather than pick one.
|
|
49
|
+
*/
|
|
50
|
+
function whyAsk(touching) {
|
|
51
|
+
if (touching.length === 0) {
|
|
52
|
+
return "quedan varias conductas válidas y ninguna decisión durable previa las cubre";
|
|
53
|
+
}
|
|
54
|
+
if (touching.length === 1) {
|
|
55
|
+
return `${touching[0]?.id} decide sobre parte de estas afirmaciones y deja el resto sin decidir`;
|
|
56
|
+
}
|
|
57
|
+
return `${touching.map((note) => note.id).join(" y ")} deciden sobre partes distintas de estas afirmaciones y ninguna responde la pregunta entera`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Whether one note answers the WHOLE question.
|
|
61
|
+
*
|
|
62
|
+
* Partial coverage is not reuse: a note that superseded two of the three
|
|
63
|
+
* assertions in play leaves the third undecided, and treating it as an answer
|
|
64
|
+
* would decide that third one by silence — exactly the preference-from-evidence
|
|
65
|
+
* this function exists to prevent, only harder to see.
|
|
66
|
+
*/
|
|
67
|
+
function coversQuestion(note, assertions) {
|
|
68
|
+
const superseded = new Set(note.supersedes_assertions);
|
|
69
|
+
return assertions.every((id) => superseded.has(id));
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=decision-choice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-choice.js","sourceRoot":"","sources":["../../src/domain/decision-choice.ts"],"names":[],"mappings":"AAmDA;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,QAA0B,EAC1B,QAA2B,EAC3B,KAA8B;IAE9B,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,GAAG,EAAE,uDAAuD;YAC5D,MAAM,EACJ,sHAAsH;SACzH,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpC,OAAO;YACL,IAAI,EAAE,cAAc;YACpB,GAAG,EAAE,mDAAmD;YACxD,MAAM,EACJ,wGAAwG;SAC3G,CAAC;IACJ,CAAC;IACD,6EAA6E;IAC7E,yEAAyE;IACzE,4DAA4D;IAC5D,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,SAAS,CAAC;IAClC,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC1D,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC7C,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAC3B,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CACzF,CAAC;IACF,MAAM,CAAC,SAAS,CAAC,GAAG,QAAQ,CAAC;IAC7B,IACE,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,SAAS,KAAK,SAAS;QACvB,cAAc,CAAC,SAAS,EAAE,QAAQ,CAAC,UAAU,CAAC,EAC9C,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,EAAE,CAAC;IAC9E,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;AAC/E,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,MAAM,CAAC,QAAiC;IAC/C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,6EAA6E,CAAC;IACvF,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,uEAAuE,CAAC;IACnG,CAAC;IACD,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,6FAA6F,CAAC;AACrJ,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,cAAc,CAAC,IAAkB,EAAE,UAA6B;IACvE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACvD,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { CRITERION_GLOBAL, isDigest } from "./design/identity.js";
|
|
2
|
+
import { sealedRecordDigest } from "./sealed-record.js";
|
|
3
|
+
export const NOTE_SCHEMA = "workline.decision-note/v1";
|
|
4
|
+
const NOTE_ID_RE = /^DEC-(?:[0-9]{3}|[1-9][0-9]{3,})$/;
|
|
5
|
+
const CALENDAR_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
6
|
+
const CRITERION_RE = new RegExp(`^${CRITERION_GLOBAL.source}$`);
|
|
7
|
+
/** The note's own digest, by the SAME code every sealed record uses. */
|
|
8
|
+
export function computeNoteDigest(note) {
|
|
9
|
+
return sealedRecordDigest(note);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Read a note, refusing anything incomplete under its own code.
|
|
13
|
+
*
|
|
14
|
+
* The codes are per-field on purpose. "La nota es inválida" sends whoever wrote
|
|
15
|
+
* it to re-read the whole record; `NOTE_RESUME_POINT_MISSING` sends them to the
|
|
16
|
+
* one line that is missing.
|
|
17
|
+
*/
|
|
18
|
+
export function validateDecisionNote(raw) {
|
|
19
|
+
const failures = [];
|
|
20
|
+
const fail = (code, message, action) => {
|
|
21
|
+
failures.push({ code, message, action });
|
|
22
|
+
};
|
|
23
|
+
if (!isRecord(raw)) {
|
|
24
|
+
fail("NOTE_NOT_OBJECT", "la nota de decisión no es un objeto JSON", "una nota es un record sellado con su propio digest");
|
|
25
|
+
return { ok: false, value: null, failures };
|
|
26
|
+
}
|
|
27
|
+
if (raw.schema !== NOTE_SCHEMA) {
|
|
28
|
+
// El gate de versión corre solo y primero: leer campos de un formato que no
|
|
29
|
+
// conocemos reporta sinsentidos derivados y manda a arreglar un campo cuando
|
|
30
|
+
// lo que falta es actualizar.
|
|
31
|
+
fail("NOTE_SCHEMA_UNKNOWN", `versión de formato no soportada: ${JSON.stringify(raw.schema)}`, `este Workline entiende ${NOTE_SCHEMA}`);
|
|
32
|
+
return { ok: false, value: null, failures };
|
|
33
|
+
}
|
|
34
|
+
if (typeof raw.id !== "string" || !NOTE_ID_RE.test(raw.id)) {
|
|
35
|
+
fail("NOTE_ID_INVALID", "'id' debe ser DEC-NNN", "numerá la nota como DEC-001");
|
|
36
|
+
}
|
|
37
|
+
const lineage = readLineage(raw.lineage, fail);
|
|
38
|
+
requireText(raw.decision, "decision", "NOTE_DECISION_MISSING", "qué se decidió, en una frase", fail);
|
|
39
|
+
requireText(raw.reason, "reason", "NOTE_REASON_MISSING", "por qué se decidió eso y no otra cosa", fail);
|
|
40
|
+
const assertions = readAssertions(raw.supersedes_assertions, fail);
|
|
41
|
+
const supersedesNote = readSupersedesNote(raw.supersedes_note, fail);
|
|
42
|
+
const scope = readScope(raw.scope, fail);
|
|
43
|
+
const consumers = requireList(raw.consumers, "consumers", "NOTE_CONSUMERS_MISSING", "enumerá los planes alcanzados; [] si no alcanza a ninguno", fail);
|
|
44
|
+
const preserved = requireList(raw.evidence_preserved, "evidence_preserved", "NOTE_EVIDENCE_PRESERVED_MISSING", "enumerá la evidencia que sigue valiendo; [] si ninguna", fail);
|
|
45
|
+
const invalidated = requireList(raw.evidence_invalidated, "evidence_invalidated", "NOTE_EVIDENCE_INVALIDATED_MISSING", "enumerá la evidencia que dejó de valer; [] si ninguna", fail);
|
|
46
|
+
const obligations = requireList(raw.obligations, "obligations", "NOTE_OBLIGATIONS_MISSING", "enumerá el trabajo compensatorio que nace; [] si ninguno", fail);
|
|
47
|
+
requireText(raw.resume_point, "resume_point", "NOTE_RESUME_POINT_MISSING", "decí desde dónde sigue la ejecución", fail);
|
|
48
|
+
if (typeof raw.date !== "string" || !CALENDAR_DATE_RE.test(raw.date)) {
|
|
49
|
+
fail("NOTE_DATE_INVALID", "'date' debe ser una fecha AAAA-MM-DD", "la cadena se ordena por fecha e id: sin fecha válida el orden es una opinión");
|
|
50
|
+
}
|
|
51
|
+
if (failures.length > 0)
|
|
52
|
+
return { ok: false, value: null, failures };
|
|
53
|
+
const value = {
|
|
54
|
+
schema: NOTE_SCHEMA,
|
|
55
|
+
id: raw.id,
|
|
56
|
+
lineage: lineage,
|
|
57
|
+
decision: raw.decision,
|
|
58
|
+
reason: raw.reason,
|
|
59
|
+
supersedes_assertions: assertions,
|
|
60
|
+
supersedes_note: supersedesNote,
|
|
61
|
+
scope: scope,
|
|
62
|
+
consumers: consumers,
|
|
63
|
+
evidence_preserved: preserved,
|
|
64
|
+
evidence_invalidated: invalidated,
|
|
65
|
+
obligations: obligations,
|
|
66
|
+
resume_point: raw.resume_point,
|
|
67
|
+
date: raw.date,
|
|
68
|
+
digest: "",
|
|
69
|
+
};
|
|
70
|
+
const expected = computeNoteDigest(stripDigest(value));
|
|
71
|
+
if (typeof raw.digest !== "string" || raw.digest.length === 0) {
|
|
72
|
+
fail("NOTE_DIGEST_MISSING", "'digest' es obligatorio", `sellá la nota con ${expected}`);
|
|
73
|
+
return { ok: false, value: null, failures };
|
|
74
|
+
}
|
|
75
|
+
if (raw.digest !== expected) {
|
|
76
|
+
fail("NOTE_DIGEST_MISMATCH", "el 'digest' no corresponde al contenido de la nota", `una nota publicada no se reescribe: el sello de este contenido es ${expected}`);
|
|
77
|
+
return { ok: false, value: null, failures };
|
|
78
|
+
}
|
|
79
|
+
return { ok: true, value: { ...value, digest: raw.digest }, failures };
|
|
80
|
+
}
|
|
81
|
+
function stripDigest(note) {
|
|
82
|
+
const { digest: _drop, ...rest } = note;
|
|
83
|
+
return rest;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The chain of notes for one lineage, in a deterministic order.
|
|
87
|
+
*
|
|
88
|
+
* Date first, id second. Two notes written the same day still have exactly one
|
|
89
|
+
* order, and it is the order they were minted in — which is what makes "apply
|
|
90
|
+
* them in order" a fact rather than whatever the filesystem returned.
|
|
91
|
+
*/
|
|
92
|
+
export function orderNotes(notes) {
|
|
93
|
+
return [...notes].sort((a, b) => a.date === b.date ? compareNoteIds(a.id, b.id) : a.date < b.date ? -1 : 1);
|
|
94
|
+
}
|
|
95
|
+
function compareNoteIds(a, b) {
|
|
96
|
+
const na = Number(a.slice(4));
|
|
97
|
+
const nb = Number(b.slice(4));
|
|
98
|
+
return na === nb ? (a < b ? -1 : a > b ? 1 : 0) : na - nb;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Whether appending this note to an existing chain keeps it append-only.
|
|
102
|
+
*
|
|
103
|
+
* Two refusals, and both are the same rule seen from different sides: an id
|
|
104
|
+
* already in the chain would REWRITE a published note, and a `supersedes_note`
|
|
105
|
+
* pointing at nothing would claim to correct a decision that was never taken.
|
|
106
|
+
*/
|
|
107
|
+
export function checkAppendOnly(chain, incoming) {
|
|
108
|
+
const failures = [];
|
|
109
|
+
const byId = new Map(chain.map((note) => [note.id, note]));
|
|
110
|
+
if (byId.has(incoming.id)) {
|
|
111
|
+
failures.push({
|
|
112
|
+
code: "NOTE_ALREADY_PUBLISHED",
|
|
113
|
+
message: `${incoming.id} ya está publicada en este linaje`,
|
|
114
|
+
action: "corregir una nota es publicar OTRA que la sustituya por referencia, nunca reescribirla",
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (incoming.supersedes_note !== null && !byId.has(incoming.supersedes_note)) {
|
|
118
|
+
failures.push({
|
|
119
|
+
code: "NOTE_SUPERSEDES_ABSENT",
|
|
120
|
+
message: `${incoming.id} dice sustituir a ${incoming.supersedes_note}, que no está en el linaje`,
|
|
121
|
+
action: "una sustitución nombra una nota publicada de la misma cadena",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return failures;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether two notes record the SAME decision, ignoring which correlative it got.
|
|
128
|
+
*
|
|
129
|
+
* This is what makes "an identical retry recovers the same result without
|
|
130
|
+
* deciding again" a property of content. The id cannot take part: a retry mints
|
|
131
|
+
* the next number off a chain that already holds the first attempt, so comparing
|
|
132
|
+
* sealed digests would call every retry a new decision and append a duplicate —
|
|
133
|
+
* the chain would grow one indistinguishable note per interrupted run, and the
|
|
134
|
+
* composition would then report an overlap nobody caused.
|
|
135
|
+
*
|
|
136
|
+
* Everything else does take part, including the fields that look like prose. Two
|
|
137
|
+
* notes that supersede the same assertions for different stated reasons are two
|
|
138
|
+
* decisions, and treating the second as a repeat of the first would drop the
|
|
139
|
+
* reason the chain exists to preserve.
|
|
140
|
+
*/
|
|
141
|
+
export function sameDecision(a, b) {
|
|
142
|
+
return canonicalDecision(a) === canonicalDecision(b);
|
|
143
|
+
}
|
|
144
|
+
function canonicalDecision(note) {
|
|
145
|
+
const { id: _id, digest: _digest, ...rest } = note;
|
|
146
|
+
return sealedRecordDigest(rest);
|
|
147
|
+
}
|
|
148
|
+
/** The notes still in force: every one nobody later superseded. */
|
|
149
|
+
export function effectiveNotes(chain) {
|
|
150
|
+
const superseded = new Set(chain.map((note) => note.supersedes_note).filter((id) => id !== null));
|
|
151
|
+
return orderNotes(chain).filter((note) => !superseded.has(note.id));
|
|
152
|
+
}
|
|
153
|
+
function isRecord(value) {
|
|
154
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
155
|
+
}
|
|
156
|
+
function requireText(value, field, code, action, fail) {
|
|
157
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
158
|
+
fail(code, `'${field}' es obligatorio y no puede estar vacío`, action);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function requireList(value, field, code, action, fail) {
|
|
162
|
+
if (!Array.isArray(value) || !value.every((v) => typeof v === "string" && v.length > 0)) {
|
|
163
|
+
fail(code, `'${field}' es obligatorio y debe ser una lista de strings`, action);
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
function readScope(value, fail) {
|
|
169
|
+
if (value !== "functional" && value !== "plan-only") {
|
|
170
|
+
fail("NOTE_SCOPE_INVALID", "'scope' debe ser 'functional' o 'plan-only'", "decí si la decisión cambia lo prometido o sólo el camino para llegar");
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
function readSupersedesNote(value, fail) {
|
|
176
|
+
if (value === null)
|
|
177
|
+
return null;
|
|
178
|
+
if (typeof value !== "string" || !NOTE_ID_RE.test(value)) {
|
|
179
|
+
fail("NOTE_SUPERSEDES_INVALID", "'supersedes_note' debe ser un DEC-NNN o null", "usá null si esta nota no corrige ninguna anterior");
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return value;
|
|
183
|
+
}
|
|
184
|
+
function readAssertions(value, fail) {
|
|
185
|
+
const list = requireList(value, "supersedes_assertions", "NOTE_ASSERTIONS_MISSING", "enumerá las afirmaciones que sustituye como S033/AC-05; [] si no sustituye ninguna", fail);
|
|
186
|
+
if (list === null)
|
|
187
|
+
return null;
|
|
188
|
+
const wrong = list.filter((id) => !CRITERION_RE.test(id));
|
|
189
|
+
if (wrong.length > 0) {
|
|
190
|
+
fail("NOTE_ASSERTIONS_INVALID", `'supersedes_assertions' no usa la gramática de criterio en: ${wrong.join(", ")}`, "direccioná cada afirmación como S033/AC-05 — es la misma gramática que ya existe, no una segunda");
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
if (new Set(list).size !== list.length) {
|
|
194
|
+
fail("NOTE_ASSERTIONS_DUPLICATE", "'supersedes_assertions' repite una afirmación", "nombrar dos veces la misma afirmación no la sustituye dos veces");
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return list;
|
|
198
|
+
}
|
|
199
|
+
function readAnchor(value, side, fail) {
|
|
200
|
+
if (!isRecord(value)) {
|
|
201
|
+
fail("NOTE_LINEAGE_MISSING", `'lineage.${side}' es obligatorio`, `anclá el ${side} con su ruta, su correlativo y su digest exacto`);
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const { path, number, digest } = value;
|
|
205
|
+
if (typeof path !== "string" || path.length === 0 || typeof number !== "string") {
|
|
206
|
+
fail("NOTE_LINEAGE_INVALID", `'lineage.${side}' necesita 'path' y 'number'`, "el linaje nombra documentos reales del workspace");
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
if (!isDigest(digest)) {
|
|
210
|
+
fail("NOTE_LINEAGE_DIGEST_INVALID", `'lineage.${side}.digest' debe ser el sha256 exacto de ese documento`, "sin el digest la nota decide sobre un nombre, no sobre unos bytes");
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return { path, number, digest };
|
|
214
|
+
}
|
|
215
|
+
function readLineage(value, fail) {
|
|
216
|
+
if (!isRecord(value)) {
|
|
217
|
+
fail("NOTE_LINEAGE_MISSING", "'lineage' es obligatorio", "una nota sin linaje no se puede componer sobre ningún baseline");
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
const spec = readAnchor(value.spec, "spec", fail);
|
|
221
|
+
const plan = readAnchor(value.plan, "plan", fail);
|
|
222
|
+
const execution = value.execution;
|
|
223
|
+
if (!isRecord(execution) ||
|
|
224
|
+
typeof execution.session !== "string" ||
|
|
225
|
+
execution.session.length === 0 ||
|
|
226
|
+
typeof execution.phase !== "string" ||
|
|
227
|
+
execution.phase.length === 0) {
|
|
228
|
+
fail("NOTE_EXECUTION_STATE_MISSING", "'lineage.execution' necesita 'session' y 'phase'", "registrá desde qué sesión y en qué fase apareció la divergencia");
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
if (spec === null || plan === null)
|
|
232
|
+
return null;
|
|
233
|
+
return { spec, plan, execution: { session: execution.session, phase: execution.phase } };
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=decision-note.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-note.js","sourceRoot":"","sources":["../../src/domain/decision-note.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { SELF_AUTHORIZABLE_CLASSES } from "./capability/effects.js";
|
|
2
|
+
import { destinationsOf, observedEffects, sealProposal } from "./proposal.js";
|
|
3
|
+
/**
|
|
4
|
+
* WHAT THE PERSON SEES, once, before deciding — and the seal their single
|
|
5
|
+
* "yes" is given over.
|
|
6
|
+
*
|
|
7
|
+
* The eight sections are not a report format. They are the eight things somebody
|
|
8
|
+
* needs in order to be responsible for the answer: what is being decided over,
|
|
9
|
+
* what actually changes, who else is reached, how far it goes, which evidence
|
|
10
|
+
* survives and which stops counting, what work the decision creates, where
|
|
11
|
+
* execution comes back, and exactly which bytes land where. A preview missing
|
|
12
|
+
* any of them asks for consent to something that was not shown.
|
|
13
|
+
*
|
|
14
|
+
* Every one of them is DERIVED from the composed contract and the sealed note.
|
|
15
|
+
* That is the load-bearing part: a preview authored beside the change is a second
|
|
16
|
+
* description of it, and the moment the two disagree the person is approving the
|
|
17
|
+
* pleasant one. Here, changing anything material changes the proposal's digest,
|
|
18
|
+
* so the grant given over the old seal stops covering the new bytes by
|
|
19
|
+
* construction rather than by anybody remembering to re-check.
|
|
20
|
+
*
|
|
21
|
+
* And it is ONE authorization. Choosing an alternative registers the note and
|
|
22
|
+
* authorizes exactly the previewed effects; there is no second confirmation
|
|
23
|
+
* asking the same question in other words, because {@link grantFor} is derived
|
|
24
|
+
* from the very seal that was shown. What that grant can never carry is the two
|
|
25
|
+
* classes below — a decision note is a document, and a path that wanted to
|
|
26
|
+
* destroy something or leave the machine to register one is not this path.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* The classes registering a decision may ever exercise.
|
|
30
|
+
*
|
|
31
|
+
* Publishing a note writes a JSON document beside the spec it amends: it creates
|
|
32
|
+
* or it replaces, and nothing else. `destructive` and `network_external` are
|
|
33
|
+
* absent by intent and not by omission — no reachable combination of a note and
|
|
34
|
+
* an index produces them, so their appearance means the artifacts are not the
|
|
35
|
+
* ones this path is for, and the preview refuses instead of asking somebody to
|
|
36
|
+
* authorize them under a decision's name.
|
|
37
|
+
*/
|
|
38
|
+
export const DECISION_EFFECT_CLASSES = [
|
|
39
|
+
"local_additive",
|
|
40
|
+
"mutate_overwrite",
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* Build the preview, or refuse to show one.
|
|
44
|
+
*
|
|
45
|
+
* It blocks rather than trims. A preview that dropped a section it could not
|
|
46
|
+
* fill would be indistinguishable from one where that section was genuinely
|
|
47
|
+
* empty — "no compensatory work" and "we did not work out the compensatory work"
|
|
48
|
+
* look identical on screen and mean opposite things to whoever says yes.
|
|
49
|
+
*/
|
|
50
|
+
export function buildDecisionPreview(input) {
|
|
51
|
+
const failures = [];
|
|
52
|
+
if (input.artifacts.length === 0) {
|
|
53
|
+
failures.push({
|
|
54
|
+
code: "PREVIEW_NO_ARTIFACTS",
|
|
55
|
+
message: "la vista previa no tiene ningún destino que mostrar",
|
|
56
|
+
action: "una decisión sin efectos no se registra: prepará el índice de decisiones antes de previsualizar",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (input.note.lineage.spec.digest !== input.contract.spec.digest) {
|
|
60
|
+
failures.push({
|
|
61
|
+
code: "PREVIEW_BASELINE_DRIFTED",
|
|
62
|
+
message: `la nota decide sobre ${input.note.lineage.spec.digest} y el contrato compone sobre ${input.contract.spec.digest}`,
|
|
63
|
+
action: "recomponé el contrato sobre la spec vigente y volvé a sellar la nota: previsualizar sobre dos baselines distintos muestra una decisión que nadie tomó",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (!input.contract.applied.includes(input.note.id)) {
|
|
67
|
+
failures.push({
|
|
68
|
+
code: "PREVIEW_NOTE_NOT_COMPOSED",
|
|
69
|
+
message: `${input.note.id} no está entre las notas aplicadas del contrato que se muestra`,
|
|
70
|
+
action: "componé el contrato incluyendo esta nota: la vista previa enseña el efecto real, no el que tendría si compusiera",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// The seal has to cover the note that was SHOWN, and the note's own digest is
|
|
74
|
+
// what proves it does. Without this, six of the eight sections are read off a
|
|
75
|
+
// record the proposal never carries: a preview could show one resume point and
|
|
76
|
+
// seal bytes holding another, and the approval would be over the pleasant one.
|
|
77
|
+
// A substring is enough because the digest seals every field of the note — the
|
|
78
|
+
// published record can only carry it by being this record.
|
|
79
|
+
if (!input.artifacts.some((artifact) => artifact.content.includes(input.note.digest))) {
|
|
80
|
+
failures.push({
|
|
81
|
+
code: "PREVIEW_NOTE_NOT_SEALED",
|
|
82
|
+
message: `ningún destino de la propuesta contiene ${input.note.id}`,
|
|
83
|
+
action: "publicá la nota dentro de la propuesta que se aprueba: mostrar una decisión y sellar otros bytes es pedir consentimiento sobre algo que no se enseñó",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
const classes = observedEffects(input.artifacts);
|
|
87
|
+
const beyond = classes.filter((effect) => !DECISION_EFFECT_CLASSES.includes(effect));
|
|
88
|
+
if (beyond.length > 0) {
|
|
89
|
+
failures.push({
|
|
90
|
+
code: "PREVIEW_EFFECT_FORBIDDEN",
|
|
91
|
+
message: `registrar una decisión ejercería ${beyond.join(", ")}`,
|
|
92
|
+
action: `una nota sólo escribe su índice: proponé sólo efectos de ${DECISION_EFFECT_CLASSES.join(", ")}`,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (failures.length > 0)
|
|
96
|
+
return { status: "blocked", failures };
|
|
97
|
+
const proposal = sealProposal({
|
|
98
|
+
operation: input.operation,
|
|
99
|
+
artifacts: input.artifacts,
|
|
100
|
+
bases: input.bases,
|
|
101
|
+
effects: classes,
|
|
102
|
+
requiresApproval: classes.filter((effect) => !SELF_AUTHORIZABLE_CLASSES.includes(effect)),
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
status: "previewed",
|
|
106
|
+
preview: {
|
|
107
|
+
baseline: input.contract.spec,
|
|
108
|
+
effective_change: changesOf(input.contract, input.note),
|
|
109
|
+
consumers: [...input.note.consumers],
|
|
110
|
+
impact: {
|
|
111
|
+
scope: input.note.scope,
|
|
112
|
+
assertions: input.note.supersedes_assertions.length,
|
|
113
|
+
consumers: input.note.consumers.length,
|
|
114
|
+
},
|
|
115
|
+
evidence: {
|
|
116
|
+
preserved: [...input.note.evidence_preserved],
|
|
117
|
+
invalidated: [...input.note.evidence_invalidated],
|
|
118
|
+
},
|
|
119
|
+
obligations: [...input.note.obligations],
|
|
120
|
+
resume_point: input.note.resume_point,
|
|
121
|
+
effects: { classes, entries: proposal.preview },
|
|
122
|
+
proposal,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The assertions THIS note moves, read off the composed contract.
|
|
128
|
+
*
|
|
129
|
+
* `from` comes from the contract's attribution rather than from the note: an
|
|
130
|
+
* assertion another note already amended is not going from `baseline`, and
|
|
131
|
+
* saying so would overstate what this decision costs.
|
|
132
|
+
*/
|
|
133
|
+
function changesOf(contract, note) {
|
|
134
|
+
const mine = new Set(note.supersedes_assertions);
|
|
135
|
+
return contract.assertions
|
|
136
|
+
.filter((assertion) => mine.has(assertion.id))
|
|
137
|
+
.map((assertion) => ({
|
|
138
|
+
assertion: assertion.id,
|
|
139
|
+
from: assertion.by === note.id ? "baseline" : "amended",
|
|
140
|
+
to: "amended",
|
|
141
|
+
by: note.id,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The grant one explicit choice gives — over this seal, and nothing wider.
|
|
146
|
+
*
|
|
147
|
+
* Derived from the preview instead of assembled beside it, so "authorize exactly
|
|
148
|
+
* what was shown" is arithmetic. A grant naming a different digest covers
|
|
149
|
+
* nothing, which is what makes a material change between the preview and the
|
|
150
|
+
* write fail closed rather than sail through on a yes that was about other bytes.
|
|
151
|
+
*/
|
|
152
|
+
export function grantFor(preview) {
|
|
153
|
+
return {
|
|
154
|
+
digest: preview.proposal.digest,
|
|
155
|
+
destinations: destinationsOf(preview.proposal),
|
|
156
|
+
classes: [...preview.proposal.requires_approval],
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=decision-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-preview.js","sourceRoot":"","sources":["../../src/domain/decision-preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { canonicalJson } from "../../application/semantic-operation/protocol.js";
|
|
1
|
+
import { sealedRecordDigest } from "../sealed-record.js";
|
|
3
2
|
import { isCalendarDate } from "./baseline.js";
|
|
4
3
|
import { CANONICAL_SCHEMAS } from "./capability.js";
|
|
5
4
|
import { isDigest, parseBaselineRef } from "./identity.js";
|
|
@@ -28,8 +27,7 @@ const DECISIONS = new Set(["proposed", "approved", "rejected"]);
|
|
|
28
27
|
* unverifiable — you cannot hash a value that contains its own hash.
|
|
29
28
|
*/
|
|
30
29
|
export function computeRecordDigest(record) {
|
|
31
|
-
|
|
32
|
-
return `sha256:${createHash("sha256").update(canonicalJson(rest), "utf8").digest("hex")}`;
|
|
30
|
+
return sealedRecordDigest(record);
|
|
33
31
|
}
|
|
34
32
|
export function validateDesignReview(raw, artifact) {
|
|
35
33
|
const r = new Reader(REVIEW_ALLOWED_KEYS);
|