Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/TODO/INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ _none open_
| mp-schema | 0 |
| contexts | 0 |
| dto-constants | 0 |
| data-flow | 0 |
| security | 0 |

---

Expand Down
2 changes: 1 addition & 1 deletion .claude/TODO/SCHEMA.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type: meta
title: <actionable title>
severity: critical | high | medium | low
tags: [security | bug | drift | missing-test | refactor | doc | perf]
area: auth | mp-provider | services | components | contexts | routing | data-flow | testing | dto-constants | utils | commands
area: auth | mp-provider | mp-schema | services | components | contexts | routing | data-flow | security | testing | dto-constants | utils | commands
files: [src/path/to/file.ts]
discovered: YYYY-MM-DD
discovered_by: <shard-name>
Expand Down
2 changes: 1 addition & 1 deletion .claude/commands/newstoredproc.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,5 +167,5 @@ All INSERT sections use `IF NOT EXISTS` guards so they skip gracefully on re-run
- The unified install script at `_INSTALL/ministryplatform-install.sql` is auto-generated by the build — never edit it manually
- SQL files in `src/lib/providers/ministry-platform/db/` are combined alphabetically by the build script
- Domain_ID is hardcoded to `1` in role grants (matches existing pattern across all deployment scripts)
- If the procedure needs to be callable from the app, a corresponding service method should be added — see `src/services/` and `.claude/references/services.md`
- If the procedure needs to be callable from the app, a corresponding service method should be added (it must gate with `AuthorizationService.requireSecurityRole()` and must NOT pass `@DomainID` in the body) — see `src/services/`, `.claude/references/services/README.md`, and add the proc to `.claude/references/mp-schema/required-procs.md`
- **`@DomainID INT` is required by the Ministry Platform API for all `api_*` stored procedures.** The platform passes the current domain ID automatically when calling procedures through the API. If this parameter is missing, the procedure will not be callable via the API even if it is registered in `dp_API_Procedures`.
24 changes: 12 additions & 12 deletions .claude/commands/newtool.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,18 @@ Create `src/app/(web)/tools/{routeName}/page.tsx`:

```tsx
import { {componentName} } from "./{fileName}";
import { parseToolParams } from "@/lib/tool-params";
import { requireToolAccess } from "@/app/(web)/tools/require-tool-access";
import { parseToolParams } from "@/lib/tool-params.server";

interface {componentName}PageProps {
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
}

export default async function {componentName}Page({ searchParams }: {componentName}PageProps) {
// Self-gating, before ANY param parsing or data call — see require-tool-access.ts.
// Pick the MP table/operation this tool actually needs.
await requireToolAccess({ table: "dp_Tools", operation: "read" });

const params = await parseToolParams(await searchParams);

return <{componentName} params={params} />;
Expand All @@ -54,7 +59,7 @@ Create `src/app/(web)/tools/{routeName}/{fileName}.tsx`:
```tsx
"use client";

import { useState, useEffect } from "react";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { ToolContainer } from "@/components/tool";
import { Users } from "lucide-react";
Expand All @@ -69,19 +74,10 @@ export function {componentName}({ params }: {componentName}Props) {
const [isSaving, setIsSaving] = useState(false);
const isNew = isNewRecord(params);

useEffect(() => {
console.log("Tool launched with params:", params);
console.log("Mode:", isNew ? "Create New" : "Edit Existing");
if (params.recordDescription) {
console.log("Editing record:", params.recordDescription);
}
}, [params, isNew]);

const handleSave = async () => {
setIsSaving(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
setIsSaving(false);
console.log("Saved!", { params });
};

const handleClose = () => {
Expand Down Expand Up @@ -163,6 +159,8 @@ After creating all files, confirm the new tool by listing the created files and
- [ ] Tool component created at `src/app/(web)/tools/{routeName}/{fileName}.tsx`
- [ ] Homepage card added to `src/app/(web)/page.tsx`
- [ ] All placeholder text replaced with the tool's displayName
- [ ] `page.tsx` calls `requireToolAccess()` before parsing params, and imports `parseToolParams` from `@/lib/tool-params.server` (not `@/lib/tool-params`, which must stay client-safe)
- [ ] No `console.log` (ESLint `no-console` allows only `warn`/`error`)
- [ ] Component uses named export (no default export) — except `page.tsx` which requires default export per Next.js

## Multiple Tools
Expand All @@ -178,5 +176,7 @@ Creates both tools and adds both cards to the homepage.
## Notes

- The `ToolContainer` renders a `DevPanel` above the title bar automatically on localhost in development builds — no manual scaffolding needed.
- `tools/layout.tsx` only redirects role-less users for UX; it does **not** protect the page. Each page gates itself with `requireToolAccess` (`src/app/(web)/tools/require-tool-access.ts`), and any server action / service the tool adds must gate again with `AuthorizationService.requireSecurityRole()` (CLAUDE.md practice #12).
- Add co-located tests (`page.test.tsx`, `{fileName}.test.tsx`) — CI enforces coverage thresholds (`vitest.config.mts`).
- The `page.tsx` uses a default export (Next.js requirement), but the tool component uses a named export per project convention
- Route names should be lowercase with no separators (matching existing pattern: `template`, `groupwizard`, `addresslabels`)
- Route names should be lowercase with no separators (matching existing pattern: `template`, `groupwizard`, `addresslabels`, `fieldmanagement`, `addeditfamily`)
6 changes: 3 additions & 3 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ Create a GitHub release with auto-generated release notes from merged pull reque
- Run `git log --oneline` (from last release tag to HEAD, or recent commits if first release) to understand what's new

3. **Identify PRs to include:**
- If this is the first release, run `gh pr list --state merged --limit 20 --json number,title,mergedAt,body,labels` to get recent merged PRs
- If a previous release exists, find PRs merged since that release using `gh pr list --state merged --search "merged:>YYYY-MM-DD" --json number,title,mergedAt,body,labels`
- If this is the first release, run `gh pr list --base dev --state merged --limit 20 --json number,title,mergedAt,body,labels` to get recent merged PRs
- If a previous release exists, find PRs merged since that release using `gh pr list --base dev --state merged --search "merged:>YYYY-MM-DD" --json number,title,mergedAt,body,labels` (feature PRs target `dev`; add `--base main` hotfix PRs if any)
- Present the list of PRs to the user and ask which to include (default: all)
- Exclude the `dev` → `main` release PR itself — it is a promotion, not a change

Expand Down Expand Up @@ -105,7 +105,7 @@ gh release list --limit 5
git tag --sort=-version:refname | head -5

# 2. Get merged PRs since last release
gh pr list --state merged --limit 20 --json number,title,mergedAt,body,labels
gh pr list --base dev --state merged --limit 20 --json number,title,mergedAt,body,labels

# 3. Get repo info for changelog URL
gh repo view --json nameWithOwner --jq .nameWithOwner
Expand Down
9 changes: 6 additions & 3 deletions .claude/commands/update-deps.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ Record these numbers — they are the comparison for everything that follows:
```bash
npm run test:run # test + file counts
npm run lint
npm run build 2>&1 | grep -cE "error TS" # may be non-zero already
npm run typecheck 2>&1 | grep -cE "error TS" # may be non-zero already
npm run build # needs a valid .env.local — the app refuses to boot (even at build) on missing/weak secrets
npm audit
node -v && npm -v
```
Expand Down Expand Up @@ -123,7 +124,8 @@ Then install that **one** package, and verify:
npm install <pkg>@<target>
npm run test:run
npm run lint
npm run build 2>&1 | grep -cE "error TS" # compare to baseline
npm run typecheck 2>&1 | grep -cE "error TS" # compare to baseline
npm run build && npm run check:prerender
```

- Any new type error is a real break even if tests pass — find the migration
Expand Down Expand Up @@ -207,7 +209,8 @@ adds no install scripts. Otherwise pin to the previous version and note why.
held with clearing conditions**, and any pre-existing issues found but not
fixed.
- Add a row to the index table in `.claude/packages/README.md`.
- Add a row to the **Dependency Audit History** table in `CLAUDE.md`.
- Add a row to the **Dependency Audit History** table in `CLAUDE.md`, and update
its **Current holds** table to match the new record.
- Commit in the same separable units used above (in-range / removals / each
major), then open a PR targeting `dev`.

Expand Down
Loading
Loading