Skip to content

build: install Collection Profiles with the current installer core; keep an earlier core for 13 connectors - #628

Open
tnunamak wants to merge 8 commits into
mainfrom
chore/installer-core-single-pin
Open

tnunamak wants to merge 8 commits into
mainfrom
chore/installer-core-single-pin

Conversation

@tnunamak

@tnunamak tnunamak commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Connector and Collection Profile installs now use one installer core instead of three, so an owner gets the same install behaviour whichever package runs it.

  • Five collectors (claude-code, codex, google-takeout, apple-photos, imessage) and three connectors (apple-health, chatgpt, ynab) move to releases the new core accepts. claude-code now requests its usage stream, so its coverage report no longer stays unknown. The seven profiles whose icon path changed render their icons again.
  • ChatGPT 0.2.5 has no sign-in setup. A credential sealed earlier stays stored, no run reads it, and the run does not fail.
  • Thirteen connectors the new core cannot install (12 *-playwright tarballs and github-pdpp) keep a second, older core until they move to signed profiles.

Proven by tests that install from one lock through both cores, register every re-pinned profile over its previous version, and pin the ChatGPT case.

Assisted-by: AI

The root, reference-implementation and local-collector packages pinned
three different data-connectors commits. All three now pin 708a6cc17d, so
npm installs a single copy of the installer core.

That core installs only signed OCI Collection Profiles: `source` is null,
`loadConnectorIndex` and `generateLock` are gone, and the certificate
identity resolver is no longer a per-call option. Call sites in the
reference server, the local collector and the pin script follow, and the
guards for APIs that older cores lacked are removed. The core imports
`ajv-formats`, so the collector declares it.

The core cannot read the 12 retired tarball entries in connectors/lock.json
or github-pdpp 0.5.0, which has no signed source declaration. A second
pinned dependency, data-connectors-legacy-installer (e1e497f3bc, an ancestor
of data-connectors main), serves only those entries in
scripts/resolve-connectors.js until they move to OCI profiles.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…claration

The installer core refuses an artifact that has no signed source
declaration. Eight pins predate it and fail that check. Each moves to a
release the core accepts.

Collector pins (reference server copies change with them):
claude-code 0.3.0 to 0.3.4, codex 0.3.0 to 0.3.3, google-takeout 0.1.0
to 0.1.3, apple-photos 0.1.0 to 0.1.3, imessage 0.1.0 to 0.1.25.
Lock entries: apple-health 0.1.0 to 0.1.17, chatgpt 0.1.0 to 0.2.5,
ynab 0.3.0 to 0.3.26. github-pdpp 0.5.0 and the 12 tarball entries
are unchanged.

Rule: the oldest release that carries a declaration and still emits
every stream of the release it replaces, unless a newer one fixes a
defect that can be named. apple-health, imessage and ynab take 0.1.17,
0.1.25 and 0.3.26 for two fixes in the shared runtime (time-bounded
runs, and failing an unproven connector read). chatgpt stays below
0.3.0, which changes every message record id without re-keying stored
records, and below 0.2.56, which needs a host that supports BLOB; the
local collector does not advertise it (collector-runtime
runtime-capabilities.ts), so it would refuse to place such a connector.

The new releases change the contract in ways the tests now pin:

- claude-code adds the required `usage` stream to profile `full`, and
  makes `sessions.kind` and `messages.has_pasted_content` required. The
  generated collector definition now requests `usage`; without it the
  collector would never emit a stream the server calls required, and
  the coverage rollup would stay `unknown`.
- chatgpt 0.2.5 drops the `setup` block, so a credential sealed under
  0.1.0 is no longer read or injected, and stays stored.
- A server that still holds the previous profile refuses `usage` at
  ingest; after the new profile is registered the same connection keeps
  its id, checkpoint and records, and its coverage proof is re-earned by
  the next run (`generation_mismatch` until then).

Pass rule (from the repository root):

  (cd reference-implementation && node --import tsx --test \
    test/static-secret-run-credentials.test.ts \
    test/collection-report-projection.test.ts \
    test/collector-profile-upgrade.test.ts \
    test/local-collector-profiles.test.ts)
  (cd packages/local-collector && npx tsx --test test/collection-profiles.test.ts)

All pass: 134 tests, and 15 tests with 1 skipped (the live GHCR test).

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…d to move them

A bare run of pin-collection-profiles moved every pin to the catalog's
latest release, which silently undid the holds that keep claude-code,
codex, google-takeout and apple-photos below releases that drop the
coverage_diagnostics stream this collector requests. `--version` ignored
an unknown connector key, and `a=b=c` truncated to `a=b`. The usage
example named a release (0.3.6) that is not a pin.

- With no flag, each connector keeps its current pin; a connector with
  no pin yet takes the latest release.
- `--latest` is explicit and still honors holds.
- Holds live in packages/local-collector/collection-profile-holds.json
  with the reason, and the script reads them. A test fails when a hold
  names a version other than the one pinned.
- Any other argument, an unknown connector key, a repeated key and a
  malformed `--version` are errors.

A bare run against the catalog rewrites the same five pins and the
reference server copies with no diff.

Pass rule (from packages/local-collector):

  npx tsx --test test/pin-selection.test.ts test/collection-profiles.test.ts

All 24 tests run; 23 pass and 1 is skipped (the live GHCR test).

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
scripts/resolve-connectors.js sends each lock entry to the core that
accepts it: the 12 tarball entries and github-pdpp (which has no signed
source declaration) to the earlier core, every other OCI entry to the
current one. Nothing tested that split, and the test file imported
installLockedConnectors without using it.

B-T9 pushes one mixed lock through installConnectorsAtomically with its
default install and checks the installed tree, the summed counts
(3 connectors, 11 files) and which signature each entry was verified
against:

- a tarball entry;
- ynab, a declaration-bearing OCI entry with no generated local-collector
  pin, so it arrives through the lock;
- github-pdpp, a declaration-less OCI entry for a listed id.

The same declaration-less release under an unlisted id is refused with
"no signed source declaration layer or referrer", and the install root
is unchanged.

The two STOPGAP comments now name the issues that remove them (#633 for
the 12 tarball entries, #634 for github-pdpp).

Pass rule (from the repository root; these are exact counts at this
commit):

  npx vitest run scripts/resolve-connectors.test.mjs         26 passed
  (cd packages/local-collector && npx tsx scripts/run-tests.ts)
                                                  301 tests, 296 pass, 5 skipped
  (cd reference-implementation && node --import tsx --test \
    test/static-secret-run-credentials.test.ts \
    test/collection-report-projection.test.ts \
    test/collector-profile-upgrade.test.ts \
    test/local-collector-profiles.test.ts)         134 pass
  (cd packages/local-collector && npx tsc --noEmit -p tsconfig.json)
  (cd reference-implementation && npx tsc --noEmit)  both exit 0

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
@tnunamak
tnunamak force-pushed the chore/installer-core-single-pin branch from 503f4b1 to 6108605 Compare November 5, 2026 21:59
@tnunamak tnunamak changed the title build: pin one installer core and re-pin OCI Collection Profiles build: install Collection Profiles with the current installer core; keep an earlier core for 13 connectors Nov 5, 2026
… whose release it cannot find

When the signed catalog did not list a release the script was asked to
keep or move to, it printed "unpinned" and went on. It then rewrote the
pins module without that connector and deleted its profile and pin
record from the reference server's profile directory, and exited 0.
`--version codex=99.99.99`, a current pin whose release left the
catalog, and `--latest` on a pinned connector the catalog dropped all
did this.

Only a connector with no pin and no request may now be left unpinned. A
named, held or already pinned connector with no matching catalog release
fails the run before anything is written, so every existing output stays
as it was.

The script's work moves into `runPinScript(deps)`, with the catalog
fetch, the artifact fetch, the paths and the log injected, and the
command-line entry builds the real ones. pin-collection-profiles.test.ts
runs it offline against a temp tree: the three refusals (each asserts the
pins module and profile directory are byte-for-byte unchanged), the
unpinned never-pinned connector, and a bare run. With the fix removed,
the three refusal tests fail; with it, all five pass.

Pass rule (from packages/local-collector; exact counts at this commit):

  npx tsx --test test/pin-collection-profiles.test.ts   5 passed
  npx tsx scripts/run-tests.ts                          306 tests, 301 pass, 0 fail, 5 skipped
  npx tsc --noEmit -p tsconfig.json                     exit 0

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
… check the list

Nothing stated which lock entries go to the earlier installer core. They
came from `!entry.oci` plus the id set PRE_DECLARATION_OCI_CONNECTORS,
and collection-profile-holds.json, a different axis with a similar
name, sat beside them.

The comment above the set now lists each of the 13, which axis holds it
back (tarball, or no signed source declaration) and the issue that
releases it (#633, #634), says why github-pdpp is named rather than
derived (a lock entry and a catalog release carry digests and a version,
not the declaration, and the catalog has no github entry; reading it
needs a registry fetch per entry on every install and `--check`, or a
new lock field), and says the profile hold is a separate thing.

B-T10 checks that connectors/lock.json routes exactly those 13 entries to
the earlier core and that each id appears in that comment, so the list
cannot drift. With the set emptied it fails; with it restored it passes.
usesEarlierInstallerCore is exported for the test.

Pass rule (from the repository root; exact count at this commit):

  npx vitest run scripts/resolve-connectors.test.mjs   27 passed

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
…gistration test

collector-profile-upgrade.test.ts proved a device survives the server
registering the new profile for claude-code only. The other four
re-pinned collector profiles (codex, google-takeout, apple-photos,
imessage) now run the same registration against the verbatim profile
the previous pin shipped, kept as a fixture:

- enroll, register the previous profile, push, write a checkpoint;
- register the new profile, push again;
- the connection id, the checkpoint and the stored records survive, and
  the connection is still the only one projected;
- for the connectors that keep a local-coverage proof (codex), the proof
  written under the previous profile reads generation_mismatch and is
  trusted again after the next write. google-takeout, apple-photos and
  imessage keep none.

Their streams, keys and cursors do not change in the re-pin, so the
claude-code test keeps the stream-level cases (usage refused by the old
profile, accepted after registration). The helpers now take the connector
id.

Pass rule (from reference-implementation; exact counts at this commit):

  node --import tsx --test test/collector-profile-upgrade.test.ts   5 passed
  node --import tsx --test test/static-secret-run-credentials.test.ts \
    test/collection-report-projection.test.ts \
    test/collector-profile-upgrade.test.ts \
    test/local-collector-profiles.test.ts                            138 passed
  npx tsc --noEmit                                                   exit 0

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
The pin script is back to the shape it has on main: it moves every
Collection Profile pin to the catalog's latest release. The only change
is the call into the new installer core, which takes no context.

The --latest and --version flags, the holds file and the selection module
had no caller beyond this change. The hold reasons named release numbers
that go stale, and the script took a nine-field injection object only so
tests could drive it. Those tests are removed with it.

The pins in this change stay where the re-pin put them. The catalog's
latest releases of claude-code, codex, google-takeout and apple-photos
drop the coverage_diagnostics stream that the collector requests, so
running the script again moves those four pins to a release the
collector cannot fully use. Re-pin by hand, with the same check, until a
collector that matches the newer releases lands.

The comment in scripts/resolve-connectors.js no longer points at the
removed holds file.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
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