# The stable toolchain's own binaries first: cargo finds its clippy and # rustfmt on the PATH, where another install's (Homebrew's) can come first # and lint with an older clippy than the check workflow's. toolchain := `dirname "$(rustup which --toolchain stable cargo)"` cargo := "env PATH='" + toolchain + ":" + env_var('PATH') + "' rustup run stable cargo" # oxfmt, from the web pages' packages, and what it formats. oxfmt := "web/node_modules/.bin/oxfmt" js := "web harness docs-site marketing/video" ignore := "(tests/|harness/|examples/|rustlib/)" # Install the reference PDS harness, the web pages' packages and coverage tools. setup: cd harness && npm install --no-audit --no-fund cd web && npm install --no-audit --no-fund rustup component add llvm-tools-preview --toolchain stable command -v cargo-llvm-cov >/dev/null || cargo install cargo-llvm-cov test: {{cargo}} test --workspace # Tests while working: only those `args` pick, as cargo test takes them, # against the harness `just pds` left running if there is one. For a change # to the web pages, `just t -p knotless-serve --test web`. t *args: KNOTLESS_TEST_NET={{justfile_directory()}}/target/test-net.json {{cargo}} test {{args}} # The source files changed since tests last passed with `t-changed`, and # the tests that cover them. changed: @node harness/changed.mjs # Runs the tests `changed` names, and marks them passed. t-changed: @node harness/changed.mjs --run # How the merge queue is doing, by day: queued to landed, ejections, # batch times, what cancels checks, and tests passing only on a retry. # `git fetch` first; `--days `, `--json`, another `/`. queue-stats *args: @node harness/queue-stats.mjs {{args}} # How a pull's files page does in a browser, loaded and read to its end: # script, layout, long tasks, fetches. `just files-bench `; # `--throttle 4`, `--nohl`, `--profile` (harness/files-bench.mjs). files-bench *args: @node harness/files-bench.mjs {{args}} # A reference PDS left running for tests to share, so each test binary # need not start its own. Stop it with ctrl-c. pds: mkdir -p target tail -f /dev/null | node harness/pds.mjs --accounts seed > target/test-net.json # The same, against the spaces alpha. test-spaces: KNOTLESS_DEV_ENV={{spaces}} {{cargo}} test --workspace # The spaces alpha build of the reference PDS, for private repositories. spaces := env_var_or_default("KNOTLESS_DEV_ENV", "../atproto-space-ref/packages/dev-env") # Fails under 100% of lines or functions, over runs against the stock # reference PDS and the spaces alpha together. cov: node harness/cov.mjs '{{spaces}}' '{{ignore}}' # Coverage over the stock run alone, failing on nothing: quicker than # `cov`, but what only the spaces alpha reaches shows as missing. cov-fast: KNOTLESS_DEV_ENV= {{cargo}} llvm-cov --workspace --ignore-filename-regex '{{ignore}}' --show-missing-lines cov-html: {{cargo}} llvm-cov --workspace --ignore-filename-regex '{{ignore}}' --html --open # The lexicons: goat's lint (github.com/bluesky-social/goat), less the two # warnings kept on purpose (CLAUDE.md); what that lint does not see; that # they only grow from main's; and that the XRPC answers the screenshots # recorded are as they say. lex: node harness/lexicons.mjs node harness/answers.mjs # Docs links, anchors and diagrams; needs web/node_modules (npm ci in web/). docs: node harness/docs.mjs # The docs as the site at /docs serves them, at http://localhost:4321/docs/. docs-site: cd docs-site && npm ci --no-audit --no-fund && npm run dev lint: {{cargo}} fmt --all --check {{oxfmt}} --check {{js}} {{cargo}} clippy --workspace --all-targets -- -D warnings node harness/layers.mjs node harness/contrast.mjs cd web && npm run check # Formats the Rust (rustfmt) and the pages', the harness's and the docs # site's code (oxfmt, by .oxfmtrc.json), as `lint` and the checks expect. fmt: {{cargo}} fmt --all {{oxfmt}} {{js}} # Latency of agent operations at a given round-trip time, in milliseconds. bench rtt="50": {{cargo}} build --release {{cargo}} run --release --example bench -- --rtt {{rtt}} # What a social card costs to draw, stage by stage, and the PNGs, written # to target/cards to look at. bench-cards: {{cargo}} run --release -p knotless-serve --example cards -- --out target/cards serve *args: {{cargo}} run --release -p knotless-serve -- {{args}} # Builds the image on a host and restarts it there (deploy/README.md). deploy *args: deploy/deploy.sh {{args}} # What knotless logs on the host: `just logs serve 'select(.duration_ms > 1000)'` # (deploy/logs.sh, deploy/README.md). [positional-arguments] logs *args: deploy/logs.sh "$@" # Builds the client for every target and puts it on the host, for # /install.sh to hand out (deploy/README.md). release *args: deploy/release.sh {{args}} # The web pages, built into web/build: a SvelteKit app that draws what # knotless-serve answers as XRPC queries. Its types come from the lexicons. web: cd web && npm run build # The pages, from web/build, for `just serve --web http://127.0.0.1:2591` # to hand them to, reading from that server. pages: web HOST=127.0.0.1 PORT=2591 KNOTLESS_SERVE=http://127.0.0.1:2590 HOST_HEADER=x-forwarded-host PROTOCOL_HEADER=x-forwarded-proto node web/build # The pages, redrawn as their source changes, reading from a # `just serve` on its default port. web-dev: cd web && npm run dev # What moved in the pages since main: those whose HTML differs, shot as # main draws them and as this checkout does (harness/shots/compare.mjs), # each that moved left in target/shots/results to look at, as its # expected, actual and diff. The pages are drawn from what # harness/shots/fixtures.json recorded, so no PDS is needed. Arguments go # to Playwright: -g ' profile$' for one page, or --project=desktop-light. # SHOTS_BASE compares with another commit. shots *args: web SHOTS_MOVED=all node harness/shots/compare.mjs {{args}} # The iOS app's screens as this checkout draws them, in any state, as pictures # in target/look: `just ios-look pull-open`, `just ios-look pull-open --set # getMergeStatus.state=conflicting --top --light`. A Mac's, so no check runs # it. Its options are at the top of ios/look.mjs. [positional-arguments] ios-look *args: node ios/look.mjs "$@" # Pele for the iOS app's diagrams, the version the pages have: run after # raising it in web/, as the ios check fails while the two differ. ios-pele: cd web && npm ci cp web/node_modules/pele/dist/pele.global.js ios/Knotless/Vendor/pele.js # The same, failing on a page that moved but that no commit since main # names on a `Shots-moved:` line, as the shots check fails on one its # pull's body does not name. shots-check *args: web node harness/shots/compare.mjs {{args}} # A look at pages as this checkout draws them, in any state, as pictures # in target/look to open: `just look pull-open`, `just look / --set # 'getFeed.events=[]' --signed-in`, `--click 'details.popover summary'`. # Drawn from harness/shots/fixtures.json, with no PDS; its options are at # the top of harness/shots/look.mjs. [positional-arguments] look *args: web node harness/shots/look.mjs "$@" # This checkout's pages drawn from the recording, against the same pages # behind knotless-serve over a seeded PDS, the one `shots-up` keeps up if # it is running: a page that moved is one whose recording is out of date. shots-live *args: web {{cargo}} build --bins SHOTS_LIVE=1 node harness/shots/compare.mjs {{args}} # The social cards, drawn by knotless-serve from a world, into # target/shots/cards to look at. shots-cards *args: web {{cargo}} build --bins cd harness && SHOTS_LIVE=1 npx playwright test -c shots/playwright.config.mjs --project=card --update-snapshots=all {{args}} # The shots check as CI runs it: in Playwright's image, under Docker, as # linux/amd64. Its node_modules are volumes of their own, as this # machine's may not be Linux's. shots-linux *args: docker run --rm --platform linux/amd64 -v "{{justfile_directory()}}":/w -w /w \ -v knotless-shots-node:/opt/node -v knotless-shots-web:/w/web/node_modules \ -v knotless-shots-harness:/w/harness/node_modules \ mcr.microsoft.com/playwright:v1.58.2-noble bash harness/shots/linux.sh {{args}} # Records again what knotless-serve answers for each page, from the world # `shots-up` keeps up or a new one, into harness/shots/fixtures.json: after # a change to what the server answers, or to the world. shots-record: web {{cargo}} build --bins node harness/shots/fixtures.mjs # The screenshots' PDS, seeded once and left running, so `shots` need only # start the server. Stop it with ctrl-c. shots-up: web {{cargo}} build --bins tail -f /dev/null | node harness/shots/world.mjs # The pages used as people use them, in a browser: logging in through the # PDS, then every write the pages make, each read back on the page and # from the PDS (harness/e2e/flows.spec.mjs), over a world of its own seeded as the # shots' is. Arguments go to Playwright: -g 'logging in through|starring' # for one flow, after the login it needs. e2e *args: web {{cargo}} build --bins cd harness && E2E=1 npx playwright test -c e2e/playwright.config.mjs {{args}} # The private flows (harness/e2e/private.spec.mjs): a private repository # used on the pages, over a world on the spaces alpha `spaces` names, a # build of test.yml's ATPROTO_COMMIT. e2e-private *args: web {{cargo}} build --bins cd harness && KNOTLESS_DEV_ENV={{spaces}} E2E=1 npx playwright test -c e2e/playwright.config.mjs {{args}} # Videos of knotless (marketing/video, marketing/README.md), into # marketing/video/out as wide, square and tall mp4s. A pull's way from # its first push to the deploy that shipped it, read from the appview: # `just video-change https://knotless.dev///pull//`. video-change url: cd marketing/video && { [ -d node_modules ] || npm ci --no-audit --no-fund; } && node change/story.mjs {{url}} && node render.mjs change "out/stories/$(basename {{url}}).json" # The pages filmed over the e2e world, as `just e2e` seeds it: each scene # of marketing/video/shoot/scenes, `--phone` for a phone's. video-shoot *scenes: web {{cargo}} build --bins cd marketing/video && { [ -d node_modules ] || npm ci --no-audit --no-fund; } && node shoot/shoot.mjs {{scenes}} # A reel of what video-shoot filmed (REELS in marketing/video/video/Feature.tsx). video-feature reel: cd marketing/video && node render.mjs feature {{reel}} # Agents doing a day's git work through the MCP server, one fresh session # per step: `harness/scenarios/tracks`, or only the tracks named. Reports # what tripped them up under target/scenarios/. Each run costs model usage. scenarios *tracks: {{cargo}} build -q --workspace --bins node harness/scenarios.mjs {{tracks}}