| name | add-bound-method |
| description | Add frontend-callable Go backend method (Wails binding round-trip). Use whenever frontend needs new data or action from backend — new App method, regenerate bindings, api.ts wrapper, test stub, mock updates. Also use when binding call mysteriously returns undefined. |
Add a frontend-callable backend method
Frontend never touch network/disk/CPU. New I/O = bound Go method. Round-trip = ONE change, 4 mandatory steps — skip any step = silent fail (no error, just undefined).
Steps
- Go method — exported method on
*App in internal/app/app*.go (pick file matching feature area: app_git.go, app_secrets.go, app_features.go, …). Return (T, error).
- Regenerate bindings — run
task generate:bindings. Regens frontend/bindings/ (gitignored — never hand-edit).
- Wrapper in
frontend/src/lib/api.ts — thin typed re-export. Components import from api.ts only, never from bindings/ direct.
- Stub line in
frontend/src/test-stubs/app.ts — one line: export const MyMethod = method("MyMethod");. Vitest + dev-mock alias bindings to this file; missing stub = call undefined at runtime, NO error.
Conditional 5th/6th steps
- Spec in
frontend/tests/e2e/ will call it → also add method to frontend/src/lib/devMock.ts (e2e webServer run bun run dev:mock). Missing devMock method = silent no-op, spec timeout, no error.
- Visual harness render that path, or
Git*/meta method touched by CollectionSettings.tsx/FolderSettings.tsx/SourceControlPanel.tsx → also add to tests/visual/mock-backend.mjs.
Rules
- Git ops:
go-git/v5 library only, never shell out to git binary.
- Collection-open paths must funnel through
refreshCollection in frontend/src/lib/actions.ts — single choke point.
- New field on
model.Collection → add to BOTH hand-copy sites in internal/store/store.go (ReadMeta AND SaveCollection copy fields explicit) or silent no-persist.
Verify
go test ./...
cd frontend && bun run typecheck (catch missing api.ts wrapper)
- Grep
test-stubs/app.ts for method name (typecheck NOT catch missing stub)