P0.8 — η Proof Store — Agent Prompts

Copy-paste-ready prompts for agents tackling each task in this group. Canonical spec: task-breakdown.md §P0.8 Master bootstrap prompt: agent-bootstrap.md

Group summary

Task ID Title Depends on Effort Unblocks
P0.8.1 Merkle Tree Construction P0.7.2 S P0.8.2, P0.8.3
P0.8.2 Three-Zone Retention P0.8.1, P0.2.2 M long-term audit storage
P0.8.3 Merkle Root Finalization Tool P0.8.1 S proof-grade workflow (audit_session_start → work → audit_verify_chain → thought_record → merkle_finalize → merkle_root)

P0.8.1 — Merkle Tree Construction

Spec source: task-breakdown.md §P0.8.1 Extraction reference: docs/reference/extractions/eta-proof-store-extraction.md Worktree: feature/p0-8-1-merkle-tree Branch command: git worktree add .worktrees/claude/p0-8-1-merkle-tree -b feature/p0-8-1-merkle-tree origin/main Estimated effort: S (Small — 1-2 hours) Depends on: P0.7.2 (Hashes from thought records) Unblocks: P0.8.2 (Retention uses Merkle proofs), P0.8.3 (Finalization builds tree)

Files to create

  • src/domains/proof/merkle.ts — Merkle tree construction and proof generation
  • tests/domains/proof/merkle.test.ts — Tree construction, proof verification, determinism tests

Acceptance criteria

  • Uses merkletreejs package (SHA-256 leaves)
  • buildMerkleTree(recordHashes[]) returns { root, tree }
  • generateProof(tree, leafHash) returns proof array
  • verifyProof(root, proof, leafHash) returns boolean
  • Empty tree: root = SHA-256(“”) (defined constant)
  • Test: 10-leaf tree root is deterministic; membership proof verifies correctly

Pre-flight reading

  • CLAUDE.md — execution rules
  • docs/guides/implementation/task-breakdown.md §P0.8.1 — full spec
  • docs/reference/extractions/eta-proof-store-extraction.md — Merkle structure
  • merkletreejs npm package documentation

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.8.1 — Merkle Tree Construction
Build Merkle trees from thought record hashes for cryptographic proof generation.

FILES TO READ FIRST:
1. CLAUDE.md (execution rules)
2. docs/guides/implementation/task-breakdown.md §P0.8.1
3. docs/reference/extractions/eta-proof-store-extraction.md
4. merkletreejs npm package (https://github.com/merkletreejs/merkletreejs)

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-8-1-merkle-tree -b feature/p0-8-1-merkle-tree origin/main
cd .worktrees/claude/p0-8-1-merkle-tree

DEPENDENCIES:
npm install merkletreejs

FILES TO CREATE:
- src/domains/proof/merkle.ts
  * Import MerkleTree from 'merkletreejs'
  * Import SHA256 from 'crypto-js' or use Node.js crypto
  * Type MerkleTreeResult = {root: string, tree: MerkleTree}
  * Constant EMPTY_TREE_ROOT = SHA-256("") in hex
  * Function buildMerkleTree(recordHashes: string[]): MerkleTreeResult
    - Input: array of SHA-256 hashes from thought records (64-hex strings)
    - If empty array: return {root: EMPTY_TREE_ROOT, tree: null} (or handle gracefully)
    - Create MerkleTree with:
      - Leaves: convert hex strings to Buffer objects
      - Hash function: SHA-256
      - Options: {sortPairs: false} (preserve insertion order, do NOT auto-sort)
    - Return {root: tree.getRoot().toString('hex'), tree}
  * Function generateProof(tree: MerkleTree, leafHash: string): string[]
    - Find leaf in tree matching leafHash (in hex)
    - Get proof path using tree.getProof(leaf)
    - Return proof as array of hex strings
  * Function verifyProof(root: string, proof: string[], leafHash: string): boolean
    - Reconstruct root from leaf + proof
    - Compare to provided root
    - Return true if match, false if mismatch
  * Use merkletreejs built-in verify method (tree.verify(proof, leaf, root))
  * Handle edge cases: empty tree, single leaf, large trees (100+)
  * Determinism: same input hashes → same root (critical for audits)

- tests/domains/proof/merkle.test.ts
  * Test buildMerkleTree with 10 hashes: tree builds, root is 64-hex string
  * Test determinism: buildMerkleTree(hashes) twice → same root
  * Test generateProof: for each leaf in 10-leaf tree, generate proof
  * Test verifyProof: verify each proof against original leaf + root → all true
  * Test membership: proof for leaf[3] verifies for leaf[3] but NOT for leaf[4] → false
  * Test empty tree: buildMerkleTree([]) → root = EMPTY_TREE_ROOT
  * Test single leaf: buildMerkleTree([leaf]) → root = SHA-256(leaf)
  * Test order preservation: buildMerkleTree([A, B, C]) vs buildMerkleTree([C, B, A]) → different roots
  * Test large tree: buildMerkleTree(100 hashes) → completes in < 100ms

ACCEPTANCE CRITERIA (headline):
✓ Uses merkletreejs with SHA-256
✓ buildMerkleTree(recordHashes[]) returns {root, tree}
✓ generateProof(tree, leafHash) returns proof array
✓ verifyProof(root, proof, leafHash) returns boolean
✓ Empty tree root = SHA-256("")
✓ 10-leaf tree root deterministic; membership proof verifies

SUCCESS CHECK:
cd .worktrees/claude/p0-8-1-merkle-tree && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.8.1", status="done", progress=100)
thought_record(task_id="P0.8.1", branch="feature/p0-8-1-merkle-tree",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented buildMerkleTree(recordHashes[]): SHA-256 MerkleTree from thought record hashes, returns {root, tree}. generateProof(tree, leafHash) produces proof array. verifyProof(root, proof, leafHash) validates membership. Empty tree root = SHA-256(''), deterministic for same inputs. Tests: 10-leaf determinism, membership verification, order preservation, large trees (100 hashes < 100ms).")

FORBIDDENS:
✗ Do not use merkletreejs with sortPairs=true (breaks determinism and order preservation)
✗ Do not confuse tree roots: empty tree is SHA-256(""), NOT null
✗ Do not allow key/value merkles (use only hash-based, no metadata)
✗ Do not edit main checkout

NEXT:
P0.8.2 — Three-Zone Retention (compresses records, uses Merkle proofs for cold zone)

Verification checklist (for reviewer agent)

  • merkletreejs imported with SHA-256
  • buildMerkleTree handles empty, single, and 10+ leaf trees
  • generateProof returns proof array (hex strings)
  • verifyProof validates membership correctly
  • EMPTY_TREE_ROOT = SHA-256(“”) constant
  • sortPairs: false to preserve insertion order (no auto-sort)
  • Determinism verified: same input → same root
  • Membership test: proof for leaf[i] verifies for i, fails for j≠i
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.8.1
  status: done
  progress: 100

thought_record:
  task_id: P0.8.1
  branch: feature/p0-8-1-merkle-tree
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented buildMerkleTree(recordHashes[]): SHA-256 MerkleTree from thought record hashes using merkletreejs, returns {root, tree}. generateProof(tree, leafHash) extracts membership proof. verifyProof(root, proof, leafHash) validates leaf membership. Empty tree root = SHA-256('') constant. sortPairs: false preserves insertion order (no auto-sort). Determinism verified: identical hashes  identical root. Tests: 10-leaf determinism, membership verification for each leaf, order preservation (same hashes different order  different root), single leaf, large tree (100 hashes)."
  blockers: []

Common gotchas

  • merkletreejs sorts leaves by default — Pass {sortPairs: false} in constructor options to disable auto-sorting. If you need insertion order preservation (critical for deterministic audits), this is mandatory.
  • Empty tree root is a sentinel — Do not return null or undefined for empty trees. Use SHA-256(“”) as the defined constant. This signals “no records yet” in the Merkle proof chain.
  • Proof format is implementation-specific — merkletreejs returns proof as an array of objects {position: 'left'|'right', data: Buffer}. Convert to hex strings for storage/transport. Ensure verifyProof accepts the same format.
  • Determinism is critical for audits — Run the same hashes twice and compare roots. If they differ, you have a non-determinism bug. merkletreejs is generally safe, but watch for: hash function changes, leaf padding, or sorting options.

P0.8.2 — Three-Zone Retention

Spec source: task-breakdown.md §P0.8.2 Extraction reference: docs/reference/extractions/eta-proof-store-extraction.md (retention section) Worktree: feature/p0-8-2-three-zone Branch command: git worktree add .worktrees/claude/p0-8-2-three-zone -b feature/p0-8-2-three-zone origin/main Estimated effort: M (Medium — 2-3 hours) Depends on: P0.8.1 (Merkle proofs for cold zone), P0.2.2 (Database for zones) Unblocks: long-term audit storage, P0.8.3 (Finalization reads zones)

Files to create

  • src/domains/proof/retention.ts — Zone management, compression, archiving
  • tests/domains/proof/retention.test.ts — Zone transitions, compression, decompression tests

Acceptance criteria

  • Hot zone: last 100 records, full content in DB
  • Warm zone: records 101–1000, content compressed (JSON → gzip → base64)
  • Cold zone: records 1001+, content hash only (full content deleted)
  • archiveRecord(id) moves record to next zone based on age/position
  • retrieveRecord(id) decompresses if Warm, returns hash stub if Cold
  • Test: hot → warm → cold transitions; verify content availability per zone

Pre-flight reading

  • CLAUDE.md — execution rules
  • docs/guides/implementation/task-breakdown.md §P0.8.2 — full spec
  • docs/reference/extractions/eta-proof-store-extraction.md (retention section)
  • src/domains/trail/repository.ts (thought records DB structure)
  • Node.js zlib module (gzip)

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.8.2 — Three-Zone Retention
Implement tiered record storage: Hot (full), Warm (compressed), Cold (hash-only).

FILES TO READ FIRST:
1. CLAUDE.md (execution rules)
2. docs/guides/implementation/task-breakdown.md §P0.8.2
3. docs/reference/extractions/eta-proof-store-extraction.md (retention section)
4. src/domains/trail/repository.ts (record structure)
5. Node.js zlib module documentation

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-8-2-three-zone -b feature/p0-8-2-three-zone origin/main
cd .worktrees/claude/p0-8-2-three-zone

FILES TO CREATE:
- src/domains/proof/retention.ts
  * Type RecordZone = 'hot' | 'warm' | 'cold'
  * Type ArchivedRecord = {id, type, task_id, agent_id, zone, timestamp, content_or_hash, proof_hash?}
  * Constants:
    - HOT_MAX = 100 (last 100 records in hot zone)
    - WARM_MAX = 1000 (records 101-1000 in warm zone)
    - COLD_THRESHOLD = 1001 (records beyond 1000 in cold zone)
  * Function determineZone(positionInSequence: number): RecordZone
    - position 0-99 → 'hot'
    - position 100-999 → 'warm'
    - position 1000+ → 'cold'
  * Function archiveRecord(record: ThoughtRecord): ArchivedRecord
    - Determine zone by position in global sequence
    - If hot: store full content
    - If warm: compress {type, task_id, agent_id, content} → JSON → gzip → base64, store as content_or_hash
    - If cold: delete content, store only {id, type, task_id, agent_id, timestamp, proof_hash: record.hash}
    - Update DB: set zone field, update content column
  * Function retrieveRecord(id: string): ArchivedRecord (or ThoughtRecord if hot)
    - Query DB by id, retrieve zone + content_or_hash
    - If hot: return as-is (content is plaintext)
    - If warm: base64 decode → gunzip → JSON parse → return reconstructed record
    - If cold: return stub {id, type, task_id, agent_id, timestamp, zone, proof_hash, content: null}
  * Function rotateZones()
    - Run periodically (on startup, on new record insertion)
    - Count total records
    - Move records from hot→warm and warm→cold as needed
    - Archive/compress/delete content as appropriate
    - Idempotent: running twice should be safe (check zone before re-archiving)
  * Error handling: log compression errors, continue (don't break on single record failure)

- tests/domains/proof/retention.test.ts
  * Test determineZone: position 50 → 'hot', 500 → 'warm', 2000 → 'cold'
  * Test hot zone: create 50 records, all in hot zone, retrieve → full content
  * Test warm zone: create 150 records, records 101-150 compress, retrieve → decompressed content matches original
  * Test cold zone: create 1100 records, records 1001-1100 hash-only, retrieve → content null, proof_hash set
  * Test zone transition: create 100 records (all hot), insert record 101
    - Run archiveRecord on record 100 → moves to warm (compress)
    - Record 101 added to hot
    - Run rotateZones() → ensure boundaries correct
  * Test compression roundtrip: serialize record → gzip → base64 → gunzip → deserialize → matches original
  * Test idempotence: archiveRecord(record) twice → no error, same result
  * Test retrieveRecord across zones:
    - Retrieve from hot → full content
    - Retrieve from warm → decompressed content
    - Retrieve from cold → stub with proof_hash, content null
  * Test performance: 100 record compression + decompression < 500ms total

ACCEPTANCE CRITERIA (headline):
✓ Hot zone: last 100 records, full content
✓ Warm zone: records 101-1000, gzip compressed (JSON→gzip→base64)
✓ Cold zone: records 1001+, hash only (content deleted)
✓ archiveRecord(id) moves to next zone based on position
✓ retrieveRecord(id) decompresses if Warm, returns stub if Cold
✓ Zone transitions: hot→warm→cold with content handling

SUCCESS CHECK:
cd .worktrees/claude/p0-8-2-three-zone && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.8.2", status="done", progress=100)
thought_record(task_id="P0.8.2", branch="feature/p0-8-2-three-zone",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented three-zone retention: Hot (last 100, full content), Warm (101-1000, gzip compressed JSON→base64), Cold (1001+, hash only). archiveRecord() compresses or deletes based on zone. retrieveRecord() decompresses Warm, returns stub for Cold. rotateZones() idempotent boundary enforcement. Tests: zone determination, compression roundtrip, zone transitions, 100-record archive < 500ms.")

FORBIDDENS:
✗ Do not forget to compress cold-zone-bound records (JSON→gzip→base64, reversible)
✗ Do not allow content retrieval from cold zone (return hash stub only, content=null)
✗ Do not run archiveRecord multiple times on same record without idempotence check
✗ Do not edit main checkout

NEXT:
P0.8.3 — Merkle Root Finalization Tool (builds Merkle tree from latest records, stores root)

Verification checklist (for reviewer agent)

  • determineZone correct for hot (0-99), warm (100-999), cold (1000+)
  • archiveRecord compresses warm records (JSON→gzip→base64)
  • archiveRecord deletes content for cold records, stores proof_hash
  • retrieveRecord decompresses warm, returns stub for cold
  • rotateZones idempotent (safe to run multiple times)
  • Compression roundtrip: original → gzip → base64 → gunzip → original (identical)
  • Hot zone content always available, warm/cold with stated limitations
  • Tests cover all zone transitions, compression/decompression, performance
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.8.2
  status: done
  progress: 100

thought_record:
  task_id: P0.8.2
  branch: feature/p0-8-2-three-zone
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented three-zone record retention: Hot zone (records 0-99, full content in DB), Warm zone (records 100-999, content compressed via JSON→gzip→base64), Cold zone (records 1000+, content deleted, proof_hash only). archiveRecord(id) determines zone by sequence position, compresses or deletes. retrieveRecord(id) decompresses Warm records, returns hash stub for Cold. rotateZones() idempotent boundary management. Tests verify zone boundaries, compression roundtrip (original→decompress=original), zone transitions, performance (100 records < 500ms)."
  blockers: []

Common gotchas

  • Compression boundary moves with each new record — The 100th record is in hot zone; the 101st triggers a boundary shift. rotateZones() must be idempotent: running it twice should not re-compress already-compressed records. Track zone in DB to avoid redundant work.
  • gzip + base64 roundtrip must be exact — Test with non-ASCII characters, special JSON escaping, etc. gunzip(base64_decode(compressed)) must equal original JSON. Use crypto-js or Node.js zlib consistently.
  • Cold zone content is DELETED — Once moved to cold, the full content is gone. Store proof_hash to preserve audit trail, but cannot reconstruct content from hash. This is intentional for long-term storage, but document it clearly.
  • Compression ratio and timing — Some records may not compress well. Monitor gzip performance; if a record type is mostly static, compression may hurt (base64 overhead). For now, assume all compress equally. Future optimization: track compression ratio per record type.

P0.8.3 — Merkle Root Finalization Tool

Spec source: task-breakdown.md §P0.8.3 Extraction reference: docs/reference/extractions/eta-proof-store-extraction.md (finalization section) Worktree: feature/p0-8-3-merkle-finalize Branch command: git worktree add .worktrees/claude/p0-8-3-merkle-finalize -b feature/p0-8-3-merkle-finalize origin/main Estimated effort: S (Small — 1-2 hours) Depends on: P0.8.1 (Merkle tree construction) Unblocks: proof-grade workflow (audit_session_start → work → audit_verify_chain → thought_record → merkle_finalize → merkle_root)

Files to create

  • src/tools/merkle.ts — MCP tools: merkle_finalize, merkle_root, audit_session_start
  • tests/tools/merkle.test.ts — Finalization logic, session ordering, root verification tests

Acceptance criteria

  • merkle_finalize MCP tool builds Merkle tree of last N unfinalized records, stores root
  • merkle_root MCP tool returns current root hash + record count + timestamp
  • audit_session_start MCP tool creates audit session record, returns session_id
  • Finalization must happen AFTER final thought record (enforced: errors if no thought_record in session)
  • Test: finalize 5-record session → root matches manual computation

Pre-flight reading

  • CLAUDE.md — execution rules, Merkle finalization ordering rule
  • docs/guides/implementation/task-breakdown.md §P0.8.3 — full spec
  • docs/reference/extractions/eta-proof-store-extraction.md (finalization section)
  • src/domains/proof/merkle.ts (Merkle tree from P0.8.1)
  • src/domains/trail/repository.ts (record hashes)

Ready-to-paste agent prompt

You are a Phase 0 builder agent for Colibri.

TASK: P0.8.3 — Merkle Root Finalization Tool
Create audit session records and finalize Merkle roots for proof-grade work.

FILES TO READ FIRST:
1. CLAUDE.md (execution rules, Merkle finalization ordering)
2. docs/guides/implementation/task-breakdown.md §P0.8.3
3. docs/reference/extractions/eta-proof-store-extraction.md (finalization section)
4. src/domains/proof/merkle.ts (Merkle tree construction)
5. src/domains/trail/repository.ts (thought records)

WORKTREE SETUP:
git fetch origin
git worktree add .worktrees/claude/p0-8-3-merkle-finalize -b feature/p0-8-3-merkle-finalize origin/main
cd .worktrees/claude/p0-8-3-merkle-finalize

DATABASE SCHEMA (P0.2.2 should have):
- audit_sessions table: {session_id, created_at, final_merkle_root, record_count}
- merkle_roots table: {id, session_id, root_hash, record_count, finalized_at}

FILES TO CREATE:
- src/tools/merkle.ts (MCP tools)
  * audit_session_start: no input parameters
    - Generate session_id (UUID v4)
    - Create audit_sessions record in DB
    - Set created_at timestamp
    - Return {session_id: string, created_at: timestamp}
    - This MUST be called FIRST in audit workflow
  
  * merkle_finalize: input {session_id, record_count?: number}
    - Query thought_records table for unfinalized records
    - If record_count provided: take last N records
    - If not provided: take all unfinalized records (up to last 100 for safety)
    - Verify session exists and has final thought_record (query thought_records.session_id)
    - Call buildMerkleTree(recordHashes[]) from merkle.ts
    - Store root in merkle_roots table: {session_id, root_hash, record_count, finalized_at}
    - Update audit_sessions.final_merkle_root = root_hash
    - Return {root: root_hash, record_count, finalized_at}
    - ERROR if no thought_record in session (enforce CLAUDE.md rule: finalize AFTER final thought)
  
  * merkle_root: input {session_id?: string}
    - If session_id: query merkle_roots for that session → return {root, record_count, finalized_at}
    - If no session_id: return CURRENT root (latest from merkle_roots) → {root, record_count, finalized_at, is_current: true}
    - Return structure: {root: hex string, record_count: number, finalized_at: timestamp, session_id?: string}

- tests/tools/merkle.test.ts
  * Test audit_session_start: returns session_id, creates DB record, timestamp valid
  * Test merkle_finalize: create 5 thought records, finalize → root matches manual buildMerkleTree(hashes)
  * Test finalize requires thought_record in session: create session, finalize WITHOUT final thought_record → error
  * Test merkle_root after finalize: returns root, record_count, finalized_at
  * Test merkle_root without session_id: returns current root (latest)
  * Test session isolation: finalize session A, then session B → different roots, correct counts
  * Test record_count parameter: create 10 records, finalize with record_count=5 → root matches last 5 only
  * Test error handling: finalize with invalid session_id → error, finalize twice → idempotent or error depending on design (clarify)

ACCEPTANCE CRITERIA (headline):
✓ audit_session_start creates session, returns session_id
✓ merkle_finalize builds Merkle tree, stores root + record_count
✓ merkle_root returns root hash + metadata
✓ Finalization errors if no thought_record in session (CLAUDE.md rule)
✓ 5-record session: finalized root matches manual computation

SUCCESS CHECK:
cd .worktrees/claude/p0-8-3-merkle-finalize && npm test && npm run lint

WRITEBACK (after success):
task_update(task_id="P0.8.3", status="done", progress=100)
thought_record(task_id="P0.8.3", branch="feature/p0-8-3-merkle-finalize",
  commit_sha=<your-sha>, tests_run=["npm test","npm run lint"],
  summary="Implemented audit_session_start MCP tool: creates session, returns session_id. merkle_finalize tool: verifies final thought_record exists, builds Merkle tree of last N records, stores root. merkle_root tool: returns current root + metadata. Enforces CLAUDE.md rule: finalize AFTER final thought (errors if missing). Tests: 5-record session finalization matches manual computation, session isolation, record_count parameter, error on missing thought_record.")

FORBIDDENS:
✗ Do not allow merkle_finalize without final thought_record in session (CLAUDE.md explicit)
✗ Do not finalize empty sessions (require at least 1 thought record)
✗ Do not allow merkle root to be mutated after finalization (immutable once set)
✗ Do not edit main checkout

NEXT:
P0.9.1 — MCP Bridge (external MCP server integration for federated orchestration)

Verification checklist (for reviewer agent)

  • audit_session_start creates DB record, returns session_id
  • merkle_finalize builds tree using buildMerkleTree from P0.8.1
  • merkle_finalize stores root in merkle_roots table
  • merkle_finalize errors if no thought_record in session
  • merkle_root returns root_hash + record_count + finalized_at
  • 5-record session finalization: manual buildMerkleTree(hashes) matches stored root
  • Session isolation: multiple sessions have independent roots
  • record_count parameter: finalize with subset of records works correctly
  • npm test and npm run lint pass

Writeback template

task_update:
  task_id: P0.8.3
  status: done
  progress: 100

thought_record:
  task_id: P0.8.3
  branch: feature/p0-8-3-merkle-finalize
  commit_sha: <sha>
  tests_run: ["npm test", "npm run lint"]
  summary: "Implemented three MCP tools: (1) audit_session_start: generates session_id, creates audit_sessions DB record. (2) merkle_finalize: queries unfinalized thought_records, builds Merkle tree using merkle.ts buildMerkleTree(), stores root in merkle_roots table; enforces CLAUDE.md rule (errors if no final thought_record in session). (3) merkle_root: returns current or session-specific root with record_count + finalized_at. Tests: 5-record session finalization matches manual computation, session isolation (different sessions  different roots), error on missing thought_record, record_count parameter handling."
  blockers: []

Common gotchas

  • merkle_finalize must be called LAST — If Merkle root is finalized before the final thought record, that record is excluded from the proof. The CLAUDE.md rule is explicit: “Do not finalize the Merkle tree before the final thought record is written.” Enforce this with a DB check (query thought_records for this session, verify at least one exists).
  • Session ordering matters — Ensure records are finalized in chronological order by timestamp. If records are added out-of-order, Merkle root will be wrong. Use ORDER BY timestamp ASC when querying.
  • Idempotence question — If merkle_finalize is called twice on the same session, should it re-finalize or error? Design decision: probably error (“already finalized”). Clarify in tests and implementation.
  • Root immutability — Once finalized, the root should never change. Store it in a separate immutable table (merkle_roots), not in a mutable audit_sessions record. This prevents accidental overwrites.

Next group

P0.9 — ν Integrations

Back to task-prompts index


Back to top

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

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