@tacuchi/agent-workflow-cli 24.0.0 → 25.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/dist/application/checkpoint/markdown.js +85 -5
- package/dist/application/checkpoint/markdown.js.map +1 -1
- package/dist/application/checkpoint-service.js +7 -6
- package/dist/application/checkpoint-service.js.map +1 -1
- package/dist/application/checkpoint-write-service.js +270 -16
- package/dist/application/checkpoint-write-service.js.map +1 -1
- package/dist/application/flow/advance.js +60 -10
- package/dist/application/flow/advance.js.map +1 -1
- package/dist/application/flow/internal-actions.js +1 -1
- package/dist/application/flow/internal-actions.js.map +1 -1
- package/dist/application/flow/submit.js +256 -9
- package/dist/application/flow/submit.js.map +1 -1
- package/dist/application/lifecycle-target.js +16 -19
- package/dist/application/lifecycle-target.js.map +1 -1
- package/dist/application/markdown.js +6 -2
- package/dist/application/markdown.js.map +1 -1
- package/dist/application/parsers/spec-functional.js +220 -0
- package/dist/application/parsers/spec-functional.js.map +1 -0
- package/dist/application/parsers/spec-relation.js +142 -5
- package/dist/application/parsers/spec-relation.js.map +1 -1
- package/dist/application/paths-service.js +13 -0
- package/dist/application/paths-service.js.map +1 -1
- package/dist/application/plan-exec-decision-service.js +57 -4
- package/dist/application/plan-exec-decision-service.js.map +1 -1
- package/dist/application/reseal-service.js +300 -0
- package/dist/application/reseal-service.js.map +1 -0
- package/dist/application/workline-index-service.js +64 -6
- package/dist/application/workline-index-service.js.map +1 -1
- package/dist/cli/commands/checkpoint-write.js +24 -22
- package/dist/cli/commands/checkpoint-write.js.map +1 -1
- package/dist/cli/commands/index.js +5 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/reseal.js +140 -0
- package/dist/cli/commands/reseal.js.map +1 -0
- package/dist/cli/commands/status.js +5 -1
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/help-groups.js +6 -0
- package/dist/cli/help-groups.js.map +1 -1
- package/dist/domain/effective-contract.js +10 -2
- package/dist/domain/effective-contract.js.map +1 -1
- package/dist/domain/flow/answer.js +4 -0
- package/dist/domain/flow/answer.js.map +1 -1
- package/dist/domain/flow/authority.js +99 -14
- package/dist/domain/flow/authority.js.map +1 -1
- package/dist/domain/flow/directive.js +2 -0
- package/dist/domain/flow/directive.js.map +1 -1
- package/dist/domain/flow/run-state.js +42 -0
- package/dist/domain/flow/run-state.js.map +1 -1
- package/dist/domain/lineage.js +38 -11
- package/dist/domain/lineage.js.map +1 -1
- package/package.json +1 -1
- package/skills/w/commands/plan-exec.md +1 -1
- package/skills/w/commands/spec-new.md +6 -5
- package/skills/w/context/MANIFEST.json +1 -1
- package/skills/w/harness/HARNESS.md +1 -1
- package/skills/w/hooks/README.md +13 -9
- package/skills/w/hooks/hooks.template.json +2 -2
- package/skills/w/loops/CHASSIS.md +1 -1
- package/skills/w/loops/plan-exec-loop/LOOP.md +7 -5
- package/skills/w/loops/plan-new-loop/LOOP.md +46 -41
- package/skills/w/loops/plan-refine-loop/LOOP.md +4 -4
- package/skills/w/loops/quick-loop/LOOP.md +12 -13
- package/skills/w/loops/spec-refine-loop/LOOP.md +7 -3
- package/skills/w/modules/COMPACTION.md +1 -1
- package/skills/w/modules/PLAN-INPUT.md +3 -3
- package/skills/w/modules/PROMPT-CONTINUITY.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/application/markdown.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../../src/application/markdown.ts"],"names":[],"mappings":"AA4BA,MAAM,eAAe,GAAG,qBAAqB,CAAC;AAC9C,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAEvC;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAc,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9D,MAAM,QAAQ,GAAsB,EAAE,CAAC;IAEvC,IAAI,KAAK,GAAkB,IAAI,CAAC;IAChC,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QACrD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,IAAI,MAAM,EAAE,CAAC;gBACX,KAAK,GAAG,MAAM,CAAC;gBACf,SAAS,GAAG,CAAC,CAAC;gBACd,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;gBACjB,SAAS;YACX,CAAC;YACD,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACzC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACjB,IAAI,MAAM,IAAI,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;YAC/C,KAAK,GAAG,IAAI,CAAC;YACb,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AACvF,CAAC;AAED,yFAAyF;AACzF,SAAS,WAAW,CAAC,IAAY,EAAE,MAAc,EAAE,IAAY;IAC7D,OAAO,MAAM,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,KAAK,MAAM,CAAC;AAClF,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,GAAW;IACpD,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,yBAAyB,OAAO,2BAA2B,EAAE,IAAI,CAAC,CAAC;IACzF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAC7B,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,OAAe,EACf,gBAAuC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;IAEpE,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAE/C,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC;gBACtC,WAAW,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;gBACzB,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC;YACzB,CAAC;QACH,CAAC;aAAM,IAAI,CAAC,CAAC,KAAK,IAAI,YAAY,EAAE,CAAC;YACnC,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,QAAQ,CAAC,KAAe;IAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC;AAED;;;;;;GAMG;AACH,MAAM,cAAc,GAAqC;IACvD,6EAA6E;IAC7E,4EAA4E;IAC5E,+EAA+E;IAC/E,+EAA+E;IAC/E,YAAY;IACZ,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC;IACrC,CAAC,UAAU,EAAE,WAAW,EAAE,UAAU,CAAC;IACrC,CAAC,aAAa,EAAE,aAAa,EAAE,aAAa,CAAC;IAC7C,CAAC,eAAe,EAAE,aAAa,CAAC;IAChC,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,OAAO,CAAC;IACT,CAAC,aAAa,EAAE,eAAe,CAAC;IAChC,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,aAAa,CAAC;IAC3D,CAAC,cAAc,EAAE,cAAc,EAAE,WAAW,CAAC;IAC7C,CAAC,sBAAsB,EAAE,kBAAkB,CAAC;IAC5C,CAAC,kBAAkB,EAAE,eAAe,CAAC;IACrC;QACE,uCAAuC;QACvC,uCAAuC;QACvC,kCAAkC;KACnC;IACD,CAAC,+BAA+B,EAAE,+BAA+B,EAAE,4BAA4B,CAAC;IAChG,CAAC,iBAAiB,EAAE,YAAY,CAAC;IACjC,CAAC,MAAM,EAAE,QAAQ,CAAC;IAClB,CAAC,OAAO,CAAC;IACT,CAAC,UAAU,EAAE,SAAS,CAAC;IACvB;QACE,yBAAyB;QACzB,yBAAyB;QACzB,qBAAqB;QACrB,YAAY;QACZ,YAAY;KACb;IACD,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,kBAAkB,CAAC;IAChE,CAAC,OAAO,EAAE,QAAQ,CAAC;IACnB,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,QAAQ,EAAE,UAAU,CAAC;IACtB,CAAC,aAAa,EAAE,SAAS,CAAC;IAC1B,CAAC,OAAO,EAAE,UAAU,CAAC;IACrB,CAAC,sBAAsB,EAAE,mBAAmB,CAAC;IAC7C,CAAC,eAAe,EAAE,aAAa,CAAC;IAChC,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;IAC1C,CAAC,qBAAqB,EAAE,mBAAmB,CAAC;IAC5C,CAAC,gBAAgB,EAAE,aAAa,CAAC;IACjC,CAAC,8BAA8B,EAAE,8BAA8B,EAAE,8BAA8B,CAAC;IAChG,CAAC,kBAAkB,EAAE,cAAc,CAAC;IACpC,CAAC,QAAQ,EAAE,OAAO,CAAC;IACnB,CAAC,iCAAiC,EAAE,+BAA+B,CAAC;IACpE,CAAC,wBAAwB,EAAE,qBAAqB,CAAC;IACjD,CAAC,8BAA8B,EAAE,2BAA2B,CAAC;IAC7D,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,gBAAgB,CAAC;IAC9D,CAAC,0BAA0B,EAAE,0BAA0B,CAAC;IACxD,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,cAAc,EAAE,aAAa,CAAC;IAC/B,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IACtC,CAAC,cAAc,EAAE,cAAc,CAAC;IAChC,CAAC,SAAS,EAAE,MAAM,CAAC;IACnB,CAAC,gBAAgB,EAAE,aAAa,CAAC;IACjC,CAAC,aAAa,EAAE,YAAY,CAAC;IAC7B,CAAC,wBAAwB,EAAE,sBAAsB,CAAC;IAClD,CAAC,eAAe,EAAE,cAAc,CAAC;IACjC,CAAC,kCAAkC,EAAE,gCAAgC,CAAC;IACtE,CAAC,yBAAyB,EAAE,yBAAyB,EAAE,qBAAqB,CAAC;IAC7E,CAAC,cAAc,CAAC;IAChB,CAAC,UAAU,EAAE,OAAO,CAAC;IACrB,CAAC,yBAAyB,EAAE,0BAA0B,CAAC;IACvD,CAAC,sBAAsB,EAAE,qBAAqB,CAAC;IAC/C,CAAC,iBAAiB,EAAE,cAAc,CAAC;IACnC,CAAC,WAAW,CAAC;IACb,CAAC,qBAAqB,EAAE,iBAAiB,CAAC;IAC1C,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,CAAC;IAC7D,CAAC,UAAU,EAAE,SAAS,CAAC;IACvB,CAAC,uBAAuB,EAAE,uBAAuB,EAAE,wBAAwB,CAAC;CAC7E,CAAC;AAEF,SAAS,gBAAgB,CAAC,CAAS;IACjC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,WAAW,GAAG,IAAI,GAAG,EAA6B,CAAC;AACzD,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;IACnC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;QAC5B,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC;IACpD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,WAAW,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,GAAW;IAC7D,KAAK,MAAM,SAAS,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC5C,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAY,EACZ,OAAe,EACf,aAAqC;IAErC,KAAK,MAAM,SAAS,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;QAC7D,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;IACxC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { specBaselineDigest } from "../../domain/lineage.js";
|
|
2
|
+
import { bilingualAliases, scanMarkdown } from "../markdown.js";
|
|
3
|
+
import { semanticDigest } from "../semantic-operation/protocol.js";
|
|
4
|
+
/**
|
|
5
|
+
* WHAT a spec promises, separated from HOW its document happens to read.
|
|
6
|
+
*
|
|
7
|
+
* A plan seals which VERSION of its spec it derives from, and until now that
|
|
8
|
+
* seal digested the spec's exact bytes. The consequence was not theoretical: a
|
|
9
|
+
* comma, a reordered list or a line rewrapped in `## Context` turned every plan
|
|
10
|
+
* of that spec `divergent`, a divergent plan cannot close
|
|
11
|
+
* (`PLAN_EXEC_DONE_BASELINE_INVALID`), and the only repair was a full
|
|
12
|
+
* `/w:plan-refine` per consumer. Editing prose is not amending a contract, so
|
|
13
|
+
* the two must not produce the same verdict.
|
|
14
|
+
*
|
|
15
|
+
* So the seal digests a PAYLOAD: the spec cut down to the sections that state
|
|
16
|
+
* the contract, normalized so that bookkeeping and typography cannot move it.
|
|
17
|
+
* The rules below ARE the contract — whoever changes one of them by accident
|
|
18
|
+
* turns plans that were fine into divergent ones, which is exactly the failure
|
|
19
|
+
* this module exists to end.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* The H2 sections that carry the functional contract. First entry of each group
|
|
23
|
+
* is the canonical key the payload uses; the rest are accepted spellings.
|
|
24
|
+
*
|
|
25
|
+
* An ALLOWLIST and not a denylist, because the failure modes are asymmetric: a
|
|
26
|
+
* new editorial section (`## Notes`, `## Glossary`) silently entering the payload
|
|
27
|
+
* would resurrect the very divergence this closes, while a new FUNCTIONAL
|
|
28
|
+
* section left out of it is caught the first time somebody notices the digest
|
|
29
|
+
* did not move — and that is a fix, not a false alarm.
|
|
30
|
+
*
|
|
31
|
+
* The bilingual half is taken from {@link bilingualAliases}: `Requirement` and
|
|
32
|
+
* `Acceptance criteria` already have their accepted spellings enumerated in the
|
|
33
|
+
* markdown keyword table, and a second copy here is how two lists of one
|
|
34
|
+
* heading's names start disagreeing.
|
|
35
|
+
*/
|
|
36
|
+
const DECLARED_SECTIONS = [
|
|
37
|
+
["Requirement"],
|
|
38
|
+
["Scope", "Alcance"],
|
|
39
|
+
["Acceptance criteria"],
|
|
40
|
+
["Scenarios", "Escenarios"],
|
|
41
|
+
["Behavioral changes", "Cambios de comportamiento"],
|
|
42
|
+
["Affected capabilities", "Capacidades afectadas"],
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* The declared groups widened with the keyword table's spellings — the ONE list
|
|
46
|
+
* the alias index below is built from, and deliberately not exported: a second
|
|
47
|
+
* reader of the section names is a second answer to "is this section contract",
|
|
48
|
+
* and the only answer that decides anything is {@link CANONICAL_BY_ALIAS}.
|
|
49
|
+
*/
|
|
50
|
+
const FUNCTIONAL_SECTIONS = DECLARED_SECTIONS.map((group) => [
|
|
51
|
+
...new Set([...group, ...bilingualAliases(group[0])]),
|
|
52
|
+
]);
|
|
53
|
+
/** Heading names compare accent- and case-insensitively, like the keyword table. */
|
|
54
|
+
function normalizeHeading(title) {
|
|
55
|
+
return title.trim().toLowerCase().normalize("NFD").replace(/\p{M}/gu, "");
|
|
56
|
+
}
|
|
57
|
+
/** Every accepted spelling → the canonical key its section writes in the payload. */
|
|
58
|
+
const CANONICAL_BY_ALIAS = new Map();
|
|
59
|
+
for (const group of FUNCTIONAL_SECTIONS) {
|
|
60
|
+
const canonical = group[0].toLowerCase();
|
|
61
|
+
for (const alias of group)
|
|
62
|
+
CANONICAL_BY_ALIAS.set(normalizeHeading(alias), canonical);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The canonical key of the criteria checklist — the ONE section whose item
|
|
66
|
+
* labels are addressable assertions.
|
|
67
|
+
*
|
|
68
|
+
* Exported so `parseSpecCriteria` locates that checklist with THIS module's
|
|
69
|
+
* answer instead of its own: the seal and the harvest disagreeing about where
|
|
70
|
+
* the criteria are means a criterion sealed as contract that no note can
|
|
71
|
+
* address (`CONTRACT_ASSERTION_ABSENT` about a criterion the spec states), which
|
|
72
|
+
* leaves the plan `inconsistent` with nothing to correct.
|
|
73
|
+
*/
|
|
74
|
+
export const ACCEPTANCE_CRITERIA_KEY = "acceptance criteria";
|
|
75
|
+
/**
|
|
76
|
+
* The allowlisted sections this spec declares, in document order, one entry per
|
|
77
|
+
* APPEARANCE.
|
|
78
|
+
*
|
|
79
|
+
* A section repeated in the document yields one entry per occurrence rather than
|
|
80
|
+
* the first: taking only the first would seal the later block's text while no
|
|
81
|
+
* reader of the section could see it — and the criteria harvest is such a
|
|
82
|
+
* reader, so a criterion in the second block would be contract and unaddressable
|
|
83
|
+
* at the same time.
|
|
84
|
+
*
|
|
85
|
+
* A section ends at the next heading of the SAME level or shallower. A deeper
|
|
86
|
+
* one belongs to the body, and that is not a detail: doctrine's own template
|
|
87
|
+
* writes every scenario as `### Scenario: <name>` under `## Scenarios`, so
|
|
88
|
+
* ending the section at any heading would empty the one section that states
|
|
89
|
+
* behavior concretely — and rewriting a THEN would then be editorial.
|
|
90
|
+
*/
|
|
91
|
+
export function functionalSections(headings, total) {
|
|
92
|
+
const out = [];
|
|
93
|
+
for (const [index, heading] of headings.entries()) {
|
|
94
|
+
if (heading.level !== 2)
|
|
95
|
+
continue;
|
|
96
|
+
const key = CANONICAL_BY_ALIAS.get(normalizeHeading(heading.title));
|
|
97
|
+
if (key === undefined)
|
|
98
|
+
continue;
|
|
99
|
+
const next = headings.slice(index + 1).find((h) => h.level <= heading.level);
|
|
100
|
+
out.push({ key, start: heading.line + 1, end: next?.line ?? total });
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
/** `- [x]` / `- [X]` at the head of a collapsed line — a tick, not a promise. */
|
|
105
|
+
const TICKED_BOX = /^([-*+]) \[[xX]\]/;
|
|
106
|
+
/**
|
|
107
|
+
* The functional sections of a spec, keyed by canonical heading and normalized.
|
|
108
|
+
*
|
|
109
|
+
* An OBJECT and not an array, deliberately: {@link semanticDigest} sorts object
|
|
110
|
+
* keys, so REORDERING the document's sections is editorial and does not move the
|
|
111
|
+
* digest — while moving text from `## Scope` to `## Acceptance criteria` does,
|
|
112
|
+
* because the text lands under a different key.
|
|
113
|
+
*
|
|
114
|
+
* Line by line, and only OUTSIDE fenced code blocks:
|
|
115
|
+
* - `trim()`, and internal runs of spaces/tabs collapsed to one — rewrapping and
|
|
116
|
+
* trailing whitespace are how a document reads, not what it promises;
|
|
117
|
+
* - `- [x]` → `- [ ]`, because ticking a criterion is bookkeeping: the spec goes
|
|
118
|
+
* on stating it, and every plan of that spec would otherwise diverge the
|
|
119
|
+
* moment somebody marked one;
|
|
120
|
+
* - runs of blank lines collapsed to one, and the blank borders of each section
|
|
121
|
+
* dropped.
|
|
122
|
+
*
|
|
123
|
+
* Inside a fence every line is kept VERBATIM except its line ending: a fenced
|
|
124
|
+
* block is a literal — a payload shape, a command, an expected output — where
|
|
125
|
+
* a space is promised, but the `\r` of a CRLF checkout is the filesystem's,
|
|
126
|
+
* not the spec's.
|
|
127
|
+
*
|
|
128
|
+
* A section that is absent is omitted rather than keyed to an empty string, so a
|
|
129
|
+
* spec that never had `## Scenarios` digests the same as one that never will. A
|
|
130
|
+
* section that appears twice is concatenated in document order.
|
|
131
|
+
*/
|
|
132
|
+
export function functionalSpecPayload(specText) {
|
|
133
|
+
const { lines, fenced, headings } = scanMarkdown(specText);
|
|
134
|
+
const payload = {};
|
|
135
|
+
for (const section of functionalSections(headings, lines.length)) {
|
|
136
|
+
const body = normalizeBody(lines, fenced, section.start, section.end);
|
|
137
|
+
const previous = payload[section.key];
|
|
138
|
+
payload[section.key] = previous === undefined ? body : `${previous}\n${body}`;
|
|
139
|
+
}
|
|
140
|
+
return payload;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The digest a plan's baseline seals — `sha256:<64 hex>`, the SAME shape the
|
|
144
|
+
* byte-exact digest published.
|
|
145
|
+
*
|
|
146
|
+
* The form does not change on purpose: `BASELINE_VALUE`, `isDigest` and a note's
|
|
147
|
+
* anchor all read that shape, and this is a legitimate sha256 — of the payload
|
|
148
|
+
* instead of the file. A second prefix would cost a regex, the note validators
|
|
149
|
+
* and three messages for no reading anybody gains.
|
|
150
|
+
*
|
|
151
|
+
* An EMPTY payload degrades to the byte-exact digest, and that is a safe
|
|
152
|
+
* degradation and not a border case somebody tolerated. A payload with no
|
|
153
|
+
* sections digests to one CONSTANT shared by every spec in that state, so a plan
|
|
154
|
+
* sealed against it stays `aligned` no matter what is done to its spec —
|
|
155
|
+
* including rewriting or emptying the whole document. And the state is reachable
|
|
156
|
+
* without anybody declaring a spec without a contract: one UNCLOSED fence in
|
|
157
|
+
* `## Context` marks the rest of the file as fenced, so `scanMarkdown` sees no
|
|
158
|
+
* further heading and every allowlisted section disappears at once. Falling back
|
|
159
|
+
* here makes the seal sensitive to any edit again, so that state is not left
|
|
160
|
+
* indistinguishable from a spec that genuinely declares no contract:
|
|
161
|
+
* {@link unclosedSpecFence} names it, and whoever reports a divergence or
|
|
162
|
+
* prepares a re-seal says WHICH fence to close instead of sending somebody to
|
|
163
|
+
* diff a spec that did not change.
|
|
164
|
+
*/
|
|
165
|
+
export function functionalSpecDigest(specText) {
|
|
166
|
+
const payload = functionalSpecPayload(specText);
|
|
167
|
+
if (Object.keys(payload).length === 0)
|
|
168
|
+
return specBaselineDigest(specText);
|
|
169
|
+
return `sha256:${semanticDigest(payload)}`;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* The line where this spec left a fence open, or `null` — 0-based, like
|
|
173
|
+
* {@link MarkdownHeading.line}.
|
|
174
|
+
*
|
|
175
|
+
* The ONE observable for the state described on {@link functionalSpecDigest}: an
|
|
176
|
+
* empty payload can mean "no contract declared" or "one fence swallowed the
|
|
177
|
+
* document", and the two need opposite corrections. A number rather than a
|
|
178
|
+
* status type because the index already distinguishes them and a second sum type
|
|
179
|
+
* would be a vocabulary nobody reads.
|
|
180
|
+
*/
|
|
181
|
+
export function unclosedSpecFence(specText) {
|
|
182
|
+
return scanMarkdown(specText).unclosedFence;
|
|
183
|
+
}
|
|
184
|
+
/** One section's body under the rules documented on {@link functionalSpecPayload}. */
|
|
185
|
+
function normalizeBody(lines, fenced, start, end) {
|
|
186
|
+
const out = [];
|
|
187
|
+
for (let i = start; i < end; i += 1) {
|
|
188
|
+
const verbatim = fenced[i] === true;
|
|
189
|
+
const raw = lines[i] ?? "";
|
|
190
|
+
const line = {
|
|
191
|
+
verbatim,
|
|
192
|
+
text: verbatim ? raw.replace(/\r$/, "") : normalizeLine(raw),
|
|
193
|
+
};
|
|
194
|
+
if (dropped(line, out[out.length - 1]))
|
|
195
|
+
continue;
|
|
196
|
+
out.push(line);
|
|
197
|
+
}
|
|
198
|
+
// The section's trailing blanks: a heading followed by whitespace before the
|
|
199
|
+
// next one must digest as the same section as one followed by none.
|
|
200
|
+
while (isBlank(out[out.length - 1]))
|
|
201
|
+
out.pop();
|
|
202
|
+
return out.map((line) => line.text).join("\n");
|
|
203
|
+
}
|
|
204
|
+
/** A blank the payload does not keep: the section's leading one, or a repeat. */
|
|
205
|
+
function dropped(line, previous) {
|
|
206
|
+
if (!isBlank(line))
|
|
207
|
+
return false;
|
|
208
|
+
return previous === undefined || isBlank(previous);
|
|
209
|
+
}
|
|
210
|
+
/** Blank AND collapsible: an empty line inside a fence is content. */
|
|
211
|
+
function isBlank(line) {
|
|
212
|
+
return line !== undefined && !line.verbatim && line.text.length === 0;
|
|
213
|
+
}
|
|
214
|
+
function normalizeLine(raw) {
|
|
215
|
+
return raw
|
|
216
|
+
.trim()
|
|
217
|
+
.replace(/[ \t]+/g, " ")
|
|
218
|
+
.replace(TICKED_BOX, "$1 [ ]");
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=spec-functional.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-functional.js","sourceRoot":"","sources":["../../../src/application/parsers/spec-functional.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,22 @@ import { CORRELATIVE_SOURCE, compareCorrelatives } from "../../domain/correlativ
|
|
|
2
2
|
import { DEFAULT_CORE_DOCS_CANON } from "../../domain/docs-canon.js";
|
|
3
3
|
import { parsePlanBaseline, specCriteria } from "../../domain/lineage.js";
|
|
4
4
|
import { parseMdSectionBilingual, scanMarkdown } from "../markdown.js";
|
|
5
|
+
import { ACCEPTANCE_CRITERIA_KEY, functionalSections, } from "./spec-functional.js";
|
|
5
6
|
// `Spec 011` / `spec 011` — a bare number is NOT evidence: `sesión 047` and
|
|
6
7
|
// `baseline 044` would match it, and the whole point is to never guess.
|
|
7
8
|
const SPEC_REFERENCE_RE = new RegExp(`\\bspec\\s+(${CORRELATIVE_SOURCE})\\b`, "gi");
|
|
8
9
|
const DERIVED_FROM_RE = /derived from/i;
|
|
10
|
+
/**
|
|
11
|
+
* `> Standalone: <prosa>` — the marker a plan born in the host conversation
|
|
12
|
+
* carries instead of a `Derived from`.
|
|
13
|
+
*
|
|
14
|
+
* Same shape as the seal's own label, deliberately: fixed English label, one
|
|
15
|
+
* blockquote line, free prose after the colon. The `>` is part of the grammar —
|
|
16
|
+
* a sentence about the marker is not the marker — and the value is checked for
|
|
17
|
+
* content by the caller, because `\s*(.+?)\s*$` still matches a line whose
|
|
18
|
+
* whole value is whitespace.
|
|
19
|
+
*/
|
|
20
|
+
const STANDALONE_LABEL = /^\s*>\s*Standalone:\s*(.+?)\s*$/i;
|
|
9
21
|
/**
|
|
10
22
|
* The spec a plan declares as its source, by NUMBER — resolving the number
|
|
11
23
|
* against the real spec inventory belongs to the caller.
|
|
@@ -21,6 +33,15 @@ const DERIVED_FROM_RE = /derived from/i;
|
|
|
21
33
|
* 3. An unambiguous `Spec NNN` reference inside `## Origin` — the weakest
|
|
22
34
|
* accepted form, scoped to that section so a mention in `## Dependencies`
|
|
23
35
|
* ("la spec 009 consumirá…") is never mistaken for provenance.
|
|
36
|
+
* 4. `> Standalone: <prosa>` in the header blockquote — the plan declaring that
|
|
37
|
+
* it has NO spec because it was born in the conversation. `standalone` is a
|
|
38
|
+
* diagnosis, not a defect: the board routes it and the deviation gate lets it
|
|
39
|
+
* register decisions, neither of which `absent` may do.
|
|
40
|
+
*
|
|
41
|
+
* The order is what makes level 4 safe to add. It is read LAST, so a plan that
|
|
42
|
+
* carries both the marker and real spec evidence resolves to its spec and the
|
|
43
|
+
* marker stays inert — the marker can never demote a declared lineage, and
|
|
44
|
+
* nothing has to be said about a document that contradicts itself.
|
|
24
45
|
*
|
|
25
46
|
* Two different specs at the same level is `ambiguous`, not a coin flip:
|
|
26
47
|
* a human wrote something contradictory and only a human should settle it.
|
|
@@ -41,7 +62,7 @@ export function parseSpecRelation(text, specDir = DEFAULT_CORE_DOCS_CANON.spec)
|
|
|
41
62
|
return { status: "declared", number: first, evidence };
|
|
42
63
|
return { status: "ambiguous", numbers, evidence };
|
|
43
64
|
}
|
|
44
|
-
return { status: "absent" };
|
|
65
|
+
return declaresStandalone(text) ? { status: "standalone" } : { status: "absent" };
|
|
45
66
|
}
|
|
46
67
|
/**
|
|
47
68
|
* The baseline a plan sealed: WHICH bytes of its spec it was derived from.
|
|
@@ -87,10 +108,100 @@ export function parseDerivedFromPath(text, specDir = DEFAULT_CORE_DOCS_CANON.spe
|
|
|
87
108
|
const unique = [...new Set(found)];
|
|
88
109
|
return unique.length === 1 ? unique[0] : null;
|
|
89
110
|
}
|
|
90
|
-
/**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
/**
|
|
112
|
+
* The acceptance criteria a spec states, in document order and without
|
|
113
|
+
* duplicates — addressable with the ONE grammar (`S033/AC-01`).
|
|
114
|
+
*
|
|
115
|
+
* Two harvests of the same closed grammar, united:
|
|
116
|
+
*
|
|
117
|
+
* 1. every literal `S{NNN}/AC-nn` the document mentions, anywhere — what the
|
|
118
|
+
* design subsystem already read, unchanged;
|
|
119
|
+
* 2. when `specNumber` is given, the LABELS inside `## Acceptance criteria` —
|
|
120
|
+
* every appearance of it, located by the same function the SEAL uses:
|
|
121
|
+
* `- [ ] AC-01: …` states criterion `S{NNN}/AC-01` of that spec.
|
|
122
|
+
*
|
|
123
|
+
* The second half is why a decision note can exist at all. Real specs rotulate
|
|
124
|
+
* their criteria the way doctrine's own template does — with the bare label —
|
|
125
|
+
* so harvesting only form 1 returned `[]` for them, every note addressing a
|
|
126
|
+
* criterion blocked as `CONTRACT_ASSERTION_ABSENT` ("the spec does not state
|
|
127
|
+
* it", about a spec that states it), and the deviation gate's composable exit
|
|
128
|
+
* was closed for every spec in existence.
|
|
129
|
+
*
|
|
130
|
+
* Both forms produce the SAME id, and the dedupe is what lets a spec label a
|
|
131
|
+
* criterion and mention it in `## Scenarios` without stating it twice.
|
|
132
|
+
*
|
|
133
|
+
* `specNumber` must be the three-digit correlative the grammar admits: `S` plus
|
|
134
|
+
* three digits is the whole vocabulary, so a four-digit correlative cannot form
|
|
135
|
+
* a valid id and derives nothing rather than emitting one nobody can address.
|
|
136
|
+
* Without `specNumber` the answer is byte-identical to harvest 1 alone.
|
|
137
|
+
*/
|
|
138
|
+
export function parseSpecCriteria(text, specNumber) {
|
|
139
|
+
const { lines, fenced, headings } = scanMarkdown(text);
|
|
140
|
+
if (specNumber === undefined || !/^\d{3}$/.test(specNumber)) {
|
|
141
|
+
return specCriteria(lines, fenced);
|
|
142
|
+
}
|
|
143
|
+
const checklists = criteriaSections(headings, lines.length);
|
|
144
|
+
const seen = new Set();
|
|
145
|
+
const out = [];
|
|
146
|
+
const add = (id) => {
|
|
147
|
+
if (seen.has(id))
|
|
148
|
+
return;
|
|
149
|
+
seen.add(id);
|
|
150
|
+
out.push(id);
|
|
151
|
+
};
|
|
152
|
+
for (const [index, line] of lines.entries()) {
|
|
153
|
+
if (line === undefined || fenced[index] === true)
|
|
154
|
+
continue;
|
|
155
|
+
// The mention harvest, asked one line at a time: the union comes out in
|
|
156
|
+
// document order and the grammar still lives in exactly one place.
|
|
157
|
+
for (const id of specCriteria([line], [false]))
|
|
158
|
+
add(id);
|
|
159
|
+
if (!checklists.some((section) => index >= section.start && index < section.end))
|
|
160
|
+
continue;
|
|
161
|
+
const label = LABELLED_AC.exec(line)?.[1];
|
|
162
|
+
if (label !== undefined)
|
|
163
|
+
add(`S${specNumber}/${label}`);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* A checklist item whose label IS its criterion: `- [ ] AC-01:`, `- [x] **AC-02**:`,
|
|
169
|
+
* `- [ ] AC-CAP-03.`
|
|
170
|
+
*
|
|
171
|
+
* Anchored at the item marker, so the older `- [ ] **S013/AC-CAP-01:**` does not
|
|
172
|
+
* match here — it is already harvest 1, and matching both would be one criterion
|
|
173
|
+
* counted twice under two spellings.
|
|
174
|
+
*
|
|
175
|
+
* The three Markdown bullet markers, the same ones the payload's tick
|
|
176
|
+
* normalization accepts: what the SEAL digests as contract is every line of the
|
|
177
|
+
* section whatever its marker, so recognizing only `-` here would make
|
|
178
|
+
* `* [ ] AC-04: …` contract and unaddressable at once — a note amending it dies
|
|
179
|
+
* in `CONTRACT_ASSERTION_ABSENT` about a criterion its own spec states.
|
|
180
|
+
*
|
|
181
|
+
* The label ends at a TOKEN BOUNDARY rather than at a closed set of delimiters,
|
|
182
|
+
* for that same reason one level down. The doctrine recommends EARS
|
|
183
|
+
* (`- [ ] AC-01 WHEN … THEN …`) and this project writes `- [ ] AC-01 — outcome`:
|
|
184
|
+
* demanding `:` or `.` right after the label would seal both as contract and
|
|
185
|
+
* leave them unaddressable. The lookahead still refuses `AC-01abc` and
|
|
186
|
+
* `AC-01-2`, which are not ids this grammar can form.
|
|
187
|
+
*/
|
|
188
|
+
const LABELLED_AC = /^\s*[-*+]\s*\[[ xX]\]\s*\*{0,2}(AC-(?:[A-Z]+-)?\d+)\*{0,2}(?![0-9A-Za-z-])/;
|
|
189
|
+
/**
|
|
190
|
+
* Every `## Acceptance criteria` section of the spec, in document order.
|
|
191
|
+
*
|
|
192
|
+
* Bounded because a bare label only means a criterion THERE: `- [ ] AC-01: …`
|
|
193
|
+
* inside `## Open questions` is a question about a criterion, and a checklist in
|
|
194
|
+
* `## Validations` is work to do.
|
|
195
|
+
*
|
|
196
|
+
* ALL of them, and located by {@link functionalSections} — the very function the
|
|
197
|
+
* SEAL uses to decide which text is contract. Two answers to "where are the
|
|
198
|
+
* criteria" is how one half of the lineage seals a criterion the other half
|
|
199
|
+
* cannot address: a spec whose checklist is split in two blocks would have its
|
|
200
|
+
* second block digested as contract while a note amending anything in it is
|
|
201
|
+
* refused as absent from the spec, and the plan never closes.
|
|
202
|
+
*/
|
|
203
|
+
function criteriaSections(headings, total) {
|
|
204
|
+
return functionalSections(headings, total).filter((section) => section.key === ACCEPTANCE_CRITERIA_KEY);
|
|
94
205
|
}
|
|
95
206
|
/**
|
|
96
207
|
* Spec paths on the `Derived from` lines of the header blockquote — the block
|
|
@@ -112,6 +223,32 @@ function derivedFromNumbers(text, specPath) {
|
|
|
112
223
|
}
|
|
113
224
|
return dedupe(found);
|
|
114
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Whether the plan DECLARES itself standalone in its header blockquote.
|
|
228
|
+
*
|
|
229
|
+
* Bounded exactly where `Derived from` and `> Baseline:` are bounded — between
|
|
230
|
+
* the title and the first `##`, fences excluded — because that is the one place
|
|
231
|
+
* a plan speaks about its own provenance. Anywhere else the same words are prose
|
|
232
|
+
* ABOUT the marker: a plan explaining in `## Origin` that "los planes standalone
|
|
233
|
+
* llevan `> Standalone: …`" is documenting the grammar, not adopting it.
|
|
234
|
+
*
|
|
235
|
+
* A marker with no value (`> Standalone:`) declares nothing, so it is not one.
|
|
236
|
+
* The point of the value is that the next reader learns WHERE the plan came from
|
|
237
|
+
* without a spec to open, and an empty label answers that with silence.
|
|
238
|
+
*/
|
|
239
|
+
function declaresStandalone(text) {
|
|
240
|
+
const { lines, fenced, headings } = scanMarkdown(text);
|
|
241
|
+
const firstSection = headings.find((h) => h.level >= 2);
|
|
242
|
+
const end = firstSection?.line ?? lines.length;
|
|
243
|
+
for (let i = 0; i < end; i += 1) {
|
|
244
|
+
const line = lines[i];
|
|
245
|
+
if (line === undefined || fenced[i] === true)
|
|
246
|
+
continue;
|
|
247
|
+
if ((STANDALONE_LABEL.exec(line)?.[1] ?? "").trim().length > 0)
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
115
252
|
/** Match a spec path under the workspace's declared documentary canon. */
|
|
116
253
|
function specPathPattern(specDir) {
|
|
117
254
|
const escaped = specDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec-relation.js","sourceRoot":"","sources":["../../../src/application/parsers/spec-relation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EAAyB,iBAAiB,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACjG,OAAO,
|
|
1
|
+
{"version":3,"file":"spec-relation.js","sourceRoot":"","sources":["../../../src/application/parsers/spec-relation.ts"],"names":[],"mappings":"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"}
|
|
@@ -83,6 +83,19 @@ export class PathsService {
|
|
|
83
83
|
cwdSessionBindingsFile() {
|
|
84
84
|
return join(this.cwdSessionsDir(), ".bindings.json");
|
|
85
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Where a lifecycle surface parks a CHECKPOINT it could not file.
|
|
88
|
+
*
|
|
89
|
+
* Dot-prefixed for the same reason as the two entries around it:
|
|
90
|
+
* `listSessionFolders` skips dot-prefixed entries, so a refuge is never read
|
|
91
|
+
* as a session — which matters more here than anywhere else, because what
|
|
92
|
+
* lands in this directory is precisely the state of a conversation whose
|
|
93
|
+
* session could NOT be resolved. It is inside `.${ns}/sessions/`, so the
|
|
94
|
+
* gitignore the CLI manages already covers it.
|
|
95
|
+
*/
|
|
96
|
+
cwdSessionsRefugeDir() {
|
|
97
|
+
return join(this.cwdSessionsDir(), ".refuge");
|
|
98
|
+
}
|
|
86
99
|
/**
|
|
87
100
|
* Monotone attempt counters of the flow runs, one file per session folder.
|
|
88
101
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths-service.js","sourceRoot":"","sources":["../../src/application/paths-service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"paths-service.js","sourceRoot":"","sources":["../../src/application/paths-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAIxD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAO1C,MAAM,OAAO,YAAY;IAEJ;IACA;IAEA;IAJnB,YACmB,EAAa,EACb,IAAY;IAC7B,+DAA+D;IAC9C,IAAY;QAHZ,OAAE,GAAF,EAAE,CAAW;QACb,SAAI,GAAJ,IAAI,CAAQ;QAEZ,SAAI,GAAJ,IAAI,CAAQ;IAC5B,CAAC;IAEJ,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,4CAA4C;IAC5C,YAAY;QACV,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,4BAA4B;IAC5B,QAAQ;QACN,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,UAAU;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IACD,WAAW;QACT,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IACD,sBAAsB;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,sBAAsB,CAAC,CAAC;IACzD,CAAC;IACD,WAAW;QACT,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC,CAAC;IACvC,CAAC;IACD;;;;;OAKG;IACH,gBAAgB,CAAC,IAAU;QACzB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,kBAAkB,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9E,CAAC;IACD,gBAAgB;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IAChD,CAAC;IACD,eAAe;QACb,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,gBAAgB,EAAE,cAAc,CAAC,CAAC;IACjE,CAAC;IACD,YAAY;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACjD,CAAC;IACD,qBAAqB,CAAC,IAAY;QAChC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC;IACxD,CAAC;IACD,iBAAiB;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC,EAAE,eAAe,CAAC,CAAC;IAClE,CAAC;IACD;;;;;OAKG;IACH,YAAY;QACV,OAAO,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,6DAA6D;IAC7D,OAAO;QACL,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;IAC1C,CAAC;IACD;;;;OAIG;IACH,sBAAsB;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACvD,CAAC;IACD;;;;;;;;;OASG;IACH,oBAAoB;QAClB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,SAAS,CAAC,CAAC;IAChD,CAAC;IACD;;;;;;;;;;OAUG;IACH,kBAAkB;QAChB,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,gBAAgB,CAAC,CAAC;IACvD,CAAC;IACD,mBAAmB,CAAC,OAAe;QACjC,OAAO,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,EAAE,GAAG,OAAO,OAAO,CAAC,CAAC;IAC5D,CAAC;IACD,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,YAAY,CAAC,CAAC;IAC5C,CAAC;IACD,WAAW;QACT,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IACD,uFAAuF;IACvF,gBAAgB;QACd,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,CAAC,CAAC;IAChD,CAAC;IACD,4FAA4F;IAC5F,YAAY;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IACxC,CAAC;IACD,sEAAsE;IACtE,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,kFAAkF;IAClF,cAAc;QACZ,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,CAAC;IAC9C,CAAC;IACD,aAAa;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;IAC7C,CAAC;IAED,8CAA8C;IAC9C,YAAY;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,WAAW,EAAE,CAAC;QACpC,OAAO;YACL,KAAK,EAAE,QAAQ,KAAK,oBAAoB;YACxC,GAAG,EAAE,QAAQ,KAAK,kBAAkB;SACrC,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAkB,EAClB,IAAa,EACb,KAAmB;IAEnB,OAAO,wBAAwB,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,EAAkB,EAClB,KAAmB,EACnB,OAAe,KAAK,CAAC,YAAY,EAAE;IAEnC,MAAM,KAAK,GAAG,IAAI,CAAC;IACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,KAAK,CAAC,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;IACzD,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,OAAO,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO,GAAG,CAAC;QACtE,CAAC;QAAC,MAAM,CAAC;YACP,0DAA0D;QAC5D,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,MAAM,KAAK,GAAG;YAAE,OAAO,KAAK,CAAC;QACjC,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -16,6 +16,7 @@ import { reconciliationOf } from "../domain/reconciliation.js";
|
|
|
16
16
|
import { noteIndexPath, readNoteIndex } from "./decision-note-service.js";
|
|
17
17
|
import { commitDecision, prepareDecision, } from "./decision-registration-service.js";
|
|
18
18
|
import { DEFAULT_DOCS_CANON } from "./docs-canon-service.js";
|
|
19
|
+
import { functionalSpecDigest } from "./parsers/spec-functional.js";
|
|
19
20
|
import { parseDerivedFromPath, parseSpecCriteria, parseSpecRelation, } from "./parsers/spec-relation.js";
|
|
20
21
|
/**
|
|
21
22
|
* Resolve fact/preference/reuse and, only for a real preference, build the
|
|
@@ -124,7 +125,7 @@ export async function commitPlanExecDecision(fs, paths, root, prepared) {
|
|
|
124
125
|
});
|
|
125
126
|
if (!chain.ok)
|
|
126
127
|
return noteFailure(chain.failures[0]);
|
|
127
|
-
const composed = composeEffectiveContract(prepared.baseline, chain.read.index.notes);
|
|
128
|
+
const composed = composeEffectiveContract(await withLegacyDigest(fs, root, prepared.baseline), chain.read.index.notes);
|
|
128
129
|
if (composed.status === "blocked")
|
|
129
130
|
return noteFailure(composed.failures[0]);
|
|
130
131
|
return {
|
|
@@ -145,6 +146,17 @@ export async function commitPlanExecDecision(fs, paths, root, prepared) {
|
|
|
145
146
|
* choice would turn a moved baseline into an invisible different preview.
|
|
146
147
|
*/
|
|
147
148
|
export async function commitStoredPlanExecDecision(fs, paths, root, preparation) {
|
|
149
|
+
// Nothing durable to commit, and nothing to re-read: the standalone record IS
|
|
150
|
+
// what the gate showed. It returns before `commitDecision` on purpose — a note
|
|
151
|
+
// written here would be a decision about a contract that does not exist.
|
|
152
|
+
if (preparation.kind === "standalone") {
|
|
153
|
+
return {
|
|
154
|
+
ok: true,
|
|
155
|
+
kind: "standalone",
|
|
156
|
+
decision: preparation.decision,
|
|
157
|
+
resume_point: preparation.resume_point,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
148
160
|
if (preparation.kind === "settled")
|
|
149
161
|
return { ok: true, kind: "settled" };
|
|
150
162
|
if (preparation.kind === "reused") {
|
|
@@ -167,6 +179,33 @@ export async function commitStoredPlanExecDecision(fs, paths, root, preparation)
|
|
|
167
179
|
indexPath: preparation.index_path,
|
|
168
180
|
});
|
|
169
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* The baseline with the spec's exact-bytes digest alongside its functional one.
|
|
184
|
+
*
|
|
185
|
+
* Needed because the baseline a run PERSISTED before its human gate carries only
|
|
186
|
+
* the functional digest (that is the durable shape, and widening it would change
|
|
187
|
+
* a format for a value that is a pure function of the spec as it reads now). Re-
|
|
188
|
+
* deriving it here is not a re-preview: `digest` and `criteria` are used exactly
|
|
189
|
+
* as they were authorized, and only the tolerance for a note pinned before the
|
|
190
|
+
* functional payload existed is restored — without it, `commitDecision` would
|
|
191
|
+
* WRITE the note and only then refuse to compose the chain it just joined.
|
|
192
|
+
*
|
|
193
|
+
* An unreadable spec degrades to the baseline as given: the composition below
|
|
194
|
+
* then refuses on its own terms, which is the same answer, one step later.
|
|
195
|
+
*/
|
|
196
|
+
async function withLegacyDigest(fs, root, baseline) {
|
|
197
|
+
if (baseline.legacy_digest !== undefined)
|
|
198
|
+
return baseline;
|
|
199
|
+
try {
|
|
200
|
+
return {
|
|
201
|
+
...baseline,
|
|
202
|
+
legacy_digest: specBaselineDigest(await fs.readText(join(root, baseline.path))),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return baseline;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
170
209
|
async function readLineage(fs, input) {
|
|
171
210
|
let planText;
|
|
172
211
|
try {
|
|
@@ -178,7 +217,11 @@ async function readLineage(fs, input) {
|
|
|
178
217
|
const relation = parseSpecRelation(planText);
|
|
179
218
|
const specPath = parseDerivedFromPath(planText);
|
|
180
219
|
if (relation.status !== "declared" || specPath === null) {
|
|
181
|
-
|
|
220
|
+
// Reserved for the plan that SHOULD name a spec and does not: absent or
|
|
221
|
+
// contradictory evidence. A plan that declares itself standalone never gets
|
|
222
|
+
// here — the gate registers its decision in the session — so the action names
|
|
223
|
+
// both exits instead of demanding a lineage the document may not have.
|
|
224
|
+
return fail("FLOW_DECISION_LINEAGE_INVALID", "el plan de la corrida no declara una única spec de origen", "abrí el plan con /w:plan-refine y declarale un único 'Derived from docs/specs/NNN-spec-…'; si nació de la conversación y no deriva de ninguna spec, declaralo con '> Standalone: <de dónde salió>' en su cabecera");
|
|
182
225
|
}
|
|
183
226
|
let specText;
|
|
184
227
|
try {
|
|
@@ -199,10 +242,20 @@ async function readLineage(fs, input) {
|
|
|
199
242
|
ok: true,
|
|
200
243
|
value: {
|
|
201
244
|
baseline: {
|
|
245
|
+
// The FUNCTIONAL digest, which is also what the board reports for an
|
|
246
|
+
// aligned plan — including one whose seal is the legacy byte-exact one.
|
|
247
|
+
// A note pinning anything else would read, the instant it is published,
|
|
248
|
+
// as deciding on a baseline that is no longer in force.
|
|
202
249
|
path: specPath,
|
|
203
250
|
number: relation.number,
|
|
204
|
-
digest:
|
|
205
|
-
|
|
251
|
+
digest: functionalSpecDigest(specText),
|
|
252
|
+
// And the exact bytes alongside it, which is what every note published
|
|
253
|
+
// BEFORE the functional payload pinned: composing this chain against the
|
|
254
|
+
// functional digest alone would refuse an old note over a spec nobody
|
|
255
|
+
// touched, and that refusal has no repair (the substitute note it asks
|
|
256
|
+
// for cannot be prepared — this very composition runs first).
|
|
257
|
+
legacy_digest: specBaselineDigest(specText),
|
|
258
|
+
criteria: parseSpecCriteria(specText, relation.number),
|
|
206
259
|
},
|
|
207
260
|
planNumber,
|
|
208
261
|
planText,
|