Verification — P2.1.2 Score Computation
Task ID: fcce8362-60c1-492b-aef3-9546a4e63ce6
Branch: feature/p2-1-2-score-compute
Step: 5 of 5 (audit ✓ → contract ✓ → packet ✓ → implement ✓ → verify)
Date: 2026-05-12
Test evidence + acceptance-criteria checklist for the λ score-computation slice.
§1. Gates (CLAUDE.md §5)
All three Phase 0 gates pass in the worktree.
PS .worktrees/claude/p2-1-2-score-compute> npm run build
> colibri@0.0.1 build
> tsc
> colibri@0.0.1 postbuild
> node scripts/copy-migrations.mjs
copy-migrations: copied 7 migration(s) src/db/migrations -> dist/db/migrations
PS .worktrees/claude/p2-1-2-score-compute> npm run lint
> colibri@0.0.1 lint
> eslint src
PS .worktrees/claude/p2-1-2-score-compute> npm test
...
Test Suites: 50 passed, 50 total
Tests: 2466 passed, 2466 total
Snapshots: 0 total
Time: 44.617 s
Ran all test suites.
Test-count delta
| Metric | Value |
|---|---|
Baseline at main @ 994db1e4 (post-P2.1.1) |
2444 tests, 48 suites |
| Post-P2.1.2 | 2466 tests, 50 suites |
| Delta | +22 tests, +2 suites |
| Regressions | 0 |
| Flakes | 0 (full suite passed on first try) |
The +2 suite count breaks down as:
src/__tests__/domains/reputation/compute.test.ts— 16 unit + 4 property = 20 testssrc/__tests__/domains/reputation/determinism.test.ts— 2 tests
20 + 2 = 22. Matches the delta exactly.
Coverage on new code
File | % Stmts | % Branch | % Funcs | % Lines |
src/domains/reputation/compute.ts | 100 | 100 | 100 | 100 |
100% statement / branch / function / line coverage on compute.ts.
§2. Acceptance-criteria coverage (contract §6)
| ID | Criterion | Evidence |
|---|---|---|
| AC-1 | compute_score exported with locked signature |
src/domains/reputation/compute.ts:105–115 exports the function with (node_id, domain, events, ack_lookup, scar_lookup) → bigint shape. Type-checked by tsc. Imported by name in test file. |
| AC-2 | ack_capped ≤ BPS_100_PERCENT before bps_mul | U-3 (ack=50000n → result equals event.delta, not 5×), P-3 (1000 iter: per-event score ≤ |delta|). Code site: compute.ts:138–142. |
| AC-3 | Arithmetic via integer-math.ts only |
compute.ts:52 imports only bps_mul; scanner test confirms zero forbidden patterns. |
| AC-4 | Ceiling = BPS_100_PERCENT - scar; clamp scar to [0n, 10000n] |
U-5 (scar=2000n → ceiling=8000n), U-6 (scar=50000n clamped to 10000n → ceiling=0n), U-14 (scar=10000n exactly → ceiling=0n), U-15 (scar=-500n clamped to 0n → ceiling=10000n). Code: compute.ts:155–166. |
| AC-5 | Floor = 0n; negative aggregate clamps | U-11 (one negative-delta event → 0n). Code: compute.ts:151–153. |
| AC-6 | Monotonicity under positive-only events | P-1, 1000 iter, pinned seed 0x1f9bc0deafn. Each prefix score asserted ≥ previous. |
| AC-7 | Determinism | P-2, 1000 iter, pinned seed 0xfa1afe1n. Two-call equality on identical inputs. |
| AC-8 | Ack-cap holds — no contribution exceeds \|delta\| magnitude |
P-3, 1000 iter, pinned seed 0xc0ffeebabe1n. Lookup returns up to 1_000_000n; per-event score ≤ BigInt(delta). |
| AC-9 | No Math., Date., Math.random in src/domains/reputation/compute.ts |
src/__tests__/domains/reputation/determinism.test.ts corpus self-scan (mirrors κ P1.1.2 pattern manifest). |
| AC-10 | Empty events → 0n | U-1. |
| AC-11 | Out-of-domain events skipped | U-8 (passes 1× execution + 1× commissioning; only execution counted). |
| AC-12 | Out-of-node events skipped | U-9. |
| AC-13 | Sort total + stable across shuffles | U-10 (same delta sum from shuffled vs sorted input; includes same-epoch / id-tiebreak case). |
| AC-14 | Return type is bigint |
U-12, P-4 (1000 iter typeof === 'bigint'). |
All 14 acceptance criteria met.
§3. Forbidden-op scanner — proof of extension
The κ P1.1.2 determinism scanner is now extended to src/domains/reputation/**
via a parallel scoped test file (rather than modifying the κ scanner test
in-place). This preserves κ test independence while satisfying the source
prompt’s “extend globs” requirement.
Scanner test location:
src/__tests__/domains/reputation/determinism.test.ts
Pattern manifest mirrors κ contract r83-a-determinism-contract.md §4:
Math.* · Date.* · new Date · timer · network · require(fs) · import fs ·
crypto.* · process.time · await · async fn · float literal.
Exclusion: schema.ts
src/domains/reputation/schema.ts (P2.1.1 shipped code) uses Math.max /
Math.min on number for offset / limit clamping in selectHistory. The
exclusion is documented in three places:
- Audit §5.7 (risk + mitigation)
- Contract §5.3 (test-contract clause)
- Scanner test file header (in-code comment)
This exclusion is symmetric with the κ scanner’s own self-exclusion of
determinism.ts (the scanner can’t include its own pattern library). The
exclusion list is a ReadonlySet<string> initialized with exactly one entry:
'schema.ts'. Any future file added under src/domains/reputation/ is
scanned by default.
Belt + suspenders test
A second test (compute.ts is present in the reputation directory and gets
scanned) explicitly pins both compute.ts and schema.ts as present. This
guards against the scenario where someone renames compute.ts without
updating the exclusion list — the first test would still pass with zero
files scanned (modulo the expect(scannedFiles).toBeGreaterThanOrEqual(1)
guard, but that fires only if every .ts ends up excluded). The second
test makes the failure mode unambiguous.
§4. File inventory
| Path | Lines | Type |
|---|---|---|
src/domains/reputation/compute.ts |
177 | TypeScript |
src/__tests__/domains/reputation/compute.test.ts |
431 | Jest |
src/__tests__/domains/reputation/determinism.test.ts |
133 | Jest |
docs/audits/p2-1-2-score-compute-audit.md |
247 | Doc |
docs/contracts/p2-1-2-score-compute-contract.md |
283 | Doc |
docs/packets/p2-1-2-score-compute-packet.md |
312 | Doc |
docs/verification/p2-1-2-score-compute-verification.md |
(this file) | Doc |
No edits to:
src/server.ts,src/config.ts, or any existing TS modulesrc/domains/reputation/schema.ts(P2.1.1 surface frozen)src/__tests__/domains/rules/determinism.test.ts(κ scanner unchanged)package.json(zero new deps — hand-rolled LCG instead of fast-check)
Greenfield function add + scoped scanner test extension; no refactor.
§5. Commit chain
| Step | SHA (HEAD-relative) | Subject |
|---|---|---|
| 1 audit | e020c555 |
audit(p2-1-2-score-compute): inventory surface |
| 2 contract | ad0ae11f |
contract(p2-1-2-score-compute): behavioral contract |
| 3 packet | a0f4c25f |
packet(p2-1-2-score-compute): execution plan |
| 4 implement | 94249ee6 |
feat(p2-1-2-score-compute): Σ-aggregation with ack-weight cap + monotonicity property test |
| 5 verify | (this commit) | verify(p2-1-2-score-compute): test evidence |
Branch is rooted at origin/main @ 994db1e4 (post-P2.1.1 PR #226).
§6. Risks tracked → resolution
| Risk (audit §5) | Status | Resolution |
|---|---|---|
| bigint × number TypeError | Resolved | BigInt(event.delta) bridge applied at function boundary; covered by U-2 / U-4 |
| schema.ts has Math.max/min | Resolved | Documented in-file exclusion in scanner test |
| event_id as acker key is convention | Locked | Compute uses event.event_id verbatim; P2.5.1 decides encoding |
| Sort stability | Resolved | Total comparator (epoch, id); V8 stable since Node 12; U-10 asserts |
| bps_mul overflow | Non-issue | Worst case 1000 × 10000 = 10_000_000n ≪ MAX_INT64 |
| scar_bps > 10000 from lookup | Resolved | Defensive if (scar > BPS_100_PERCENT) scar = BPS_100_PERCENT; at compute.ts:163 |
| Self-scan exclusion symmetry | Resolved | Mirrors κ pattern; documented in audit §5.7 |
| No fast-check | Non-issue | Hand-rolled LCG with pinned seeds; reproducible across CI |
§7. Phase 0 surface impact
- MCP tool count: 14, unchanged (P2.1.2 is library-only).
- Database schema: unchanged (no migration, no new table).
- Public TypeScript surface adds
compute_score,AckLookup,ScarLookupfromsrc/domains/reputation/compute.ts. Consumers (P2.4.1 capability gates, P2.5.1reputation_get) will import these in subsequent waves. - κ scanner contract: untouched. The parallel scoped scanner test in
src/__tests__/domains/reputation/determinism.test.tsis the canonical extension tosrc/domains/reputation/**per AC-9.
§8. Phase 0 / Phase 2 posture
- λ Reputation per
docs/3-world/social/reputation.md§Phase 0 posture: “Thereputations,experience_tokens, andpenalty_eventstables exist in the schema but are not populated.” This task does not change that — it ships the pure function that a future Phase 2 tool (P2.5.1) will call. - ADR-005 §Decision: no multi-model routing in Phase 0; P2.1.2 has zero interaction with the δ router surface.
- ADR-006 graduation: λ stays
colibri_code: noneuntil P2.5.1 mounts an MCP-tool surface. P2.1.2 is a library-only graduation towardpartial.
§9. Sign-off
All five steps of the executor chain complete. All three CLAUDE.md §5 gates green. Twenty-two new tests landed; zero regressions; zero flakes. 100% coverage on the new function. Ready for PR open.