CtrlK
BlogDocsLog inGet started
Tessl Logo

petrinaut-shipping

End-to-end procedure for shipping a change to the Petrinaut packages (libs/@hashintel/petrinaut, petrinaut-core, petrinaut-cli, apps/petrinaut-opt, apps/petrinaut-website): the verification gates per package, the changeset step, docs sync, pre-PR hygiene checks, and CI expectations from draft to ready. Use when implementing any change in these packages, when creating a PR for Petrinaut work, when running tests or lints for them, or when checking CI on a Petrinaut PR.

SKILL.md
Quality
Evals
Security

Shipping a Petrinaut change

Standing conventions (changeset policy, docs and diagram placement, CI quirks, the petrinaut-opt boundary) live in libs/@hashintel/petrinaut/AGENTS.md and apps/petrinaut-opt/AGENTS.md. This skill is the procedure that applies them.

Gates

Run for every touched package, after every increment:

yarn fix:format >/dev/null 2>&1
turbo run build test:unit lint:tsc lint:eslint --filter @hashintel/petrinaut --filter @hashintel/petrinaut-core --filter @hashintel/petrinaut-cli --force --output-logs errors-only
yarn lint:format
  • turbo is the version pinned by mise in .config/mise/config.toml; running it through npx bypasses the pin and downloads the latest release.
  • Trim the --filters to the touched packages; add --filter @apps/petrinaut-website when it consumes the change.
  • build belongs in that list. test:unit depends on ^build, which builds dependencies and not the selected package, so the React Compiler check, the browser-entry check, and the website's Vite build are otherwise never run.
  • Structure changed (new folder, moved module): also yarn workspace @local/petrinaut-arch-docs lint:arch-docs, and add the layer declaration the AGENTS.md architecture section calls for.
  • Arch-docs authored content or content/diagrams/*.d2 changed: lint:arch-docs does not compile MDX or render D2. Run the site build once before pushing: mise x -- yarn exec turbo run build --filter @apps/petrinaut-docs. D2 labels containing : or [ must be quoted.
  • Python packages go through Turborepo: turbo run test:unit lint:ruff lint:types --filter @apps/petrinaut-opt --filter @local/petrinaut-python --force. A plain uv run pytest skips the end-to-end tests when the CLI bundle is missing, and skips the comparison of regenerated openapi.json that @apps/petrinaut-opt runs as part of its own test:unit.
  • Formatting is oxfmt via the yarn scripts; never run prettier directly. yarn lint:format prints its verdict before its final line, so check the exit code rather than the last line of output.

Changesets

One patch changeset per PR covering the published packages the PR touches (@hashintel/petrinaut, @hashintel/petrinaut-core); none for pure refactors. Keep the text to one or two plain sentences. See the AGENTS.md conventions for the full policy.

Docs sync

User-visible behaviour changes update the user guide in the same PR; new pages need registration and a raw import, both test-enforced. The steps are in the "User-facing docs" section of libs/@hashintel/petrinaut/AGENTS.md. Doc screenshots cannot be uploaded by an agent: produce candidate captures, list the exact pages and sections to re-capture, and flag "screenshots pending" in the PR body and the summary.

Pre-PR hygiene

  • Read git diff --stat against the base: no accidental directories, no unstaged leftovers, no generated output, no mise.lock churn.
  • A Bin line in the stat for a text file means escape sequences became literal control bytes; fix it before pushing or the diff is unreviewable.
  • A diff too large for one review gets split into stacked PRs, one concern per layer.

Draft, CI, ready

  • Open the PR as a draft, body per the repo PR template.

  • Watch checks until the run settles, and decide that in jq. Neither the exit status nor a pending count answers it alone: gh pr checks exits non-zero when checks are pending, when they failed, and when none are scheduled yet, and with --json it prints [] before any exist, so a pending count of zero also reads as "finished" on a PR that has not started.

    settled='[length, ([.[] | select(.bucket == "pending")] | length)] | .[0] > 0 and .[1] == 0'
    for _ in $(seq 60); do
      [ "$(gh pr checks NNNN --json bucket --jq "$settled" 2>/dev/null)" = "true" ] && break
      sleep 60
    done
    gh pr checks NNNN

    The wait ends once checks exist and none are pending, failures included, so the table below reports them. Anything else, an empty list or a failed call, keeps waiting. The loop is bounded and the last call keeps its output, so an expired token or an unreachable API surfaces as an error rather than sleeping for ever.

  • Judge failures against the CI bullet in the AGENTS.md conventions (Bench-CI non-blocking, the known flaky check, Vercel-side docs failures) before treating them as caused by the diff.

  • Flip to ready only when checks are green. AI reviewers run at that point; triage their threads rather than leaving them unresolved.

  • End any turn that changed the branch by stating what was committed and pushed, or that nothing was.

Repository
hashintel/hash
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.