Skip to content

H-6788: Port the Petrinaut GAIOS wrapper's diff support and deploy it from Vercel - #160

Draft
claude[bot] wants to merge 10 commits into
mainfrom
cm/h-6788-petrinaut-gaios-vercel-deploy
Draft

H-6788: Port the Petrinaut GAIOS wrapper's diff support and deploy it from Vercel#160
claude[bot] wants to merge 10 commits into
mainfrom
cm/h-6788-petrinaut-gaios-vercel-deploy

Conversation

@claude

@claude claude Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

<!-- ccr-slack-attribution -->
Requested by Ciaran Morinan · Slack thread

Before: pocs/petrinaut-patchwork pinned Petrinaut 0.0.11 and prop-drilled the net into the editor, published through the patchwork CLI, and offered a chat host no way to author a net beyond the existing patchwork:skill. Opening a draft showed the net exactly as main did, with nothing to indicate what the draft had changed. Nothing built or deployed the package: publishing was a manual pnpm push from someone's laptop.

After: the wrapper tracks Petrinaut 0.0.16 through its handle-driven editor API, publishes with pushwork, registers an llm:skill so Patchwork's chat computer can build nets against the raw document, and lights up on the canvas whatever the current draft added, changed or removed. A vercel.json builds the package on push and serves package.json alongside dist/, so a Patchwork host can load the tool from a stable URL.

How

The first four commits are ported unchanged from inkandswitch/hash-labs@petrinaut-with-diffs, cherry-picked with Paul Sonnentag's authorship intact. That branch is a four-commit descendant of 4c1a5fd on this repo's main with no file overlap against anything since, so the port is a clean fast-forward of pocs/petrinaut-patchwork and touches nothing else.

The fifth commit adds vercel.json. The build stages a vercel-static/ directory holding package.json next to dist/, and serves that as the output directory, because a Patchwork host fetches package.json at the root of the URL it is given and resolves that file's exports entry relative to the same root — so the deployment has to serve /package.json and /dist/index.js, which is also the layout pushwork sync mirrors into an Automerge folder doc. The Access-Control-Allow-Origin: * header is required because the host loading the module is a different origin, and Vercel sends no CORS header by default.

Setting up the Vercel project

Two things cannot be expressed in vercel.json and have to be set on the project:

  • Root Directory must be pocs/petrinaut-patchwork. There is no rootDirectory property in the vercel.json schema, so this is dashboard-only. Install command, build command and output directory all come from the committed vercel.json and should be left unset.
  • The URL handed to Patchwork must be the production alias (PROJECT.vercel.app), not a deployment-specific URL. Vercel protects every deployment except the most recent production one by default, so a preview URL answers the host's package.json fetch with a 401. The URL must also not end in .js, or the host skips package.json resolution entirely.

There is deliberately no ignoreCommand: the documented pattern (git diff --quiet HEAD^ HEAD -- ., where exit 0 means skip) only inspects the tip commit, and this repo merges through a merge queue that can advance main by several commits at once — so a batched merge group could silently skip a deploy that should have happened. Worth adding later if the build volume becomes annoying, but a wasted build is cheaper than a missed one.

Notes

  • Verified locally: pnpm install --frozen-lockfile and pnpm build both pass on pnpm 10.6.0 / Node 22, producing 27 files and 25 MB in dist/, and the staged vercel-static/ has the intended layout. No CI in this repo builds JS, so that check was manual.
  • tsc --noEmit reports one error, src/skill-api.ts:2 importing the undeclared @patchwork/llm. It is identical on main, type-only, and stripped by esbuild, so the build is unaffected. Left alone as pre-existing; worth a follow-up to either declare the dependency or drop the import.
  • The @hashintel/ds-helpers@0.1.2 and @hashintel/petrinaut@0.0.16 pins are load-bearing, for the reasons the README now records. Renovate runs on this repo and no workflow builds this package, so a bump here will break the deployment silently.
  • src/SKILL.md still documents no inhibitor arcs, although skill-api.ts now reads and writes an input-arc type. Ported as-is.

paulsonnentag and others added 5 commits September 1, 2026 12:55
The tool could not be built, so it could not be published. `@hashintel/ds-components`
imports `@hashintel/ds-helpers/css`, but `ds-helpers@0.1.1` is a broken publish whose
tarball omits the `styled-system` directory its `files` field promises, so the import
fails to resolve. `0.1.2` is the only release that ships it; later ones, `0.2.1`
included, are broken the same way, which rules out moving to a newer `@hashintel/petrinaut`
until the release is fixed upstream.

Pin `ds-helpers` to `0.1.2` as both a direct dependency and a `pnpm.overrides` entry.
The override alone has no effect, because `ds-helpers` reaches this package as an
auto-installed peer of `ds-components` rather than as a dependency.

Adopt pushwork as the publishing path, matching `packages/gaios` in CatColab: take it
as a devDependency and point the `push` script at `pushwork sync`, replacing the
`patchwork push` command that no longer exists. Pushwork always ignores `node_modules`
and reads `.pushworkignore` rather than `.gitignore`, so `dist` still syncs as the
artifact directory while staying out of git.

Move `@inkandswitch/patchwork-bootloader` to devDependencies, where it belongs now that
it only supplies the build-time externals list, and bump it to ^0.6.2 so that list
matches the importmap the Patchwork hosts actually serve. Under ^0.0.4 the list was a
stale subset, which bundled second copies of modules the host already provides.
0.0.16 redesigned the editor's props. It no longer takes a net definition plus a
mutation callback; it takes a `PetrinautDocHandle`, Petrinaut's own interface over
a bare SDCPN. Adapt the Automerge handle Patchwork supplies into that shape,
projecting the net out of `petriNetDefinition` and writing edits back in place so
the surrounding `title` and `@patchwork` metadata are untouched. This also retires
the React Automerge hooks and the `RepoContext` wrapper, which resolves the
duplicate-`Repo` type conflict they carried.

Pass the simulation, Monte Carlo and language-server workers explicitly from
`@hashintel/petrinaut-core/workers/*`, which is what consumers of the published
dist are meant to do — the bundled fallback workers are only reliable for source
builds. Those exports are factory functions rather than worker entry scripts, so
importing them with Vite's `?worker` suffix builds without complaint but emits
empty 1-byte workers.

Follow the data model where it moved: input arcs now carry a `type` of "standard"
or "inhibitor", and a differential equation's `colorId` is nullable. The skill API
gains inhibitor arcs as a result, since they are new capability rather than only a
type change.

Pin `@hashintel/ds-components` forward to ^0.2.2 to match what 0.0.16 expects,
while holding `@hashintel/ds-helpers` at the broken-publish workaround of 0.1.2 —
the new chain only needs its `css` and `tokens` entry points, both of which 0.1.2
ships. Both have to be direct dependencies: they arrive as auto-installed peers,
which pnpm resolves before overrides apply, so an override alone does not bite.

Bump `@inkandswitch/patchwork-plugins` to ^1.2.2 and move it to devDependencies
alongside the bootloader. It is externalized through the host's importmap, so it
is a build-time type dependency only, and the old version pinned automerge-repo
to 2.5.0 against the 2.5.4 used elsewhere.
The existing `patchwork:skill` pairs SKILL.md with the typed API in skill-api.ts,
which only helps an agent that can call that API. Patchwork's chat computer has
no such surface — it edits documents through generic read_doc and automerge_op
calls — so it needs the net's schema written out to do anything useful with a
Petrinaut document.

Register an `llm:skill` carrying the SDCPN shape, the automerge_op recipes for
adding and removing entities, and the exact module contracts for the code
surfaces (lambdas, transition kernels, dynamics, visualizers, metrics). It names
the traps that are invisible from the document alone: place names are part of the
code surface, so renaming one breaks every lambda that reads it, and deleting a
place leaves arcs pointing at nothing.

The skill declares the petrinaut-petrinet datatype, so it activates on its own
whenever such a document is focused.

The domain content is adapted from `petrinautAiPrompt`, which petrinaut-core now
exports, rather than reused verbatim: that prompt directs the model at Petrinaut's
own in-app tools, which do not exist in the Patchwork chat.
Subscribe to Patchwork's `draft:baseline` provider and diff the net at the
draft's fork point against the live one, the way the gaios tool does for
CatColab notebooks.

Petrinaut takes no diff prop, so the result is drawn from the outside: added
and edited places, transitions and arcs glow via a stylesheet keyed on the ids
React Flow stamps onto its nodes and edges, and removed ones are redrawn as
ghosts in React Flow's viewport portal, which pans and zooms with the canvas.
Nothing is written back into the net, so a removed place cannot turn up in the
sidebar, the compiler or a simulation.
The build stages package.json next to dist/ in vercel-static/ and serves
that directory, because a Patchwork host fetches package.json at the root
of the URL it is given and resolves that file's exports entry relative to
the same root. The Access-Control-Allow-Origin header is needed because
the host loading the module is a different origin.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
petrinaut-gaios Ready Ready Preview Sep 4, 2026 8:11am UTC
petrinaut-hazel Ready Ready Preview Sep 4, 2026 8:11am UTC

Request Review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Dependency Review

The following issues were found:

  • ❌ 4 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 59 packages with OpenSSF Scorecard issues.

View full job summary

@claude

claude Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

setup and the Merging enabled gate are red here, but not because of this PR — they fail identically on main.

The setup job in rust.yml dies in step 1, Set up job, before checkout ever runs:

Could not find file '/home/runner/work/_actions/temp…/_staging/labs-711a0263…/apps/sim-core/scripts/builtin_behaviors'.

rust.yml:40 is uses: $GITHUB_REPOSITORY/.github/actions/setup-rust-ci, which makes the runner download the whole repository as an action tarball. Extraction fails on a dangling symlink: apps/sim-core/scripts/builtin_behaviors points at ../packages/core/src/behaviors, which does not exist — the behaviours live under apps/sim-core/packages/engine/src/behaviors.

It isn't this branch: the symlink blob is byte-identical on main (fa094aa), setup fails at the same step on the main runs of 24 and 26 August and on #157, #158 and #159, and no Rust run has passed since 27 July. lint is skipped as a consequence, and Merging enabled fails purely on its check setup step. PRs have continued to merge with it red, so the gate evidently isn't enforced.

No fix for it exists anywhere to port into this PR, and repointing the symlink needs someone who knows whether packages/engine/src/behaviors is the intended target, so it belongs in its own change rather than widening this one. The smallest patch is either ln -sfn ../packages/engine/src/behaviors apps/sim-core/scripts/builtin_behaviors, or dropping the symlink and having apps/sim-core/scripts/upload_builtin_behaviors.ts:73 read the real path directly.

Vercel – hcore and the FOSSA License Compliance status are also red on main and on #159, so likewise not from this change. Dependencies / Review is the one red check that does look like ours, and I'm chasing that separately.


Generated by Claude Code

@claude

claude Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Dependencies / Review is this PR's, and it is one package: postcss@8.5.14, newly added to pocs/petrinaut-patchwork/pnpm-lock.yaml at runtime scope. Two advisories apply — GHSA-r28c-9q8g-f849 (High, 7.5, patched in 8.5.18) and GHSA-fxqj-rqcc-2cmp (Moderate, 6.3, patched in 8.5.23), so 8.5.23 is the first version that clears both. The 51 OpenSSF Scorecard findings in the same report are warn-only and cannot fail the job; the reusable workflow sets no fail-on-scorecard-* input, and fail-on-severity defaults to low with fail-on-scopes defaulting to runtime.

Where it comes from: @hashintel/petrinaut 0.0.11 → 0.0.16 pulls @hashintel/ds-components@^0.2.2, and 0.2.2 newly lists @pandacss/dev@1.11.1 and @pandacss/preset-panda@1.11.1 in its runtime dependencies. Every one of the 236 paths to postcss in the lockfile runs through @pandacss/dev, and the @pandacss/* packages exact-pin postcss: 8.5.14. main's lockfile has no Panda at all.

There is nothing to port: @pandacss/core still pins 8.5.14 at every published version through 1.12.0, and @hashintel/ds-components pins @pandacss/dev: 1.11.1 at 0.2.2, 0.3.0 and 0.3.1. So no dependency bump available today lifts it.

The fix belongs upstream in hashintel/hash, at libs/@hashintel/ds-components. Panda is a codegen toolchain, not a runtime dependency of a component library: dist/main.js never references @pandacss/* and never loads the chunk that does — only the ./preset and ./tokens subpath exports do, and those are imported from a consumer's panda.config.ts at codegen time. Moving @pandacss/dev and @pandacss/preset-panda into devDependencies (with peerDependencies entries for consumers that do run Panda against ./preset) and publishing a patch would drop the whole Panda subtree, postcss included, out of this POC's graph — and clear most of those 51 Scorecard warnings for every other consumer too.

Actual exposure here is nil, which is why this isn't being treated as urgent: both advisories are a Node filesystem read triggered by parsing untrusted CSS, this package has no panda.config.* or postcss.config.* and registers no Panda or PostCSS Vite plugin, grep -rl postcss dist and grep -rl pandacss dist both return nothing, and main already resolves postcss@8.5.6 through Vite — which is inside the affected range of both advisories. The check flags this diff only because the version string is newly added.

Deliberately not doing two things: no global pnpm.overrides on postcss, which would hide the advisory rather than fix it, and no allow-ghsas entry — the reusable workflow's own contract reserves that for advisories whose database entry is wrong, not for accepting known risk, and these entries are correct.

One caveat for whoever does the bump: the @hashintel/ds-components 0.3.x line declares @hashintel/ds-helpers: ^0.2.1, while this package pins 0.1.2 because that is the last release shipping its styled-system directory. That interaction needs checking rather than assuming the bump is clean.


Generated by Claude Code

`@pandacss/core` exact-pins `postcss` 8.5.14, which carries
GHSA-r28c-9q8g-f849 and GHSA-fxqj-rqcc-2cmp, and `@pandacss/dev` reaches
the wrapper through `@hashintel/ds-components`' runtime dependencies.
Panda's own pin was itself a CVE bump rather than a compatibility
constraint, so moving it forward is safe. `hashintel/hash` carries the
same override in its root manifest.
Bump @hashintel/petrinaut to 0.0.19, @hashintel/petrinaut-core to 0.0.4,
@hashintel/ds-components to 0.3.1 and @hashintel/ds-helpers to 0.2.2. The
ds-helpers override is no longer needed because 0.2.2 ships its styled-system
directory again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4q91RTouLDQUpcwnTPfjN
petrinaut-core 0.0.4 made an arc's endpoint optional so that an arc can target
a component-instance port instead of a place, and added a "read" input-arc
type. The diff overlay and the skill API stay place-only, so arcs targeting a
component port are excluded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4q91RTouLDQUpcwnTPfjN
fast-uri and qs re-resolve on their own, because ajv and express already
allow the patched versions. browserslist and postcss-selector-parser need
overrides, because @pandacss/* exact-pins both in every published release,
including 1.12.0, and Panda only drops those pins in its v2 line, which is
still in beta. Overriding browserslist also collapses the dev-only copy
that @vitejs/plugin-react pulls in, since its range admits the patched
version. All four reach the graph as runtime dependencies only because
@hashintel/ds-components lists @pandacss/dev in dependencies, and none of
them appear in the built bundle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4q91RTouLDQUpcwnTPfjN
Petrinaut core supports a third input arc type, "read", which the chat
computer's instructions did not mention.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K4q91RTouLDQUpcwnTPfjN
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.

3 participants