Packet — P2.4.1 Capability Gates (derived limits)
Task ID: fcaac02d-62bd-4ecc-8cdd-23984731c787
Branch: feature/p2-4-1-limits
Base: main @ 7e5cf9d3
Date: 2026-05-12
Step: 3 of 5 (audit → contract → packet → implement → verify)
§1. Purpose
Execution plan for implementing src/domains/reputation/limits.ts and its boundary
test file. Audit (Step 1) inventoried the surface. Contract (Step 2) locked the
behaviour. This packet sequences the work; Step 4 follows it verbatim.
§2. File deltas (closed list)
| Action | Path | Purpose | Approx size |
|---|---|---|---|
| CREATE | src/domains/reputation/limits.ts |
Five derivations | ~180 lines incl. JSDoc |
| CREATE | src/__tests__/domains/reputation/limits.test.ts |
Boundary + ban + determinism tests | ~280 lines |
| (none) | — | No edits to existing files | — |
No package.json deltas; no tsconfig.json deltas; no schema migration.
§3. Sequence (Step 4 implementation)
§3.1 Implementation file — src/domains/reputation/limits.ts
Layout (in order):
- JSDoc header — Phase context, canonical references, determinism inheritance,
κ DSL alias note (
isqrt/ilog2are the canonical κ names for what the spec callssqrt_floor/log2_floor). - Imports (closed set per contract §2.1):
import { BUILTINS, type BuiltinFn } from '../rules/builtins.js'; import { bps_mul, safe_mul, safe_div } from '../rules/integer-math.js'; import { BPS_100_PERCENT } from '../rules/bps-constants.js'; import type { ReputationRow } from './schema.js'; - Module-private builtin handles (resolved once at module load, cast at the
κ boundary —
BUILTINS.getreturnsBuiltinFn | undefined; we assert presence with a non-null assertion guarded by a one-time module-init check):const isqrt: BuiltinFn = BUILTINS.get('isqrt')!; const ilog2: BuiltinFn = BUILTINS.get('ilog2')!;(Determinism scanner allows this — no
Math.*, no clock.) max_parallel_tasks— §4.1 of the contract, with a thin JSDoc.rate_limit_bonus— §4.2 of the contract.stake_discount— §4.3 of the contract.can_arbitrate— §4.4 of the contract.can_govern— §4.5 of the contract.
Each derivation is < 15 lines of body code. Total file with JSDoc lands around 180 lines.
§3.2 Test file — src/__tests__/domains/reputation/limits.test.ts
Layout (in order):
- Header comment — purpose, AC traceability.
- Imports — module under test +
ReputationRowschema type. - Test fixture helper —
makeRow(overrides)builds aReputationRowwith sensible defaults (mirrorscompute.test.ts§Fixtures style). describe('max_parallel_tasks', ...)— 5 boundary cases per §7 of the contract.describe('rate_limit_bonus', ...)— 5 boundary cases.describe('stake_discount', ...)— 5 boundary cases.describe('can_arbitrate', ...)— 7 cases (3 threshold + 3 ban off-by-one + 1 well-above).describe('can_govern', ...)— 5 cases (2 threshold + 3 ban).describe('determinism', ...)— 1 case running each derivation twice and asserting reference-equal output. (5 derivations × 1 test = 5 assertions wrapped in oneit, or split — either is fine; we split for readability.)
Total tests: 5 + 5 + 5 + 7 + 5 + 5 = 32 tests. Comfortably inside the +15–25 estimate from the dispatch packet (audit §8). Step 5 verification reports the actual delta against the 2478 baseline.
§3.3 Pre-flight checks (no commit gate)
Run before implementation:
npm run build(should be clean againstmain; if dirty, escalate before editing).- (Optional)
npm testbaseline confirmation — we trust the dispatch packet’s “2478” claim but a one-shot sanity is cheap.
§3.4 Build/lint/test gates (each is blocking before Step 5)
npm run build— TypeScript compilation. Must be 0 errors.npm run lint— ESLint. Must be 0 errors and 0 warnings on touched files.npm test— Jest. Must show +15–25 tests vs baseline 2478, all green.
If any of the three fails, fix in place and re-run all three. Do not advance to Step 5 until all three are clean.
§4. Risk-aware decisions
§4.1 κ built-in invocation style
BUILTINS.get('isqrt') is the κ source of truth (P1.3.2 contract). The
alternative — re-implementing isqrt/ilog2 locally — was considered and
rejected because:
- It would duplicate Newton’s-method code and create a drift surface.
- The κ built-ins already pass their own determinism + boundary tests.
- Cost: one extra
Map.getper call (negligible).
The downside (cast BuiltinValue → bigint) is acceptable because the κ contract
guarantees isqrt([bigint]) → bigint and ilog2([bigint]) → bigint. We assert
the bigint return inline; if κ ever changes its return shape, our tests will
catch it before merge.
§4.2 BigInt(rep.score) vs BigInt(row.score | 0)
ReputationRow.score is typed number; the schema validator guarantees it is
Number.isInteger and 0 ≤ score ≤ 10000. BigInt(...) of a small integer
number is well-defined. No | 0 truncation needed.
§4.3 BigInt(rep.ban_until_epoch) when null
Contract §4.4–4.5 specify the null check first:
if (rep.ban_until_epoch !== null) {
if (BigInt(rep.ban_until_epoch) > current_epoch) return false;
}
The null branch falls through to the score check. BigInt(null) would throw —
the null guard prevents that.
§4.4 Determinism scanner extension
P2.1.2 (compute.ts) extended the determinism scanner globs to
src/domains/reputation/**. P2.4.1’s new limits.ts automatically inherits
this guard — no scanner config edit required. The test file
limits.test.ts is under src/__tests__/**, outside the production-source
glob, so it can use BigInt and bigint literals freely.
§4.5 Test file location
Place at src/__tests__/domains/reputation/limits.test.ts to match the
existing P2.1.1/P2.1.2/P2.2.1/P2.2.2 test placement
(src/__tests__/domains/reputation/{compute,decay,penalties,schema,determinism}.test.ts).
§5. Commit plan
| # | Commit message | Files |
|---|---|---|
| 1 | audit(p2-4-1-limits): inventory surface (done) |
docs/audits/p2-4-1-limits-audit.md |
| 2 | contract(p2-4-1-limits): behavioral contract (done) |
docs/contracts/p2-4-1-limits-contract.md |
| 3 | packet(p2-4-1-limits): execution plan (this commit) |
docs/packets/p2-4-1-limits-packet.md |
| 4 | feat(p2-4-1-limits): derived capability limits from κ built-ins + reputation scores |
src/domains/reputation/limits.ts, src/__tests__/domains/reputation/limits.test.ts |
| 5 | verify(p2-4-1-limits): test evidence |
docs/verification/p2-4-1-limits-verification.md |
All commits use the imperative mood per CONTRIBUTING.md. No --no-verify, no
--amend, no force-push.
§6. Out-of-scope (binding refusal list)
- ❌ No edits to
compute.ts,decay.ts,penalties.ts,schema.ts. - ❌ No edits to
builtins.ts,integer-math.ts,bps-constants.ts. - ❌ No new MCP tool registration in
src/server.ts. - ❌ No new domain directory; no new schema; no migration.
- ❌ No package upgrades.
§7. Acceptance criteria for this packet
This packet is “approved” if Step 4 implementation:
- Creates exactly the two files in §2.
- Each derivation matches its §4.x algorithm in the contract.
- The 32-test matrix in §3.2 lands green.
npm run build && npm run lint && npm testall pass with the expected delta.
— End of packet —