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
Run everything on this page from the repository root.
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
chant dev check-lexicon lexicons/<name>is the contract, andjust checkfails on any tier-1 item. Tier 1 wants all of this, so plan for it rather than discovering it at the gate:Requirement Where it lives The package exports a LexiconPluginsrc/index.tsA Serializerwithname,rulePrefixandserializesrc/serializer.tslintRules()returns at least one rulesrc/lint/rules/postSynthChecks()returns at least one checksrc/lint/post-synth/, barrel vianpm run generate:barrelsEvery rule id starts with rulePrefixor anextraRulePrefixesentryibid. completionProviderandhoverProviderregisteredsrc/lsp/completions.ts,src/lsp/hover.tsdocs()registeredsrc/codegen/docs.tsplussrc/codegen/docs-cli.tsdist/manifest.jsonexists and declareschantVersionproduced by npm run prepackAt least one example under examples/, and it buildsstep 3 Registered intrinsics are exported, with correct isTag/foldsAsCallsrc/index.tsMCP tools and resources under one well-formed namespace src/plugin.tsplugin.test.tsandserializer.test.tsexistsrc/At least one .mdxdoc page, reachable from the sidebar, with adiataxisquadrantdocs/pages/Depending on the platform you may also want observation, live export, and an emulator. Tier 2 adds
auditCatalog()coverage for every post-synth check, three skills, and eight doc pages; it warns rather than failing.Detail: Implement Generate, Create a Serializer, Write Lint Rules, Post-Synth Checks, LSP & MCP Providers, Docs Site Setup.
-
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 six files: the root
package.json, the roottsconfig.jsonpathsmap,.github/workflows/chant.yml, thepublish.ymltest-job prepack line, and both smoke Dockerfiles. It is idempotent, and it prints achant dev check-lexiconreport at the end.Ignore one line in that printed report. It says to tag the first publish
v<version>; that tag matches no workflow trigger. Step 7 has the real commands.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 lexiconnpm run prepack -w @intentius/chant-lexicon-<name> # generated artifacts + dist/just check # build, lint, full suite, lexicon contractjust checkisbuild lint test check-lexicons. The last of those runsscripts/check-lexicons.ts. It loops everylexicons/*directory and runs the full tier-1 set against each one, then runs that lexicon’s owntsconfig.build.jsonbuild. ItsKNOWN_FAILURESallowlist is currently empty, so every tier-1 check must pass for every lexicon, including yours. A tsc failure is never allowlistable.just smokeis separate and local-only. Run it once before the first release, because that is when yourtest/integration.shsection executes for the first time. -
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.
.github/workflows/publish.ymltriggers onchant-v*andlexicon-*-v*only; a barev0.57.0tag matches nothing and the release silently never happens.Publishing itself is
scripts/publish-packages.sh, which enumerates every non-private workspace package. No workflow edit is needed for a new lexicon. -
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