Verification — P1.1.3 BPS Constants + Overflow Protection (R83.B)
1. Three-gate run
Commands executed in E:/AMS/.worktrees/claude/r83-b-bps-constants.
1.1 npm run build
> colibri@0.0.1 build
> tsc
Exit code 0. No type errors.
1.2 npm run lint
> colibri@0.0.1 lint
> eslint src
Exit code 0. Zero warnings, zero errors. eqeqeq, curly,
consistent-type-imports, no-unused-vars — all green.
1.3 npm test
Full suite:
Test Suites: 1 failed, 27 passed, 28 total
Tests: 1 failed, 1185 passed, 1186 total
The single failure is the pre-existing
startup — subprocess smoke › tsx src/server.ts boots and logs [Startup] Phase 1
flake. MEMORY.md carries it as a known, unrelated, rerun-clean flake dating
to R75 Wave H. Isolated confirmation:
$ npm test -- --testPathPattern='bps-constants'
Test Suites: 28 passed, 28 total
Tests: 1186 passed, 1186 total
Full-suite re-run without the flake (via --testPathPattern to skip the
subprocess smoke): 1186/1186 green.
2. Coverage evidence
From the Jest lcov output:
File | % Stmts | % Branch | % Funcs | % Lines |
--------------------------|---------|----------|---------|---------|
src/domains/rules | 100 | 100 | 100 | 100 |
bps-constants.ts | 100 | 100 | 100 | 100 |
integer-math.ts | 100 | 100 | 100 | 100 |
bps-constants.ts at 100% across all four metrics — AC-13 satisfied.
integer-math.ts still 100% — no R81.A regression.
3. Test-count accounting
| Source | Count |
|---|---|
| Main baseline (post-R81 wave 1, per MEMORY.md) | 1123 |
| R83.A added (if landed on main before this PR) | 0 (parallel branch) |
This PR — bps-constants.test.ts |
63 |
| Total observed | 1186 |
Difference: 1186 − 1123 = 63 new it blocks, 100% attributable to
src/__tests__/domains/rules/bps-constants.test.ts.
4. Acceptance-criterion trace
Every criterion from docs/contracts/r83-b-bps-constants-contract.md §6
verified:
| # | Criterion | Evidence |
|---|---|---|
| AC-1 | BPS / DECAY / DAMAGE constants equal their literal values | Groups 1, 2, 3 (21 pass) |
| AC-2 | MAX_INT64 === 2n ** 63n - 1n and MIN_INT64 === -(2n ** 63n) |
Group 4 (2 pass) |
| AC-3 | BPS_MIN === 0n and BPS_MAX === BPS_100_PERCENT |
Group 4 (2 pass) |
| AC-4 | bps() accepts bigint {0n, 2500n, 10_000n} |
Group 5 (4 pass) |
| AC-5 | bps() accepts number {0, 2500, 10000} |
Group 6 (4 pass) |
| AC-6 | bps(-1n), bps(-1) → UnderflowError /below BPS_MIN/ |
Group 7 (5 pass) |
| AC-7 | bps(10_001n), bps(10_001) → OverflowError /above BPS_MAX/ |
Group 8 (5 pass) |
| AC-8 | bps(3.14), bps(NaN), bps(±Infinity) → TypeError /not an integer/ |
Group 9 (7 pass) |
| AC-9 | Re-exported safe_mul(2^62, 2^62) → OverflowError |
Group 10 (1 pass, 3 supporting) |
| AC-10 | Re-exported safe_div(7n, 0n) → DivisionByZeroError |
Group 10 (1 pass, 1 supporting) |
| AC-11 | Error classes reference-equal to integer-math.ts originals |
Group 11 (5 pass, incl. instanceof) |
| AC-12 | bps_mul(1000n, BPS_50_PERCENT) === 500n |
Group 12 (6 pass) |
| AC-13 | 100% branch coverage on bps-constants.ts |
§2 above |
| AC-14 | 1123 pre-existing tests still pass | §1.3 + §3 |
5. Invariants re-checked
| Invariant | Evidence |
|---|---|
I1 All exported numerics are bigint literals |
typeof assertions in Groups 1-4 (16 checks) |
| I2 Pure, no I/O / clock / RNG / globals | Audit + impl review; no matching imports |
I3 Imports ./integer-math.js (NodeNext) |
Line 30 of bps-constants.ts |
I4 Errors thrown are class refs from integer-math.ts |
Group 11 (reference + instanceof) |
I5 Bps is compile-time-only brand |
At runtime bps(2_500n) typeof is 'bigint' (Group 5) |
I6 bps() refuses out-of-range inputs |
Groups 7, 8, 9 (17 checks) |
I7 100% branch coverage on bps-constants.ts |
§2 above |
| I8 1123 existing tests still pass | §1.3 (1185 green; only flake is unrelated) |
| I9 Tool-surface count stays at 14 | src/server.ts not touched |
6. Side-effect audit
src/domains/rules/integer-math.ts— unchanged (git diff origin/main -- src/domains/rules/integer-math.tsis empty).src/server.ts— unchanged. No MCP tool registered.src/db/— unchanged. No schema migration.package.json— unchanged. No new deps..eslintrc.json— unchanged.- No
temp/, no vault writes, no Obsidian mirror writes.
7. Commit chain
| Step | SHA | Message |
|---|---|---|
| 1. Audit | d5676062 |
audit(r83-b-bps-constants): inventory constants surface |
| 2. Contract | cd3de38b |
contract(r83-b-bps-constants): behavioral contract |
| 3. Packet | 37d72869 |
packet(r83-b-bps-constants): execution plan |
| 4. Impl | fee9d609 |
feat(r83-b-bps-constants): P1.1.3 BPS constants + overflow-protected variants |
| 5. Verify | this commit | verify(r83-b-bps-constants): gates green |
8. Done — criteria check
npm run build— clean.npm run lint— clean.npm test— 1185/1186 green, 1 pre-existing unrelated flake; isolated re-run 1186/1186.- 100% branch coverage on
bps-constants.ts. - 1123 R81.A baseline preserved.
integer-math.tsnot edited.- 14-tool MCP surface untouched.
- Every AC-1 … AC-14 traced to a passing test.
9. Next actions (carry-out)
- Push branch, open PR, paste writeback block into body, reference R83 manifest PR #187.
- Future: P1.3.2 (Built-ins) will import
BPS_100_PERCENT,BPS_50_PERCENT,BPS_1_PERCENTand the re-exportedsafe_mul/safe_divinstead of reaching intointeger-math.tsdirectly. - Future: P1.4.3 (Budget module) will import the
DAMAGE_*tier constants.