R81.A verification — P1.1.1 Basis Point Arithmetic
Scope recap
First Phase 1 κ Rule Engine code: greenfield src/domains/rules/integer-math.ts — bigint-based basis-point arithmetic primitives that every downstream κ built-in (P1.1.2 determinism harness, P1.1.3 constants/overflow, P1.3.2 built-ins) will delegate to. Authoritative spec: the pre-authored prompt at docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md §P1.1.1 (lines 62–196) + docs/reference/extractions/kappa-rule-engine-extraction.md §3–4 + docs/3-world/physics/laws/rule-engine.md.
Deliverables
| File | LOC | Purpose |
|---|---|---|
src/domains/rules/integer-math.ts |
183 | bigint bps helpers + safe arithmetic + typed errors |
src/__tests__/domains/rules/integer-math.test.ts |
298 | 38 tests, full branch coverage |
Public API (confirmed in impl)
// Basis-point arithmetic
bps_mul(value: bigint, bps: bigint): bigint // (value * bps) / 10_000n, floor
bps_div(value: bigint, bps: bigint): bigint // (value * 10_000n) / bps, throws on bps === 0n
apply_bps(value: bigint, bps: bigint): bigint // value - bps_mul(value, bps)
decay(value: bigint, rate_bps: bigint, epochs: bigint): bigint // compounded, per-step floor
// Safe arithmetic primitives
safe_mul(a: bigint, b: bigint): bigint // throws OverflowError outside int64
safe_div(a: bigint, b: bigint): bigint // throws DivisionByZeroError on b === 0n
// Typed errors
class OverflowError extends Error
class DivisionByZeroError extends Error
class UnderflowError extends Error
All signatures use bigint (not number) per contract §3 invariants I1 + I6.
Quality gates (run 2026-04-19 on commit 5c2ef4de against origin/main @ 77e579b8)
| Gate | Result |
|---|---|
npm run build |
✓ exit 0 |
npm run lint |
✓ exit 0 |
npm test |
✓ 1123 passed / 1123 total across 27 suites in 32.3 s (0 failures, 0 flakes) |
Test delta: 1085 → 1123 (+38 tests from R81.A). No pre-existing test regressions. The documented startup — subprocess smoke flake did not fire in this run — the isolated new-domain coverage let it stay green.
Invariant checks (contract §3)
- I1 Integer-only: no
Math.*, noDate.*, noNumberfor quantities. Confirmed via explicit grep ofsrc/domains/rules/integer-math.ts. - I2 No side effects: functions pure, no I/O, no globals.
- I3 Synchronous: no
Promise, noawait. - I4 No RNG / no clock: no
Math.random, noDate.now, nocrypto.randomBytes. - I5 Floor rounding: bigint
/truncates toward zero; equivalent to floor for non-negative bps inputs. - I6 int64 boundary:
safe_multhrowsOverflowErroroutside[INT64_MIN, INT64_MAX]range. - I7 Divide-by-zero:
bps_div(x, 0n)andsafe_div(x, 0n)throwDivisionByZeroErrorexplicitly. - I8 Deterministic: same inputs → same outputs (property covered by P1.1.2 determinism harness, delegated to Wave 2).
Acceptance-criteria mapping (task-prompt §P1.1.1)
- AC#1 All arithmetic uses 64-bit signed integers, no floating point
- AC#2
bps_mul(value, bps)→(value * bps) / 10000(floor) - AC#3
bps_div(value, bps)→(value * 10000) / bps(floor) - AC#4
apply_bps(value, bps)→value - bps_mul(value, bps) - AC#5
decay(value, rate_bps, epochs)→ multi-epoch compounded decay with per-step floor - AC#6 Overflow detection:
safe_mulrejects inputs where product > 2^63 - 1 - AC#7 Underflow:
apply_bpsnever goes below 0 for non-negative inputs - AC#8 Division by zero: explicit typed error, not silent wrap
Branch + commit chain
- Branch:
feature/r81-a-p1-1-1-integer-math - Base:
origin/main@77e579b8(R77 pages-finish seal) - Chain:
- audit:
1214a8e8— inventory target surface - contract:
b6973d05— bigint bps helper contract - packet:
4946d43d— execution plan + test matrix - impl:
5c2ef4de— bigint bps arithmetic helpers + test suite (481 insertions, 2 files) - verify: this commit
- audit:
Completion note
Impl + test files were authored by the dispatched T3 executor; Sigma finished the chain (git add + impl commit + this verification) after the agent hit its session quota at step 4. Work product is entirely the executor’s; only the commit ceremony was performed post-rate-limit.
Follow-ups / carry-forward
- None from R81.A itself. Public API matches contract exactly.
- Wave 2 unblocked: P1.1.2 (Determinism Verification Harness — wraps these helpers under fuzz + property tests), P1.1.3 (Branded
Bpstype + overflow-safe variants), P1.3.2 (when P1.3.1 lands, Built-ins delegate here). - Branded-type wrapper (
Bps = bigint & {__brand: 'Bps'}) is intentionally NOT in this PR per contract §3 I6 + the task-prompt’s gotcha #3.