Working directory: apps/next_pms. Everything below assumes this path.
Active work: next-pms module redesign (runs on the feat/redesign integration branch — see §5 for branching rules).
This is a blocking workflow. Every task — FE, BE, or mixed — must follow steps 1 → 2 → 3 → 4 in order. No shortcuts, no exceptions.
- Do not write, edit, or create any file until step 1 is complete AND the user has explicitly approved the plan in chat.
- Do not mark a task complete until step 2's browser-check subagent reports green for every section AND the full-page final check passes.
- Do not re-implement any UI primitive until step 3's Storybook lookup is done and recorded in the plan.
- Do not merge / hand off code that fails step 4's DRY/KISS/SOLID checklist.
If any prerequisite is missing (Storybook unreachable, browser MCP not connected, Figma frame inaccessible, issue unclear), STOP and ask the user — do not proceed on assumptions.
If the user requests a quick change that would skip any step, call it out and ask for explicit confirmation to deviate. A user saying "just do it" in a single message is not standing authorization to bypass this flow for future tasks.
The goal: plan first, build section-by-section with live browser feedback, reuse the design system, keep the code tight.
Before editing any file, produce a detailed, drilled-down plan and post it for manual review. Do not start coding until the user approves.
Where the plan lives: save the plan as plan_issue_<issue_number>.md at the repo root (e.g. apps/next_pms/plan_issue_1194.md) in addition to posting a summary in chat. One plan file per issue — no overwriting across tasks, since the filename is keyed on the issue number. For ad-hoc work without an issue number, use plan_<short-slug>.md. Never commit plan files — they are working docs for human review. .gitignore already covers plan.md and plan_issue_*.md / plan_*.md; verify git status does not list the plan file as staged before any commit.
The plan must contain:
- Issue summary — 2–3 lines restating what's being built, with a link to the issue/Figma.
- Scope + out-of-scope — explicit list of what this task will and will not change.
- Section breakdown — for any non-trivial page, split the page into sections (e.g. Header / Breadcrumb / View switcher / Toolbar / Table body / Row actions / Empty state / Loading state) and write a sub-plan for each. This lets us iterate section-by-section in step 2.
- Routes / nav / store changes — new routes, sidebar entries, global-search entries, context/store slices being added.
- Component inventory — for each UI piece, list the Storybook component ID you intend to reuse (from
mcp__storybook__*). Mark each as REUSE (found in Storybook/design-system/frappe-ui-react) or NEW (needs to be created). - New components requiring human review — a clearly-flagged list. If a required component isn't in Storybook/design-system/frappe-ui-react, call it out here with a proposed API (props, variants, file location) and wait for approval before creating it.
- Data/API dependencies — which whitelisted endpoints or frappe-ui-react resources this page calls; flag any BE work that must land first.
- Acceptance criteria mapping — map each acceptance-criteria checkbox from the issue to the section(s) that satisfy it, so nothing is missed.
- Test plan — what will be verified in the browser (golden path + key edge cases), and whether Playwright/visual tests need updating.
Use the TaskCreate tool to mirror the plan as trackable subtasks once approved.
After plan approval, implement one section at a time. For each section:
- Main agent writes the code for that section (FE or BE), reusing components per §3, respecting principles in §4.
- Rebuild the frontend (see
## Build & dev flow) — typicallycd apps/next_pms/frontend && npm run build:appinsidefm shell. - Spawn a browser-check subagent using the Agent tool (
subagent_type: general-purpose) with the Claude-in-Chrome MCP tools available. Give it:- The section's acceptance criteria.
- The exact route to navigate (e.g.
https://pms-temp.frappe.rt.gw/next-pms/project-list). - The Figma frame/screenshot reference to compare against.
- A short list of things to verify (layout matches Figma, interactive states work, no console errors, correct network calls fired).
- Instructions to return a structured report: what passed, what failed, screenshots, console errors, network errors.
- Act on feedback — fix issues the subagent surfaces before moving to the next section. The main agent keeps the feature context; the subagent only verifies.
- Mark the subtask completed (TaskUpdate) when the subagent reports green and acceptance criteria are met for that section.
This split keeps the main agent's context clean (no giant screenshots / DOM dumps) and forces every section to be visually verified, not just type-checked.
The storybook MCP is connected at http://localhost:6006/mcp (tools: mcp__storybook__*). The frappe-ui-react submodule + frontend/packages/design-system/ are the component libraries. Reuse, don't re-invent.
Before writing any component-like JSX:
- Call
mcp__storybook__list-all-documentationonce per task to see what exists. - Call
mcp__storybook__get-documentationfor each component you plan to use — read props, variants, examples. - If a story exists for the exact pattern you need, use
mcp__storybook__get-documentation-for-storyto see the variant. - Import and compose — do not re-implement a component that already exists.
- After FE changes, call
mcp__storybook__preview-storiesand include every returned preview URL in your update to the user. - Run
mcp__storybook__run-story-testsafter changes; fix failures before reporting success.
If and only if a needed component is genuinely absent from Storybook / frappe-ui-react / design-system:
- Do not silently create it. Add it to the plan's "New components requiring human review" list with:
- Proposed name, file location (
frontend/packages/design-system/src/...orfrappe-ui-react/packages/frappe-ui-react/src/...), props, variants, and why none of the existing components fit.
- Proposed name, file location (
- Wait for user approval, then build it with a matching story under Storybook so it's discoverable next time.
- Prefer adding to
design-system/for next-pms-specific primitives; add tofrappe-ui-react/only if it's genuinely reusable across rtCamp apps (changes to that submodule need cross-repo coordination).
Every change must hold up to this checklist before you call a section done:
- DRY — no copy-pasted component logic. Shared styles → Tailwind tokens /
cn(); shared behavior → hooks infrontend/packages/hooks/; shared UI →design-system/orfrappe-ui-react/. - KISS — the simplest thing that satisfies the acceptance criteria. No speculative abstractions, no "future-proof" layers, no flags for states that don't exist yet.
- SOLID:
- Single responsibility — one component = one visual/behavioral concern. Extract when a component handles two.
- Open/closed — expose props/slots for variation instead of forking components.
- Liskov — subcomponents honor the parent's contract (don't change prop meaning).
- Interface segregation — tight, purpose-specific props; no catch-all
configbag. - Dependency inversion — pages depend on hooks/interfaces, not on concrete API client internals; inject via props/context where it matters.
- No silly mistakes — before handing off a section:
- Run the type check / build (
npm run build:app) — must pass. - Open the browser via the subagent — no red console errors, no 4xx/5xx network calls the page didn't expect.
- Re-read the diff yourself; verify no
console.log, no dead commented code, no TODOs left unannotated. - Check imports — nothing unused, nothing duplicated.
- Run the type check / build (
- Comments — follow the project default: don't write them unless the why is non-obvious. Named identifiers document the what.
Only report a task complete when every section's acceptance criteria are green and the subagent's final full-page check passes.
Hard rules. Never open a PR that violates any of these:
- Base branch is always
feat/redesign. Never open PRs againstmainfor this work. The redesign lands onmainin larger drops; individual task PRs go tofeat/redesign. - Head branch is a feature trunk named
claude/feat/<trunk-name>. One trunk per feature/task (e.g.claude/feat/projects-page,claude/feat/timesheet-filters). Cut the trunk off the latestupstream/feat/redesign. Do not pile unrelated work onto a catch-all branch likeclaude/redesign. - One PR per feature — do not split into stacked/sequential PRs. Deliver the complete feature in a single PR on its trunk branch. Plan phases (
plan_issue_<n>.mdsections S1, S2, …) are for implementation ordering and section-by-section browser checks, not for PR boundaries. If work remains after an initial push, add commits to the same branch and the same PR — update the PR title/description if the scope shifts. - Always add
ayushnirwalas a reviewer ongh pr create(--reviewer ayushnirwal). - If the base advances mid-flight, rebase — don't merge. Run
git rebase upstream/feat/redesignandgit push --force-with-lease. Never mergefeat/redesignback into the PR branch; that pollutes the diff. - Before running
gh pr create: confirmgit diff --stat upstream/feat/redesign..HEADshows only the files this PR is supposed to touch. If a stale base has padded the diff, rebase first. - Stacked PRs are allowed when a new feature depends on another feature's in-flight branch — and only in that case. Rule #3 still holds (don't split one feature across stacked PRs). If feature A needs a placeholder/component that lives on feature B's not-yet-merged branch, base A on
claude/feat/<B-trunk>instead offeat/redesignso A's diff stays scoped to its own files. Document the stacking in the PR body, and rebase A ontofeat/redesignthe moment B merges (git rebase upstream/feat/redesign && git push --force-with-lease). Example: PR #1264 (project-detail-skeleton) was stacked onclaude/feat/projects-list(PR #1220) so the detail-page diff didn't include PR #1220's list files.
The repo has AI review bots (e.g. CodeRabbit) plus GitHub Actions CI. Both post results a few minutes after a PR opens. Whenever you run gh pr create, follow this protocol before declaring the PR "ready":
- Sleep ~10 minutes after PR creation so the AI bots and CI jobs finish. (Use a background Bash sleep or equivalent — do not poll in a tight loop.)
- Pull the full PR state:
- Reviews & summary comments:
gh pr view <pr> --json reviews,comments - Inline file comments:
gh api repos/:owner/:repo/pulls/:num/comments - CI checks:
gh pr checks <pr>(orgh pr view <pr> --json statusCheckRollup)
- Reviews & summary comments:
- Triage every signal — don't silently ignore anything:
- AI review comment, valid — fix locally, commit on the same branch, push. The bot usually resolves the thread automatically once the offending code is gone.
- AI review comment, not valid — reply on the thread explaining why the current implementation is correct. Cite the acceptance criteria, the plan, the Figma frame, or the architectural reason. Never silently ignore an AI comment.
- Failing CI check caused by the PR's diff — treat as a blocker. Read the failing job log (
gh run view <run-id> --log-failed), reproduce locally if needed, fix, commit, push. Do not merge or hand off until CI is green for changes the PR introduced. To decide whether a failure is PR-caused, compare the failing file paths / test names / line numbers against the PR's own diff — if the failure touches code this PR changed (or its direct dependents), it's PR-caused. - Failing CI check unrelated to the PR's diff (pre-existing breakage, infra flake, unrelated config, missing secret) — leave a comment on the PR explicitly noting the failure is pre-existing and not introduced by this PR, with a short reason / link to the failing job. This tells the human reviewer it's safe to ignore. Do not attempt to fix unrelated CI issues in the same PR.
- Only after every AI thread has been addressed or answered AND all PR-related CI failures are resolved should you notify the human reviewer that the PR is ready.
CI checks to ignore for now — the following workflows/jobs are explicitly out-of-scope for the redesign work. Do not try to fix their failures, do not block a PR on them, and do not leave long-winded "pre-existing" comments about them — a one-liner pointing at this list is enough:
| Workflow file | Display name | Job(s) | Why ignored |
|---|---|---|---|
.github/workflows/e2e-playwright-test.yml |
Playwright Tests | get-branch, deploy, test |
End-to-end Playwright suite against a deployed preview site. Triggered on pull_request_review / schedule. Not part of the redesign scope; URL / data assumptions in the tests often lag the redesign branch. |
.github/workflows/visual-test-playwright.yml |
Visual Regression Tests | run-visual-tests (Run Visual Tests) |
Playwright-based visual snapshot regression. Baselines are captured against pre-redesign UI, so every redesign PR is expected to show diffs until the baselines are refreshed post-redesign. |
.github/workflows/linter.yml |
Linters | pre-commit (Frappe Linter) — semgrep findings ONLY |
Only the Sempgrep: frappe hook is out-of-scope: it runs semgrep security rules over the whole repo (~70+ pre-existing findings on feat/redesign in next_pms/**/*.py), which are backend security items with their own hardening cleanup. The rest of this workflow is NOT ignorable — see the warning below. |
⚠️ Frappe Linter is only partially ignorable. The samepre-commitjob also runs JS/TS Formatter (prettier), ESLint React, and ESLint JS against the PR's own changed files. These are NOT covered byFrontend Build Test(which only runsvite/tsc— no prettier, no eslint), so a green build does not mean the formatter/linter will pass. When the Frappe Linter check is red, open the log (gh run view <run-id> --log-failed) and split it: anyJS/TS Formatter/ESLint *failure on a file this PR changed is PR-caused → fix it and re-push; only theSempgrep: frappePython findings are the pre-existing, out-of-scope part. Do not blanket-dismiss the whole check.Prevent it: there is no installed
.git/hooks/pre-commit, sogit commitskips pre-commit entirely (silently — not via--no-verify). Before committing any.ts/.tsx/.js/.jsx, run prettier on the changed files exactly as CI does (defaults → 80-col): insidefm shell,cd apps/next_pms && npx --yes prettier@4.0.0-alpha.8 --write <changed files>, then stage. (See thenext-pms-conventionsskill.)
Everything outside that table still goes through the normal PR-caused-vs-pre-existing triage in step 3 (e.g. Frontend Build Test, Bench-Build-Test, CodeQL, Vulnerable Dependency Check, WIP).
Every code-level convention lives in .claude/skills/next-pms-conventions/SKILL.md (≈28 KB, auto-loaded by description match for any code work in this repo, and loaded explicitly by /next-pms-task). That skill is the single source of truth — do not maintain a parallel copy here. It contains:
- Pre-implementation scan (≈2 min, mandatory before writing any new component / cell / helper / styling override) — utilities check against
lib/utils.ts, cva variants, design-system primitives, in-flight upstream PRs, one-component-per-file +cells/subfolder rule, file-extension rule, route completeness. - Comment discipline — default to zero comments; rationale belongs in commit messages and PR bodies, not files.
- Page file layout — folder follows URL segment; per-folder
constants.ts+types.ts;index.tsxfor main component; per-cell files; camelCase file names, kebab-case folder names;.tsfor no-JSX files. - UI details — design-system Tailwind tokens (extend
global.css@themeblock when a stop is missing); cva co-located with its component; Tailwind v4 trailing!modifier;text-base+truncateon cell text;@rtcamp/frappe-ui-react/icons(SolidDotLg,SolidStatus) before hand-rolling SVGs;<Button>icon props take aComponentType, not a rendered element. - Reading Figma — drill to the leaf node; never call metadata on file root
0:1; substitute the rtBot-accessible Copy fileKeyh1EnhdK8swe6FCyxUW1XHx. - Formatting + utility reuse —
date-fnsfor dates; checklib/utils.tsfirst; no helper infra for fake/placeholder data. - Interaction patterns — per-cell click handlers (not
onRowClick);Button variant="ghost"for inside-SPA targets;<base>/desk/user/<email>for employee cells; complete the adjacent route in the same PR. - Workaround discipline — ping the maintainer before a workaround grows defensive; check upstream PRs first.
- Reading review comments — path anchor wins over body text; reviewer fix commits trump earlier comments (always
git fetchbefore a round of fixes). - Review-round retrospectives for PR #1208, #1212, #1220.
The auto-memory index at ~/.claude/projects/<project-slug>/memory/MEMORY.md mirrors the highest-leverage rules as standalone entries. The <project-slug> is the working-directory path with slashes turned into dashes — Claude Code resolves it automatically; no need to hardcode it.
next-pms-task—/next-pms-task <github-issue-url>. End-to-end pipeline: fetch issue, write plan, post Slack thread in#bots-ai-workflow-next-pms(channelC0AUXBY5WMB), gate sections onRESOLVED, open PR per §5–§6. Loadsnext-pms-conventionsautomatically. Start here for any task link — don't hand-roll the workflow.next-pms-conventions— load FIRST for any manual coding task (tasks not entered via/next-pms-task, which handles this automatically). The full reference described above.next-pms-slack-poll—/next-pms-slack-poll <thread_ts>. Read-only 15-min poll of the task's Slack thread; surfaces gate state (WAITING/RESOLVED/BLOCK) and free-form maintainer replies. Armed by/next-pms-taskstep 6.7a.next-pms-pr-poll—/next-pms-pr-poll <pr_number> <thread_ts>. Write-capable 15-min poll of PR inline comments; runs the auto-fix-and-notify-Slack cycle when unaddressed comments exist. Armed by/next-pms-taskstep 6.7b in parallel with the slack-poll cron. Both crons self-cancel on terminal Slack state.next-pms-e2e— load FIRST for anything touching the Playwright suite intests/e2e/, including a one-line selector fix. Architecture map, theTC<n>:-driven seeding contract, post-redesign DOM contract, failure playbook, plusscripts/probe.mjs(inspect the live app in ~1s instead of paying 5–7 min of seeding),scripts/results.py(parseresults.json),scripts/cleanup.mjs(delete strays; handles the Frappe CSRF handshake). The suite's failure modes —slowMo: 500against a 30s default timeout, auth state cached with no validity check,disabledfields that makefill()hang — are not visible from the code in front of you, so reasoning from first principles reliably produces a wrong theory.react-agents-review— run before closing any FE section (step 4). Rules of Hooks, stale closures, missing deps, a11y, TypeScript safety.advanced-react-patterns— consult when a section addsuseMemo/useCallback/React.memoor global state.webapp-testing— generic upstream Playwright toolkit. For this repo's own e2e suite prefernext-pms-e2eabove; this one is for ad-hoc local web-app driving (and is NOT the step-2 live browser-check, which uses Claude-in-Chrome MCP).
Provenance and refresh instructions: .claude/skills/README.md.
This bench is managed by Frappe Manager (fm), a Docker-Compose based CLI.
Docs: https://opensource.rtcamp.com/Frappe-Manager/dev/ (Cloudflare-gated — open in browser).
- Bench name:
pms-temp.frappe.rt.gw(also the site name) - Bench root on host:
/home/frappe/frappe/sites/pms-temp.frappe.rt.gw/ - Inside containers, the bench root is mounted at
/workspace/frappe-bench/(the host's./workspace→ container/workspace). - Python:
>=3.14,<3.15· Node:>=24·use_uv = true·developer_mode = true(frombench_config.toml). - fm version: 0.19.0.
| Service | Container name | Purpose |
|---|---|---|
frappe |
fm__pms-temp_frappe_rt_gw__frappe |
Main backend (bench, gunicorn) |
nginx |
fm__pms-temp_frappe_rt_gw__nginx |
Web entrypoint |
socketio |
fm__pms-temp_frappe_rt_gw__socketio |
Realtime |
schedule |
fm__pms-temp_frappe_rt_gw__schedule |
Scheduler + workers |
redis-cache |
fm__pms-temp_frappe_rt_gw__redis-cache |
Cache |
redis-queue |
fm__pms-temp_frappe_rt_gw__redis-queue |
Queue |
# Shell into the frappe container as the frappe user (default service = frappe)
fm shell pms-temp.frappe.rt.gw
# Run a one-shot command inside the frappe container
fm shell pms-temp.frappe.rt.gw -c "bench --version"
fm shell pms-temp.frappe.rt.gw -- bench --site pms-temp.frappe.rt.gw migrate
# Run multi-line commands via heredoc
fm shell pms-temp.frappe.rt.gw <<'EOF'
cd apps/next_pms
npm run build
EOF
# Shell as root, or into a different service
fm shell pms-temp.frappe.rt.gw --user root
fm shell pms-temp.frappe.rt.gw --service nginx --user nginx
# IPython console with frappe context for this site
fm shell pms-temp.frappe.rt.gw --bench-console --site pms-temp.frappe.rt.gw
# Logs (default service = frappe); -f to follow
fm logs pms-temp.frappe.rt.gw -f
fm logs pms-temp.frappe.rt.gw --service nginx -f
# Restart services (default: web + workers via supervisor, no container restart)
fm restart pms-temp.frappe.rt.gw # web + workers
fm restart pms-temp.frappe.rt.gw --web --no-workers
fm restart pms-temp.frappe.rt.gw --nginx
fm restart pms-temp.frappe.rt.gw --container # full container restart
fm restart pms-temp.frappe.rt.gw --supervisor # faster, processes only
# Other
fm list # list benches
fm info pms-temp.frappe.rt.gw # bench details
fm start|stop pms-temp.frappe.rt.gwPassthrough -- runs args inside the container as given; -c takes a single string; stdin works for scripts (incl. Python via --bench-console).
apps/next_pms/
├── next_pms/ # Python / Frappe module (hooks.py, doctypes, api, www, ...)
│ ├── hooks.py
│ ├── api/
│ ├── timesheet/ # timesheet feature
│ ├── resource_management/
│ ├── project_currency/
│ ├── public/ # built frontend assets are served from here
│ ├── www/
│ └── patches.txt
├── frontend/ # React app (npm workspaces, Vite 6, React 19, Tailwind 4)
│ ├── package.json # workspace root: name "next-pms"
│ ├── vite.config.ts
│ └── packages/
│ ├── app/ # @next-pms/app — main SPA (src/, index.html)
│ ├── design-system/ # shared UI primitives (shadcn-style)
│ └── hooks/ # shared hooks
├── frappe-ui-react/ # git submodule — shared rtCamp UI kit (pnpm workspace)
│ │ # submodule URL: rtCamp/frappe-ui-react, branch develop
│ └── packages/frappe-ui-react/
├── tests/ # Playwright e2e + visual tests
├── package.json # top-level scripts (build runs submodule + frontend)
├── pyproject.toml
└── .gitmodules
Always run these inside the frappe container via fm shell (host may not have matching Node/pnpm/uv).
# Dev (Vite dev server on the app package)
fm shell pms-temp.frappe.rt.gw <<'EOF'
cd apps/next_pms && npm run dev
EOF
# Full build (submodule update + frappe-ui-react + frontend)
fm shell pms-temp.frappe.rt.gw <<'EOF'
cd apps/next_pms && npm run build
EOF
# Frontend-only rebuild (skip submodule/ui-react if unchanged)
fm shell pms-temp.frappe.rt.gw <<'EOF'
cd apps/next_pms/frontend && npm install && npm run build:app
EOF
# After touching Python / hooks / doctypes
fm shell pms-temp.frappe.rt.gw -- bench --site pms-temp.frappe.rt.gw migrate
fm restart pms-temp.frappe.rt.gwTop-level package.json build script = git submodule update --init --recursive && pnpm (frappe-ui-react) install + build && npm (frontend) install + build. Per README: "If using frappe-manager, you might require fm restart to provision the worker queues."
When you bump the frappe-ui-react submodule SHA, also run pnpm --filter @rtcamp/frappe-ui-react build (or cd apps/next_pms && npm run build for the full chain) before trusting npm run build:app. build:app consumes frappe-ui-react/packages/frappe-ui-react/dist/ as-is — if your local dist/ is stale from a previous bump, the app build can pass locally while CI (which rebuilds ui-react from source) fails. Symptom: CI errors like "<Name>" is not exported by .../frappe-ui-react/dist/index.js when the source clearly has the export. Fix: fresh pnpm build in the submodule package, then re-run the app build, then push. (PR #1264 lesson: cost a bump → revert → re-bump cycle.)
- Canonical file (
rtBothas access):h1EnhdK8swe6FCyxUW1XHx— "Frappe-PMS--Copy-" URL: https://www.figma.com/design/h1EnhdK8swe6FCyxUW1XHx/Frappe-PMS--Copy-?t=GcilHC5EC5w1H0xO-0 - Issue bodies sometimes link to the original file
HGoZEoIH64xUwnqO5Y3zqx("Frappe-PMS"), whichrtBotdoes not have access to. When drafting aplan_issue_*.mdor callingmcp__figma__*, always substitute the fileKey toh1EnhdK8swe6FCyxUW1XHx— node IDs typically resolve across both files (verified for3518:444604on Issue #1194). If a node ID doesn't resolve in the Copy file, ask the maintainer before proceeding. - Never call
get_metadata/get_design_contexton the file root (0:1) — it returns ~5.8MB of metadata and blows context. Always scope to a specific frame/node ID from the issue's Figma link.
- Site URL: https://pms-temp.frappe.rt.gw
- Next PMS SPA mount:
/next-pms/(seewebsite_route_rulesinnext_pms/hooks.py). Default landing:/next-pms/timesheet. - Left-nav sections in the SPA: Home, Tasks, Projects, Timesheet (Personal / Team / Projects), Allocations, Roadmap, Reports.
- Redirect:
/timesheet→/next-pms/timesheet(website_redirects). - Dev login (username only — ask the user for the password each session; never store secrets here):
ayush.nirwal@rtcamp.com. - DB name:
fm_pms_temp_frappe_rt_gw_563af3bec9cff2dd(inbench_config.toml) - Site config:
workspace/frappe-bench/sites/pms-temp.frappe.rt.gw/site_config.json - Admin tools creds are in
bench_config.toml(admin_tools_username/password).
ghCLI is installed on the host at/usr/bin/gh. Use it for repo/PR/issue work onrtCamp/next-pms,rtCamp/frappe-ui-react, andrtCamp/Frappe-Manager.- If
gh auth statusreports not logged in, ask the user to rungh auth login(or exportGH_TOKEN) before attempting PR/issue operations. ghis NOT installed inside the frappe container — run gh from the host.
- The app uses npm workspaces at the top level and pnpm inside the
frappe-ui-reactsubmodule. A strayyarn.lockat the repo root is not part of the build — don't commit one if it appears. - Always
git statusat the start of a task and rungit fetch upstreambefore cutting a new feature trunk so the branch is off the latestfeat/redesign.
All commits must follow Conventional Commits. Format: <type>[scope]: <description> with an optional body and footers.
| Type | When |
|---|---|
feat |
new feature |
fix |
bug fix |
docs |
documentation only |
refactor |
restructuring without behaviour change |
style |
whitespace / formatting |
perf |
performance improvement |
test |
adding / updating tests |
build |
build system or dependency changes |
ci |
CI/CD config |
chore |
other maintenance |
- Scope (optional): noun in parentheses describing the affected area, e.g.
feat(projects):. - Breaking change: append
!after type/scope and/or add aBREAKING CHANGE:footer. - Always sign commits:
git commit -S(GPG). Configure once withgit config commit.gpgsign true.