This document is the runbook for publishing @takk/gaptime to npm and GitHub. The release flow is intentionally two-step: a GitHub Release is created and reviewed FIRST, and only then promoted to NPMJS.
The first published cut is 1.0.0. From there, SemVer 2.0.0 applies per the policy in SPEC.md section 11.
These require credentials and cannot be performed from inside this repository.
The npm organization takk owns the @takk/* scope. Verify locally:
npm whoami
npm org ls takkA granular automation token is provisioned on npm:
- Name:
takk-ci(year-less so the convention survives rotation; track the active issuance date in the npm UI) - Scope:
@takk - Permissions: read and write
- Bypass two-factor authentication: enabled
- Expiration: 90 days from issuance; rotate before expiry
In the GitHub repo settings:
- Settings -> Secrets and variables -> Actions -> New repository secret
- Name:
NPM_TOKEN - Value: the token issued at https://www.npmjs.com/settings/takk/tokens
Rotation flow when the token expires (or is suspected leaked):
- Issue a new token on npm with the same name, scope, and permissions.
- Update the
NPM_TOKENGitHub secret with the new value. - Re-run any failed publish workflow.
- Revoke the old token.
Settings -> Branches -> Add branch protection rule for main:
- Require linear history.
- Require conversation resolution.
- Do not allow force pushes; do not allow deletions.
- (Optional, after first CI run) Require status checks to pass: select
test (Node 22),test (Node 24),biome.
Repo settings -> About -> set topics for organic discoverability:
knowledge-graph temporal-knowledge-graph bi-temporal agent-memory llm-memory time-travel valid-time transaction-time contradiction-detection audit-trail provenance llm ai-agents vercel-ai-sdk opentelemetry mcp hermes-agent edge zero-dependency typescript
And set Website: https://gaptime.takk.ag/ (canonical for the family; DNS provisioning is pending family-wide, the link-check gate tolerates the miss until it resolves).
The release pipeline is intentionally non-atomic, split across two GitHub Actions workflows that the Creator triggers manually:
| Step | Workflow | What it does | Touches NPMJS? |
|---|---|---|---|
| 1 | release.yml |
Validates package, version, tag absence, and CHANGELOG entry. Lints, typechecks, tests, builds, checks documentation links, packs (dry-run). Creates git tag v<version> and a GitHub Release titled [REVIEW REQUIRED: NOT YET ON NPMJS] with a matching status banner in the body. |
No |
| 2 | npm-publish.yml |
Verifies the tag and GitHub Release from Step 1 exist. Verifies monotonic version against the npm registry (dependency-free semver compare). Lints, typechecks, tests, builds, packs (dry-run). Publishes to npm with --provenance. Flips the GitHub Release title to [PUBLISHED ON NPMJS] and rewrites the status banner. |
Yes |
The Creator runs Step 1 immediately after a release-worthy change is merged to main; reviews the resulting GitHub Release page (the changelog, the tag, the commit, the pack-smoke result in the workflow logs); and only then runs Step 2 to push the artifact to NPMJS.
Why the split? Once a version is on the npm registry, it cannot be unpublished after 72 hours. The two-step flow gives the Creator a reviewable artifact on GitHub before the release becomes permanent on npm.
Step 1 runs node scripts/check-links.mjs as a blocking step before the tag is created. The gate:
- verifies every relative link target in the markdown docs exists on disk;
- probes external URLs, tolerating bot-gated hosts (LinkedIn, X, and similar return 403/429 to CI; treated as reachable);
- skips the package's own npm page pre-publish (it cannot exist before Step 2 ever ran);
- treats a DNS miss on the canonical
gaptime.takk.agas pending-by-design (warns, does not fail) and re-arms automatically once the record resolves, from then on a failure there blocks again.
Run it locally before any docs PR: node scripts/check-links.mjs --skip-external checks the relative targets only and must exit 0.
# Example: releasing 1.0.1
# 1. Bump the version
npm version 1.0.1 --no-git-tag-version
# 2. Prepend a new section to CHANGELOG.md with a UTC timestamp
$EDITOR CHANGELOG.md
# Add: ## [1.0.1] - 2026-MM-DDTHH:MM:SSZ
# Use: date -u +%Y-%m-%dT%H:%M:%SZ
# 3. Commit on a branch + open PR (branch protection blocks direct push to main)
git checkout -b chore/release-1.0.1
git add package.json CHANGELOG.md
git commit -s -m "chore: release 1.0.1"
git push -u origin chore/release-1.0.1
gh pr create --fill
# 4. After PR merges to main, fetch main locally (no need to tag manually)
git checkout main && git pullgh workflow run release.yml \
-f version=1.0.1 \
-f confirm=YES-CREATE-GITHUB-RELEASEThe workflow validates, builds, tests, checks links, packs, then creates the tag v1.0.1 and a GitHub Release titled [REVIEW REQUIRED: NOT YET ON NPMJS] @takk/gaptime@1.0.1.
Visit https://github.com/davccavalcante/gaptime/releases/tag/v1.0.1 (the release page the workflow just created) and review:
- The changelog body extracted from
CHANGELOG.md. - The commit SHA the tag points to.
- The pack-smoke and link-check results in the workflow logs.
When the GitHub Release looks good:
gh workflow run npm-publish.yml \
-f version=1.0.1 \
-f confirm=I-AM-THE-CREATOR-AND-I-PUBLISH-TO-NPMJSThe workflow verifies the Step 1 artifacts exist, validates monotonic version against the npm registry, builds, tests, packs, then runs npm publish --access public --tag <auto-resolved> --provenance. After publish, the GitHub Release title flips to [PUBLISHED ON NPMJS] @takk/gaptime@1.0.1 and the review-required banner in the body is replaced by the published-status banner.
# Cache may take ~1 min to update; the workflow itself verifies the registry.
npm view @takk/gaptime versions
npm view @takk/gaptime dist-tags
# Try installing into a temporary directory
mkdir /tmp/verify && cd /tmp/verify
npm init -y
npm install @takk/gaptime
# CLI contract: the first help line must read exactly "gaptime 1.0.1"
npx gaptime version
# Verify provenance
npm view @takk/gaptime@1.0.1 --json | jq .dist.attestationsThe Creator's binding rule: no version skipping, no backwards versions, no deprecations without a cycle.
- Initial cut:
1.0.0. - Patches:
1.0.1,1.0.2, ... - Minors:
1.1.0,1.2.0, ... - Majors:
2.0.0, only after a full deprecation cycle per SPEC.md section 11. The TeleologHI provider is reserved for 2.0.0.
Prereleases use the standard semver qualifiers (1.0.0-alpha.1, 1.0.0-beta.1, 1.0.0-rc.1) and route to matching dist-tags (alpha, beta, rc) instead of latest. The npm-publish.yml workflow auto-resolves the dist-tag from the version qualifier when the dist_tag input is left blank.
Remember the version constant: src/cli/index.ts carries VERSION, printed as the first help line and by gaptime version, and asserted by CI. Bump it together with package.json.
If a release is published under a non-latest dist-tag and you later want it to be the default npm install target, run:
npm dist-tag add @takk/gaptime@1.1.0-rc.1 latest(Requires the same NPM_TOKEN and 2FA bypass.)
npm allows npm unpublish only within 72 hours of publish AND when no other public package depends on it. Within the window:
npm unpublish @takk/gaptime@1.0.1After 72 hours, unpublishing is not permitted. Use npm deprecate:
npm deprecate @takk/gaptime@1.0.1 "Replaced by 1.0.2, fixes the resource refresh boundary."The Creator's discipline is to AVOID this stage: strict CI, provenance, a small surface, and the two-step review flow aim to make every published version stable enough to never deprecate. When a deprecation is unavoidable, document it in the next CHANGELOG with a ### Deprecated section AND ship a non-deprecated alternative in the same release.
| Action | Command |
|---|---|
| Run all tests locally | pnpm test |
| Run full verify pipeline | pnpm verify |
| Link gate (local, offline) | node scripts/check-links.mjs --skip-external |
| Build only | pnpm build |
| Pack smoke (no publish) | pnpm pack --pack-destination /tmp |
| Manual publish (DO NOT use; let CI do it) | npm publish --access public --provenance |
| Step 1, create GitHub Release | gh workflow run release.yml -f version=<semver> -f confirm=YES-CREATE-GITHUB-RELEASE |
| Step 2, publish to NPMJS | gh workflow run npm-publish.yml -f version=<semver> -f confirm=I-AM-THE-CREATOR-AND-I-PUBLISH-TO-NPMJS |
| Promote a prerelease to latest | npm dist-tag add @takk/gaptime@<semver> latest |
| List versions | npm view @takk/gaptime versions |
| Verify provenance | npm view @takk/gaptime@<semver> --json | jq .dist.attestations |
| Rotate NPM_TOKEN | Issue new token at https://www.npmjs.com/settings/takk/tokens, update GitHub secret, revoke old |
See SPEC.md section 11 for the binding stability contract: public surface definition, the minor-extensible unions (ProviderId, LintCode, TelemetryEvent), SemVer rules, deprecation cycle, and license and provenance invariants.