Skip to content

fix(process-graph): consumer-facing surface found by the Switchboard integration - #53

Merged
Mearman merged 12 commits into
mainfrom
fix/consumer-surface
Oct 11, 2026
Merged

Mearman merged 12 commits into
mainfrom
fix/consumer-surface

Conversation

@Mearman

@Mearman Mearman commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

Fixes the problems the Switchboard integration (ExaCapLtd/switchboard#345) found in the published surface of process-graph.

Stable peer dependencies are now caret ranges whose floor is the version the workspace tests run against; the oRPC betas, drizzle-orm's release candidate and the SQLite wasm build stay exact, because a prerelease has no compatible-range semantics. scripts/check-published-manifests.ts validates peers against the catalog by satisfies instead of equality, requires the caret form for a stable peer and an exact version for a prerelease, and keeps dependencies and devDependencies exact. The smoke consumer installs each peer at its declared floor.

prepareDataFile (0700 directory, 0600 database before SQLite opens it, narrowing an owned store others can read) moves into sqlite-node and is exported from process-graph/store/node, and nodePorts({ dataPath }) gives a host-owned data file the same private treatment as the command line's own XDG file.

The exports map carries ./package.json, and process-graph/cli exports version, written into the bundle by tsdown's define from the manifest at build time; the smoke test imports both from the packed tarball.

The library sync now throws SyncInterruptedError, carrying the resume request, the scopes not reached and whether pages were saved as data; only the command line renders the resume with: and then: hints. A token port that fails is reported by the GitHub adapter as unauthorised, naming the token port and owner, so the command line exits with the source-failure status, and the interrupted sync's cause is the port's own error, one level deep.

process-graph/ui/theme exports the theme contract vars, the light and dark theme classes and their values without the components, built as a separate entry of graph-ui; the smoke test imports it in the consumer that has none of the optional peers, where React does not resolve.

modelSchema is declared as the exported interface ModelSchema (its unwrap() still returns the strict object schema), so a consumer's z.object({ model: modelSchema }) under declaration: true emits a reference to ModelSchema instead of the whole schema with trilean's JsonValue inline (TS7056). A test compiles that consumer module with the TypeScript API and checks the emit has no diagnostics and names the interface.

The root README gains an install section saying a project with an install age gate must exempt trilean and trilean-regex beside process-graph.

Two tests that start Node processes (the umask probe, now beside prepareDataFile in sqlite-node, and the CLI's bin smoke test) run without a per-test clock bound, following the projection package's precedent, since their duration is the machine's scheduling.

Pinning every peer dependency to one exact version forced each consumer
onto exactly those versions. A stable peer is now a caret range whose
floor is the version the workspace tests run against; the oRPC betas,
drizzle-orm's release candidate and the SQLite wasm build stay exact,
since a prerelease has no compatible-range semantics.

The published-manifest check validates peers against the catalog by
satisfies rather than equality, requires the caret form for a stable
peer and an exact version for a prerelease one, and keeps dependencies
and devDependencies exact. The smoke consumer installs each peer at the
floor its specifier declares.
…ta path

prepareDataFile, which creates a store's directory 0700 and its database 0600
before SQLite opens it and narrows an owned store others can read, moves from
the command line into sqlite-node, so process-graph/store/node exports it
with its modes and options for any host that opens a store itself.

nodePorts takes a dataPath: a data file the host owns, used as the default
data path and given the same private treatment as the command line's own XDG
data file, so a host embedding the command line keeps its store private.
A token port that threw anything but a GraphSourceError escaped the adapter
as a plain error, which the router turned into an internal server error, so
the command line exited with a general failure and the port's own error sat
two causes deep. The adapter now wraps it in an unauthorised GraphSourceError
naming the token port and the owner, with the port's error as its cause, and
passes a GraphSourceError the port throws, or an abort, through unchanged.
The library sync operation ended its error with command-line wording
("resume with: sync ..."), which a host calling it as a library could only
parse. It now throws SyncInterruptedError, a CliError carrying the source's
failure as its message, the request that resumes the failing source, the
scopes not reached and whether pages were saved; the command line renders
those as the commands it prints. The error's cause is the error underneath
the source failure, so a token port's own error is one level deep, and such
a failure exits with the source-failure status.
…stant

A consumer had no way to read the package's version. The exports map now
carries ./package.json, and process-graph/cli exports version, which tsdown
writes into the bundle through define from the manifest when the package is
built, so reading it touches no file at run time. Vitest applies the same
define, and the bin's --version uses the constant too. The smoke test imports
both from the packed tarball.
The umask probe opened the command line's default store through nodePorts,
loading the whole command line to test a guarantee that belongs to
prepareDataFile. It now lives in sqlite-node and prepares and opens a store
directly, with no per-test clock bound, since a spawned process's duration is
the machine's scheduling; the command line keeps an in-process check that it
hands its XDG data file to prepareDataFile as one it owns.
The test starts the built bin as a process three times, so how long it takes
is the machine's scheduling rather than a property of the code, and the
default per-test bound failed it on a loaded host. It now runs unbounded, as
the projection package's work-counting cases do, leaving a hang to the CI
job's own timeout.
A consumer with an install age gate exempted process-graph but still could
not install it, because trilean and its dependency trilean-regex are ExaDev
packages released alongside it and often younger than the gate. The root
README gains an install section saying so, and the package README points
to it.
The theme contract vars was only exported beside the components, so a
Vanilla Extract stylesheet that themed them loaded the whole component
library and React. The ./theme entry carries vars, the light and dark theme
classes and their values on their own; the library build emits it as a
separate file sharing one chunk with the components' entry.
process-graph/ui/theme exports the theme contract, the theme classes and
their values without the components, for a .css.ts file that themes
process-graph/ui. The smoke test imports it in the consumer project that has
none of the optional peers, where React does not resolve, and checks it
carries the same contract and values as process-graph/ui.
A consumer that embedded modelSchema in a schema of its own and emitted
declarations had the whole model schema written out inline, trilean's JSON
value included, which could exceed the length TypeScript will serialise
(TS7056) and forced an annotation. modelSchema is now declared as the
exported interface ModelSchema, whose unwrap still returns the strict
object schema, so such a declaration refers to the name. A test compiles a
consumer-style z.object({ model: modelSchema }) under declaration: true and
checks it emits without diagnostics and names ModelSchema.
@Mearman
Mearman marked this pull request as ready for review October 11, 2026 17:11
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-11T17:29:44.606980Z afdc3bf Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

The test builds a TypeScript program over the schema package and zod's
declarations, which under coverage instrumentation on a CI runner took past
the default per-test bound. Its duration is the compiler's work on the
machine at hand, so it runs unbounded, leaving a hang to the job's timeout.
@Mearman

Mearman commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

@codex security review

1 similar comment
@Mearman

Mearman commented Oct 11, 2026

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: afdc3bffdf

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@Mearman
Mearman merged commit 0a4bf62 into main Oct 11, 2026
14 checks passed
@Mearman
Mearman deleted the fix/consumer-surface branch October 11, 2026 18:24
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.

1 participant