Skip to content

chore(release): unblock the 0.1.0 publish pipeline - #51

Merged
rpvilo merged 5 commits into
mainfrom
feature/release-prep
Aug 3, 2026
Merged

chore(release): unblock the 0.1.0 publish pipeline#51
rpvilo merged 5 commits into
mainfrom
feature/release-prep

Conversation

@rpvilo

@rpvilo rpvilo commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

Gets the publish pipeline into a state where it can actually run. The headline is that the release workflow could not have succeeded as it stood — see the first item.

The blocker

.changeset/config.json still listed ignore: ["intentface"], the registry package deleted back in #44. Changesets treats an ignore entry that matches no workspace package as a fatal ValidationError, so changeset status, changeset version and changeset publish all aborted before doing anything:

ValidationError: The package or glob expression "intentface" is specified in the
`ignore` option but it is not found in the project.

Removed the entry. changeset status now resolves @intentface/chat at minor, and .changeset/README.md no longer refers to the deleted registry CLI either.

Changeset text

initial-release.md advertised a surface that no longer exists: StepQueue (removed in #30), ArtifactCard / ArtifactsPanel, and "styled components ship separately via the shadcn-compatible registry" (the registry was deleted in #44). Since this text becomes the published 0.1.0 changelog entry, it's rewritten to describe the eleven entry points that actually ship.

Workflows

Both were workflow_dispatch-only, commented "paused during initial development". Restored push: [main] + pull_request on ci.yml and push: [main] on release.yml.

Because this is the PR that turns CI on, it also has to leave main green — and main currently has two biome check failures, both pre-existing and unrelated to any of the above:

  • packages/chat/tests/a11y/harness.a11y.test.tsx — the negative-control fixture trips useValidAriaValues on its deliberate aria-label="". Extended the ignore comment. (Same one-line fix as in feat(composer)!: remove TipTap engine and batch pre-1.0 renames #50; identical text, so the two merge cleanly.)
  • components/chat.tsx — an unsorted import and an over-wrapped import block failing organizeImports. Applied Biome's own fix; reordering only, no semantic change.

Manifest

Added author and bugs to packages/chat/package.json. Deliberately did not add engines: the package targets browsers, and a Node floor there produces install warnings for consumers it doesn't actually constrain.

Verified

A real npm pack (not --dry-run, so the prepack/postpack export swap genuinely executes):

  • Packed manifest's exports point at ./dist/src/*, and publishConfig.exports is consumed — the swap works
  • All 11 entry points resolve to files that exist in the tarball
  • 25 files, 74.5 kB: dist/, LICENSE, README.md and nothing else — no source, tests, or scripts leaked
  • postpack restored the working tree to the ./src dev exports, leaving no package.json.prepack-bak

changeset version in a throwaway clone: bumps 0.0.0 -> 0.1.0 and generates a correctly attributed CHANGELOG.md. Worth knowing that @changesets/changelog-github hard-fails without a valid GITHUB_TOKEN (it escapes cleanly and changes nothing) — release.yml sets it at step level so both the action and the spawned bunx changeset version inherit it, which is correct.

Also green: 184 tests, tsc --noEmit, biome check, package build + publint.

Still needs you — not doable from here

  1. Configure a trusted publisher on npmjs.com for @intentface/chat, pointing at this repo and release.yml. This can only be done after 0.1.0 exists on the registry — npm requires the package to already be published, both via the web UI and via npm trust. So 0.1.0 gets published manually; every release after that comes from CI with no credentials.
  2. Flip the repo to public. Provenance is generated automatically under trusted publishing, but it requires a public source repository.

No NPM_TOKEN is needed — release.yml now authenticates via OIDC, so there is no long-lived secret to store or rotate. The @intentface org already exists.

Heads-up: npm/cli#8976 reports E404 on scoped packages published via changeset publish from changesets/action under OIDC — open since February 2026. That is this exact shape, so treat the first CI-driven release as unproven until it succeeds once.

Ordering

Merges cleanly with #50 (verified with git merge-tree). Either order works; both branch from main and target it independently.


Update: two release-blocking bugs in the built artifact

After the pipeline work above, I packed the tarball and installed it into a throwaway Next 16 app with no transpilePackages, so it resolved dist/ the way a real consumer does. That found two bugs that every green check we had — publint, tsc --noEmit, 184 tests, the app build — was structurally incapable of catching, because nothing in this repo has ever consumed the built output. The app compiles ./src via transpilePackages.

1. Every compound component shipped as unknown

bunup's default declaration emit uses TypeScript's isolated-declarations mode, which cannot infer the type of an Object.assign(Root, {...}) compound. It warned TS9010 and emitted:

declare const Thread: unknown;
declare const useThread: () => {};

All eight compound components — Thread, Composer, Message, Steps, Reasoning, Chip, Attachments, AskUser — plus several hooks and utilities were erased. The entire public API was unusable from TypeScript: JSX element type 'Thread' does not have any construct or call signatures.

Those TS9010 warnings were visible in the build all along, on main too, and I dismissed them earlier in this work as non-blocking because .d.ts files were still produced. That was wrong — they were produced with the types erased.

Fixed with dts: { inferTypes: true }, which routes declaration emit through tsc. reasoning.d.ts went from 568 B to 5.80 KB, which is the measure of how much was being dropped. publint passed before and after: it validates packaging, not type correctness.

2. The dev JSX runtime was bundled

dist/ imported jsxDEV from react/jsx-dev-runtime — 68 call sites, zero uses of the production react/jsx-runtime. Any consumer's production build throws:

TypeError: (0 , d.jsxDEV) is not a function

Bun's transpiler picks the runtime from the build process's NODE_ENV, and bunup was running with it unset. The jsx: { development: false } option looks like the declarative fix, but bunup 0.16.32 ignores it — verified, still 68 jsxDEV calls — so the build script pins NODE_ENV=production instead.

prepack also ran bare bunup, so it would have kept emitting the dev runtime even after build was fixed — and prepack is what produces the published tarball. It now calls bun run build, so there's one source of truth.

Verified against the packed tarball

With NODE_ENV explicitly unset in the shell, npm pack → install → next build:

  • 15 react/jsx-runtime imports, zero jsxDEV
  • Zero declare const … : unknown in any .d.ts
  • A "use client" page using the compounds, their sub-components, useComposer / useReasoning / useThread, and event handlers — builds and prerenders
  • A server component using /types, /message-utils, /chip-markdown — builds and prerenders
  • A real consumer install pulls 29 packages: only @floating-ui/dom and nanoid beyond React

Also documented: compound sub-components and RSC

While isolating the above I hit a third thing, which is a genuine React constraint rather than a bug. Reaching a sub-component from a server component fails at runtime:

// Server component → "Element type is invalid… but got: undefined"
<Composer><Composer.Container /></Composer>

A server component importing a client module gets a proxy of that module's named exports; it cannot read properties off an exported value, and the sub-components live on the Composer object. Radix-style import * as Dialog sidesteps this because the namespace's properties are named exports — a compound object can't. Bare <AskUser /> works; <AskUser.Header /> does not.

This needs no code change — chat UI is interactive, so consumers will be in client components anyway — but the failure mode is cryptic enough to deserve documentation, so the README now has a React Server Components section stating the rule and showing both shapes.

@vercel

vercel Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
intentface-chat Ready Ready Preview Aug 3, 2026 3:30pm

Request Review

@rpvilo
rpvilo merged commit e2a5e11 into main Aug 3, 2026
3 checks passed
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