Skip to content

build: pin the host the plugin runs on - #109

Open
viztor wants to merge 84 commits into
claude/vigilant-mendel-bctz9n-p2from
claude/vigilant-mendel-bctz9n-p-t1a
Open

viztor wants to merge 84 commits into
claude/vigilant-mendel-bctz9n-p2from
claude/vigilant-mendel-bctz9n-p-t1a

Conversation

@viztor

@viztor viztor commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Phase P-T1a of docs/improvement-plan.md. It is stacked on #108 (P2), which sits on #107, #106 and #105.

After this PR, the test install runs the host users run (D14'', D22''). It is a pinning phase, so no src/ behaviour changes.

Restacked onto main 1.8.0

The branch now carries main 1.8.0 through its parent (P2). main had followed the harness onto its alpha track, and the owner decided on 2026-11-03 that the plugin supports the latest release of both harness channels, with no pi patch. That decision replaces the scope described in the sections below this one; they are kept as the record of the original phase.

New scope

  • Two channel records in test/support/host-versions.ts (D22''):

    • rc = dsh@latest 0.2.1-rc.5, pi ^1.1.0 (floor 1.1.0)
    • alpha = dsh@alpha 0.2.2-alpha.2, pi ^1.3.0 (floor 1.3.0)

    Each record gives the cluster, cordis, the loader, schemastery, the bridge's pi range, the newest pi it resolves (1.5.0 today) and its floor (the harness pin).

  • No pi patch (D14''). Each channel's committed lockfile pins unpatched pi at the newest version its bridge range admits: the root lockfile for alpha (the committed default, as on main), and channels/rc/pnpm-lock.yaml for rc. The pi devDependency is the union range ^1.1.0.

  • scripts/use-channel.ts <rc|alpha> [--floor|--relock] switches the dev install and leaves no diff. Its pure half (scripts/channel-manifest.ts) is unit-tested. pnpm-workspace.yaml sets verifyDepsBeforeRun: warn, so pnpm run cannot silently reinstall the default channel under an rc run.

  • Tests:

    • host-versions.test.ts checks the installed tree against the selected record.
    • harness-channel.test.ts keeps main's both-channel peer rule and holds each lockfile to its record. It also checks that each lockfile's importer specifiers and settings/catalogs/overrides match package.json and the root lockfile (so a dependency change that forgets rc --relock fails offline), that Dependabot ignores the harness cluster and pi, and that the README pair's support-policy paragraph names each channel's versions.
    • mount-protocols.test.ts F10 is now per pi line: no Go Google on >=1.1.0 <1.3.0, native on >=1.3.0 <2.0.0. A pi outside every line fails and says to measure it.
    • An architecture tripwire fails if src reads streamed tool-call deltas, partial arguments (including through pi's partial message) or thought signatures.
  • CI:

    • Unit and mock run as a matrix over [alpha, rc] on every PR, and on each channel's floor pi nightly and on dispatch.
    • A new host-drift job (schedule and dispatch only) runs scripts/report-host-drift.ts. It reports when a channel's dsh, cordis, loader, schemastery or pi has moved past its record. It warns and always exits 0.
    • Dependabot ignores @deepseek-ai/* and @earendil-works/pi-ai: a host bump moves the record, the lockfiles and the pins together, and P-T3 proposes it.
  • Docs: the plan's D14''/D17/D22''/D26 rows, the F2/F10/G1/G2 facts, the status rows and restack rows, and the README compatibility section (both languages) state the policy. CONTRIBUTING's release step 3 lists everything a channel bump moves, and says when a bump opens a new prerelease tuple and so becomes a reviewed peer-range change.

Dropped, because main or the owner's decision now covers it

  • The pi 1.1.0 pin and the harness's patch: patches/@earendil-works__pi-ai@1.1.0.patch, the pi overrides entry, patchedDependencies, and the patch-hash and patch-hunk tests.
  • The alpha-only variant of this phase (a pi 1.3.0 override plus a copy of the alpha tag's 1.3.0 patch).
  • main's exact pi 1.4.0 devDependency, which matched neither channel's host. It is replaced by ^1.1.0.
  • The rc.2 cluster this branch used to pin. The two records above replace it.
  • minimumReleaseAgeExclude: this branch takes main's list.

Decisions applied

  • D14'': no pi patch anywhere. Each channel's lockfile pins unpatched pi. The desktop's patched pi stays invisible, and the architecture tripwire enforces that.
  • D22'': two channel records. A bump is version data only, never src or test logic. P-T1a reports drift nightly, and P-T3 proposes the bump PR.
  • D26: this phase adds the tripwire and the per-pi F10 row only. The HOST_LIMITATION row for Gemini 4 thinking levels on rc floor pi 1.1.0, the feature-detected skips, the "Host limitations" list and the upstream report belong to P-R1. main's fix: send Gemini 4 thinking levels on older host runtimes #130 Gemini-level code stays until P-R1 deletes it, and it passes on all four installs.
  • D24 (debug gate) and D25 (tool-fallback plane) don't apply here. They land in P1a and P1b.

Live re-measures CI must confirm

  • This branch changes no src behaviour, so no live result can change its code. The PR's test · alpha and test · rc jobs must pass.
  • The nightly or dispatch pi floor cells must pass: rc on pi 1.1.0 and alpha on pi 1.3.0. The host-drift job must run on dispatch.
  • Locally, all four installs passed: alpha with pi 1.5.0 and 1.3.0, and rc with pi 1.5.0 and 1.1.0. Each ran 1393 unit and 52 mock tests.
  • Owed by P-T1b's binary e2e job, per channel:
    • install dsh@alpha and dsh@latest from the registry
    • confirm the pi that the loader's resolver loads (1.5.0 today)
    • confirm there is one instance of it

What is pinned

Original scope. The section above supersedes it.

Before After Why
@earendil-works/pi-ai 1.3.0 1.1.0 + the harness's patch The harness's own lockfile pins 1.1.0 and patches it. A pnpm overrides entry holds every copy in the tree to it, including the one dsh-llm-pi-ai's ^1.1.0 would otherwise float. The patch is copied verbatim into patches/, and its hash matches the one the harness lockfile records.
@deepseek-ai/* 0.2.1-alpha.5 0.2.1-rc.2 (cordis 4.0.5, loader 1.0.6, schemastery 3.18.5) This is what a registry install of the published dsh resolves. The rc.2 builds of llm-pi-ai, cordis and schemastery have the same code as alpha.5; only their package.json ranges differ.
new devDependencies — dsh-llm, dsh-app-boot, dsh-config-editor, dsh-settings, cordis-plugin-loader The next phase, P-T1b, builds its real-host harness on these. This phase only installs them and version-tests them.

minimumReleaseAgeExclude is now empty. Every pinned version is older than pnpm's 24-hour release-age window, so nothing needs that window lifted.

Tests

Original scope. The section above supersedes it.

  • test/support/host-versions.ts names the pinned versions once.
  • test/host-versions.test.ts checks four things:
    1. The pi the plugin loads at runtime (loadHostPi) is 1.1.0, and it is the same module instance the tests import.
    2. The lockfile holds exactly one pi, and it is 1.1.0.
    3. A known context line from the harness patch is present in the installed dist, and the lines the patch removes are absent.
    4. The installed @deepseek-ai cluster matches the pinned versions, within the plugin's own declared ranges.
  • test/mount-protocols.test.ts is rewritten. It now checks that every catalog SDK maps to a protocol that pi's exported api/* modules implement. It keeps a characterization row: pi 1.1.0's opencode-go provider has no Google API (F10). That gap is what the plugin's provider fills in P-R1.
  • test/harness-channel.test.ts reads the channel from HOST_VERSIONS instead of an alpha-channel regex.
  • test/no-local-paths.test.ts now also scans patches/ and pnpm-workspace.yaml.

Review

The change went through three review lenses (correctness and host contracts, tests, repository rules), and two independent skeptics checked each finding. Over three rounds, every upheld finding was fixed: 3 medium and 21 low.

The restack had three more review rounds. They found 26 findings, upheld 24 and fixed all of them.

Verification

All run locally:

Node Step Result
26 pnpm install --frozen-lockfile lockfile up to date
26 pnpm run check 0 errors; 21 warnings, unchanged from the base
26 unit suite 1241 tests pass, up from 1234
26 shuffled, seeds 1 / 42 / 1234 pass
26 test:mock 51 pass
26 build and scripts/check.ts pass. The consumer install resolves no pi of its own, because the package has no pi dependency.
26 coverage 96.4% lines
24 shuffled (seed 7) and test:mock pass

These figures are from the original scope. After the restack, the unit suite has 1393 tests and the mock suite has 52, and both pass on all four installs.

The diff and the patch file contain no local paths.

Note on CI: the catalog freshness check will fail here for the reason described on #108. The vendor added a Go model (kimi-k3.1-pro), and this PR doesn't touch the catalog. The nightly refresh fixes it.


Generated by Claude Code

claude added 15 commits October 22, 2026 22:24
- Stop the run on a Node below engines.node, before collecting a file.
  Below the floor, failures came from the engine (node:sqlite,
  Intl.DurationFormat, ICU strings) and looked like code regressions.
  .node-version names the Node CI uses.
- Add a CI job that runs the unit and mock suites on Node 24, the floor
  engines.node promises but no job ran.
- The zh fallback duration unit is now 分钟, the platform's own narrow
  output, so the meter row reads the same with or without
  Intl.DurationFormat. The DurationFormat tests restore the engine as they
  found it, rather than leaving a stand-in installed or asserting the
  engine ships the constructor.
- Move the node:sqlite store fixture out of the helpers that the jsdom
  tests share, into test/support/opencode-store-fixture.ts.
- Add an architecture test that walks the client bundle's runtime import
  graph from settings-page.tsx. It forbids node built-ins, the cordis
  runtime, pi, and process.env. Checked by planting a violation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…n discovery

- A rejected internal-route mount ran from a `void` IIFE with no catch, so
  a Google bridge failure became an unhandled rejection, which Node's
  default mode answers by exiting the process. mountInternalRoutes now
  owns the mount and its withdrawal, and reports a rejection.
- Three early exits in registerResponsesProvider (internalRoutes off,
  every route declared, no llm-pi-ai) answered `undefined` after the
  Gemini bridges had already registered. That left them with no
  disposer, and the next reload failed DUPLICATE_ADAPTER. They now hand
  back the bridges' disposer.
- A mount that failed to start, or that captured nothing, kept its fiber
  and every route it had registered. Both paths now release the routes
  and dispose the fiber.
- Model discovery was registered on every activation, and the host's
  duplicate refusal was swallowed, so the first activation's settings
  answered for the life of the process. It is now registered once per
  root and answers from the latest activation's settings. Each namespace
  registers on its own, so one refusal no longer skips the rest.
- Move three doc blocks back above the functions they describe.

Each fix has a regression test. The two mount tests fail without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…e the suite

Request path and meter:
- Price a turn on its route's plane. Go-first lookups priced every Zen
  turn on deepseek-v4-pro at 0.66/1.98 instead of 1.74/3.48.
- The listing and model-info patches now act only on the routes this
  plugin claims. Before, a direct anthropic or openai route lost rows the
  OpenCode routing called "unservable", and a refused foreign id was
  answered with OpenCode's catalog.
- A relay's Go /models is answered with the Go catalog. The enrichment
  re-checked the URL against the default gateway markers and fell back to
  Zen.
- The free-model check reads a Request-carried body. That is how DSH
  sends a turn, and every keyless free turn was reported as a missing
  credential. The body is now read once for both uses.
- The meter's session fallback is the session that most recently ran a
  turn, not the most recently created one.
- A captured key's tier comes from the observed URL, then the route, and
  never the key's prefix. OpenCode issues oc_sk_ keys for Go, so a
  working Go key was filed under Zen. An OpenAI sk- key on a claimed
  relay was filed under Go.

Suite isolation:
- The unit and mock suites refuse non-loopback requests. Each unit run
  sent 54 requests to models.opencode.ai through the catalog's background
  revalidation, so on a networked runner a result could depend on the
  vendor's live catalog.
- Captured keys, session spend and the live catalog are reset after every
  test (resetCatalogState is new). Shuffled runs failed catalog cases on
  seeds 42 and 1234. test:shuffle runs the suite in random order, and the
  Node 24 CI job uses it.

Each behaviour change has a test that fails without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…egration

- The card's store-inspection row called remote.opencodeStore.inspect, but
  no client contribution carried that namespace's descriptor. The row
  reported "unavailable" on a host that had the service. storeRemote now
  carries it, with a whitelist codec, and the client mounts it.
- The host/client parity test missed this because it matched only a
  string literal in super(ctx, "name"), and the store passes a constant.
  It now resolves constants, and checks every declared Remote method
  against a client descriptor, not just every namespace.
- inspectStore parses through the whitelist instead of a cast, so a field
  the host did not mean to send (a token) is dropped. An ok:false
  envelope reports the remote's own message.
- The credential reader's legacy-row fallback was uncorrelated: one
  flagged opencode-go row hid every legacy opencode row, and the Zen key
  disappeared. The fallback is now per integration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
… keeps

- AGENTS.md's repo map described nine modules this build does not have
  (the Console login, token store, org cache and sign-in row). They are
  replaced with one line saying sign-in is v2 work. The go-limits
  duplication note is gone, because attachAllowance calls allowanceFor.
  adapter-replacement.ts is marked as not wired. The client-bundle line
  names the boundary test.
- CONTRIBUTING.md documents the engine floor, test:shuffle and test:mock,
  the offline guard, the per-test state reset, where host-only fixtures
  live, and that a regression test must fail without its fix.
- The no-credential warning no longer tells the reader to "sign in
  through the settings card", which this build does not have. It points
  at readOpencodeStore instead, and a test asserts the advice.
- CLIENT_BUDGET is raised from 128 to 132 KB for the store descriptor
  and its parser (about 1.2 KB of source, not a dependency), following
  the gate's own instruction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
docs/improvement-plan.md groups the review's findings by cause and orders
the fixes in nine phases after this one. Each phase lists its problems,
its steps, the test that has to fail first, its acceptance criteria and
any decision the owner needs to make. Findings are marked "measured"
(reproduced) or "traced" (read in the code). Among the measured ones:
the picker and dispatch derive reasoning levels differently for 9 of 95
catalog models.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Replace docs/improvement-plan.md with the plan of record. It now holds:
- the owner's decisions (D1-D11);
- what the DeepSeek Harness host supports, with references into
  packages/llm, llm-pi-ai, session-controller, gateway and
  ui-settings-models;
- the defects this audit found that are not yet fixed;
- the target architecture and the phases, one PR each, with a status
  table that each PR updates.

The decisions that change the code:
- Settings offers the whole catalog, and the composer offers only the
  declared models.
- The plugin never writes the user's profile and follows the user's
  configuration at dispatch.
- The adapter patch exists to merge provider modes and nothing else. It
  is generalized to every mode, and its catalog fill is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Investigation of the host's source and of the pi catalog it pins (1.1.0)
changed the routing design.

- llm-pi-ai resolves `api` and `baseUrl` per model, and pi's installed
  `opencode` catalog already spans Anthropic, Google, Completions and
  Responses with per-protocol endpoints. One route serves every mode for
  every model pi knows.
- The plugin fills only three gaps: models newer than pi's catalog
  (including all Gemini on Go), endpoint drift, and profile-level
  `api`/`baseURL` collapsing the modes.
- The new design is one unregistered catalog adapter per plane, keyed by
  the user's route id, and the adapter patch (merge-modes) delegates only
  gap models to it. That deletes the hidden routes, the listing hide,
  the stream redirect, the child mount and the Gemini bridge
  registration.
- A real-host test harness (P-T1) comes before any of it.
- One upstream change (per-entry `api`/`baseURL` in llm-pi-ai, U1)
  would remove the routing layer entirely. It would also let Settings
  save a model pi doesn't know, which pi-ai's strict write validation
  rejects today.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
pi-ai registers exactly the providers in its resolved config, and the
fiber keeps whatever the internal/config waterfall returns, while the
config editor writes the raw value. A prepended listener that resolves
pi-ai's config without opencode/opencode-go therefore keeps pi-ai from
claiming them, without touching the user's file, and the plugin registers
its own PiAiAdapter under the same ids through the public API.

Measured on the real host with pi 1.1.0 and the harness's patch: both
routes held by the plugin, pi-ai's other routes unchanged, every protocol
on both planes (Gemini on Go included) sent to its own endpoint, a
catalog-unknown model id saved, listed and dispatched, and both routes
handed back to pi-ai on release.

Replaces the upstream asks (no upstream changes) and the save-gate
design, which is kept only as the fallback.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
A nine-lens review of the routing design, with two independent skeptics
per finding (78 of 82 upheld), changed five things before any code moves:

- Settings reads pi-ai's resolved config, so a read-only wrapper on
  settings.describe puts the plane entries back into the displayed value;
  writes were never affected, the Models page edits by path.
- The route claim gets an explicit lifecycle: commits are told from
  validations by identity, reconcile runs on commit, mount and teardown
  are ordered, and pi-ai restarts at most once per transition.
- The plugin's adapter is built from public pieces only, with pi-ai's
  credential rule and no captured keys on the adapter path.
- The protocol comes from the catalog map, never from a guess at the
  model's name; metadata and levels are catalog first.
- Releases stay on 1.x, phases land as stacked pull requests, and a real
  host harness plus a binary e2e job pin the behaviour first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
- The deprecated @viztor/dsh-opencode wrapper re-exported a `default` the
  barrel does not have, an ESM link error on every host load. It now
  forwards `export *` only, written by one shared helper that a test links
  against the built barrel in a real Node process.
- isZenProvider("opencode-google") answered false: "go" inside "google"
  read as the Go plane. It now uses the plane rule the catalog already has.
- enrichModelsResponse answered a 401, 403, 429 or 5xx with a 200 catalog.
  Those statuses now pass through; a 404 is still filled from the catalog,
  because the Go plane's inference base has no /models of its own.
- The warning colour named a token the host does not define, so warnings
  rendered as caption text. They now use --dsw-alias-state-warn-label.
- The settings spinner only animated while the meter was mounted; the
  settings surface now carries its own keyframes.
- The stream hook wrote debugFile with debug off. debug is the switch and
  debugFile only where it writes, the same gate the fetch patch uses.
- The purge row reported the host's own { ok: false } failure as "not a
  purge result" and cast the answer; it now shows the host's message and
  parses through the contract's whitelisting parser.
- The endpoint copy described modern as /inference/v1, which the plugin
  never sends. It now names the per-protocol inference family, and auto as
  its alias, which is what the code already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
- Delete adapter-replacement.ts and its test. Nothing imported it, and
  deleting another plugin's registry entry is not a host API; the
  engineering notes record why, and what the plugin does instead.
  A gate fails the build if src/ writes the llm registry map directly.
- Delete the stream hook's `prepared` handling: the host passes the
  prepared dispatch as its own argument and never reads options.prepared.
- Untrack .freebuff/ and ignore it.
- The settings partition test walks Config.dict, which is how debugFile
  went missing from it; a manifest test checks cordis.patch.yml against
  the schema and the field registry, and the manifest's phantom
  showUsagePrice, wrong marker text and incomplete lists are fixed.
- utils/retry.ts takes its timer and logger as parameters, and the utils
  purity gate now also rejects console and global timers, walking the
  folder recursively.
- tool-fallback reads free-tier ids from the request's own plane (Go iff
  opencode-go or opencode-go-*) and loses its unreachable Anthropic
  branch. A Zen-only free model is no longer treated as free on Go.
- test/support/src.ts is the one door tests use into src/, so later
  module moves touch one file; CONTRIBUTING.md states the rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The barrel is the host's contract and nothing more: apply, Config,
inject, name and the config types. Everything else is internal, and
test/public-api.test.ts pins the list, so widening it is a reviewed
change. The 1.x line keeps its version scheme: nobody consumes the
removed exports (D6).

- Every test reaches src through test/support/src.ts, importing each
  name from the module that defines it; a gate rejects a static src
  import anywhere else outside its named exemptions.
- package.json no longer exports ./src/*, and check.ts fails when an
  exports target is outside the published files.
- resolveRoutedKey is deleted: it was exported and never called.
- The internalRoutes setting is deleted; the per-protocol routes always
  mount until the plugin owns the plane routes. A row that still carries
  it validates and the key is ignored.
- @viztor/dsh-opencode is no longer published. The deprecation message
  already on npm stays as its final word, and check.ts asserts that the
  release workflow verifies exactly what publish-scoped.ts publishes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The test install now runs the host users run.

- pi is pinned to 1.1.0, the version the harness's own lockfile pins,
  with the harness's patch applied verbatim (patches/, hash recorded by
  pnpm). A pnpm override holds every copy in the tree to it, not just the
  direct devDependency.
- The @deepseek-ai packages move from 0.2.1-alpha.5 to 0.2.1-rc.2, the
  version a registry install of the published dsh resolves (cordis 4.0.5,
  loader 1.0.6, schemastery 3.18.5). The boot-tier packages the real-host
  harness needs next (dsh-llm, dsh-app-boot, dsh-config-editor,
  dsh-settings, cordis-plugin-loader) are added as devDependencies.
- test/support/host-versions.ts names those versions once.
  test/host-versions.test.ts checks that the pi the plugin loads at
  runtime is 1.1.0 and the same copy the tests import, that the patch is
  applied, and that the installed cluster matches.
- mount-protocols now checks that every catalog SDK maps to a protocol
  pi's exported api modules implement, and records as a characterization
  row that pi 1.1.0's opencode-go provider has no Google API.

No src/ behaviour changes: the rc.2 builds of llm-pi-ai, cordis and
schemastery are code-identical to alpha.5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD

viztor commented Oct 23, 2026

Copy link
Copy Markdown
Owner Author

catalog freshness is red for the same reason as on #108: the vendor catalog added a Go model (opencode-go: 28 -> 29, + kimi-k3.1-pro). This PR doesn't touch src/catalog-data.ts or its generator.

No fix exists yet. The shim needs pnpm run catalog:shim -- --write, run with network access to models.opencode.ai. The repository's nightly run will open the refresh PR against main. Once that merges, main gets merged up through this stack. The other checks on this commit are unaffected.


Generated by Claude Code

The vendor added kimi-k3.1-pro to the Go plane, so `catalog freshness`
went red on every branch of this stack. This takes the two generated
files exactly as the nightly refresh produced them (#111), with no
hand edits, and updates the counts that hard-code the catalog's size:
the Go shim test (28 -> 29), the plane table in redirect-planes.md
(Go served 28 -> 29, the no-SDK Go cell 17 -> 18), and the Go
completions list in both READMEs. The listed half of "29 served / 33
listed" is models.dev's count, inferred from the one added entry,
because models.dev cannot be reached from here.

It no-ops once main carries #111. P-T2 turns these counts into
invariants so a refresh stops needing this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The vendor renamed the Go plane's one Gemini model (dropping its
contributor suffix, halving its price and publishing an allowance for
it where the limits table had none) and added a free model with no
SDK, so `catalog freshness` went red on every branch of this stack.
This takes the two generated files exactly as the nightly refresh
produced them (#111), with no hand edits, and follows them:

- the Go Gemini fixtures in six test files (attachment-services,
  catalog, go-google, go-google-prepare, models-discovery,
  responses-routes) now name the renamed id, since each means "the
  Go Gemini model the catalog serves";
- the usage panel test built its unknown allowance from the table's
  one unpriced row, which the vendor has now priced, so it builds the
  `Number.NaN` allowance the generator emits itself;
- the Go shim count in catalog.test.ts (29 -> 30);
- redirect-planes.md: Go 29 -> 30 served, the no-SDK Go cell
  18 -> 19, and "0 of 29" -> "0 of 30" in the prose;
- both READMEs: the renamed Gemini id, and the new model in the Go
  completions list, since it declares no SDK.

The listed half of "30 served / 34 listed" is models.dev's count,
inferred from one rename (no change) and one addition (+1), because
models.dev cannot be reached from here. The dated S0 measurement row
in improvement-plan.md keeps the id it was measured with.

It no-ops once main carries #111.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The live routing suite picks its free-tier subjects from the catalog by
asking the gateway, but it counted "400 Upstream request failed: Model
is unavailable" as an observable answer. The upstream withdrew
nemotron-3-ultra-free while the catalog still lists it, so the picker
chose it and two cases asserted on a model the gateway no longer
serves; the reachability case also failed on a 429 throttle.

A withdrawn model (that 400, or a 410) is now skipped like a throttled
one. The free-tier routing case only accepts a candidate whose answer
is the free-tier gate it asserts, so an unmetered model cannot become
its subject, and the reachability walk moves past gated, withdrawn and
throttled candidates before it reports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The vendor moves its model catalog, its Go limits page and its CLI
version almost daily. Each move turned every open pull request red,
although none of them caused it: vendor drift is not a regression of
the change under test.

A pull request now fails a freshness check only when its diff changes
that check's generator code: the script, every local module it loads,
transitively, and the pinned version file for the CLI check.
scripts/freshness-scope.ts derives those inputs from the sources and
makes the call, so a new import moves them without a list to edit.
Any other pull request reports drift as a warning, as a push to main
already did. The offline check that every copy of the CLI version
agrees with the pin still gates every pull request, since only the
repository can break it. The gates fail closed: a lost scope answer
gates rather than skips.

The nightly run still regenerates the two tables and proposes the
refresh as its own pull request, and warns on the CLI version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
claude added 13 commits November 3, 2026 04:47
The status table and the P1b section say what the merge of P1a kept:
main's tool-fallback plane rule (D25) with only the Anthropic schemas
deleted, and one catalogue fixture behind the door.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The id is the test's data; the comment describes the rule it shows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Closes the review of the restacked P1b head.

- free-tier-contract: the comment states D25 (the injector reads its plane
  from the URL, with the plane-blind row as fallback), not the dropped
  route rule. No assertion changes.
- tool-fallback: the shipped-shim fallback case asserts its premise (the id
  is free on Zen and absent from the Go catalog), so a regeneration that
  lists it on Go fails loudly instead of leaving the case vacuous. Checked by
  seeding the id onto Go: the premise assertion fails.
- improvement-plan: the six defect rows P1b resolves are struck and marked
  "Fixed by P1b", with the pre-fix locations kept.
- AGENTS.md: the retry.ts row is in the past tense; it no longer says P1b
  has yet to move the timer and console out.
- testing.md and the catalogs fixture: "like every other support module"
  becomes "like the other fixtures"; the setup file names the defining
  modules directly because it also loads in the jsdom files. CONTRIBUTING's
  exception list is left to P2, which names the setup file in its gate.
- The #107 body (item 6, the behaviour-change section, the model id and the
  verification table) is rewritten after step 11 with the other bodies, as
  the restack plan schedules; nothing in the tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…try points

- fixed: both-kept-2-P1B-R3-2 and rules-docs-2-P1B-R3-DOC-2 (applied once):
  CONTRIBUTING.md's door-rule exception list gains P2's clause verbatim,
  "and the setup file `test/support/reset-process-state.ts`, which runs
  before the jsdom tests too", so it agrees with test/support/catalogs.ts
  and docs/testing.md. The plan's P2 restack row records that the merge
  conflict on that line resolves to P2's line.
- fixed: rules-docs-2-P1B-R3-DOC-1: docs/testing.md's lead-in says
  "Three entry points", matching the three bullets the fixture exports.

Wording only; no code or test changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Outcomes of the P1b restack review:

- both-kept-3-P1B-R4-2, resolutions-3-P1B-R3-CH-1,
  rules-docs-3-P1B-R3b-1 (one item): no code or decision change on
  P1b. D14' and D22' stay as P0 wrote them; the P-T1a restack row now
  records the open question (one verified channel against a both-channel
  goal), that the owner decides it as D14''/D22'', the cheapest coverage
  (a per-channel HOST_VERSIONS and an rc row in the e2e-host matrix,
  floating first, pinned only once the latest rc tag's pi is confirmed),
  and that the 1.1.0 patch is kept until then.
- both-kept-3-P1B-R4-1, rules-docs-3-P1B-R3b-2 (one item): no tree
  change. The pull request body predates the restack; at push, one PR
  comment line says so, and the body is rewritten (no behaviour change,
  the plane rule is main's D25, the shim case named by its premise,
  test/support/catalogs.ts, the restacked gate's numbers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…b' into claude/vigilant-mendel-bctz9n-p2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Three files arrived from main without a conflict and import src paths directly
(the Go-limits HTML reader, the Google thinking-level test and the gateway
contract mock), which the door gate refuses. The door gains LEGACY_PLANE_API,
and the gate's own walk check names P1b's merged fixture module
(support/catalogs.ts) instead of the file it replaced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
main's testing guide named scripts/wrapper-forwarder.ts and its link test,
both of which P2 deletes when it re-retires the alias (D9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The paragraph under "Every count in the documentation, checked against
the data" stated an earlier shim's counts in the present tense and said
every live count in the docs agreed with them, which stopped being true
when the shim was regenerated. It now records the sweep as done against
the shim of that day, restates no number (a number there is one no test
reads), and points to redirect-planes.md sections 1 and 2, the dated
tables test/catalog.test.ts reads back against the shipped shim.

Outcomes:
- fixed: rules-docs-1-P2-DOC-1. The line is main's and every branch
  below P2 still carries it; landing it here carries it to every branch
  above through the cascade, and the bottom-of-stack fix is recorded as
  a follow-up for #105's docs sweep.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The vendor added a free Zen model, `pulse-alpha-free`, after main's
2026-10-31 regeneration, and `catalog freshness` gates this pull request
because it changes the generator's code. `src/catalog-data.ts` is taken
exactly as the nightly refresh produced it (0f22c3e on
chore/generated-refresh), with no hand edits; `src/go-limits-data.ts`
did not move. The counts that restate the catalog follow it:

- Zen 80 -> 81 served, no SDK 23 -> 24, free 8 -> 9 (catalog.test.ts,
  redirect-planes.md, protocol-routing-and-merge.md, both READMEs);
- the registry's listed total 130 -> 131, since an added model is
  listed as well as served;
- the count dates move to 2026-11-03.

It no-ops once main carries the same refresh.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…del-bctz9n-p1a

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…-mendel-bctz9n-p1b

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Merge origin/claude/vigilant-mendel-bctz9n-p1b (835c793) into P2, so #108
is one merge from its parent's green tip again. No conflicts; P2's own
commits touch none of the count-bearing hunks.

- fixed resolutions-3-P2-R3-1: the parent's 798ba2e arrives unchanged.
  src/catalog-data.ts is the nightly blob (0f22c3e) with the newly listed
  free Zen model, byte-identical to the parent tip, never hand-edited;
  src/go-limits-data.ts did not move. The restated counts (Zen 81 served /
  131 listed, no-SDK 24, free 9, dated 2026-11-03) come from the parent's
  side in both READMEs, docs/redirect-planes.md,
  docs/protocol-routing-and-merge.md and test/catalog.test.ts. Correction
  2 ("take main's blobs") is superseded for the generated files by the
  parents' decision to carry the nightly refresh, so every branch above
  P2 inherits the same blob through its parent merge.
- deferred resolutions-3-P2-R3-2: no worktree file is wrong
  (docs/improvement-plan.md states D9 correctly); the PR body's D9 line
  is rewritten in the post-step-11 body pass, per the plan.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
@viztor viztor mentioned this pull request Nov 3, 2026
…' into claude/vigilant-mendel-bctz9n-p-t1a

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
scripts/use-channel.ts <rc|alpha> [--floor | --relock] installs a
channel's committed lockfile (pi unpatched at the newest version the
bridge's range admits, what an npm user runs), or the same cluster with
pi at the range's floor, and restores every tracked file it rewrote, so a
switch leaves no diff. It leaves a marker the suite reads to know which
record to check. pnpm now only warns before a run when the install looks
stale, since an automatic reinstall would silently put the default
channel back under a run meant to test the other one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The harness's own pi patch, which only the desktop app ships, removes
the per-delta re-parse of streamed tool-call arguments and round-trips
thought signatures. npm installs run pi unpatched. The plugin supports
both because no src module reads either; this tripwire keeps it so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
A matrix over [alpha, rc] on every pull request, each on the channel's
npm install, and on each channel's floor pi nightly and on dispatch.
DSH_CHANNEL and DSH_PI make the host-versions test fail by name if the
switch did not happen, and the job checks the switch left no diff.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
The plan's D14 and D22 rows become D14'' and D22'': the plugin supports
the latest release of both harness channels as each ships, with the
measurement that npm installs run pi unpatched and why this repository
carries no pi patch. The README pair states the support policy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
A pipe inside a code span split the row once the formatter ran, and the
patch's thought-signature hunk is described without naming a model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
Closes the audit's findings on the both-channel, no-patch restack. Docs and
comments only; no behaviour changes, so no new failing-first test.

- both-kept-1-F1, rules-docs-1-R2: the plan of record gains D26 ("host
  gaps are the host's job", owner 2026-11-03): the Google level wrapper
  goes, pi-ai's defaults are not restated, no base or protocol is read
  from pi's catalog entries, host gaps are HOST_LIMITATION rows skipped by
  feature detection. D16's order and last clause, D17's defaults sentence,
  the P-R1 design-table rows and the P-R1 status and adjustment rows now
  say so; P-R1 implements it.
- both-kept-1-F2: the status, P3, L13 and gate rows use D14'''s
  vocabulary (both channels, newest and floor); L13 moves off P-T3,
  since each channel's default install already is the newest pi.
- resolutions-1-L1: kept the rc repin (one coherent cluster per lockfile)
  and said so in the rc lockfile test, the CI comment and pinFor: the rc
  cell runs the host's schemastery, not the published pin. Widening the
  plugin's own pin is left to the owner.
- rules-docs-1-R1: CONTRIBUTING's release step names the "Supported" line
  and the Support policy paragraph, and says their versions come from the
  channel records and move with the lockfile.
- rules-docs-1-R3: the two per-shape count headers carry the shim's
  2026-11-03 date, matching every other stamp (counts re-derived from the
  shim: Zen 81 = 24/31/17/8/1, Go 30 = 19/5/5/1).
- rules-docs-1-R5: both READMEs name both test installs (alpha default,
  rc) instead of one.
- rules-docs-1-R4: the PR body is rewritten after the push, not here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
…pure half

Outcomes of the review round on P-T1a:

- both-kept-2-rr2-L1 / resolutions-2-rr2-T1 (one finding): fixed.
  test/use-channel.test.ts feeds withoutHostPackages a two-document
  lockfile in pnpm's shape (the package-manager record, then the project
  document with quoted host keys in the importer's three dependency
  sections, packages and snapshots, one peer-suffixed snapshot key, a pi
  entry and a non-host package naming a host peer in its sub-map). It
  asserts every host key is gone from all five sections, the input held one
  in each (so a strip that read nothing fails), pi and the non-host
  entries survive with their sub-maps, the first document is unchanged,
  and the strip is idempotent. Planted four mutations (a section dropped
  from the list, the importer's optional section dropped, the key match
  broken, deletion at the wrong path): each turns it red.
  harness-channel.test.ts also reads every @deepseek-ai version in each
  committed lockfile and fails on one from the other channel's prerelease
  line, which the record fields (dsh, cordis, loader, schemastery) did not
  cover; planting an alpha cordis-plugin-include entry in the rc lockfile
  turns it red.
- both-kept-2-rr2-L2 / rules-docs-2-rules-docs-2-R1 (one finding): fixed.
  docs/testing.md's pure-halves table gains the use-channel.ts ->
  scripts/channel-manifest.ts row, and AGENTS.md's use-channel line names
  the pure half, its four rules and test/use-channel.test.ts.
- both-kept-2-rr2-L3: no tree change, by the finding's preferred route.
  The README policy sentence stays as written in both languages; the plan
  of record's D16 row already says main's #130 Gemini level rewrite leaves
  with the module P-R1 (#113) deletes, and the #109 pull request body
  carries the same sentence, so P-R1 does not have to undo a README clause.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD
… P-T1a

- fixed (resolutions-3-t1a-rr3-1, rules-docs-3-rr3-docs-1, both-kept-3-rr3-L2):
  the nightly drift report D22'' asks for now exists. scripts/host-drift.ts
  is its pure half (hostDrift: dsh at each record's dist-tag; cordis, the
  loader, schemastery and pi at the newest version the bridge's declared
  ranges admit; the pi range and its floor; "read nothing" is a line, never
  "current"), scripts/report-host-drift.ts the CLI (one ::warning:: per
  drift, exit 0 always), and CI's host-drift job runs it on schedule and
  dispatch only. test/host-drift.test.ts holds the rules with known answers,
  runs the CLI against a loopback registry and evaluates the job's
  condition. D22'' now says P-T1a reports and P-T3 proposes the bump pull
  request; P-T3's status row, item 11 and restack row carry that half.
- fixed (resolutions-3-t1a-rr3-2): the other-channel prerelease check reads
  each other channel's tag with semver.prerelease and skips a stable
  channel, so rc going stable no longer fails the alpha cell; a known-answer
  case (alpha, rc, a synthetic stable record) fails with the old reader.
- fixed (both-kept-3-rr3-L1, resolutions-3-t1a-rr3-3): D22'' states a bump
  is version data only and cites CONTRIBUTING's release step 3, now the one
  list (rc: record + channels/rc lockfile via --relock; alpha: record,
  package.json's exact pins, root lockfile; both: the README pair's "today"
  sentences and minimumReleaseAgeExclude lines for a cut under a day old).
  test/harness-channel.test.ts now holds both READMEs' support-policy
  versions to the records.
- fixed (resolutions-3-t1a-rr3-4): D26's split and P-R1's restack row give
  P-R1 the HOST_LIMITATION row, the feature-detected skips, the "Host
  limitations" list and the upstream report; P-T1b keeps only the
  per-channel tagging of rows that already differ.
- fixed (rules-docs-3-rr3-docs-2, rules-docs-3-rr3-docs-3): the restack
  intro names both channels' hosts, and names P0's nightly vendor refresh
  (798ba2e) as the one exception to "generated files take main's blobs", in
  the intro and in P0's status and restack rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD

viztor commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

Restacked onto main 1.8.0. This branch now carries main 1.8.0 through its parent, P2 (#108). Pushed head: 4679c1da4c7df379c7fc74cca182ecd348cf43a9. The PR body has a new "Restacked onto main 1.8.0" section with the new scope: two harness channels and no pi patch (D14'', D22'').

Conflicts and how they were resolved

Dependencies and install

  • package.json: took main's alpha cluster and kept the branch's real-host tier devDependencies at 0.2.2-alpha.2. pi is now the union range ^1.1.0, replacing both the branch's exact 1.1.0 and main's exact 1.4.0.
  • pnpm-workspace.yaml: took main's minimumReleaseAgeExclude. Dropped the pi override and patchedDependencies, and said why in a comment. Added verifyDepsBeforeRun: warn.
  • pnpm-lock.yaml: took the parent's lockfile, then regenerated it. It now holds one unpatched pi 1.5.0. The new channels/rc/pnpm-lock.yaml comes from use-channel.ts rc --relock.

Docs

  • AGENTS.md: took the parent's client-modules line. The host-versions line now describes two records and the rule for selecting one. Dropped the patches/ line. Added a line for scripts/use-channel.ts, with the no-patch rule and the tripwire.
  • docs/improvement-plan.md: took the parent's restack table. Rewrote D14' as D14'' and D22' as D22'', plus the P-T1a status row, phase section, restack rows, test list and e2e-host cells, and the D14' cross-references in F2/G1/G2/S0.
  • src/google-bridge.ts: comment only. It now gives both channels' bridge ranges and says the dev install follows the channel's lockfile.

Tests

  • test/harness-channel.test.ts: kept main's exact pins, peer-range and both-channel typert checks. Kept the branch's lockfile checks and now runs them per channel. Added checks for the manifest default, the pi devDependency range, each record's floor, and no pi patch.
  • Wrong after the merge, though git reported no conflict:
    • host-versions.test.ts was rewritten to check the tree against the selected record, and the patch-hunk test was deleted.
    • The F10 row in mount-protocols.test.ts is now keyed by the installed pi.
  • Stale after the merge: no-local-paths.test.ts now scans channels/ instead of patches/. The google-bridge.test.ts comments and CONTRIBUTING's prerequisite line now describe the two channels.

Dropped

  • The pi 1.1.0 pin, the harness's patch, the pi override, patchedDependencies and the patch-hash and hunk tests (D14'').
  • The alpha-only variant of this phase: a pi 1.3.0 override plus a copy of the 1.3.0 patch.
  • main's exact pi 1.4.0 devDependency.
  • The rc.2 cluster pin. Two records replace it: alpha 0.2.2-alpha.2, the committed default, and rc 0.2.1-rc.5.

Review rounds

Round Found Upheld Fixed
1 9 8 8
2 6 5 5
3 9 9 9

Round 3's fixes:

  • Added a nightly host-drift job, with scripts/host-drift.ts and scripts/report-host-drift.ts, tested against a loopback registry.
  • Hardened the check that the other channel's tag is a prerelease, so it still passes once rc ships a stable release.
  • D22'' now says a channel bump is version data only, and CONTRIBUTING lists everything a bump moves.
  • Moved D26's HOST_LIMITATION work to P-R1.
  • Corrected the restack intro.

Live re-measures still owed

  • CI on this PR: the new test · alpha and test · rc jobs must pass.
  • Nightly or dispatch: the pi floor cells must pass, with rc on pi 1.1.0 and alpha on pi 1.3.0.
  • P-T1b's binary e2e job, per channel: install dsh@alpha and dsh@latest from the registry, confirm the pi the loader resolves (1.5.0 today), and confirm there is one instance of it.
  • Locally: all four installs passed 1393 unit and 52 mock tests: alpha with pi 1.5.0 and 1.3.0, and rc with pi 1.5.0 and 1.1.0.

Follow-ups, not done in this restack

  • Registry drift: dsh@alpha is now 0.2.2-alpha.3, published today, with cordis 4.0.6-alpha.2, the loader at 1.0.7-alpha.2 and schemastery 3.18.6-alpha.2. The records keep the alpha.2 cluster, and the new drift job reports the gap. P-T3 proposes the bump.
  • Two schemastery copies for rc users: the runtime @deepseek-ai/schemastery pin, exact 3.18.6-alpha.1, is outside the rc bridge's ~3.18.5, so an npm user on rc gets two copies. The owner needs to decide whether to widen it.
  • Next phases:

Four reviews ran on the general model because the dedicated reviewer was unavailable: review:P-T1a:rules-docs#3, review:P-T1a:both-kept#3, review:P-T1a:resolutions#3 and verify:P-T1a#3.1.


Generated by Claude Code

…s of P-T1a

Completes the restack of P-T1a: the review's upheld findings, one outcome
each.

- both-kept-3-t1a-rr3b-1 (fixed): Dependabot's npm entry ignores
  `@deepseek-ai/*` and pi for every update type, and its comment states
  D22'' (a host or pi move is a channel bump: record, lockfile and alpha's
  exact pins together; the nightly host-drift job reports it, P-T3 proposes
  it). The wrong google-bridge claim is gone. A new harness-channel case
  reads .github/dependabot.yml and requires both ignores (red before the
  config change).
- resolutions-3-resolutions-4-t1a-1 (fixed): a per-lockfile case holds each
  importer section's specifiers to channelManifest(package.json, record),
  and the lockfile's lockfileVersion, settings, catalogs, overrides and
  package-manager record to the root lockfile's; its message names
  `scripts/use-channel.ts rc --relock`. Mutation-checked: a vitest
  specifier change turns both lockfile cells red, an edited rc override
  only the rc cell. CONTRIBUTING, AGENTS.md and dependabot.yml say any
  change to package.json's dependencies or the workspace's catalog or
  overrides relocks rc in the same commit.
- resolutions-3-resolutions-4-t1a-2 (fixed): F10 is keyed by pi line
  (>=1.1.0 <1.3.0 none, >=1.3.0 <2.0.0 native), with known answers, so a
  pi bump inside a line stays version data and a pi outside every line or
  contradicting its row still fails. Release step 3 and D22'' name the
  pi-moving commands (`pnpm update @earendil-works/pi-ai --no-save`, then
  `rc --relock`).
- rules-docs-3-rr3-docs-E (fixed): step 3's "whole set" is scoped to cuts
  inside the admitted prerelease tuples; a cut opening a new tuple is a
  reviewed range change (peer clause, README peer-range line). Mirrored in
  D22'' and P-T3's item and row.
- both-kept-3-t1a-rr3b-4 (fixed): the patched-pi tripwire also matches a
  `.partial` property read (dotted, optional-chained, bracketed), not a
  `.partial()` call or `Partial<>`; planted cases go red on the old pattern.
- both-kept-3-t1a-rr3b-2 and rules-docs-3-rr3-docs-A (fixed, one change):
  D17 keys the default window and max tokens on the bridge, i.e. the
  channel, whichever pi runs; the P-T1b restack row keys the unknown-id
  context row by channel.
- both-kept-3-t1a-rr3b-3 and rules-docs-3-rr3-docs-B (fixed, one change):
  F2 carries one version-exact note (1.3/1.4 bases, 1.5.0's per-protocol
  roots, routing reads neither); G1 and G2 point to it.
- rules-docs-3-rr3-docs-C (fixed): F10's note is in D14''/D26 terms (holds
  on rc's floor, per pi line, routing not a gap-fill).
- rules-docs-3-rr3-docs-D (fixed): P-T1a's item 4 and test list name the
  host-drift job, the Dependabot ignore and the lockfile-sync case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141fKp5k6hiaJcP5ovhGtSD

viztor commented Nov 3, 2026

Copy link
Copy Markdown
Owner Author

Restacked onto main 1.8.0. The branch now carries main 1.8.0 through its parent (P2, #108). Pushed: 63106633a95ac86394603cd6d7d7194ae90320ed.

The scope follows the owner's 2026-11-03 decision: the plugin supports the latest release of both harness channels, with no pi patch (D14'', D22''). The PR body has the full new scope.

Conflicts and resolutions

Dependencies and install:

  • package.json: took main's alpha cluster (schemastery 3.18.6-alpha.1, cordis 4.0.6-alpha.1, @deepseek-ai/* 0.2.2-alpha.2) and kept the branch's real-host tier devDependencies at alpha.2. The pi devDependency is the union range ^1.1.0, replacing both the branch's 1.1.0 and main's 1.4.0.
  • pnpm-workspace.yaml: took main's minimumReleaseAgeExclude. Dropped the pi override and patchedDependencies, with a comment saying why. Added verifyDepsBeforeRun: warn.
  • pnpm-lock.yaml: took P2's lockfile and regenerated it. It now holds one unpatched pi 1.5.0 snapshot and no patch_hash. The new channels/rc/pnpm-lock.yaml comes from scripts/use-channel.ts rc --relock.

Code and tests:

  • src/google-bridge.ts: comment only. It now describes rc's ^1.1.0 and alpha's ^1.3.0 bridge ranges and says which pi each install gets.
  • test/harness-channel.test.ts: merged both sides. From main: exact pins, pins inside the peer range, and the both-channel list. From the branch: the per-lockfile checks, now run against both channels. New: the manifest is the default channel's, pi is a devDependency only, each floor equals its range's floor, and there is no pi patch.
  • test/host-versions.test.ts (no conflict, wrong after the merge): rewritten to check the selected channel record, with four selection cases. The patch-hash and patch-hunk test is deleted.
  • test/mount-protocols.test.ts (no conflict, wrong after the merge): F10 is now per pi line.
  • test/no-local-paths.test.ts, test/google-bridge.test.ts and CONTRIBUTING (no conflict, stale after the merge): updated for channels/ and the two-channel switch.

Docs:

  • AGENTS.md: took P2's client-modules line, rewrote the host-versions line for two records, dropped the patches/ line, and added a scripts/use-channel.ts line.
  • docs/improvement-plan.md: took P2's restack table, then rewrote D14'' and D22'', the P-T1a status, phase section and test list, the restack rows, the e2e-host cells, and the cross-references in F2/G1/G2/S0.

Dropped

  • The pi 1.1.0 pin and the harness's patch, the pi override, patchedDependencies, and the patch-hash and patch-hunk tests.
  • The alpha-only variant of this phase (a pi 1.3.0 override plus a copy of the alpha tag's patch).
  • main's exact pi 1.4.0 devDependency, which matched neither channel's host.
  • The rc.2 cluster. Two records replace it: alpha 0.2.2-alpha.2 (the committed default) and rc 0.2.1-rc.5.

Review rounds

Round Found Upheld Fixed
1 9 8 8
2 6 5 5
3 11 11 11

The round 3 fixes:

  • Dependabot ignores the harness cluster and pi, and a test enforces it.
  • A per-lockfile check catches an rc lockfile left stale by a dependency change.
  • F10 is keyed by pi line.
  • The tripwire also catches partial reads.
  • D17 now attributes the default window and max tokens to each channel's bridge.
  • F2 carries one version-exact note on pi 1.5.0's bases.
  • The plan lists the host-drift job.
  • CONTRIBUTING separates a bump inside the current prerelease tuple from one that opens a new tuple.

Live re-measures still owed

  • The PR's test · alpha and test · rc jobs must pass.
  • The nightly or dispatch pi floor cells must pass: rc on pi 1.1.0 and alpha on pi 1.3.0. The host-drift job must run on dispatch.
  • For P-T1b's binary e2e job, per channel: install dsh@alpha and dsh@latest from the registry, confirm which pi the loader's resolver loads (1.5.0 today), and confirm there is one instance of it.

Locally, all four installs passed 1393 unit and 52 mock tests: alpha on pi 1.5.0 and 1.3.0, and rc on pi 1.5.0 and 1.1.0.

Four reviews ran on the general model because the dedicated reviewer was unavailable: round 3's resolutions, both-kept and rules-docs reviews, and the verification pass after round 3.


Generated by Claude Code

@viztor
viztor force-pushed the claude/vigilant-mendel-bctz9n-p2 branch 3 times, most recently from 5e7a442 to 3654e35 Compare November 4, 2026 19:57
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.

2 participants