@tacuchi/agent-workflow-cli 22.0.1 → 22.1.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 +52 -0
- package/dist/application/resume-service.js.map +1 -1
- package/dist/application/workline-index-service.js +154 -4
- package/dist/application/workline-index-service.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/context/MANIFEST.json +10 -0
- package/skills/w/loops/plan-exec-loop/LOOP.md +37 -12
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { CORRELATIVE_SOURCE } from "./correlative.js";
|
|
2
|
+
import { CRITERION_GLOBAL, isDigest } from "./design/identity.js";
|
|
3
|
+
import { baseDigest } from "./proposal.js";
|
|
4
|
+
const BASELINE_LABEL = /^\s*>\s*Baseline:\s*(.+?)\s*$/i;
|
|
5
|
+
/** `<spec path>@sha256:<64 hex>` — the path is the hint, the digest is the seal. */
|
|
6
|
+
const BASELINE_VALUE = /^(\S+?)@(sha256:[0-9a-fA-F]+)$/;
|
|
7
|
+
/**
|
|
8
|
+
* The digest of a spec's bytes, in the ONE form this system writes.
|
|
9
|
+
*
|
|
10
|
+
* `sha256:`-prefixed because that is what `isDigest` accepts and what every
|
|
11
|
+
* design baseline already publishes. A bare-hex second spelling would be a
|
|
12
|
+
* comparison that fails for a reason nobody can see — the exact failure
|
|
13
|
+
* `baseDigest` exists to prevent, one level up.
|
|
14
|
+
*/
|
|
15
|
+
export function specBaselineDigest(specText) {
|
|
16
|
+
return `sha256:${baseDigest(specText)}`;
|
|
17
|
+
}
|
|
18
|
+
/** The line a publication writes into the plan's header blockquote. */
|
|
19
|
+
export function formatSpecBaseline(baseline) {
|
|
20
|
+
return `> Baseline: ${baseline.path}@${baseline.digest}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Read the seal from a plan's header blockquote.
|
|
24
|
+
*
|
|
25
|
+
* Bounded to the block between the title and the first `##` section, exactly
|
|
26
|
+
* like `Derived from`: a plan that quotes the marker inside its own prose is
|
|
27
|
+
* describing the contract, not declaring its own lineage.
|
|
28
|
+
*/
|
|
29
|
+
export function parsePlanBaseline(lines, fenced, end, specDir) {
|
|
30
|
+
for (let i = 0; i < end; i += 1) {
|
|
31
|
+
const line = lines[i];
|
|
32
|
+
if (line === undefined || fenced[i] === true)
|
|
33
|
+
continue;
|
|
34
|
+
const labelled = BASELINE_LABEL.exec(line);
|
|
35
|
+
if (labelled === null)
|
|
36
|
+
continue;
|
|
37
|
+
return readBaselineValue(labelled[1] ?? "", specDir);
|
|
38
|
+
}
|
|
39
|
+
return { status: "absent" };
|
|
40
|
+
}
|
|
41
|
+
function readBaselineValue(raw, specDir) {
|
|
42
|
+
const value = raw.replace(/^`|`$/g, "").trim();
|
|
43
|
+
const parts = BASELINE_VALUE.exec(value);
|
|
44
|
+
if (parts === null) {
|
|
45
|
+
return {
|
|
46
|
+
status: "malformed",
|
|
47
|
+
raw: value,
|
|
48
|
+
why: "no tiene la forma '<ruta de la spec>@sha256:<64 hex>'",
|
|
49
|
+
action: `escribí '> Baseline: ${specDir}/NNN-spec-<slug>.md@sha256:<64 hex>'`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
const path = parts[1];
|
|
53
|
+
const digest = parts[2].toLowerCase();
|
|
54
|
+
if (!isDigest(digest)) {
|
|
55
|
+
return {
|
|
56
|
+
status: "malformed",
|
|
57
|
+
raw: value,
|
|
58
|
+
why: "el digest no es 'sha256:' seguido de 64 hex",
|
|
59
|
+
action: "volvé a sellar el plan desde la spec que consumió",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const number = correlativeOfSpecPath(path, specDir);
|
|
63
|
+
if (number === null) {
|
|
64
|
+
return {
|
|
65
|
+
status: "malformed",
|
|
66
|
+
raw: value,
|
|
67
|
+
why: `la ruta no es una spec bajo '${specDir}/'`,
|
|
68
|
+
action: `apuntá el baseline a un documento de '${specDir}/'`,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return { status: "sealed", baseline: { path, number, digest } };
|
|
72
|
+
}
|
|
73
|
+
function correlativeOfSpecPath(path, specDir) {
|
|
74
|
+
const escaped = specDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
75
|
+
const re = new RegExp(`^${escaped}/(${CORRELATIVE_SOURCE})-spec[^\\s]*\\.md$`);
|
|
76
|
+
return re.exec(path)?.[1] ?? null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The plan text with its baseline sealed — inserted when absent, corrected when
|
|
80
|
+
* it says anything else.
|
|
81
|
+
*
|
|
82
|
+
* The seal is written by PUBLICATION, not by whoever authored the document: a
|
|
83
|
+
* digest is not something an author can be asked to compute by hand, and a
|
|
84
|
+
* baseline that is optional in practice is a baseline that drifts. Returning the
|
|
85
|
+
* text (rather than writing it) is what keeps it inside the SAME proposal that
|
|
86
|
+
* publishes the plan — the stamped bytes are the ones previewed, approved and
|
|
87
|
+
* written, so no reader ever sees a published plan whose seal landed separately.
|
|
88
|
+
*
|
|
89
|
+
* The line goes into the header blockquote, right after `Derived from`: the two
|
|
90
|
+
* belong together — one names the spec, the other pins its version.
|
|
91
|
+
*/
|
|
92
|
+
export function withSpecBaseline(planText, baseline) {
|
|
93
|
+
const line = formatSpecBaseline(baseline);
|
|
94
|
+
const lines = planText.split("\n");
|
|
95
|
+
const end = headerBlockEnd(lines);
|
|
96
|
+
let lastQuote = -1;
|
|
97
|
+
let derivedAt = -1;
|
|
98
|
+
for (let i = 0; i < end; i += 1) {
|
|
99
|
+
const raw = lines[i];
|
|
100
|
+
if (raw === undefined)
|
|
101
|
+
continue;
|
|
102
|
+
if (!/^\s*>/.test(raw))
|
|
103
|
+
continue;
|
|
104
|
+
lastQuote = i;
|
|
105
|
+
if (BASELINE_LABEL.test(raw)) {
|
|
106
|
+
if (raw === line)
|
|
107
|
+
return planText;
|
|
108
|
+
lines[i] = line;
|
|
109
|
+
return lines.join("\n");
|
|
110
|
+
}
|
|
111
|
+
if (/derived from/i.test(raw))
|
|
112
|
+
derivedAt = i;
|
|
113
|
+
}
|
|
114
|
+
const at = derivedAt >= 0 ? derivedAt + 1 : lastQuote + 1;
|
|
115
|
+
// No blockquote at all: the document does not have the header this seal lives
|
|
116
|
+
// in, and inventing one would restructure somebody's plan to fit a field.
|
|
117
|
+
if (lastQuote < 0)
|
|
118
|
+
return planText;
|
|
119
|
+
lines.splice(at, 0, line);
|
|
120
|
+
return lines.join("\n");
|
|
121
|
+
}
|
|
122
|
+
/** Index of the first `##` section, or the end — the header block's bound. */
|
|
123
|
+
function headerBlockEnd(lines) {
|
|
124
|
+
let fenced = false;
|
|
125
|
+
for (const [index, raw] of lines.entries()) {
|
|
126
|
+
if (raw === undefined)
|
|
127
|
+
continue;
|
|
128
|
+
if (/^\s*```/.test(raw)) {
|
|
129
|
+
fenced = !fenced;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (!fenced && /^##\s/.test(raw))
|
|
133
|
+
return index;
|
|
134
|
+
}
|
|
135
|
+
return lines.length;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* The seal against the spec as it reads NOW.
|
|
139
|
+
*
|
|
140
|
+
* `specText` is `null` when the workspace holds no such spec: that is
|
|
141
|
+
* `unresolved`, not `divergent` — a document that is not there did not change,
|
|
142
|
+
* and saying it did would send whoever reads it to diff against nothing.
|
|
143
|
+
*/
|
|
144
|
+
export function alignSpecBaseline(seal, specText) {
|
|
145
|
+
if (seal.status === "absent")
|
|
146
|
+
return { status: "unsealed" };
|
|
147
|
+
if (seal.status === "malformed") {
|
|
148
|
+
return { status: "malformed", why: seal.why, action: seal.action };
|
|
149
|
+
}
|
|
150
|
+
if (specText === null) {
|
|
151
|
+
return { status: "unresolved", reason: "spec-not-found", path: seal.baseline.path };
|
|
152
|
+
}
|
|
153
|
+
const current = specBaselineDigest(specText);
|
|
154
|
+
if (current === seal.baseline.digest)
|
|
155
|
+
return { status: "aligned", digest: current };
|
|
156
|
+
return {
|
|
157
|
+
status: "divergent",
|
|
158
|
+
sealed_digest: seal.baseline.digest,
|
|
159
|
+
current_digest: current,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The acceptance criteria a spec states, in document order and without
|
|
164
|
+
* duplicates.
|
|
165
|
+
*
|
|
166
|
+
* The grammar is NOT a second one: it is `CRITERION_GLOBAL`, the same closed
|
|
167
|
+
* form the design subsystem already harvests (`S033/AC-01`, `S013/AC-SEM-11`).
|
|
168
|
+
* A note that has to say WHICH assertion it replaces addresses it with this and
|
|
169
|
+
* nothing else — inventing a parallel syntax is how two ids for one criterion
|
|
170
|
+
* start disagreeing.
|
|
171
|
+
*/
|
|
172
|
+
export function specCriteria(lines, fenced) {
|
|
173
|
+
const seen = new Set();
|
|
174
|
+
const out = [];
|
|
175
|
+
for (const [index, line] of lines.entries()) {
|
|
176
|
+
if (line === undefined || fenced[index] === true)
|
|
177
|
+
continue;
|
|
178
|
+
// Fresh instance per line: the shared literal carries /g, whose lastIndex
|
|
179
|
+
// would otherwise leak between lines.
|
|
180
|
+
for (const match of line.matchAll(new RegExp(CRITERION_GLOBAL.source, CRITERION_GLOBAL.flags))) {
|
|
181
|
+
const id = match[0];
|
|
182
|
+
if (seen.has(id))
|
|
183
|
+
continue;
|
|
184
|
+
seen.add(id);
|
|
185
|
+
out.push(id);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return out;
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=lineage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lineage.js","sourceRoot":"","sources":["../../src/domain/lineage.ts"],"names":[],"mappings":"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"}
|
package/dist/domain/proposal.js
CHANGED
|
@@ -88,4 +88,22 @@ export function proposalDigest(body) {
|
|
|
88
88
|
export function destinationsOf(proposal) {
|
|
89
89
|
return [...new Set(proposal.artifacts.map((a) => a.path))].sort();
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* What publishing these artifacts really exercises — observed, never declared.
|
|
93
|
+
*
|
|
94
|
+
* One rule, one place. Whoever seals a proposal has to answer "does this
|
|
95
|
+
* overwrite anything?", and two callers answering it separately is how a preview
|
|
96
|
+
* comes to say "creates" about a write the authorization then classifies as a
|
|
97
|
+
* replacement. Callers that know a destination is theirs already (a slot the run
|
|
98
|
+
* reserved to hold a number, and never published into) pass `overwrite: false`
|
|
99
|
+
* for it: nobody's content is lost, which is the only thing the classes measure.
|
|
100
|
+
*/
|
|
101
|
+
export function observedEffects(artifacts) {
|
|
102
|
+
const effects = [];
|
|
103
|
+
if (artifacts.some((a) => !a.overwrite))
|
|
104
|
+
effects.push("local_additive");
|
|
105
|
+
if (artifacts.some((a) => a.overwrite))
|
|
106
|
+
effects.push("mutate_overwrite");
|
|
107
|
+
return effects;
|
|
108
|
+
}
|
|
91
109
|
//# sourceMappingURL=proposal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/domain/proposal.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"proposal.js","sourceRoot":"","sources":["../../src/domain/proposal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project what the composed contract still owes.
|
|
3
|
+
*
|
|
4
|
+
* Reads the contract's obligations rather than the chain's, because the contract
|
|
5
|
+
* is the one place that already decided which notes are in force. Walking the
|
|
6
|
+
* chain again here would be a second answer to that question.
|
|
7
|
+
*/
|
|
8
|
+
export function reconciliationOf(contract, chain) {
|
|
9
|
+
const resumeOf = new Map(chain.map((note) => [note.id, note.resume_point]));
|
|
10
|
+
const pending = [];
|
|
11
|
+
for (const obligation of contract.obligations) {
|
|
12
|
+
const resume = resumeOf.get(obligation.by);
|
|
13
|
+
// An obligation whose note is not in the chain handed over cannot say where
|
|
14
|
+
// to resume, and inventing a point would send the run somewhere nobody
|
|
15
|
+
// decided. It stays pending — which is the safe half — and names its note.
|
|
16
|
+
pending.push({
|
|
17
|
+
text: obligation.text,
|
|
18
|
+
by: obligation.by,
|
|
19
|
+
resume_point: resume ?? `${obligation.by} (nota ausente de la cadena)`,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
pending,
|
|
24
|
+
resume_point: pending[0]?.resume_point ?? null,
|
|
25
|
+
closable: pending.length === 0,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Whether this plan may be declared closed, and what to do when it may not.
|
|
30
|
+
*
|
|
31
|
+
* The refusal is the point of the whole phase: a validated result stops counting
|
|
32
|
+
* as sufficient proof of a contract it no longer satisfies. Letting the plan
|
|
33
|
+
* close anyway would file the compensatory work as finished by the very run that
|
|
34
|
+
* created it.
|
|
35
|
+
*/
|
|
36
|
+
export function checkClosable(reconciliation) {
|
|
37
|
+
if (reconciliation.closable)
|
|
38
|
+
return [];
|
|
39
|
+
const owed = reconciliation.pending
|
|
40
|
+
.map((obligation) => `${obligation.by}: ${obligation.text}`)
|
|
41
|
+
.join("; ");
|
|
42
|
+
return [
|
|
43
|
+
{
|
|
44
|
+
code: "RECONCILIATION_PENDING",
|
|
45
|
+
message: `el plan tiene compensación abierta y no se puede declarar cerrado — ${owed}`,
|
|
46
|
+
action: `resolvé el trabajo compensatorio desde ${reconciliation.resume_point} y publicá una nota que sustituya a la que lo creó sin volver a arrastrarlo: así se salda una obligación en una cadena append-only`,
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=reconciliation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconciliation.js","sourceRoot":"","sources":["../../src/domain/reconciliation.ts"],"names":[],"mappings":"AAgDA;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA2B,EAC3B,KAA8B;IAE9B,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAC5E,MAAM,OAAO,GAAwB,EAAE,CAAC;IACxC,KAAK,MAAM,UAAU,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC;QAC9C,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;QAC3C,4EAA4E;QAC5E,uEAAuE;QACvE,2EAA2E;QAC3E,OAAO,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,UAAU,CAAC,IAAI;YACrB,EAAE,EAAE,UAAU,CAAC,EAAE;YACjB,YAAY,EAAE,MAAM,IAAI,GAAG,UAAU,CAAC,EAAE,8BAA8B;SACvE,CAAC,CAAC;IACL,CAAC;IACD,OAAO;QACL,OAAO;QACP,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,YAAY,IAAI,IAAI;QAC9C,QAAQ,EAAE,OAAO,CAAC,MAAM,KAAK,CAAC;KAC/B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,cAAkC;IAC9D,IAAI,cAAc,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO;SAChC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,GAAG,UAAU,CAAC,EAAE,KAAK,UAAU,CAAC,IAAI,EAAE,CAAC;SAC3D,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,OAAO;QACL;YACE,IAAI,EAAE,wBAAwB;YAC9B,OAAO,EAAE,uEAAuE,IAAI,EAAE;YACtF,MAAM,EAAE,0CAA0C,cAAc,CAAC,YAAY,oIAAoI;SAClN;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { canonicalJson } from "../application/semantic-operation/protocol.js";
|
|
3
|
+
/**
|
|
4
|
+
* A sealed record's own digest, over its canonical JSON WITHOUT `digest`.
|
|
5
|
+
*
|
|
6
|
+
* Extracted so the two kinds of record that need it — a design governance
|
|
7
|
+
* record and a decision note — seal by the SAME code and not merely by the same
|
|
8
|
+
* described rule. Two implementations of "hash this record" drift the day one of
|
|
9
|
+
* them changes, and the failure is silent: both sides keep producing a digest
|
|
10
|
+
* and only their comparison stops matching.
|
|
11
|
+
*
|
|
12
|
+
* Dropping `digest` is not a detail: a value cannot contain its own hash, so a
|
|
13
|
+
* record that included it would be unverifiable by construction.
|
|
14
|
+
*/
|
|
15
|
+
export function sealedRecordDigest(record) {
|
|
16
|
+
const { digest: _drop, ...rest } = record;
|
|
17
|
+
return `sha256:${createHash("sha256").update(canonicalJson(rest), "utf8").digest("hex")}`;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=sealed-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sealed-record.js","sourceRoot":"","sources":["../../src/domain/sealed-record.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,+CAA+C,CAAC;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAyC;IAC1E,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAC1C,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AAC5F,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tacuchi/agent-workflow-cli",
|
|
3
|
-
"version": "22.0
|
|
3
|
+
"version": "22.1.0",
|
|
4
4
|
"description": "Runtime CLI for Workline — the stages + loops + artifacts system for agent work. Bundles the universal `w` skill set under `skills/w/` (slash commands `/w:*`: spec-new/spec-refine, plan-new/plan-exec, quick, persist, workspace-init, export-*); `self install --target <host>` copies SKILL + commands + hooks into the host. Pluggable capability skills resolve through `.workflow/skills.toml`. Namespace auto-detected from any `.<ns>/sessions/` dir in CWD; default `workflow`.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
"quick.deliverable-is-analysis": "what the task produces is an analysis or a design, so its criterion is a rubric a person ratifies",
|
|
33
33
|
"spec.functional-ambiguity": "a functional ambiguity with two readings that would change what gets built is blocking this spec",
|
|
34
34
|
"spec.solution-space-unexplored": "a trigger of the ideation gate fires: the spec settled on its first idea and the alternatives would change the outcome",
|
|
35
|
+
"spec.escalation-adopted": "this refine consumed the escalation package an execution handed over, instead of re-deriving its diagnosis",
|
|
36
|
+
"spec.escalation-absent": "no escalation package reached this refine, so it starts from the spec alone",
|
|
35
37
|
"spec.independent-outcomes": "the prompt holds outcomes each worth delivering on its own",
|
|
36
38
|
"spec.enumerated-features": "the prompt enumerates distinct features explicitly",
|
|
37
39
|
"spec.distinct-moments": "the parts are requested for different moments or in a stated order",
|
|
@@ -50,6 +52,14 @@
|
|
|
50
52
|
"plan.entry-gap-structural": "phases, contracts or journey are missing, or temporary behavior leaves its boundary undeclared",
|
|
51
53
|
"plan.deviation-structural": "the change that appeared while implementing touches a contract, the participating components, the phase order or the simulation boundary",
|
|
52
54
|
"plan.deviation-functional": "the change that appeared while implementing touches the expected result, the scope, a business rule or an acceptance criterion",
|
|
55
|
+
"plan.deviation-composable": "the change keeps the functional lineage and can be reconciled forward as a durable decision note instead of returning to a refine",
|
|
56
|
+
"plan.deviation-escalation": "what was found does not compose against this lineage and belongs to a spec of its own",
|
|
57
|
+
"plan.closure-lineage": "the divergence stays inside the same functional lineage: it amends how the promise is met, not who promised it",
|
|
58
|
+
"plan.closure-intent": "the intent behind the divergence is settled, so nothing further has to be elicited before deciding",
|
|
59
|
+
"plan.closure-impact": "the transitive impact can be enumerated: every consumer the decision reaches is nameable",
|
|
60
|
+
"plan.closure-recoverable": "the result can be sealed and recovered: an identical retry gives the same outcome and a failure leaves nothing half-written",
|
|
61
|
+
"plan.escalation-adopted": "this refine consumed the escalation package an execution handed over, instead of re-deriving its diagnosis",
|
|
62
|
+
"plan.escalation-absent": "no escalation package reached this refine, so it starts from the document alone",
|
|
53
63
|
"chassis.context-pressure": "a fresh reader would need the CHECKPOINT to continue this run",
|
|
54
64
|
"plan.tasks-to-mark": "at least one task of the batch finished its local work and its checkbox is still open",
|
|
55
65
|
"plan.plan-closable": "every phase of the plan will be validated once this batch closes, so the plan can be sealed done",
|
|
@@ -139,22 +139,47 @@ reconciliation** in `CHECKPOINT`, never reported as published.
|
|
|
139
139
|
|
|
140
140
|
## Deviation gate
|
|
141
141
|
|
|
142
|
-
Execution resolves **detail**, never **redesign
|
|
142
|
+
Execution resolves **detail**, never **redesign** — and between those two there is a third thing: a
|
|
143
|
+
divergence that keeps the functional lineage and can be reconciled **forward**.
|
|
144
|
+
This gate lives **only** in this loop — the chassis does not carry it. It has **four** exits, and the
|
|
145
|
+
run stops at it: declaring a deviation no longer carries on to the commit by itself.
|
|
143
146
|
|
|
144
|
-
- **Local decision — `plan-exec` continues.** A class or method name; a local helper; internal code layout; imports; a choice between equivalent APIs already allowed; a fix needed to compile; a minor refactor that does not move the journey; one extra focused test for a risk found while implementing.
|
|
147
|
+
- **Local decision — `plan-exec` continues.** A class or method name; a local helper; internal code layout; imports; a choice between equivalent APIs already allowed; a fix needed to compile; a minor refactor that does not move the journey; one extra focused test for a risk found while implementing. Declares **no signal** — the gate is not raised — and is recorded in `DECISION` only when it is not obvious.
|
|
148
|
+
- **Composable decision — a note is registered and execution continues.** The change amends how the promise is met without changing who promised it. It is the only exit that writes a **decision note**, and it is available **only** when all four closures hold (below). The spec and the plan stay byte-exact.
|
|
145
149
|
- **Structural deviation — stop and return to `plan-refine`.** Stop when the change touches: an input or output; an observable state; a relevant endpoint or public contract; the set of participating components or repositories; the phase order; the simulation boundary; the integration strategy; a material dependency; the main persistence mechanism; a phase already `validada`; the evidence needed to demonstrate the result. A decision that substantially expands or shrinks the work counts too.
|
|
146
150
|
- **Functional change — return to `spec-refine`.** Stop when the change touches: the expected result; the functional scope; a business rule; an acceptance criterion; the actor or consumer; or a product decision.
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
152
|
+
**Eligibility is closure, never size.** The composable exit opens only when the four close: the
|
|
153
|
+
divergence stays in the **same functional lineage**; its **intent is settled**, so nothing further has
|
|
154
|
+
to be elicited; its **transitive impact can be enumerated**, every consumer nameable; and its
|
|
155
|
+
**result can be sealed and recovered**, so an identical retry gives the same outcome and a failure
|
|
156
|
+
leaves nothing half-written. Counts — of criteria, phases or repositories — are reported and decide
|
|
157
|
+
nothing: a divergence touching one criterion that closes three of the four is not composable, and one
|
|
158
|
+
touching nine that closes all four is.
|
|
159
|
+
|
|
160
|
+
> **Which exit the run takes is the person's, and which are offered is not this document's call:** the deterministic steps below are decided by the CLI (`aw flow advance`), not by this document. Recognizing the divergence and closing its eligibility are judgment; counting the closures and raising the choice are a rule.
|
|
161
|
+
|
|
162
|
+
On any exit that leaves the phase: `CHECKPOINT` records the state reached and the trigger, the phase
|
|
163
|
+
keeps the state it really has (`en ejecución` or `bloqueada`, never `validada`), and the working tree
|
|
164
|
+
is left committed or acknowledged (Delta 2). On the two return paths and on an escalation, the
|
|
165
|
+
**escalation package** travels with it — diagnosis, decisions already taken, evidence gathered and the
|
|
166
|
+
return point — and the destination **declares that it consumed it**, so returning never costs redoing
|
|
167
|
+
the analysis execution had already done. Resuming later over the corrected plan is a normal
|
|
168
|
+
`create_or_resume`.
|
|
169
|
+
|
|
170
|
+
**More than one divergence may appear in a run, and every one is kept.** A later recognition never
|
|
171
|
+
erases an earlier one.
|
|
172
|
+
|
|
173
|
+
| Finding | `plan-exec` | note | `plan-refine` | `spec-refine` |
|
|
174
|
+
|---|---|---|---|---|
|
|
175
|
+
| Rename a helper · internal implementation · test for a local risk | continues | — | — | — |
|
|
176
|
+
| Same promise, different route, four closures held | continues | yes | — | — |
|
|
177
|
+
| Change the public DTO | stops | — | yes | yes, if behavior changes |
|
|
178
|
+
| Add a participating repository | stops | — | yes | — |
|
|
179
|
+
| Move the simulation to another boundary | stops | — | yes | — |
|
|
180
|
+
| Change the phase order | stops | — | yes | — |
|
|
181
|
+
| Add a functional rule · change an acceptance criterion | stops | — | — | yes |
|
|
182
|
+
| Does not compose against this lineage at all | stops | — | — | a spec of its own |
|
|
158
183
|
|
|
159
184
|
## Delta 2 — Git policy: **safe branch + proposed commits**
|
|
160
185
|
|