Skip to content

fix: make CI able to install, and let the tests run for the first time - #2

Open
chongkan wants to merge 5 commits into
mainfrom
fix/ci-install-and-tests
Open

chongkan wants to merge 5 commits into
mainfrom
fix/ci-install-and-tests

Conversation

@chongkan

Copy link
Copy Markdown
Member

Why

pnpm install --frozen-lockfile had never succeeded in this repo. CI was added in 74f12f0 on 2026-04-13 and failed at the install step on that very commit, so lint and build have never run here, not once, on any commit. Reproduced at HEAD and at 74f12f0 in throwaway checkouts before changing anything.

Root cause is not the lockfile

The lockfile drift is a symptom. 16150d6 renamed core to @attestto/identity-resolver and nothing referring to it was updated. Three stale names were left behind:

where was count
deps via workspace:* identity-resolver 4 packages
deps via registry range identity-resolver: >=0.2.0 1 (sns)
source imports from 'wallet-identity-resolver' 5 files
doc example from 'identity-resolver' 1 file

workspace:* resolves only inside the workspace and hard-fails when no such package exists. The >=0.2.0 range would have resolved to the unscoped identity-resolver@0.2.2 on npm — the pre-rename core. Not a hijack risk, that package is ours, but it would have silently bound to a stale copy.

Why it hid for four months: every stale import is import type. Type-only imports compile away, so nothing at runtime noticed. Only tsc and the dts build care, and neither had run.

Tests that could not run

packages/core has had vitest run since 0d862af, but the root had no test script to recurse into it and ci.yml had no test step, so 15 real assertions sat unrunnable. The commit that added those tests also added the vitest devDependency that broke --frozen-lockfile — the change that introduced the tests guaranteed they could not run.

Lockfile decision

Root package-lock.json deleted, pnpm-lock.yaml regenerated. The npm lock was last written at v0.1.0 before the monorepo restructure and contained zero packages/ entries, describing a layout that no longer exists, while pnpm-workspace.yaml and the workspace: protocol make this a pnpm workspace npm cannot express.

The untracked packages/core/package-lock.json was left alone — a CI checkout never sees it.

gate-self-test

Byte-identical shared copy (7e8fb2601bf5, 344 lines, unedited). Two gates with genuinely violating seeds:

gate-self-test: OK (proven able to fail: lint, test)

Negative controls run by hand, restored, ci.yml verified byte-identical after:

  • branches: ['**'] narrowed to [main, develop] → RED
  • Gate self-test step deleted → RED

build deliberately not registered: tsup compiles only the entry import graph, so a seed the entry does not import cannot make it fail.

Verified, each for the first time in this repo

1. pnpm install --frozen-lockfile   PASS
2. pnpm gate-self-test              PASS
3. pnpm lint                        PASS  (6 packages)
4. pnpm test                        PASS  (15 tests)
5. pnpm build                       PASS  (incl. dts builds that were failing)

Run under Node 22. ci.yml pins Node 20 and there is no .nvmrc, so nothing makes local and CI agree — worth closing separately.

Consumers unaffected

CORTEX/frontend and the (now archived) did-method-checklist/app depend on published @attestto/identity-resolver ^0.3.0, and published @attestto/wir-sns@0.1.1 declares no dependencies. None of this drift ever shipped; the breakage was entirely internal.

Tracked as ATT-1288.

chongkan and others added 5 commits August 12, 2026 21:17
`pnpm install --frozen-lockfile` had never succeeded in this repo. CI was
added in 74f12f0 on 2026-04-13 and failed at the install step on that very
commit, so lint and build have never run here, not once, on any commit.
Reproduced at HEAD and at 74f12f0 in throwaway checkouts before changing
anything.

Root cause is the rename in 16150d6, "Rename core to
@attestto/identity-resolver". The package was renamed; nothing that
referred to it was updated. Three different stale names were left behind:

  - five packages depended on `identity-resolver`, four via `workspace:*`,
    which resolves only inside the workspace and hard-fails when no such
    package exists;
  - packages/sns used `identity-resolver: >=0.2.0`, an ordinary registry
    range that would have resolved to the unscoped identity-resolver@0.2.2
    on npm, which is the pre-rename core. Not a hijack risk, that package
    is ours, but it would have silently bound to a stale copy;
  - six source files imported types from `wallet-identity-resolver`, a
    name that has never been a package at all.

All of them now point at `@attestto/identity-resolver`.

The type-only imports are why this stayed hidden for four months: they
compile away, so nothing at runtime ever noticed. Only tsc and the dts
build care, and neither had run.

Adds a root `test` script. packages/core has had `vitest run` since
0d862af, but the root had no test script to recurse into it and ci.yml has
no test step, so the 15 tests added that day had no path to execution.
Worth stating plainly: the commit that added those tests also added the
vitest devDependency that broke --frozen-lockfile, so the change that
introduced the tests is the change that guaranteed they could not run.

Verified in CI's order, each of these for the first time in this repo:
`pnpm install --frozen-lockfile` clean, `pnpm lint` clean across all six
packages, `pnpm test` 15 passed, `pnpm build` success including the dts
builds that were failing.

Consumers are unaffected. CORTEX/frontend and did-method-checklist/app
depend on the published @attestto/identity-resolver ^0.3.0, and the
published @attestto/wir-sns@0.1.1 declares no dependencies, so none of
this drift ever shipped. The breakage was entirely internal.

Root package-lock.json is deleted. It was last written at v0.1.0 before
the monorepo restructure and contained zero packages/ entries, describing
a layout that no longer exists, while pnpm-workspace.yaml and the
workspace: protocol make this a pnpm workspace that npm cannot express.
Two lockfiles is also what the shared gate-self-test control refuses to
run against, correctly.
The push and pull_request triggers were filtered to [main, develop], so a
pull request whose base is any other branch matched no trigger and started
no job at all. Stacked PRs (feature-b based on feature-a) therefore merged
with zero checks having run, and the branch protection status showed as
"expected" rather than failed.

Widening both filters to '**' makes every branch and every PR base run the
workflow, which is the only configuration where a required check is
actually required.

Note: this does not make CI green. `pnpm install --frozen-lockfile` fails on
this repo (ERR_PNPM_OUTDATED_LOCKFILE) and has failed since the workflow was
first added, so lint and build have never executed. That is tracked
separately along with the two-lockfile question.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Adds the shared gate-self-test control, byte-identical to the copy in the
other Attestto repos (sha256 7e8fb2601bf5..., 344 lines, unedited), plus a
Test step this workflow never had.

This repo is the strongest argument for the control. Its CI had been red at
the install step since the workflow was added, which is indistinguishable
from having no gates at all: every step below install had simply never
executed. The self-test asserts install matches the lockfile actually
present, which is exactly the condition that was wrong here, and it refuses
to run against two lockfiles, which is what this repo had.

Two gates registered, each with a seed that genuinely violates it: lint (a
real type error) and test (a really-failing assertion).

build is deliberately NOT registered. tsup compiles only the entry import
graph, so a seed the entry does not import cannot make it fail, and a seed
that passes would assert nothing. It stays as buildScript for the
test-before-build ordering check.

Proven able to fail rather than assumed:

  pnpm gate-self-test -> OK (proven able to fail: lint, test)

and, breaking it on purpose, each restored afterwards with ci.yml verified
byte-identical to its backup:

  branches ['**'] -> [main, develop]  -> RED, "would skip branches outside"
  delete the Gate self-test step      -> RED, "never runs gate-self-test"

The Test step goes before Build so a failure lands on the cheaper step.
CI failed on the first real run of this pipeline, at install, for a reason
that had been hidden until now:

  warn: This version of pnpm requires at least Node.js v22.13
  warn: The current version of Node.js is v20.20.2
  Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:sqlite

The workflow pinned Node 20 while `pnpm/action-setup` with `version:
latest` resolved to pnpm 11.21.0, which requires Node >= 22.13 and loads
`node:sqlite`. The two pins contradicted each other.

This was invisible before. Install had never succeeded in this repo, so it
never got far enough to hit the incompatibility. Fixing the lockfile did
not create this failure, it exposed the next one underneath, which is what
happens when a gate runs for the first time in four months.

Two changes, and the second matters more than the first.

Node now comes from `.nvmrc` (new, set to 22), so local and CI cannot
disagree about the version. There was no `.nvmrc` at all, and nothing else
made them agree.

pnpm is pinned to major 10 rather than `latest`. `latest` is how a
pipeline that passed yesterday breaks today with nobody touching it, and
pnpm 11 dropping Node 20 support is precisely that. A version this
pipeline never chose should not be able to redden it.

Verified locally under Node 22 with pnpm 10, in CI's order: install
--frozen-lockfile, gate-self-test, lint, test (15), build. All pass.
…st its declarations

CI failed at `gate-self-test` with "`lint` fails on a clean tree, so its
failure above is not evidence of anything", and it was right.

The five provider packages import their types from
`@attestto/identity-resolver`, which resolves to the GENERATED
`dist/index.d.ts` rather than to core's source. On a fresh checkout there
is no dist/, so `tsc --noEmit` cannot resolve the module and lint dies
with TS2307 in four packages.

My earlier local verification did not catch this and should have. I had
run a build earlier in the session, so dist/ existed when lint ran, and
"all five steps pass" was true only on a machine that had already built.
Reproduced properly by deleting every packages/*/dist and running CI's
order from scratch, which fails exactly as CI did.

Adds a step that builds only core, after install and before gate-self-test.
It has to precede the self-test, not just lint, because the self-test runs
each gate on a clean tree and would otherwise judge lint broken for an
environmental reason rather than a seeded one.

This is deliberately not the full build, which still runs after the tests
so a failure lands on the cheaper step first. It is a prerequisite of
linting at all.

Verified from a genuinely clean state: every packages/*/dist removed, then
install --frozen-lockfile, build core, gate-self-test, lint, test (15),
build. All pass under Node 22 with pnpm 10.

Known wart, not fixed here: a developer running `pnpm lint` on a fresh
clone hits the same TS2307 until they build. The durable fix is to have
the providers resolve core's types from source via tsconfig paths, so the
declarations stop being a build-order dependency.
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