Skip to main content

Dev workflow

Every Shikanime repo runs the same loop: jj workspaces, plain-English commits with trailers, PRs to protected main, squash landing. The loop is codified in the sks-* skill family (shikanime-labs/skills) and is identical for humans and agents.

Lifecycle​

#PhaseToolGate
0Discussion (RFC) if openDiscussionentry
1Issue: create → triageissue—
2Refine to convergenceissue refineledger settled
3Branch + implementjj workspaceworkspace created
4Commitcommit envelopecommit shape
5Adversarial reviewreview lensreview gate
6PR: open → triagegh pr create—
7Land (--squash)gh pr mergebranch protection
8Close issue (N of N)issueledger discharged

jj is the working surface​

All org repos are jj-managed. Two rules prevent most damage:

  • Never work in the cloned checkout. Every unit opens a fresh workspace pinned to the remote tip: jj workspace add ../<repo>.<unit> -r 'main@origin'. Concurrent WIP in the checkout never folds into your change.
  • Pushed bookmarks are immutable. To rebuild a branch: jj new off main@origin, jj restore --from <old> only the intended files, re-point the bookmark with --allow-backwards, push. Always re-diff against main — restore carries over whatever the old commit held.

jj does not auto-track bookmarks: jj bookmark track <branch> --remote=origin before jj git push, or the push is rejected. A success line proves nothing — verify the bookmark moved and the PR head OID equals local @.

Commit envelope​

Plain capitalized title (no conventional-commit prefix), body with labels (Design:, Related:, Issue: per the repo's AGENTS.md), plus two trailers on agent-assisted commits:

Co-authored-by: Automata <automata@shikanime.studio>
Signed-off-by: <contributor> <email>

manifests and skills enforce Signed-off-by via gitlint (CC1) — a commit without it is rejected at push.

PRs and landing​

  • PRs open with --head <org>:<branch> (the repo owner, not the login) and body sections # Why / ## What / ## References restating the commit. Bodies are written to a file and passed via --body-file — inline --body gets mangled by shell expansion.
  • main is protected in both orgs: 1 approval, linear history, signed commits, squash-only. A verbal "lgtm" is the approval signal; after it, gh pr merge --squash --admin lands (self-approval is blocked by the org "Landing protections" ruleset).
  • Duplicate check first: if an open PR already delivers the change, stack on its branch instead of opening another. Stacks land in dependency order, base first.
  • Inside a jj workspace gh has no git remote — pass -R <org>/<repo>.

Branch classes​

SignalImplication
main protectedPR mandatory, squash landing
.jj/ presentjj bookmark tracking before push
gitlint / DCO hooksSigned-off-by mandatory
AGENTS.md with Related:follow its conventions
NixOS/infra reponix eval / nix build before land

Formatting​

nix fmt (treefmt) reformats the whole tree per run — scope it (nix fmt apps/<app>) and restore anything outside your change before committing. Markdown wraps at 80 columns; never skip the formatter, and never run it over GitHub issue/PR bodies (free text, exempt).

Verification before "done"​

"Pushed / landed / merged" are claims until checked against real output: gh pr view <n> --json state,headRefOid after create and merge, and re-measure any file/line count with jj diff --stat — GitHub's web diff pads context and overstates size.