Skip to content

FE plan — Foundation

Pseudo-module. Foundation is not a backend module — it's the cross-cutting setup work that must land before any real BE module (item_catalog, manufacturing, …) can be implemented on the frontend. It is plan-formatted as a pseudo-module here so the existing linear-issue-creator-fe skill can consume it without adaptation.

Missing Inputs

  • [x] OpenAPI spec — not needed. Foundation does not generate any Orval hooks; per strategy decision D7 the orval.config.ts has all 7 module entries commented out and is only uncommented as each BE module ships its spec.
  • [x] Prototype HTMLs — partially. The sidebar layout has ux-tests/prototypes/action-flow-prototypes/sidebar-template.txt; the dashboard placeholder has ux-tests/prototypes/action-flow-prototypes/01-home.html for chrome reference only. No other foundation surface has or needs a prototype.
  • [x] Backend auth contract — explicitly deferred per CLAUDE.md § Authentication and authorization (deferred). Foundation scaffolds the Authorization header slot but reads an empty string; identity module fills it in. The existing vibe-coded src/routes/login.tsx and the identity locale content are deleted as part of M0 — login screen lands back with identity's first ticket against a real auth contract.
  • [x] Permission-Gated UI convention doc — landing in docs/standards.md and docs/design-system.md from a parallel session. Foundation references the convention in M4's permission-gated UI ticket and adds the required i18n keys; we do not duplicate the convention text.
  • [x] BE business doc — N/A. Foundation is fully FE-driven (system tickets, no business use cases). Every other module's fe-plan.md will cite its BE business doc here; this one does not.

Scope

In scope (foundation backlog):

  • Repo cleanup of pre-realignment legacy folders, deletion of the vibe-coded login screen + locale entries, and updates to CLAUDE.md / docs/architecture.md to reflect the dropped patterns (no features/<module>/types.ts re-export barrel; mutation side-effects via shared helper).
  • Mirror the BE's full List Endpoints convention block in FE docs/standards.md — pagination + snapshot_at, sorting (?sort=field / ?sort=-field), flat-scalar filtering with tri-state status + date-range pairs, single ?q= search, separate /stats endpoints, URL-as-source-of-truth, draft-vs-committed filter UX. Documentation only; no helper code (those lift out of the first list-page consumer in item_catalog).
  • Developer-experience baseline that mirrors CI locally: prettier, commitizen + commitlint (BE-aligned schema), husky + lint-staged, env-var Zod parsing, .env.example, PR template, and a full pnpm script suite culminating in pnpm check (one command runs format-check + lint + typecheck + tests + build).
  • Design-system primitive layer: shadcn primitives + display primitives + semantic buttons + common reusables.
  • Route registry + nav constants convention + the documenting section in docs/standards.md.
  • AppShell, fixed grouped sidebar, header, breadcrumbs, page container.
  • API/data layer plumbing: axios instance, idempotency interceptor, auth header stub, error envelope parser, ApiError type, RHF field-error mapper, TanStack Query client, useIdempotentMutation hook, and a withSideEffects(mutationOptions, { successToast, errorToast, invalidates }) helper that standardises toast + cache-invalidation wiring so per-module mutation hooks stay 5-line wrappers.
  • Forms layer: RHF + Zod field wrappers.
  • Permission-Gated UI convention: i18n keys + semantic-button requiredPermission contract (no <PermissionGate> or usePermission impl — those ship with identity).
  • Observability: OpenTelemetry SDK + OTLP exporter + W3C traceparent on fetch + global error boundary.
  • Testing conventions doc (docs/testing.md) + cross-references from CLAUDE.md and FE skills.
  • Testing harness: vitest config, RTL custom render, MSW base, sample tests.
  • CI: GitHub Actions PR-Checks workflow mirroring the BE structure — separate parallel jobs for commit-check (commitlint), format-check, lint-check, type-check, tests, build-check, plus a summary-comment job that posts a per-job ✅/❌ table on every PR. Plus a docs-check job that builds MkDocs in strict mode (added once the docs site lands in M6).
  • Deployment & release infrastructure mirroring BE: multi-stage Dockerfile (base / development / build / runtime, with nginx serving Vite's dist/), MkDocs Material docs site (same theme as BE) with mkdocs.yml + a merge-main.yml CI job that builds docs strict on PRs, and release-please configured for release-type: node with the same changelog-section taxonomy + emojis as BE, bumping package.json automatically on every merge to main.

Out of scope:

  • Authentication wiring (JWT extraction, token refresh, login flow integration) — owned by the identity module's first ticket. Foundation only scaffolds the header slot.
  • usePermission() hook implementation — owned by the identity module's first ticket. Foundation only wires the requiredPermission prop contract on semantic buttons (it's a no-op pass-through until identity flips the switch).
  • <PermissionGate> component implementation — same reason as above. The convention doc describes the contract; the component is identity's deliverable.
  • PII scrubber in the OTel UserInteractionInstrumentation — explicit TODO comment placed in src/lib/observability.ts; must land before any non-dev environment, but not gating foundation completion.
  • Shared DataTable component — lifts out of the first list-page consumer in item_catalog, not pre-built.
  • Concurrent-modification refresh banner component — lifts out of the first edit-form consumer in identity, not pre-built.
  • Source map upload to SigNoz — deferred to the first non-dev deployment.

Decisions

  • Treat foundation as a pseudo-module under docs/modules/foundation/. Reuses the existing seven-doc planner format so linear-issue-creator-fe consumes it unmodified. Alternative (cross-cutting "infra" project format) was rejected — would have required a separate skill or a hand-curated Linear project.
  • Route registry as a new convention. src/lib/routes.ts is the single source of truth for navigation metadata (path, i18n key, icon, group, optional permission). String-literal paths in components are forbidden. Documented in a new docs/standards.md § Route Registry & Navigation Constants section. Rationale: the sidebar, breadcrumb, and cross-module <Link> targets all need the same data; centralising it avoids drift and gives the sidebar a typed source for grouping.
  • Testing conventions get their own doc. New docs/testing.md — short and comprehensive, not over-engineered. Referenced from CLAUDE.md § Further reading and from the three FE skills (frontend-module-planner, implement-linear-ticket-fe, linear-issue-creator-fe). Rationale: current testing rules are scattered across CLAUDE.md § Testing and standards.md; one short authoritative doc is easier to keep current.
  • No X-Tenant-Id header. Tenant is resolved BE-side from JWT claims. The axios interceptor wires Authorization only.
  • No bespoke queryOptions wrapper layer. Components call Orval hooks directly. The useIdempotentMutation wrapper is the only abstraction over generated code, and it exists only because the idempotency-key lifecycle straddles retries.
  • Permission gating wired but inert in foundation. Semantic buttons accept requiredPermission?: Permission; usePermission() is a stub returning true always. Identity's first ticket replaces the stub. Side benefit: foundation tickets and identity tickets do not share the same files heavily, so they can land in either order without rework.
  • Two-batch shadcn primitive baseline (M1). Splits 16 primitives into two tickets (a 9-primitive batch, then a 7-primitive batch focused on form + tooltip dependencies) to keep PRs reviewable. Coarser would have produced a 16-file diff; finer would have produced 16 PRs of one file each.
  • Sidebar supports per-group collapse + a whole-sidebar rail toggle. Per user direction (updated in GRF-17). Each NavGroup section can fold to header-only with a chevron; the header rail toggle collapses the entire sidebar to an icon-only rail. Collapse state is in-memory only — active highlighting is URL-driven so it always works on refresh. The sidebar-template.txt prototype still drives the expanded visual; rail mode is an additive state.
  • CI mirrors the BE PR-Checks structure. Separate parallel jobs for commit-check, format-check, lint-check, type-check, tests, build-check, with a final summary-comment job. Each job invokes the matching pnpm script defined in M0 so the local pnpm check command is byte-for-byte the same set of checks that CI runs. Rationale: when CI fails, the dev runs pnpm check locally to reproduce — no "works on my machine, fails on CI" ambiguity from divergent toolchains. Drives the M0 hygiene ticket's script suite as a hard dependency.
  • Commitizen schema mirrors BE. Same regex ((feat|fix|docs|style|refactor|perf|test|chore|build|ci)(\([a-zA-Z0-9_,-]+\))?: [A-Z]+-\d+ .+), same prompts. Lets one dev work across FE + BE without learning two conventions, and lets cross-cutting PRs that touch both repos use identical commit headers.
  • Docs site = MkDocs Material. Same engine + theme as BE (mkdocs.yml mirrors BE config — Material theme, navigation tabs/sections/instant, mermaid via pymdownx superfences, link/nav strict validation). Source: the existing docs/ tree. No per-component or per-symbol docs (no TypeDoc) — code is self-documenting; the docs site exists for planning, architecture, standards, design-system, and per-module plans. Rationale: visual + structural consistency with the BE docs site lowers cognitive load when reading both, and the BE has already paid the toolchain cost of pinning a known-good Material theme.
  • release-please mirrors BE. Same release-please-config.json shape, same changelog-sections taxonomy and emojis (🚀 Features, 🐛 Bug Fixes, ⬆️ Dependency Updates, ⚡ Performance, 🎨 Code Style, 🧹 Maintenance, 🤖 CI/CD, 📝 Documentation, 🔧 Refactoring, 🧪 Tests hidden). Differs only in release-type: node + extra-files rewriting package.json.version. Even though only one environment exists today, having semantic versioning + an automated CHANGELOG from the very first release means deploy provenance is trivially auditable and rollback targets are obvious.
  • Docker mirrors BE multi-stage structure. Stages: base (node:22-alpine + pnpm), development (sources + pnpm dev), build (pnpm install --frozen-lockfile + pnpm build/dist), runtime (nginx:alpine serving /dist on port 80, non-root user). Matches BE's base / development / build / runtime naming and use of LABEL maintainer / VERSION envvar so the two repos look the same to ops.
  • Drop the features/<module>/types.ts re-export barrel. Components import generated schemas directly from @/generated/schemas/<module>. FE-only schemas (e.g. filter-bar Zod or local UI state) live next to the file that uses them; if reused, they get promoted to features/<module>/schemas.ts (singular intent — schemas only, not a grab-bag types file). Rationale: the barrel adds an indirection that drifts (devs forget to update it when generated content changes), and export * doesn't actually narrow the surface area. Architectural docs in CLAUDE.md § Orval workflow and docs/architecture.md directory tree updated as part of the M0 cleanup ticket to match.
  • Standardise mutation side-effects via a shared withSideEffects helper, keep per-module wrapper hooks thin. Per-module hooks (useItemMutations etc.) still exist because they own the module-specific cache keys and translation keys, but the per-hook body is a 5-line call to withSideEffects(useCreateItem, { successToast: 'item-catalog:created', invalidates: [itemKeys.lists()] }). Avoids the BE-style "every hook re-implements the same try/catch + toast + invalidate" boilerplate without going all the way to opaque TanStack Query mutationCache global handlers (those are powerful but hide what's happening from the call site). Pattern documented in data-fetching.md and shipped by the M3 useIdempotentMutation ticket.
  • Delete the existing src/routes/login.tsx + identity locale content + identity-as-always-loaded entry in src/lib/i18n.ts. Done in the M0 cleanup ticket. Identity content lands back with identity's first ticket, against a real auth contract. Until then: the unauthenticated landing page is the / dashboard placeholder (M2 AppShell ticket), which renders without an auth guard for now — auth gating wires up when identity ships.
  • Mirror BE list-endpoint conventions in FE docs/standards.md rather than just linking out to BE. The BE describes the contract (what the wire looks like); the FE doc translates each rule into how to consume it (URL state, debounce, tri-state select widget, draft-vs-committed UX, TanStack Table sort wiring). Section structure stays one-to-one with BE so cross-referencing is trivial, but the prose is FE-perspective. Rationale: every list page in every feature module hits these patterns, and FE devs read FE docs first. No helpers are built in the foundation ticket — the typed useListSearchParams<T>(schema), debounced search input, tri-state status <SelectField> variant, and column-header sort wiring all lift out of item_catalog's first list-page consumer to avoid speculative abstraction.

Milestones (proposed — Linear becomes canonical once created)

M0 — Cleanup & Developer Experience Baseline

Tickets: - Cleanup: delete stale features/ folders + locales, delete vibe-coded login screen, update CLAUDE.md + docs/architecture.md - Developer experience baseline: prettier, commitizen, husky + lint-staged, full pnpm script suite, env validation, .env.example, PR template - List-endpoint consumption conventions: mirror BE conventions.md § List Endpoints in FE docs/standards.md

Estimated: 3 tickets.

The Cleanup ticket scope is broader than just folder deletion:

  • Delete features/{batch-tracking,production,recipe,warehouse,identity}/ (pre-realignment legacy).
  • Delete src/routes/login.tsx (vibe-coded, no real auth contract).
  • Delete src/locales/{en,pl}/identity.json content (login keys only — file gets re-added empty or re-introduced by identity's first ticket).
  • Update src/lib/i18n.ts to drop identity from the always-loaded ns array (becomes lazy-loaded by identity when it ships).
  • Update CLAUDE.md:
  • § Orval workflow — drop the features/<module>/types.ts re-export example; replace with "components import generated schemas directly from @/generated/schemas/<module>".
  • § i18n quick reference — identity moves out of the "Always" row into the per-module row.
  • § Authentication and authorization (deferred) — remove any text implying a working login skeleton exists.
  • Update docs/architecture.md:
  • Directory-tree examples — remove the types.ts line from features/<module>/ example.
  • Any callouts referencing the login skeleton.
  • Update docs/standards.md § Data Fetching — add a forward reference to the withSideEffects helper documented in data-fetching.md.

The List-endpoint consumption conventions ticket is a dedicated documentation deliverable that mirrors the BE's conventions.md § List Endpoints (lines 636–842) section-for-section on the FE side, from the consumer's perspective. Scope:

  • Expand docs/standards.md § Pagination — currently a thin response-shape blurb; grows to cover request shape (?page=&page_size=, default 20, BE max 100), full envelope including the new snapshot_at field, and the snapshot_at lifecycle (preserved on next/prev/page-size; dropped on filter/sort/search/refresh).
  • Add docs/standards.md § Sorting — single-sort ?sort=field / ?sort=-field syntax (JSON:API style), allowed-fields enum (per endpoint, published in OpenAPI), INVALID_SORT_FIELD 422 handling, and the TanStack Table column-header-click-only UI convention.
  • Add docs/standards.md § Filtering — flat typed scalar params (no filter[x]= nesting), the "no nesting" hard constraint driven by Orval codegen, repeated-scalar lists (?item_type=ingredient&item_type=packaging), tri-state status convention (active/inactive/all), date-range pattern (_after/_before scalar pair, inclusive bounds), AND-only combination, the "only write non-default values to URL" rule, and the draft vs committed UX pattern (local state until [Apply filters]; search debounces directly).
  • Add docs/standards.md § Search — single ?q= param, BE-side unaccent + ILIKE (FE wire just sends q), debounce convention (300ms baseline).
  • Add docs/standards.md § Stats endpoints — pattern explanation, when a resource gets /stats, response shape variability per resource, why it's cached independently of the list query, and how the list view's "showing X of Y" reads from paginated.total while the page-level "124 items in your catalog" reads from stats.
  • Add docs/standards.md § URL as Source of Truth (lists) — every list-state value (page, page_size, sort, filters, q, snapshot_at) lives in TanStack Router search params and is validated with a per-page Zod schema via validateSearch. Component state derives from URL, never the reverse.
  • Cross-link from CLAUDE.md § Key rules — replace the existing thin pagination bullet with a one-liner that points at the new docs/standards.md sections.
  • Cross-link from foundation data-fetching.md — the long "List endpoints" section in that doc already summarises this, with ../../standards.md as the authoritative source.

The deliverable is documentation only — no helpers are built in this ticket (typed useListSearchParams<T>(schema), debounced search input, tri-state status select, etc.). Each helper lifts out of the first list-page consumer in item_catalog to avoid speculative abstraction. The conventions doc is enough for module plans to be specified correctly.

Source of truth for the BE side: grinsystem-api/docs/architecture/conventions.md § List Endpoints. Concrete worked example: grinsystem-api/docs/modules/item_catalog/api-contract.md GET /items request + response.

The "Developer experience baseline" ticket is the local mirror of CI — every check the CI workflow (M5) runs has a matching pnpm script so devs can run the full check pipeline locally with one command before pushing. Scope:

  • Prettier.prettierrc + .prettierignore aligned with the existing ESLint flat config.
  • Commitizen + commitlint — mirror the BE schema (@commitlint/cli, @commitlint/config-conventional, commitizen + cz-customizable or equivalent). Schema: <type>(<scope>): <TICKET-ID> <subject> with regex (feat|fix|docs|style|refactor|perf|test|chore|build|ci)(\([a-zA-Z0-9_,-]+\))?: [A-Z]+-\d+ .+. Ticket prefix [A-Z]+-\d+ accepts any team prefix (GFE-, GRI-, etc.) so cross-cutting commits compile cleanly.
  • Husky + lint-staged — pre-commit hook runs prettier --write + eslint --fix on staged files only. commit-msg hook runs commitlint.
  • pnpm scripts (in package.json):
Script Command Purpose
pnpm format prettier --write . Format the whole repo.
pnpm format:check prettier --check . CI-friendly format check (no writes).
pnpm lint eslint . Lint the whole repo.
pnpm lint:fix eslint . --fix Lint + auto-fix where possible.
pnpm typecheck tsc --noEmit Renamed from today's loose pnpm tsc.
pnpm test vitest Interactive watch (already exists).
pnpm test:run vitest run One-shot run for CI.
pnpm build vite build Production build (already exists).
pnpm orval orval Regenerate src/generated/ (already exists).
pnpm cz cz Interactive conventional-commit prompt.
pnpm check pnpm format:check && pnpm lint && pnpm typecheck && pnpm test:run && pnpm build Single command to run the full CI matrix locally before pushing.
  • Env validationsrc/lib/env.ts parses import.meta.env through a Zod schema at app entry. Fails loud on startup if any required VITE_* is missing/invalid. Schema covers VITE_API_BASE_URL, VITE_APP_ENV, VITE_APP_VERSION, VITE_OTEL_EXPORTER_OTLP_URL.
  • .env.example — checked in; same keys as the Zod schema with documented defaults.
  • .github/PULL_REQUEST_TEMPLATE.md — tidied; sections for summary, test plan, screenshots (where applicable).

M1 — Design System Primitives

Tickets: - shadcn primitive baseline: Button, Input, Select, Table, Tabs, Dialog, Toast, Badge, Card - shadcn primitive baseline (cont.): Separator, Label, Textarea, Checkbox, Switch, Tooltip, Popover - Display primitives + semantic buttons + common reusables

Estimated: 3 tickets.

M2 — App Shell, Layouts & Route Registry

Tickets: - Route registry + nav constants + standards.md convention - AppShell + Sidebar (grouped, per-group + rail collapse) + Header + PageContainer + Breadcrumbs

Estimated: 2 tickets.

M3 — API / Data Layer

Tickets: - Axios instance + Idempotency-Key interceptor + Authorization stub - Error envelope parser + ApiError type + field-error → RHF mapping helper - TanStack Query client + useIdempotentMutation hook + withSideEffects helper

Estimated: 3 tickets.

The third ticket carries the mutation side-effects standardisation scope: a generic helper that takes Orval mutation options + a small declarative descriptor and produces ready-to-use mutation options that handle toast + invalidation in a consistent way. Per-module wrapper hooks then call this helper instead of re-implementing the wiring per file.

M4 — Forms & Permission-Gated UI Convention

Tickets: - RHF + Zod field wrappers - Permission-Gated UI convention: i18n keys + semantic-button contract

Estimated: 2 tickets.

M5 — Observability & Testing

Tickets: - OpenTelemetry: WebTracerProvider + OTLP exporter + instrumentations + error boundary - Testing conventions doc: docs/testing.md + cross-references - Testing harness: vitest config + RTL custom render + MSW base + sample tests

Estimated: 3 tickets.

M6 — Deployment & Release Infrastructure

Tickets: - CI: GitHub Actions PR-Checks workflow (full check matrix + summary comment) - Dockerfile (multi-stage: base / development / build / runtime) - Docs site: MkDocs Material + mkdocs.yml + docs/index.md landing + docs-strict CI job - release-please: release-please-config.json + .release-please-manifest.json + merge-main.yml workflow

Estimated: 4 tickets.

CI ticket detail

Mirrors BE's .github/workflows/pr-checks.yml structure. Workflow at .github/workflows/pr-checks.yml, triggered on PR + push-to-main. Each job runs in parallel and invokes its matching pnpm script (from the M0 dev-experience ticket) so local and CI behaviour stay aligned:

Job When Command
commit-check PR only commitlint against the PR's commit range; enforces the conventional-commit schema with ticket-id prefix.
format-check PR + main pnpm format:check
lint-check PR + main pnpm lint
type-check PR + main pnpm typecheck
tests PR + main pnpm test:run
build-check PR + main pnpm build
docs-check PR + main mkdocs build --strict (depends on the MkDocs ticket in this milestone; if MkDocs ships in a later commit, the job is added in the same PR that adds mkdocs.yml)
summary PR only actions/github-script posts a comment listing each job's status with ✅/❌/➖ icons; updates the existing comment if one is already posted (mirrors BE's pattern).

Every job uses actions/setup-node + pnpm/action-setup and caches the pnpm store keyed on pnpm-lock.yaml. The docs job adds actions/setup-python + pip install mkdocs-material (cached). All check jobs run in parallel; summary depends on all of them with if: always() so it always runs even when one of the checks fails.

Dockerfile ticket detail

Mirrors BE's multi-stage structure (base / development / build / runtime), same LABEL maintainer="GrinSystem" + description=… convention, same use of an ARG VERSION baked into a /code/version file. Stages:

  • basenode:22-alpine + corepack enable && corepack prepare pnpm@<version> --activate. Sets up a non-root grinsystem user (mirrors BE) and WORKDIR /code. Pinned tool versions, no implicit latests.
  • development — sources mounted; runs pnpm dev (Vite dev server on the configured port). Used by Docker Compose for local dev if/when added.
  • buildCOPY package.json pnpm-lock.yaml /code/pnpm install --frozen-lockfileCOPY src public index.html vite.config.ts tsconfig*.json /code/pnpm build. Produces /code/dist.
  • runtimeFROM nginx:alpine. COPY --from=build /code/dist /usr/share/nginx/html. Custom nginx.conf configures SPA fallback (try_files $uri /index.html) so client-side routes work on hard refresh. Drops to non-root nginx user. Exposes port 80. ARG VERSION baked into a /code/version file (matches BE pattern).

Out of scope for this ticket (per the broader plan): no docker-compose.yml wiring — that's a deployment-environment concern that lands when there's a target environment. The Dockerfile is buildable in isolation.

Docs site (MkDocs Material) ticket detail

mkdocs.yml at repo root, structurally identical to BE's mkdocs.yml:

  • site_name: GrinSystem Frontend Documentation
  • site_description: Frontend documentation for GrinSystem, a modular-monolith ERP for food production companies.
  • repo_url: https://github.com/mcfilu/grinsystem-front (or whatever the actual remote ends up as)
  • Same theme: material block, same palette toggle (auto / light / dark), same features: set (navigation.tabs, navigation.sections, navigation.indexes, navigation.instant, content.code.copy, mermaid via pymdownx, etc.).
  • Same validation: block — nav and link checks set to warn, so docs-check catches dangling references.

Source tree: the existing docs/ directory. Nav is configured per the actual file layout (planning, architecture, design-system, standards, testing, modules/foundation, modules/). A small docs/index.md landing page lands with this ticket (one-liner per top-level area linking to the relevant page).

Explicitly out of scope: no TypeDoc, no per-component reference docs, no per-symbol API docs. The repo is small, code is self-documenting via TypeScript types + meaningful names, and the docs site exists for planning + architecture + standards + per-module plans — not as a substitute for reading the source.

Adds mkdocs-material to a docs/requirements.txt (kept separate from package.json because mkdocs is Python-tooled; matches BE).

release-please ticket detail

Three files mirroring BE:

  • release-please-config.json — same shape as BE's. include-v-in-tag: false, single . package, release-type: node (vs BE's python), changelog-path: CHANGELOG.md, identical changelog-sections taxonomy with emojis (🚀 Features, 🐛 Bug Fixes, ⬆️ Dependency Updates / Build process, ⚡ Performance, 🎨 Code Style, 🧹 Maintenance, 🤖 CI/CD, 📝 Documentation, 🔧 Refactoring, 🧪 Tests hidden). extra-files rewrites package.json.version instead of BE's pyproject.toml.project.version.
  • .release-please-manifest.json — seeded with { ".": "0.0.0" }.
  • .github/workflows/merge-main.yml — mirrors BE's. Triggers on push to main. First job: release-please-action@v4 with the config + manifest files. Outputs release_created and tag_name. Second job (gated on release_created == 'true'): build + push the Docker image with the new tag. (Image push target is left as a TODO if no registry is wired up yet — the workflow still tags the release and updates CHANGELOG.md regardless.)

Explicitly deferred (out of scope for the M6 tickets — added when there's a real consumer): - Source-map upload to SigNoz — same gating as the PII scrubber TODO in the M5 observability ticket; needed before the first non-dev env. - Bundle-size budget (size-limit, bundlewatch, or similar) — add when a regression actually hurts. - Orval drift check (assert pnpm orval is a no-op given the checked-in openapi/) — add when the first OpenAPI spec lands and we've actually seen src/generated/ drift bite us. - Visual regression (Chromatic, Percy) — not justified at current scale. - docker-compose.yml for local dev — add when team grows past 2–3 devs and the cognitive cost of running services manually starts to bite.

M7 — Responsive & PWA Strategy Docs

Tickets: - R1 — Write docs/responsive-strategy.md (source of truth: product surfaces, breakpoint tokens, three-bucket rubric, same-component rule, PWA scope, deferrals) - R2 — Update CLAUDE.md (§ Browser support, § Key rules → General, § Further reading) to reference the new strategy - R3 — Add § Responsive contracts + /floor/* placeholder to docs/architecture.md - R4 — Add responsive tokens + table↔cards pairing + dialog + touch-target + tooltip-on-touch sections to docs/design-system.md - R5 — Add § Responsive to docs/standards.md (three-bucket rubric, tablet rule, module-plan contract, implementation contract) - R6 — Update frontend-module-planner and frontend-ux-direction skill files to bake-in per-page responsive bucket tables and phone-width mockup variants

Estimated: 6 tickets.

Dependency: R1 must land first — R2 through R6 all cross-link to docs/responsive-strategy.md. They can land in parallel after R1 (each touches a different file).

This is a pure-documentation milestone. No application code is written; no pnpm tsc / pnpm test runs (no .ts or .tsx files change). The only gate is pnpm format:check. After M7 lands, GRF-113 (item-catalog responsive bucketing) is unblocked.

The motivation: today CLAUDE.md § Browser support says "Desktop-first; mobile rendering is a non-goal until a real use case appears." That use case has arrived — owners need to glance at the dashboard and quick-create invoices and orders on their phone. M7 unwinds the old policy and threads the new one through every standards doc and the two FE planning skills so every future module plan inherits the bucketing rule automatically.

Total: 26 tickets across 8 milestones.

Permissions overview

Foundation tickets do not gate themselves on user permissions — they ship system code that runs before any role-aware UI exists. The Permission-Gated UI convention ticket (M4) establishes the pattern that every future feature ticket uses. Per-feature permission rows will live in each BE module's own fe-plan.md.

Use case Allowed actors
All foundation tickets (M0–M5) All FE engineers (no in-app permission gate)

References

BE source docs: N/A — foundation is fully FE-driven, no BE business module backs it.

FE plan detail docs (this folder): - routes.md — current route tree, planned additions, route registry convention. - pages.md — system pages (login, dashboard, account stub, forbidden) + composition patterns established here. - data-fetching.md — direct Orval hooks, idempotency, error envelope, optimistic locking — canonical reference linked by feature plans. - forms.md — RHF + Zod + field wrapper layer + server error mapping. - i18n-keys.md — proposed keys for common, identity, validation namespaces (incl. errors.insufficientPermission). - prototype-refs.md — sidebar template + dashboard chrome reference.

FE foundation context: - CLAUDE.md — stack, conventions, deferred items. - ../../architecture.md — directory layout, layer model, app shell. - ../../standards.md — data fetching, idempotency, optimistic locking, error envelope, pagination, forms, i18n, observability. - ../../design-system.md — tokens, dark mode, component layers, Tailwind rules. BE conventions referenced (for idempotency / optimistic locking / error envelope / list endpoints): - grinsystem-api/docs/architecture/conventions.md — especially § List Endpoints (lines 636–842) which is mirrored in FE docs/standards.md by the M0 list-endpoint-conventions ticket. - grinsystem-api/docs/modules/item_catalog/api-contract.md — concrete GET /items request + response shape, used as the worked example throughout the FE list-endpoint sections.

Next step

Review these seven docs. When approved, invoke /linear-issue-creator-fe foundation to create the Linear project + milestones + 20 issues under the existing Grinsystem Front team (id e682216e-25ff-4f54-a051-1f39e951038c).