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):

  1. JSDoc header — Phase context, canonical references, determinism inheritance, κ DSL alias note (isqrt/ilog2 are the canonical κ names for what the spec calls sqrt_floor/log2_floor).
  2. 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';
    
  3. Module-private builtin handles (resolved once at module load, cast at the κ boundary — BUILTINS.get returns BuiltinFn | 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.)

  4. max_parallel_tasks — §4.1 of the contract, with a thin JSDoc.
  5. rate_limit_bonus — §4.2 of the contract.
  6. stake_discount — §4.3 of the contract.
  7. can_arbitrate — §4.4 of the contract.
  8. 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):

  1. Header comment — purpose, AC traceability.
  2. Imports — module under test + ReputationRow schema type.
  3. Test fixture helpermakeRow(overrides) builds a ReputationRow with sensible defaults (mirrors compute.test.ts §Fixtures style).
  4. describe('max_parallel_tasks', ...) — 5 boundary cases per §7 of the contract.
  5. describe('rate_limit_bonus', ...) — 5 boundary cases.
  6. describe('stake_discount', ...) — 5 boundary cases.
  7. describe('can_arbitrate', ...) — 7 cases (3 threshold + 3 ban off-by-one + 1 well-above).
  8. describe('can_govern', ...) — 5 cases (2 threshold + 3 ban).
  9. describe('determinism', ...) — 1 case running each derivation twice and asserting reference-equal output. (5 derivations × 1 test = 5 assertions wrapped in one it, 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 against main; if dirty, escalate before editing).
  • (Optional) npm test baseline 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)

  1. npm run build — TypeScript compilation. Must be 0 errors.
  2. npm run lint — ESLint. Must be 0 errors and 0 warnings on touched files.
  3. 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.get per call (negligible).

The downside (cast BuiltinValuebigint) 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 test all pass with the expected delta.

— End of packet —


Back to top

Colibri — documentation-first MCP runtime. Apache 2.0 + Commons Clause.

This site uses Just the Docs, a documentation theme for Jekyll.