Lexicon Onboarding Skill
This page is written to be handed to an AI agent on its own. Point yours at this URL and it has everything needed to take a lexicon from nothing to published, without reading the rest of the authoring section first.
Every step is either a command to run or a check to make. Where a step needs depth, it links out. Do the steps in order: later ones assume earlier ones.
What you are building
Section titled “What you are building”A lexicon is a plugin that teaches chant about one operational area. It lives
in lexicons/<name>/, is published as @intentius/chant-lexicon-<name>, and
is versioned in lockstep with core.
-
Scaffold
Terminal window npx tsx packages/core/src/cli/main.ts init lexicon <name>npm installVerify it typechecks before writing anything:
Terminal window npx tsc --noEmit -p lexicons/<name>/tsconfig.build.jsonDetail: Scaffold a Lexicon.
-
Implement the plugin
At minimum a serializer and the generate lifecycle. Depending on the platform you may also want observation, live export, lint rules, and post-synth checks.
Detail: Implement Generate, Create a Serializer, Write Lint Rules.
-
Create an example workspace
npm workspace resolution needs at least one dependent per package, or
import("@intentius/chant-lexicon-<name>")fails frompackages/core. Createlexicons/<name>/examples/basic-deployment/with apackage.jsondepending on both@intentius/chantand your lexicon, then addlexicons/<name>/examples/*to the rootworkspacesarray.Full
package.jsonshape: CI & Distribution. -
Wire it into the monorepo
Terminal window npx tsx packages/core/src/cli/main.ts dev onboard <name>npm installThis patches the root
package.json,.github/workflows/chant.yml, thepublish.ymltest-job prepack line, and both smoke Dockerfiles. It is idempotent.You do not add a publish step.
scripts/publish-packages.shenumerates every non-private workspace package, so a new lexicon is published the moment it exists. -
Verify locally
Terminal window npx vitest run lexicons/<name> # your lexiconjust check # build, lint, full suite, lexicon contractjust checkruns the completeness contract, which gates on the tracked allowlist inscripts/check-lexicons.ts. An untracked failure exits 1. -
Create the npm trusted-publisher record
Do this before the first release. On npmjs.com, open
@intentius/chant-lexicon-<name>, then Settings, Trusted Publisher:Field Value Organization / repository INTENTIUS/chantWorkflow filename publish.ymlEnvironment leave empty The environment field matters. The publish job declares no
environment:, so any value here fails to match the OIDC claims and the record silently does nothing.The record is per package. It is not inherited from the organization and not shared with the other lexicons, so a new one is always needed.
A brand-new package name has to exist on the registry before npm will let you attach a record, so for a first-ever publish the order is: publish once by hand, create the record, and let every release after that go through CI.
-
Release
Terminal window just release patch # every package, in lockstepjust release-lexicon <name> patch # this lexicon onlyNever hand-roll the tag. The workflow triggers on
chant-v*andlexicon-*-v*only; a barev0.34.0tag matches nothing and the release silently never happens. -
Confirm it actually published
Terminal window npm view @intentius/chant-lexicon-<name> versionnpm view @intentius/chant-lexicon-<name> dist.attestationsA version with no attestation did not go out through trusted publishing, which means step 6 is incomplete even if the version number looks right.
When the publish run goes red
Section titled “When the publish run goes red”scripts/publish-packages.sh never stops at the first failure. Every package
gets its attempt, the summary table names the ones that failed, and the run
exits non-zero at the end. Re-running is safe: a package already at its
version is skipped, so a partial release is recovered by dispatching the
workflow again.
| Symptom | Cause | Fix |
|---|---|---|
ENEEDAUTH on your package while others publish | No trusted-publisher record | Step 6 |
registry REFUSED the OIDC exchange: HTTP 404 | The registry saying the same thing outright | Step 6 |
registry ACCEPTED the OIDC exchange but publish still fails | The record is fine | Read the npm error; the auth path is not the problem |
EOTP on the NPM_TOKEN fallback | The secret is a publish or granular token, so npm wants a one-time password | Replace it with an npm automation token |
| Your package sits a version behind the others | An earlier run failed on it | Fix its auth, then re-dispatch publish |
Why ENEEDAUTH is misleading here
Section titled “Why ENEEDAUTH is misleading here”npm’s OIDC helper is written never to throw. When the token exchange fails it
logs at verbose level and returns undefined, so npm publish falls through to
ordinary auth and reports a bare ENEEDAUTH. That one error covers both “no
record at all” and “a record that does not match this workflow”, and it reads
exactly like having no credentials.
scripts/publish-packages.sh replays the exchange and prints the registry’s
own status and message, so read that line instead of inferring. A successful
exchange answers 201, not 200.
Because the record is matched against the workflow filename, a probe run
from any workflow other than publish.yml returns 404 for every package and
proves nothing.
Related pages
Section titled “Related pages”- Completeness Checklist — what a mature lexicon covers
- Package & Publish — build output and package shape
- CI & Distribution — what each patched file does and why
- Skills — shipping agent skills from your lexicon