@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
package/CHANGELOG.md ADDED
@@ -0,0 +1,85 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented here. Format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
+
6
+ ## [0.1.1] - 2026-08-30
7
+
8
+ ### Changed
9
+
10
+ - Updated the package's public repository, issue-tracker, and homepage metadata to the canonical Foundry repository. This change is not a publication or qualification claim.
11
+
12
+ ## [0.1.0] - 2026-08-21
13
+
14
+ First release. This package is the butler role: everything about what a
15
+ person wants, now and standing.
16
+
17
+ This changelog starts here rather than carrying either donor's history,
18
+ which cites decisions and issues that would mean nothing — or the wrong
19
+ thing — to a reader who arrives at this package first.
20
+
21
+ ### Added
22
+
23
+ - A want schema in three states, not two: `absent`, `denied`, and
24
+ `granted`, plus a `stale` evaluation status that is computed and never
25
+ stored. Absence is a value, so it can never be read as permission.
26
+ - `evaluateStandingInstruction`, which compares one stored answer against
27
+ the policy version in force **and** the clock. A row that exists, says
28
+ `granted`, and is a year past its own declared window comes back `stale`.
29
+ - `decideStandingChange` plus `recordReopened` and `recordStaleness`: the
30
+ pure decision core and the audit-event builders. Actor and subject are
31
+ separate parameters and separate fields, always, and
32
+ `src/audit-shape.check.ts` fails the build if the audit event ever gains
33
+ a personal-data-shaped key or loses that separation.
34
+ - Three gates, all reachable from the single `butler-check` bin:
35
+ `confirmation-completeness`, `currency`, and `withdrawal-parity`. Each
36
+ dispatches on `argv[0]` matching exactly — never on
37
+ `basename(process.argv[1])`, which would see `cli.js` and silently run
38
+ the wrong command wherever a gate is invoked by compiled path.
39
+ - The `0` / `1` / `2` exit contract, with `2` reachable on every gate by
40
+ more than one route — an unreadable or invalid record store, an empty
41
+ record set, a required declared value that was not supplied, and no gate
42
+ selected at all — and each route tested. A bare `butler-check` exits `2`:
43
+ nothing was selected, so nothing was checked. Only an explicitly
44
+ requested `--help` exits `0`.
45
+ - An `./inbound` subpath: admission on any channel as a pure function of
46
+ the caller's own signature verification and a host ledger's dedupe
47
+ answer. An unreachable ledger rejects rather than acknowledging, because
48
+ acking an event whose dedupe never ran would silently discard it.
49
+ - A `./web` subpath: `useStandingWants`, a currency-aware preference-surface
50
+ hook whose `withdraw` shares `grant`'s and `deny`'s exact call shape.
51
+ React is an optional peer, asserted at import time by `assertPeerVersion`
52
+ so an absent or incompatible version fails loudly and by name.
53
+ - **The published tarball carries this changelog.** `files` includes
54
+ `CHANGELOG.md`: a consumer reading the installed package should not have
55
+ to leave it to find out what changed.
56
+
57
+ ### Design notes
58
+
59
+ - **Confidence is a first-class value with a declared floor.** An intent
60
+ carries its confidence on the record, and the floor is supplied by the
61
+ caller with no default anywhere in this package. A reading below the
62
+ floor may be confirmed or handed off; what it may never be is acted on
63
+ silently, and `handed-off` is a first-class disposition precisely so
64
+ declining to act is representable as a decision rather than as an
65
+ absence of one.
66
+ - **An inferred standing instruction is not binding until confirmed.** It
67
+ evaluates to `absent`, with its own reason, so relying on it is a
68
+ finding rather than a pass.
69
+ - **The currency window has no default.** A window this package invented
70
+ would be this package authoring one of the consumer's own values, and a
71
+ missing window silently read as "forever" is the open loop the currency
72
+ gate exists to close.
73
+ - **`invalidateDenialOnPolicyBump` has no default in either direction.**
74
+ Whether a policy bump invalidates a prior refusal is a jurisdiction
75
+ judgment, and this package answers no jurisdiction questions.
76
+
77
+ ### Not included
78
+
79
+ - No topic vocabulary, no jurisdiction logic, no obligations, and no
80
+ values of any kind. Storage and audit are host-supplied ports and no
81
+ implementation of either ships here. No claim of legal compliance is
82
+ made anywhere.
83
+ - No person-attributable record is written into this repository. `subjectId`
84
+ and `actorId` are opaque host-owned references carrying no email, name,
85
+ phone number, address, or IP.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Calvin Hung
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # @clossys/butler
2
+
3
+ **Everything about what a person wants — now, and standing.**
4
+
5
+ The question this role answers, and no other role does:
6
+
7
+ > Do we have what this person wants — this request in their own
8
+ > confirmation, and their standing instructions, still current?
9
+
10
+ Ships the schema and the checkers; every consumer authors its own values.
11
+
12
+ ## The loop this closes
13
+
14
+ A stated want is the setpoint. Acting on it is the act. Reading the
15
+ interpretation back, and asking whether the standing answer is still
16
+ current, is the observation. Unconfirmed intents and expired instructions
17
+ are the comparison. Re-asking is the correction.
18
+
19
+ A preference written to a row and never re-checked has no observation and
20
+ no comparison. It is an open loop, and closing it is the reason this
21
+ package exists.
22
+
23
+ ## The defect this exists to prevent
24
+
25
+ A weaker tool checks that a consent row **exists**. It passes on a row
26
+ three policy versions old, and on a row a year past the window its own
27
+ author declared, because a boolean read of "granted" cannot see age.
28
+ Presence is not currency.
29
+
30
+ Two rules follow, and both are structural here rather than advisory:
31
+
32
+ 1. **Consent is three states, never two.** `absent` (never asked) is a
33
+ distinct value from `denied` (asked, refused), and neither is a boolean.
34
+ Absence can therefore never be read as permission — including when the
35
+ record store is unreachable, when the subject was never asked, and when
36
+ the only thing on file is an inference nobody confirmed.
37
+ 2. **A gate that cannot run must say so.** `butler-check` exits `2` when
38
+ the record store cannot be read or there was nothing to scan, and `2` is
39
+ never collapsed into `0` or `1`.
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ npm install @clossys/butler
45
+ ```
46
+
47
+ Installing from this registry needs a GitHub personal access token with
48
+ `read:packages` — see the repository root README.
49
+
50
+ ```ts
51
+ import { checkCurrency, evaluateStandingInstruction } from "@clossys/butler";
52
+ ```
53
+
54
+ ## The three gates
55
+
56
+ All three are reachable from one installed bin, `butler-check`, dispatched
57
+ on the first argument matching a gate name exactly.
58
+
59
+ ```bash
60
+ butler-check confirmation-completeness ./intents.json ./confirmations.json --floor 0.8
61
+ butler-check currency ./instructions.json ./usages.json --invalidate-denial-on-policy-bump false
62
+ butler-check withdrawal-parity ./paths.json
63
+ ```
64
+
65
+ ### `confirmation-completeness`
66
+
67
+ Every acted-on intent has the subject's own confirmation record, or an
68
+ explicit below-floor hand-off. It fails on an intent dispositioned `acted`
69
+ with no confirmation at all, on one acted against a `misread` or `unclear`
70
+ read-back, on a reading below the declared floor acted on with neither a
71
+ hand-off nor a confirmation, and on a read-back answering an intent outside
72
+ the set being checked.
73
+
74
+ `--floor` is required and has no default. The number below which a reading
75
+ is too weak to act on is one of the consumer's own values.
76
+
77
+ ### `currency`
78
+
79
+ No standing instruction is used past its declared window. It reads
80
+ **usages**, not instructions alone: a set of instructions nobody relies on
81
+ proves nothing, while a usage record is the loop actually being closed or
82
+ not. Each usage carries the policy version in force at the moment it
83
+ happened, so the gate replays a real decision rather than re-deriving one
84
+ against today's policy. It fails on a usage past the window, on a usage
85
+ after the answered policy version was superseded, on a usage of an
86
+ instruction with no answer on record — including an unconfirmed inference —
87
+ and on a usage naming an instruction outside the set being checked.
88
+
89
+ `--invalidate-denial-on-policy-bump` is required and has no default in
90
+ either direction. Whether a policy bump also invalidates a prior refusal is
91
+ a jurisdiction judgment this package does not make.
92
+
93
+ ### `withdrawal-parity`
94
+
95
+ Withdrawing is no harder than granting. It compares the grant route and the
96
+ withdraw route a consumer measured itself, using three coarse countable
97
+ facts rather than a score, and fails when withdrawing takes more steps,
98
+ demands contacting a human that granting did not, demands an account that
99
+ granting did not, or is not offered at all.
100
+
101
+ Reopening is not a degraded path, and this gate is only the measurement
102
+ half of that. The API half is structural and lives in `./web`.
103
+
104
+ ### Exit codes
105
+
106
+ | Code | Meaning |
107
+ | --- | --- |
108
+ | `0` | Ran against a non-empty record set and found nothing. |
109
+ | `1` | Ran and found at least one real violation. |
110
+ | `2` | Could not run: a missing, unreadable, unparseable or schema-invalid record store; an empty record set; a required declared value that was not supplied; or no gate selected at all. |
111
+
112
+ `2` is not a variant of failure. "I checked and it is fine" and "I never
113
+ checked" are different answers, and a gate that reports the second as the
114
+ first is worse than no gate.
115
+
116
+ A bare `butler-check` with no subcommand exits `2`, not `0`. Nothing was
117
+ selected, so nothing was checked, and a CI step with a dropped argument
118
+ must go red rather than green on the strength of having examined nothing.
119
+ An explicitly requested `--help` is the one exception, and exits `0`,
120
+ because asking for help is a run that did exactly what was asked.
121
+
122
+ ## API
123
+
124
+ Everything below is exported from the package root.
125
+
126
+ ### Evaluating and deciding
127
+
128
+ | Export | What it does |
129
+ | --- | --- |
130
+ | `evaluateStandingInstruction` | Compares one stored answer against the policy version in force and the clock. Returns `granted`, `denied`, `absent`, or `stale`. |
131
+ | `decideStandingChange` | The pure decision core for one change: returns the new instruction and its audit event. Takes the actor and the subject as separate arguments. |
132
+ | `recordReopened` | Builds the audit event for a subject reopening their preference surface, independent of any decision made inside it. |
133
+ | `recordStaleness` | Builds the audit event for an answer found stale, with the reason as the event type. |
134
+
135
+ ### The gates, as pure functions
136
+
137
+ | Export | What it does |
138
+ | --- | --- |
139
+ | `checkConfirmationCompleteness` | Gate 1, over intents, confirmations, and a declared floor. |
140
+ | `checkCurrency` | Gate 2, over instructions, usages, and the caller's denial-invalidation decision. |
141
+ | `checkWithdrawalParity` | Gate 3, over measured preference paths. |
142
+
143
+ ### Validators and guards
144
+
145
+ | Export | What it does |
146
+ | --- | --- |
147
+ | `validateStandingInstruction` | Validates one untyped standing instruction. |
148
+ | `validateStandingInstructions` | Validates an untyped array of them. |
149
+ | `validateIntentRecord` | Validates one untyped intent. |
150
+ | `validateIntentRecords` | Validates an untyped array of them. |
151
+ | `validateConfirmationRecord` | Validates one untyped read-back answer. |
152
+ | `validateConfirmationRecords` | Validates an untyped array of them. |
153
+ | `validateInstructionUsages` | Validates an untyped array of usage records. |
154
+ | `validatePreferencePaths` | Validates an untyped array of measured preference paths. |
155
+ | `validateConfidenceFloor` | Validates a declared confidence floor. |
156
+ | `validatePolicyVersion` | Validates a policy-version reference. |
157
+ | `isStandingInstruction` | Boolean guard over `validateStandingInstruction`. |
158
+ | `isIntentRecord` | Boolean guard over `validateIntentRecord`. |
159
+ | `isConfirmationRecord` | Boolean guard over `validateConfirmationRecord`. |
160
+
161
+ ### Vocabularies
162
+
163
+ `STANDING_PROVENANCES`, `INTENT_DISPOSITIONS`, `CONFIRMATION_VERDICTS`, and
164
+ `STANDING_AUDIT_EVENT_TYPES` are the closed lists a caller validating
165
+ untyped input needs.
166
+
167
+ ### Types
168
+
169
+ The record types are `StandingInstruction`, `StandingState`,
170
+ `StandingTopic`, `StandingProvenance`, `CurrencyWindow`, `PolicyVersion`,
171
+ `IntentRecord`, `IntentDisposition`, `ConfirmationRecord`,
172
+ `ConfirmationVerdict`, `ConfidenceFloor`, `InstructionUsage`,
173
+ `PreferencePath` and `PathCost`.
174
+
175
+ The evaluation and decision types are `StandingEvaluation`,
176
+ `StandingEvaluationPolicy`, `StandingAction`, `StandingAuditEvent` and
177
+ `StandingAuditEventType`.
178
+
179
+ The host-implemented ports are `StandingInstructionStore` and
180
+ `StandingAuditLedger`. No implementation of either ships here.
181
+
182
+ Each gate returns its own result type — `ConfirmationCompletenessResult`,
183
+ `CurrencyResult`, `WithdrawalParityResult` — carrying findings typed as
184
+ `ConfirmationFinding`, `CurrencyFinding` and `WithdrawalParityFinding`,
185
+ whose kinds are `ConfirmationFindingKind`, `CurrencyFindingKind` and
186
+ `WithdrawalParityFindingKind`, and whose non-clean outcomes are named by
187
+ `ConfirmationFailureReason`, `CurrencyFailureReason` and
188
+ `WithdrawalParityFailureReason`.
189
+
190
+ Validation surfaces `ValidationIssue`, `ValidationResult` and `Validator`.
191
+
192
+ ## The `./inbound` subpath
193
+
194
+ Admission on any channel, and deliberately not an HTTP handler. The
195
+ consumer owns the route, the raw body, and signature verification —
196
+ signature schemes are provider-specific and this package must not pretend
197
+ to verify what it cannot exercise. This package owns the admission
198
+ decision: dedupe, ack/reject doctrine, replay tolerance, and ordering
199
+ tolerance, as a pure function of the caller's own verification result plus
200
+ a host ledger's dedupe answer.
201
+
202
+ ```ts
203
+ import { admitInboundEvent } from "@clossys/butler/inbound";
204
+ ```
205
+
206
+ The decision is never a bare boolean: acknowledging and processing are two
207
+ separate questions. A replay is an acknowledgement with `action: "ignore"`,
208
+ never an error. A rejection is reserved for a failed signature, so a
209
+ provider is never told to keep retrying data that will never become
210
+ processable. And a throwing ledger rejects the promise rather than
211
+ acknowledging — if durable dedupe could not run, nothing was durably
212
+ accepted, and saying otherwise would silently discard an event.
213
+
214
+ ## The `./web` subpath
215
+
216
+ `useStandingWants` is a currency-aware preference-surface hook: it reads
217
+ every stored instruction for a subject once, then evaluates each requested
218
+ topic against the policy version in force and a caller-supplied `now`. An
219
+ instruction that exists, and was granted, and is past its own window comes
220
+ back `stale`, so a surface rendered from it re-asks rather than quietly
221
+ continuing.
222
+
223
+ ```ts
224
+ import { useStandingWants } from "@clossys/butler/web";
225
+ ```
226
+
227
+ `withdraw` shares `grant`'s and `deny`'s exact call shape — one topic, one
228
+ promise, one function on the same object. That is withdrawal parity
229
+ enforced structurally at the API surface rather than asserted in prose.
230
+
231
+ React and React DOM are optional peers of this subpath specifically.
232
+ Importing the package root or `./inbound` never pulls in React. Importing
233
+ `./web` asserts the installed version against the declared range at import
234
+ time, so an absent or incompatible React fails loudly, by name, instead of
235
+ crashing later inside a hook with no version named as the cause.
236
+
237
+ ## Requirements
238
+
239
+ Node 20 or newer. Zero runtime dependencies. React and React DOM are
240
+ optional peers, needed only by the `./web` subpath.
241
+
242
+ ## What this package is not
243
+
244
+ - It carries no topics, no confidence floor, no currency window, no
245
+ jurisdiction logic, and no obligations. Every one of those is a value,
246
+ and values belong in each consumer's own repository.
247
+ - It makes **no claim of legal compliance**. It is record machinery, not
248
+ advice.
249
+ - It ships no storage and no audit implementation. Both are ports the host
250
+ implements, and no person-attributable record is written into this
251
+ repository.
252
+ - `subjectId` and `actorId` are opaque host-owned references. Neither ever
253
+ carries an email address, a name, a phone number, an address, or an IP,
254
+ and they are separate identifiers in every signature — an actor recording
255
+ a subject's own decision and an actor deciding on a subject's behalf must
256
+ stay distinguishable in the audit trail.
257
+
258
+ ## Licence
259
+
260
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Compile-time proof that `StandingAuditEvent` (`./schema.ts`) carries no
3
+ * raw personal-data field — no email, name, phone, address, or IP — only
4
+ * the opaque `subjectId` and the separately-opaque `actorId`.
5
+ *
6
+ * Named `*.check.ts`, not `*.test.ts`, so it is part of the REAL `tsc` run
7
+ * (`npm run typecheck`) rather than only being transpiled, never
8
+ * type-checked, by vitest — see this repository's own contribution
9
+ * guide, "Type-level assertions live in `.check.ts(x)` files" entry, and
10
+ * `scripts/check-typechecked-assertions.mjs` for the gate that enforces it.
11
+ * Nothing here is ever imported by `index.ts` or any runtime code; its only
12
+ * job is to fail `tsc` if the contract regresses.
13
+ *
14
+ * `ExactKeys` fails to compile unless `Keys` is EXACTLY the allowed set —
15
+ * neither a subset (a required field silently dropped) nor a superset (a
16
+ * new field silently added, personal-data-shaped or not, without a human
17
+ * deciding it belongs here).
18
+ *
19
+ * `subjectId` and `actorId` are both in the allowed set, deliberately and
20
+ * separately. Merging them into one id would still pass a personal-data
21
+ * scan and would still be wrong: the whole point of this package is
22
+ * telling "the person asked for this" apart from "something acted on its
23
+ * own reading", and a single conflated identifier makes that distinction
24
+ * unrecoverable after the fact.
25
+ */
26
+ import type { StandingAuditEvent } from "./schema.js";
27
+ type ExactKeys<Keys extends string, Allowed extends string> = [Allowed] extends [Keys] ? ([Keys] extends [Allowed] ? true : never) : never;
28
+ declare const ALLOWED_KEYS: readonly ["subjectId", "actorId", "topic", "type", "policyVersion", "occurredAt", "previousPolicyVersion"];
29
+ type AllowedKey = (typeof ALLOWED_KEYS)[number];
30
+ export declare const auditEventKeysAreExactlyTheAllowedSet: ExactKeys<keyof StandingAuditEvent, AllowedKey>;
31
+ export {};
32
+ //# sourceMappingURL=audit-shape.check.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-shape.check.d.ts","sourceRoot":"","sources":["../src/audit-shape.check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,KAAK,SAAS,CAAC,IAAI,SAAS,MAAM,EAAE,OAAO,SAAS,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC;AAE3I,QAAA,MAAM,YAAY,4GAA6G,CAAC;AAChI,KAAK,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAMhD,eAAO,MAAM,qCAAqC,EAAE,SAAS,CAAC,MAAM,kBAAkB,EAAE,UAAU,CAAQ,CAAC"}
@@ -0,0 +1,7 @@
1
+ const ALLOWED_KEYS = ["subjectId", "actorId", "topic", "type", "policyVersion", "occurredAt", "previousPolicyVersion"];
2
+ // If `StandingAuditEvent` ever gains or loses a key relative to
3
+ // `ALLOWED_KEYS` above, this assignment stops compiling — including if
4
+ // someone adds an `email`, `name`, `ip`, `phone`, or `address`-shaped
5
+ // field, and including if `actorId` is ever folded into `subjectId`.
6
+ export const auditEventKeysAreExactlyTheAllowedSet = true;
7
+ //# sourceMappingURL=audit-shape.check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit-shape.check.js","sourceRoot":"","sources":["../src/audit-shape.check.ts"],"names":[],"mappings":"AA6BA,MAAM,YAAY,GAAG,CAAC,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,YAAY,EAAE,uBAAuB,CAAU,CAAC;AAGhI,gEAAgE;AAChE,uEAAuE;AACvE,sEAAsE;AACtE,qEAAqE;AACrE,MAAM,CAAC,MAAM,qCAAqC,GAAoD,IAAI,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * `butler-check` — one bin, three gates, dispatched on `argv[0]` matching a
4
+ * subcommand name EXACTLY.
5
+ *
6
+ * Dispatch is on `argv[0]`, never on `basename(process.argv[1])`. A bin-name
7
+ * dispatch would see `cli.js` wherever this gate is invoked by its compiled
8
+ * path — which is exactly how this repository's own `npm run check` invokes
9
+ * other packages' gates — and would silently run the wrong command, or none.
10
+ *
11
+ * Presentation only: parse argv, load JSON files, run the pure checkers in
12
+ * `contract.ts`, print a report, pick an exit code. Every decision worth
13
+ * testing lives in `contract.ts` and is tested there directly, against
14
+ * plain values, with no filesystem involved.
15
+ *
16
+ * EXIT CODES — the contract a consumer's CI depends on (this repository's
17
+ * own contribution guide, "Gate CLIs exit `0` clean, `1` findings, `2`
18
+ * could not run"):
19
+ *
20
+ * 0 — ran cleanly against a non-empty record set and found nothing.
21
+ * 1 — ran cleanly and found at least one real violation.
22
+ * 2 — could not run. Kept strictly distinct from `1`, because "I checked
23
+ * and it is fine" and "I never checked" are different answers and a
24
+ * gate that reports the second as the first is worse than no gate.
25
+ *
26
+ * `2` is genuinely reachable here, on every subcommand, and each route is
27
+ * tested:
28
+ * - a record store that cannot be read — missing file, unreadable file,
29
+ * a directory where a file was named, invalid JSON, or JSON that does
30
+ * not validate against this package's own schema;
31
+ * - nothing to scan — an empty record set, which is not a clean run, it
32
+ * is a run that examined nothing;
33
+ * - a required declared value that was not supplied — the confidence
34
+ * floor, or the denial-invalidation policy. Neither has a default
35
+ * anywhere in this package, and a run missing one declines rather than
36
+ * inventing one of the consumer's own values;
37
+ * - NO GATE SELECTED AT ALL. A bare `butler-check` with no subcommand is
38
+ * a run that never happened, and it exits `2`, not `0` — see `main()`'s
39
+ * own comment for why an explicitly requested `--help` is the one
40
+ * argument-shaped `0` here and a dropped argument is not.
41
+ */
42
+ /** Exported for `cli.test.ts` — anything wrong with the arguments themselves always maps to exit code 2, never 1. */
43
+ export declare class CliInputError extends Error {
44
+ }
45
+ /**
46
+ * Exported (unlike a typical CLI `main`) so `cli.test.ts` can exercise the
47
+ * whole argv-to-exit-code contract directly, against real `mkdtemp` temp
48
+ * directories, without spawning a subprocess per case. Takes `argv` as a
49
+ * parameter rather than reading `process.argv` itself for exactly that
50
+ * reason — `run()` below is the only caller that reads the real
51
+ * `process.argv`.
52
+ */
53
+ export declare function main(argv: string[]): number;
54
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AA2EH,qHAAqH;AACrH,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAkQ3C;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAyB3C"}