Commit Graph
8 Commits
Author SHA1 Message Date
Chinesezjc 5dad49f4db docs(code-runtime-python): name the roster aliases by subset direction, align metering prose
Rename UnionCoversRoster/RosterCoversUnion to UnionSubsetOfRoster/RosterSubsetOfUnion so
the names read in the same direction as their extends clauses, share the python3 -I -B
flags between the two mirror probes, and align the README and Agent Note prose with the
checkDoneValue JSDoc: the escaped-size scan is the metering itself, not deferred work.
Regenerate docs/module-graph.md, which listed code-runtime-python twice.
2026-08-07 15:37:19 +08:00
Chinesezjc 4674d8fa92 fix(code-runtime-python): verify union<->roster both ways, stop pycache writes, refresh metering prose
Address the latest review round:

- WireFrameShapesCoverUnions checked only union ⊆ roster, so removing a frame
  from a message union (e.g. dropping ReplyErr from ReplyMessage) left the check
  true while the public TS union diverged from the wire. Replace it with a
  bidirectional equivalence between MessageFrames and the roster's message-frame
  value types (nested Namespace/ErrorClass/DoneErrorField excluded): both a frame
  added to a union without a roster entry and a frame removed from a union now
  fail typecheck (both verified).
- The mirror e2e's python3 probes imported protocol.py without -B, writing
  py/__pycache__/*.pyc into the (un-ignored) source tree. Add -B to both.
- Refresh the metering prose (checkDoneValue JSDoc + README both sides + Agent
  Note both sides): the incremental-work list no longer says "per-key
  JSON.stringify" now that jsonStringBytesUpTo scans without stringifying;
  re-record the README and Agent Note i18n pairings.
2026-08-07 13:27:54 +08:00
Chinesezjc 4de8c914c9 refactor(code-runtime-python): export PROTOCOL_FD and tidy the mirror binding
Address the remaining review findings on the wire-mirror layer:

- Export PROTOCOL_FD from protocol.ts as the TS-side source of truth the host
  wires, and assert the Python constant against it in the mirror e2e instead of
  a bare literal 3, so an fd drift on either side is caught.
- Correct the FrameFieldRoles JSDoc to point at the actual assertion site
  (WIRE_FRAME_FIELD_ROLES's satisfies clause, not WIRE_FRAME_FIELDS).
- Drop the redundant explicit type annotation on WIRE_FRAME_FIELDS (the trailing
  `as` cast already types it; Object.fromEntries returns an index signature).
- Refresh the mirror-test comment to describe the roles-map binding (a TS-side
  add/remove/rename/optionality-flip fails typecheck; a Python-side change fails
  the comparison).
2026-08-07 13:27:54 +08:00
Chinesezjc 4d49406bd5 fix(code-runtime-python): bind the wire-field mirror to TS required/optional keys
The previous mirror binding (FrameFields<keyof T>) only checked membership: it
could not see a TS-side optionality flip (truncated? -> truncated leaves keyof
unchanged) or a field added on one side, so the "depends on the TS
declaration" claim was overstated.

- Promote the inline frame shapes (Namespace, ErrorClass, DoneErrorField,
  RunMessage, and the two Reply variants) to named interfaces so every frame
  binds uniformly.
- Derive FrameFields from RequiredKeys<T>/OptionalKeys<T>, so `required` and
  `optional` each accept only that side's keys. An optionality flip or a rename
  now fails typecheck (verified: flipping LogMessage.truncated to required
  errors at the constant).
- Enumerate EVERY public TypedDict in py/protocol.py in the mirror e2e (not a
  name list taken from the TS side) and assert both the frame roster and each
  frame's required/optional sets by exact equality, so a frame or field present
  on only one side of the wire fails the test.
2026-08-07 13:27:54 +08:00
Chinesezjc be839a8e53 fix(code-runtime-python): count non-lossless bytes and bind the mirror gate to TS types
Two gaps from the previous round's fixes:

- checkDoneValue flagged a non-lossless number but skipped counting its encoded
  bytes, so a value over budget ONLY through that number classified as
  non-lossless instead of over-budget (e.g. [Infinity] at cap 3, whose encoding
  is 10 bytes). Count the scalar's bytes even when flagging, so the budget check
  wins as the JSDoc promises. Add cap-3 regression cases.

- The mirror e2e compared the Python TypedDict keys against a hand-written
  constant, so a field change on the TS side alone would not fail it, and the
  reply frames were not probed at all. Introduce WIRE_FRAME_FIELDS in
  protocol.ts, bound to each frame interface's key set via `satisfies` (a
  renamed/removed field breaks typecheck — verified), and drive the mirror test
  from it, now covering ReplyOk/ReplyErr too. The test therefore fails on
  one-sided drift from either language.
2026-08-07 13:27:54 +08:00
Chinesezjc 8a60b5f005 feat(code-runtime-python): make the TypedDict wire mirror an executable gate
Address the two standing review suggestions in this layer rather than deferring
them to PR #4:

- Extend tests/protocol-mirror.e2e.ts to read each py/protocol.py TypedDict's
  required/optional key set and assert it against the wire field names
  src/protocol.ts declares (global included, via functional TypedDict). The
  round-12 class of drift — a renamed/dropped field, or one side making a field
  optional the other requires — now fails a test instead of relying on review.
  Field types remain review-guarded (no mechanical TS/Python equivalent).
- Drop the forward references to PR #4's internal mechanisms from this layer's
  prose: the "256 MiB frame ceiling" figure and the "(index.ts)" fd-3 pinning
  citation become an abstract "host-side inbound frame-size cap" so the JSDoc,
  spec, README, and Agent Note describe only what this layer owns.

Update both README sides and the Agent Note (both languages) to state the
mirror is now executable, and re-record their i18n pairings.
2026-08-07 13:27:54 +08:00
Chinesezjc f0d669883f fix(code-runtime-python): close coverage gap and tighten the wire mirror
- Cover the log-frame `truncated` rebuild branch: assert a literal-true flag
  rides along and any other value (1, string, false) is dropped, closing the
  protocol.ts branch the coverage gate flagged.
- Correct encodeJsonPlain's JSDoc: it matches compact JSON.stringify EXCEPT on
  a beyond-safe-range integral double, where it emits the exact BigInt digits
  (`...846976`) rather than the rounded `...847000` — the divergence the
  "emits exact digits" test pins.
- Declare py/protocol.py's `global`-bearing frames (Namespace, CallMessage)
  with functional TypedDict syntax so they carry the real wire key instead of
  a `global_` attribute the wire never sends, and split optional-field messages
  (Namespace/LogMessage/DoneMessage) into a required base plus a total=False
  subclass so `type` and other required fields cannot be dropped. Widen
  HostToChild to include the boot and run frames the host sends before replies.
- Reword the mirror e2e's py/ directory assertion to describe the source-tree
  layout it actually checks.
2026-08-07 13:27:54 +08:00
Chinesezjc e0f22aeaad feat(code-runtime-python): add the fd-3 frame protocol
Introduce @deepseek-ai/dsh-code-runtime-python with the versionless
JSON-lines protocol between the Node host and the CPython subprocess:
the host-side hostile-frame codec (validateChildFrame, encodeJsonPlain,
checkDoneValue, hasUnsafeIntegerToken, hasNonLosslessNumber,
logTruncationMarker) and the Python-side wire-vocabulary mirror
(py/protocol.py).

This is the protocol layer of the code-runtime-python stack, split from
#436 and based on the multi-language seam extension. The PythonCodeRuntime
implementation and its Python JSON codec land in the backend-core PR on
top of this branch.

Ship the minimal buildable package skeleton (package.json, tsconfig,
tsdown, barrel index, invariant companion, bilingual README) because the
workspace-constraint, coverage, and invariant-topology gates require the
package to exist and build the moment its directory does; the backend-core
PR extends those files rather than creating them.

Align py/protocol.py with src/protocol.ts (the round-12 review of #436
found LogMessage.truncated, DoneMessage.error.kind, and Namespace.errorClass
stale) and guard the two runtime-executed surfaces (PROTOCOL_FD and the log
truncation marker) with a real-python3 cross-language mirror e2e test.
2026-08-07 13:27:54 +08:00