@clossys/butler 0.1.1

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 (54) hide show
  1. package/CHANGELOG.md +85 -0
  2. package/LICENSE +21 -0
  3. package/README.md +260 -0
  4. package/dist/audit-shape.check.d.ts +32 -0
  5. package/dist/audit-shape.check.d.ts.map +1 -0
  6. package/dist/audit-shape.check.js +7 -0
  7. package/dist/audit-shape.check.js.map +1 -0
  8. package/dist/cli.d.ts +54 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +426 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/contract.d.ts +256 -0
  13. package/dist/contract.d.ts.map +1 -0
  14. package/dist/contract.js +377 -0
  15. package/dist/contract.js.map +1 -0
  16. package/dist/inbound/index.d.ts +120 -0
  17. package/dist/inbound/index.d.ts.map +1 -0
  18. package/dist/inbound/index.js +125 -0
  19. package/dist/inbound/index.js.map +1 -0
  20. package/dist/index.d.ts +50 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +47 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/schema.d.ts +374 -0
  25. package/dist/schema.d.ts.map +1 -0
  26. package/dist/schema.js +304 -0
  27. package/dist/schema.js.map +1 -0
  28. package/dist/validation.d.ts +74 -0
  29. package/dist/validation.d.ts.map +1 -0
  30. package/dist/validation.js +140 -0
  31. package/dist/validation.js.map +1 -0
  32. package/dist/web/index.d.ts +5 -0
  33. package/dist/web/index.d.ts.map +1 -0
  34. package/dist/web/index.js +25 -0
  35. package/dist/web/index.js.map +1 -0
  36. package/dist/web/internal/peer-version.d.ts +53 -0
  37. package/dist/web/internal/peer-version.d.ts.map +1 -0
  38. package/dist/web/internal/peer-version.js +136 -0
  39. package/dist/web/internal/peer-version.js.map +1 -0
  40. package/dist/web/useStandingWants.d.ts +75 -0
  41. package/dist/web/useStandingWants.d.ts.map +1 -0
  42. package/dist/web/useStandingWants.js +66 -0
  43. package/dist/web/useStandingWants.js.map +1 -0
  44. package/package.json +93 -0
  45. package/src/audit-shape.check.ts +37 -0
  46. package/src/cli.ts +445 -0
  47. package/src/contract.ts +534 -0
  48. package/src/inbound/index.ts +190 -0
  49. package/src/index.ts +113 -0
  50. package/src/schema.ts +622 -0
  51. package/src/validation.ts +172 -0
  52. package/src/web/index.ts +27 -0
  53. package/src/web/internal/peer-version.ts +159 -0
  54. package/src/web/useStandingWants.ts +139 -0
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Inbound admission doctrine — deliberately NOT an HTTP handler.
3
+ *
4
+ * This is the front door of the butler role: a request arrives on some
5
+ * channel and has to be admitted before anything can be interpreted into
6
+ * an intent. Admission is the only question answered here. Interpretation,
7
+ * the confidence it carries, and the read-back that follows all live in
8
+ * the root export (`../schema.js`, `../contract.js`); nothing in this file
9
+ * reads a request's content, and no field here can hold one.
10
+ *
11
+ * The ownership split is the same one the storage and audit ports use at
12
+ * the root: the host implements a ledger interface and owns the transport;
13
+ * this package owns the decision logic on top of it.
14
+ *
15
+ * - The consumer owns the HTTP route, raw-body access, and SIGNATURE
16
+ * VERIFICATION. Signature schemes are provider-specific — this package
17
+ * cannot test a provider's signing algorithm against a real secret and
18
+ * must not pretend to verify what it cannot exercise.
19
+ * - This package owns the ADMISSION DECISION: dedupe, ack/reject doctrine,
20
+ * replay tolerance, and ordering tolerance, all as a pure function of the
21
+ * caller's own verification result plus a ledger's dedupe answer.
22
+ *
23
+ * Zero runtime dependencies, matching the rest of this package.
24
+ */
25
+ /**
26
+ * The host implements durable, atomic dedupe against its own storage.
27
+ * Inbound events are at-least-once and may be delivered more than once and out of order, so
28
+ * `recordIfNew` must be a single atomic check-and-record operation — not a
29
+ * separate existence check followed by a later insert, or two concurrent
30
+ * deliveries of the same event can both observe `"new"`.
31
+ */
32
+ export interface InboundEventLedger {
33
+ recordIfNew(event: {
34
+ readonly provider: string;
35
+ readonly eventId: string;
36
+ }): Promise<"new" | "duplicate">;
37
+ }
38
+ /**
39
+ * One inbound provider webhook event, prior to any admission decision.
40
+ *
41
+ * `signature` is the result of the caller's OWN signature verification —
42
+ * there is no default and no third option that means "not checked yet".
43
+ * An unverified event is not representable by omission: leaving the field
44
+ * out is a type error, and any runtime value other than the literal
45
+ * `"verified"` is treated as not verified.
46
+ */
47
+ export interface InboundAdmissionInput {
48
+ /** The provider's name for this event source (e.g. "resend"). Scopes dedupe together with `eventId`. */
49
+ provider: string;
50
+ /** Unique within the provider; required for dedupe. */
51
+ eventId: string;
52
+ /** The provider-reported event timestamp, as an ISO-8601 (or otherwise `Date`-parseable) string. */
53
+ occurredAt: string;
54
+ /** The result of the CALLER's own signature verification. No default. */
55
+ signature: "verified" | "invalid";
56
+ }
57
+ /** Why a durably-accepted event was not handed off for processing. */
58
+ export type InboundAdmissionIgnoreReason = {
59
+ kind: "duplicate";
60
+ } | {
61
+ kind: "malformed";
62
+ field: string;
63
+ message: string;
64
+ };
65
+ /**
66
+ * The admission decision. Never a bare boolean — a caller must be able to
67
+ * see and act on ack vs. process as two separate questions.
68
+ *
69
+ * Doctrine encoded here:
70
+ * - **Ack on durable acceptance, not on successful processing.** A
71
+ * processing failure downstream of `action: "process"` is not a reason to
72
+ * have withheld the ack; it already happened.
73
+ * - **Reject only on signature failure.** `ack: false` is reserved for
74
+ * `reason: "signature-invalid"` — every other rejection of work is
75
+ * expressed as `ack: true, action: "ignore"` so a provider is never told
76
+ * to keep retrying data that will never become processable.
77
+ * - **A replay is an ack with `action: "ignore"`, never an error.**
78
+ */
79
+ export type InboundAdmissionDecision = {
80
+ ack: true;
81
+ action: "process";
82
+ } | {
83
+ ack: true;
84
+ action: "ignore";
85
+ reason: InboundAdmissionIgnoreReason;
86
+ } | {
87
+ ack: false;
88
+ reason: "signature-invalid";
89
+ };
90
+ /**
91
+ * The pure decision core, kept separate from the ledger round-trip so it is
92
+ * directly testable with a plain `"new" | "duplicate"` value instead of a
93
+ * mock ledger. `admitInboundEvent` below is the only caller that needs an
94
+ * actual `InboundEventLedger`.
95
+ */
96
+ export declare function decideInboundAdmission(input: InboundAdmissionInput, dedupe: "new" | "duplicate"): InboundAdmissionDecision;
97
+ /**
98
+ * Decide whether an inbound provider webhook event should be acknowledged
99
+ * and, if so, whether it should be processed.
100
+ *
101
+ * This function does no I/O of its own beyond calling `ledger.recordIfNew`
102
+ * once, and only after `input` has already passed structural and signature
103
+ * validation — a malformed or unverified event never reaches the ledger.
104
+ *
105
+ * No input combination yields `{ ack: true, action: "process" }` unless
106
+ * `input.signature === "verified"` AND the ledger reports the event as new.
107
+ *
108
+ * A THROWING LEDGER REJECTS THIS PROMISE, AND THAT IS THE CORRECT DECLINE
109
+ * PATH — do not catch it and ack. This is the same three-state discipline
110
+ * the gates keep at the other end of the package: "could not check" is
111
+ * never reported as "checked and fine". Inbound does not own anything
112
+ * yet: if durable dedupe could not be performed, this function
113
+ * cannot know whether the event is a replay, so acking it would silently
114
+ * discard an event that may never have been processed. Rejecting lets the
115
+ * caller's route return a 5xx and the provider redeliver, which is exactly
116
+ * what at-least-once delivery is for. Ack means "durably accepted"; an
117
+ * unreachable ledger means nothing was durably accepted.
118
+ */
119
+ export declare function admitInboundEvent(input: InboundAdmissionInput, ledger: InboundEventLedger): Promise<InboundAdmissionDecision>;
120
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/inbound/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,kBAAkB;IACjC,WAAW,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,GAAG,WAAW,CAAC,CAAC;CAC3G;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,qBAAqB;IACpC,wGAAwG;IACxG,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,oGAAoG;IACpG,UAAU,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,SAAS,EAAE,UAAU,GAAG,SAAS,CAAC;CACnC;AAED,sEAAsE;AACtE,MAAM,MAAM,4BAA4B,GACpC;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,wBAAwB,GAChC;IAAE,GAAG,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAChC;IAAE,GAAG,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,4BAA4B,CAAA;CAAE,GACrE;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAA;CAAE,CAAC;AA+DhD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,KAAK,GAAG,WAAW,GAC1B,wBAAwB,CAO1B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,qBAAqB,EAC5B,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,wBAAwB,CAAC,CAKnC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Inbound admission doctrine — deliberately NOT an HTTP handler.
3
+ *
4
+ * This is the front door of the butler role: a request arrives on some
5
+ * channel and has to be admitted before anything can be interpreted into
6
+ * an intent. Admission is the only question answered here. Interpretation,
7
+ * the confidence it carries, and the read-back that follows all live in
8
+ * the root export (`../schema.js`, `../contract.js`); nothing in this file
9
+ * reads a request's content, and no field here can hold one.
10
+ *
11
+ * The ownership split is the same one the storage and audit ports use at
12
+ * the root: the host implements a ledger interface and owns the transport;
13
+ * this package owns the decision logic on top of it.
14
+ *
15
+ * - The consumer owns the HTTP route, raw-body access, and SIGNATURE
16
+ * VERIFICATION. Signature schemes are provider-specific — this package
17
+ * cannot test a provider's signing algorithm against a real secret and
18
+ * must not pretend to verify what it cannot exercise.
19
+ * - This package owns the ADMISSION DECISION: dedupe, ack/reject doctrine,
20
+ * replay tolerance, and ordering tolerance, all as a pure function of the
21
+ * caller's own verification result plus a ledger's dedupe answer.
22
+ *
23
+ * Zero runtime dependencies, matching the rest of this package.
24
+ */
25
+ function isNonEmptyString(value) {
26
+ return typeof value === "string" && value.trim().length > 0;
27
+ }
28
+ function isParseableTimestamp(value) {
29
+ return typeof value === "string" && value.trim().length > 0 && !Number.isNaN(Date.parse(value));
30
+ }
31
+ /**
32
+ * Validate an admission input independently of dedupe. Returns either
33
+ * `{ valid: true }` — proceed to the ledger — or `{ valid: false, decision
34
+ * }` with the exact terminal decision to return without ever consulting a
35
+ * ledger.
36
+ *
37
+ * Order matters: signature is checked first. An unverified caller's claims
38
+ * about `eventId`, `provider`, or `occurredAt` are not trustworthy input,
39
+ * so nothing past the signature check runs until it passes.
40
+ */
41
+ function validateInboundAdmissionInput(input) {
42
+ // Anything other than the exact literal "verified" — including "invalid",
43
+ // an unrecognized string, undefined, or any other malformed value — fails
44
+ // closed the same way. There is no default and no way to express "not yet
45
+ // checked" that reaches processing.
46
+ if (input?.signature !== "verified") {
47
+ return { valid: false, decision: { ack: false, reason: "signature-invalid" } };
48
+ }
49
+ if (!isNonEmptyString(input.provider)) {
50
+ return {
51
+ valid: false,
52
+ decision: {
53
+ ack: true,
54
+ action: "ignore",
55
+ reason: { kind: "malformed", field: "provider", message: "must be a non-empty string" },
56
+ },
57
+ };
58
+ }
59
+ if (!isNonEmptyString(input.eventId)) {
60
+ return {
61
+ valid: false,
62
+ decision: {
63
+ ack: true,
64
+ action: "ignore",
65
+ reason: { kind: "malformed", field: "eventId", message: "must be a non-empty string" },
66
+ },
67
+ };
68
+ }
69
+ if (!isParseableTimestamp(input.occurredAt)) {
70
+ return {
71
+ valid: false,
72
+ decision: {
73
+ ack: true,
74
+ action: "ignore",
75
+ reason: { kind: "malformed", field: "occurredAt", message: "must be a parseable timestamp" },
76
+ },
77
+ };
78
+ }
79
+ return { valid: true };
80
+ }
81
+ /**
82
+ * The pure decision core, kept separate from the ledger round-trip so it is
83
+ * directly testable with a plain `"new" | "duplicate"` value instead of a
84
+ * mock ledger. `admitInboundEvent` below is the only caller that needs an
85
+ * actual `InboundEventLedger`.
86
+ */
87
+ export function decideInboundAdmission(input, dedupe) {
88
+ const validation = validateInboundAdmissionInput(input);
89
+ if (!validation.valid)
90
+ return validation.decision;
91
+ if (dedupe === "duplicate") {
92
+ return { ack: true, action: "ignore", reason: { kind: "duplicate" } };
93
+ }
94
+ return { ack: true, action: "process" };
95
+ }
96
+ /**
97
+ * Decide whether an inbound provider webhook event should be acknowledged
98
+ * and, if so, whether it should be processed.
99
+ *
100
+ * This function does no I/O of its own beyond calling `ledger.recordIfNew`
101
+ * once, and only after `input` has already passed structural and signature
102
+ * validation — a malformed or unverified event never reaches the ledger.
103
+ *
104
+ * No input combination yields `{ ack: true, action: "process" }` unless
105
+ * `input.signature === "verified"` AND the ledger reports the event as new.
106
+ *
107
+ * A THROWING LEDGER REJECTS THIS PROMISE, AND THAT IS THE CORRECT DECLINE
108
+ * PATH — do not catch it and ack. This is the same three-state discipline
109
+ * the gates keep at the other end of the package: "could not check" is
110
+ * never reported as "checked and fine". Inbound does not own anything
111
+ * yet: if durable dedupe could not be performed, this function
112
+ * cannot know whether the event is a replay, so acking it would silently
113
+ * discard an event that may never have been processed. Rejecting lets the
114
+ * caller's route return a 5xx and the provider redeliver, which is exactly
115
+ * what at-least-once delivery is for. Ack means "durably accepted"; an
116
+ * unreachable ledger means nothing was durably accepted.
117
+ */
118
+ export async function admitInboundEvent(input, ledger) {
119
+ const validation = validateInboundAdmissionInput(input);
120
+ if (!validation.valid)
121
+ return validation.decision;
122
+ const dedupe = await ledger.recordIfNew({ provider: input.provider, eventId: input.eventId });
123
+ return decideInboundAdmission(input, dedupe);
124
+ }
125
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/inbound/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * @clossys/butler — everything about what a person wants, now and
3
+ * standing.
4
+ *
5
+ * The question this role answers, and no other role does: **do we have what
6
+ * this person wants — this request in their own confirmation, and their
7
+ * standing instructions, still current?**
8
+ *
9
+ * Three halves ship here, and the third is what justifies the first two:
10
+ *
11
+ * 1. THE SCHEMA (`schema.ts`). Hand-rolled, dependency-free validators
12
+ * over the two record families: `IntentRecord` and `ConfirmationRecord`
13
+ * for one request read back to the person who made it, and
14
+ * `StandingInstruction` for the durable answers that keep speaking
15
+ * afterwards. Consent is three states — `absent`, `denied`, `granted`
16
+ * — never a boolean, so absence can never read as permission. Storage
17
+ * and audit are host-supplied ports (`StandingInstructionStore`,
18
+ * `StandingAuditLedger`); no implementation of either ships here.
19
+ *
20
+ * 2. THE EVALUATION (`contract.ts`). `evaluateStandingInstruction`
21
+ * compares one stored answer against the policy in force AND the
22
+ * clock, adding `stale` as a fourth EVALUATION status that is never a
23
+ * stored state. `decideStandingChange` is the pure decision core for
24
+ * one change, and `recordReopened`/`recordStaleness` build the audit
25
+ * events a host chooses to record.
26
+ *
27
+ * 3. THE GATES. Three checkers, all reachable from the single
28
+ * `butler-check` bin: `checkConfirmationCompleteness`,
29
+ * `checkCurrency`, and `checkWithdrawalParity`. Each is a pure
30
+ * function returning a three-state result, and `cli.ts` folds those
31
+ * onto the `0`/`1`/`2` exit contract without ever collapsing "could
32
+ * not run" into either "clean" or "findings".
33
+ *
34
+ * Two subpaths sit beside this one. `./inbound` is admission — whether an
35
+ * event arriving on a channel should be acknowledged and processed at all,
36
+ * decided as a pure function of the caller's own signature verification and
37
+ * a host ledger's dedupe answer. `./web` is preference-surface state, and
38
+ * is the only entry point that touches React.
39
+ *
40
+ * Nothing in this package's own source is a real topic vocabulary, a real
41
+ * confidence floor, a real currency window, a jurisdiction rule, or an
42
+ * obligation. It makes no claim of legal compliance. Ships the schema and
43
+ * the checkers; every consumer authors its own values.
44
+ */
45
+ export { CONFIRMATION_VERDICTS, INTENT_DISPOSITIONS, STANDING_AUDIT_EVENT_TYPES, STANDING_PROVENANCES, isConfirmationRecord, isIntentRecord, isStandingInstruction, validateConfidenceFloor, validateConfirmationRecord, validateConfirmationRecords, validateInstructionUsages, validateIntentRecord, validateIntentRecords, validatePolicyVersion, validatePreferencePaths, validateStandingInstruction, validateStandingInstructions, } from "./schema.js";
46
+ export type { ConfidenceFloor, ConfirmationRecord, ConfirmationVerdict, CurrencyWindow, InstructionUsage, IntentDisposition, IntentRecord, PathCost, PolicyVersion, PreferencePath, StandingAction, StandingAuditEvent, StandingAuditEventType, StandingAuditLedger, StandingEvaluation, StandingEvaluationPolicy, StandingInstruction, StandingInstructionStore, StandingProvenance, StandingState, StandingTopic, } from "./schema.js";
47
+ export { checkConfirmationCompleteness, checkCurrency, checkWithdrawalParity, decideStandingChange, evaluateStandingInstruction, recordReopened, recordStaleness, } from "./contract.js";
48
+ export type { ConfirmationCompletenessResult, ConfirmationFailureReason, ConfirmationFinding, ConfirmationFindingKind, CurrencyFailureReason, CurrencyFinding, CurrencyFindingKind, CurrencyResult, WithdrawalParityFailureReason, WithdrawalParityFinding, WithdrawalParityFindingKind, WithdrawalParityResult, } from "./contract.js";
49
+ export type { ValidationIssue, ValidationResult, Validator } from "./validation.js";
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,QAAQ,EACR,aAAa,EACb,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,kBAAkB,EAClB,aAAa,EACb,aAAa,GACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,2BAA2B,EAC3B,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EACV,8BAA8B,EAC9B,yBAAyB,EACzB,mBAAmB,EACnB,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,cAAc,EACd,6BAA6B,EAC7B,uBAAuB,EACvB,2BAA2B,EAC3B,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @clossys/butler — everything about what a person wants, now and
3
+ * standing.
4
+ *
5
+ * The question this role answers, and no other role does: **do we have what
6
+ * this person wants — this request in their own confirmation, and their
7
+ * standing instructions, still current?**
8
+ *
9
+ * Three halves ship here, and the third is what justifies the first two:
10
+ *
11
+ * 1. THE SCHEMA (`schema.ts`). Hand-rolled, dependency-free validators
12
+ * over the two record families: `IntentRecord` and `ConfirmationRecord`
13
+ * for one request read back to the person who made it, and
14
+ * `StandingInstruction` for the durable answers that keep speaking
15
+ * afterwards. Consent is three states — `absent`, `denied`, `granted`
16
+ * — never a boolean, so absence can never read as permission. Storage
17
+ * and audit are host-supplied ports (`StandingInstructionStore`,
18
+ * `StandingAuditLedger`); no implementation of either ships here.
19
+ *
20
+ * 2. THE EVALUATION (`contract.ts`). `evaluateStandingInstruction`
21
+ * compares one stored answer against the policy in force AND the
22
+ * clock, adding `stale` as a fourth EVALUATION status that is never a
23
+ * stored state. `decideStandingChange` is the pure decision core for
24
+ * one change, and `recordReopened`/`recordStaleness` build the audit
25
+ * events a host chooses to record.
26
+ *
27
+ * 3. THE GATES. Three checkers, all reachable from the single
28
+ * `butler-check` bin: `checkConfirmationCompleteness`,
29
+ * `checkCurrency`, and `checkWithdrawalParity`. Each is a pure
30
+ * function returning a three-state result, and `cli.ts` folds those
31
+ * onto the `0`/`1`/`2` exit contract without ever collapsing "could
32
+ * not run" into either "clean" or "findings".
33
+ *
34
+ * Two subpaths sit beside this one. `./inbound` is admission — whether an
35
+ * event arriving on a channel should be acknowledged and processed at all,
36
+ * decided as a pure function of the caller's own signature verification and
37
+ * a host ledger's dedupe answer. `./web` is preference-surface state, and
38
+ * is the only entry point that touches React.
39
+ *
40
+ * Nothing in this package's own source is a real topic vocabulary, a real
41
+ * confidence floor, a real currency window, a jurisdiction rule, or an
42
+ * obligation. It makes no claim of legal compliance. Ships the schema and
43
+ * the checkers; every consumer authors its own values.
44
+ */
45
+ export { CONFIRMATION_VERDICTS, INTENT_DISPOSITIONS, STANDING_AUDIT_EVENT_TYPES, STANDING_PROVENANCES, isConfirmationRecord, isIntentRecord, isStandingInstruction, validateConfidenceFloor, validateConfirmationRecord, validateConfirmationRecords, validateInstructionUsages, validateIntentRecord, validateIntentRecords, validatePolicyVersion, validatePreferencePaths, validateStandingInstruction, validateStandingInstructions, } from "./schema.js";
46
+ export { checkConfirmationCompleteness, checkCurrency, checkWithdrawalParity, decideStandingChange, evaluateStandingInstruction, recordReopened, recordStaleness, } from "./contract.js";
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,EACvB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,2BAA2B,EAC3B,4BAA4B,GAC7B,MAAM,aAAa,CAAC;AAyBrB,OAAO,EACL,6BAA6B,EAC7B,aAAa,EACb,qBAAqB,EACrB,oBAAoB,EACpB,2BAA2B,EAC3B,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAC"}