chore(deps-dev): bump conventional-changelog-conventionalcommits from 7.0.2 to 9.3.1#7
Merged
strausmann merged 1 commit intoMay 10, 2026
Conversation
9 tasks
strausmann
added a commit
that referenced
this pull request
May 10, 2026
When Dependabot opens a PR against main and main does not yet contain a backend/pyproject.toml (e.g. before the first feature branch lands), the existing actions/setup-python step would fail because cache: pip insists on resolving requirements.txt or pyproject.toml at checkout time. Fix by detecting the project layout up-front and: - using setup-python WITH cache when a pyproject.toml exists (in either ./backend or ./), with explicit cache-dependency-path - using setup-python WITHOUT cache otherwise, and skipping all the ruff/mypy/pytest steps with a single no-op message Also moves all working directories explicitly so we can later add a backend/ matrix entry without rewriting steps. This unblocks PRs #2-#9 (currently red on the cache step) and prevents the same class of failure for any future contributor branch that touches infrastructure but doesn't include the backend yet. Refs #2, #3, #4, #6, #7, #8, #9
Owner
|
@dependabot rebase |
Bumps [conventional-changelog-conventionalcommits](https://github.com/conventional-changelog/conventional-changelog/tree/HEAD/packages/conventional-changelog-conventionalcommits) from 7.0.2 to 9.3.1. - [Release notes](https://github.com/conventional-changelog/conventional-changelog/releases) - [Changelog](https://github.com/conventional-changelog/conventional-changelog/blob/master/packages/conventional-changelog-conventionalcommits/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/conventional-changelog/commits/conventional-changelog-conventionalcommits-v9.3.1/packages/conventional-changelog-conventionalcommits) --- updated-dependencies: - dependency-name: conventional-changelog-conventionalcommits dependency-version: 9.3.1 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
dependabot
Bot
force-pushed
the
dependabot/npm_and_yarn/conventional-changelog-conventionalcommits-9.3.1
branch
from
May 10, 2026 18:16
bd428a3 to
8d67e1f
Compare
strausmann
deleted the
dependabot/npm_and_yarn/conventional-changelog-conventionalcommits-9.3.1
branch
May 10, 2026 18:17
github-actions Bot
pushed a commit
that referenced
this pull request
May 10, 2026
## 1.0.0 (2026-05-10) * ci: make Python lint/test job branch-tolerant for dependabot PRs (#30) ([6ee8e16](6ee8e16)), closes [#30](#30) [2-#9](https://github.com/2-/issues/9) [#2](#2) [#3](#3) [#4](#4) [#6](#6) [#7](#7) [#8](#8) [#9](#9) * ci(deps): bump the actions-all group across 1 directory with 14 updates (#9) ([a88a027](a88a027)), closes [#9](#9) * chore: initial repository skeleton ([0319e7e](0319e7e)) * chore(deps-dev): bump @commitlint/cli from 19.8.1 to 21.0.0 (#3) ([2642b26](2642b26)), closes [#3](#3) * chore(deps-dev): bump @commitlint/config-conventional (#4) ([44be5f9](44be5f9)), closes [#4](#4) * chore(deps-dev): bump @semantic-release/exec from 6.0.3 to 7.1.0 (#2) ([a8097b0](a8097b0)), closes [#2](#2) * chore(deps-dev): bump @semantic-release/github from 11.0.6 to 12.0.8 (#8) ([098e766](098e766)), closes [#8](#8) * chore(deps-dev): bump conventional-changelog-conventionalcommits (#7) ([9f5370f](9f5370f)), closes [#7](#7) * chore(deps-dev): bump semantic-release from 24.2.9 to 25.0.3 (#6) ([306010f](306010f)), closes [#6](#6) * feat(status): add Brother 32-byte status block parser (#29) ([ced0ff8](ced0ff8)), closes [#29](#29) [#11](#11) [#19](#19) [#29](#29) * docs: refactor — ADRs in docs/decisions/, policies in docs/policies/, slim CLAUDE.md ([51c2cf1](51c2cf1)), closes [#1](#1) * docs(decisions): ADR 0006 — PT vs QL ESC i S behaviour from Phase-0 hardware test ([0d12c63](0d12c63)), closes [#12](#12) [#11](#11) * docs(decisions): ADR 0012 — layout management; clarify integration push/pull capabilities ([effcbdf](effcbdf)), closes [#17](#17) [#19](#19) * docs(decisions): ADR 0013 + cart UI spec + AI-review workflow ([c72dc85](c72dc85)), closes [#26](#26) [#27](#27) [#28](#28) * docs(docker): document image tag scheme (latest, 1.0.0, 1.0, 1) ([1f72396](1f72396)) * docs(examples): sample compose files for standalone/Traefik/Pangolin/Caddy ([a2f6f3d](a2f6f3d)) [skip ci]
github-actions Bot
pushed a commit
that referenced
this pull request
May 10, 2026
## 0.1.0 (2026-05-10) * docs: refactor — ADRs in docs/decisions/, policies in docs/policies/, slim CLAUDE.md ([51c2cf1](51c2cf1)), closes [#1](#1) * docs(ci): document mypy as hard gate; clarify CI gate policy in CONTRIBUTING (#31) ([e1e6f18](e1e6f18)), closes [#31](#31) [#30](#30) [#30](#30) [#30](#30) * docs(decisions): ADR 0006 — PT vs QL ESC i S behaviour from Phase-0 hardware test ([0d12c63](0d12c63)), closes [#12](#12) [#11](#11) * docs(decisions): ADR 0012 — layout management; clarify integration push/pull capabilities ([effcbdf](effcbdf)), closes [#17](#17) [#19](#19) * docs(decisions): ADR 0013 + cart UI spec + AI-review workflow ([c72dc85](c72dc85)), closes [#26](#26) [#27](#27) [#28](#28) * docs(docker): document image tag scheme (latest, 1.0.0, 1.0, 1) ([1f72396](1f72396)) * docs(examples): sample compose files for standalone/Traefik/Pangolin/Caddy ([a2f6f3d](a2f6f3d)) * docs(learnings): add code-review-patterns + reference from CLAUDE/CONTRIBUTING/AI configs (#33) ([0fc61d2](0fc61d2)), closes [#33](#33) [#29](#29) [#30](#30) [#32](#32) [#6](#6) * ci: make Python lint/test job branch-tolerant for dependabot PRs (#30) ([6ee8e16](6ee8e16)), closes [#30](#30) [2-#9](https://github.com/2-/issues/9) [#2](#2) [#3](#3) [#4](#4) [#6](#6) [#7](#7) [#8](#8) [#9](#9) * ci(deps): bump the actions-all group across 1 directory with 14 updates (#9) ([a88a027](a88a027)), closes [#9](#9) * ci(release): trigger releases via cron + workflow_dispatch only (#32) ([9941958](9941958)), closes [#32](#32) [#32](#32) [#32](#32) * chore(deps-dev): bump @commitlint/cli from 19.8.1 to 21.0.0 (#3) ([2642b26](2642b26)), closes [#3](#3) * chore(deps-dev): bump @commitlint/config-conventional (#4) ([44be5f9](44be5f9)), closes [#4](#4) * chore(deps-dev): bump @semantic-release/exec from 6.0.3 to 7.1.0 (#2) ([a8097b0](a8097b0)), closes [#2](#2) * chore(deps-dev): bump @semantic-release/github from 11.0.6 to 12.0.8 (#8) ([098e766](098e766)), closes [#8](#8) * chore(deps-dev): bump conventional-changelog-conventionalcommits (#7) ([9f5370f](9f5370f)), closes [#7](#7) * chore(deps-dev): bump semantic-release from 24.2.9 to 25.0.3 (#6) ([306010f](306010f)), closes [#6](#6) * chore(release): 1.0.0 ([15abeb3](15abeb3)) * feat(status): add Brother 32-byte status block parser (#29) ([ced0ff8](ced0ff8)), closes [#29](#29) [#11](#11) [#19](#19) [#29](#29) [skip ci]
strausmann
added a commit
that referenced
this pull request
May 15, 2026
Five Critical + several Important findings from a fresh-eyes review. The plan was producing tests against StatusBlock / Job / TemplateLoader APIs that don't exist in the real codebase. Add a Phase 1.5 — Domain Model Extensions — before any backend code is written: * Task 1.5.1 — StatusBlock @Property tape_empty / cover_open / loaded_tape_mm (derived from the real errors IntFlag and media_width_mm fields). Plus tests/_helpers/status.make_status_block so subsequent tests don't spell out all 18 dataclass fields. * Task 1.5.2 — TemplateNotFoundError in template_loader (subclasses KeyError; the route maps it to HTTP 404 per Acceptance #6). * Task 1.5.3 — LookupFailedError umbrella + UnknownAppError as a subclass; AppLookupService.lookup wraps any plugin runtime exception. Route maps to HTTP 502 per Acceptance #7. * Task 1.5.4 — Job carries error_code / error_message / error_detail (Acceptance #4 needs structured detail like {expected_mm, loaded_mm}). * Task 1.5.5 — PrintQueue worker catches PrinterError subclasses, populates the three new Job fields, then transitions to FAILED. TapeMismatchError gets a typed detail dict. * Task 1.5.6 — @runtime_checkable on _PrinterLike Protocol so the Phase 8 isinstance() check works. Additional fixes: * Phase 6.1 parse_status_reply delegates to the existing StatusBlockParser.parse instead of inventing a partial parser. The legacy _MEDIA_TYPE_LOOKUP table is removed. * Phase 8 commit body no longer claims "returns empty bytes" — build_print_job raises NotImplementedError, which the code already does. Stale doc text removed. * PTouchBackend._ptouch_print is now model-aware via _PTOUCH_PRINTER_ CLASSES[model_id] lookup. Previously hardcoded PTP750W defeated Extensibility Path 1 (PT-P900, PT-E550W). * PrintService no longer forwards copies to queue.submit. Multi-copy delivery is a Phase-5 follow-up; clients can post N times today. Test assertion updated to "copies not in kwargs"; commit body of the PrintService task corrected. * app/main.py snippet has `import logging` (was missing while _log = logging.getLogger(__name__) was already present). * app/api/routes/print.py snippet now has one consolidated import block — the previous "Required imports for the route module" trailing block is folded in to prevent paste mistakes. * Convention note added at the top of Phase 2 spelling out that every later test uses make_status_block(...) instead of StatusBlock(...). The implementer rewrites the six existing StatusBlock(...) call sites to make_status_block(...) accordingly. Refs #22
strausmann
added a commit
that referenced
this pull request
May 15, 2026
TDD-strict implementation plan for the First-Print pipeline, tracking docs/designs/2026-05-15-first-print.md. 17 phases, ~70 atomic tasks. Phase 1.5 (Domain Model Extensions) extends StatusBlock with derived properties, adds TemplateNotFoundError / LookupFailedError, structured error fields on Job, and the @runtime_checkable decorator on _PrinterLike — unblocking three Acceptance Criteria (#4, #6, #7). Plan covers: - Phase 0: commitlint scope (printer-backends) - Phase 1: ptouch + Brother status block + SNMP OID references - Phase 1.5: Domain model extensions - Phase 2: PrinterError hierarchy (incl. SnmpDiscoveryError + SnmpQueryError) - Phase 3: PrinterBackend Protocol - Phase 4: MockPrinterBackend - Phase 5: BackendRegistry + entry_points - Phase 6: ESC i S status query, PTouchBackend, SNMP helper - Phase 7: ModelRegistry.find_by_model_id + entry_points - Phase 8: PTP750WDriver + make_queue_printer + _PTPQueuePrinter - Phase 9: Pydantic schemas (PrintRequest, RawLabelData, etc.) - Phase 10: PrintService orchestrator - Phase 11: REST routes incl. live SNMP block while PRINTING - Phase 12: Settings (printer_backend, printer_model, discover_via_snmp, snmp_community, queue_timeout_s) - Phase 13: Lifespan with SNMP-first discovery + fallback - Phase 14: Integration tests (happy + tape mismatch + offline + SNMP discovery) - Phase 15: Hardware smoke script - Phase 16: Final verification + handoff (implementer never pushes) Went through Gemini + superpowers code-review iterations (6 + 5 + 12 findings, all addressed or explicitly deferred). CI checks all green including privacy / secret scan. Refs #22
github-actions Bot
pushed a commit
that referenced
this pull request
May 16, 2026
## 0.4.0 (2026-05-16) * ci(deps): bump the actions-all group with 2 updates (#44) ([d4a0401](d4a0401)), closes [#44](#44) * docs(designs): First-Print pipeline design (#57) ([b100574](b100574)), closes [#57](#57) [#58](#58) [#22](#22) * docs(plans): First-Print implementation plan (#58) ([e7c93de](e7c93de)), closes [#58](#58) [#4](#4) [#6](#6) [#7](#7) [#22](#22) * feat(integrations): plugin architecture for lookup clients (Phase 3.5) (#55) ([488256b](488256b)), closes [#55](#55) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#55](#55) [#22](#22) * feat(templates): default templates + TemplateLoader (Phase 4) (#56) ([7e500aa](7e500aa)), closes [#56](#56) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [skip ci]
strausmann
added a commit
that referenced
this pull request
May 16, 2026
…ed) (#59) * build(ci): add printer-backends scope to commitlint First-Print introduces app/printer_backends/, which is conceptually separate from app/printer_models/ (driver vs. transport). Add the matching commit scope so backend-layer changes can be tagged correctly in the changelog. Refs #22 * docs(printer-backends): record ptouch entry-points + status gap Reference for implementers: pinned ptouch classes we depend on, exception hierarchy to wrap, and the explicit gap that ptouch exposes no public status-query API — so PTouchBackend must implement ESC i S over a raw socket. Refs #22 * docs(printer-backends): wire-level Brother status block (ESC i S) Single-page reference for the 3-byte request + 32-byte reply implemented in PTouchBackend.query_status(). Pulled from Brother Raster Command Reference (PT-Series). Refs #22 * docs(printer-backends): Brother SNMP OIDs reference Pins the two SNMP queries First-Print depends on: * 1.3.6.1.4.1.2435.2.3.9.1.1.7.0 — Brother private PJL string, used for lifespan model discovery via ModelRegistry.find_by_pjl * 1.3.6.1.2.1.25.3.5.1.1.1 / .2.1 — standard Host-Resources Printer MIB hrPrinterStatus + hrPrinterDetectedErrorState, used for live status while a print is running. Notes on the bitmap of hrPrinterDetectedErrorState and why SNMP is not enough on its own (no direct tape_mm — uses ESC i S for that). Refs #22 * feat(status): derived tape_empty / cover_open / loaded_tape_mm properties StatusBlock already carries every Brother byte (media_width_mm, media_type, errors IntFlag, ...). The First-Print backend layer prefers a tape_empty / cover_open / loaded_tape_mm vocabulary in its validation chain. Add the three as @Property on the existing dataclass without changing the parser or any persisted field. Plus tests/_helpers/status.py: make_status_block(loaded_tape_mm=..., tape_empty=..., cover_open=...) so every later test stops needing to spell out all 18 dataclass fields. Refs #22 * feat(api): TemplateNotFoundError typed exception TemplateLoader.get() raised bare KeyError. The /print route needs a typed exception to map onto HTTP 404 (Acceptance #6). TemplateNotFoundError subclasses KeyError so any existing catcher keeps working; new code can pattern-match precisely. Refs #22 * feat(api): LookupFailedError umbrella exception Typed exception family for /print route HTTP 502 (Acceptance #7). LookupFailedError is the umbrella; UnknownAppError (config mismatch) is a subclass. AppLookupService.lookup wraps any runtime exception from the plugin into LookupFailedError so the REST layer has one type to catch. AppLookupNotFoundError (entity-not-found) is explicitly re-raised unchanged — it is not a transport failure and must not be swallowed. Deviation from plan: IntegrationRegistry.get raises IntegrationNotFoundError (not KeyError) — except clause adjusted accordingly. Monkeypatch target corrected to app.integrations.registry.IntegrationRegistry.get. Refs #22 * feat(queue): Job carries error_code / error_message / error_detail REST /jobs/{id} surfaces these to the client (Acceptance #4 — tape mismatch must produce error_code='tape_mismatch' and error_detail={'expected_mm':24,'loaded_mm':12}). The worker (next task) populates them when a PrinterError subclass surfaces from the printer.print_image call. The existing error_msg / error_flags fields stay for backward compatibility. Refs #22 * feat(queue): worker maps PrinterError subclasses to Job error fields When printer.print_image raises a PrinterError, the worker now writes a structured (error_code, error_message, error_detail) record onto the Job before transitioning to FAILED. TapeMismatchError gets a typed detail dict carrying expected_mm + loaded_mm so the REST client gets actionable diagnostics. Other PrinterError subclasses map to error_code only. The legacy error_msg field is kept in sync for backwards-compatible callers. Also lands the app/printer_backends/ package with its exception hierarchy (PrinterError + 8 subclasses incl. Snmp*) — earlier than Phase 2 nominally specifies because the worker hook needs it now. The Phase 2 exception-hierarchy task will only add the dedicated test file (test_exceptions.py); the production code already lives. Refs #22 * refactor(queue): runtime_checkable _PrinterLike Phase 8 tests do isinstance(qp, _PrinterLike). That requires the @runtime_checkable decorator; otherwise Python raises TypeError at the isinstance call. runtime_checkable only relaxes isinstance -- the Protocol's structural shape is unchanged. Refs #22 * test(printer-backends): coverage for PrinterError hierarchy Parametrised test verifying every PrinterError subclass (8 in total incl. Snmp* added in the SNMP-hybrid design) is properly rooted in PrinterError → Exception, plus dedicated tests for TapeMismatchError field access. The production module landed earlier (commit f92dc0c) because the PrintQueue worker mapping depended on it. This commit closes Phase 2 by adding the dedicated test surface. Refs #22 * feat(printer-backends): PrinterBackend Protocol Two-method runtime_checkable Protocol: print_image (encode + send) and query_status (ESC i S equivalent). Backends are bound to one host at construction time. send_bytes raw-raster escape hatch is deliberately omitted — see design doc for rationale. Refs #22 * feat(printer-backends): add MockPrinterBackend In-memory PrinterBackend used by unit/integration tests and by local dev runs without real hardware (PRINTER_HUB_PRINTER_BACKEND=mock). Failure modes are constructor flags: offline, tape_empty, cover_open, loaded_tape_mm. The backend records every printed image so tests can assert dimensions and order. Refs #22 * feat(printer-backends): BackendRegistry + entry_points discovery Class-level registry of backend factory classes, populated at app start via setuptools entry_points (group label_hub.printer_backends). ensure_discovered() is idempotent. find_by_backend_id raises UnknownBackendError with the list of registered options when the requested backend is missing. Third-party backends ship as pip packages declaring an entry-point in this group; no core changes required. Refs #22 * feat(printer-backends): register mock backend in entry_points Declares mock = app.printer_backends.mock_backend:MockPrinterBackend under the label_hub.printer_backends group so BackendRegistry. ensure_discovered() picks it up at app start. ptouch backend will be registered alongside in a later commit once PTouchBackend exists. Refs #22 * feat(printer-backends): ESC i S status query over asyncio socket Implements the wire-level Brother status query that ptouch does not expose. Uses asyncio.open_connection so the call is non-blocking inside an async def; flushes via drain(), closes cleanly via close() + wait_closed() to avoid truncating mid-transfer. Parser delegates to the existing StatusBlockParser after a fast header sanity check; malformed replies raise StatusQueryFailedError. Refs #22 * feat(printer-backends): PTouchBackend wrapping ptouch library Implements PrinterBackend against the ptouch Python library: * query_status — uses the raw-socket ESC i S helper, makes exactly 3 attempts with back-off (0s, 1s, 2s) on PrinterOfflineError. * print_image — pre-validates against query_status (tape_empty, cover_open, tape mismatch); dispatches the synchronous ptouch.print via asyncio.to_thread; wraps ptouch's exception family into our PrinterError subtypes. * from_settings — reads pt750w_host / pt750w_port and looks up the ptouch printer class from printer_model; raises clearly on empty host or unknown model. * _ptouch_print is module-level + model-aware via _PTOUCH_PRINTER_CLASSES[model_id] — Extensibility Path 1 (PT-P900, PT-E550W) works without a fork. Note: PrinterWriteError/PrinterPermissionError are caught before the base PrinterConnectionError as both are subclasses of it — order matters for correct exception routing. Refs #22 * feat(printer-backends): register ptouch backend in entry_points Joins the label_hub.printer_backends entry-points group so BackendRegistry.ensure_discovered() picks it up at app start. Refs #22 * feat(printer-backends): SNMP helpers (discovery + live status) Two async helpers built on pysnmp's asyncio API: * query_model_pjl(host) — reads Brother private OID 1.3.6.1.4.1.2435. 2.3.9.1.1.7.0 → full PJL identification string. Used by the lifespan to resolve the model via ModelRegistry.find_by_pjl (ADR 0004). * query_live_status(host) — reads Host-Resources Printer MIB hrPrinterStatus + hrPrinterDetectedErrorState in one round trip, returns LiveStatus { hr_printer_status, error_flags } for the /jobs/{id} response while a print is running. Failure modes are separate: SnmpDiscoveryError stops app start; SnmpQueryError is non-fatal at request time (live block is omitted). SNMPv2c with a configurable community (default 'public'); printer is on the LAN, so v2c is fine. pysnmp 7.x API deviation: UdpTransportTarget no longer accepts the address in __init__; production code uses await UdpTransportTarget.create((host, port), timeout=..., retries=0) Tests monkeypatch get_cmd directly so they are unaffected by the transport construction; an integration test would catch real-API mismatches at runtime. Test import fix: pysnmp.smi.rfc1902 does not re-export OctetString / Integer directly in 7.x; tests use from pysnmp.proto import rfc1902. Refs #22 * feat(printer-models): find_by_model_id + entry_points discovery ModelRegistry gains find_by_model_id(model_id) — used by the lifespan to resolve settings.printer_model to a driver class — and ensure_discovered() which walks the label_hub.printer_models entry-points group once at app start. register() now accepts class objects too (entry-points return classes); the existing instance form keeps working. Refs #22 * feat(printer-models): PTP750WDriver + queue-printer factory PT-P750W driver implements PrinterModel (model_id, dpi, pins, PJL/SNMP signatures, width_to_pixels, query_status, build_print_job) and exposes make_queue_printer(tape_registry, default_media_type=LAMINATED) which produces a private _PTPQueuePrinter satisfying PrintQueue._PrinterLike. query_status raises ValueError on a non-matching host argument rather than silently ignoring it (the driver is bound to one backend). build_print_job raises NotImplementedError so any unintended caller fails loudly; the First-Print happy path uses backend.print_image directly; raw-byte encoding is deferred until a concrete caller appears. Registered at module import via ModelRegistry.register (cast to type[PrinterModel] to satisfy mypy strict — Protocol conformance is verified by runtime isinstance check) and via the label_hub.printer_models entry-points group. Refs #22 * feat(api): PrintRequest + RawLabelData + PrintOptions schemas Top-level POST /print body. PrintRequest enforces exactly-one of `lookup` or `data` via a model_validator. PrintOptions is frozen with copies bounds (1..10). RawLabelData mirrors LabelData but rejects source_app at the wire — PrintService sets it to "manual" for the raw-data path. PrintOptions uses Field(default_factory=) on PrintRequest so each request gets its own instance (Pydantic shared-mutable-default anti-pattern avoided). Refs #22 * feat(api): PrintJobResponse + PrintJobStatusResponse schemas POST /print returns a 202 with PrintJobResponse — status is the literal 'queued'. GET /jobs/{job_id} returns PrintJobStatusResponse typed as the real JobState enum (queued/paused/printing/completed/ failed/cancelled) so clients receive the same vocabulary the PrintQueue uses internally. `live: LiveStatus | None` is populated by the route handler when the job is currently PRINTING (SNMP live-status fetched on demand). Refs #22 * style(schemas): fix import sort order in Phase 9 test files ruff I001 auto-fix: reorder imports so third-party (pydantic) follows stdlib/local per isort convention enforced by ruff. Refs #22 * feat(api): PrintService orchestrator Three-step pipeline behind POST /print: template_loader.get → either lookup_service.lookup (integration path) or LabelData(..., source_app= 'manual') from RawLabelData (raw-data path) → renderer.render → print_queue.submit(printer_id, image, tape_mm=, **options). source_app for the raw-data path is fixed to "manual" here so the wire schema doesn't have to police it. Options propagate to submit as keyword args (auto_cut, high_resolution). `copies` is intentionally NOT forwarded for First-Print — multi-copy delivery is a Phase-5 follow-up (clients can post N times today). Refs #22 * feat(api): POST /print + GET /jobs/{job_id} POST returns 202 with PrintJobResponse on success. Synchronous errors (TemplateNotFoundError, LookupFailedError) map to 404 / 502 with an error_code in the JSON body; hardware/print errors travel to the worker and surface via GET /jobs/{job_id}. GET returns PrintJobStatusResponse with JobState (queued/paused/ printing/completed/failed/cancelled) and any error_* fields the worker recorded. While the job is PRINTING and a printer host is in app.state, the response includes a `live` sub-object fetched via SNMP (1s timeout, non-fatal on failure). Unknown job_id is 404. Refs #22 * feat(api): new First-Print settings (backend, model, snmp, timeout) Five new fields on Settings: * printer_backend — resolves against BackendRegistry at app start (built-ins: ptouch, mock) * printer_model — fallback when SNMP discovery is off or fails (resolves against ModelRegistry) * printer_discover_via_snmp — try SNMP discovery first, fall back to printer_model (default True) * printer_snmp_community — SNMPv2c community (default 'public') * printer_queue_timeout_s — graceful queue.stop() timeout Existing pt750w_host / pt750w_port / ql820_host / ql820_port fields are kept untouched. PTouchBackend.from_settings reads pt750w_host and looks up the right ptouch class via printer_model. Refs #22 * feat(api): lifespan wires plugin discovery + queue + service App startup: 1. Discover printer-model + backend plugins via entry_points (idempotent ensure_discovered). 2. Resolve model id: SNMP-first via query_model_pjl when host is configured and discovery is enabled; falls back to settings.printer_model on SnmpDiscoveryError (warned in log). 3. Build the configured backend via BackendRegistry + from_settings(settings). 4. Resolve the driver via ModelRegistry.find_by_model_id and bind it to the backend. 5. Build the _PrinterLike via driver.make_queue_printer(tape_registry). 6. Start the PrintQueue with [printer]. 7. Wire PrintService into app.state (template loader + renderer + queue + lookup_service + printer_id). App shutdown: queue.stop(timeout_s=settings.printer_queue_timeout_s). _LifespanManager wrapper intercepts ASGI lifespan scopes and runs startup lazily on the first HTTP request when no real ASGI server sends the lifespan scope (httpx ASGITransport test pattern). A real server (uvicorn, TestClient) sends the lifespan scope which the wrapper intercepts and relays without double-running the inner app. ModelRegistry.ensure_discovered() handles module-type entry_points (pt-series = app.printer_models.pt) by scanning module members for PrinterModel classes, enabling correct re-registration after test fixtures clear the registry. Integration tests get an autouse fixture in tests/integration/conftest.py that sets the mock backend so the lifespan succeeds without hardware. Unknown backend, unknown model, or SnmpDiscoveryError without a printer_model fallback all raise at app start with clear errors. Refs #22 * test(api): POST /print → GET /jobs/{id} happy path + 404 Drives the full lifespan: plugin discovery, mock backend, PrintQueue worker, status polling. Asserts the job transitions to completed. Template-not-found returns 404 synchronously with error_code set; no job record is created. Also fixes a latent bug exposed by the integration test: LabelRenderer.render had parameters (data, template) but _RendererProto + PrintService called it as (template, data). Parameter order is now aligned to the Protocol contract. Removed the now-unnecessary type: ignore[arg-type] in main.py. All call sites in unit tests updated accordingly. Refs #22 * test(api): hardware smoke for PT-P750W First-Print Manual smoke script (scripts/smoke_first_print.py) renders the qr-only-24mm seed template with primary_id=SMOKE-001 and prints it on a real PT-P750W identified by PRINTER_HUB_PT750W_HOST. Adds the gated test tests/hardware/test_pt_p750w_smoke.py — skipped by default, runs only with `pytest --hardware` and a configured host. The conftest hardware-marker is already in place from earlier work. Refs #22 * feat(api): trim whitespace on length axis to save tape material Hardware smoke against a real PT-P750W revealed that the QR-only-12mm template was producing 85mm-long labels for a ~12mm-wide QR. Background: LabelRenderer assumes QL-series 300 DPI canvas geometry, but ptouch prints 1:1 at PT-series 180 DPI — the canvas-pixel-to-mm ratio is wrong on PT hardware. Until the DPI-aware renderer refactor lands (follow-up), trim the returned image to its inked bounding box on the length axis with a 6 px (~1mm) margin. Height stays fixed — that's the pin-locked tape axis. Real-world impact in the smoke test: QR-only-12mm went from 600×106 to 86×106 px → 12mm tape consumed instead of 85mm. 72.5 mm saved per print. Also update test_seed_templates to reflect that width may now be trimmed (asserts height pin-locked + 1 <= width <= DEFAULT_LABEL_WIDTH_PX). Refs #22 * feat(printer-backends): SNMP preflight helpers (loaded tape + combined query) Hardware smoke revealed ESC i S over TCP/9100 times out on PT-P750W in idle state — the planned pre-print check doesn't work. SNMP runs in parallel and is reliable. Two new helpers: * parse_loaded_tape_mm + query_loaded_tape_mm — read OID 1.3.6.1.2.1.43.8.2.1.12.1.1 (prtInputMediaType), parse strings like '12mm(0.47")' into int mm. Returns None for empty / unparsable replies (no tape inserted). * query_preflight — single round-trip combining hrPrinterStatus + hrPrinterDetectedErrorState + prtInputMediaType into a PreflightStatus dataclass. Used by PTouchBackend.preflight_check (next task). Refs #22 * fix(printer-backends): use generic ptouch Tape*mm classes (not laminated) Hardware smoke against a real PT-P750W revealed that ptouch.PTP750W.get_tape_config() whitelists ONLY the generic Tape*mm classes and raises ValueError on any LaminatedTape*mm subclass: ValueError: LaminatedTape24mm is not supported by PTP750W. Supported tapes: Tape3_5mm, ... Tape24mm, Tape36mm, ... This is a hard regression-block guarded by a unit test that scans the whole _PTOUCH_TAPE_CLASSES dict for 'Laminated' in the class name. The test exists because the same trap is easy to fall into when adding new tape widths. The user's tape is still laminated — the physical Brother TZe-Tape works fine; ptouch just doesn't model the laminated/non-laminated distinction at this API surface. Media-type-specific handling lives in our own TapeRegistry.lookup_pt(width_mm, media_type), which still differentiates laminated vs heat-shrink etc. for our own validation. Refs #22 * feat(printer-backends): PTouchBackend.preflight_check (SNMP-based) Hardware smoke revealed ESC i S over TCP/9100 (the old query_status path) times out on PT-P750W in idle state — the planned pre-print check doesn't work. SNMP runs in parallel on UDP/161 and is reliable. Adds preflight_check() that wraps query_preflight() from snmp_helper: * Returns PreflightStatus(hr_printer_status, loaded_tape_mm, error_flags) on a healthy printer. * Raises TapeEmptyError on noPaper error bit. * Raises PrinterCoverOpenError on doorOpen error bit. * Wraps SnmpQueryError as PrinterOfflineError. Deliberately does NOT raise TapeMismatchError — the caller (PrintService, next commit) compares preflight.loaded_tape_mm against the request's tape_mm. Backend stays agnostic of what's being printed. The old ESC-i-S query_status() stays in place for use during active print sessions and as a fallback for other backends; this new method is for the synchronous pre-print path only. Refs #22 * feat(api): synchronous SNMP preflight + on_tape_mismatch + resume endpoint User feedback after hardware smoke: "before submitting a print, always check status + loaded tape via SNMP. Only print if request matches. Otherwise: fail synchronously OR optionally queue the job, let user change the tape and resume." Three coupled changes: 1. PrintRequest gains on_tape_mismatch: Literal["fail", "queue"] (default "fail"). Documents the user's intent on mismatch. 2. PrintService.submit_print_job calls backend.preflight_check() BEFORE queue.submit. Three outcomes: * match → normal 202 + job_id, job will print. * mismatch + fail → raise TapeMismatchError → route maps to 409 with error_detail={expected_mm, loaded_mm}. No job record. * mismatch + queue → job is created, immediately transitioned to PAUSED with error_code="tape_mismatch" + error_detail set. User changes physical tape, POSTs /jobs/{id}/resume to release. Other preflight failures (offline / tape empty / cover open) always raise synchronously → 503 / 409. 3. POST /jobs/{job_id}/resume — transitions PAUSED → QUEUED via PrintQueue.resume_job(), clears the tape-mismatch metadata. Returns 200 + updated status. Returns 404 if unknown, 409 if not PAUSED. REST sync error map extended with TapeMismatchError, TapeEmptyError, PrinterCoverOpenError, PrinterOfflineError. TapeMismatchError responses include error_detail={expected_mm, loaded_mm}. PrintService now takes `backend` as a constructor kwarg; lifespan passes it in. MockPrinterBackend gains preflight_check() so integration tests work end-to-end. Integration tests updated: tape mismatch and offline now assert synchronous 409/503 (pre-submit preflight) instead of the previous async FAILED path. Refs #22 * feat(api): worker preflight + queue pause on recoverable errors User requirement: "Tape kann leer werden während des Drucks, muss erkannt werden und zur Unterbrechung der Warteschlange führen." Three coupled changes: 1. PTouchBackend.print_image now uses preflight_check() (SNMP-based, reliable on PT-P750W) instead of the broken ESC i S query_status() path. Pre-print validation is the same — tape mismatch / empty / cover open / offline raise the matching PrinterError subtype. query_status() stays in place for future use by custom backends. 2. PrintQueue worker classifies caught PrinterError into recoverable vs fatal: * Recoverable (TapeMismatchError, TapeEmptyError, PrinterCoverOpenError, PrinterOfflineError) — job goes to FAILED with error_code, AND the printer worker is paused via pause_printer(). The whole queue halts until POST /printer/resume. * Fatal (PrintFailedError, StatusQueryFailedError, …) — existing behaviour: job FAILED, queue continues with the next job. 3. New REST endpoint POST /printer/resume — wakes the paused worker so subsequent jobs in the queue resume. User changes the physical tape, closes the cover, fixes connectivity, then calls this. This makes the queue self-protective: a tape running out mid-stream no longer cascades into N failed jobs — it stops at the first recoverable error and waits for the user to intervene. Refs #22 * feat(api): smoke script auto-discovers loaded tape via SNMP Updated the hardware smoke script to use the new APIs: * Replaces backend.query_status() (legacy ESC i S, broken on PT-P750W in idle) with backend.preflight_check() (SNMP, reliable). * Reads loaded_tape_mm from preflight and picks the matching qr-only-<n>mm seed template automatically (12 / 18 / 24 supported). * Explicit error reporting for each PrinterError subtype with a non-zero exit code. Verified end-to-end against a real PT-P750W: SNMP detects 12mm tape idle, qr-only-12mm template is picked, render+trim yields an 86x106 image (~12mm tape used), printer accepts the job, label exits. Refs #22 * ci(security): wire CODECOV_TOKEN + add CI/CodeQL/codecov README badges The Codecov upload step ran tokenless before — that works for public repos but is rate-limited and produces inconsistent uploads. CODECOV_TOKEN is now configured at the repo level; wire it through the codecov/codecov-action@v6 step and add a `flags: backend` so the report appears under the right component in the Codecov dashboard. README header gains three quality badges (CI, CodeQL, codecov) alongside the existing License / Conventional Commits / semantic-release / GitHub Issues — contributors see at a glance whether main is green and where coverage stands. Refs #22 * ci(security): also upload pytest JUnit XML to Codecov Test Analytics Codecov has two adjacent features: * Coverage (covered/uncovered lines) — already wired in a prior commit. * Test Analytics (slow tests, flaky tests, pass/fail trends) — this commit. Requires JUnit XML output from pytest plus a separate uploader action. pytest now emits both coverage.xml (cobertura) and junit.xml (JUnit legacy format — the format codecov-test-results-action accepts). codecov/test-results-action@v1 uploads junit.xml with the same CODECOV_TOKEN that's already configured for the coverage upload. Both XML artefacts are added to .gitignore so local pytest runs don't leave stray files behind. Refs #22 * docs(readme): drop dead project-board link The /projects page on the repo is empty — no Projects (classic or v2) have been created. Replace the broken link with a pointer to the master tracking issue (#22) plus the open-issues list, which is where phase progress actually lives. Refs #22 * docs: deploy-ready env, backend-only compose, REST API + ENV docs Three coupled additions so the feat/first-print branch can actually be deployed and tested on an end-user docker host (hhdocker03 etc.): * backend/.env.example — adds the five First-Print Phase-17 settings (PRINTER_HUB_PRINTER_{BACKEND,MODEL,DISCOVER_VIA_SNMP,SNMP_COMMUNITY, QUEUE_TIMEOUT_S}) with inline rationale for each. The existing per-model PT750W_HOST/PORT and QL820_HOST/PORT stay untouched. * examples/compose.backend-only.yml — minimal compose recipe for early-development REST API testing. Builds the image from source (no GHCR pull required, no pre-built tag needed), maps host port 8090 → container 8000, reads `.env` at the repo root. Designed for the phase where the frontend container isn't ready yet. * README — adds a "REST API surface" table with all five endpoints (POST /print, GET /jobs/{id}, POST /jobs/{id}/resume, POST /printer/resume, GET /healthz), a `POST /print` body schema with on_tape_mismatch documented, and the synchronous-error-code map. Also adds an "Environment variables" reference table covering every PRINTER_HUB_* setting from the Settings model. The Quick Start snippet now points at compose.backend-only.yml (buildable today) instead of compose.standalone.yml (references a not-yet-published frontend image). Refs #22 * ci(coverage): tighten gates with --cov-fail-under, codecov.yml components, hardware-only exclusions Make coverage drift fail CI loudly rather than silently: - pytest invocation gains --cov-fail-under=80; the threshold mirrors [tool.coverage.report].fail_under so the gate surfaces in the pytest step's own TOTAL line. - codecov.yml introduces project + patch status checks (project: drift ≤1pp + 80% floor; patch: 80% on changed lines) and per-component views aligned with the on-disk module layout (printer_backends, printer_models, services, api, schemas, integrations). Ignored paths cover tests, scripts, docs, examples, and frontend. - _ptouch_print is marked `# pragma: no cover`: it runs only against real hardware and is monkeypatched in every unit test. Lifts the printer_backends folder from 84% to ~95% without losing real signal. - coverage.run.omit drops app/__init__.py (package-metadata shim only; no measurable logic). main.py stays in scope — its lifespan wiring is exercised by integration tests. - .gitignore picks up package-lock.json and backend/uv.lock so stray artefacts from local tooling don't end up in PRs. Project coverage moves from 91.18% to 91.84% on the test suite as it stands today. Refs #22 * ci(docker): publish images for feature branches (slug) and main (dev) in addition to releases Extend the existing release publisher to cover the full branch lifecycle: push to feature branch → ghcr.io/.../label-printer-hub-{backend,frontend}:<branch-slug> push to main → ghcr.io/.../label-printer-hub-{backend,frontend}:dev release v1.2.3 → ghcr.io/.../label-printer-hub-{backend,frontend}:{1.2.3,1.2,1,latest} (plus Docker Hub mirror if DOCKERHUB_USERNAME is set) Implementation: - on.push triggers added with path filters scoped to backend/, frontend/, and the workflow itself — docs-only pushes do not rebuild images. - A concurrency group per ref cancels superseded branch builds but never release builds (a half-published release would leave the registry inconsistent). - "Determine flavor and tag" classifies the run as release / dev / branch and emits both outputs. Replaces the previous tag-only step in both phases. Workflow_dispatch with no tag input now falls through to dev/branch behaviour (lets maintainers force a dev build from a feature branch via the Actions UI). - docker/metadata-action tag rules are gated per flavor so each event produces exactly the tags it should. type=ref,event=branch handles branch-slug sanitisation for feature branches. - Docker Hub is excluded from the images list for non-release flavors — branch/dev builds stay on GHCR only and don't pollute the public Docker Hub namespace. The release path is unchanged in behaviour (same semver chain, same multi-arch composition, same per-platform native runners). Refs #22 * docs(governance): add Contributor License Agreement and link from PR template Adapt the standard Dockhand-style CLA to Label-Printer-Hub: a perpetual, worldwide, no-charge copyright + patent grant to the Maintainer with explicit outbound-relicense permission. The wording mirrors the Finsys CLA (which has its own field-tested legal precedent) with three local adaptations: - Project name → Label-Printer-Hub - Maintainer → Björn Strausmann (the GitHub repo owner) - Outbound license example → the MIT License under which the Project is currently distributed (instead of BSL/Apache 2.0) The PR template's "Community Contribution License Agreement" section is replaced with a short pointer to CLA.md plus a single acknowledgement checkbox. The full legal text lives in CLA.md at the repo root so GitHub renders it next to LICENSE — anyone evaluating the project can read both side by side before contributing. Why now: external contributors are starting to land via Copilot review loops, and an unambiguous IP grant protects both sides. Refs #22 * docs(readme): add CLA assistant badge cla-assistant.io is now linked to this repo and reads the CLA text from the public gist. The badge shows real-time signing status for any open PR and links straight to the CLA page where contributors can sign once for all future contributions. Refs #22 * fix(ci): replace RFC1918 IPs and internal hostname references in tests/examples Privacy scan in ci.yml flags any RFC1918 address (10.0.0.0/8, 172.16/12, 192.168/16, 100.64/10) and a list of internal hostnames so private network artefacts never leak into a public release. Two regressions were caught on the last push: - backend/tests/ used the literal 10.0.0.5 as a fixture printer host in 14 places. Replaced with 192.0.2.10 (RFC 5737, reserved for documentation — explicitly NOT a private network). - examples/compose.backend-only.yml suggested running the container on "hhdocker03 (or any docker host …)" in a usage comment. Removed the internal-hostname example and kept just the generic guidance. No functional change: tests use the host string only as an opaque identifier (SNMP/TCP calls are monkeypatched). 355/355 tests still pass. Refs #22 * docs(designs): spec for Templates Discovery API + minimal-payload validation Closes the documentation gap identified during First-Print smoke testing: the API requires `title` + `primary_id` + `qr_payload` on every POST /print even when the chosen template (e.g. `qr-only-12mm`) renders only the QR. Three coupled improvements lay out the cleanup path: 1. GET /templates + GET /templates/{id} — discovery endpoints so clients learn per-template which payload fields the renderer will read, what role each field plays (qr-content / headline / subtitle / extra-line), tape width, and app-binding. 2. RawLabelData.title/primary_id/qr_payload default to "" and PrintService validates against TemplateSchema.required_fields() at submit-time. Missing required fields raise a 422 with error_code=missing_template_fields and a detail body listing what's missing. 3. Field(..., description=...) on RawLabelData and the new template- response schemas so FastAPI's Swagger UI at /docs becomes the canonical field reference (replacing the missing user-facing docs). The spec is committed on feat/first-print because the validation hook lives in PrintService which is part of PR #59. Implementation is scheduled for AFTER PR #59 merges to main, on a fresh feat/templates-api-and-minimal- payload branch off main — per user decision during brainstorming. Output of superpowers:brainstorming. writing-plans runs next. Refs #22
github-actions Bot
pushed a commit
that referenced
this pull request
May 17, 2026
## 0.5.0 (2026-05-17) * feat(api): First-Print pipeline — POST /print to PT-P750W (E2E verified) (#59) ([d89d057](d89d057)), closes [#59](#59) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#6](#6) [#22](#22) [#7](#7) [#22](#22) [#4](#4) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#22](#22) [#59](#59) [#22](#22) * feat(api): Phase 5 — SQLModel persistence layer (#63) ([806076f](806076f)), closes [#63](#63) * feat(api): Phase 6a — REST API (21 endpoints, DB-backed) ([41da51d](41da51d)), closes [#18](#18) [#22](#22) [#14](#14) * feat(api): Phase 6b — SSE EventBus for live status pages ([b589a30](b589a30)), closes [#14](#14) [#22](#22) [#22](#22) * feat(ui): Phase 7a — Frontend-Basis (Tailwind v4 + 8 pages + SSE proxy) ([0b49d6a](0b49d6a)), closes [#15](#15) [#22](#22) * fix(integration,printer-backends): perf — pooling + caching + SnmpEngine singleton + registry.clear ([0e1d60f](0e1d60f)), closes [#67](#67) [#67](#67) [#67](#67) [#67](#67) * fix(api): Phase 6b review-findings — 5 KRITISCH + 3 WICHTIG + 7 bot-review followups ([76f57e2](76f57e2)), closes [#14](#14) [#22](#22) [#65](#65) [63/#64](#64) * fix(api): print-queue review-followups — race, 409 contracts, stop() cleanup ([650f823](650f823)), closes [#67](#67) [#67](#67) [#67](#67) [#68](#68) [#67](#67) [#68](#68) [#67](#67) [#68](#68) [#67](#67) * ci(ci): raise commitlint header-max-length to 120 + add deps scopes (#60) ([d6be187](d6be187)), closes [#60](#60) [#43](#43) [skip ci]
This was referenced May 17, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebasing might not happen immediately, so don't worry if this takes some time.
Note: if you make any changes to this PR yourself, they will take precedence over the rebase.
Bumps conventional-changelog-conventionalcommits from 7.0.2 to 9.3.1.
Release notes
Sourced from conventional-changelog-conventionalcommits's releases.
... (truncated)
Changelog
Sourced from conventional-changelog-conventionalcommits's changelog.
... (truncated)
Commits
08f44dcchore(release): monorepo release (#1442)c598bf1fix(conventional-changelog-angular,conventional-changelog-conventionalcommits...7d4a4f6chore(release): monorepo release (#1435)0d5a4a6feat(conventional-changelog-angular,conventional-changelog-atom,conventional-...44bc44dchore(release): monorepo release (#1423)441af37docs: fix libraries io linksb0721e1feat(conventional-changelog,conventional-changelog-angular,conventional-chang...b3bec6cchore(release): monorepo release (#1392)81da809feat(conventional-changelog-conventionalcommits): scope option can be an arra...93c34e4ci: eslint v9 (#1389)Maintainer changes
This version was pushed to npm by dangreen, a new releaser for conventional-changelog-conventionalcommits since your current version.