Skip to content

feat(printer-models): Brother PT-Series TapeRegistry with TZe and heat-shrink specs#47

Merged
strausmann merged 3 commits into
mainfrom
feat/tape-registry
May 11, 2026
Merged

feat(printer-models): Brother PT-Series TapeRegistry with TZe and heat-shrink specs#47
strausmann merged 3 commits into
mainfrom
feat/tape-registry

Conversation

@strausmann
Copy link
Copy Markdown
Owner

Summary

Phase 2 Task 2.6 from the plan. Pure lookup database for Brother PT-Series tape specifications. TapeRegistry.lookup_pt(width_mm, media_type) returns a frozen TapeSpec record with print-area geometry, byte-stride, length bounds, and cutter minimum.

Source: Brother Raster Command Reference v1.02 (PT-E550W / PT-P710BT / PT-P750W). No empirical learning — every number is manufacturer-published.

What's in this PR

backend/app/services/tape_specs_pt.py

  • @dataclass(frozen=True, slots=True) TapeSpec record matching status_block.py's convention.
  • PT_TZE_TAPES: tuple[TapeSpec, ...] — 6 entries (4, 6, 9, 12, 18, 24 mm laminated TZe).
  • PT_HS_2_1_TAPES: tuple[TapeSpec, ...] — 5 entries for heat-shrink 2:1.
  • tuple rather than list so the manufacturer tables can't be mutated at runtime (frozen records inside an immutable container).

backend/app/services/tape_registry.py

  • UnknownTapeError(Exception) for both unknown media types and unknown widths.
  • TapeRegistry.lookup_pt(width_mm, media_type) — static-method lookup:
    • LAMINATED and NON_LAMINATED both resolve via PT_TZE_TAPES (TZe-N tapes share TZe-laminated geometry).
    • HEAT_SHRINK_2_1 resolves via PT_HS_2_1_TAPES.
    • HEAT_SHRINK_3_1 and QL types raise UnknownTapeError — intentionally unregistered until Phase 2 hardware testing covers them.

backend/tests/unit/services/test_tape_registry.py

  • 5 tests: 12mm laminated geometry, 24mm pin-count, unknown-width raise, 12mm HS 2:1 pin-count, NON_LAMINATED alias-dispatch.

What's NOT in this PR

  • QL-Series die-cut label specs (deferred — depends on hardware byte-sequence verification).
  • Heat-shrink 3:1 (deferred — separate manufacturer table, not yet extracted).
  • print_area_pins == print_area_dots post-init validator (deferred — QL series may legitimately differ).
  • Splitting UnknownTapeError into media/width subclasses (premature at 11 entries).

Test plan

  • pytest -q → 43 passed (42 existing + 5 new — net +1 from spec; the spec specified 4 tests, +1 NON_LAMINATED coverage added in review).
  • pytest tests/unit/services/test_tape_registry.py -v → 5/5.
  • ruff format --check . clean.
  • ruff check . clean.
  • mypy app/ (strict) clean.

Review history (subagent-driven)

  1. Implementer e6d1df2 — initial commit.
  2. Spec compliance: ✅ — all values match the plan.
  3. Code quality: APPROVED_WITH_NITS — flagged mutable module-level lists, missing slots=True, an untested NON_LAMINATED dispatch alias.
  4. Fix commit 8cfc0e1 — tuple-typed tables, slots=True on TapeSpec, NON_LAMINATED test, dead-branch comment.

Linked plan

docs/superpowers/plans/2026-05-11-label-printer-hub.md Task 2.6 (Phase 2 — Plugin Foundation).

strausmann and others added 2 commits May 11, 2026 07:35
…t-shrink specs

Adds a pure-data lookup module (tape_specs_pt.py) with manufacturer-published
print-area tables for TZe laminated/non-laminated tapes (4–24 mm) and
heat-shrink 2:1 tapes (6–24 mm), and a static TapeRegistry.lookup_pt()
that maps (width_mm, MediaType) → TapeSpec or raises UnknownTapeError.

HS 3:1 and QL-Series die-cut labels are explicitly out of scope pending
Phase 2 hardware tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ON_LAMINATED test

- Change PT_TZE_TAPES and PT_HS_2_1_TAPES from list[TapeSpec] to
  tuple[TapeSpec, ...] — module-level manufacturer constants must not be
  mutable at runtime; tuple makes that intent explicit and enforced.
- Add slots=True to @DataClass(frozen=True) on TapeSpec to match the
  project convention established in status_block.py (line 167).
- Document the unregistered catch-all branch in tape_registry.py so a
  future contributor adding HEAT_SHRINK_3_1 or QL-Series support knows
  exactly where to wire it in.
- Add test_lookup_pt_series_non_laminated_uses_tze_table to cover the
  NON_LAMINATED dispatch path and pin the intentional aliasing behaviour:
  a NON_LAMINATED query returns a LAMINATED-typed spec because both share
  the same TZe geometry table.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 11, 2026 07:40
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request establishes the foundational lookup mechanism for Brother PT-Series printer tape specifications. By centralizing manufacturer-provided geometry and constraints into an immutable registry, the system ensures consistent and reliable printer configuration. The changes focus on providing a clean, type-safe interface for retrieving tape-specific parameters while maintaining strict adherence to documented hardware specs.

Highlights

  • Tape Specification Registry: Introduced a new TapeRegistry service to look up Brother PT-Series tape specifications based on physical width and media type.
  • Data Modeling: Added a frozen TapeSpec dataclass with slots=True to represent immutable tape geometry, print-area, and cutter constraints.
  • Manufacturer Data Integration: Hardcoded manufacturer-published specifications for TZe laminated tapes and 2:1 heat-shrink tubing into immutable tuple-based tables.
  • Error Handling: Implemented UnknownTapeError to handle unsupported media types or invalid width lookups gracefully.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds a tape registry and specifications for Brother PT-Series printers, covering TZe and heat-shrink tapes. The review identifies architectural violations of Rule 17, recommending that model-specific data and logic be moved to the appropriate plugin directory. It also suggests using dataclasses.replace to ensure the TapeSpec identity matches the requested media_type and updating the unit tests accordingly.

Comment thread backend/app/services/tape_specs_pt.py Outdated
Comment on lines +1 to +5
"""Tape specifications for Brother PT-Series printers (180 DPI, 128-pin head).

Source: Brother Raster Command Reference (PT-E550W / PT-P710BT / PT-P750W) v1.02.
The numbers here come straight from the manufacturer's printable-area tables.
"""
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This file contains model-specific data and specifications for the PT-Series. According to the Repository Style Guide (Rule 17), model-specific code must reside in app/printer_models/<series>.py.

Consider moving the PT_TZE_TAPES and PT_HS_2_1_TAPES tuples to app/printer_models/pt.py. The TapeSpec dataclass definition itself could move to a more central location (like app/services/tape_registry.py or a new app/models/tape.py) if it is intended to be shared with other series like QL.

References
  1. Model-specific code must be located within the appropriate plugin file in app/printer_models/. (link)

Comment thread backend/app/services/tape_registry.py Outdated
Comment on lines +7 to +14
from __future__ import annotations

from app.services.status_block import MediaType
from app.services.tape_specs_pt import (
PT_HS_2_1_TAPES,
PT_TZE_TAPES,
TapeSpec,
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Add import dataclasses to support returning a spec with the correct media_type identity for aliased types like NON_LAMINATED.

Suggested change
from __future__ import annotations
from app.services.status_block import MediaType
from app.services.tape_specs_pt import (
PT_HS_2_1_TAPES,
PT_TZE_TAPES,
TapeSpec,
)
from __future__ import annotations
import dataclasses
from app.services.status_block import MediaType
from app.services.tape_specs_pt import (
PT_HS_2_1_TAPES,
PT_TZE_TAPES,
TapeSpec,
)

Comment on lines +23 to +37
def lookup_pt(width_mm: int, media_type: MediaType) -> TapeSpec:
"""Return the PT-Series tape spec for the given width + media type.

Non-laminated TZe-N tapes share TZe laminated dimensions, so both
MediaType.LAMINATED and MediaType.NON_LAMINATED resolve to the same
PT_TZE_TAPES table.
"""
if media_type in (MediaType.LAMINATED, MediaType.NON_LAMINATED):
table = PT_TZE_TAPES
elif media_type == MediaType.HEAT_SHRINK_2_1:
table = PT_HS_2_1_TAPES
else:
# MediaType.HEAT_SHRINK_3_1 and QL-Series types are intentionally
# unregistered — add a new table and branch here when supported.
raise UnknownTapeError(f"No PT-Series tape table for media_type={media_type.name}")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The lookup_pt method contains hardcoded logic mapping MediaType to PT-specific tables. Per Rule 17 of the Repository Style Guide, this model-specific logic should reside in the PT plugin. Consider refactoring TapeRegistry to be a generic dispatcher that delegates to series-specific plugins, keeping the core registry decoupled from specific hardware details.

References
  1. Model-specific code must be located within the appropriate plugin file in app/printer_models/. (link)

Comment on lines +39 to +41
for spec in table:
if spec.width_mm == width_mm:
return spec
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

When media_type is MediaType.NON_LAMINATED, the registry currently returns a TapeSpec where media_type is LAMINATED. This identity mismatch can be confusing for downstream consumers. Use dataclasses.replace to ensure the returned object reflects the requested media_type.

Suggested change
for spec in table:
if spec.width_mm == width_mm:
return spec
for spec in table:
if spec.width_mm == width_mm:
return dataclasses.replace(spec, media_type=media_type) if spec.media_type != media_type else spec

"""MediaType.NON_LAMINATED resolves via the TZe table (same geometry)."""
spec = TapeRegistry.lookup_pt(width_mm=12, media_type=MediaType.NON_LAMINATED)
assert spec.print_area_pins == 70 # same as 12mm laminated TZe
assert spec.media_type == MediaType.LAMINATED # returned spec is the laminated record
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This assertion codifies the current identity mismatch where a NON_LAMINATED lookup returns a LAMINATED spec. If the registry is updated to return a spec with the correct media_type, this test should be updated to expect MediaType.NON_LAMINATED.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a PT-Series tape specification “lookup database” for Brother media widths/types, exposing an immutable TapeSpec record and a TapeRegistry.lookup_pt() API, with unit tests to validate a few key geometry values and error cases.

Changes:

  • Introduces TapeSpec plus immutable PT-Series spec tables for TZe and heat-shrink 2:1 media.
  • Adds TapeRegistry.lookup_pt(width_mm, media_type) and UnknownTapeError for unsupported media/width combinations.
  • Adds unit tests covering common lookups, aliasing of NON_LAMINATED, and unknown-width failures.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
backend/app/services/tape_specs_pt.py Adds frozen TapeSpec dataclass and PT-Series tape spec tables (TZe + heat shrink 2:1).
backend/app/services/tape_registry.py Adds registry lookup API and error type for mapping (width, media_type)TapeSpec.
backend/tests/unit/services/test_tape_registry.py Adds unit tests for PT-Series tape spec lookup behavior and error handling.

Comment thread backend/app/services/tape_registry.py Outdated
"""Lookup of Brother tape specifications by physical width + media type.

QL-Series die-cut labels will be added once Phase 2 hardware tests confirm the
status-block byte sequence — see `docs/superpowers/plans/2026-05-11-label-printer-hub.md`.
Comment on lines +24 to +33
"""Return the PT-Series tape spec for the given width + media type.

Non-laminated TZe-N tapes share TZe laminated dimensions, so both
MediaType.LAMINATED and MediaType.NON_LAMINATED resolve to the same
PT_TZE_TAPES table.
"""
if media_type in (MediaType.LAMINATED, MediaType.NON_LAMINATED):
table = PT_TZE_TAPES
elif media_type == MediaType.HEAT_SHRINK_2_1:
table = PT_HS_2_1_TAPES
…ix media_type identity, drop private doc link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@strausmann strausmann merged commit 7526019 into main May 11, 2026
9 checks passed
github-actions Bot pushed a commit that referenced this pull request May 12, 2026
## 0.3.0 (2026-05-12)

* feat(config): pydantic-settings module with env-driven runtime configuration (#45) ([878e9e0](878e9e0)), closes [#45](#45)
* feat(integrations): AppLookupService aggregator — Phase 3 complete (#53) ([222bef4](222bef4)), closes [#53](#53)
* feat(integrations): Grocy + Spoolman lookup clients with shared NotFoundError base (#52) ([b1c9c3c](b1c9c3c)), closes [#52](#52)
* feat(integrations): LabelData schema + Snipe-IT lookup client (#51) ([3bc180f](3bc180f)), closes [#51](#51)
* feat(label-renderer): Template schema + Pillow/qrcode renderer for 1-bit label bitmaps (#54) ([fb77028](fb77028)), closes [#54](#54)
* feat(printer-models): Brother PT-Series TapeRegistry with TZe and heat-shrink specs (#47) ([7526019](7526019)), closes [#47](#47)
* feat(printer-models): Job lifecycle FSM with explicit state machine (#49) ([1a8c40e](1a8c40e)), closes [#49](#49)
* feat(printer-models): PrinterModel Protocol + ModelRegistry for plugin discovery (#48) ([2ae0e09](2ae0e09)), closes [#48](#48)
* feat(printer-models): PrintQueue worker with pause/resume/cancel/retry (#50) ([dfdf6fe](dfdf6fe)), closes [#50](#50)

[skip ci]
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.

2 participants