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
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

## How it was checked

- [ ] Every commit is signed off (`git commit -s`, CONTRIBUTING.md, "Licence and sign-off")
- [ ] `pnpm typecheck`, `pnpm lint`, `pnpm test`
- [ ] `pnpm e2e`, then `pnpm design`
- [ ] Screenshots for anything you can see (light and dark, wide and phone)
Expand Down
33 changes: 33 additions & 0 deletions .github/workflows/dco.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Every commit in a pull request carries a Signed-off-by line: the Developer Certificate of Origin
# (CONTRIBUTING.md, "Licence and sign-off"). No app to install; it reads the pull request's own commits.
name: DCO

on:
pull_request:

permissions:
contents: read

jobs:
sign-off:
name: Signed off
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- name: Every commit is signed off
env:
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: |
missing=0
for c in $(git rev-list --no-merges "$BASE..$HEAD"); do
if ! git log -1 --format=%B "$c" | grep -qE '^Signed-off-by: .+ <.+>$'; then
# The short hash only: a commit subject could carry workflow commands.
echo "::error::Commit $(git rev-parse --short "$c") has no Signed-off-by line. Add one with git commit --amend -s, or git rebase --signoff for the branch."
missing=1
fi
done
exit $missing
9 changes: 5 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# CLAUDE.md — Pluck

Pluck is a fast, open-source URL saver with an MCP connector. There is no AI inside the app. Read `docs/handover/pluck-technical-spec-v2.md` before any non-trivial change. Use the words in `GLOSSARY.md`. While the build loop runs, `docs/loop/PROGRESS.md` is the plan and overrides the spec's build order.
Pluck is a fast, open-source URL saver with an MCP connector. There is no AI inside the app. Read `docs/architecture.md` before any non-trivial change; `docs/handover/pluck-technical-spec-v2.md` has the background. Use the words in `GLOSSARY.md`. `docs/history/` is the record of the first build, not current.

## Non-negotiables

- **No model calls on the server.** No LLM SDKs, no embeddings, no vector stores, no "smart" anything. AI features are MCP prompts in `packages/mcp` that a connected client runs. A PR that adds a model call is declined.
- **Only `packages/db` touches MongoDB.** Every query is scoped by `userId`. Every new query gets a two-user test.
- **Public reads use one projection.** `packages/db/public.ts` is the only function that serves public list data. It never joins `contents`, `tags`, `ticked` or `status`. Its field list is pinned by a test.
- **Nothing is deleted by expiry or by MCP.** Expiry and "Archive it all" set `status: archived`. MCP has no delete tool outside the `library:delete` scope, which the consent screen does not request.
- **Every type starts as a Zod schema** in `packages/shared-schemas`. OpenAPI is generated from it.
- **Every type starts as a Zod schema** in `packages/shared-schemas`; the API parses requests with it.
- **MCP is the only public API.** The REST API serves our clients and the MCP layer. Do not add other integrations.

## Product words
Expand All @@ -19,7 +19,7 @@ Use these and no others in UI copy: Pluck (verb), Plucked (confirmation), Item,

## Stack

Next.js 16 App Router, TypeScript strict, Node 22, pnpm 10, ESM. MongoDB (`pluck` database; Atlas in prod, `mongo:8` single-node replica set locally). Better Auth with **email magic link only** (social logins later) and its OAuth provider for the extension, iOS and MCP. WXT for the extension. Expo (current SDK) plus a Swift share extension for iOS. One Docker image for web and worker. Search is `$text` only (no Atlas Search branch in v1).
Next.js 16 App Router, TypeScript strict, Node 22, pnpm 10, ESM. MongoDB (`pluck` database; Atlas in prod, `mongo:8` single-node replica set locally). Better Auth with **email sign-in only** (a magic link on the web, a 6-digit code on the iPhone; App Review's account signs in with a password set in env; social logins later) and its OAuth provider for the extension, iOS and MCP. WXT for the extension. Expo (current SDK) plus a Swift share extension for iOS. One Docker image for web and worker. Search is `$text` only (no Atlas Search branch in v1).

## Decisions that differ from the spec

Expand All @@ -38,7 +38,8 @@ Next.js 16 App Router, TypeScript strict, Node 22, pnpm 10, ESM. MongoDB (`pluck

- Build in vertical slices (db → API → UI → e2e → Chrome check). Infra lands with the first slice that needs it.
- `pnpm typecheck`, `pnpm lint`, `pnpm test`, `pnpm e2e`, then `pnpm design` must pass. Services come up with `pnpm stack`. No `any` without a `// why:` comment.
- **During the build loop:** each slice that passes its gate is one squash commit on local `main`. No remote, no PRs until the first draft is pushed by a human. After that, short branches and PRs; a human merges.
- Short branches and PRs; a human merges. Every commit is signed off (`git commit -s`, the DCO in CONTRIBUTING.md), and a CI check refuses a PR with a commit that isn't.
- Everything is Apache-2.0; keep `NOTICE`. The name and logo are trademarks (`TRADEMARKS.md`), not part of the licence.
- Tests that matter: `packages/extract` fixtures, `packages/db` scoping and the public projection, `packages/mcp` tool tests, the MCP OAuth end-to-end, Playwright e2e per slice.
- Keep `docs/self-host.md` and `docs/mcp.md` in the same commit as any env var or tool change.
- Never run `cdk deploy`, `vercel deploy` or anything against real AWS or Vercel. Deploying is a human step in `docs/deploy.md`.
12 changes: 10 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ If you cannot translate, write your best attempt and say so in the pull request;
- Fill in the template: what and why, how you checked it, screenshots for anything visible (light and dark, wide and phone).
- A maintainer reviews and squash-merges. Merging to `main` deploys getpluck.io, so `main` stays green.

## Licence
## Licence and sign-off

By contributing you agree your work is released under the licence of the folder you changed: AGPL-3.0 for the server and everything it runs on, MIT for the apps and the packages others build on. See the [README](README.md#licence).
Your contribution is released under the [Apache License 2.0](LICENSE), like the rest of Pluck (its section 5: what comes in goes out under the same licence). There is no CLA to sign.

Each commit carries a sign-off, the [Developer Certificate of Origin](https://developercertificate.org): a line saying you wrote the change, or otherwise have the right to submit it under this licence. `git commit -s` adds it from your git name and email:

```
Signed-off-by: Your Name <you@example.com>
```

Forgot it? `git commit --amend -s` for the last commit, or `git rebase --signoff main` for the whole branch, then push again. A check on each pull request looks for it.
Loading
Loading