@tacuchi/agent-workflow-cli 25.2.0 → 25.3.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.
Files changed (57) hide show
  1. package/README.md +1 -0
  2. package/dist/application/decision-note-service.js +20 -3
  3. package/dist/application/decision-note-service.js.map +1 -1
  4. package/dist/application/flow/advance.js +91 -5
  5. package/dist/application/flow/advance.js.map +1 -1
  6. package/dist/application/flow/internal-actions.js +167 -7
  7. package/dist/application/flow/internal-actions.js.map +1 -1
  8. package/dist/application/flow/submit.js +187 -4
  9. package/dist/application/flow/submit.js.map +1 -1
  10. package/dist/application/plan-current-point.js +54 -0
  11. package/dist/application/plan-current-point.js.map +1 -0
  12. package/dist/application/plan-exec-decision-service.js +143 -4
  13. package/dist/application/plan-exec-decision-service.js.map +1 -1
  14. package/dist/application/plan-locator.js +78 -0
  15. package/dist/application/plan-locator.js.map +1 -0
  16. package/dist/application/plan-open-run.js +129 -0
  17. package/dist/application/plan-open-run.js.map +1 -0
  18. package/dist/application/reseal-service.js +14 -44
  19. package/dist/application/reseal-service.js.map +1 -1
  20. package/dist/application/resume-service.js +9 -3
  21. package/dist/application/resume-service.js.map +1 -1
  22. package/dist/application/settle-service.js +442 -0
  23. package/dist/application/settle-service.js.map +1 -0
  24. package/dist/application/workline-index-service.js +165 -62
  25. package/dist/application/workline-index-service.js.map +1 -1
  26. package/dist/cli/commands/index.js +2 -0
  27. package/dist/cli/commands/index.js.map +1 -1
  28. package/dist/cli/commands/settle.js +197 -0
  29. package/dist/cli/commands/settle.js.map +1 -0
  30. package/dist/cli/commands/status.js +2 -0
  31. package/dist/cli/commands/status.js.map +1 -1
  32. package/dist/cli/help-groups.js +7 -0
  33. package/dist/cli/help-groups.js.map +1 -1
  34. package/dist/cli/parser.js +6 -0
  35. package/dist/cli/parser.js.map +1 -1
  36. package/dist/domain/decision-note.js +166 -3
  37. package/dist/domain/decision-note.js.map +1 -1
  38. package/dist/domain/decision-preview.js.map +1 -1
  39. package/dist/domain/effective-contract.js +4 -3
  40. package/dist/domain/effective-contract.js.map +1 -1
  41. package/dist/domain/flow/answer.js +10 -0
  42. package/dist/domain/flow/answer.js.map +1 -1
  43. package/dist/domain/flow/authority.js +116 -0
  44. package/dist/domain/flow/authority.js.map +1 -1
  45. package/dist/domain/flow/directive.js +8 -1
  46. package/dist/domain/flow/directive.js.map +1 -1
  47. package/dist/domain/flow/run-state.js +95 -9
  48. package/dist/domain/flow/run-state.js.map +1 -1
  49. package/dist/domain/obligation-correspondence.js +95 -0
  50. package/dist/domain/obligation-correspondence.js.map +1 -0
  51. package/dist/domain/reconciliation.js +142 -25
  52. package/dist/domain/reconciliation.js.map +1 -1
  53. package/dist/domain/settlement.js +129 -0
  54. package/dist/domain/settlement.js.map +1 -0
  55. package/package.json +1 -1
  56. package/skills/w/commands/resume.md +2 -2
  57. package/skills/w/loops/plan-exec-loop/LOOP.md +31 -0
@@ -1,3 +1,4 @@
1
+ import { correspondingPlanItem, planHandoffItems } from "./obligation-correspondence.js";
1
2
  /**
2
3
  * Project what the composed contract still owes.
3
4
  *
@@ -5,46 +6,162 @@
5
6
  * is the one place that already decided which notes are in force. Walking the
6
7
  * chain again here would be a second answer to that question.
7
8
  */
8
- export function reconciliationOf(contract, chain) {
9
+ export function reconciliationOf(contract, chain, planText) {
9
10
  const resumeOf = new Map(chain.map((note) => [note.id, note.resume_point]));
11
+ // Read once for the whole reconciliation: every legacy obligation of one plan
12
+ // is judged against one reading of that plan's own enumerated handoffs.
13
+ const items = planText === undefined ? [] : planHandoffItems(planText);
10
14
  const pending = [];
15
+ const handoffs = [];
11
16
  for (const obligation of contract.obligations) {
12
17
  const resume = resumeOf.get(obligation.by);
13
18
  // An obligation whose note is not in the chain handed over cannot say where
14
19
  // to resume, and inventing a point would send the run somewhere nobody
15
20
  // decided. It stays pending — which is the safe half — and names its note.
16
- pending.push({
21
+ const cited = obligation.declared ? null : correspondingPlanItem(obligation.text, items);
22
+ const entry = {
17
23
  text: obligation.text,
18
24
  by: obligation.by,
19
- resume_point: resume ?? `${obligation.by} (nota ausente de la cadena)`,
20
- });
25
+ index: obligation.index,
26
+ declared_point: resume ?? `${obligation.by} (nota ausente de la cadena)`,
27
+ // A declared class is the note's own word and is never second-guessed. An
28
+ // undeclared one reads as a handoff only when the plan itself enumerates
29
+ // that exact work; anything else keeps the safe reading.
30
+ kind: obligation.declared ? obligation.kind : cited === null ? "compensation" : "handoff",
31
+ legacy: !obligation.declared,
32
+ ...(cited === null ? {} : { corresponds_to: cited }),
33
+ };
34
+ (entry.kind === "handoff" ? handoffs : pending).push(entry);
21
35
  }
36
+ return { pending, handoffs, closable: pending.length === 0 };
37
+ }
38
+ /**
39
+ * WHAT A PLAN OWES AND HOW SOMEBODY GETS OUT OF IT — said once, for everybody.
40
+ *
41
+ * Three surfaces used to describe this one situation in three sets of words: the
42
+ * refusal that would not let the plan be sealed, the board's headline, and the
43
+ * action under the pipeline row. They drifted, as three texts about one fact
44
+ * always do — and the drift was not cosmetic. One of them sent the reader back
45
+ * to the point a note recorded at birth, one offered no command at all, and none
46
+ * of them mentioned the command that actually settles anything. So the exit is
47
+ * derived HERE, and each surface takes the part it renders.
48
+ *
49
+ * `null` means nothing is owed. Otherwise there is always a `command`: a refusal
50
+ * with no exit is a wall, and this whole phase exists because the board had one.
51
+ */
52
+ export function obligationExit(reconciliation, context) {
53
+ const owed = whatIsOwed(reconciliation);
54
+ if (owed === null)
55
+ return null;
56
+ if (owed.repair !== undefined)
57
+ return unreadableExit(owed.text, owed.repair, context);
58
+ return owed.kind === "compensation"
59
+ ? compensationExit(owed.text, context)
60
+ : handoffExit(owed.text, context);
61
+ }
62
+ /**
63
+ * The first thing owed, and of which class — compensation before handoff.
64
+ *
65
+ * A plan owing both is held by the compensation, so that is what its exit has to
66
+ * be about; the handoff is still listed underneath. And "not closable with
67
+ * nothing named" is the composition refusing itself — reported as the shape it
68
+ * is, because falling through to the handoffs there would describe a plan that
69
+ * cannot be read as one that merely delegated some work.
70
+ */
71
+ function whatIsOwed(reconciliation) {
72
+ const [compensation] = reconciliation.pending;
73
+ if (compensation !== undefined) {
74
+ return {
75
+ kind: "compensation",
76
+ text: named(compensation, reconciliation.pending.length),
77
+ ...(compensation.repair === undefined ? {} : { repair: compensation.repair }),
78
+ };
79
+ }
80
+ if (!reconciliation.closable) {
81
+ return { kind: "compensation", text: "el contrato efectivo no se puede componer" };
82
+ }
83
+ const [handoff] = reconciliation.handoffs;
84
+ if (handoff === undefined)
85
+ return null;
86
+ return { kind: "handoff", text: named(handoff, reconciliation.handoffs.length) };
87
+ }
88
+ /**
89
+ * How a surface says that a class is a READING, not the note's own word.
90
+ *
91
+ * One phrase for every surface that shows it, because two spellings of it is
92
+ * two answers to "did a person ratify this?" — and that question is the whole
93
+ * point of keeping the distinction.
94
+ */
95
+ export function readingMark(kind) {
96
+ return `clase no declarada, leída ${kind === "handoff" ? "traspaso" : "compensación"}`;
97
+ }
98
+ /** `DEC-001 — revalidar F1 (+2 más)`: the cause, the work, and the rest of them. */
99
+ function named(obligation, total) {
100
+ const more = total - 1;
101
+ const rest = more > 0 ? ` (+${more} más)` : "";
102
+ // A fabricated pending has no cause to keep: its `by` is a label the
103
+ // projection invented for a note nobody wrote, and printing it as the author
104
+ // of the work would be the second wrong text about the same fact.
105
+ const cause = obligation.repair === undefined ? `${obligation.by} — ` : "";
106
+ // And an undeclared class travels as what it is. The non-blocking reading is
107
+ // the one most worth flagging: a legacy handoff makes the plan closable, and a
108
+ // board asserting it as the note's own word hides that nobody ratified it.
109
+ const read = obligation.legacy ? ` (${readingMark(obligation.kind)})` : "";
110
+ return `${cause}${obligation.text}${read}${rest}`;
111
+ }
112
+ function compensationExit(owed, context) {
113
+ const settle = `aw settle prepare ${context.plan}`;
114
+ const run = context.run;
22
115
  return {
23
- pending,
24
- resume_point: pending[0]?.resume_point ?? null,
25
- closable: pending.length === 0,
116
+ kind: "compensation",
117
+ owed,
118
+ point: context.current_point,
119
+ command: run === null ? settle : run.command,
120
+ // With a run open the settlement rows are inside it, and the hedge is not
121
+ // padding: the cursor only ever grows, so a run already past them cannot go
122
+ // back — and for that one the exit is the command, once its journey ends.
123
+ action: run === null
124
+ ? `no hay corrida abierta sobre '${context.plan}': leé y declará el saldo con '${settle}', y aplicalo con el digest que devuelve`
125
+ : `la sesión ${run.session} ${run.why}, y la frontera de saldo de una corrida es donde se declara la evidencia: si todavía no la pasó, seguila con '${run.command}'; si ya la pasó, la salida es '${settle}' cuando el recorrido termine`,
126
+ headline: `COMPENSACIÓN VIGENTE por ${owed}: retomá en ${context.current_point}, ni ejecutable ni cerrable tal cual`,
26
127
  };
27
128
  }
28
129
  /**
29
- * Whether this plan may be declared closed, and what to do when it may not.
130
+ * The exit of a lineage nobody can read — the repair, and nothing about phases.
30
131
  *
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.
132
+ * `point` carries the plan's current point anyway, because a caller may still
133
+ * want to say where the plan stands; what changes is that no TEXT offers it,
134
+ * since a broken chain is not fixed by going back to a phase. The old board
135
+ * printed `retomá en <repair>` — the right instruction under the wrong label —
136
+ * and F4's rule that a declared point never reaches a surface would have dropped
137
+ * it altogether: the one actionable sentence in the whole situation.
35
138
  */
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
- ];
139
+ function unreadableExit(owed, repair, context) {
140
+ const settle = `aw settle prepare ${context.plan}`;
141
+ const run = context.run;
142
+ return {
143
+ kind: "compensation",
144
+ owed,
145
+ point: context.current_point,
146
+ command: run === null ? settle : run.command,
147
+ action: `${repair}. Después de eso el linaje vuelve a leerse${run === null
148
+ ? `, y la salida es '${settle}'`
149
+ : `, y la sesión ${run.session} —que ${run.why}— sigue con '${run.command}'`}`,
150
+ headline: `LINAJE ILEGIBLE por ${owed} — ${repair}: ni ejecutable ni cerrable tal cual`,
151
+ };
152
+ }
153
+ function handoffExit(owed, context) {
154
+ const settle = `aw settle prepare ${context.plan}`;
155
+ const run = context.run;
156
+ return {
157
+ kind: "handoff",
158
+ owed,
159
+ point: context.current_point,
160
+ command: run === null ? settle : run.command,
161
+ action: run === null
162
+ ? `el traspaso no bloquea nada: reconocelo con '${settle}', y saldalo ahí mismo cuando el trabajo de afuera esté hecho`
163
+ : `el traspaso no bloquea nada: la sesión ${run.session} ${run.why}, así que lo ve en la frontera de saldo de esa corrida; fuera de una corrida se reconoce con '${settle}'`,
164
+ headline: `TRASPASO VIGENTE por ${owed}: es trabajo de afuera y no bloquea el cierre; el plan está en ${context.current_point}`,
165
+ };
49
166
  }
50
167
  //# sourceMappingURL=reconciliation.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"reconciliation.js","sourceRoot":"","sources":["../../src/domain/reconciliation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The successor of one carrier note, or `null` when it would change nothing.
3
+ *
4
+ * Everything but the settled obligations crosses over: the decision, its reason,
5
+ * the assertions it amends, its scope, its consumers and its evidence. What the
6
+ * settlement adds is the evidence each discharge produced — evidence that still
7
+ * counts is exactly what `evidence_preserved` is for — and what it removes is
8
+ * the obligations somebody actually did.
9
+ *
10
+ * `null` and not an empty note: publishing a successor identical in force to its
11
+ * predecessor would grow the chain by one record per attempt and say nothing.
12
+ */
13
+ export function deriveSettlementNote(carrier, settlements, at) {
14
+ const mine = settlements.filter((settlement) => settlement.note === carrier.id);
15
+ const failures = checkSettlements(carrier, mine);
16
+ if (failures.length > 0)
17
+ return { ok: false, failures };
18
+ const byIndex = new Map(mine.map((settlement) => [settlement.index, settlement]));
19
+ const kept = [];
20
+ const classified = [];
21
+ const settled = [];
22
+ const evidence = [];
23
+ for (const [index, obligation] of carrier.obligations.entries()) {
24
+ const settlement = byIndex.get(index);
25
+ if (settlement !== undefined && settlement.outcome === "settled") {
26
+ settled.push(obligation.text);
27
+ if (settlement.evidence !== undefined)
28
+ evidence.push(settlement.evidence);
29
+ continue;
30
+ }
31
+ const held = settlement?.outcome === "handoff" ? reclassified(obligation, "handoff") : obligation;
32
+ kept.push(held);
33
+ // Anything still unclassed leaves carrying the reading the board already
34
+ // reported FOR THAT POSITION — that is what "clasifica lo que queda" means,
35
+ // and without it the successor could not be appended at all. The position is
36
+ // the carrier's, never the survivors': dropping one shifts the rest.
37
+ classified.push(held.declared ? held : reclassified(held, at.resolved.get(index) ?? held.kind));
38
+ }
39
+ if (settled.length === 0 && sameObligations(carrier.obligations, classified)) {
40
+ return { ok: true, draft: null, settled: [] };
41
+ }
42
+ return {
43
+ ok: true,
44
+ settled,
45
+ draft: {
46
+ schema: carrier.schema,
47
+ lineage: {
48
+ spec: carrier.lineage.spec,
49
+ plan: carrier.lineage.plan,
50
+ // The settling run when there is one; the carrier's own when `aw settle`
51
+ // acts on a plan whose run is long closed. Either way it is a real
52
+ // session and a real phase, never a placeholder.
53
+ execution: { session: at.session, phase: at.phase },
54
+ },
55
+ decision: carrier.decision,
56
+ reason: carrier.reason,
57
+ supersedes_assertions: [...carrier.supersedes_assertions],
58
+ supersedes_note: carrier.id,
59
+ scope: carrier.scope,
60
+ consumers: [...carrier.consumers],
61
+ evidence_preserved: [...carrier.evidence_preserved, ...evidence],
62
+ evidence_invalidated: [...carrier.evidence_invalidated],
63
+ obligations: classified,
64
+ resume_point: carrier.resume_point,
65
+ date: at.date,
66
+ },
67
+ };
68
+ }
69
+ function reclassified(obligation, kind) {
70
+ return { text: obligation.text, kind, declared: true };
71
+ }
72
+ function sameObligations(before, after) {
73
+ return (before.length === after.length &&
74
+ before.every((obligation, index) => {
75
+ const other = after[index];
76
+ return (other !== undefined &&
77
+ other.text === obligation.text &&
78
+ other.kind === obligation.kind &&
79
+ other.declared === obligation.declared);
80
+ }));
81
+ }
82
+ /**
83
+ * Every way a settlement can fail to name real work, each with its own code.
84
+ *
85
+ * A settlement that points at an obligation the note does not carry, or that
86
+ * claims work is done without saying what proves it, is not a slightly worse
87
+ * settlement: it is a discharge nobody can audit, and the chain would record it
88
+ * as authoritative.
89
+ */
90
+ function checkSettlements(carrier, settlements) {
91
+ const failures = [];
92
+ const seen = new Set();
93
+ for (const settlement of settlements) {
94
+ const obligation = carrier.obligations[settlement.index];
95
+ if (obligation === undefined) {
96
+ failures.push({
97
+ code: "SETTLEMENT_OBLIGATION_ABSENT",
98
+ message: `${carrier.id} no tiene una obligación en la posición ${settlement.index}`,
99
+ action: `esa nota carga ${carrier.obligations.length} obligación(es): saldá una que exista`,
100
+ });
101
+ continue;
102
+ }
103
+ if (seen.has(settlement.index)) {
104
+ failures.push({
105
+ code: "SETTLEMENT_OBLIGATION_REPEATED",
106
+ message: `${carrier.id} recibe dos saldos para la misma obligación: ${obligation.text}`,
107
+ action: "una obligación se salda una vez; decidí cuál de las dos lecturas vale",
108
+ });
109
+ continue;
110
+ }
111
+ seen.add(settlement.index);
112
+ if (settlement.outcome === "settled" && (settlement.evidence ?? "").trim().length === 0) {
113
+ failures.push({
114
+ code: "SETTLEMENT_EVIDENCE_MISSING",
115
+ message: `se declara cumplida '${obligation.text}' sin decir qué lo prueba`,
116
+ action: "declará la evidencia del saldo: un comando corrido, una prueba o una inspección; sin eso no es un saldo, es una afirmación",
117
+ });
118
+ }
119
+ if (settlement.outcome !== "settled" && settlement.evidence !== undefined) {
120
+ failures.push({
121
+ code: "SETTLEMENT_EVIDENCE_UNEXPECTED",
122
+ message: `'${obligation.text}' no se declara cumplida y aun así trae evidencia`,
123
+ action: "la evidencia acredita un saldo: sacala, o declará la obligación cumplida",
124
+ });
125
+ }
126
+ }
127
+ return failures;
128
+ }
129
+ //# sourceMappingURL=settlement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settlement.js","sourceRoot":"","sources":["../../src/domain/settlement.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tacuchi/agent-workflow-cli",
3
- "version": "25.2.0",
3
+ "version": "25.3.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,10 +32,10 @@ No target → **every** pending item as `candidates` in the CLI's order, `propos
32
32
 
33
33
  ## What the CLI decides (do not re-derive)
34
34
 
35
- - **Priority**: unrefined spec → refined spec with no plan → plan not `done`; started first. A loose session is a notice, never a candidate.
35
+ - **Priority**: unrefined spec → refined spec with no plan → plan not `done` → live handoff; started first. A loose session is a notice, never a candidate.
36
36
  - **Ties**: equal priority and progress → no single recommendation; date never splits.
37
37
  - **Spec→plan link**: `Derived from` or `## Origin`, never the slug; unproven stays unplanned.
38
- - **The route**: `/w:spec-refine`, `/w:plan-new`, `/w:plan-exec`, `aw session-resume --reopen`.
38
+ - **The route**: `/w:spec-refine`, `/w:plan-new`, `/w:plan-exec`, `aw settle prepare`, `aw session-resume --reopen`.
39
39
 
40
40
  > A plan is not finished because its boxes are ticked: re-entry is the first phase not `validada`, a `bloqueada` phase with its declared reason, or — phases green, plan never closed — the final validation. One declaring `done` over open work comes back as inconsistent. An obligation leaving it neither runnable nor closable is said before its percentage.
41
41
 
@@ -151,6 +151,37 @@ run stops at it: declaring a deviation no longer carries on to the commit by its
151
151
 
152
152
  > **How an affirmation is addressed:** a decision note names the criteria it amends as `S{NNN}/AC-nn` — `NNN` from the spec file's number, `AC-nn` the label the criterion carries in the spec's checklist. The CLI **derives** that id from the label plus the file number, so the readable form the spec template writes is already addressable (a criterion that spells the full id is read the same way, never counted twice). A note amending a criterion the spec does not state **at all** is refused with `CONTRACT_ASSERTION_ABSENT`, and the exit is the spec stating it (`spec-refine`), never a second spelling of the id here.
153
153
 
154
+ ### Obligations — the two classes, and how each one is settled
155
+
156
+ A note may leave work behind, and it declares **which of two classes** each piece is. A
157
+ **compensation** is work this lineage owes: it holds the plan's closure shut until it is settled. A
158
+ **handoff** is work somebody outside the run took on: it stays listed, it stays visible after the plan
159
+ closes, and it blocks nothing — a run that cannot discharge it could never close. Nothing historical
160
+ moves: the boxes and the phase states record what happened, and the obligation is new work of the
161
+ effective contract.
162
+
163
+ The class is the agent's to state while drafting the note, and the person sees it in the sealed
164
+ preview before registering it; a new note leaving work without saying which class is refused. A note
165
+ published before the class existed is read tolerantly — an undeclared obligation is a **compensation**
166
+ unless the plan itself enumerates that exact work as a handoff — and every surface marks that as a
167
+ reading somebody supplied, never as the note's own word. The class is revocable: the
168
+ settlement boundary may declare a live compensation a handoff after all. So **when in doubt,
169
+ compensation** — the only error a later boundary still catches.
170
+
171
+ Settling one is publishing a note that **supersedes** the one carrying it without carrying it forward;
172
+ there is no second ledger of discharged obligations: one recorded outside the chain
173
+ could disagree with it. The closure does this by itself: between the last `batch-close` and
174
+ the final validation the run declares, per compensation, whether it is done and with what evidence,
175
+ and the CLI derives and publishes the settlement note. A compensation still pending leaves that
176
+ boundary open with an executable action and costs the run no attempt. With **no** open run — a plan
177
+ blocked today whose run closed long ago — the exit is `aw settle`: it lists what is owed, previews the
178
+ note, and publishes it against an approval.
179
+
180
+ **The split between scaffolding and content**, in three lines. A gap with a **single** reading is
181
+ repaired by the run itself, and the repair is recorded. A gap with **more than one** reading is asked
182
+ **once for all of them**, with the run's own reading offered as the recommendation. Everything else
183
+ is **content**, and content is the person's.
184
+
154
185
  **Eligibility is closure, never size.** The composable exit opens only when the four close: the
155
186
  divergence stays in the **same functional lineage**; its **intent is settled**, so nothing further has
156
187
  to be elicited; its **transitive impact can be enumerated**, every consumer nameable; and its