@plasius/economy 0.3.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,10 +4,11 @@ Provider-neutral TypeScript contracts and deterministic invariants for the
4
4
  Plasius sitewide Token economy.
5
5
 
6
6
  This package models exact TokenSubunit amounts, immutable double-entry journal
7
- transactions, source lots, family gameplay reservations, early-backer basis,
8
- future spend requests, acquisition contracts, projections, and persistence
9
- ports. It intentionally contains no HTTP, authentication, provider SDK,
10
- database driver, secret, or Azure implementation.
7
+ transactions, source lots, family gameplay reservations, purpose-bound learning
8
+ module allowances, early-backer basis, future spend requests, acquisition
9
+ contracts, projections, and persistence ports. It intentionally contains no
10
+ HTTP, authentication, provider SDK, database driver, secret, or Azure
11
+ implementation.
11
12
 
12
13
  ## Product boundary
13
14
 
@@ -92,6 +93,7 @@ const transaction: LedgerTransactionV1 = {
92
93
  transactionId: "txn:purchase:1",
93
94
  activityType: "purchase",
94
95
  status: "settled",
96
+ // Legacy V1 example. V3 writes the domain-separated HMAC digest here.
95
97
  idempotencyKey: "intent:1:paid",
96
98
  effectiveAt: "2026-07-15T10:00:00.000Z",
97
99
  recordedAt: "2026-07-15T10:00:01.000Z",
@@ -119,13 +121,133 @@ assertBalancedTransaction(transaction);
119
121
  const payloadForApprovedHashAdapter = canonicalTransactionPayload(transaction);
120
122
  ```
121
123
 
122
- The package produces canonical bytes but deliberately leaves SHA-256/HSM
123
- signing to an approved infrastructure adapter. Metadata keys and posting IDs
124
- are ordered with locale-independent UTF-16 code-unit comparison. Their
124
+ The package produces canonical bytes but deliberately leaves SHA-256 hashing
125
+ and any signing to an approved infrastructure adapter. Metadata keys and
126
+ posting IDs are ordered with locale-independent UTF-16 code-unit comparison. Their
125
127
  validated ASCII alphabet makes that ordering identical to PostgreSQL
126
128
  `COLLATE "C"`; adapters must hash the exact UTF-8 bytes returned by
127
129
  `canonicalTransactionPayload()`.
128
130
 
131
+ ## Audit receipts and integrity verification
132
+
133
+ `AuditedEconomyCommandEnvelopeV1` is a standalone, fingerprint-only V3
134
+ contract. It binds actor, subject, principal type, delegated
135
+ relationship/version, sanitized capability/flag/assurance evidence, route,
136
+ build, region, writer fence, correlation, causation, and payload. It never
137
+ extends the legacy raw-key envelope.
138
+
139
+ `EconomyHmacFingerprintV1` records an exact domain, key version, and
140
+ `hmac-sha256:` digest. Raw idempotency keys, provider event/object IDs,
141
+ callback bodies, and reconciliation material use distinct domains. Provider
142
+ commands require a canonical `EconomyProviderEvidenceManifestV1`; callback
143
+ signatures and payment data have no contract field.
144
+
145
+ Encrypted operational provider handles remain site-owned.
146
+ `EconomyEncryptedOperationalHandleBindingV1` binds only their purpose,
147
+ provider, AES-256-GCM label, key version, ciphertext-content hash, and
148
+ encryption-context hash. It contains no ciphertext, nonce, authentication tag,
149
+ plaintext, or Key Vault URI.
150
+
151
+ `EconomyAcceptedCommandReceiptV1` is safe to return after durable acceptance:
152
+ it exposes only receipt, command, and correlation IDs plus the command-envelope
153
+ hash and acceptance time. `EconomyCommandResultReceiptV1` is terminal and
154
+ exclusive:
155
+
156
+ - `completed` requires a transaction ID and its existing canonical hash and
157
+ forbids outcome codes;
158
+ - `failed` requires one bounded safe failure code and forbids transaction
159
+ fields;
160
+ - `no-op` requires one bounded safe no-op code and forbids transaction fields.
161
+
162
+ Every terminal shape retains a result hash. Replay is a processing mode, not a
163
+ command source: `assertExactAuditedEconomyCommandReplay()` requires identical
164
+ canonical bytes and preserves the original source. A collision with different
165
+ bytes is a security conflict.
166
+
167
+ The audit canonicalizers use explicit fixed field order and omit absent optional
168
+ fields. Runtime validators reject unknown fields rather than allowing raw
169
+ evidence to be persisted accidentally. `canonicalTransactionPayload()` is
170
+ unchanged. A V3 transaction stores the HMAC digest in its legacy canonical
171
+ `idempotencyKey` field; the HTTP key is never stored.
172
+
173
+ `EconomyAuthorityHeadV1` and hash-linked
174
+ `EconomyAuthorityCommitManifestV1` cover every immutable record, conditional
175
+ projection replacement, idempotency result, and outbox effect. Manifests
176
+ support only `create` and `conditional-replace`; batches are limited to 80
177
+ operations and 1.5 MiB. The manifest create and head replacement reserve
178
+ two operations, leaving at most 78 canonical record references.
179
+ `assertEconomyAuditGraph()` recomputes the complete command/evidence/receipt/
180
+ transaction/manifest graph and expected head.
181
+ `assertEconomyAuthorityRecoveryEvidence()` additionally recomputes the
182
+ successful prior verification receipt bound by any reopening manifest.
183
+
184
+ First-party commands use one atomic authority boundary. Provider workflows use
185
+ one boundary to durably accept verified evidence before acknowledgement and a
186
+ second boundary to record the externally reconciled completed, failed, or
187
+ no-op result.
188
+
189
+ `verifyJournalChainSegment()` accepts an approved canonical-payload hash
190
+ function, recomputes every transaction hash, checks previous-hash links and
191
+ duplicate transaction IDs, and proves the resulting head against an expected
192
+ head. `EconomyIntegrityVerificationReceiptV1` binds authority, journal, and
193
+ projection verification outcomes. `EconomyIntegrityAnchorManifestV1` defines
194
+ canonical hourly Merkle evidence while signing, keys, and storage remain
195
+ infrastructure responsibilities.
196
+
197
+ ## Recoverable acknowledgements and portable receipts
198
+
199
+ The additive recovery protocol protects the acknowledgement boundary without
200
+ turning an evidence store into a second economy writer:
201
+
202
+ - `EconomyRecoveryAcceptanceEnvelopeV1` binds the provider-neutral audited
203
+ command, accepted receipt, HMAC idempotency fingerprint, and an AES-256-GCM
204
+ sealed reconstruction payload before command processing;
205
+ - `EconomyRecoveryCommittedResultV1` binds that exact acceptance to the
206
+ terminal result receipt, authority commit/head, sequence, optional completed
207
+ transaction, and sealed reconstruction payload after the authority commits;
208
+ - `EconomyRegionalEvidenceReceiptV1` is a signed, sequence-addressed,
209
+ hash-chained retention assertion for the byte-identical acceptance or result
210
+ stored in one evidence region; and
211
+ - `EconomyPortableCustomerReceiptV1` binds a positive TokenSubunit amount and
212
+ direction to the completed transaction, result, authority sequence, at least
213
+ two regional evidence receipts, and an optional authority-commit Merkle
214
+ inclusion proof.
215
+
216
+ Every recovery record has a SHA-256 content-addressed ID derived from a
217
+ separate, explicitly ordered canonical body. Detached signatures cover that ID,
218
+ the complete body, and public algorithm/key-version/time metadata. The package
219
+ does not implement hashing, encryption, signing, key lookup, Blob access, or
220
+ identity. Callers supply approved hash and signature-verification functions;
221
+ `assertEconomyRegionalEvidenceChain()`,
222
+ `assertEconomyRegionalEvidenceEquality()`,
223
+ `assertEconomyMerkleInclusion()`, and
224
+ `assertEconomyPortableCustomerReceiptEvidence()` run without a cloud SDK or
225
+ authentication dependency.
226
+
227
+ Recovery validators reject unknown fields. Plaintext records have no field for
228
+ raw idempotency keys, provider/payment facts, callback bodies/signatures,
229
+ storage URIs, payer/household/account identity, email, session data, or exact
230
+ birth data. A portable receipt contains only opaque command/transaction
231
+ references, proof hashes, amount/direction, safe activity, terms version, and
232
+ `cashRedemptionAllowed: false`. The sealed payload's plaintext remains subject
233
+ to the same approved privacy-minimized authority schema; encryption is not
234
+ permission to retain unnecessary data.
235
+
236
+ A consuming service may acknowledge a provider callback after identical
237
+ acceptance evidence is durable in both approved regions, then process it
238
+ asynchronously. A successful browser value command is not customer-acknowledged
239
+ until the authoritative commit and both regional committed-result receipts
240
+ exist. Cross-service writes are not a distributed ACID transaction: exact
241
+ content IDs and idempotent create-only completion make orphan acceptances and
242
+ committed-but-not-yet-acknowledged results recoverable without duplicating
243
+ value.
244
+
245
+ These contracts enable a scoped recoverability claim only after the consuming
246
+ infrastructure proves dual durable writes, locked retention, independent keys,
247
+ monitoring, and tested reconstruction. They do not by themselves establish
248
+ zero RPO, WORM retention, HSM custody, administrator-proof storage, or
249
+ availability.
250
+
129
251
  ## Source-lot policy and allocations
130
252
 
131
253
  `selectSourceLots()` selects spendable slices in credited-time/lot-ID order and
@@ -160,6 +282,44 @@ settlements after `evaluatedAt`. The caller must derive both window timestamps
160
282
  from the entire public cohort and exclude non-production/test lots: staff,
161
283
  closed-beta, and test availability must never open or close the public window.
162
284
 
285
+ ## Junior Coder Module Allowances
286
+
287
+ `ModuleAllowanceV1` is a Guardian-funded reservation for one linked child and
288
+ one purpose: purchasing independently sellable Junior Coder module
289
+ entitlements. It is deliberately separate from `GameplayAllocationV1`; callers
290
+ cannot spend gameplay value as a learning allowance or silently move learning
291
+ value into gameplay.
292
+
293
+ The purchase contracts implement the deterministic parts of:
294
+
295
+ ```text
296
+ immutable quote -> allowance hold -> pending entitlement
297
+ -> settle spend -> activate entitlement -> durable receipt
298
+ ```
299
+
300
+ An immutable quote binds the exact module version, Token price, child,
301
+ Guardian acknowledgement, catalog version, and requirements-manifest version
302
+ plus canonical SHA-256 reference. This makes software-only declarations and
303
+ robotics bills of materials durable pre-purchase evidence even after a live
304
+ manifest changes.
305
+
306
+ `createModuleSpendHold()` moves whole Tokens from available to held value.
307
+ `settleModuleSpendHold()` requires a pending entitlement ID before it can move
308
+ held value to spent value. `releaseModuleSpendHold()` returns failed or expired
309
+ holds to the same Module Allowance. `createModulePurchaseReceipt()` accepts only
310
+ matching quote and settled-hold evidence. `reconcileModulePurchase()` returns
311
+ one forward-safe repair instruction and fails closed to manual review for a
312
+ settled debit without an entitlement or an active paid entitlement without a
313
+ settled debit.
314
+
315
+ These functions produce immutable next-state projections; they do not perform
316
+ authorization or persistence. The consuming service must evaluate
317
+ `learning.junior-coder.purchase.enabled`, enforce Guardian/child capabilities,
318
+ store actor/subject/operation-scoped idempotency evidence, and compare-and-swap
319
+ allowance and hold versions alongside the balanced journal, entitlement,
320
+ receipt, and outbox in a serializable transaction. A disabled feature may still
321
+ release and reconcile existing holds, but must reject new quotes and purchases.
322
+
163
323
  ## Acquisition and future contracts
164
324
 
165
325
  Purchase intents bind payer, receiving household/wallet, pack, catalog,
@@ -267,6 +427,15 @@ journal rows directly. The V2 adapter is responsible for:
267
427
  - managed identity and least-privilege database access; and
268
428
  - immutable audit/integrity evidence outside this package.
269
429
 
430
+ `EconomyPersistencePortV3` is additive and retains every V2 economic primitive.
431
+ It removes V2's raw-key idempotency operations from its unit and replaces them
432
+ with `EconomyAuditedIdempotencyScopeV1` and
433
+ `EconomyAuditedIdempotencyResultV1`. It adds audited commands, evidence
434
+ manifests, encrypted-handle bindings, receipts, authority commits/head CAS,
435
+ integrity receipts, and anchor manifests. Its journal method accepts
436
+ `AuditedChainedEconomicJournalTransactionV1`, whose legacy idempotency slot is
437
+ type-narrowed to an HMAC digest. V1 and V2 remain exported unchanged.
438
+
270
439
  ## Development
271
440
 
272
441
  ```bash
@@ -282,10 +451,20 @@ Coverage must remain at least 80%, and every changed source file must appear in
282
451
  LCOV. Generated property tests cover exact arithmetic, double-entry balance,
283
452
  projection rebuilds, idempotency, reversals, lot isolation, and allocation
284
453
  non-negativity. npm publication is performed only by the approved GitHub CD
285
- workflow.
454
+ workflow on configurable trusted self-hosted runners. Release LCOV and the
455
+ CycloneDX SBOM remain retained even when an external coverage or npm provenance
456
+ service is unavailable. Release tags and GitHub Releases use a current-repository
457
+ GitHub App token with explicit Contents and Workflows write permissions; npm
458
+ publication continues to use only the protected `NPM_TOKEN`. Both release jobs
459
+ install a checksum-pinned GitHub CLI under `RUNNER_TEMP`. A requested version
460
+ bump reuses the current version only when neither npm nor GitHub records it as a
461
+ completed release; use `bump=none` for deliberate recovery of prepared metadata.
462
+ Publication checks out the verified current release-branch HEAD so that recovery
463
+ always includes the reviewed workflow tooling.
286
464
 
287
465
  ## Security
288
466
 
289
- Do not include raw payment details, personal data, provider callback bodies, or
290
- secrets in contracts, metadata, tests, examples, or logs. Report vulnerabilities
291
- privately according to [SECURITY.md](./SECURITY.md).
467
+ Do not include raw idempotency keys, provider identifiers, provider callback
468
+ bodies or signatures, payment details, personal data, encrypted-handle
469
+ ciphertext, or secrets in contracts, metadata, tests, examples, or logs.
470
+ Report vulnerabilities privately according to [SECURITY.md](./SECURITY.md).