Skip to content

Seed a new machine's service card from the farm's own interval table, instead of from a literal - #116

Merged
dimmus merged 2 commits into
mainfrom
claude/maintenance-defaults
Sep 24, 2026
Merged

dimmus merged 2 commits into
mainfrom
claude/maintenance-defaults

Conversation

@dimmus

@dimmus dimmus commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Issue #29, the maintenance intervals row (the kit's «Периодичность по умолчанию»). Stacked on #115 because both touch the same HANDOFF section; merge #115 first and this diff shrinks to its own commit.

What it is. service.maintenance_defaults, the fifth Kind.TABLE field: rows of {code, interval_hours}, one per MaintenanceKind. Default: every kind at the 500 hours CreateServiceOperation always assumed, so an untouched table changes nothing (the settings context's rule).

Who reads it — the part the issue says must not be missing.

  • POST /printers seeds the new machine's service card from the table. A machine used to be registered with an empty card.
  • POST /printers/{id}/services gives an operation sent without a periodicity its kind's row; an explicit 500 is told apart from an omitted one by model_fields_set. A kind the table does not carry keeps the schema's default rather than being refused.
  • Removing a kind from the table means a new machine does not get that operation. The editor draws such a row with a dash and «Вернуть в таблицу».

What is deliberately not stored. The kit's «Простой» and «Расход» columns: ServiceOperation carries neither and nothing prices a service, so they would be settings rows nothing reads (CLAUDE.md §1). They arrive with whatever reads them.

Where the shape lives. contexts/fleet/maintenance.py — validation (error.fleet.maintenance_kind_unknown, …_duplicate, …_interval_invalid), default, JSON shape and the seeding — because the consumer owns what a valid row is, the way pricing owns ZoneTariffs. Settings stores, audits and resolves (resolve_maintenance_defaults).

Verified

  • Backend: 15 unit tests (catalogue, parsing, resolver, seeding) and 4 API tests (default card, table edits change the next machine and not the previous one, omitted vs explicit periodicity); the existing printers, fleet, settings-catalogue and zones tests pass; six gates each exit=0. Mutations, run and reverted: register ignoring the table (3 API tests fail), the services route ignoring it (1).
  • Frontend, from the lockfile: tsc --build --force, lint, test (33 files, 320 passed) and build each exit=0; the API client regenerated from the new schema. The settings screen test edits an interval, drops a kind and asserts the exact PUT body.
  • The full backend suite was not re-run locally (28 min); CI's backend job runs it.

Left on #29 after this: the event matrix, API keys and webhooks. Each needs a channel or a credential store that does not exist yet.

🤖 Generated with Claude Code

… instead of from a literal (#29)

The maintenance-intervals row of #29, and the one of its six tables with a
consumer waiting: a printer was registered with an empty service card, and
every operation an engineer then added defaulted to 500 hours from a literal
in `CreateServiceOperation`. `service.maintenance_defaults` is now the fifth
Kind.TABLE field - rows of {code, interval_hours}, one per MaintenanceKind,
default every kind at 500 so an untouched table changes nothing.

The shape, its validation and the seeding live in fleet (`maintenance.py`):
the consumer owns what a valid row is, as pricing owns ZoneTariffs. Settings
stores, audits and resolves it. Two readers, both at the API edge: POST
/printers seeds the card from the table, and POST /printers/{id}/services
gives an operation sent without a periodicity its kind's row - told apart
from an explicit 500 by model_fields_set. A kind removed from the table is
not seeded on a new machine; the editor draws it with a dash and a way back.

The kit's «Простой» and «Расход» columns are deliberately not stored:
ServiceOperation carries neither and nothing prices a service, so they would
be rows nothing reads (CLAUDE.md §1).

Verified: 15 unit + 4 API tests; the six backend gates each exit=0; the
editor's screen test saves exactly the rows the server keys by code; frontend
from the lockfile - tsc --build --force, lint, test (33 files, 320 passed),
build, and a regenerated API client - each exit=0. Mutating either router
reading to ignore the table fails the API tests (3 and 1). The zones test's
table-list guard now names the fifth table.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
@dimmus
dimmus force-pushed the claude/maintenance-defaults branch from 4cfe514 to ecbc6b1 Compare September 24, 2026 15:38
…st reads them

`tests/unit/test_docs_screen_inventory.py` checks §1 and §2.1 against
`len(FIELDS)` and the per-kind counts, and both moved by one with
`service.maintenance_defaults`: 105 parameters, five tables. §2.1's status
of #29 now lists the maintenance table among the built ones and names the
two columns it does not store, so the document says what the code does
rather than what the issue said was missing (CLAUDE.md §4).

Verified: the docs tests, 32 passed, 1 skipped.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
@dimmus
dimmus merged commit a82d7fe into main Sep 24, 2026
7 checks passed
@dimmus
dimmus deleted the claude/maintenance-defaults branch September 24, 2026 16:08
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