mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-09-10 15:05:40 +00:00
a1716d8170
* ci(web): decide the npm audit gate from the report, not the exit code, and run it last (BUG-2881) `npm audit` exits non-zero identically for "a HIGH/CRITICAL advisory exists" and "the advisory service was unreachable". The Web job ran it before Build / Type check / vitest under `bash -e`, so a registry timeout (main, 03:50Z) and a 503 (#1246, 04:33Z) on 2026-09-04 each produced a red row with every frontend verification step SKIPPED — a lane that read like a failure and had asked nothing. scripts/ci-audit.mjs runs the audit in --json mode and decides from the report: metadata.vulnerabilities present → fail iff high+critical > 0, naming the advisories; an error envelope or unparseable output → a GitHub warning annotation saying the gate did not run, exit 0. The step moves to the end of the job so the frontend's own verdict always exists whatever the audit does. Verified locally against five report shapes (transport timeout envelope, E503 envelope, one high advisory, clean, garbage) and two live runs (the real registry: clean; a dead registry: warning, exit 0). `--input <file>` is the seam those checks use. Fixes BUG-2881 * ci(web): the audit gate fails closed — retry an unreachable advisory service, then fail under its own title Codex round 1 on #1247: the first draft warned and exited 0 when the advisory service could not be asked, which made the only supply-chain gate pass exactly when it had not run. A gate that passes when it cannot run is not a gate. Now: up to three attempts with backoff (registry blips are usually seconds long), then `::error title=npm audit did not run` and exit 1. The title is distinct from `::error title=npm audit` (a real advisory) so the checks tab tells the two apart without opening the log; re-running is the remedy for the first and never for the second. Because the step runs last, Build / Type check / vitest have already produced their result either way — the original blindness is gone regardless of which way this step fails. Verified against the same five saved shapes (transport and E503 envelopes and garbage now exit 1 under the did-not-run title; a high advisory exits 1 under the advisory title; clean exits 0) and two live runs (real registry: clean; dead registry: three attempts logged, exit 1). Refs BUG-2881 * ci(web): the audit gate refuses counts it cannot read, and refuses bad tuning without crashing Codex round 2 on #1247. (1) metadata.vulnerabilities was checked for presence, not for shape: Number("x") + Number(null) > 0 is false, so a malformed count read as a clean audit — a second fail-open, one layer deeper than round 1's. high/critical must now be non-negative integers or the report is unreadable, which is the fail-closed path. (2) The two env knobs are operator-set, but CI_AUDIT_ATTEMPTS=NaN left the retry loop unexecuted and threw a TypeError, and CI_AUDIT_BACKOFF_MS=Infinity parked Atomics.wait forever; both now fall back to the default with a line saying so. Refs BUG-2881 * build: the local preflight runs the same audit gate CI does, and runs it last Codex round 3 on #1247 (blast radius): `make web-check` still chained bare `npm audit && npm run check`, so a registry blip stopped svelte-check locally exactly as it had in CI, and CONTRIBUTING documented the bare command as the way to reproduce the gate. New `web-audit` target runs `npm run audit:ci`; `check` runs it after web-check and web-test, mirroring the Web job's order. CONTRIBUTING and docs/architecture.md say so. Refs BUG-2881 * build: web-audit stands alone — no `web` prerequisite, so `check` runs npm ci once and no new target reaches it Codex round 4 on #1247: `web-audit: web` made `check` run `npm ci` twice (`web` is .PHONY) and added a target CLAUDE.md's worktree rule did not list as reaching `npm ci`. `npm audit` reads the lockfile and needs neither node_modules nor a build — verified by running it with node_modules removed — so the prerequisite goes; CLAUDE.md's safe list gains `web-audit`. Refs BUG-2881
160 lines
6.2 KiB
Markdown
160 lines
6.2 KiB
Markdown
# Contributing to Pad
|
|
|
|
Thanks for your interest in contributing to Pad! This guide will help you get set up and familiar with how we work.
|
|
|
|
## Where to start
|
|
|
|
Pad is built with Pad — we track our own work as Pad items and mirror the newcomer-friendly ones to GitHub. If you're looking for something to pick up, start with these labels:
|
|
|
|
- [`good first issue`](https://github.com/PerpetualSoftware/pad/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) — small, self-contained, one focused PR.
|
|
- [`help wanted`](https://github.com/PerpetualSoftware/pad/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) — a bit bigger, still scoped to a single PR.
|
|
|
|
Each issue states the problem, the concrete fix, and pointers to the relevant files. Area labels (`area:cli`, `area:web`, `area:ci`) tell you where the code lives; `effort:S` / `effort:M` set expectations.
|
|
|
|
**How PRs are triaged:** open a draft PR early and link the issue — a maintainer reviews within a few days. Small, focused PRs merge fastest: one issue, one branch. Comment on an issue to claim it before you start so we don't double up.
|
|
|
|
Found a problem that isn't an issue yet? Open one describing it (and how to reproduce, for bugs) before writing code, so we can agree on the approach first.
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- [Go 1.26+](https://go.dev/dl/)
|
|
- [Node.js 22+](https://nodejs.org/)
|
|
- Make
|
|
|
|
### Setup
|
|
|
|
```bash
|
|
git clone https://github.com/PerpetualSoftware/pad
|
|
cd pad
|
|
make build # Build web UI + Go binary
|
|
```
|
|
|
|
This produces a `./pad` binary in the project root.
|
|
|
|
### Development
|
|
|
|
```bash
|
|
make build # Full build: web UI + Go binary
|
|
make build-go # Go only (skip web — faster for backend changes)
|
|
make dev-web # SvelteKit dev server with hot reload (localhost:5173)
|
|
make test # Run Go tests
|
|
make lint # Run go vet
|
|
make install # Build, install to ~/.local/bin/pad, restart server
|
|
```
|
|
|
|
**Typical workflow:**
|
|
|
|
1. Make your changes
|
|
2. `make build` to verify everything compiles
|
|
3. `make test` to run tests
|
|
4. `make install` to test the full binary locally
|
|
5. Open http://localhost:7777 to verify the web UI
|
|
|
|
### Project Structure
|
|
|
|
```
|
|
cmd/pad/main.go — CLI entry point (Cobra commands)
|
|
internal/
|
|
server/ — HTTP API handlers, SSE, middleware
|
|
store/ — SQLite CRUD, migrations, FTS
|
|
models/ — Go types
|
|
items/ — Field validation
|
|
collections/ — Default schemas, templates
|
|
cli/ — HTTP client, formatting
|
|
events/ — EventBus for real-time SSE
|
|
config/ — Workspace detection
|
|
web/src/
|
|
routes/ — SvelteKit pages
|
|
lib/api/client.ts — TypeScript API client
|
|
lib/types/index.ts — TypeScript types
|
|
lib/components/ — Reusable UI components
|
|
skills/pad/SKILL.md — Claude Code agent skill
|
|
```
|
|
|
|
## Making Changes
|
|
|
|
### Branch Naming
|
|
|
|
Use descriptive branch names:
|
|
|
|
- `feat/relation-field-picker` — new features
|
|
- `fix/dashboard-progress-bar` — bug fixes
|
|
- `docs/update-api-reference` — documentation
|
|
- `refactor/store-interface` — refactoring
|
|
|
|
### Commit Messages
|
|
|
|
Write clear, concise commit messages that explain **why**, not just what:
|
|
|
|
```
|
|
Add phase relation field to Tasks collection
|
|
|
|
Tasks can now be linked to phases via a relation field,
|
|
enabling progress tracking on the phase detail page.
|
|
```
|
|
|
|
### Pull Requests
|
|
|
|
- Keep PRs focused — one feature or fix per PR
|
|
- Include a description of what changed and why
|
|
- Add tests for new backend functionality
|
|
- Verify `make build` and `make test` pass before opening
|
|
|
|
### Code Style
|
|
|
|
- **Go:** Standard `gofmt` formatting. Run `go vet ./...` to catch issues.
|
|
- **Svelte:** Follow existing component patterns. Use Svelte 5 runes (`$state`, `$derived`, `$effect`).
|
|
- **TypeScript:** Types live in `web/src/lib/types/index.ts`.
|
|
|
|
### Quality Gates
|
|
|
|
PR CI runs two security gates that block merging on regressions:
|
|
|
|
- **`npm run audit:ci`** (from `web/`; `make web-audit` from the root) — `npm audit --audit-level=high --omit=dev` through `web/scripts/ci-audit.mjs`, which fails on any HIGH or CRITICAL advisory in production frontend deps and, separately, fails under its own title when the advisory service cannot be reached (after retries) rather than passing. CI runs it after build, svelte-check and vitest so those verdicts exist either way; `make check` does the same.
|
|
- **`make vuln`** (`govulncheck -mode binary`) — builds the pad binary and scans it for known vulnerabilities in any Go package it actually reaches. Runs in **binary mode** rather than source mode (`govulncheck ./...`): source mode builds an SSA call-graph over the whole dependency tree and can consume multiple GB of RAM (BUG-2084), while binary mode reads the compiled binary's symbol table for a fraction of the memory. Pinned to a specific govulncheck version in `.github/workflows/ci.yml`; bump intentionally rather than tracking `@latest`.
|
|
|
|
Both are fast enough to run locally:
|
|
|
|
```bash
|
|
make web-audit
|
|
make vuln
|
|
```
|
|
|
|
## Adding Features
|
|
|
|
### New API Endpoint
|
|
|
|
1. Add handler in `internal/server/handlers_*.go`
|
|
2. Register route in `internal/server/server.go` (`setupRouter()`)
|
|
3. Add store method in `internal/store/` if needed
|
|
4. Add CLI client method in `internal/cli/client.go`
|
|
5. Add TypeScript type in `web/src/lib/types/index.ts`
|
|
6. Add API method in `web/src/lib/api/client.ts`
|
|
|
|
### New CLI Command
|
|
|
|
1. Add function in `cmd/pad/main.go`
|
|
2. Register in `rootCmd.AddCommand()`
|
|
|
|
### Database Migration
|
|
|
|
1. Add migration in `internal/store/migrations/`
|
|
2. Update models in `internal/models/`
|
|
3. Migrations run automatically on server start
|
|
|
|
## Reporting Issues
|
|
|
|
- **Bugs:** Use the bug report template — include steps to reproduce
|
|
- **Features:** Use the feature request template — describe the problem first, then your proposed solution
|
|
- **Questions:** Open a discussion or issue
|
|
|
|
## Contributor License Agreement
|
|
|
|
By submitting a pull request, you agree to the terms of our [Contributor License Agreement](.github/CLA.md). This is a lightweight CLA that preserves your rights while granting the project maintainers flexibility for future licensing decisions.
|
|
|
|
## License
|
|
|
|
Pad is licensed under [Apache 2.0](LICENSE). By contributing, your code is released under the same license.
|