Convert Manifest to frozen dataclasses #4

Merged
didericis merged 4 commits from convert-manifest-to-dataclass into main 2026-05-10 21:42:35 -04:00
Owner

Summary

  • Replace Manifest = dict[str, Any] with frozen dataclasses: SshEntry, BottleEgress, Bottle, Agent, Manifest.
  • Drop the 14 manifest_* free functions. Call sites import Manifest and use attribute access (m.agents[name].skills, m.bottle_for(name).runtime, etc.).
  • Validation runs once at construction (Manifest.from_json_obj / Manifest.resolve); getters trust the shape.

Behavior changes

  • agent.bottle is required and must reference a defined bottle — enforced at load instead of lazily per-launch.
  • pipelock_bottle_allowlist's per-element string check is gone (covered by the Manifest validator).
  • ssh.py takes SshEntry instances; shape checks moved upstream, only the IdentityFile filesystem-existence check remains in ssh_validate_entries.
## Summary - Replace `Manifest = dict[str, Any]` with frozen dataclasses: `SshEntry`, `BottleEgress`, `Bottle`, `Agent`, `Manifest`. - Drop the 14 `manifest_*` free functions. Call sites import `Manifest` and use attribute access (`m.agents[name].skills`, `m.bottle_for(name).runtime`, etc.). - Validation runs once at construction (`Manifest.from_json_obj` / `Manifest.resolve`); getters trust the shape. ## Behavior changes - `agent.bottle` is required and must reference a defined bottle — enforced at load instead of lazily per-launch. - `pipelock_bottle_allowlist`'s per-element string check is gone (covered by the Manifest validator). - `ssh.py` takes `SshEntry` instances; shape checks moved upstream, only the IdentityFile filesystem-existence check remains in `ssh_validate_entries`.
didericis added 1 commit 2026-05-10 21:13:08 -04:00
refactor(manifest): add TypedDict schema and eager validation
test / run tests/run_tests.py (pull_request) Successful in 20s
36cb0c53bf
Move schema checks out of per-access getters into a single
manifest_validate pass invoked by manifest_resolve. Getters can now
assume bottles/agents are well-typed dicts and every agent has a
defined bottle, so the .get(...) or {} chains collapse. Behavior
change: a bad runtime / shape error anywhere in the manifest now
fails at load instead of on the N-th read.

Intermediate step toward replacing TypedDict with a dataclass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis added 1 commit 2026-05-10 21:20:20 -04:00
refactor(manifest): convert TypedDict to frozen dataclasses
test / run tests/run_tests.py (pull_request) Successful in 14s
1f36d53f7b
Replace the TypedDict + 14 manifest_* free functions with frozen
dataclasses (SshEntry, BottleEgress, Bottle, Agent, Manifest) carrying
their own validators and constructors. Call sites import Manifest and
chain attribute access; the manifest_* helpers and manifest_validate
are gone.

Behavior changes worth flagging:
- Agent.bottle is now required (was optional with a "(none)" fallback).
  Manifest.from_json_obj dies if any agent lacks a 'bottle' field or
  references an undefined bottle, where previously start.py raised the
  error lazily for the specific agent being launched.
- ssh.py now takes SshEntry instances; Host/IdentityFile shape checks
  moved upstream into Manifest construction, leaving only the IdentityFile
  filesystem-existence check in ssh_validate_entries.
- pipelock_bottle_allowlist's per-element string check is dropped — the
  Manifest validator enforces it at load.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis changed title from refactor(manifest): add TypedDict schema and eager validation to refactor(manifest): convert to frozen dataclasses 2026-05-10 21:21:51 -04:00
didericis added 1 commit 2026-05-10 21:34:07 -04:00
fix(types): make manifest.py clean under pyright strict
test / run tests/run_tests.py (pull_request) Successful in 14s
e9a3de49af
- log.die() typed NoReturn so pyright knows it terminates control flow
  (was returning the unreachable Die instance type).
- manifest.py: raw inputs typed object (not Any) and narrowed via a new
  _as_json_object helper that validates str keys and returns
  dict[str, object]. Eliminates the Unknown cascade through .get()
  calls under strict.
- _from_dict classmethods renamed to from_dict so cross-class
  construction (Bottle.from_dict from Manifest.from_json_obj, etc.)
  doesn't trip reportPrivateUsage.
- _SUPPORTED_RUNTIMES typed tuple[Runtime, ...] so the membership
  check narrows runtime_raw to Literal["runc", "runsc"] and the
  # type: ignore[assignment] is no longer needed.
- Bottle.env uses a typed _empty_str_dict factory; bare dict resolves
  to dict[Unknown, Unknown] under strict.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis added 1 commit 2026-05-10 21:36:43 -04:00
refactor(manifest): drop _json_type, use type(x).__name__ in error messages
test / run tests/run_tests.py (pull_request) Successful in 14s
9343f6f21d
The jq-style mapping (bool→"boolean", list→"array", None→"null", etc.)
existed only to match the original bash error wording. Not worth the
extra function; Python's native type names are clear enough.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis merged commit a39c7b1b7b into main 2026-05-10 21:42:35 -04:00
didericis changed title from refactor(manifest): convert to frozen dataclasses to Convert Manifest to frozen dataclasses 2026-05-10 21:42:55 -04:00
Sign in to join this conversation.