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.tshas 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 hasux-tests/prototypes/action-flow-prototypes/01-home.htmlfor 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 theAuthorizationheader slot but reads an empty string; identity module fills it in. The existing vibe-codedsrc/routes/login.tsxand theidentitylocale 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.mdanddocs/design-system.mdfrom 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.mdwill 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.mdto reflect the dropped patterns (nofeatures/<module>/types.tsre-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/statsendpoints, URL-as-source-of-truth, draft-vs-committed filter UX. Documentation only; no helper code (those lift out of the first list-page consumer initem_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 fullpnpmscript suite culminating inpnpm 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,
useIdempotentMutationhook, and awithSideEffects(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
requiredPermissioncontract (no<PermissionGate>orusePermissionimpl — those ship with identity). - Observability: OpenTelemetry SDK + OTLP exporter + W3C traceparent on fetch + global error boundary.
- Testing conventions doc (
docs/testing.md) + cross-references fromCLAUDE.mdand 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-checkjob 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'sdist/), MkDocs Material docs site (same theme as BE) withmkdocs.yml+ amerge-main.ymlCI job that builds docs strict on PRs, andrelease-pleaseconfigured forrelease-type: nodewith the same changelog-section taxonomy + emojis as BE, bumpingpackage.jsonautomatically 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 therequiredPermissionprop 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
DataTablecomponent — lifts out of the first list-page consumer initem_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 solinear-issue-creator-feconsumes 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.tsis 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 newdocs/standards.md § Route Registry & Navigation Constantssection. 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 fromCLAUDE.md § Further readingand from the three FE skills (frontend-module-planner,implement-linear-ticket-fe,linear-issue-creator-fe). Rationale: current testing rules are scattered acrossCLAUDE.md § Testingandstandards.md; one short authoritative doc is easier to keep current. - No
X-Tenant-Idheader. Tenant is resolved BE-side from JWT claims. The axios interceptor wiresAuthorizationonly. - No bespoke
queryOptionswrapper layer. Components call Orval hooks directly. TheuseIdempotentMutationwrapper 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 returningtruealways. 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
NavGroupsection 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. Thesidebar-template.txtprototype 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 matchingpnpmscript defined in M0 so the localpnpm checkcommand is byte-for-byte the same set of checks that CI runs. Rationale: when CI fails, the dev runspnpm checklocally 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.ymlmirrors BE config — Material theme, navigation tabs/sections/instant, mermaid via pymdownx superfences, link/nav strict validation). Source: the existingdocs/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.jsonshape, samechangelog-sectionstaxonomy and emojis (🚀 Features, 🐛 Bug Fixes, ⬆️ Dependency Updates, ⚡ Performance, 🎨 Code Style, 🧹 Maintenance, 🤖 CI/CD, 📝 Documentation, 🔧 Refactoring, 🧪 Tests hidden). Differs only inrelease-type: node+extra-filesrewritingpackage.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/diston port 80, non-root user). Matches BE'sbase / development / build / runtimenaming and use ofLABEL maintainer/VERSIONenvvar so the two repos look the same to ops. - Drop the
features/<module>/types.tsre-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 tofeatures/<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), andexport *doesn't actually narrow the surface area. Architectural docs inCLAUDE.md§ Orval workflow anddocs/architecture.mddirectory tree updated as part of the M0 cleanup ticket to match. - Standardise mutation side-effects via a shared
withSideEffectshelper, keep per-module wrapper hooks thin. Per-module hooks (useItemMutationsetc.) still exist because they own the module-specific cache keys and translation keys, but the per-hook body is a 5-line call towithSideEffects(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 QuerymutationCacheglobal handlers (those are powerful but hide what's happening from the call site). Pattern documented indata-fetching.mdand shipped by the M3useIdempotentMutationticket. - Delete the existing
src/routes/login.tsx+ identity locale content + identity-as-always-loaded entry insrc/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.mdrather 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 typeduseListSearchParams<T>(schema), debounced search input, tri-state status<SelectField>variant, and column-header sort wiring all lift out ofitem_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.jsoncontent (login keys only — file gets re-added empty or re-introduced by identity's first ticket). - Update
src/lib/i18n.tsto dropidentityfrom the always-loadednsarray (becomes lazy-loaded by identity when it ships). - Update
CLAUDE.md: - § Orval workflow — drop the
features/<module>/types.tsre-export example; replace with "components import generated schemas directly from@/generated/schemas/<module>". - § i18n quick reference —
identitymoves 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.tsline fromfeatures/<module>/example. - Any callouts referencing the login skeleton.
- Update
docs/standards.md§ Data Fetching — add a forward reference to thewithSideEffectshelper documented indata-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 newsnapshot_atfield, and thesnapshot_atlifecycle (preserved on next/prev/page-size; dropped on filter/sort/search/refresh). - Add
docs/standards.md § Sorting— single-sort?sort=field/?sort=-fieldsyntax (JSON:API style), allowed-fields enum (per endpoint, published in OpenAPI),INVALID_SORT_FIELD422 handling, and the TanStack Table column-header-click-only UI convention. - Add
docs/standards.md § Filtering— flat typed scalar params (nofilter[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/_beforescalar 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-sideunaccent+ILIKE(FE wire just sendsq), 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 frompaginated.totalwhile 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 viavalidateSearch. 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 newdocs/standards.mdsections. - Cross-link from foundation
data-fetching.md— the long "List endpoints" section in that doc already summarises this, with../../standards.mdas 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+.prettierignorealigned with the existing ESLint flat config. - Commitizen + commitlint — mirror the BE schema (
@commitlint/cli,@commitlint/config-conventional,commitizen+cz-customizableor 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 --fixon staged files only.commit-msghook runscommitlint. - 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 validation —
src/lib/env.tsparsesimport.meta.envthrough a Zod schema at app entry. Fails loud on startup if any requiredVITE_*is missing/invalid. Schema coversVITE_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:
base—node:22-alpine+corepack enable && corepack prepare pnpm@<version> --activate. Sets up a non-rootgrinsystemuser (mirrors BE) andWORKDIR /code. Pinned tool versions, no implicit latests.development— sources mounted; runspnpm dev(Vite dev server on the configured port). Used by Docker Compose for local dev if/when added.build—COPY package.json pnpm-lock.yaml /code/→pnpm install --frozen-lockfile→COPY src public index.html vite.config.ts tsconfig*.json /code/→pnpm build. Produces/code/dist.runtime—FROM nginx:alpine.COPY --from=build /code/dist /usr/share/nginx/html. Customnginx.confconfigures SPA fallback (try_files $uri /index.html) so client-side routes work on hard refresh. Drops to non-rootnginxuser. Exposes port 80.ARG VERSIONbaked into a/code/versionfile (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 Documentationsite_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: materialblock, same palette toggle (auto / light / dark), samefeatures:set (navigation.tabs,navigation.sections,navigation.indexes,navigation.instant,content.code.copy, mermaid via pymdownx, etc.). - Same
validation:block — nav and link checks set towarn, 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/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'spython),changelog-path: CHANGELOG.md, identicalchangelog-sectionstaxonomy with emojis (🚀 Features, 🐛 Bug Fixes, ⬆️ Dependency Updates / Build process, ⚡ Performance, 🎨 Code Style, 🧹 Maintenance, 🤖 CI/CD, 📝 Documentation, 🔧 Refactoring, 🧪 Tests hidden).extra-filesrewritespackage.json.versioninstead of BE'spyproject.toml.project.version..release-please-manifest.json— seeded with{ ".": "0.0.0" }..github/workflows/merge-main.yml— mirrors BE's. Triggers on push tomain. First job:release-please-action@v4with the config + manifest files. Outputsrelease_createdandtag_name. Second job (gated onrelease_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).