Skip to content

contributing: one step per PR, a required gate, SDK release train - #230

Merged
foxnne merged 3 commits into
mainfrom
contributing/policy
Oct 8, 2026
Merged

foxnne merged 3 commits into
mainfrom
contributing/policy

Conversation

@foxnne

@foxnne foxnne commented Oct 8, 2026 •

Copy link
Copy Markdown
Collaborator

Builds on #229, now merged. sdk-tag.yml no longer repacks a released SDK tarball, which the release train below depends on. Its first live run, on #229's own merge, published nothing, as intended.

What changes

This writes down how a change gets to main, in the repo, where every session and contributor reads it. Until now these rules lived in one person's local agent memory, where a cloud session (or a second person) never saw them. CLAUDE.md had also drifted: it still called the SDK "0.2.0, unreleased".

CONTRIBUTING.md, imported by CLAUDE.md (@CONTRIBUTING.md):

  • One plan step, one branch, one PR.
    • Plans are issues; a plan is never the PR its implementation lands in.
    • Aim for about 800 lines or fewer of non-test, non-doc diff; bigger work goes in a stack or behind a flag.
    • Branches are named <area>/<slug> and never reused.
    • The PR title is the squash commit: area: what changes, at most 72 characters.
  • Claim before you build. Check gh pr list for overlap, then open a draft PR on the first push. Drafts don't take the web test copy. The open PR list is the who's-on-what board for laptop sessions, cloud sessions and people alike.
  • jj.
    • No git write commands.
    • Work in a workspace of your own (the default checkout is shared).
    • jj new before the work, not after.
    • Backticks go through --stdin / --body-file.
    • Push a branch, never main.
    • After a merge, forget the PR's workspace, then jj git fetch, which drops the merged changes (seen on ci: sdk-tag never replaces a released SDK tarball #229).
  • Verify, and say how.
    • The five gates (CI doesn't run test-integration).
    • Read the Build Summary rather than the test count.
    • A plugin build installs into the real plugins dir.
    • UI changes say how they were seen, and on which platforms.
  • The SDK on a release train.
    • A feature PR records recorded_sdk_shape_fingerprint, leaves sdk_version alone, and is labelled sdk.
    • Only an sdk: release 0.2.N PR bumps the version. Two branches never race for the same number, and a morning's SDK changes cost one repin round instead of several (0.2.3 → 0.2.17 was 15 releases in 12 days).
    • Release soon after a fingerprint move, because fizzyed.it/app runs main.
    • An app release never ships an unreleased fingerprint.

Kept consistent with it: the doc comments in sdk/src/version.zig, sdk/sdk_version.zig and sdk/src/dylib.zig, docs/PLUGINS.md § Compatibility, and RELEASING.md (VERSION is bumped through a PR, with the SDK released first if the fingerprint moved). In CLAUDE.md, the Build block now lists test-integration and notes that CI doesn't run it. The LIB_CHECKPOINT.md link now says its ground rules are superseded where they differ.

CI (ci.yml):

  • ci-ok is one job that passes when every other job passed or was skipped, and fails on any failure or cancellation. It is the single check the ruleset requires.
  • The workflow-level paths-ignore is gone. It would never start CI for a Markdown-only PR, leaving that PR's required check waiting forever. A changes job decides instead whether the builds run.
  • merge_group: is added, so turning on a merge queue later needs no workflow change.

.github/rulesets/main.json is the reviewable record of main's ruleset: squash-only PRs, ci-ok required, no force-push or deletion. Org admins may merge a PR past a failing check (bypass_mode: pull_request) but can't push to main directly.

.github/pull_request_template.md has sections for what changes, SDK impact, per-platform verification, and follow-ups.

After merging: repo settings (yours to apply)

# 1. The ruleset (after this PR's ci-ok has run once, so the check exists)
gh api -X POST repos/fizzyedit/fizzy/rulesets --input .github/rulesets/main.json

# 2. Squash commits take the PR title and body; rebase merges off
gh api -X PATCH repos/fizzyedit/fizzy -f squash_merge_commit_title=PR_TITLE -f squash_merge_commit_message=PR_BODY -F allow_rebase_merge=false

# 3. The label the release train uses
gh label create sdk --repo fizzyedit/fizzy --color 5319e7 --description "Moves the plugin boundary's fingerprint; ships at the next SDK release"

# 4. Optional, once the ruleset is active: retire the classic branch protection so one place says what main requires
gh api -X DELETE repos/fizzyedit/fizzy/branches/main/protection

Behaviour changes to expect:

  • Direct pushes to main are refused, including the old RELEASING.md flow, which this PR changes.
  • The Markdown-only skip now happens inside the workflow, so Changes and ci-ok run (a few seconds each) on every PR.

SDK impact

#228 bumps sdk_version in a feature PR, the old way. Merging it as it stands is fine: it would be the last release done like that.

Verified

  • The changes filter's script, extracted from the workflow and run against a scratch repo:
    • Markdown-only, LICENSE, and app/layout/*.md changes → code=false
    • .zig, .yml + .md, and docs/demos/*.zon changes → code=true
    • an unfetchable push base and a workflow_dispatch → code=true
  • The ci-ok script: success/skipped pass; any failure or cancelled fails.
  • Both workflows parse (Ruby Psych). The ruleset is valid JSON. zig ast-check passes on the three edited Zig files.
  • Live CI is this PR's own run, which takes the full matrix since it changes non-Markdown files. The merge_group path is untested until a queue is turned on.
  • The ruleset JSON hasn't been posted. The ci-ok context and the OrganizationAdmin bypass (actor id 1) are as GitHub documents them.

Follow-ups

🤖 Generated with Claude Code

How a change gets to main, written down where every session and contributor reads it. The rules
lived in one person's local agent memory, where a cloud session (or a second person) never saw
them, and CLAUDE.md had drifted (it still called the SDK "0.2.0, unreleased").

CONTRIBUTING.md, imported by CLAUDE.md: one plan step per PR (plans are issues, ~800 lines of
non-test diff, `<area>/<slug>` branches never reused, the PR title is the squash commit);
claiming work with a draft PR before building it; jj in a workspace of one's own; verifying and
saying how; and the SDK on a release train. A feature PR records `recorded_sdk_shape_fingerprint`
and leaves `sdk_version` alone; only an `sdk: release 0.2.N` PR bumps it. Two branches no longer
race for the same number, and a morning's SDK changes cost one repin round instead of several.
The doc comments in `sdk/src/version.zig`, `sdk/sdk_version.zig`, `sdk/src/dylib.zig` and
docs/PLUGINS.md that tied a bump to every fingerprint change now say the same.

CI: `ci-ok`, one job that passes when every other job passed or was skipped, is the check main's
ruleset requires (recorded in `.github/rulesets/main.json`). The workflow-level `paths-ignore`
goes, since it would leave a Markdown-only PR's required check waiting for good; a `changes` job
decides instead whether the builds run. CI also runs for `merge_group`, so a merge queue needs
no workflow change. RELEASING.md bumps VERSION through a PR, SDK release first if the
fingerprint moved. A PR template asks for the SDK impact and per-platform verification.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
foxnne and others added 2 commits October 8, 2026 08:35
The step told agents to abandon `::<area>/<slug> ~ ::main@origin` after fetching, but by then the
bookmark is gone: GitHub deletes a merged branch, and `jj git fetch` abandons the changes only it
held (seen on #229's merge). What the step needs is the order: forget the PR's workspace first,
since a change still checked out somewhere is kept, then fetch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@foxnne
foxnne merged commit d635466 into main Oct 8, 2026
7 checks passed
@foxnne
foxnne deleted the contributing/policy branch October 8, 2026 13:44
foxnne added a commit that referenced this pull request Oct 8, 2026
…#231)

## What changes

`CONTRIBUTING.md`'s workspace rule now says where a workspace goes:
`../fizzy-<task>`, beside the main checkout, and never under `/tmp` or a
session scratchpad.

A session's scratchpad is under `/private/tmp` on macOS, and the OS
clears files there that go untouched for a few days. On 2026-10-08 that
took `build.zig` and most of `src/` out of a workspace mid-task, and
jj's next snapshot recorded the 159 deletions into the working-copy
change. Committed changes survived in the main store.

The rule had been written to one agent's local memory. This PR moves it
to `CONTRIBUTING.md`, where #230 says such rules belong.

## SDK impact

- [x] None

## Verified

- [x] Markdown only. This is the first live run of `ci.yml`'s docs-only
path: expect `Changes` → `code=false`, the builds skipped, and `ci-ok`
passing.

## Follow-ups

None.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
foxnne added a commit that referenced this pull request Oct 8, 2026
Follow-up from #230.

## What changes

`zig build test-integration` now runs in CI, and `ci-ok` waits on it.
The suite is 146 integration tests that drive real widgets, layouts and
drags on dvui's testing backend, plus the SDK, sizing and plugin-loader
suites: 292 tests in all. No CI job ran it before, so a layout or drag
regression reached `main` unless someone remembered to run it locally.
On 10-03, two of its tests had been failing unnoticed.

- **A Linux job of its own**, `Integration tests (Linux)`, beside the
test matrix. It adds a compile to each run, but not to how long a run
takes, and a failure shows under its own check name. It is Linux only
because the code under test is the same on every OS, and on Windows the
suite needs MSVC (`build/app.zig`). It runs whenever the builds run: on
PRs, the merge queue, and pushes to `main`.
- **The job's comment explains a misleading log line.** The log prints
`failed command: …fizzy-integration-tests` on a pass whenever a test
logs a warning, and the demo-replay test always does. The exit code is
the verdict.
- **Docs.** `CONTRIBUTING.md` and `CLAUDE.md` no longer say CI skips
this suite.

**Cost:** one more Linux job per run, and one more Actions cache entry
(setup-zig keys caches by job). The repo's cache budget is already at
about 9.6 of its 10 GB, so GitHub evicts the least recently used entries
a little sooner. See the follow-ups for the larger cache problem I found
while checking this.

## SDK impact

- [x] None

## Verified

- [x] **macOS, locally, on `main` (`4dc4d51a`):** `Build Summary: 28/28
steps succeeded; 292/292 tests passed` in 1:13, exit 0. The known replay
warning printed its `failed command:` line.
- [ ] **Linux:** this PR's own `Integration tests (Linux)` run is the
first. It is the same headless code, but this is the first time the
suite has run on a Linux runner.
- [x] The workflow parses; `ci-ok` needs `[changes, test, integration,
windows-fizzy-backend]`.

## Follow-ups

- **Cancelled runs leave an empty cache, so the next run builds from
scratch.** setup-zig still saves its cache when a run is cancelled (a
newer push cancels the older run), and that cache is nearly empty. The
next run restores the newest key, which is the empty one. Run
37785306976 on `main` restored a 188-byte cache and built from scratch,
and `gh cache list` shows several 0 MB Linux caches. Saving only from
runs that weren't cancelled would make most runs warm. Not changed here.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant