PH1.4 -- Engine contracts: PlanningSnapshot, PlanResult, Finding, projector/scorer I/O, task payloads, code lists, fixtures #55

Merged
bart merged 3 commits from feature/engine-contracts into main 2026-09-11 11:14:23 +00:00
Collaborator

Summary

  • Adds polaris.contracts: frozen Pydantic models crossing module boundaries per the import-rules table in docs/12-implementation-plan.md -- PlanningSnapshot (engine/validator input), PlanResult (engine output), Finding (validator output: code + structured params, message explicitly dev-only), projector I/O (ProjectionInput/ProjectionOutput) and scorer I/O (ScoreInput/ScoreOutput) including the AdjustedConfidenceFn hook signature agreed here for T-B/T-G, and the TaskPayload discriminated union (project_windows, solve, solve_event, validate, score).
  • Adds closed code lists (polaris.contracts.codes): FindingCode + severity table, NotificationKind, and error_codes()/build_x_polaris_codes() for the x-polaris-codes OpenAPI extension.
  • Adds three fixture snapshots in backend/tests/fixtures/snapshots/: tiny.json (hand-computable optimum), medium.json (realistic mix), every_lock_type.json (all eleven plan_lock types).
  • Adds polaris replay-run --file <snapshot> (cli/replay_run.py): loads and summarises a snapshot, runs it through clearly-marked stub solve()/validate() (T-C/T-D don't exist yet). Discovered automatically by the existing CLI package walk.
  • Adds backend/tests/unit/test_import_rules.py: a data-driven AST walker enforcing the full backend import-rules table, at the path the docs already reserve for it. Only checks a package once it exists on disk, so later tracks get coverage for free.

Acceptance criteria (issue #12)

  • A hand-built PlanningSnapshot serialises to JSON and back with equality
  • Every validator/error/notification code appears in x-polaris-codes in the generated spec -- the builder function exists and is tested; wiring it into backend/openapi.json needs a 2-line change to polaris/cli/openapi.py, which this ticket doesn't own. Filed as seam request #54.
  • Fixtures exist: tiny (known optimum), medium, every-lock-type; each loads through the contracts
  • polaris replay-run --file <snapshot> runs end to end with a no-op engine
  • Import-rule test exists and passes: contracts import only core

Notes for downstream tracks

  • FindingCode severity is a table (FINDING_SEVERITY), not derived from the E_/W_/I_ prefix -- W_IDLE_CREW/W_WAITING are documented info severity in docs/04-lifecycles.md despite the W_ prefix. Build Findings via Finding.for_code(...).
  • NotificationKind is an initial v1 list inferred from the scenarios (no doc enumerates one) -- flagged on #12, extending it later is a ! commit since contracts/ is sealed after PH1.
  • TaskPayload splits event-triggered replans into their own solve_event kind (matching PH5.2's worker/handlers/solve_event.py) rather than folding everything into solve's mode field -- flagging in case T-E/PH5.2 want a different split before it's load-bearing.

Test plan

  • ruff check / ruff format --check clean
  • mypy --strict src clean (39 source files)
  • pytest green: 70 passed (62 unit + 8 integration, including all pre-existing PH0.2 tests)

Closes

Closes #12

🤖 Generated with Claude Code

https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM

## Summary - Adds `polaris.contracts`: frozen Pydantic models crossing module boundaries per the import-rules table in `docs/12-implementation-plan.md` -- `PlanningSnapshot` (engine/validator input), `PlanResult` (engine output), `Finding` (validator output: `code` + structured `params`, `message` explicitly dev-only), projector I/O (`ProjectionInput`/`ProjectionOutput`) and scorer I/O (`ScoreInput`/`ScoreOutput`) including the `AdjustedConfidenceFn` hook signature agreed here for T-B/T-G, and the `TaskPayload` discriminated union (`project_windows`, `solve`, `solve_event`, `validate`, `score`). - Adds closed code lists (`polaris.contracts.codes`): `FindingCode` + severity table, `NotificationKind`, and `error_codes()`/`build_x_polaris_codes()` for the `x-polaris-codes` OpenAPI extension. - Adds three fixture snapshots in `backend/tests/fixtures/snapshots/`: `tiny.json` (hand-computable optimum), `medium.json` (realistic mix), `every_lock_type.json` (all eleven `plan_lock` types). - Adds `polaris replay-run --file <snapshot>` (`cli/replay_run.py`): loads and summarises a snapshot, runs it through clearly-marked stub `solve()`/`validate()` (T-C/T-D don't exist yet). Discovered automatically by the existing CLI package walk. - Adds `backend/tests/unit/test_import_rules.py`: a data-driven AST walker enforcing the full backend import-rules table, at the path the docs already reserve for it. Only checks a package once it exists on disk, so later tracks get coverage for free. ## Acceptance criteria (issue #12) - [x] A hand-built `PlanningSnapshot` serialises to JSON and back with equality - [ ] Every validator/error/notification code appears in `x-polaris-codes` in the generated spec -- **the builder function exists and is tested; wiring it into `backend/openapi.json` needs a 2-line change to `polaris/cli/openapi.py`, which this ticket doesn't own. Filed as seam request #54.** - [x] Fixtures exist: tiny (known optimum), medium, every-lock-type; each loads through the contracts - [x] `polaris replay-run --file <snapshot>` runs end to end with a no-op engine - [x] Import-rule test exists and passes: contracts import only `core` ## Notes for downstream tracks - `FindingCode` severity is a table (`FINDING_SEVERITY`), not derived from the `E_`/`W_`/`I_` prefix -- `W_IDLE_CREW`/`W_WAITING` are documented `info` severity in `docs/04-lifecycles.md` despite the `W_` prefix. Build `Finding`s via `Finding.for_code(...)`. - `NotificationKind` is an initial v1 list inferred from the scenarios (no doc enumerates one) -- flagged on #12, extending it later is a `!` commit since `contracts/` is sealed after PH1. - `TaskPayload` splits event-triggered replans into their own `solve_event` kind (matching PH5.2's `worker/handlers/solve_event.py`) rather than folding everything into `solve`'s `mode` field -- flagging in case T-E/PH5.2 want a different split before it's load-bearing. ## Test plan - [x] `ruff check` / `ruff format --check` clean - [x] `mypy --strict src` clean (39 source files) - [x] `pytest` green: 70 passed (62 unit + 8 integration, including all pre-existing PH0.2 tests) ## Closes Closes #12 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
PH1.4 (#12): the frozen Pydantic contracts that cross module boundaries
per docs/12-implementation-plan.md's import-rules table --
PlanningSnapshot (engine/validator input), PlanResult (engine output),
Finding (validator output: code + structured params, message dev-only),
projector and scorer I/O (including the AdjustedConfidenceFn hook
signature agreed here for T-B/T-G), and the TaskPayload discriminated
union for the task queue. Also FindingCode/error-code/NotificationKind
closed lists and build_x_polaris_codes(), the x-polaris-codes OpenAPI
extension payload (wiring it into `polaris openapi` is a seam request,
see issue #54 -- polaris/cli/openapi.py is outside this ticket's owned
paths).

polaris.contracts imports only polaris.core and stdlib/pydantic, per the
import-rules table; no SQLAlchemy, no FastAPI, no I/O -- these are pure,
frozen snapshot/result models.

No dependency added: pydantic already arrives transitively via
fastapi[standard] and is used the same way core.errors already uses it.

Closes #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
PH1.4 (#12):

- Unit tests for every contracts model (snapshot round-trip/frozen/
  defaults, Finding severity table incl. the W_IDLE_CREW/W_WAITING
  "info despite W_ prefix" quirk in docs/04-lifecycles.md, TaskPayload
  discrimination, PlanResult, projector/scorer I/O and the
  AdjustedConfidenceFn hook, closed code lists).
- Three fixture snapshots in tests/fixtures/snapshots/, serialised from
  PlanningSnapshot itself (built via a throwaway generator script, not
  committed): tiny.json (two jobs, one crew/shift, co-located
  full-confidence windows -- a hand-computable optimum for T-C's future
  engine tests), medium.json (three crews, eight jobs, mixed
  priorities/confidences, a tight window, a below-min-confidence job, a
  pin lock and a soft-locked confirmed assignment), every_lock_type.json
  (one of each of the eleven plan_lock types from docs/08-data-model.md).
- tests/unit/test_import_rules.py: a data-driven AST walker enforcing
  the whole backend import-rules table from docs/12-implementation-plan.md,
  at the path the docs already name for it. Only checks a package once
  it exists on disk, so later tracks (engine, validator, domain, ...)
  get coverage automatically as their packages land -- extend the RULES
  table, don't restructure the file. This ticket's own acceptance
  criterion is the `contracts` row: contracts may import only `core`.

Closes #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
PH1.4 (#12): `polaris replay-run --file <snapshot>` loads a
PlanningSnapshot from a JSON file, prints a summary, and runs it
through clearly-marked stub solve()/validate() steps -- polaris.engine
(T-C) and polaris.validator (T-D) don't exist yet. Discovered by
polaris.cli's existing package walk; cli/__init__.py needed no edit.

Closes #12

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
bart merged commit c5cf962e15 into main 2026-09-11 11:14:23 +00:00
Sign in to join this conversation.
No reviewers
No labels
ready-for-agent
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
patrick/Polaris!55
No description provided.