R83.A verification — P1.1.2 Determinism Verification Harness
Scope recap
Second Phase 1 κ Rule Engine sub-task: greenfield src/domains/rules/determinism.ts —
pure N-run equality harness + function-body forbidden-op regex scanner that every downstream
κ sub-task (P1.1.3, P1.2.x, P1.3.x, P1.4.x, P1.5.x) will call to self-assert their
helpers stay integer-only, RNG-free, clock-free, async-free. Authoritative spec: the
pre-authored prompt at
docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md
§P1.1.2 (lines 199–318) +
docs/3-world/physics/laws/rule-engine.md
§Forbidden operations + the R83 orchestrator dispatch prompt (splits the
task-prompts doc’s single test file into harness library + test pair).
Deliverables
| File | LOC | Purpose |
|---|---|---|
src/domains/rules/determinism.ts |
276 | N-run harness + forbidden-op scanner + deep-equal helper |
src/__tests__/domains/rules/determinism.test.ts |
622 | 87 tests, 100% branch coverage on determinism.ts |
Public API (confirmed in impl)
// Typed error
class DeterminismError extends Error
// N-run harness — default iterations = 10, throws DeterminismError on drift
assertDeterministic<A, R>(fn, args, opts?): R
// Forbidden-op regex scanner over Function.prototype.toString()
inspectFunctionForbidden(fn: Function): readonly string[]
// Throwing wrapper — throws DeterminismError when inspect returns non-empty
assertNoForbiddenOps(fn: Function, opts?: { label?: string }): void
// Deep structural equality with bigint + Error awareness
deepEqualDeterministic(a: unknown, b: unknown): boolean
All signatures are synchronous. No outbound imports from determinism.ts — self-standing
to preserve invariant I1 (the library itself uses none of the forbidden ops it scans for).
Forbidden-op patterns enforced
13 regexes, manifest-frozen in determinism.ts lines 55–70 (= contract §4):
Math.*— any static Math accessDate.*— any static Date accessnew Date— constructor formsetTimeout|setInterval|setImmediate— timersfetch|XMLHttpRequest— networkrequire('fs')/require('node:fs')from 'fs'/from 'node:fs'— ESM fscrypto.*— any crypto randomnessprocess.hrtime/process.nextTick— process timersawaitasync function/async (- Float literal
\d+\.\d+(does NOT match1nor1_000_000) [native code]— native-code bodies
Quality gates (run 2026-04-19 on commit 34ffa8d7 against origin/main @ 657d4ef4)
| Gate | Result |
|---|---|
npm run build |
✓ exit 0 |
npm run lint |
✓ exit 0 |
npm test (first run) |
1209 / 1210 — 1 pre-existing startup — subprocess smoke flake hit |
npm test (second run — rerun-once policy per prompt) |
✓ 1210 passed / 1210 total across 28 suites in 23.6 s (0 failures) |
Coverage on the new library file
--------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered
--------------------------|---------|----------|---------|---------|-------------------
src/domains/rules | 99.23 | 98.46 | 100 | 99.22 |
determinism.ts | 100 | 100 | 100 | 100 |
integer-math.ts | 96.42 | 83.33 | 100 | 96.29 | 180
--------------------------|---------|----------|---------|---------|-------------------
AC#9 satisfied: 100% branch coverage on determinism.ts. (integer-math.ts
coverage numbers carry over from R81.A untouched — line 180 is the safe_div: b ===
0n branch covered by P1.1.1 tests in the co-located integer-math test file but
the new test-path filter reports it as “uncovered” because the filter ran only
determinism tests. Full-suite coverage under npm test without filter shows it
fully exercised; unchanged vs R81.A.)
Test-count delta
- Baseline (R81.A main): 1123 tests
- Post-R83.A: 1210 tests
- Delta: +87 new tests, all in
src/__tests__/domains/rules/determinism.test.ts
Acceptance-criteria traceability
| AC | Status | Test reference |
|---|---|---|
| AC#1 — five exports + DeterminismError | ✓ | Group 1 (DeterminismError), every import in the test file |
| AC#2 — harness wraps all six P1.1.1 helpers, 10 iterations each | ✓ | Group 2 (happy path over P1.1.1 helpers) — 7 tests |
| AC#3 — FAIL loudly on non-deterministic mock | ✓ | Group 4 (fails on non-deterministic functions) — 7 tests |
AC#4 — inspectFunctionForbidden returns [] for P1.1.1 helpers |
✓ | Group 6 (clean P1.1.1 helpers) — 6 tests |
| AC#5 — inspect detects each of 13 forbidden patterns | ✓ | Group 7 (detects every forbidden pattern) — 20 tests |
AC#6 — assertNoForbiddenOps wrapper |
✓ | Group 8 (assertNoForbiddenOps) — 5 tests |
AC#7 — deepEqualDeterministic all branches |
✓ | Group 9 (deepEqualDeterministic) — 18 tests |
AC#8 — corpus self-scan over src/domains/rules/*.ts |
✓ | Group 12 (rule-engine corpus self-scan) — 1 test |
AC#9 — 100% branch coverage on determinism.ts |
✓ | Jest lcov shows 100/100/100/100 on determinism.ts |
| AC#10 — all three gates green on 1123+ baseline | ✓ | 1210/1210 second-run |
Scope divergence from task-prompts canonical doc
The canonical docs/guides/implementation/task-prompts/p1.1-kappa-rule-engine.md §P1.1.2
lists one file (src/domains/rules/__tests__/determinism.test.ts) with the harness
inline inside the test. The R83 orchestrator dispatch prompt (authoritative for this
execution) splits that into two files:
src/domains/rules/determinism.ts— reusable librarysrc/__tests__/domains/rules/determinism.test.ts— tests (repo-convention path per R81.A)
The orchestrator’s reason: “This is a guardrail for all of Phase 1” — future sub-tasks
(P1.1.3, P1.3.1, P1.3.2) can import { assertDeterministic, assertNoForbiddenOps } from
their own test files rather than re-rolling the harness. The library shape is strictly
more capable than the inline-only shape; the test surface remains equivalent.
Additional deliberate deferrals recorded in contract §9:
- No seeded-PRNG fuzz over 10k tuples. The task-prompts doc mentions fuzz; the orchestrator scoped this task to “N times (configurable, e.g. 10 iterations)”. Property-style fuzz will land in later P1 sub-tasks that operate on larger input spaces (P1.3.1 eval loop).
- No
fast-checkdev-dep. Not in package.json; adding it would be scope-creep. - No MCP tool registration. Library-only, same as P1.1.1.
What was NOT changed
src/server.ts— untouched. 14-tool Phase 0 MCP surface preserved.src/db/*— untouched. No schema migration.src/domains/{tasks,router,skills,trail,proof,integrations}/— untouched.src/domains/rules/integer-math.ts— untouched.- No ADR authored or amended.
- No concept-doc graduation. κ stays
colibri_code: none; graduation waits on the evaluator (P1.3.1 earliest, P1.4.1 most likely). - No frontmatter edits outside this chain’s own quartet.
- No changes under
.claude/,.agents/,docs/5-time/,CLAUDE.md.
Worktree discipline
- Worktree:
E:\AMS\.worktrees\claude\r83-a-determinism - Branch:
feature/r83-a-determinism - Base:
origin/main @ 657d4ef4(post-R82 seal) - Pre-round state verified clean: zero commits ahead of main before audit.
Commit chain on feature/r83-a-determinism
34ffa8d7 feat(r83-a-determinism): P1.1.2 determinism verification harness
506ad720 packet(r83-a-determinism): execution plan
c411644c contract(r83-a-determinism): behavioral contract
2973a340 audit(r83-a-determinism): inventory harness surface
Plus this verification commit.
Gate-capture transcript
npm run build
> colibri@0.0.1 build
> tsc
(exit 0; no output)
npm run lint
> colibri@0.0.1 lint
> eslint src
(exit 0; no output)
npm test (second run, rerun-once policy)
Test Suites: 28 passed, 28 total
Tests: 1210 passed, 1210 total
Snapshots: 0 total
Time: 23.633 s, estimated 34 s
Ran all test suites.
Residual risks / notes for reviewer
- Pre-existing
startup — subprocess smokeflake — first-run only, clears on rerun. Not introduced by this task. Known pre-R83 flake (memory file). Rerun policy per prompt. - P1.1.1
integer-math.tscoverage numbers — unchanged vs R81.A (96.42/83.33/100/96.29). R83.A does not modify this file. - Lint
@typescript-eslint/no-unsafe-function-typedisables — five inline disables for the intentionalFunctiontype ininspectFunctionForbidden/assertNoForbiddenOps- four in the test file for the
{ toString() } as unknown as Functionforgery pattern. Documented in contract §R2. NarrowingFunctionto(...args: unknown[]) => unknownwould reject class-method refs; we accept the lint exemption for the inspector’s introspection surface.
- four in the test file for the
- Line 180 in
integer-math.tsshown uncovered only under the filtered--testPathPatternrun becauseinteger-math.test.tsdoesn’t match the filter. Under fullnpm testthe line is covered by R81.A’s tests. No R83.A regression.
R83 Wave 2 context
- Round: R83 — kappa-phase-1-wave-2
- Manifest PR: https://github.com/LastEld/AMS/pull/187
- Parallel tasks in this wave: R83.B (P1.1.3 BPS constants) and R83.C (P1.2.1 lexer retry); this PR (R83.A) is independent of both — P1.1.3 depends on P1.1.1 only, P1.2.1 depends on nothing.
- Sigma seal (Phase B) expected to aggregate A + B + C into the R83 seal commit.
Writeback
The formal writeback lands in the PR body at push time (see packet §8). A live-MCP
thought_record is not possible in the current round environment (no MCP client
attached); the PR body + this verification doc carry the writeback until Colibri
MCP client attaches per CLAUDE.md §4 branch-on-MCP-client-presence.