Contract — P0.7.3 ζ Chain Verification Tool

1. Purpose

Specify the behavioral contract for src/domains/trail/verifier.ts and the audit_verify_chain MCP tool. This is the final ζ axis task; it closes the Decision Trail surface.

2. Exports

2.1 VerifyChainResult (type)

export interface VerifyChainResult {
  readonly valid: boolean;
  readonly broken_count: number;
  readonly first_broken_at?: string;   // id of the first broken record; absent when valid
}

2.2 verifyChain(records: ThoughtRecord[]): VerifyChainResult

Pure function. No DB access, no side effects.

Inputs:

  • records — an ordered array of ThoughtRecord values, in insertion order (caller must supply them sorted by rowid ASC as returned by listThoughtRecords).

Algorithm (O(n)):

For each record at position i:

  1. Content-hash check: recompute computeHash(record) and compare to record.hash. If mismatch → record is broken.

  2. Link check: compare record.prev_hash to the expected prev_hash:

    • If i === 0: expected = ZERO_HASH.
    • If i > 0: expected = records[i-1].hash. If mismatch → record is broken.

A record is broken if either check fails. The first broken record sets first_broken_at. The walk continues to count all broken records (broken_count).

Output invariants:

Condition valid first_broken_at broken_count
Empty input true absent 0
All records pass both checks true absent 0
One or more records fail false id of first broken ≥ 1

Note on multi-task chains: if records spans multiple task_id values, the prev_hash check uses position-in-array (not task boundary) as context. Callers that want per-task isolation must filter before calling verifyChain. The MCP tool always filters by task_id when a filter is supplied.

2.3 AuditVerifyChainToolInputSchema (Zod schema)

export const AuditVerifyChainToolInputSchema = z.object({
  task_id: z.string().min(1).optional(),
});

Both fields optional; omitting task_id verifies the full chain across all tasks.

2.4 registerVerifyChainTool(ctx: ColibriServerContext): void

Registers the audit_verify_chain MCP tool against ctx. Idempotent — a second registration on the same ctx throws via registerColibriTool’s duplicate-registration guard.

Tool name: audit_verify_chain

MCP tool description: "Verify the integrity of the ζ decision-trail hash chain. Returns {valid, broken_count, first_broken_at?, record_count}."

Handler behaviour:

  1. Lazy-resolve DB via getDb() at call-time (Phase-2 safe).
  2. Call listThoughtRecords(db, filters) with the optional task_id filter.
  3. Call verifyChain(records).
  4. Return { ...result, record_count: records.length }.

Return shape (success, wrapped in {ok: true, data: ...} by α middleware):

{
  valid: boolean;
  broken_count: number;
  first_broken_at?: string;
  record_count: number;
}

3. Purity contract

  • verifyChain is a pure function — no DB access, no I/O, no side effects. Tests drive it directly with fabricated ThoughtRecord[] arrays.
  • registerVerifyChainTool has no module-level state. getDb() is resolved inside the handler closure (call-time), not at registration time.
  • No new dependencies. Reuses computeHash from schema.ts and listThoughtRecords from repository.ts.

4. Error handling

Condition Behaviour
verifyChain with empty array Returns { valid: true, broken_count: 0 }
DB error in MCP handler Propagates; α middleware wraps in {ok: false, error}
Zod validation failure Propagates ZodError; α middleware wraps

5. No migration

This task adds no new SQL tables, columns, or indexes. user_version is not bumped. verifier.ts is read-only against the existing thought_records table.

6. Server.ts wiring

server.ts imports registerVerifyChainTool from ./domains/trail/verifier.js and calls it alongside registerThoughtTools. One import line, one call site.

7. Acceptance criteria mapping

Spec criterion Contract clause
verifyChain(records[]) iterates chain, recomputes each hash §2.2 algorithm
Returns { valid, first_broken_at?, broken_count } §2.1 type
audit_verify_chain MCP tool, optional task_id filter §2.3, §2.4
Tamper test: valid: false at correct position §2.2 + test plan
100-record chain: valid: true in < 500ms §2.2 + test plan

Back to top

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

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