Description
Migrate grotTrack’s task surface to just
1. Outcome
grotTrack has no Makefile today, so this migration is scoped to: introduce a top-level
justfile covering both the Swift/macOS app and the Chrome extension (grot-track-extension/);
absorb scripts/generate-icons.mjs’s invocation and scripts/update-appcast.sh’s invocation into
recipes (both scripts themselves are KEEP — real program / control-flow script — only their
call-sites change); rewrite the relevant run: steps in .github/workflows/build.yml and
.github/workflows/release.yml to call just <recipe>; and update AGENTS.md, README.md, and
backlog/config.yml to reference just instead of raw xcodegen/xcodebuild/swiftlint/npm
invocations. When done, just --list is the single answer to “what can I run in this repo”, and
just check is exactly what CI enforces on every PR.
No Makefile exists anywhere in this repo (verified: find . -iname Makefile -o -iname GNUmakefile
returns nothing outside node_modules), so there is no Makefile disposition table and no git rm of
a Makefile in this task.
2. The complete justfile
Create justfile at the repo root:
set shell := ["bash", "-euo", "pipefail", "-c"]
# show the task surface
default:
@just --list
# install toolchain + project dependencies (idempotent)
setup:
brew install xcodegen swiftlint
npm ci
cd grot-track-extension && npm ci
just xcodeproj
# regenerate the local Xcode project from project.yml (gitignored, not committed)
[group('dev')]
[macos]
xcodeproj:
xcodegen generate
# open the generated Xcode project (long-running once the app is launched)
[group('dev')]
[macos]
run: xcodeproj
open GrotTrack.xcodeproj
# run the Chrome extension dev server with hot reload (long-running)
[group('dev')]
extension-dev:
cd grot-track-extension && npx wxt
# auto-fix swiftlint violations in place and format this justfile
[group('check')]
[macos]
fmt:
swiftlint --fix --quiet
just --fmt
# verify formatting without mutating (swiftlint has no separate check mode; lint covers style)
[group('check')]
[no-exit-message]
fmt-check:
just --fmt --check
# run swiftlint static analysis on the Swift sources
[group('check')]
[macos]
[no-exit-message]
lint:
swiftlint lint --strict
# type-check the Chrome extension (generates WXT types first)
[group('check')]
[no-exit-message]
typecheck:
cd grot-track-extension && npx wxt prepare && npx tsc --noEmit
# run the Swift test suite (optional `filter` narrows via -only-testing)
[group('check')]
[macos]
[no-exit-message]
test filter="": xcodeproj
#!/usr/bin/env bash
set -euo pipefail
if [ -n "{{filter}}" ]; then
xcodebuild test -project GrotTrack.xcodeproj -scheme GrotTrackTests \
-destination 'platform=macOS' -only-testing "{{filter}}" \
CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=NO
else
xcodebuild test -project GrotTrack.xcodeproj -scheme GrotTrackTests \
-destination 'platform=macOS' \
CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=NO
fi
# regenerate committed icon assets (Chrome extension + macOS AppIcon) from assets/icon.svg
[group('gen')]
gen:
npm ci
node scripts/generate-icons.mjs
# regenerate icons and fail if the tree goes dirty (drift gate)
[group('gen')]
[no-exit-message]
gen-check: gen
git diff --exit-code -- grot-track-extension/public GrotTrack/Assets.xcassets/AppIcon.appiconset
# the full local gate — exactly what CI's build-extension job + release.yml's test-gate enforce
[group('check')]
check: fmt-check lint typecheck gen-check test
# CI-only superset of check: split build-for-testing + coverage-instrumented run (build.yml build-swift job)
[group('check')]
[macos]
ci: xcodeproj lint
xcodebuild build-for-testing -project GrotTrack.xcodeproj -scheme GrotTrackTests \
-destination 'platform=macOS' -derivedDataPath ./build \
CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=NO
xcodebuild test-without-building -project GrotTrack.xcodeproj -scheme GrotTrackTests \
-destination 'platform=macOS' -derivedDataPath ./build \
-resultBundlePath TestResults.xcresult -enableCodeCoverage YES \
CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=NO
# build the unsigned macOS app for local testing
[group('build')]
[macos]
build: xcodeproj
xcodebuild build -project GrotTrack.xcodeproj -scheme GrotTrack \
-destination 'platform=macOS' CODE_SIGN_IDENTITY="-" CODE_SIGNING_ALLOWED=NO
# build the Chrome extension for production (MV3, output in .output/chrome-mv3/)
[group('build')]
build-extension: gen
cd grot-track-extension && npm ci && npx wxt build
# package the Chrome extension as a distributable zip (for release / Chrome Web Store upload)
[group('build')]
extension-zip: gen
cd grot-track-extension && npm ci && npx wxt zip
# update appcast.xml with a new Sparkle release entry — run from CI only, expects _site/appcast.xml
[group('release')]
[working-directory('_site')]
appcast version sig length:
../scripts/update-appcast.sh {{version}} '{{sig}}' {{length}}
3. Makefile disposition
Not applicable. No Makefile / GNUmakefile exists anywhere in this repo. Skip this step entirely
— there is nothing to git rm.
4. Script disposition
| Script | Disposition | Replacement | Why |
|---|---|---|---|
scripts/update-appcast.sh |
KEEP | just appcast <version> <sig> <length> (§2, [working-directory('_site')]) |
Non-trivial control flow — mktemp, awk with a getline loop, an if/else creating vs. patching appcast.xml. Per §6 this is “anything with non-trivial control flow” and stays a file; the recipe is the entry point. |
scripts/generate-icons.mjs |
KEEP | just gen runs node scripts/generate-icons.mjs (§2) |
A real Node program (uses sharp to rasterize SVG → 13 PNG sizes across two output directories) — a generator, not a task sequencer. Per §6, real programs of substance stay files. |
Both scripts are already invoked only as node scripts/generate-icons.mjs / ./scripts/update-appcast.sh <args> from CI and package.json’s generate-icons npm script — nothing here has meaningfully complex CLI wrapping to strip out; the change is purely at the call-sites (§5, §6).
5. CI changes
.github/workflows/build.yml
build-swift job — add a setup-just step right after checkout:
- uses: extractions/setup-just@<pinned-sha> # v4
with:
just-version: '1.58.0'
Then:
- Delete the “Install tools” step’s
swiftlinthalf — keepbrew install xcodegenonly ifjust setup/just xcodeprojdon’t already cover it, but simplest: leave “Install tools” (brew install xcodegen swiftlint) as-is;justrecipes assume the toolchain is already on PATH, matching current CI structure. - Replace the “Generate Xcode project” step body (
xcodegen generate) withrun: just xcodeproj. - Replace the “Lint” step. Current:
Becomes:- name: Lint run: swiftlint lint --reporter github-actions-logging continue-on-error: true
Drop- name: Lint run: just lintcontinue-on-error: trueand the--reporter github-actions-loggingflag. This is a deliberate behavior change — see Traps §9 below.just lintusesswiftlint lint --strict(plain reporter); GitHub inline annotations from swiftlint are lost, replaced by plain log output. This is required forcheck/cicompleteness (§1 of the standard: “If a repo has no meaningful content… check must be complete”). - Replace the “Build for Testing” + “Run Tests” steps (two
run: |blocks) with a single step:- name: Build and Test run: just cijust ciruns the identical twoxcodebuildinvocations with the same-derivedDataPath ./buildand-resultBundlePath TestResults.xcresult -enableCodeCoverage YESflags, so the downstream “Coverage Summary” step (unchanged, readsTestResults.xcresult) keeps working. - Leave “Coverage Summary” unchanged — it’s markdown-summary formatting via inline
python3, not build/test/lint/gen logic. - Leave signing/archiving steps (“Import signing certificate”, “Archive & sign”, “Package signed app”) unchanged — secrets-dependent, not a local dev task (§8 of the standard, out of scope).
build-extension job — add the same setup-just step after checkout. Then:
- Delete the “Install dependencies” step (
npm ciingrot-track-extension) — subsumed bybuild-extension’s ownnpm ci. - Replace “Install icon dependencies” (
npm ciat repo root) + “Generate icons” (node scripts/generate-icons.mjs) with one step:- name: Generate icons run: just gen working-directory: . - Replace “Prepare WXT types” + “Type check” with:
- name: Type check run: just typecheck - Replace “Build extension” with:
(- name: Build extension run: just build-extensionbuild-extensionalready depends ongenand runs its ownnpm ci, so this is safe even thoughgenalso ran moments earlier — both are idempotent.)
ci-success job — unchanged. needs: [build-swift, build-extension] and the job name stay exactly as-is.
.github/workflows/release.yml
test-gate job — add setup-just after checkout. Then:
- Replace “Generate Xcode project” with
run: just xcodeproj. - Replace “Run Tests” (plain
xcodebuild test, no split build) withrun: just test. - Leave “Install XcodeGen” (
brew install xcodegen) unchanged.
build-release job — add setup-just after checkout. Then:
- Leave “Install XcodeGen”, “Generate Xcode project” as raw commands OR replace “Generate Xcode project” with
run: just xcodeprojfor consistency (recommended — no functional difference,lookup-only: truecache steps around it are unaffected). - Leave signing/archiving/notarizing/re-signing steps unchanged (secrets-dependent, out of scope).
- Replace the “Install icon dependencies” (
npm ci) + “Generate icons” (node scripts/generate-icons.mjs) + the extension-build lines inside “Build Chrome Extension” (cd grot-track-extension && npm ci && npx wxt zip) with:
(- name: Build Chrome Extension run: just extension-zipextension-zipdepends ongen, so the separate icon-generation step is no longer needed here.) - Leave “Upload Release Assets” unchanged.
update-appcast job — add setup-just after checkout (runs on macos-latest). Then replace “Generate appcast entry”:
Current:
- name: Generate appcast entry
run: |
VERSION="${NEEDS_RELEASE_PLEASE_OUTPUTS_TAG_NAME}"
VERSION="${VERSION#v}" # strip leading 'v'
mkdir -p _site
curl -fsSL "https://rknightion.github.io/grotTrack/appcast.xml" -o _site/appcast.xml 2>/dev/null || true
cd _site
../scripts/update-appcast.sh \
"$VERSION" \
"${STEPS_SIGN_OUTPUTS_SIGNATURE}" \
"${STEPS_SIGN_OUTPUTS_LENGTH}"
env:
NEEDS_RELEASE_PLEASE_OUTPUTS_TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
STEPS_SIGN_OUTPUTS_SIGNATURE: ${{ steps.sign.outputs.signature }}
STEPS_SIGN_OUTPUTS_LENGTH: ${{ steps.sign.outputs.length }}
Becomes:
- name: Generate appcast entry
run: |
VERSION="${NEEDS_RELEASE_PLEASE_OUTPUTS_TAG_NAME}"
VERSION="${VERSION#v}" # strip leading 'v'
mkdir -p _site
curl -fsSL "https://rknightion.github.io/grotTrack/appcast.xml" -o _site/appcast.xml 2>/dev/null || true
just appcast "$VERSION" "${STEPS_SIGN_OUTPUTS_SIGNATURE}" "${STEPS_SIGN_OUTPUTS_LENGTH}"
env:
NEEDS_RELEASE_PLEASE_OUTPUTS_TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
STEPS_SIGN_OUTPUTS_SIGNATURE: ${{ steps.sign.outputs.signature }}
STEPS_SIGN_OUTPUTS_LENGTH: ${{ steps.sign.outputs.length }}
(just appcast uses [working-directory('_site')], so _site must exist and hold appcast.xml before the call — the mkdir -p _site and curl lines stay exactly where they are, before the just appcast line.)
publish-extension job — add setup-just after checkout. Then replace “Install icon dependencies” + “Generate icons” + the npm ci && npx wxt zip lines inside “Build extension zip” with:
- name: Build extension zip
working-directory: grot-track-extension
run: cd .. && just extension-zip
(extension-zip is defined at repo root and expects to run from there; since the step already sets working-directory: grot-track-extension, either drop that working-directory: and run just extension-zip directly from the job’s default root, or keep the cd .. shown above. Prefer dropping working-directory: grot-track-extension entirely and using run: just extension-zip — simpler, no cd.)
- Leave “Upload to Chrome Web Store” / “Publish on Chrome Web Store” unchanged (secrets-dependent, not build logic).
Workflows explicitly NOT touched
actionlint.yml, zizmor.yml, codeql.yml, dependency-review.yml, scorecard.yml,
arm-automerge.yml, notarize-log.yml, trigger-docs-sync.yml — all either call a
rknightion/.github reusable workflow (uses:) or are GitHub-native/dispatch-only. Do not add
setup-just or touch a single line in these eight files.
6. Docs and agent-contract changes
AGENTS.md
Replace the entire “Build & Development” section (currently: xcodegen generate, an unsigned
xcodebuild build block, swiftlint lint, a full-suite xcodebuild test block, a single-test
xcodebuild test -only-testing block, and the Chrome-extension npm ci && npx wxt prepare && npx tsc --noEmit && npx wxt build block) with:
## Task interface
This repo's task surface is a `justfile`. Discover it, don't guess it:
just --list # human-readable
just --dump --dump-format json # machine-readable
just --show <recipe> # what a recipe actually runs
- `just check` is the full local gate — `fmt-check`, `lint`, `typecheck`, `gen-check`, `test` — and
is a subset of what CI enforces (CI additionally runs `just ci`'s coverage-instrumented build in
the macOS job). It must pass before you commit.
- Prefer `just <recipe>` over the underlying tool. If you are typing `xcodebuild` or `swiftlint`, you
want `just build` / `just test` / `just lint`.
- `just setup` installs the toolchain (XcodeGen, SwiftLint, npm deps for both the root icon generator
and `grot-track-extension/`) and regenerates the Xcode project. Idempotent — safe to re-run.
- Run `just` with stdin from /dev/null. No recipe in this repo is currently `[confirm]`-gated, but if
one is added later, stop and ask before running it — never pass `--yes` or `JUST_YES=1`.
- If a task you need does not exist, add a recipe with a `#` doc comment and a `[group(...)]` rather
than running a bare `xcodebuild`/`swiftlint`/`npm` command.
Do not paste the recipe list itself into AGENTS.md — it rots.
README.md
- “Quick Start” → “Build & Run” (currently
xcodegen generatethenopen GrotTrack.xcodeproj): replacexcodegen generatewithjust xcodeproj. - “Chrome Extension” → “Building” (currently
cd grot-track-extension && npm install && npx wxt build): replace with:
Drop thejust build-extensioncd grot-track-extension/npm installlines —build-extensiondoes its ownnpm ci. - “Development” → “Project Generation”: replace the
xcodegen generatecode block withjust xcodeproj. - “Development” → “Running Tests”: replace the full
xcodebuild test -project ...block withjust test. - “Development” → “Linting”: replace
swiftlint lintwithjust lint.
No other files reference make or a script path directly (CONTRIBUTING.md does not exist;
docs.toml and docs/ contain no build instructions).
7. backlog/config.yml
Current definition_of_done:
definition_of_done:
- "xcodebuild build -project GrotTrack.xcodeproj -scheme GrotTrack -destination 'platform=macOS' CODE_SIGN_IDENTITY=\"-\" CODE_SIGNING_ALLOWED=NO"
- "xcodebuild test -project GrotTrack.xcodeproj -scheme GrotTrackTests -destination 'platform=macOS' CODE_SIGN_IDENTITY=\"-\" CODE_SIGNING_ALLOWED=NO"
- "swiftlint lint"
- "xcodegen generate (run before the first build, and again after any project.yml change; GrotTrack.xcodeproj is generated and gitignored — never commit it)"
- "cd grot-track-extension && npx wxt prepare && npx tsc --noEmit (only if the extension changed)"
New:
definition_of_done:
- "just build"
- "just test"
- "just lint"
- "just xcodeproj (run before the first build, and again after any project.yml change; GrotTrack.xcodeproj is generated and gitignored — never commit it)"
- "just typecheck (only if the extension changed)"
Edit this file by hand — backlog/config.yml is the documented exception to the “never hand-edit
tracker markdown” rule (list-valued keys can’t be set through backlog config set).
8. Order of work
- Add
justfileat repo root (§2). Do not touch CI or docs yet. - Locally (macOS):
just setup, thenjust check, thenjust ci, thenjust build,just build-extension,just extension-zip,just appcast <fake-version> <fake-sig> <fake-len>against a hand-created_site/appcast.xml— prove every recipe runs clean before touching CI. - Run
just --fmt --checkand fix until clean. - Update
.github/workflows/build.yml(§5) on a branch/PR-style diff (even though this repo pushes straight tomain— verify the workflow YAML is valid withactionlint/zizmorstill passing, since both run on every push). - Update
.github/workflows/release.yml(§5). This path only executes on a real release-please release — cannot be fully exercised pre-merge; review the diff very carefully against the current file (reproduced in full above) since a mistake here is a broken release, not a broken PR check. - Update
AGENTS.md(§6). - Update
README.md(§6). - Hand-edit
backlog/config.yml’sdefinition_of_done(§7). - Run
just checkone final time, then push. Watch the nextbuild.ymlrun onmain(build-swift + build-extension + ci-success) to confirm the migrated CI steps actually pass — this is the first real exercise of the[macos]cirecipe and the extension job’s collapsed steps. - No deletions in this repo (no Makefile, no absorbed scripts to remove — both scripts are KEEP).
9. Traps specific to this repo
- Two-ecosystem repo, two CI runners.
build-swiftruns onmacos-26;build-extensionruns onubuntu-latest. Every[macos]-tagged recipe (xcodeproj,run,fmt,lint,test,ci,build) will hard-fail witherror: recipe ... requires ... os ...if invoked from the Linux-runner job — do not add setup-just + a[macos]recipe call tobuild-extension. .xcodeprojis gitignored —xcodeproj(thexcodegen generatewrapper) is deliberately NOT wired intogen/gen-check.gen/gen-checkonly cover the committed icon PNGs. Do not merge these two concepts even though both start with “regenerate a generated file”.swiftlint lintgoes from advisory to blocking. Today’s CI hascontinue-on-error: trueon the Lint step, so a swiftlint failure has never blocked a merge. Foldinglintintocheck/ciremoves that safety valve, per the standard’s “check must be complete” rule. Runjust lintagainst the current tree BEFORE merging this migration — if it’s currently red, either fix the violations first or explicitly decide (and note in the PR) to keepcontinue-on-error: truea little longer, which would then meancheckis knowingly ahead of CI rather than matching it.just appcastcannot run standalone from a clean checkout. It needs_site/appcast.xmlto already exist (created by themkdir -p _site+curllines that remain directly in the workflow, immediately before thejust appcastcall). Don’t try to fold those two lines into the recipe itself — the recipe’s[working-directory('_site')]attribute requires the directory to already exist whenjuststarts, or every recipe in the file fails to parse the[working-directory]target.- EdDSA signature quoting.
sigin theappcastrecipe is base64 (+,/,=characters) — it is single-quoted in the recipe body ('{{sig}}') per the fleet-standard interpolation gotcha (§10 of the standard). Do not remove the quotes even though base64 rarely contains shell metacharacters — GitHub’s own token/signature values have occasionally broken unquoted recipe interpolation elsewhere in the fleet. gen-checkregenerates real PNGs on everyjust check. This is per-contract (§1 of the standard: gen-check belongs inside check wherever gen exists) but meansjust checknow shells out tonpm ci+sharp+ rewrites 13 PNG files + does agit diff --exit-codeevery single run. This is slower than the oldswiftlint lint+xcodebuild testgate. If this becomes a real friction point, that’s a fleet-standard question (whethergen-checkbelongs incheckvs. only inci) — raise it, don’t silently dropgen-checkfromcheckunilaterally.gen-check’s diff scope must cover both icon output directories —grot-track-extension/public/*.png(Chrome icons) ANDGrotTrack/Assets.xcassets/AppIcon.appiconset/*.png(macOS icons).generate-icons.mjswrites both from the same SVG in one invocation; scoping thegit diff --exit-codeto only one directory silently misses drift in the other.- The Coverage Summary step in
build.ymlis untouched and depends onjust ci’s exact-resultBundlePath TestResults.xcresultflag. Ifci’s xcodebuild invocation is ever refactored, that path must stayTestResults.xcresultat the repo root or the (untouched)python3coverage-parsing step silently reportsN/A. - Signing/notarizing/archiving stay raw CI script.
build.yml’s “Archive & sign” and release.yml’s “Build Release Archive” / “Re-sign Sparkle framework binaries” / “Notarize App” steps need Apple secrets (APPLE_CERTIFICATE_BASE64,APPLE_TEAM_ID,APPLE_ID,NOTARY_PASSWORD) that don’t exist on a developer machine — deliberately not migrated intojustrecipes. Don’t “complete” this migration by wrapping them; they’re CI-only per §6 of the standard. - Root
package.jsonvs. extensionpackage.jsonare two separate dependency sets — root has onlysharp(forgenerate-icons.mjs);grot-track-extension/package.jsonhaswxt,typescript,@types/chrome.just setupandjust genrunnpm ciat the root; extension recipescd grot-track-extension && npm ciseparately. Don’t merge these into onenpm cicall.
10. Out of scope
actionlint.yml,zizmor.yml,codeql.yml,dependency-review.yml,scorecard.yml,arm-automerge.yml— GitHub-native / reusable-workflow (uses: rknightion/.github/...) calls. Do not touch.notarize-log.yml—workflow_dispatch-only manual debugging tool, not a build/test/lint step.trigger-docs-sync.yml— repository-dispatch tom7kni/m7kni-net-site, no build logic.release-pleasejob inrelease.yml— untouched, including thebroker-tokenmint step.- All signing/notarization/codesign-re-signing steps in
build.ymlandrelease.yml— secrets-only, no local equivalent. scripts/update-appcast.shandscripts/generate-icons.mjsas files — both KEEP, neither is deleted, both remain exactly where they are..swiftlint.yml,project.yml,docs.toml— no build logic to extract; leave as configuration.- No Makefile exists — nothing to delete in this repo for that step of the fleet migration.
ci-success’sneeds:list, job names,permissions:blocks,concurrency:groups,persist-credentials: false, SHA-pinned actions, and thebroker-tokenreusable-action calls — structurally unchanged everywhere they appear.
Acceptance Criteria
- #1 just check passes locally on a clean checkout (fmt-check, lint, typecheck, gen-check, test)
- #2 just –fmt –check passes with no diff
- #3 just –list shows a # doc comment and a [group(…)] for every public recipe
- #4 scripts/update-appcast.sh and scripts/generate-icons.mjs remain as files, reachable only via just appcast / just gen — no raw ./scripts/… invocation remains in workflows, README.md, AGENTS.md, or package.json
- #5 .github/workflows/build.yml and release.yml call just for xcodegen generation, linting, testing, icon generation, extension build/typecheck/zip, and appcast update, each job preceded by a pinned extractions/setup-just step; ci-success’s needs list and job names in build.yml are unchanged
- #6 AGENTS.md and README.md no longer instruct running xcodegen generate, swiftlint lint, xcodebuild test, npm install && npx wxt build, or ./scripts/*.sh directly
- #7 backlog/config.yml’s definition_of_done lists just build, just test, just lint, just xcodeproj, just typecheck in place of the raw xcodebuild/swiftlint/xcodegen commands
- #8 No Makefile is introduced (repo has none today) and no unstable just features are used
- #9 Top-level justfile exists with default, setup, fmt, fmt-check, lint, test, check plus typecheck, build, build-extension, gen, gen-check, xcodeproj, run, extension-dev, extension-zip, appcast recipes, each with a doc comment and required group; no ci recipe is present because this repository has no Docker, service-container, or cross-compilation leg.
Definition of Done
- #1 xcodebuild build -project GrotTrack.xcodeproj -scheme GrotTrack -destination ‘platform=macOS’ CODE_SIGN_IDENTITY=“-” CODE_SIGNING_ALLOWED=NO
- #2 xcodebuild test -project GrotTrack.xcodeproj -scheme GrotTrackTests -destination ‘platform=macOS’ CODE_SIGN_IDENTITY=“-” CODE_SIGNING_ALLOWED=NO
- #3 swiftlint lint
- #4 xcodegen generate (run before the first build, and again after any project.yml change; GrotTrack.xcodeproj is generated and gitignored — never commit it)
- #5 cd grot-track-extension && npx wxt prepare && npx tsc –noEmit (only if the extension changed)
Implementation Plan
- Inventory the live task surface, workflow runners, shared-workflow calls, hook configuration, and all direct task/script references.
- Add a standards-compliant top-level justfile; validate every local, non-secret recipe and fix migration-exposed lint/config defects within scope.
- Route build/test/lint/generate CI through pinned
justrecipes while preserving shared reusable calls, signatures, job names, and secret-only release steps. - Replace stale developer-facing command references with the task interface, run focused local and workflow gates, then review the named staged diff.
- Commit and push named paths to main; obtain a green repository CI run at the final SHA; finalize this task through the Backlog CLI.
Implementation Notes
Decision: the task originally required a ci recipe, but the ratified fleet amendment and its binding task comment prohibit ci without Docker, service-container, or cross-compilation work. This repository has none. Coverage remains in test/check so the build workflow retains TestResults.xcresult; the obsolete ci acceptance criterion was replaced through the CLI.
Repaired the strict-lint refactor by separating export models/support and session helpers; strict lint is clean and the focused LLM export suite passed (8 tests). Added the standards-compliant just task surface, routed eligible workflow steps through it, and updated developer documentation and definition of done. Broader build, extension, appcast, workflow, and final review gates remain.
Parked: the local implementation and workflow gates are complete, but the mandatory CodeRabbit review could not begin because its plan rate limit is exhausted and no on-demand review is available. Resume by retrying coderabbit review –agent –base main after review capacity is restored; fix any material findings, then commit the staged migration, push to main, wait for a successful CI run at that SHA, and finalize this task.
Unparked and completed 2026-08-29. CodeRabbit’s four doc findings were all fixed. A follow-up gated check on [macos]: it depends on [macos] recipes, and just validates the whole file at parse time, so every Linux invocation failed until then. Migration at 0ecbcd8; exact-head CI green.
Comments
author: campaign-ordering created: 2026-08-29 09:18
Fleet ordering — WAVE 2. Starts after the Wave 0 pilot (sf2loki / SFL-0073) and the Wave 1 hubs land.
Within Wave 2 the order is free — these repos do not depend on each other. Batching by language is worthwhile so one lane reuses its Makefile-to-recipe mapping across similar repos.
Do not start before the pilot reports. The standard may be amended off the back of it, and picking this up early risks coding against a superseded seam.
Provisioning just in CI. Which mechanism depends on the runner, and the two must not be mixed:
| Runner | Mechanism |
|---|---|
arc-arm64 (m7kni self-hosted) |
just is baked into the runner image by m7kni/ci-tools (runner-image/Dockerfile, ARG JUST_VERSION). Do not add extractions/setup-just, and delete the step if this repo already has one — it installs a second just earlier on PATH and turns the image pin into a lie. |
GitHub-hosted (all rknightion repos) |
extractions/setup-just, SHA-pinned, with an explicit just-version:. |
Both sides currently sit on 1.58.0 and are Renovate-managed. ci-tools’ Tool version drift workflow fails if the Dockerfile ARG and the published image ever disagree, and lists any repo still carrying a second pin.
While you are in the workflow files, check the hub pin. On 2026-08-29 Renovate was unfrozen for rknightion/.github in m7kni/renovate-config — it had been enabled: false on the mistaken belief that callers tracked @main, which froze the fleet across 19 different hub SHAs (v1.3.1 June → v1.9.7 August) so that no hub fix ever propagated. Bumps now arrive as one grouped, CI-gated, automerged PR per repo. A uses: whose comment is not a real # vX.Y.Z still cannot be bumped (it resolves to a digest-only update, which the fleet rules disable) — if you find one, repair the comment as part of this task.
author: campaign-ordering created: 2026-08-29 10:42
Standard amendment — ci is the sanctioned superset of check (RATIFIED)
This supersedes the frozen wording “check is the complete local gate and reproduces every CI job that can run off a GitHub runner”, which several lanes could not honour without making the pre-commit gate depend on a Docker daemon.
The definitions now are:
check— everything that runs with only the language toolchain installed. This is the pre-commit gate. A leg that runs on a bare toolchain belongs here however long it takes.ci—checkplus the legs CI gates that need a Docker daemon, a service container, or cross-compilation, and nothing else. Written asci: check <heavy legs>.
Every leg you put in ci must carry a comment naming which of those three it needs. That comment is the guard: without it ci becomes the bin for anything slow or awkward, check quietly stops meaning much, and the fleet is back to a per-repo gate.
Eleven of the 42 lanes arrived at this shape independently before it was ratified, which is why it won.