Skip to content

refactor(v2): bind op definitions to models and require zod payload schemas#1593

Merged
kermanx merged 4 commits into
mainfrom
v2/persisted-op-map
Jul 13, 2026
Merged

refactor(v2): bind op definitions to models and require zod payload schemas#1593
kermanx merged 4 commits into
mainfrom
v2/persisted-op-map

Conversation

@kermanx

@kermanx kermanx commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Related Issue

None — the problem is explained below.

Problem

In the experimental v2 engine (packages/agent-core-v2, behind a flag), every wire op's payload was declared twice: once as a hand-written TypeScript interface and again in the PersistedOpMap/TransientOpMap registries, so the two could drift. Op definitions were also detached from their models (defineOp(SomeModel, ...) as a free function), and roughly a third of all ops were missing from the registries entirely, leaving replay classification implicit.

What changed

  • Ops are declared on their model: SomeModel.defineOp('type', { ... }) (the standalone defineOp remains as the internal primitive). Payload and return types of apply / toEvent are inferred, so per-op payload interfaces were deleted; the few consumers that named them now use PayloadOf<typeof op>.
  • A required schema (zod, declared before apply) is now the payload type's single source of truth and is stored on the op descriptor for future wire-boundary validation; dispatch / replay do not consult it. Complex domain types (ContextMessage, LoopRecordedEvent, etc.) use z.custom<T>() rather than being restated.
  • The registries were inverted: entries map op types to typeof the op, and payload lookup (OpPayload / ModelReducers) recovers the payload from the op's own type. Registry classification is key-level only, which keeps the whole graph free of type cycles.
  • Every op is now registered: 32 persisted ops in PersistedOpMap, 12 transient ops (all with persist: false) in TransientOpMap — the map membership now enforces the persistence policy at the type level.

Verified: tsc --noEmit clean, lint:domain OK, oxlint 0 errors, full suite 3140/3140 passing. The type-level guarantees (payload inference, persistence constraints, literal-type guards, registry recovery) are covered by the compile-time assertions in test/lint/op-uniqueness.test.ts.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update. (Internal experimental engine; no user-facing docs affected.)

kermanx added 2 commits July 13, 2026 13:26
Colocate persisted/transient op payload shapes with their op definitions
via interface-merge into #/wire/types (PersistedOpMap / TransientOpMap),
replacing the per-service WireRecordMap augmentations. defineOp apply
handlers now infer payloads from the map, and taskService restores
delivered-notification marks by iterating getRecords() on restore instead
of a wireRecord restore hook.
…chemas

- ModelDef.defineOp(type, opts) replaces defineOp(model, type, opts);
  the standalone function stays as the internal primitive
- schema (zod, before apply) is required and is the payload type's
  single source of truth; apply/toEvent payload and return types are
  inferred, so per-op payload interfaces are deleted and consumers use
  PayloadOf<typeof op>
- Op registries (PersistedOpMap/TransientOpMap) now map op types to
  typeof the op; classification stays key-level to avoid type cycles
- Every op is registered: 12 transient ops carry persist: false,
  32 persisted ops ride the wire log
@changeset-bot

changeset-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 4821912

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@moonshot-ai/kimi-code Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 13, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@moonshot-ai/kimi-code@4821912
npx https://pkg.pr.new/@moonshot-ai/kimi-code@4821912

commit: 4821912

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 29a298a09f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +49 to +53
/**
* Zod schema for the payload — the payload type's single source of truth
* (`P` is inferred from it). Stored on the descriptor so wire boundaries
* (replay of `wire.jsonl`, record export) can validate payloads against the
* Op's declared shape. Not consulted by `dispatch` / `replay` themselves.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Move new inline comments to the file header

packages/agent-core-v2/AGENTS.md requires comments to live only in the top-of-file /** */ block and says never to place them beside functions, methods, or statements. This new property-level JSDoc adds inline API documentation inside OpDescriptor; please fold this detail into the module header or remove it so new v2 code follows the documented comment convention.

Useful? React with 👍 / 👎.

@kermanx kermanx merged commit 2185237 into main Jul 13, 2026
10 checks passed
@kermanx kermanx deleted the v2/persisted-op-map branch July 13, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant