write CONTRIBUTING.md; dedupe Unreleased; add make verify (T-109)
test / unit + widget + golden + a11y (push) Failing after 33s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s

Three docs sweeps from the consultant batch:

* CONTRIBUTING.md lands as a human-addressed guide — clone, build,
  test, DQR, tickets, commit conventions. README links to it.
* CHANGELOG `[Unreleased]` consolidated to one subsection per kind
  in Keep a Changelog order (Added, Changed, Removed, Fixed,
  Security). Released sections left frozen.
* `make verify` is the no-tests sweep: analyze + format +
  decisions-validate + changelog-gate. Bridges the gap between
  `make analyze` alone and the full `make push-check`.

Item 3 (cut an interim release) deferred — that's the user's call
on timing.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-18 10:06:03 +02:00
co-authored by Claude
parent 31d40ad8ce
commit 0160f5e018
6 changed files with 277 additions and 133 deletions
@@ -1811,3 +1811,4 @@ INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by,
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-111', 'status', 'in_progress', 'done', NULL, '2026-05-18 07:43:45', '2026-05-18 07:43:45', '2026-05-18 07:43:45', NULL, '792efcf7c772de7cda7069ea3fc3ac0a', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-104', 'status', 'backlog', 'in_progress', NULL, '2026-05-18 07:49:44', '2026-05-18 07:49:44', '2026-05-18 07:49:44', NULL, 'bf7f2746c252f8b8893f4fdc06d459b8', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-104', 'status', 'in_progress', 'done', NULL, '2026-05-18 07:53:10', '2026-05-18 07:53:10', '2026-05-18 07:53:10', NULL, 'ce2b66658977edc3fddc78079d7852e5', 1) ON CONFLICT(hash) DO NOTHING;
INSERT INTO ticket_history (ticket_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-109', 'status', 'backlog', 'in_progress', NULL, '2026-05-18 08:02:06', '2026-05-18 08:02:06', '2026-05-18 08:02:06', NULL, 'ee75fea173ae492a6ad8a1d888a2142d', 1) ON CONFLICT(hash) DO NOTHING;
+9
View File
@@ -1866,3 +1866,12 @@ INSERT INTO tickets (id, type, parent_id, title, description, status, priority,
**Acceptance:** unit tests cover the rejection path for each injection vector listed.
Source: consultants.md "Security — Findings — [Major]".', 'done', 'high', NULL, NULL, NULL, '2026-05-17 18:47:42', '2026-05-18 07:53:10', NULL, '688f888ba89cefe636ece09f32bea8ab', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-109', 'task', 'T-97', 'write CONTRIBUTING.md; cut interim release to drain Unreleased; merge duplicate changelog headings', 'Three docs items:
1. **No `CONTRIBUTING.md`.** Build/test story is scattered across `CLAUDE.md` (Claude-addressed), `README.md` (partly wrong see T-101), and Makefile help. Write a human-addressed contributor guide: clone `make hooks && flutter pub get` `make test` DQR workflow commit conventions.
2. **CHANGELOG has duplicate subsection headings in `[Unreleased]`** three `### Changed`, two `### Fixed`, two `### Removed` in the 2.0.0 section. Keep a Changelog 1.1.0 expects one of each per release. Merge.
3. **~80-commit Unreleased backlog** undermines the format. Cut an interim release (`vX.Y.Z`) to drain it before continuing.
Order matters: 2 3 1 (clean changelog, release, then doc the workflow).
Source: consultants.md "Docs — Findings".', 'in_progress', 'medium', NULL, NULL, NULL, '2026-05-17 18:48:03', '2026-05-18 08:02:06', NULL, '01396c3a11fb562a859cdf76f933824c', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
+111 -133
View File
@@ -16,26 +16,6 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
## [Unreleased]
### Removed
- **`bin/clide.dart` + `DaemonServer`** — completing the D-56 dissolution.
The separate daemon process was dissolved on 2026-04-23 but the entry
point and socket server class were never actually deleted. Gone now,
along with orphaned tests (`test/cli/`, `test/daemon/subprocess_test`,
`test/daemon/in_process_test`), stale i18n strings, and "start
`clide --daemon`" error messages.
- **`ptyc/` source tree + `PtySession` + `scm_rights.dart`** — PTY
spawning migrated to Dart FFI `forkpty()` (`NativePty`) but the old
C helper and its Dart wiring were never cleaned up. Removed from
toolchain resolution, `ToolCheck` gate, backend serialization,
testmode harness, CI scripts, Makefile, and sandbox entitlements.
D-5 amended to record the retirement.
- CI golden images (`test/goldens/goldens/ci/`) — Skia anti-aliasing
of geometric shapes differs between macOS and Linux even with the
Ahem font, so a single set of CI goldens can't serve both platforms.
Replaced with platform-keyed goldens (`goldens/linux/`,
`goldens/macos/`), each only compared on its own OS.
### Added
- Contrast gate split — baseline `canonicalPairs` every theme passes,
@@ -46,8 +26,8 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
which fails if total line coverage drops below `coverage_floor:` in
`pubspec.yaml`. Floor ratchets up only; target 95% (D-66).
- Pre-push changelog gate — `ci/changelog_gate.sh` fails on any
`## [Unreleased]` bullet over 60 words; warns at 40. Enforces the
Keep-a-Changelog conciseness rule in the git-commit skill.
`## [Unreleased]` bullet over 60 words. Enforces the Keep-a-Changelog
conciseness rule in the git-commit skill.
- Keymap layer (`KeymapService`) — typed Intents, YAML presets,
VS-Code-style when-clauses, layered preset → user file → settings
overlay. Default preset ships; vim/vscode/jetbrains unblocked
@@ -74,47 +54,26 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
17% → 96%. Crosses the 95% global target (T-91).
- Staged `dart doc` CI job — generates and uploads an HTML API
reference for the public `lib/` surface. The step wraps
`dart doc --validate-links` and grep-fails the build on any warning,
so broken doc refs and dangling links can't accumulate. Inert with
the rest of the workflow until Gitea Actions activates.
`dart doc --validate-links` and grep-fails the build on any warning.
Inert with the rest of the workflow until Gitea Actions activates.
- Mouse wheel scrolling in Claude pane — converts scroll events to
PgUp/PgDown so Claude Code (and other TUI apps) scroll their
history naturally.
PgUp/PgDown so TUI apps scroll their history naturally.
- Welcome screen Tips card — six common keybindings shown below the
START / RECENT row when the viewport is tall enough.
- `MultitabPane` widget + `MultitabController` for panes that host
N runtime tab instances of the same kind. Generic over a payload
type, supports pinned/non-closeable tabs (primary), drag-reorder,
close × on hover, and an optional `+` add button. Used by the
Claude pane to render primary + secondaries.
- `MultitabPane.keepAlive` mode — when set, all entry bodies stay
mounted via IndexedStack so switching tabs preserves their state
(PTY connections, scroll position, etc.).
### Security
- IPC: `git.checkout`, `git.push` reject branch/remote args starting
with `-` (closes the `--upload-pack=...` argv-injection vector).
`files.read` rejects files over 10 MB. `git.log` caps `count` at
1000; `git.diff` / `git.stage` cap paths at 256. (T-104.)
### Fixed
- `TerminalView.onTapUp` now actually fires on primary tap — was
wired to a dead code path (T-93). Dead `onTapUp` surface on
`TerminalGestureHandler` / `TerminalGestureDetector` removed.
- `BufferLine.eraseRange` no longer panics when called with `end == 0`.
The right-side wide-char guard read `_data[-1]` via `getWidth(-1)`,
which threw a `RangeError`. Real trigger path: `Terminal.eraseDisplayAbove`
with the cursor at column 0 — common after `ESC[H\x1b[1J`
(home + erase-above) sequences that many TUIs emit on redraw.
- Terminal selections no longer vanish when resizing narrower —
reflow's tail-anchor handler left anchors detached past the
trimmed range. Common triggers: Ctrl+A then resize, drag past a
partially-filled line (T-92).
- `BufferLine.removeCells` / `insertCells` / `dispose` no longer skip
anchors due to concurrent list modification during iteration —
iteration now snapshots the list first (T-91).
type, supports pinned/non-closeable tabs, drag-reorder, close × on
hover, and an optional `+` add button.
- `MultitabPane.keepAlive` mode — entry bodies stay mounted via
IndexedStack so switching tabs preserves their state (PTY
connections, scroll position, etc.).
- `CONTRIBUTING.md` — human-addressed contributor guide covering
clone / build / test / DQR / tickets / commit conventions. The
`[Unreleased]` section is reorganised to one subsection per kind
per Keep a Changelog 1.1.0 (T-109).
- `make verify` — no-tests sweep (analyze + format + decisions +
changelog gate). For mid-edit checks; `make push-check` stays the
full pre-push pipeline.
### Changed
@@ -139,102 +98,121 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit.
- `TreeSitterService` and `TreeSitterLib` accept injectable FFI + asset
loaders for fake-driven tests; production paths unchanged.
- Tidied test imports flagged by `unnecessary_import`.
- `README.md` rewritten to match current architecture; `docs/initial-plan.md`
bannered as historical; new `docs/architecture.md` describes today's
shape (T-101).
- `README.md` rewritten to match current architecture;
`docs/initial-plan.md` bannered as historical; new
`docs/architecture.md` describes today's shape (T-101).
- `SchedulerService._stopTicker` now awaits the in-flight isolate spawn
before killing — closes the same race shape we fixed in PTY (T-106).
- `make push-check-full` added — runs `push-check` plus integration +
smoke for pre-release checks. Integration tests skip the hanging
theme_picker case (T-116) until that's fixed (T-103).
theme_picker case until that's fixed (T-103, T-116).
- Governance bookkeeping: D-66 amended (floor at `coverage_floor:` in
`pubspec.yaml`); `licenses.yaml` reconciled with `pubspec.yaml`;
Q-1/Q-2/Q-3/Q-25 triaged; `.claude/skills/README.md` inventory
added; `--no-fatal-infos` dropped from `ci/test.sh` (T-113).
- Terminal panes now render bold attributes with a real bold weight —
bundled JetBrainsMono Bold + BoldItalic are registered with the
- Terminal panes render bold attributes with a real bold weight —
bundled JetBrainsMono Bold + BoldItalic registered with the
`JetBrainsMono` family at `weight: 700`. The painter's bold
suppression workaround (added when only Regular + Italic were wired
and Flutter's synthetic bold drifted advance widths) is gone.
suppression workaround is gone.
- Claude pane uses `MultitabPane` for primary + secondaries — drops
~100 lines of bespoke tab-strip code, gains drag-to-reorder.
- UI spacing constants live in `lib/widgets/src/spacing.dart`
`clideInset*` for paddings, `clideGap*` for sibling distances,
`clideIcon*` / `clideControlHeight` for control sizes. Inline
pixel literals replaced where they were repeated.
### Changed
`clideIcon*` / `clideControlHeight` for control sizes.
- Tagline reads "IDE for Claude Code CLI" everywhere (welcome
subtitle, README, CLAUDE.md, pubspec, web manifest, CLI banner).
### Fixed
- Closing a secondary Claude pane tab now kills its tmux session
on the clide socket, honouring D-41's "closing a secondary kills
that tmux session" lifecycle. Previously `pane.close` only killed
the ptyc-spawned tmux client and the server-side session leaked.
- Cold-start reap: every clide launch kills any leftover secondary
tmux sessions for the current repo before spawning new ones, so
D-41's "secondary numbering resets between runs" holds even after
an abrupt previous exit (kill -9, crash, force-quit).
- `claude.kill-all-sessions` command now actually kills the
server-side tmux sessions for the repo, not just the panes.
- Terminal cell grid no longer drifts on bold text — bold rendering
is suppressed at the painter level since synthetic bold (with no
Bold.ttf registered) shifts glyph advance widths.
- PTY surfaces errno on `forkpty` / `write` / `ioctl` failures
instead of swallowing. `execve` failures write a diagnostic to
the slave before `_exit`. `NativePty.write` and `PtySession.write`
loop on short writes; both throw `PtyException` on hard errors.
- PTY teardown order fixed — kill child first so the master fd
returns EOF, await reader isolate exit, then close the fd.
Closing earlier could briefly target a reused fd.
- Reader isolate spawn errors in `NativePty` / `PtySession` are now
surfaced via the output stream instead of silently dropped.
`_recvFdAsync` no longer leaks the `ReceivePort` on spawn throw;
`PtySession.spawn` closes the master fd if any post-receive step
fails.
- IPC server hardening: per-request 60s timeout (configurable),
broadcast/response write failures logged instead of swallowed,
client dropped on response-write failure, and the stale-socket
retry now probes for a live daemon before unlinking the socket
(refusing to start if one answers).
- `pane.spawn` and `editor.open` now map POSIX errno values to
actionable IPC error kinds. ENOENT → `not_found`, EACCES/EPERM
`user_error` with a permissions hint, EISDIR/ENOTDIR/EEXIST
→ distinct user-error/conflict, EMFILE/ENFILE → `tool_error`
with a "fd limit hit" hint. Previously every spawn/open failure
was an indistinguishable `tool_error`.
### Security
- Toolchain no longer resolves the dugite git binary against the open
workspace — a malicious repo could plant `native/dugite/bin/git`
and clide would run it on auto-fired `git.status`. Dugite now
resolves against the install dir + `CLIDE_DUGITE_DIR` env override
only (T-98).
- `files.read` and `files.ls` now reject symlinks whose targets live
outside the workspace — closes a path-safety bypass via in-repo
symlinks (T-102).
- `files.read` and `files.ls` now reject paths that resolve outside
the workspace root. Previously a relative path containing `..`
could read arbitrary files via path traversal.
### Changed
- Inline terminal emulator based on xterm.dart v4.0.0 — replaces the
pub.dev dependency with owned code under `lib/src/terminal/`. Drops
three transitive dependencies (xterm, quiver, zmodem).
- Bundle clide-specific tmux.conf for Claude pane sessions: no status
bar, 50k scrollback, mouse on, zero escape delay, isolated socket.
- Claude pane spawns `claude` directly inside tmux with
`CLAUDE_CODE_NO_FLICKER=1` to enable Claude's fullscreen TUI mode
(input box pinned at the bottom).
- PTY read buffer increased from 4KB to 64KB.
- Terminal view 2px padding on all sides.
- Remove bold JetBrains Mono font registration to prevent glyph width
mismatch in terminal rendering.
`CLAUDE_CODE_NO_FLICKER=1` to enable Claude's fullscreen TUI mode.
- PTY read buffer increased from 4 KB to 64 KB.
- Terminal view 2 px padding on all sides.
### Removed
- **`bin/clide.dart` + `DaemonServer`** — completing the D-56
dissolution. The separate daemon process was dissolved on 2026-04-23
but the entry point and socket server class were never deleted.
Gone now, along with orphaned tests, stale i18n strings, and
"start `clide --daemon`" error messages.
- **`ptyc/` source tree + `PtySession` + `scm_rights.dart`** — PTY
spawning migrated to Dart FFI `forkpty()` (`NativePty`) but the old
C helper and its Dart wiring were never cleaned up. Removed from
toolchain resolution, `ToolCheck` gate, backend serialization,
testmode harness, CI scripts, Makefile, and sandbox entitlements.
D-5 amended to record the retirement.
- CI golden images (`test/goldens/goldens/ci/`) — Skia anti-aliasing
of geometric shapes differs between macOS and Linux even with the
Ahem font. Replaced with platform-keyed goldens (`goldens/linux/`,
`goldens/macos/`).
- Bold JetBrains Mono font registration that prevented glyph-width
mismatch in terminal rendering — superseded by the Bold/BoldItalic
re-registration above.
### Fixed
- `TerminalView.onTapUp` now actually fires on primary tap — was
wired to a dead code path (T-93). Dead `onTapUp` surface on
`TerminalGestureHandler` / `TerminalGestureDetector` removed.
- `BufferLine.eraseRange` no longer panics when called with `end == 0`.
Real trigger: `Terminal.eraseDisplayAbove` with the cursor at
column 0 — common after `ESC[H\x1b[1J` (home + erase-above).
- Terminal selections no longer vanish when resizing narrower —
reflow's tail-anchor handler left anchors detached past the
trimmed range. Common triggers: Ctrl+A then resize, drag past a
partially-filled line (T-92).
- `BufferLine.removeCells` / `insertCells` / `dispose` no longer skip
anchors due to concurrent list modification during iteration —
iteration now snapshots the list first (T-91).
- Closing a secondary Claude pane tab now kills its tmux session on
the clide socket, honouring D-41's lifecycle. Previously
`pane.close` only killed the ptyc-spawned tmux client.
- Cold-start reap: every clide launch kills any leftover secondary
tmux sessions for the current repo before spawning new ones, so
D-41's "secondary numbering resets between runs" holds.
- `claude.kill-all-sessions` command now actually kills the
server-side tmux sessions for the repo, not just the panes.
- Terminal cell grid no longer drifts on bold text — bold rendering
is suppressed at the painter level since synthetic bold (with no
Bold.ttf registered) shifts glyph advance widths.
- PTY surfaces errno on `forkpty` / `write` / `ioctl` failures
instead of swallowing. `execve` failures write a diagnostic to the
slave before `_exit`. `NativePty.write` and `PtySession.write`
loop on short writes; both throw `PtyException` on hard errors.
- PTY teardown order fixed — kill child first so the master fd
returns EOF, await reader isolate exit, then close the fd.
- Reader isolate spawn errors in `NativePty` / `PtySession` are now
surfaced via the output stream instead of silently dropped.
`_recvFdAsync` no longer leaks the `ReceivePort` on spawn throw.
- IPC server hardening: per-request 60s timeout (configurable),
broadcast/response write failures logged instead of swallowed,
client dropped on response-write failure, and the stale-socket
retry now probes for a live daemon before unlinking the socket.
- `pane.spawn` and `editor.open` map POSIX errno values to actionable
IPC error kinds (ENOENT → `not_found`, EACCES/EPERM → `user_error`,
EISDIR/ENOTDIR/EEXIST → distinct kinds, EMFILE/ENFILE →
`tool_error` with an fd-limit hint).
### Security
- IPC: `git.checkout`, `git.push` reject branch/remote args starting
with `-` (closes the `--upload-pack=...` argv-injection vector).
`files.read` rejects files over 10 MB. `git.log` caps `count` at
1000; `git.diff` / `git.stage` cap paths at 256 (T-104).
- Toolchain no longer resolves the dugite git binary against the open
workspace — a malicious repo could plant `native/dugite/bin/git`
and clide would run it on auto-fired `git.status`. Dugite now
resolves against the install dir + `CLIDE_DUGITE_DIR` env override
only (T-98).
- `files.read` and `files.ls` reject symlinks whose targets live
outside the workspace — closes a path-safety bypass via in-repo
symlinks (T-102).
- `files.read` and `files.ls` reject paths that resolve outside the
workspace root. Previously a relative path containing `..` could
read arbitrary files via path traversal.
## [2.0.0] — 2026-05-03
+152
View File
@@ -0,0 +1,152 @@
# Contributing to clide
clide is an IDE for the Claude Code CLI, built as a single Flutter
package at the repo root. This guide is for people working on clide
itself.
For day-to-day model-driven work in the codebase, see
[`CLAUDE.md`](CLAUDE.md) — it documents the same guardrails from the
agent's point of view and is the place to look for "why is the code
shaped like this?"
## One-time setup
```
git clone git@github.com:postmeridiem/clide.git
cd clide
make hooks && flutter pub get
```
`make hooks` installs the repo's git hooks (pre-commit + post-merge).
Flutter must be on the stable channel and on `$PATH`.
If you haven't used [pql](https://github.com/postmeridiem/pql) before,
install it and run `pql init` once in the repo root. pql is a hard
dependency for the governance + ticket workflow described below.
## The five commands you'll actually use
```
make run # launch the desktop app
make verify # no-tests sweep — analyze + format + decisions + changelog gate
make test # fast suite — analyze + format + unit + widget + golden
make test-a11y # WCAG-AA contrast + keyboard traversal contracts
make push-check # the pre-push gate; what CI runs
make build-linux # release artefact for the host platform
```
`make verify` is the lightweight "are the gates green?" check for
mid-edit iteration. `make push-check` is the full pre-push pipeline
(verify + every test suite + coverage gate).
`make` with no target prints the full list. `make test-integration`
boots a real app process and is slow; reserve it for the rare change
that touches startup wiring.
The pre-push hook runs `make push-check` automatically. Don't bypass
it with `--no-verify` — fix the underlying issue and create a new
commit. Pre-push includes:
- `flutter analyze` (zero warnings)
- `dart format --set-exit-if-changed`
- the fast unit/widget/golden suites
- accessibility contract tests
- a coverage floor read from `coverage_floor:` in `pubspec.yaml`
(currently 95 %; ratchets up only — see
[D-66](governance/decisions/testing.md#d-66))
- `CHANGELOG.md` `[Unreleased]` bullets ≤ 60 words each
## Decisions, questions, rejected (DQR)
clide tracks architectural commitments as durable records under
[`governance/`](governance/):
- `decisions/<domain>.md` — confirmed decisions (`D-NNN`)
- `questions/<domain>.md` — open questions (`Q-NNN`)
- `rejected/<domain>.md` — rejected proposals (`R-NNN`)
When you make a non-trivial architectural choice, write it down:
```
pql decisions claim D <domain> "short title"
```
This reserves a fresh ID and tells you where to add the record. The
[governance/README.md](governance/README.md) explains the format and
the recommended domain list.
Pre-push validates that every `D-NNN` / `Q-NNN` / `R-NNN` link in
the docs and code resolves to an actual record (`pql decisions
validate`). Broken references fail the build.
## Tickets
All non-trivial work is tracked in `pql ticket`:
```
pql ticket list --status in_progress
pql ticket show T-NNN[,T-NNN…] # batch form on pql 1.4.33+
pql ticket new task "title" --parent T-NNN --priority medium
pql ticket status T-NNN in_progress
pql ticket status T-NNN done
```
A ticket exists for any change a reviewer might want to ask "why?"
about. Bug fixes, refactors, and consultant findings all become
tickets before the diff lands. Trivial typo fixes don't need one.
## Commit conventions
See [D-37](governance/decisions/process.md#d-37) and the bundled
[`git-commit` skill](.claude/skills/git-commit/SKILL.md). In short:
- Imperative subject ≤ 70 chars, no Conventional Commits prefix
(this isn't a Conventional Commits repo — the archived Python
predecessor under [`legacy/`](legacy/) is, but the rebuild isn't).
- One logical change per commit. If the subject needs "and", split it.
- Every user-visible commit adds an entry to `CHANGELOG.md` under
`[Unreleased]` in the right subsection (Added, Changed, Deprecated,
Removed, Fixed, Security). Keep entries to one or two short
sentences — the 60-word cap is enforced by `ci/changelog_gate.sh`.
- Co-author trailer:
`Co-Authored-By: Claude <noreply@anthropic.com>` when Claude wrote
any of the diff.
Never `--amend` a commit unless explicitly asked. Never force-push
to `main`. Never `git add -A` / `git add .` when staging — name
files explicitly so stray secrets or build artefacts don't sneak in.
## Cutting a release
A release is a single commit:
1. Move all `## [Unreleased]` entries under a new `## [X.Y.Z] —
YYYY-MM-DD` heading.
2. Leave an empty `## [Unreleased]` skeleton at the top.
3. Bump `pubspec.yaml` `version:` to `X.Y.Z` (no `-dev` suffix on
the release tag; add it back on the next development commit if
you like).
4. Commit subject: `release vX.Y.Z`.
`pubspec.yaml` is the single source of truth for the version — the
Makefile reads it for ldflag stamping and the app reads it for build
info.
## What goes where
- **Bug, feature, sweep:** file/claim a ticket, branch, code, test,
commit, push. Push triggers `make push-check`.
- **Architectural decision:** `pql decisions claim`, write the
record, then file the implementation ticket linked via
`decision_ref`.
- **Open question:** drop a `Q-NNN` under
`governance/questions/<domain>.md`. Triage later.
- **Rejected proposal:** drop an `R-NNN` under
`governance/rejected/<domain>.md`. Future-you (or a reviewer)
will be glad it's written down.
## Reporting issues
The public issue tracker lives at
<https://github.com/postmeridiem/clide/issues>. The Gitea mirror is
read-only.
+3
View File
@@ -79,6 +79,9 @@ analyze: ## flutter analyze.
format: ## dart format --set-exit-if-changed.
dart format --set-exit-if-changed .
.PHONY: verify
verify: analyze format decisions-validate changelog-gate ## No-tests sweep — analyze + format + decisions-validate + changelog-gate. For mid-edit "are the gates green?" checks; `push-check` is the full pre-push pipeline.
.PHONY: test
test: ## Fast: analyze + format + unit + widget + golden (<60s).
ci/test.sh
+1
View File
@@ -45,6 +45,7 @@ Pre-v2.0 (`2.0.0-dev`). Interaction model and panel system landed. The Python Te
## Documentation
- [`docs/architecture.md`](docs/architecture.md) — current architecture (read this first).
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — how to clone, build, test, file tickets, and write D-records.
- [`docs/initial-plan.md`](docs/initial-plan.md) — historical design doc; preserved as a snapshot of the 2026-04 plan, much of it now superseded.
- [`governance/decisions/`](governance/decisions/) — confirmed decisions (`D-NNN`), open questions (`Q-NNN`), rejected alternatives (`R-NNN`).
- [`CLAUDE.md`](CLAUDE.md) — Claude-addressed working notes (guardrails, repo layout).