agentleFS
vscarpentervscarpenter/gsd-task-manager/.agents/skills/pb-collection/SKILL.md

pb-collection

Skill40 starsChanged 39 days agoMITView on GitHub

Add or modify a field on the PocketBase tasks collection end-to-end — schema script, task-mapper, Zod schema, Dexie version bump, migration test. Use when the user wants to add/remove/rename a synced task field. Codifies the PocketBase v0.23+ gotchas and recurring step-order mistakes.

The file

Show its rules and text

We split this skill into the rules it gives an agent

23 rules. About 0 are common, 9 are distinctive, and 14 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈9

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 14

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Do NOT invoke for local-only Dexie fields (those don't need the PB layers).

    line 25
  2. Very specific:

    Read .claude/rules/pocketbase-sync.md.

    line 29
  3. Very specific:

    Add the field to the taskSchema (and taskInputSchema if user-settable).

    line 37
  4. Very specific:

    Use .safeParse() callers; if this is a user-input path, the schema must already be wired to a safeParse boundary.

    line 38
  5. Very specific:

    Update both exportSchema (.strict()) and importSchema (.strip()).

    line 40
  6. Distinctive:

    Add the camelCase ↔ snake_case mapping.

    line 43
  7. Very specific:

    Test the mapper round-trip in tests/data/task-mapper.test.ts.

    line 45
  8. Very specific:

    Add the field to the tasks collection definition in the script.

    line 48
  9. Very specific:

    If the field needs to be sortable/filterable, add a custom index — but the index must reference the new field, NEVER created or updated.

    line 49
  10. Very specific:

    Run the script against https://api.vinny.io (requires _superusers admin auth — endpoint is /api/collections/_superusers/auth-with-password, …

    line 50
  11. Distinctive:

    Verify the field appears in PB admin UI before continuing.

    line 51
  12. Distinctive:

    Add the field to the relevant table schema.

    line 55
  13. Distinctive:

    If the field is indexed locally, add it to the index list.

    line 56
  14. Very specific:

    Write a migration block if existing rows need backfill (use upgrade() callback).

    line 57
  15. Distinctive:

    If the field is settable on create/update, expose it on the input type.

    line 60
  16. Distinctive:

    Default it where appropriate.

    line 61
  17. Very specific:

    Verify push-side serializes the new field via task-mapper.

    line 64
  18. Distinctive:

    Verify pull-side deserializes it.

    line 65
  19. Distinctive:

    If the field affects LWW conflict resolution, document why in a code comment.

    line 66
  20. Very specific:

    Add the field to schemas in tools/schemas/ if exposed through MCP tools.

    line 69
  21. Very specific:

    Update handlers in tools/handlers/ to read/write it.

    line 70
  22. Very specific:

    Run npm run build in packages/mcp-server/.

    line 71
  23. Very specific:

    Run bun run test -- --coverage and confirm changed-file coverage ≥80%.

    line 81

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

pb-collection — End-to-end PocketBase tasks-collection field change

This skill walks through every place a field touches when added to the synced tasks collection. Skipping any step has bitten us before — the order matters.

When to invoke

User says any of:

  • "Add a <field> to tasks"
  • "Sync <field> across devices"
  • "PocketBase needs <field>"
  • "Update the tasks schema"

Do NOT invoke for local-only Dexie fields (those don't need the PB layers).

Prerequisites — verify before touching code

  1. Read .claude/rules/pocketbase-sync.md. The gotchas below are condensed but the source of truth is there.
  2. Confirm the user has the PocketBase admin password handy (needed to run scripts/setup-pocketbase-collections.sh).
  3. Confirm the field is not a system field name (id, created, updated, collectionId, collectionName).
  4. Confirm field type is supported by both Dexie and PocketBase: text, number, bool, json, date. Never use relation to _pb_users_auth_ (placeholder doesn't resolve at runtime).

Step order (do not reorder)

1. Update Zod schema — lib/schema.ts

  • Add the field to the taskSchema (and taskInputSchema if user-settable).
  • Use .safeParse() callers; if this is a user-input path, the schema must already be wired to a safeParse boundary.
  • For optional fields, default to a sensible value (.optional().default(...)) so legacy tasks parse without error.
  • Update both exportSchema (.strict()) and importSchema (.strip()).

2. Update task-mapper — lib/sync/task-mapper.ts

  • Add the camelCase ↔ snake_case mapping. PocketBase uses snake_case; the app uses camelCase.
  • Both directions: mapToRemote() and mapFromRemote().
  • Test the mapper round-trip in tests/data/task-mapper.test.ts.

3. Update PocketBase collection — scripts/setup-pocketbase-collections.sh

  • Add the field to the tasks collection definition in the script.
  • If the field needs to be sortable/filterable, add a custom index — but the index must reference the new field, NEVER created or updated.
  • Run the script against https://api.vinny.io (requires _superusers admin auth — endpoint is /api/collections/_superusers/auth-with-password, not /api/admins/).
  • Verify the field appears in PB admin UI before continuing.

4. Bump Dexie version — lib/db.ts

  • Increment the version (read the latest version(...) call in lib/db.ts; CLAUDE.md tracks the current number).
  • Add the field to the relevant table schema.
  • If the field is indexed locally, add it to the index list.
  • Write a migration block if existing rows need backfill (use upgrade() callback).

5. Update CRUD — lib/tasks.ts and lib/tasks/crud/

  • If the field is settable on create/update, expose it on the input type.
  • Default it where appropriate.

6. Update sync engine — lib/sync/pb-sync-engine.ts

  • Verify push-side serializes the new field via task-mapper.
  • Verify pull-side deserializes it.
  • If the field affects LWW conflict resolution, document why in a code comment.

7. Update MCP server — packages/mcp-server/src/tools/

  • Add the field to schemas in tools/schemas/ if exposed through MCP tools.
  • Update handlers in tools/handlers/ to read/write it.
  • Run npm run build in packages/mcp-server/.

8. Tests — REQUIRED before claiming done

Write or update:

  • A schema test under tests/data/ — Zod parse/safeParse with and without the new field.
  • tests/data/sync/task-mapper.test.ts — round-trip mapping.
  • tests/data/db-migrations.test.ts — Dexie version upgrade preserves existing data.
  • tests/data/sync/pb-sync-engine.test.ts (and pb-push/pb-pull) — push/pull serializes the field correctly.
  • tests/data/import.test.ts and tests/data/import-export-sync.test.ts — import (lenient .strip()) accepts old exports without the field; export (strict .strict()) emits it.

Run bun run test -- --coverage and confirm changed-file coverage ≥80%.

9. Verification gate — before declaring done

  • bun typecheck passes
  • bun lint passes
  • bun run test passes
  • Created a task with the new field locally, synced to PocketBase, verified field present in PB admin
  • Logged out + back in on a second device (or cleared local data and re-pulled), verified field round-trips
  • Exported tasks → imported them back → field preserved
  • If the field is user-input, verified safeParse rejects bad input with a useful error toast (sonner, not alert())

10. Commit

Conventional commit format: feat(sync): add <field> to tasks collection or feat(schema): .... Bump package.json patch or minor depending on user impact.

Gotchas (condensed — full list in .claude/rules/pocketbase-sync.md)

  • Sort/filter restriction — Do not reference created or updated in PB sort/filter strings. Use client_updated_at.
  • Admin endpoint — /api/collections/_superusers/auth-with-password, not /api/admins/....
  • Relation placeholder — _pb_users_auth_ doesn't resolve. Use text for owner FK.
  • Custom indexes — Cannot reference system columns. Index on the new custom field only.
  • Rate limiting — Push throttled to 100ms between requests; if your change increases write volume, verify no 429s in PB logs.
  • Test runner — Use bun run test, NOT bun test (the latter invokes bun's built-in runner, not vitest).
  • localStorage in tests — localStorage.clear() doesn't work in jsdom under bun; use localStorage.removeItem(key).

Anti-goals

This skill does NOT:

  • Set up a brand-new PB collection (that's a one-time scripts/setup-pocketbase-collections.sh run).
  • Migrate from a different sync backend.
  • Handle local-only Dexie fields (skip steps 2, 3, 6, 7 — but the schema and Dexie steps still apply).

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.