Help us improve
Share bugs, ideas, or general feedback.
From midnight-verify
Cross-domain witness verification pipeline. Compiles the Compact contract, type-checks the TypeScript witness against the compiled contract's generated Witnesses type, runs structural checklist analysis (name matching, return tuple shape, WitnessContext usage, private state immutability, side effects), executes the circuit with the witness via JS runtime, and recommends devnet E2E to the orchestrator if needed. Loaded by @"midnight-verify:witness-verifier (agent)".
npx claudepluginhub devrelaicom/midnight-expert --plugin midnight-verifyHow this skill is triggered — by the user, by Claude, or both
Slash command
/midnight-verify:verify-by-witnessThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are verifying that a TypeScript witness implementation correctly matches and works with a Compact contract. Follow these phases in order.
Witness claim classification and method routing. Determines what kind of witness claim is being verified and dispatches to the witness-verifier. Handles claims about witness type correctness, name matching, return tuple shape, type mappings, behavioral correctness, private state patterns, and two-file verification. Loaded by the /midnight-verify:verify command alongside the hub skill.
This skill should be used when the user asks about implementing Compact witness functions in TypeScript, the WitnessContext pattern, private state management, Compact-to-TypeScript type mappings (Field to bigint, Bytes to Uint8Array, Uint to bigint), compiler-generated .d.ts files (Witnesses interface, Circuits type, Contract class), the Compact JavaScript runtime, how contract.circuits works, pure circuits in TypeScript, reading ledger state from TypeScript, the witness return tuple pattern [PrivateState, ReturnValue], or how to connect a Compact contract to its TypeScript implementation.
Provides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Share bugs, ideas, or general feedback.
You are verifying that a TypeScript witness implementation correctly matches and works with a Compact contract. Follow these phases in order.
Witness verification is cross-domain. You need both the .compact contract file and the .ts witness implementation file. If you only have one, ask for the other. Do not attempt verification with only one file.
The workspace lives at .midnight-expert/verify/witness-workspace/ relative to the project root.
First time (workspace does not exist):
mkdir -p .midnight-expert/verify/witness-workspace
cd .midnight-expert/verify/witness-workspace
npm init -y
npm install @midnight-ntwrk/compact-runtime typescript
Subsequent times:
cd .midnight-expert/verify/witness-workspace
npm ls typescript
If errors, npm install to repair.
Create the job directory:
JOB_ID=$(uuidgen | tr '[:upper:]' '[:lower:]')
mkdir -p .midnight-expert/verify/witness-workspace/jobs/$JOB_ID
Locate the files:
.compact file stays where it is (it may have imports and dependencies on other .compact files in its directory). Copy the .ts witness file to the job directory.AskUserQuestion to ask which files to verify.Compile the contract where it lives, directing build output to the job directory:
compact compile -- --skip-zk <source-path> .midnight-expert/verify/witness-workspace/jobs/$JOB_ID/build/
If the orchestrator indicated this claim also needs PLONK verification (Witness + ZKIR), compile without --skip-zk instead:
compact compile -- <source-path> .midnight-expert/verify/witness-workspace/jobs/$JOB_ID/build/
This produces build/contract/index.js and build/contract/index.d.ts which export the generated Witnesses type.
Create a type-check harness in the job directory that imports the witness file and validates it against the generated types:
// jobs/$JOB_ID/witness-check.ts
import type { Witnesses } from './build/contract/index.js';
import witnesses from '<absolute-path-to-witness-file>';
// This assignment checks that the witness object satisfies the generated Witnesses type
const _typeCheck: Witnesses<any> = witnesses;
Create a tsconfig.json for the job:
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"strict": true,
"noEmit": true,
"skipLibCheck": false,
"esModuleInterop": true
},
"include": ["witness-check.ts"]
}
Run the type check:
cd .midnight-expert/verify/witness-workspace/jobs/$JOB_ID
npx tsc --noEmit --project tsconfig.json 2>&1
If tsc exits 0: Types match — the witness satisfies the generated Witnesses type.
If tsc exits non-zero: Type mismatches found — the compiler errors are evidence of what doesn't match.
Note: The exact shape of the type-check harness depends on how the compiled contract exports the Witnesses type. Read build/contract/index.d.ts to understand the export shape and adapt the harness accordingly.
Read both the compiled build/contract/index.d.ts (for witness declarations) and the .ts witness file (for implementations). Perform these automated checks:
Parse the witness function names from the compiled type declarations. Check that every declared witness name exists in the TypeScript implementation with exact casing.
PASS: All declared witness names have matching implementations. FAIL: List missing or misspelled names.
Check that each witness function returns [PrivateState, ReturnValue] — a two-element tuple where the first element is the private state type. Look for return type annotations or actual return statements.
PASS: All witnesses return a tuple with private state as the first element. FAIL: List witnesses that return just the value without the private state wrapper.
Check that each witness function's first parameter is the WitnessContext type (containing ledger, privateState, contractAddress).
PASS: All witnesses accept WitnessContext as their first parameter.
FAIL: List witnesses with wrong or missing first parameter.
Check that witness functions create new state objects rather than mutating the existing privateState in place. Look for:
{ ...context.privateState, key: newValue }), Object.assign({}, ...), creating new objectscontext.privateState.key = value), array .push(), .splice(), etc.PASS: No direct mutation patterns found. FAIL: List locations where private state appears to be mutated directly.
Check for non-deterministic or side-effecting code that should not appear in witnesses:
console.log, console.warn, console.errorfetch, XMLHttpRequestfs.readFile, fs.writeFile, any fs usageMath.random, Date.now, crypto.getRandomValuessetTimeout, setIntervalPASS: No side effects found. FAIL: List locations of non-deterministic or side-effecting code.
Note: These checks are heuristic — they read source text and look for patterns. They are not as authoritative as compilation or execution. Report findings alongside the mechanical results.
Import the compiled contract and execute the circuit(s) with the witness:
import { Contract } from '<job-dir>/build/contract/index.js';
import witnesses from '<absolute-path-to-witness-file>';
// Create contract instance with the witness implementations
const contract = new Contract(witnesses);
// Create initial state
const initialZswapLocalState = { coinPublicKey: new Uint8Array(32) };
const state = contract.initialState({
initialZswapLocalState,
initialPrivateState: { /* appropriate initial private state */ }
});
// Create circuit context
const context = compactRuntime.createCircuitContext(
compactRuntime.dummyContractAddress(),
initialZswapLocalState.coinPublicKey,
state.currentContractState.data,
state.currentPrivateState
);
// Execute each circuit that uses witnesses
const result = contract.circuits.<circuitName>(context);
Check build/compiler/contract-info.json — circuits with a non-empty witnesses array use witnesses.
Success: The circuit executed without error and produced valid proof data. The contract + witness combination works.
Failure: Capture the error. Common witness execution errors:
"expected tuple, got <type>" — witness returns wrong shape"missing witness: <name>" — witness function not provided"Contract constructor: expected 1 argument" — witnesses object not passedYou cannot dispatch other agents. Phase 5 is a recommendation to the orchestrator (the /midnight-verify:verify command), not something you execute.
In your report, include a recommendation:
The orchestrator will decide whether to dispatch @"midnight-verify:sdk-tester (agent)" based on your recommendation and devnet availability.
### Witness Verification Report
**Contract:** [path to .compact]
**Witness:** [path to .ts]
**Type Check:** PASS / FAIL
[tsc output if failed]
**Structural Checklist:**
- Name matching: PASS / FAIL — [details]
- Return tuple shape: PASS / FAIL — [details]
- WitnessContext pattern: PASS / FAIL — [details]
- Private state immutability: PASS / FAIL — [details]
- No side effects: PASS / FAIL — [details]
**Execution:** PASS / FAIL
[execution output or error]
**Devnet E2E Recommendation:** Recommended / Not required
**Interpretation:** [Confirmed / Refuted / Inconclusive] — [summary]
If the orchestrator indicated PLONK verification is needed, include the build output path in the report so the orchestrator can pass it to @"midnight-verify:zkir-checker (agent)":
**Build output:** .midnight-expert/verify/witness-workspace/jobs/$JOB_ID/build/
rm -rf .midnight-expert/verify/witness-workspace/jobs/$JOB_ID
Do NOT remove the base workspace — it's shared across jobs. If the orchestrator needs the build output for @"midnight-verify:zkir-checker (agent)", do NOT clean up until the orchestrator confirms the zkir-checker is done.