@agenticprimitives/fabric 0.0.0-alpha.5 → 0.0.0-alpha.9

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 (82) hide show
  1. package/CLAUDE.md +12 -2
  2. package/README.md +16 -16
  3. package/dist/case-emissions.d.ts +95 -0
  4. package/dist/case-emissions.d.ts.map +1 -0
  5. package/dist/case-emissions.js +208 -0
  6. package/dist/case-emissions.js.map +1 -0
  7. package/dist/case-machine.d.ts +28 -2
  8. package/dist/case-machine.d.ts.map +1 -1
  9. package/dist/case-machine.js +34 -7
  10. package/dist/case-machine.js.map +1 -1
  11. package/dist/enum-retirement.d.ts +11 -2
  12. package/dist/enum-retirement.d.ts.map +1 -1
  13. package/dist/enum-retirement.js +12 -6
  14. package/dist/enum-retirement.js.map +1 -1
  15. package/dist/exchange-record.d.ts +16 -1
  16. package/dist/exchange-record.d.ts.map +1 -1
  17. package/dist/exchange-record.js.map +1 -1
  18. package/dist/exchange-reducer.d.ts +0 -5
  19. package/dist/exchange-reducer.d.ts.map +1 -1
  20. package/dist/exchange-reducer.js +27 -1
  21. package/dist/exchange-reducer.js.map +1 -1
  22. package/dist/exchange-state.d.ts +30 -8
  23. package/dist/exchange-state.d.ts.map +1 -1
  24. package/dist/exchange-state.js +12 -10
  25. package/dist/exchange-state.js.map +1 -1
  26. package/dist/inbox-doc.d.ts +2 -2
  27. package/dist/inbox-doc.d.ts.map +1 -1
  28. package/dist/inbox-doc.js.map +1 -1
  29. package/dist/inbox-projection.d.ts +2 -2
  30. package/dist/inbox-projection.d.ts.map +1 -1
  31. package/dist/inbox-projection.js.map +1 -1
  32. package/dist/inbox-revision.d.ts +42 -0
  33. package/dist/inbox-revision.d.ts.map +1 -0
  34. package/dist/inbox-revision.js +57 -0
  35. package/dist/inbox-revision.js.map +1 -0
  36. package/dist/index.d.ts +9 -3
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +13 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/interactions/machine.d.ts +2 -2
  41. package/dist/interactions/machine.d.ts.map +1 -1
  42. package/dist/interactions/machine.js +2 -2
  43. package/dist/interactions/machine.js.map +1 -1
  44. package/dist/interactions/store.d.ts +9 -0
  45. package/dist/interactions/store.d.ts.map +1 -1
  46. package/dist/interactions/store.js +57 -2
  47. package/dist/interactions/store.js.map +1 -1
  48. package/dist/messaging/board.d.ts +3 -3
  49. package/dist/messaging/board.d.ts.map +1 -1
  50. package/dist/messaging/board.js +2 -2
  51. package/dist/messaging/board.js.map +1 -1
  52. package/dist/messaging/delivery.d.ts +2 -2
  53. package/dist/messaging/delivery.d.ts.map +1 -1
  54. package/dist/messaging/delivery.js +8 -1
  55. package/dist/messaging/delivery.js.map +1 -1
  56. package/dist/messaging/envelope.d.ts +7 -3
  57. package/dist/messaging/envelope.d.ts.map +1 -1
  58. package/dist/messaging/envelope.js +18 -1
  59. package/dist/messaging/envelope.js.map +1 -1
  60. package/dist/messaging/hash.d.ts +8 -2
  61. package/dist/messaging/hash.d.ts.map +1 -1
  62. package/dist/messaging/hash.js +6 -0
  63. package/dist/messaging/hash.js.map +1 -1
  64. package/dist/messaging/inbox-assistant.d.ts +11 -2
  65. package/dist/messaging/inbox-assistant.d.ts.map +1 -1
  66. package/dist/messaging/inbox-assistant.js +4 -2
  67. package/dist/messaging/inbox-assistant.js.map +1 -1
  68. package/dist/messaging/projector.d.ts +3 -3
  69. package/dist/messaging/projector.d.ts.map +1 -1
  70. package/dist/messaging/projector.js.map +1 -1
  71. package/dist/messaging/receipts.d.ts +3 -3
  72. package/dist/messaging/receipts.d.ts.map +1 -1
  73. package/dist/messaging/receipts.js.map +1 -1
  74. package/dist/messaging/rules.d.ts +2 -2
  75. package/dist/messaging/rules.d.ts.map +1 -1
  76. package/dist/messaging/rules.js.map +1 -1
  77. package/dist/messaging/store.d.ts +4 -4
  78. package/dist/messaging/store.d.ts.map +1 -1
  79. package/dist/messaging/store.js.map +1 -1
  80. package/dist/messaging/vault-store.d.ts +2 -2
  81. package/dist/messaging/vault-store.d.ts.map +1 -1
  82. package/package.json +1 -1
package/CLAUDE.md CHANGED
@@ -27,8 +27,18 @@ adapter ships from `./cloudflare`.
27
27
  `CaseState` enums; `createDurableObjectInteractionStore` is their durable home on the gateway DO,
28
28
  reachable as `do.interactions`. **The store exposes transitions, never state assignment** — the
29
29
  creditor discharges, not the debtor, and persistence must not become a bypass.
30
- - **Next:** the `MessageKind`/`CaseState` REMOVAL (spec 340 §R — the mapping is total and
31
- compiler-checked; the rewrite spans 26 files and is its own wave); an interaction-aware ingress that
30
+ - **W10a DONE (spec 340):** the case flow EMITS. `caseTransitionEmissions` + `applyCaseEmissions` put
31
+ `approve`/`deny`/`expire` into the decision machine and `issue-credential`/`revoke` into the
32
+ commitment machine, applied THROUGH those machines (never state assignment), wired into the
33
+ interaction store the Home drives. `deliver-credential` deliberately emits nothing — completing the
34
+ work discharges no obligation (ADR-0058 §15).
35
+ - **W10b DONE (spec 340):** `CaseState` is EXECUTION-ONLY (11 → 8). `approved`/`denied`/`expired`/
36
+ `revoked` removed, `closed` added. The transition VERBS are unchanged — which is why stored logs
37
+ replay untouched and no migration exists. `issue-credential`'s ordering is now a cross-machine
38
+ precondition (`requires-granted-decision`), projected onto `ExchangeRecordV1.decision` for the
39
+ reducer path, because that path had been getting the guarantee free from the FSM edge.
40
+ - **Next:** `MessageEnvelopeV1.kind` — the LAST enum, and the hard one: it is inside the signed wire
41
+ hash, so removal is an `ap.message.v2` cutover with re-signing, not a cleanup (spec 340 §R.1/§R.3); an interaction-aware ingress that
32
42
  opens/joins an Interaction rather than only writing an Exchange; on-chain `atl:gatewayRoot` record
33
43
  (contracts, separate); `A2aTaskDO`/`SessionStoreDO` absorption + `alarm()` drain; `fetch`/`alarm`
34
44
  e2e via vitest-pool-workers.
package/README.md CHANGED
@@ -1,21 +1,21 @@
1
1
  # @agenticprimitives/fabric
2
2
 
3
- The **Agentic Interaction Fabric** (spec 316): the A2A-native substrate that absorbs `messaging`
4
- (envelope/inbox) and `interactions` (case/run) into ONE canonical exchange object over a two-level
5
- exec/case state machine. Transport-agnostic core; the Cloudflare gateway ships as a `./cloudflare` adapter.
3
+ Ring-0 **operational** realization of Agentic Interaction. Semantics live under `apix:`
4
+ ([ADR-0058](../../docs/architecture/decisions/0058-agentic-interaction-canonical-bounded-context.md) ·
5
+ [spec 340](../../specs/340-agentic-interaction.md)); this package owns wire types, reducers, and the
6
+ Cloudflare gateway adapter — not the ontology namespace (`apfab:` retired).
6
7
 
7
- - **One object:** `ExchangeRecordV1` — an `ExecState` transport envelope carrying an optional 11-state
8
- `CaseState` business substate, coupled by an exhaustive fail-closed table.
9
- - **One reducer:** `applyExchangeEvent` over a single append-only event stream (message | case | artifact |
10
- transport). The inbox is a projection (`projectInboxItem`).
11
- - **Subpaths:** `@agenticprimitives/fabric/messaging` + `/interactions` re-export the absorbed surfaces.
8
+ Runtime doctrine still [spec 316](../../specs/316-agentic-interaction-fabric.md) /
9
+ [ADR-0047](../../docs/architecture/decisions/0047-agent-exchange-fabric-a2a-native.md): A2A-native
10
+ substrate absorbing messaging + interactions into one exchange object over a two-level exec/case FSM.
12
11
 
13
- Authority is never satisfied by the exchange — a request carries an `authorityRef`; approving a case advances
14
- the lifecycle, it never unlocks a resource (ADR-0041).
12
+ - **Exchange:** `ExchangeRecordV1` — `ExecState` + optional `CaseState`, fail-closed coupling.
13
+ - **Interaction (spec 340):** `InteractionRecordV1` + profiles + context snapshots on
14
+ `PrincipalGatewayDO` — **in package; apps still use demo-a2a `InteractionsDO` + vault board**.
15
+ - **Subpaths:** `@agenticprimitives/fabric/messaging` + `/interactions`.
15
16
 
16
- See `CLAUDE.md` for the implementation guide and `../../specs/316-agentic-interaction-fabric.md` (architect
17
- of record) + `../../docs/architecture/agentic-interaction-fabric-target.md` + ADR-0047.
18
- Interaction-flow diagrams (person↔person, org channels, cases) + XMTP/Diode alignment:
19
- [`docs/architecture.md`](docs/architecture.md) · commercial comparison:
20
- [`docs/fabric-capabilities-vs-commercial.md`](docs/fabric-capabilities-vs-commercial.md) · gaps:
21
- [`docs/fabric-capability-gaps.md`](docs/fabric-capability-gaps.md).
17
+ Authority is never satisfied by the exchange (ADR-0041).
18
+
19
+ **Start here for app wiring + adoption matrix:** [`docs/architecture.md`](docs/architecture.md).
20
+ Also: `CLAUDE.md` · [target architecture](../../docs/architecture/agentic-interaction-fabric-target.md) ·
21
+ [commercial comparison](docs/fabric-capabilities-vs-commercial.md) · [gaps](docs/fabric-capability-gaps.md).
@@ -0,0 +1,95 @@
1
+ import type { CanonicalAgentId } from '@agenticprimitives/types';
2
+ import type { CommitmentRecordV1, CommitmentTransition, DecisionRecordV1, DecisionTransition, MachineRejection } from './interaction-machines.js';
3
+ import type { CaseFsmFields, CaseTransition, CaseTransitionEventV1 } from './case-machine.js';
4
+ /**
5
+ * A record this emission needs to exist before its transition can land, given in its INITIAL state.
6
+ *
7
+ * Both machines refuse a transition out of a state the record is not in — `grant` is `pending →
8
+ * granted`, `void` is `proposed|active → void`. So an emission that only carried a transition would be
9
+ * unapplicable on the first move of a case that never had a decision record. The applier creates the
10
+ * record if absent, in exactly this state, and then applies the transition through the machine. It
11
+ * does NOT create it in the resulting state; that would be the state-assignment bypass this module
12
+ * exists to avoid.
13
+ */
14
+ export interface DecisionSeedV1 {
15
+ decisionId: string;
16
+ decider: CanonicalAgentId;
17
+ subject: string;
18
+ expiresAt?: string;
19
+ }
20
+ export interface CommitmentSeedV1 {
21
+ commitmentId: string;
22
+ debtor: CanonicalAgentId;
23
+ creditor: CanonicalAgentId;
24
+ }
25
+ export interface CaseEmissionsV1 {
26
+ decision?: {
27
+ seed: DecisionSeedV1;
28
+ transition: DecisionTransition;
29
+ createIfAbsent: boolean;
30
+ };
31
+ commitment?: {
32
+ seed: CommitmentSeedV1;
33
+ transition: CommitmentTransition;
34
+ createIfAbsent: boolean;
35
+ };
36
+ }
37
+ /**
38
+ * Deterministic ids, derived from the case id.
39
+ *
40
+ * Deliberately not random and not clock-derived: a case transition may be replayed (the interaction
41
+ * store is idempotent on `idempotencyKey`, and `processDue` re-runs sweeps), and a fresh id per replay
42
+ * would fork one determination into several. Same case ⇒ same decision ⇒ the second application is a
43
+ * no-op against a terminal state rather than a duplicate record.
44
+ */
45
+ export declare function decisionIdForCase(caseId: string): string;
46
+ export declare function commitmentIdForCase(caseId: string): string;
47
+ /**
48
+ * Derive what a case transition emits. Pure, and total over `CaseTransition` — a transition with no
49
+ * emission returns `{}` rather than throwing, because most transitions genuinely emit nothing.
50
+ *
51
+ * Call this only for a transition the case machine ACCEPTED. It does not re-check legality: duplicating
52
+ * the case machine's role and ordering rules here would create a second, quietly diverging copy of them.
53
+ */
54
+ export declare function caseTransitionEmissions(current: CaseFsmFields, event: CaseTransitionEventV1): CaseEmissionsV1;
55
+ /** The case transitions that emit something. Exported so a test can assert the set rather than
56
+ * restate it, and so a newly-added emitting transition shows up in a diff of this constant. */
57
+ export declare const EMITTING_CASE_TRANSITIONS: readonly CaseTransition[];
58
+ /** Seed a decision record in its INITIAL state. Never the resulting state — see `DecisionSeedV1`. */
59
+ export declare function seedDecision(seed: DecisionSeedV1, at: string): DecisionRecordV1;
60
+ /** Seed a commitment record in its INITIAL state. */
61
+ export declare function seedCommitment(seed: CommitmentSeedV1, at: string): CommitmentRecordV1;
62
+ /** Why an emission produced no record. `absent` is the deliberate `void`-without-a-commitment case;
63
+ * the others are the owning machine's own typed rejection, surfaced rather than swallowed. */
64
+ export type EmissionOutcome<T> = {
65
+ applied: true;
66
+ record: T;
67
+ } | {
68
+ applied: false;
69
+ reason: MachineRejection | 'absent';
70
+ };
71
+ export interface CaseEmissionApplyResult {
72
+ decision?: EmissionOutcome<DecisionRecordV1>;
73
+ commitment?: EmissionOutcome<CommitmentRecordV1>;
74
+ }
75
+ /**
76
+ * Apply emissions through the OWNING machines.
77
+ *
78
+ * The point of this function is what it does not do: it never assigns a state. A record is created
79
+ * only in its initial state and every move goes through `applyDecisionTransition` /
80
+ * `applyCommitmentTransition`, so their role and ordering checks still hold — the creditor still
81
+ * voids, the decider still decides.
82
+ *
83
+ * Rejections are RETURNED, not thrown and not swallowed. A caller that persists these must treat a
84
+ * rejected emission as a fact about the record it failed to write, because a silent no-op here is
85
+ * indistinguishable from success and would leave a case whose determination was never recorded
86
+ * looking exactly like one whose determination was.
87
+ */
88
+ export declare function applyCaseEmissions(emissions: CaseEmissionsV1, existing: {
89
+ decision: DecisionRecordV1 | null;
90
+ commitment: CommitmentRecordV1 | null;
91
+ }, event: {
92
+ actor: CanonicalAgentId;
93
+ at: string;
94
+ }): CaseEmissionApplyResult;
95
+ //# sourceMappingURL=case-emissions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"case-emissions.d.ts","sourceRoot":"","sources":["../src/case-emissions.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,KAAK,EACV,kBAAkB,EAClB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,EAEjB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE9F;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,gBAAgB,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,cAAc,CAAC;QAAC,UAAU,EAAE,kBAAkB,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;IAC7F,UAAU,CAAC,EAAE;QAAE,IAAI,EAAE,gBAAgB,CAAC;QAAC,UAAU,EAAE,oBAAoB,CAAC;QAAC,cAAc,EAAE,OAAO,CAAA;KAAE,CAAC;CACpG;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAqGD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,aAAa,EACtB,KAAK,EAAE,qBAAqB,GAC3B,eAAe,CAmBjB;AAED;gGACgG;AAChG,eAAO,MAAM,yBAAyB,EAAE,SAAS,cAAc,EAE1C,CAAC;AAEtB,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,GAAG,gBAAgB,CAS/E;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,GAAG,kBAAkB,CAQrF;AAED;+FAC+F;AAC/F,MAAM,MAAM,eAAe,CAAC,CAAC,IACzB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GAC5B;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE5D,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,EAAE,eAAe,CAAC,gBAAgB,CAAC,CAAC;IAC7C,UAAU,CAAC,EAAE,eAAe,CAAC,kBAAkB,CAAC,CAAC;CAClD;AAeD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,eAAe,EAC1B,QAAQ,EAAE;IAAE,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI,CAAA;CAAE,EACtF,KAAK,EAAE;IAAE,KAAK,EAAE,gBAAgB,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC7C,uBAAuB,CAsBzB"}
@@ -0,0 +1,208 @@
1
+ // spec 340 W10 — what a case transition EMITS into the decision and commitment machines.
2
+ //
3
+ // §R.2 established that the eleven `CaseState`s were never one machine: `approved`/`denied`/`expired`
4
+ // are **decisions**, `revoked` is what happens to a **commitment**, and the rest are **execution**. One
5
+ // field was answering three questions with three different signers, which is why a case could be
6
+ // `denied` while a commitment stayed outstanding with nowhere to record it.
7
+ //
8
+ // §R.3 names the order this has to happen in: the case flow EMITS decision and commitment records
9
+ // FIRST, and only then can `CaseState` shrink to execution. Deleting the enum before this module
10
+ // existed would have replaced working behaviour with machines nothing writes to — a regression wearing
11
+ // the costume of a cleanup.
12
+ //
13
+ // WHAT THIS MODULE IS NOT. It derives records; it does not move state. Every emission is expressed as a
14
+ // TRANSITION to be applied by the owning machine, never as a state assignment, so the machines keep
15
+ // their role checks — the creditor still discharges, the decider still decides, and persistence does
16
+ // not become a bypass (ADR-0058 §15, and the same rule `createDurableObjectInteractionStore` follows).
17
+ //
18
+ // NONE OF THIS IS AUTHORITY. A `grant` here records that a determination was made. It permits nothing:
19
+ // the authority re-check happens at the moment of acting regardless of what any of these records say
20
+ // (ADR-0041, ADR-0043).
21
+ import { applyCommitmentTransition, applyDecisionTransition } from './interaction-machines.js';
22
+ /**
23
+ * Deterministic ids, derived from the case id.
24
+ *
25
+ * Deliberately not random and not clock-derived: a case transition may be replayed (the interaction
26
+ * store is idempotent on `idempotencyKey`, and `processDue` re-runs sweeps), and a fresh id per replay
27
+ * would fork one determination into several. Same case ⇒ same decision ⇒ the second application is a
28
+ * no-op against a terminal state rather than a duplicate record.
29
+ */
30
+ export function decisionIdForCase(caseId) {
31
+ return `dec_${caseId}`;
32
+ }
33
+ export function commitmentIdForCase(caseId) {
34
+ return `cmt_${caseId}`;
35
+ }
36
+ /**
37
+ * WHO IS THE CREDITOR. The approving side — `responder` / `resource-owner`.
38
+ *
39
+ * This is a derivation, not a preference. Case `revoke` is signed by `responder`/`resource-owner`, and
40
+ * the only creditor-only commitment transition is `void`. Under the opposite assignment (debtor =
41
+ * responder, creditor = requester) an issuer revoking what it granted has no legal transition at all,
42
+ * which contradicts a case machine that plainly permits it. One reading is self-consistent; the other
43
+ * is not. `issue-credential` corroborates it: its signers are `resource-owner`/`responder` and
44
+ * `activate` is likewise creditor-only.
45
+ */
46
+ function creditorOf(c) {
47
+ return c.resourceOwner ?? c.responder;
48
+ }
49
+ /**
50
+ * WHO IS THE DECIDER — and why it is not one rule.
51
+ *
52
+ * For `approve`/`deny` it is `event.actor`: the party that actually made the determination. It cannot
53
+ * be the deciding ROLE, because the case machine accepts EITHER `responder` or `resource-owner` for
54
+ * those moves while the decision machine requires exactly `decision.decider`. Pinning the decider to
55
+ * the role would make a responder-signed approval on a case that also has a resource-owner emit a
56
+ * decision the decision machine then rejects as `unauthorized-actor` — a case that succeeded and an
57
+ * emission that silently could not land.
58
+ *
59
+ * For `expire` it is the creditor. `expire` is signed by role `any` (time is not an actor, so a
60
+ * deterministic sweep may record it), and attributing an expiry to whichever party happened to run the
61
+ * sweep would put a determination in someone's name they never made. The decision machine skips the
62
+ * actor check for `expire` precisely so this is possible.
63
+ */
64
+ function decisionSeed(c, event, transition) {
65
+ return {
66
+ decisionId: decisionIdForCase(c.id),
67
+ decider: transition === 'expire' ? creditorOf(c) : event.actor,
68
+ subject: c.id,
69
+ expiresAt: c.expiresAt,
70
+ };
71
+ }
72
+ function commitmentSeed(c) {
73
+ return {
74
+ commitmentId: commitmentIdForCase(c.id),
75
+ debtor: c.requester,
76
+ creditor: creditorOf(c),
77
+ };
78
+ }
79
+ /**
80
+ * The mapping, stated once.
81
+ *
82
+ * `deliver-credential` is deliberately ABSENT. It completes the case, and completing the work does not
83
+ * discharge the obligation — that is exactly the collapse ADR-0058 §15 forbids, and `fabric` already
84
+ * refuses to draw that edge implicitly (`mayDischargeOnExecution` exists so a profile has to choose it
85
+ * in a diff). A creditor who wants to discharge does so as its own act, against the commitment
86
+ * machine, with its own signature.
87
+ *
88
+ * `fail` is absent for the same reason in the other direction: a failed execution is not a refusal by
89
+ * anyone, and recording it as one would attribute a determination nobody made.
90
+ */
91
+ const EMITTING_TRANSITIONS = {
92
+ approve: { decision: 'grant' },
93
+ deny: { decision: 'refuse' },
94
+ expire: { decision: 'expire' },
95
+ // Issuing is what creates the obligation, so this emission may bring the commitment into being.
96
+ 'issue-credential': { commitment: 'activate' },
97
+ revoke: { commitment: 'void' },
98
+ };
99
+ /**
100
+ * Whether an emission may bring its record into existence.
101
+ *
102
+ * Deciding CREATES a determination — there was no prior record and the act of approving or denying is
103
+ * what makes one. Issuing likewise creates the obligation.
104
+ *
105
+ * `void` must not. `revoke` is legal from any non-terminal case, including `submitted`, where nothing
106
+ * was ever approved and no obligation ever arose. Seeding a commitment there so it could immediately
107
+ * be voided would write a record asserting an obligation existed and was released, when neither
108
+ * happened — inventing history to satisfy a type. Absent a commitment, this emission is a no-op, and
109
+ * the case's own `revoked` state remains the honest record of what occurred.
110
+ */
111
+ const CREATES_ITS_RECORD = {
112
+ grant: true,
113
+ refuse: true,
114
+ expire: true,
115
+ activate: true,
116
+ withdraw: false,
117
+ supersede: false,
118
+ 'claim-fulfilment': false,
119
+ 'dispute-claim': false,
120
+ discharge: false,
121
+ 'assert-breach': false,
122
+ void: false,
123
+ };
124
+ /**
125
+ * Derive what a case transition emits. Pure, and total over `CaseTransition` — a transition with no
126
+ * emission returns `{}` rather than throwing, because most transitions genuinely emit nothing.
127
+ *
128
+ * Call this only for a transition the case machine ACCEPTED. It does not re-check legality: duplicating
129
+ * the case machine's role and ordering rules here would create a second, quietly diverging copy of them.
130
+ */
131
+ export function caseTransitionEmissions(current, event) {
132
+ const spec = EMITTING_TRANSITIONS[event.transition];
133
+ if (!spec)
134
+ return {};
135
+ const out = {};
136
+ if (spec.decision) {
137
+ out.decision = {
138
+ seed: decisionSeed(current, event, spec.decision),
139
+ transition: spec.decision,
140
+ createIfAbsent: CREATES_ITS_RECORD[spec.decision],
141
+ };
142
+ }
143
+ if (spec.commitment) {
144
+ out.commitment = {
145
+ seed: commitmentSeed(current),
146
+ transition: spec.commitment,
147
+ createIfAbsent: CREATES_ITS_RECORD[spec.commitment],
148
+ };
149
+ }
150
+ return out;
151
+ }
152
+ /** The case transitions that emit something. Exported so a test can assert the set rather than
153
+ * restate it, and so a newly-added emitting transition shows up in a diff of this constant. */
154
+ export const EMITTING_CASE_TRANSITIONS = Object.keys(EMITTING_TRANSITIONS);
155
+ /** Seed a decision record in its INITIAL state. Never the resulting state — see `DecisionSeedV1`. */
156
+ export function seedDecision(seed, at) {
157
+ return {
158
+ decisionId: seed.decisionId,
159
+ state: 'pending',
160
+ decider: seed.decider,
161
+ subject: seed.subject,
162
+ expiresAt: seed.expiresAt,
163
+ updatedAt: at,
164
+ };
165
+ }
166
+ /** Seed a commitment record in its INITIAL state. */
167
+ export function seedCommitment(seed, at) {
168
+ return {
169
+ commitmentId: seed.commitmentId,
170
+ state: 'proposed',
171
+ debtor: seed.debtor,
172
+ creditor: seed.creditor,
173
+ updatedAt: at,
174
+ };
175
+ }
176
+ function applyOne(emission, existing, create, apply) {
177
+ if (!emission)
178
+ return undefined;
179
+ const record = existing ?? (emission.createIfAbsent ? create() : null);
180
+ if (record === null)
181
+ return { applied: false, reason: 'absent' };
182
+ const res = apply(record, emission.transition);
183
+ return res.ok ? { applied: true, record: res.next } : { applied: false, reason: res.reason };
184
+ }
185
+ /**
186
+ * Apply emissions through the OWNING machines.
187
+ *
188
+ * The point of this function is what it does not do: it never assigns a state. A record is created
189
+ * only in its initial state and every move goes through `applyDecisionTransition` /
190
+ * `applyCommitmentTransition`, so their role and ordering checks still hold — the creditor still
191
+ * voids, the decider still decides.
192
+ *
193
+ * Rejections are RETURNED, not thrown and not swallowed. A caller that persists these must treat a
194
+ * rejected emission as a fact about the record it failed to write, because a silent no-op here is
195
+ * indistinguishable from success and would leave a case whose determination was never recorded
196
+ * looking exactly like one whose determination was.
197
+ */
198
+ export function applyCaseEmissions(emissions, existing, event) {
199
+ const out = {};
200
+ const decision = applyOne(emissions.decision, existing.decision, () => seedDecision(emissions.decision.seed, event.at), (record, transition) => applyDecisionTransition(record, { transition, actor: event.actor, at: event.at }));
201
+ if (decision)
202
+ out.decision = decision;
203
+ const commitment = applyOne(emissions.commitment, existing.commitment, () => seedCommitment(emissions.commitment.seed, event.at), (record, transition) => applyCommitmentTransition(record, { transition, actor: event.actor, at: event.at }));
204
+ if (commitment)
205
+ out.commitment = commitment;
206
+ return out;
207
+ }
208
+ //# sourceMappingURL=case-emissions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"case-emissions.js","sourceRoot":"","sources":["../src/case-emissions.ts"],"names":[],"mappings":"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"}
@@ -52,9 +52,19 @@ export interface CaseTransitionEventV1 {
52
52
  authorityRef?: AuthorityRef;
53
53
  signature?: SignatureProof;
54
54
  }
55
+ /** W10b-3: `denied`/`expired`/`revoked` collapsed into `closed` — same terminality, and the verdict
56
+ * that distinguished them now lives in the decision and commitment records. */
55
57
  export declare const TERMINAL_CASE_STATES: readonly CaseState[];
56
58
  export declare function isTerminalCaseState(state: CaseState): boolean;
57
- export type CaseRejection = 'wrong-case' | 'invalid-transition' | 'unauthorized-actor' | 'case-expired' | 'not-yet-due';
59
+ /** The transitions that cannot land on the case machine alone. Exported so the set is a constant a
60
+ * caller can read rather than a rule buried in a branch. */
61
+ export declare const TRANSITIONS_REQUIRING_GRANTED_DECISION: readonly CaseTransition[];
62
+ export declare function requiresGrantedDecision(transition: CaseTransition): boolean;
63
+ export type CaseRejection = 'wrong-case' | 'invalid-transition' | 'unauthorized-actor' | 'case-expired' | 'not-yet-due'
64
+ /** W10b-3: the transition needs a granted decision and none was supplied. Its own reason rather than
65
+ * `invalid-transition`, because the case IS in a legal state — what is missing is the determination,
66
+ * and a caller that cannot tell those apart will "fix" the wrong one. */
67
+ | 'requires-granted-decision';
58
68
  export type CaseResult = {
59
69
  ok: true;
60
70
  case: FabricCaseV1;
@@ -62,11 +72,27 @@ export type CaseResult = {
62
72
  ok: false;
63
73
  reason: CaseRejection;
64
74
  };
75
+ /**
76
+ * What this machine cannot know on its own (spec 340 W10b-3).
77
+ *
78
+ * Until W10b-3, "you cannot issue before approving" was an FSM edge: `issue-credential` ran only
79
+ * `from: ['approved']`. With the determination moved to the decision machine, `triaged` is reachable by
80
+ * plain triage, so that ordering has to be re-established as a precondition or issuance could skip the
81
+ * determination entirely.
82
+ *
83
+ * Passed IN rather than looked up: this module is pure and has no store. The caller that holds the
84
+ * decision supplies it, and supplying nothing is a refusal, not a pass — a machine that treated
85
+ * "no decision provided" as "no decision required" would be exactly the fail-open this repo forbids.
86
+ */
87
+ export interface CaseTransitionContext {
88
+ /** The state of the decision for this case, or `null`/absent when there is none. */
89
+ decisionState?: 'pending' | 'granted' | 'refused' | 'withdrawn' | 'expired' | 'superseded' | null;
90
+ }
65
91
  /**
66
92
  * Pure, fail-closed (spec 316 §2). Generic over the concrete case type: preserves whatever extra fields
67
93
  * `T` carries beyond `CaseFsmFields`. Returns the next case or a typed rejection — never a silent no-op.
68
94
  */
69
- export declare function applyCaseTransition<T extends CaseFsmFields>(current: T, event: CaseTransitionEventV1): {
95
+ export declare function applyCaseTransition<T extends CaseFsmFields>(current: T, event: CaseTransitionEventV1, ctx?: CaseTransitionContext): {
70
96
  ok: true;
71
97
  case: T;
72
98
  } | {
@@ -1 +1 @@
1
- {"version":3,"file":"case-machine.d.ts","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"case-machine.d.ts","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"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"}
@@ -14,10 +14,24 @@
14
14
  // are inbox read-receipt facts. So `admit`(submitted→delivered)/`view`(delivered→seen) are DROPPED (they
15
15
  // become `ExchangeEventV1` `kind:'message'` events), and the moves that referenced `seen`/`delivered`
16
16
  // re-anchor on `submitted`/`triaged`.
17
- export const TERMINAL_CASE_STATES = ['denied', 'completed', 'expired', 'revoked', 'failed'];
17
+ /** W10b-3: `denied`/`expired`/`revoked` collapsed into `closed` — same terminality, and the verdict
18
+ * that distinguished them now lives in the decision and commitment records. */
19
+ export const TERMINAL_CASE_STATES = ['completed', 'closed', 'failed'];
18
20
  export function isTerminalCaseState(state) {
19
21
  return TERMINAL_CASE_STATES.includes(state);
20
22
  }
23
+ /**
24
+ * spec 340 W10b-3. The VERBS are unchanged — that is deliberate and load-bearing. A stored v1 case-event
25
+ * log names transitions, not states, so keeping every verb is what lets history replay with no
26
+ * migration (W10b-1). What changed is where four of them LAND, because `approved`/`denied`/`expired`/
27
+ * `revoked` were never execution states (§R.2):
28
+ *
29
+ * · `approve` now lands on `triaged` — the responder is working toward issuance. The DETERMINATION
30
+ * goes to the decision machine via `caseTransitionEmissions`. "Approved" was never a description
31
+ * of how far the work got; reading it as one is the "deciding is not doing" collapse.
32
+ * · `deny`/`expire`/`revoke` land on `closed` — taken up, ended without issuance. The verdict is in
33
+ * the decision record; the voided obligation is in the commitment record.
34
+ */
21
35
  const TRANSITIONS = {
22
36
  submit: { from: ['draft'], to: 'submitted', signers: ['requester'] },
23
37
  triage: { from: ['submitted'], to: 'triaged', signers: ['responder'] },
@@ -25,14 +39,24 @@ const TRANSITIONS = {
25
39
  // needs-info → triaged (NOT submitted): the requester supplied info, the responder RESUMES working
26
40
  // (couples to exec `working`), preserving the "responder engaged" phase across the info loop.
27
41
  'submit-info': { from: ['needs-info'], to: 'triaged', signers: ['requester'] },
28
- approve: { from: ['submitted', 'triaged'], to: 'approved', signers: ['responder', 'resource-owner'] },
29
- 'issue-credential': { from: ['approved'], to: 'issued', signers: ['resource-owner', 'responder'] },
42
+ approve: { from: ['submitted', 'triaged'], to: 'triaged', signers: ['responder', 'resource-owner'] },
43
+ // `from: ['triaged']` plus a REQUIRED granted decision — see `requiresGrantedDecision`. The ordering
44
+ // "you cannot issue before approving" used to be an FSM edge (`from: ['approved']`); with the
45
+ // decision out of this enum it becomes a cross-machine precondition instead, because `triaged` is
46
+ // reachable by plain triage and would otherwise let issuance skip the determination entirely.
47
+ 'issue-credential': { from: ['triaged'], to: 'issued', signers: ['resource-owner', 'responder'] },
30
48
  'deliver-credential': { from: ['issued'], to: 'completed', signers: ['resource-owner', 'responder'] },
31
- deny: { from: 'any-non-terminal', to: 'denied', signers: ['responder', 'resource-owner'] },
32
- expire: { from: 'any-non-terminal', to: 'expired', signers: ['any'] },
33
- revoke: { from: 'any-non-terminal', to: 'revoked', signers: ['responder', 'resource-owner'] },
49
+ deny: { from: 'any-non-terminal', to: 'closed', signers: ['responder', 'resource-owner'] },
50
+ expire: { from: 'any-non-terminal', to: 'closed', signers: ['any'] },
51
+ revoke: { from: 'any-non-terminal', to: 'closed', signers: ['responder', 'resource-owner'] },
34
52
  fail: { from: 'any-non-terminal', to: 'failed', signers: ['any'] },
35
53
  };
54
+ /** The transitions that cannot land on the case machine alone. Exported so the set is a constant a
55
+ * caller can read rather than a rule buried in a branch. */
56
+ export const TRANSITIONS_REQUIRING_GRANTED_DECISION = ['issue-credential'];
57
+ export function requiresGrantedDecision(transition) {
58
+ return TRANSITIONS_REQUIRING_GRANTED_DECISION.includes(transition);
59
+ }
36
60
  function actorRoles(c, actor) {
37
61
  const roles = new Set(['any']);
38
62
  if (actor === c.requester)
@@ -50,7 +74,7 @@ function isPastExpiry(c, atIso) {
50
74
  * Pure, fail-closed (spec 316 §2). Generic over the concrete case type: preserves whatever extra fields
51
75
  * `T` carries beyond `CaseFsmFields`. Returns the next case or a typed rejection — never a silent no-op.
52
76
  */
53
- export function applyCaseTransition(current, event) {
77
+ export function applyCaseTransition(current, event, ctx = {}) {
54
78
  if (event.caseId !== current.id)
55
79
  return { ok: false, reason: 'wrong-case' };
56
80
  const spec = TRANSITIONS[event.transition];
@@ -59,6 +83,9 @@ export function applyCaseTransition(current, event) {
59
83
  const fromOk = spec.from === 'any-non-terminal' ? !isTerminalCaseState(current.state) : spec.from.includes(current.state);
60
84
  if (!fromOk)
61
85
  return { ok: false, reason: 'invalid-transition' };
86
+ if (requiresGrantedDecision(event.transition) && ctx.decisionState !== 'granted') {
87
+ return { ok: false, reason: 'requires-granted-decision' };
88
+ }
62
89
  // `expire` only lands once the deadline passed; everything else (except `fail`) rejects on an expired case.
63
90
  if (event.transition === 'expire') {
64
91
  if (current.expiresAt === undefined || !isPastExpiry(current, event.at))
@@ -1 +1 @@
1
- {"version":3,"file":"case-machine.js","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,wGAAwG;AACxG,kCAAkC;AAClC,+GAA+G;AAC/G,4EAA4E;AAC5E,sGAAsG;AACtG,8BAA8B;AAC9B,EAAE;AACF,4GAA4G;AAC5G,6GAA6G;AAC7G,2FAA2F;AAC3F,EAAE;AACF,2GAA2G;AAC3G,yGAAyG;AACzG,sGAAsG;AACtG,sCAAsC;AAwEtC,MAAM,CAAC,MAAM,oBAAoB,GAAyB,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;AAElH,MAAM,UAAU,mBAAmB,CAAC,KAAgB;IAClD,OAAO,oBAAoB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAC9C,CAAC;AAWD,MAAM,WAAW,GAA2C;IAC1D,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE;IACpE,MAAM,EAAE,EAAE,IAAI,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE;IACtE,UAAU,EAAE,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE;IACxF,mGAAmG;IACnG,8FAA8F;IAC9F,aAAa,EAAE,EAAE,IAAI,EAAE,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE;IAC9E,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE;IACrG,kBAAkB,EAAE,EAAE,IAAI,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,gBAAgB,EAAE,WAAW,CAAC,EAAE;IAClG,oBAAoB,EAAE,EAAE,IAAI,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,gBAAgB,EAAE,WAAW,CAAC,EAAE;IACrG,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE;IAC1F,MAAM,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE;IACrE,MAAM,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,WAAW,EAAE,gBAAgB,CAAC,EAAE;IAC7F,IAAI,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE;CACnE,CAAC;AAWF,SAAS,UAAU,CAAC,CAAgB,EAAE,KAAa;IACjD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACrC,IAAI,KAAK,KAAK,CAAC,CAAC,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAClD,IAAI,KAAK,KAAK,CAAC,CAAC,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAClD,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,IAAI,KAAK,KAAK,CAAC,CAAC,aAAa;QAAE,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC5F,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,CAAgB,EAAE,KAAa;IACnD,OAAO,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAU,EACV,KAA4B;IAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAE5E,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAE9D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,KAAK,kBAAkB,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1H,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAEhE,4GAA4G;IAC5G,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,OAAO,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IACvH,CAAC;SAAM,IAAI,KAAK,CAAC,UAAU,KAAK,MAAM,IAAI,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;QAC1E,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;IAC/C,CAAC;IAED,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,CAAC;IAEhG,MAAM,IAAI,GAAM;QACd,GAAG,OAAO;QACV,KAAK,EAAE,IAAI,CAAC,EAAE;QACd,SAAS,EAAE,KAAK,CAAC,EAAE;QACnB,eAAe,EAAE,KAAK,CAAC,SAAS,IAAI,OAAO,CAAC,eAAe;QAC3D,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY;QACrG,aAAa,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa;KAC3G,CAAC;IACF,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"case-machine.js","sourceRoot":"","sources":["../src/case-machine.ts"],"names":[],"mappings":"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"}
@@ -28,7 +28,16 @@ export type OwningMachine = 'execution' | 'decision' | 'commitment';
28
28
  * "deciding is not doing" collapse.
29
29
  */
30
30
  export declare const MACHINE_FOR_CASE_STATE: Record<CaseState, OwningMachine>;
31
- /** How many distinct machines the single `CaseState` field was standing in for. Asserted in tests so
32
- * the claim in §W5 cannot drift away from the table above. */
31
+ /**
32
+ * How many distinct machines the single `CaseState` field stands in for.
33
+ *
34
+ * It was THREE, and that number was the whole argument for W10b-3. It is now ONE, and this function is
35
+ * kept — rather than deleted along with the states — precisely so that claim stays falsifiable: if
36
+ * anyone re-adds a decision-shaped or commitment-shaped member to `CaseState`, they must give it a home
37
+ * in the table above, and this count moves off 1 and the test fails.
38
+ *
39
+ * A cleanup is not finished when the code compiles; it is finished when re-introducing the thing you
40
+ * removed is caught automatically.
41
+ */
33
42
  export declare function machinesSpannedByCaseState(): number;
34
43
  //# sourceMappingURL=enum-retirement.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"enum-retirement.d.ts","sourceRoot":"","sources":["../src/enum-retirement.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG9D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CAcxE,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,SAAS,EAAE,aAAa,CAYnE,CAAC;AAEF;+DAC+D;AAC/D,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD"}
1
+ {"version":3,"file":"enum-retirement.d.ts","sourceRoot":"","sources":["../src/enum-retirement.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAG9D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAAW,EAAE,cAAc,CAcxE,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,YAAY,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,SAAS,EAAE,aAAa,CASnE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD"}
@@ -71,16 +71,22 @@ export const MACHINE_FOR_CASE_STATE = {
71
71
  submitted: 'execution',
72
72
  triaged: 'execution',
73
73
  'needs-info': 'execution',
74
- approved: 'decision',
75
- denied: 'decision',
76
74
  issued: 'execution',
77
75
  completed: 'execution',
78
- expired: 'decision',
79
- revoked: 'commitment',
76
+ closed: 'execution',
80
77
  failed: 'execution',
81
78
  };
82
- /** How many distinct machines the single `CaseState` field was standing in for. Asserted in tests so
83
- * the claim in §W5 cannot drift away from the table above. */
79
+ /**
80
+ * How many distinct machines the single `CaseState` field stands in for.
81
+ *
82
+ * It was THREE, and that number was the whole argument for W10b-3. It is now ONE, and this function is
83
+ * kept — rather than deleted along with the states — precisely so that claim stays falsifiable: if
84
+ * anyone re-adds a decision-shaped or commitment-shaped member to `CaseState`, they must give it a home
85
+ * in the table above, and this count moves off 1 and the test fails.
86
+ *
87
+ * A cleanup is not finished when the code compiles; it is finished when re-introducing the thing you
88
+ * removed is caught automatically.
89
+ */
84
90
  export function machinesSpannedByCaseState() {
85
91
  return new Set(Object.values(MACHINE_FOR_CASE_STATE)).size;
86
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"enum-retirement.js","sourceRoot":"","sources":["../src/enum-retirement.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,mGAAmG;AACnG,sDAAsD;AACtD,EAAE;AACF,kGAAkG;AAClG,8EAA8E;AAC9E,EAAE;AACF,6FAA6F;AAC7F,qGAAqG;AACrG,sEAAsE;AACtE,EAAE;AACF,qGAAqG;AACrG,mGAAmG;AACnG,qDAAqD;AACrD,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,yEAAyE;AACzE,EAAE;AACF,kGAAkG;AAClG,sGAAsG;AACtG,uGAAuG;AACvG,sGAAsG;AACtG,8FAA8F;AAC9F,EAAE;AACF,uEAAuE;AAKvE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErG;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAwC;IAC3E,KAAK,EAAE,cAAc,CAAC,cAAc;IACpC,MAAM,EAAE,cAAc,CAAC,cAAc;IACrC,SAAS,EAAE,cAAc,CAAC,cAAc;IACxC,oGAAoG;IACpG,mEAAmE;IACnE,OAAO,EAAE,oBAAoB,CAAC,cAAc;IAC5C,QAAQ,EAAE,oBAAoB,CAAC,cAAc;IAC7C,mGAAmG;IACnG,iGAAiG;IACjG,gGAAgG;IAChG,iFAAiF;IACjF,qBAAqB,EAAE,mBAAmB,CAAC,cAAc;IACzD,OAAO,EAAE,cAAc,CAAC,cAAc;CACvC,CAAC;AAKF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAqC;IACtE,KAAK,EAAE,WAAW;IAClB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,WAAW;IACpB,YAAY,EAAE,WAAW;IACzB,QAAQ,EAAE,UAAU;IACpB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,WAAW;IACnB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF;+DAC+D;AAC/D,MAAM,UAAU,0BAA0B;IACxC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"enum-retirement.js","sourceRoot":"","sources":["../src/enum-retirement.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,mGAAmG;AACnG,sDAAsD;AACtD,EAAE;AACF,kGAAkG;AAClG,8EAA8E;AAC9E,EAAE;AACF,6FAA6F;AAC7F,qGAAqG;AACrG,sEAAsE;AACtE,EAAE;AACF,qGAAqG;AACrG,mGAAmG;AACnG,qDAAqD;AACrD,EAAE;AACF,qGAAqG;AACrG,sGAAsG;AACtG,mGAAmG;AACnG,qGAAqG;AACrG,yEAAyE;AACzE,EAAE;AACF,kGAAkG;AAClG,sGAAsG;AACtG,uGAAuG;AACvG,sGAAsG;AACtG,8FAA8F;AAC9F,EAAE;AACF,uEAAuE;AAKvE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErG;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAwC;IAC3E,KAAK,EAAE,cAAc,CAAC,cAAc;IACpC,MAAM,EAAE,cAAc,CAAC,cAAc;IACrC,SAAS,EAAE,cAAc,CAAC,cAAc;IACxC,oGAAoG;IACpG,mEAAmE;IACnE,OAAO,EAAE,oBAAoB,CAAC,cAAc;IAC5C,QAAQ,EAAE,oBAAoB,CAAC,cAAc;IAC7C,mGAAmG;IACnG,iGAAiG;IACjG,gGAAgG;IAChG,iFAAiF;IACjF,qBAAqB,EAAE,mBAAmB,CAAC,cAAc;IACzD,OAAO,EAAE,cAAc,CAAC,cAAc;CACvC,CAAC;AAKF;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAqC;IACtE,KAAK,EAAE,WAAW;IAClB,SAAS,EAAE,WAAW;IACtB,OAAO,EAAE,WAAW;IACpB,YAAY,EAAE,WAAW;IACzB,MAAM,EAAE,WAAW;IACnB,SAAS,EAAE,WAAW;IACtB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,WAAW;CACpB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC"}