Skip to content

Specification

Author: Claude Code for jmpicnic Date: 2026-03-30 Status: Draft

Extend the printing format system with X_SMALL and SPECIAL_01 through SPECIAL_10 enum values for all three print types (card, label, breadcrumb). Update the Documint template spreadsheet, backend configuration and code in operations, and deliver a frontend specification as a GitHub ticket for arda-frontend-app.

  • Goal file: Extra Printing Options — Goal
  • Worktrees: extra-printing-options-worktrees/{operations,documentation} on branch jmpicnic/extra-printing-options-management-820
  • Spreadsheet: Printing Template Mapping (readable/writable via Google Workspace MCP)
  • Project type: Modification — extending existing enums, protobuf definitions, configuration, and CSV upload logic.
  1. Spreadsheet — New versioned sheet with X_SMALL, SPECIAL_01–10 rows, Active column, and Notes column.
  2. Backend (operations) — Domain enums, protobuf enums, protobuf-to-domain mapping, PrintingTemplateConfiguration data class, pdf-templates.json, CSV alias support.
  3. Frontend ticket — Detailed GitHub issue in Arda-cards/management assigned to danmerb specifying frontend changes for arda-frontend-app.
  • Direct modifications to arda-frontend-app (delivered as a ticket only).
  • Changes to common-module (reference dependency only).
  • Changes to breadcrumb/label/card rendering logic or Documint template design.
  • New API endpoints — existing print endpoints are unchanged.
  1. Follow the implementation-task skill for execution workflow and expected byproducts.
  2. Follow guidelines in dev-workflows.md for build and test commands.
  3. Use git -C <path> instead of cd <path> && git for cross-repo git operations.
#TaskPhaseDepends OnAcceptance Criteria
T-1Create new spreadsheet sheet with all rows and columns1: SpreadsheetNew sheet exists with 45 data rows (15 per category), Active and Notes columns populated
T-2Extend domain enums in Printing.kt2: BackendT-1CardSize, LabelSize, BreadcrumbSize each have X_SMALL, SMALL, MEDIUM, LARGE, X_LARGE, SPECIAL_01SPECIAL_10
T-3Extend protobuf enums in item_row.proto2: BackendT-1Sequential field numbers, all new values present
T-4Update protobuf → domain mapping in ItemCsvUploadService.kt2: BackendT-2, T-3All new protobuf values map to correct domain values; UNSPECIFIED still defaults to MEDIUM
T-5Add CSV aliases for Size of Template display names2: BackendT-4CSV upload accepts display names (e.g., “Quarter-Index”) as aliases for enum values
T-6Add active field to PrintingTemplateConfiguration2: BackendData class includes active: Boolean, defaults to true for backwards compatibility
T-7Update pdf-templates.json with all entries from spreadsheet2: BackendT-1, T-6All 45 entries present with correct template IDs, columns, descriptions, and active flags
T-8Verify existing data deserialization2: BackendT-2, T-7Items with SMALL/MEDIUM/LARGE/X_LARGE deserialize and print correctly
T-9Update existing tests, add tests for new values2: BackendT-2–T-7All tests pass including new enum values and CSV aliases
T-10make clean build and verify coverage2: BackendT-2–T-9Zero errors, zero test failures, code coverage meets or exceeds Gradle-configured targets
T-11Update CHANGELOG.md2: BackendT-10Entry added under new version with Added section
T-12Write frontend specification and create GitHub ticket3: Frontend TicketT-1, T-10Ticket created in Arda-cards/management, assigned to danmerb, with full acceptance criteria including npm coverage targets

Create a new sheet called Documint Template Mapping (YYYYMMDD) (where YYYYMMDD is today’s date) by populating it with the following structure.

Columns: Form, Size (Back-end names), Number of columns, Documint Template, Default, Size of Template (UI Displayed name), Template Change, Name Change, Template URL, Active, Notes

Rows per category (Label, Breadcrumb, Kanban Card): copy existing X_SMALL through X_LARGE rows from the (20260129) sheet, then add SPECIAL_01 through SPECIAL_10.

Specific transformations:

  1. For Kanban Card / SPECIAL_01: copy values from the LARGE+QR row (template 690a35cc4d1069adadcc052c, 1 column, “3 x 5 - Large QR Code”). Set Active = TRUE.
  2. For all other SPECIAL_NN rows: copy the template ID, columns, and description from the Default row of that category (the row marked with X in the Default column). Set Active = FALSE.
  3. For Breadcrumb / X_SMALL: change the display name to “3 x 1 (xs Label Printer)” (strip the “Dup-” prefix and differentiate from SMALL). Add a Notes entry: “Shares template ID with Breadcrumb/SMALL (695eb8be226280f110894177)”.
  4. Set Active = TRUE for all rows from X_SMALL through X_LARGE (all categories) and SPECIAL_01 (Kanban Card only).
  5. Set Active = FALSE for all other SPECIAL_NN rows.
  6. The LARGE+QR row is not carried forward to the new sheet (its values are in SPECIAL_01).

Verify the new sheet has the correct row count and structure before proceeding to backend changes.

Phase 2: Backend Implementation (T-2 through T-11)

Section titled “Phase 2: Backend Implementation (T-2 through T-11)”

Worktree: extra-printing-options-worktrees/operations

In src/main/kotlin/cards/arda/operations/reference/item/domain/Printing.kt, add to each enum:

@Serializable
enum class CardSize {
X_SMALL,
SMALL,
MEDIUM,
LARGE,
X_LARGE,
SPECIAL_01,
SPECIAL_02,
SPECIAL_03,
SPECIAL_04,
SPECIAL_05,
SPECIAL_06,
SPECIAL_07,
SPECIAL_08,
SPECIAL_09,
SPECIAL_10
}

Same pattern for LabelSize and BreadcrumbSize. Ordering: X_SMALL before SMALL (ascending logical size), then SPECIAL_* after X_LARGE.

In src/main/proto/cards/arda/v1/operations/reference/item/csv/v1beta1/item_row.proto, extend each enum with sequential field numbers:

enum CardSize {
CARD_SIZE_UNSPECIFIED = 0;
CARD_SIZE_SMALL = 1;
CARD_SIZE_MEDIUM = 2;
CARD_SIZE_LARGE = 3;
CARD_SIZE_EXTRA_LARGE = 4;
CARD_SIZE_EXTRA_SMALL = 5;
CARD_SIZE_SPECIAL_01 = 6;
CARD_SIZE_SPECIAL_02 = 7;
CARD_SIZE_SPECIAL_03 = 8;
CARD_SIZE_SPECIAL_04 = 9;
CARD_SIZE_SPECIAL_05 = 10;
CARD_SIZE_SPECIAL_06 = 11;
CARD_SIZE_SPECIAL_07 = 12;
CARD_SIZE_SPECIAL_08 = 13;
CARD_SIZE_SPECIAL_09 = 14;
CARD_SIZE_SPECIAL_10 = 15;
}

Same pattern for LabelSize (prefix LABEL_SIZE_) and BreadcrumbSize (prefix BREADCRUMB_SIZE_). Note: new values are appended after existing values to maintain wire compatibility.

In ItemCsvUploadService.kt, extend each mapping function. Example for CardSize:

fun CardSizeMessage.domain(): Result<CardSize> = when (this) {
CardSizeMessage.CARD_SIZE_UNSPECIFIED -> Result.success(CardSize.MEDIUM)
CardSizeMessage.CARD_SIZE_SMALL -> Result.success(CardSize.SMALL)
CardSizeMessage.CARD_SIZE_MEDIUM -> Result.success(CardSize.MEDIUM)
CardSizeMessage.CARD_SIZE_LARGE -> Result.success(CardSize.LARGE)
CardSizeMessage.CARD_SIZE_EXTRA_LARGE -> Result.success(CardSize.X_LARGE)
CardSizeMessage.CARD_SIZE_EXTRA_SMALL -> Result.success(CardSize.X_SMALL)
CardSizeMessage.CARD_SIZE_SPECIAL_01 -> Result.success(CardSize.SPECIAL_01)
// ... SPECIAL_02 through SPECIAL_10
CardSizeMessage.UNRECOGNIZED -> Result.failure(AppError.ArgumentValidation("cardSize", "unrecognized"))
}

Add alias resolution so that CSV uploads can use the Size of Template display names as values. The alias map is derived from the spreadsheet:

Label aliases:

Display NameEnum Value
3x1 (Label Printer)X_SMALL
Quarter-IndexSMALL
Half-IndexMEDIUM
3x2 (Label Printer)LARGE
Business Card StockX_LARGE

Breadcrumb aliases:

Display NameEnum Value
3 x 1 (xs Label Printer)X_SMALL
3 x 1 (Label Printer)SMALL
3 x 2 (Label Printer)MEDIUM
Business Card StockLARGE
Half-IndexX_LARGE

Card aliases:

Display NameEnum Value
Quarter-IndexX_SMALL
Half-IndexSMALL
Business Card StockMEDIUM
3 x 5LARGE
3 x 5 - Large QR CodeSPECIAL_01
4 x 6X_LARGE

T-6: Add active field to PrintingTemplateConfiguration

Section titled “T-6: Add active field to PrintingTemplateConfiguration”

In src/main/kotlin/cards/arda/operations/shopaccess/pdfrender/business/RenderJob.kt, update the data class:

@Serializable
data class PrintingTemplateConfiguration(
val template: String,
val columns: Int,
val description: String,
val active: Boolean = true
)

The default = true ensures backwards compatibility — the existing default field in pdf-templates.json is already ignored by the deserializer (ignoreUnknownKeys), and active defaults to true if absent. The active flag is stored for future use but does not affect PrintTemplates resolution logic.

Replace src/main/resources/reference/item/printing/pdf-templates.json with all 45 entries (15 per category) sourced from the new spreadsheet sheet. Each entry includes template, columns, description, default, and active.

For SPECIAL_NN rows where Active = FALSE, the template ID, columns, and description are copied from the category’s default row (the entry marked default: true).

  • Verify enumerationByName deserialization: existing database rows with SMALL/MEDIUM/LARGE/X_LARGE must load without error.
  • Run existing test suites; update test fixtures that enumerate size values.
  • make clean build must pass with zero errors.
  • Code coverage must meet or exceed the targets configured in the Gradle build scripts.

Add entry under a new version (minor bump — new enum values are additive):

## [2.20.0-jmpicnic-820] - 2026-03-30
### Added
- `X_SMALL` and `SPECIAL_01` through `SPECIAL_10` to `CardSize`, `LabelSize`, and `BreadcrumbSize` enums.
- Protobuf enum values for all new sizes.
- CSV upload alias support for `Size of Template` display names.
- `active` flag in `PrintingTemplateConfiguration` for future frontend filtering.
- 33 new entries in `pdf-templates.json`.

All backend changes must compile and pass tests before proceeding to the frontend ticket.

Worktree: extra-printing-options-worktrees/documentation (for the specification text)

Create a GitHub issue in Arda-cards/management assigned to danmerb (using the /gh-ticket skill) containing:

  1. Context: Link to this specification and the parent issue #820.
  2. Changes required:
    • Extend cardSizeOptions, labelSizeOptions, breadcrumbSizeOptions in constants.ts with all new values. Reconcile X-SMALL (hyphenated) to X_SMALL (underscore).
    • Add an enabled property to each option sourced from the spreadsheet Active column.
    • Update TypeScript union types in items.ts to include new values.
    • Update size mappers in ardaMappers.ts.
    • Encapsulate size selection in a reusable React component per UX Component Guidelines.
    • Only enabled options are selectable in dropdowns; disabled options are visible but not selectable.
    • The array of size options should be determined at design time; the specific mappings of each option should be determined at mount time.
  3. Acceptance criteria: Frontend-specific items from the goal file (criteria 15–20), including code coverage meeting or exceeding npm-configured targets.
  • Do not modify files in arda-frontend-app.
  • Do not modify files in common-module.
  • Do not change print rendering logic or Documint templates.
  • Do not change API endpoint signatures.

Two worktrees already exist at the workspace root. Both are on the same branch, targeting different repositories. This is a single-agent project — no additional worktrees needed.

Worktree directoryBranchRepositoryTasks
extra-printing-options-worktrees/operationsjmpicnic/extra-printing-options-management-820operationsT-2 through T-11
extra-printing-options-worktrees/documentationjmpicnic/extra-printing-options-management-820documentationGoal, specification, verification, T-12

Spreadsheet tasks (T-1) are executed via Google Workspace MCP and do not require a worktree.

RiskLikelihoodImpactMitigation
enumerationByName fails on new enum values for existing DB rowsLowHighNew values are additive; existing string names unchanged. Verify at T-8.
Protobuf wire compatibility broken by new field numbersLowHighNew values appended after existing ones; UNSPECIFIED = 0 unchanged.
Breadcrumb X_SMALL/SMALL alias collision in CSVResolvedDifferentiated display names: “3 x 1 (xs Label Printer)” vs “3 x 1 (Label Printer)”.
Spreadsheet MCP write fails mid-updateLowMediumCreate new sheet (don’t modify existing); can retry from scratch.
Frontend misinterprets active flag semanticsLowMediumFrontend ticket specifies exact behavior; backend stores flag but does not filter.
#QuestionOptionsRecommendationDecision
1Breadcrumb X_SMALL/SMALL share display name “3 x 1 (Label Printer)” — how to resolve?A) Alias precedence; B) Differentiate display namesB) Differentiate: X_SMALL → “3 x 1 (xs Label Printer)”, SMALL → “3 x 1 (Label Printer)“B) Differentiate
2Should active default to true or false in the data class?A) true (backwards-compatible); B) false (explicit opt-in)A) true — existing entries in JSON lack the field and should remain activetrue
3CHANGELOG version numberA) 2.20.0 (minor — additive); B) 2.19.5 (patch)A) Minor bump — new enum values are additive feature2.20.0

Copyright: (c) Arda Systems 2025-2026, All rights reserved