- packages/domain: zod schemas for the 07-scenario object set with a schemaRegistry driving schema export, fixture generation, and Python module naming; decimal-string/sha256/curve primitives enforce the docs/11 §3.3 representation rules at the type layer - contracts/: 12 JSON Schemas (draft 2020-12), 12 golden fixtures, 3 invalid fixtures crafted to fail on both sides (float money, bad digest, missing concurrency version) - skills-py: generated pydantic models (committed, never hand-edited), regeneration script, mirror pytest using JSON-mode validation — dual-side contract tests agree on all 15 fixtures - packages/services: content-addressed immutable snapshot store (memory + fs, rejects floats), LedgerService v1 with optimistic concurrency and the P7 constraint cascade (monthly position bounds day-ahead bids; no-anchor bids rejected), quality-gate stub - cascade band shape is an OPEN-QUESTION A5 placeholder (pro-rata daily share ±band) — see ledger.ts checkCascade All green: tsc typecheck, 41 TS tests, 15 Python tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019u5SLNweVio6ozJX7yfxQr 🔮 View transcript: https://logs.lojong.info/s/e8u90k3t33w590r7b5y7yzqh
76 lines
2.1 KiB
TypeScript
76 lines
2.1 KiB
TypeScript
import { z } from 'zod'
|
|
import { Curve96, DecimalString, Id, IsoUtc, MarketDate, SnapshotRef, Timescale } from './common.js'
|
|
|
|
export const ProposalType = z.enum(['BID', 'INVITATION', 'CONTROL_PLAN', 'DISPATCH_PLAN'])
|
|
export type ProposalType = z.infer<typeof ProposalType>
|
|
|
|
export const TriggerKind = z.enum(['MANUAL', 'EVENT', 'SCHEDULED'])
|
|
|
|
/** Full lifecycle per docs/03 §2 (incl. FRESH_CHECK/STALE/AUTHORIZED from the staleness model). */
|
|
export const ProposalStatus = z.enum([
|
|
'DRAFT',
|
|
'RULE_CHECK',
|
|
'SIMULATION',
|
|
'ENVELOPE_CHECK',
|
|
'PENDING_HUMAN',
|
|
'AUTO_APPROVED',
|
|
'APPROVED',
|
|
'FRESH_CHECK',
|
|
'STALE',
|
|
'AUTHORIZED',
|
|
'RELEASED',
|
|
'EXECUTING',
|
|
'COMPLETED',
|
|
'ROLLED_BACK',
|
|
'REJECTED',
|
|
])
|
|
|
|
export const ToolCallRef = z.object({
|
|
tool_call_id: Id,
|
|
tool: Id,
|
|
version: z.string().min(1),
|
|
inputs_ref: SnapshotRef,
|
|
outputs_ref: SnapshotRef,
|
|
})
|
|
|
|
/**
|
|
* Provenance attached to every proposal (docs/03 §1). Rule check enforces that
|
|
* every numeric payload field traces to one of these tool calls (invariant/P2).
|
|
*/
|
|
export const Lineage = z.object({
|
|
tool_calls: z.array(ToolCallRef),
|
|
data_refs: z.array(SnapshotRef),
|
|
ledger_version: z.int().nonnegative(),
|
|
policy_pack_version: z.string().min(1),
|
|
})
|
|
|
|
/** Day-ahead bid: per-interval price/quantity pairs. */
|
|
export const BidPayload = z.object({
|
|
kind: z.literal('BID'),
|
|
market_date: MarketDate,
|
|
prices_yuan_per_mwh: Curve96,
|
|
quantities_mwh: Curve96,
|
|
expected_revenue_yuan: DecimalString,
|
|
})
|
|
|
|
const ProposalBase = z.object({
|
|
id: Id,
|
|
timescale: Timescale,
|
|
originator: z.object({ agent: Id, trigger: TriggerKind, task_id: Id }),
|
|
lineage: Lineage,
|
|
envelope_ref: Id.nullable(),
|
|
/** sha256 over canonical payload + lineage. Approvals and permits bind to this (I3/I4). */
|
|
digest: SnapshotRef,
|
|
status: ProposalStatus,
|
|
created_at: IsoUtc,
|
|
})
|
|
|
|
/**
|
|
* M1 carries the BID variant only; INVITATION / CONTROL_PLAN / DISPATCH_PLAN
|
|
* payloads arrive with their milestones (ROADMAP M4 / phase 2).
|
|
*/
|
|
export const Proposal = z.discriminatedUnion('type', [
|
|
ProposalBase.extend({ type: z.literal('BID'), payload: BidPayload }),
|
|
])
|
|
export type Proposal = z.infer<typeof Proposal>
|