Phase 1 of pluggable languages: plain ids, owner setting, opt-in enablement, ABI and profiles, cost fixes #308

Closed
buildagent wants to merge 0 commits from p1-integration into master
Member

Phase 1 builds the foundation for moving languages into plugin packages. No language is switched to a package yet: the builtin parsers still index everything, and enabling a language chooses who owns it only under auto.

Language ids, owner setting and enablement (lane P)

  • Reserved plain ids. First-party packages write rows with plain ids (ruby), byte-identical to the builtin's, so filters, baselines and bridges don't move. A third-party package that declares a reserved id is refused with language_reserved, at install and at activation. plugin trust add --first-party lets an operator anchor their own key for reserved ids.
  • Owner setting. [languages] <lang> = auto | builtin | package. A switch re-extracts only the files that change hands, and project_overview and index_freshness report it. This is the one-release fallback.
  • Opt-in enablement.
    • code-index init enables the languages the repository contains and writes .code-index.toml.
    • An automatic first start records the same decision in .code-index/languages.toml, so the user's git tree stays clean.
    • Upgrades keep every language the index already holds.
    • A language that isn't enabled is reported as language_not_enabled, with the command to fix it.
    • New command: code-index languages list | enable | owner.

Plugin interface, profiles and guest kit (lane Q)

  • Declared profiles. A package can declare [languages.profile] (extends plus 17 fields, as closed enums). Unknown values are refused at install. The first_party_profiles gate checks that each builtin's canonical declaration matches its compiled answers.
  • New ABI tags, all additive:
    • doc span (0x8005);
    • qualifier segments (0x8006);
    • exports fact (0x8007);
    • grantable lexical_local and pytest_fixture roles;
    • an extension_methods capability;
    • multi-grammar packages ([[grammars]]), with one worker lane per grammar.
  • module_map hook. Sandboxed and cached. The Composer PSR-4 mapping agrees with the builtin on all 129 comparable files of php-guzzle. The resolver does not read module maps or the exports fact yet; that happens before PHP and Python switch to packages.
  • code-index-guest-kit. The shared guest helpers, used by the Ruby, Svelte and TimeLine packages, whose wasm is unchanged. A native Ruby differential runs over ruby-sinatra: 152 files, 0 differences from the builtin.

Cost and robustness (lane R)

  • Influence-pass exemption. A package signed by a compiled-in first-party key, whose languages all adopt builtin profiles, skips the general influence pass. SQLite work: 1.76× the builtin becomes 1.00×. Third-party packages still pay it.
  • HOST_EXTRACTION_EPOCH (m0073). Package rows are keyed by the extraction epoch, not the host release. A gate fingerprints every shipped package's derived rows and fails if they change without an epoch bump. The epoch is now 2, because the plain-id stamp changed Ruby's rows.
  • Verified caches. Precompiled grammars and extractors are cached, checked against a manifest and garbage-collected. Loading the Ruby grammar drops from 215 ms to 24 ms, and the pool grows lazily.
  • Ruby overhead record (_prdoc/records/P1-ruby-package-overhead.md): the earlier 1.44× vs 3.4× disagreement is settled, and cold start is attributed per phase.

Integration

  • Extractor cache per lane. The extractor cache is keyed per lane by the wasm's content hash. Otherwise lanes of one multi-grammar package would share one .cwasm.
  • First party has two meanings:
    • Permission to claim a reserved id: compiled-in keys or an operator --first-party anchor.
    • The influence exemption: compiled-in keys only. That errs toward paying the pass, never toward hiding it.
  • Package baseline. Re-recorded: the Ruby package now resolves 17 more refs. The bind diff shows all 17 are newly resolved, with none lost or retargeted, and each matches the builtin's target and rule. The package now resolves exactly as many refs as the builtin (2,966).
  • ruby-package-cost. Re-measured under schema 73. It stays within its band; the −1.9% drift predates Phase 1.
  • Bounding registry. Phase 1's bounds are registered in bounding_site_registry. Two indexer constants that reused package-crate names were renamed.
  • CI. The guest-kit corpus differentials run in the CI corpus job.
  • project_overview is back under its token budget (4,046 of 4,050).

Validation

  • One full local gate run on the merged tree (0397b0c):
    • Passed:
      • clippy on Linux and the Windows target, and strict rustdoc;
      • corpus_ratchet, corpus_mutation, corpus_cost and corpus_stage;
      • agent_task_bench;
      • precision 7/7 with 0 phantoms.
    • It found 12 failing test targets across 11 issues. All are fixed in this PR, each with its mutation run.
  • After the fixes:
    • The touched crates: 3,328 passed, 0 failed.
    • The release corpus set (ratchet, mutation, stage, cost, package baseline, package cost attribution, Ruby package parity and cost): all green.
    • I re-ran every previously failing target on this head myself, and all pass. I also ran the influence-exemption mutation: putting the operator flag back into the exemption turns first_party_influence red.
  • Baselines: tests/corpus/baseline.json, cost-baseline.json and stage-baseline.json are unchanged. Only package-baseline.json and ruby-package-cost.json moved, for the reasons above.

Not in this PR

  • #301 (resumable resolve) and #307 (promotion lock): prerequisites before any language flips.
  • Declared profiles are stored in a lazily created table rather than a numbered migration.
  • Grammar cache: named grammar lanes run without it.
  • Resolver wiring: the resolver does not yet read module maps or the exports fact.
  • Cold start: the budget is met on wall time but not yet re-measured on a quiet machine.

🤖 Generated with Claude Code

https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu

Phase 1 builds the foundation for moving languages into plugin packages. No language is switched to a package yet: the builtin parsers still index everything, and enabling a language chooses who owns it only under `auto`. ## Language ids, owner setting and enablement (lane P) - **Reserved plain ids.** First-party packages write rows with plain ids (`ruby`), byte-identical to the builtin's, so filters, baselines and bridges don't move. A third-party package that declares a reserved id is refused with `language_reserved`, at install and at activation. `plugin trust add --first-party` lets an operator anchor their own key for reserved ids. - **Owner setting.** `[languages] <lang> = auto | builtin | package`. A switch re-extracts only the files that change hands, and `project_overview` and `index_freshness` report it. This is the one-release fallback. - **Opt-in enablement.** - `code-index init` enables the languages the repository contains and writes `.code-index.toml`. - An automatic first start records the same decision in `.code-index/languages.toml`, so the user's git tree stays clean. - Upgrades keep every language the index already holds. - A language that isn't enabled is reported as `language_not_enabled`, with the command to fix it. - New command: `code-index languages list | enable | owner`. ## Plugin interface, profiles and guest kit (lane Q) - **Declared profiles.** A package can declare `[languages.profile]` (`extends` plus 17 fields, as closed enums). Unknown values are refused at install. The `first_party_profiles` gate checks that each builtin's canonical declaration matches its compiled answers. - **New ABI tags,** all additive: - doc span (`0x8005`); - qualifier segments (`0x8006`); - exports fact (`0x8007`); - grantable `lexical_local` and `pytest_fixture` roles; - an `extension_methods` capability; - multi-grammar packages (`[[grammars]]`), with one worker lane per grammar. - **`module_map` hook.** Sandboxed and cached. The Composer PSR-4 mapping agrees with the builtin on all 129 comparable files of `php-guzzle`. The resolver does not read module maps or the exports fact yet; that happens before PHP and Python switch to packages. - **`code-index-guest-kit`.** The shared guest helpers, used by the Ruby, Svelte and TimeLine packages, whose wasm is unchanged. A native Ruby differential runs over ruby-sinatra: 152 files, 0 differences from the builtin. ## Cost and robustness (lane R) - **Influence-pass exemption.** A package signed by a compiled-in first-party key, whose languages all adopt builtin profiles, skips the general influence pass. SQLite work: 1.76× the builtin becomes 1.00×. Third-party packages still pay it. - **`HOST_EXTRACTION_EPOCH`** (m0073). Package rows are keyed by the extraction epoch, not the host release. A gate fingerprints every shipped package's derived rows and fails if they change without an epoch bump. The epoch is now 2, because the plain-id stamp changed Ruby's rows. - **Verified caches.** Precompiled grammars and extractors are cached, checked against a manifest and garbage-collected. Loading the Ruby grammar drops from 215 ms to 24 ms, and the pool grows lazily. - **Ruby overhead record** (`_prdoc/records/P1-ruby-package-overhead.md`): the earlier 1.44× vs 3.4× disagreement is settled, and cold start is attributed per phase. ## Integration - **Extractor cache per lane.** The extractor cache is keyed per lane by the wasm's content hash. Otherwise lanes of one multi-grammar package would share one `.cwasm`. - **First party has two meanings:** - Permission to claim a reserved id: compiled-in keys or an operator `--first-party` anchor. - The influence exemption: compiled-in keys only. That errs toward paying the pass, never toward hiding it. - **Package baseline.** Re-recorded: the Ruby package now resolves 17 more refs. The bind diff shows all 17 are newly resolved, with none lost or retargeted, and each matches the builtin's target and rule. The package now resolves exactly as many refs as the builtin (2,966). - **`ruby-package-cost`.** Re-measured under schema 73. It stays within its band; the −1.9% drift predates Phase 1. - **Bounding registry.** Phase 1's bounds are registered in `bounding_site_registry`. Two indexer constants that reused package-crate names were renamed. - **CI.** The guest-kit corpus differentials run in the CI corpus job. - **`project_overview`** is back under its token budget (4,046 of 4,050). ## Validation - **One full local gate run on the merged tree (0397b0c):** - Passed: - clippy on Linux and the Windows target, and strict rustdoc; - `corpus_ratchet`, `corpus_mutation`, `corpus_cost` and `corpus_stage`; - `agent_task_bench`; - precision 7/7 with 0 phantoms. - It found 12 failing test targets across 11 issues. All are fixed in this PR, each with its mutation run. - **After the fixes:** - The touched crates: 3,328 passed, 0 failed. - The release corpus set (ratchet, mutation, stage, cost, package baseline, package cost attribution, Ruby package parity and cost): all green. - I re-ran every previously failing target on this head myself, and all pass. I also ran the influence-exemption mutation: putting the operator flag back into the exemption turns `first_party_influence` red. - **Baselines:** `tests/corpus/baseline.json`, `cost-baseline.json` and `stage-baseline.json` are unchanged. Only `package-baseline.json` and `ruby-package-cost.json` moved, for the reasons above. ## Not in this PR - **#301** (resumable resolve) and **#307** (promotion lock): prerequisites before any language flips. - **Declared profiles** are stored in a lazily created table rather than a numbered migration. - **Grammar cache:** named grammar lanes run without it. - **Resolver wiring:** the resolver does not yet read module maps or the exports fact. - **Cold start:** the budget is met on wall time but not yet re-measured on a quiet machine. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
`influence::apply_general` ran over every reference the moment ANY package
held a capability. On ruby-sinatra that is 14.04M vm_step of a 17.10M builtin
pass (+82%), all four of its UPDATEs, attributed by package_cost_attribution.

A package that stands in for a builtin is now excluded from the
admitted-dynamic relation exactly as the reserved keys are:

* `Store::get_classified` answers WHICH key verified a package (the anchor
  `verifying_anchor` already returned and `get` discarded); first party is
  the key BYTES in FIRST_PARTY_ANCHORS (`Store::is_first_party_key`).
* `InstalledPackage::builtin_equivalent` = first party AND every claimed
  language adopts a builtin profile.
* m0072 adds `generation_packages.builtin_equivalent` (default 0, no
  backfill: third party until the next ordinary pass stamps it);
  `activation::record` stamps it from the live, signature-checked set only.
* `influence::first_party_exemption` excludes a key only when EVERY
  container it has in the active generation is equivalent.

Third-party packages pay exactly what they paid before.

Measured (release, contended box): builtin 17,096,223 vm_step; third-party
Ruby package 30,095,980 (1.76x); first-party Ruby package 17,099,436
(1.00x builtin). The first-party leg is now part of package_cost_attribution
and gated on taking the short-circuit.

Tests, each mutation RUN: first_party_influence.rs (4 tests, third-party
still influenced, first-party exempt with identical binds, key-bytes rule,
every-claim rule) and influence_classification's every-container test.
`Store::with_first_party_keys` is the one seam that lets a test stand a
first-party package up, because the release key's private half is not in
the repository.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
A first-party package now keeps the plain id its builtin predecessor
stamped: rows from `de.h-dv.ruby` carry `lang = 'ruby'`, byte-identical
to the builtin's, so filters, baselines and bridges do not move when the
owner changes. A third-party package may not claim such an id.

- `langid::row_lang` / `is_reserved_local`: the wire id stays the
  PRODUCER's identity (claims, routes, component keys, grants, dirty
  domain, activation digest are untouched); the ROW language is the
  plain id when the local half is reserved. `component_of` returns it
  as the component's language; the writer, the stat-touch, the
  generation build and the bridge rows stamp it.
- Reserved ids are refused BY NAME (`language_reserved`) at both store
  doors, `Store::install` and `Store::get`, unless the verifying key is
  in FIRST_PARTY_ANCHORS or was anchored with the new
  `plugin trust add --first-party` (`TrustAnchor::first_party`).
- The producer is now part of the stat and hash tiers' route gate
  (`FileSnapshot::producer`, read off the active contribution only when
  a package has ever produced, so projects without packages pay
  nothing): an owner switch between a builtin and a first-party package
  moves no `files.lang` and would otherwise be skipped.
- The withheld-grant census attributes a plain-id row by its producer.
- Tests: fixture key anchored first-party in test roots; Ruby parity
  harness asserts the plain id instead of normalising the wire id away;
  new e2e for both doors through the CLI and a running daemon.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
A package row is the package's extractor plus this host's derivation of
what it said (abi::validate, facts_to_extract, facts_diagnostics, the
outline hash, the writer). The package half is keyed by its
extraction_identity; nothing keyed the host half. What actually re-derived
package rows at a core release was a blanket `kind = 'code'` migration
(m0070, m0071), which re-runs every wasm worker on every package file
whether or not a row moves — and a host change shipped without such a
migration left stale package rows standing (lane O's finding).

(The planners' premise that `host_version` in the activation identity
reparses every package file is not live: `dirty_domain` is only ever
called with both sides built from the running binary, and
`activation_digest` is recorded, never read. The live vector was the
migrations.)

* `packages::HOST_EXTRACTION_EPOCH = 1`, bumped only when the host half
  changes what it writes; its doc states the rule for the next builtin
  migration (mark only builtin-produced files).
* m0073 adds `plugin_packages.host_epoch` (NULL: the first pass with a
  PackageHost re-extracts package files once, because no migration can
  know what derived them).
* `packages::reconcile_extraction_epoch`, at the top of every pass that
  has a PackageHost (no statement at all without one), marks the files a
  stale key produced as changed and stamps the key in one transaction.
* The gate, tests/host_extraction_epoch.rs: derives every shipped
  package's fixtures (discovered, 4 packages / 16 fixtures / 244 rows)
  through the production host path and writer with no resolve, and
  fingerprints every non-NULL non-identity column against
  host_extraction_epoch.json. A fingerprint that moves under the same
  epoch and package digest is RED, and COSI_BLESS_HOST_EPOCH=1 refuses to
  re-record it. Behavioural, not a file list: it fires on what the epoch
  promises, and not on a comment edit.

Mutations RUN: signature dropped in facts_to_extract (RED, and the bless
refuses); epoch bumped alone (RED, re-record); reconcile call removed
(RED); reconcile marking every code file (RED, parsed 4 not 3).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
ABI (crates/abi, crates/guest), all optional tags, ABI_MINOR unmoved:
- TAG_SYMBOL_DOC_SPAN 0x8005: a doc-comment block's span; the host
  derives doc_start_line and withholds a claim over another symbol's
  line, by the same two clauses attribute lines are held to.
- TAG_REF_QUALIFIER_SEGMENTS 0x8006: a qualifier spelled by n>=2
  ascending spans (Rust `use a:🅱️:{c::d}`); the HOST joins them with
  the adopting profile's separator and withholds for a language with
  none. Exclusive with qualifier_span and the rel-qualifier marker,
  refused in either record order; extents charged.
- TAG_EXPORTS 0x8007: Python `__all__` as none/literal/dynamic, with
  absence meaning NOT MEASURED. Names are spans; the resolver is NOT
  moved onto it (no bind-for-bind parity run) - decoded and exposed via
  ValidatedFacts::exports only.
- REQUESTABLE_ROLES: LEXICAL_LOCAL and PYTEST_FIXTURE named, legal on
  the measured lookup kinds / binding, outside every default grant.
- Guest SDK encoder methods for all three; wire_parity grades bytes and
  tag numbers; fuzz generator and oracles extended (anti-vacuity counts
  printed); SDK_SOURCE_DIGEST moved (the rev pin moves next commit).

Fact capabilities (package manifest + indexer):
- lexical_local, pytest_fixture, extension_methods ride the resolver
  capability request/approval path but open no pool (POOL_CAPABILITIES
  is the list's head, so builtins' grant_all is unchanged).
- The host ORs requested role bits per package per request
  (Supervisor::set_granted_roles), honours TAG_SYMBOL_IS_EXTENSION only
  under extension_methods (which requires member_candidate), and
  temp.ext_cands is now Gated(MEMBER) instead of NeverDynamic.
- dirty::Cause::FactCapability: a fact-grant move re-parses the
  package's files.

Declared profiles:
- core: closed NAMES tables, BOOL_TRAITS, canonical encode/decode,
  set_by_name refusing by field and value, ProfileBook.
- package: [languages.profile] ProfileSpec (extends + 17 optional
  fields), refused by name at install; extends must be the profile the
  id adopts; hashed into extraction_identity in a trailing block absent
  when nothing is declared (no pinned identity moves).
- indexer: m0072 declared_profiles table written by
  write_capability_grants (GrantChange.profiles_moved -> reresolve);
  temp.lang_profile, follow_own_imports and local_scope read a
  ProfileBook loaded from the index; daemon check_rename too.
- first_party_profiles gate: a complete canonical declaration for each
  of the seven builtins, diffed against PROFILE_TRAITS, and every
  checked-in first-party declaration held to it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Rule 3 of sdk_pin compares the pinned rev's SDK tree with this tree; the
digest moved in 7ebf88e and the rev cannot name the commit it is written
in, so this is the second of the two commits sdk_pin.rs documents. If the
branch is REBASED before merge, both 7ebf88e and this rev must be redone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
code-index-guest-kit (crates/guest/kit, a no_std, dependency-free workspace
member) holds what the Ruby, Svelte and TimeLine guests each wrote out by
hand: the wire indices of every SYMBOL_KINDS / REF_KINDS / VISIBILITIES
entry and every grantable and requestable role bit (graded entry for entry
by tests/ids.rs), tree-sitter's error symbol, the buffers-below-SRC_OFFSET
arithmetic, and guest_exports! — the two globals --inject-globals replaces
plus a panic handler emitted on wasm32 only, so guest source also compiles
for the host.

All three guests now use it, and their shipped extractor.wasm did not move:
the Ruby and TimeLine rebuild gates stay green under COSI_GUEST_REBUILD=1,
and the Svelte guest (no rebuild gate) was built before and after and is
identical after --strip-name-section. ruby_guest_wire now grades that the
Ruby guest takes its indices from the kit and the SDK and declares none of
its own.

tests/ruby_native_differential.rs compiles crates/guest/ruby/src/extract.rs
itself (#[path]) for the host and runs it through the worker's own tree
serializer, the parent's validator and facts_to_extract_for, then diffs
every symbol, ref and import against the builtin plugin. Fixtures always;
ruby-sinatra with COSI_CORPUS_DIR (REQUIRE=1 makes absence fatal). Measured:
152 files, 1311 symbols, 20065 refs, 223 imports, zero differences, with
the one named normalisation (the host's DERIVED_NAME stamp, 13 refs) held
to the count the guest declared.

CI: the kit is compile-checked for wasm32 (--lib).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
`[languages]` in `.code-index.toml` now means something:

  enabled = ["ruby", "rust"]   # first-party languages this project opted into
  ruby = "auto"                # owner: auto | builtin | package

Owner setting (`code_index_indexer::languages`). It acts on exactly one
mechanism: whether an approved package's consented `[[displaces]]` of a
compiled-in language takes effect. `auto` = the package when the
language is enabled and the package healthy, else the builtin;
`builtin` always the builtin; `package` the package whenever one is
healthy. Applied where a live package is admitted
(`PackageSet::finish_activation`) and where the record's planned
activation is built, so the router, the dirty domain and a
`plugin enable` preview agree. The table is part of the reactivation
witness, so a running daemon re-arms on an edit, and the stat tier's
producer gate re-extracts exactly the files that change hands.

Opt-in enablement (operator decision 2). `code-index init` detects the
languages present and writes exactly those; the first daemon or MCP
start in a project with no `enabled` decision does the same and says so
in `project_overview.language_ownership.enablement`. UPGRADE: on the
first start of this build an index's languages missing from an existing
list are added, once (a marker in `.code-index/` records the pass).
Nothing is enabled silently afterwards; a present language that is not
enabled is disclosed as `language_not_enabled` with the exact command.
Until Phase 3 flips a language the builtin still indexes everything:
enablement selects the owner under `auto` and never removes coverage.

- `code-index languages [list|enable <lang>...|owner <lang> <setting>]`.
- `project_overview.language_ownership` and
  `index_freshness.owner_switch` disclose owners, the enablement, and
  each owner switch with the number of files it re-extracted.
- Only `enabled` is ever written automatically: owner keys are refused
  by older binaries (`deny_unknown_fields`), and the CLI says so.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Every worker spawn JIT-compiled its package's grammar: measured ~215 ms
(release) for the shipped 2 MB Ruby grammar, paid once per lane per daemon
start and again after every idle retirement. tree-sitter 0.26's
`WasmStore::load_language` takes wasm bytes and compiles them itself
(`wasmtime_module_new` in its C), so there is no door for a serialized
module. The door there is, is the engine: the grammar engine is now built
with wasmtime's module cache, and `Module::new` inside tree-sitter
deserializes instead of compiling — ~24 ms per load from the cache.

* `GrammarSpec::cache_dir` (supervisor) -> `--grammar-cache` on the worker
  command line -> `Grammar::load` builds its engine with the cache there.
* `packages::grammar_cache_for` (parent, at discovery): one directory per
  (package digest, plugin-host build) under the operator's 0700 user root.
  It is handed to workers ONLY when every compiled entry matches, by name,
  length and sha256, the manifest the parent wrote when the entry was
  produced; otherwise the directory is wiped and warmed again by one
  `--kind-table --grammar-cache` subprocess (polled and killed at the
  precompile budget). A warm that fails leaves the old JIT path. wasmtime
  applies its own engine/version/flags check on deserialize.
* Needs wasmtime's `cache` feature, which brings zstd; deny.toml now
  allows BSD-3-Clause for zstd-safe/zstd-sys — an OPERATOR DECISION,
  reverted by dropping the feature.

Measured on ruby-sinatra (first-party Ruby package, 5 reps, CPU of the
process tree): cold 4,870 ms CPU with every worker JIT-ing vs 2,860 ms with
the cache (builtin 1,560).

Tests (grammar_cache.rs), mutations RUN: `--grammar-cache` not passed ->
RED (the worker did not write through the cache it was handed); a verified
directory not passed on -> RED; a directory with any manifest trusted ->
RED (a flipped byte survived discovery).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Settles the plan's 1.44x vs ~3.4x: both are ruby_package_cost's paired
wall ratio (warm host, full index, THIRD-PARTY leg), taken either side of
#113. Adds ruby_package_overhead.rs (#[ignore]), which measures builtin,
third-party, first-party-with-JIT and first-party-with-grammar-cache legs
through cold / warm full / warm edit phases, CPU of the whole process tree
plus wall, and records the results against the operator's budget in
_prdoc/records/P1-ruby-package-overhead.md: the edit loop meets the warm
budget, a warm full re-index is at it, cold does not (1.5-1.8x CPU; fixed
per-start worker and precompile cost).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
A package may declare named [[grammars]] beside its mandatory [grammar]
and route a claim to one with claims.grammar. Each grammar is a LANE with
its own extractor component: a worker holds one grammar and verifies one
kind_table_digest, so one extractor cannot serve two grammars, and the
TS/TSX/JS shape is three builds of one guest source. No guest-ABI change.

- package: NamedGrammar, ClaimDecl.grammar, Manifest::lanes, lane rules
  refused by name (undeclared grammar, component on two lanes, two
  components on one lane, unused named grammar, unused [grammar]); the
  rules apply only when named grammars exist, so every existing manifest
  parses as before. Named grammars and claim routing hashed in a second
  trailing identity block, absent for single-grammar packages.
- indexer: named lanes staged beside the default one, each precompiled
  and run under a derived per-lane worker key (health is per lane);
  InstalledPackage::lane_for picks the lane by the file's claim. The
  default lane is the exact path every package took before.
- e2e: multi_grammar_package installs a real two-lane package (the
  shipped Ruby extractor on [grammar], the shipped Svelte extractor on a
  named lane) through store/approval/discovery and extracts one file of
  each through a live PackageHost; the Svelte answer carries the
  component module symbol no Ruby worker could emit.
- Known gap: plugin check (conform) grades the DEFAULT lane only.

Also: code-index-guest-kit moves to crates/guest-kit. manifest_layering
read crates one level deep, so nested guest crates were attributed to the
SDK and the Ruby guest's kit dependency read as undeclared; it now reads
nested manifests (longest containing dir wins) and treats a literal
#[path] include as the declaration (the native differential compiles the
Ruby guest's source that way). Guest artifacts re-verified byte-identical.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The stat tier's producer disjunct exists for one phenomenon: two
producers stamping the SAME row language (a builtin and a first-party
package). A recorded producer whose component does not stamp the row's
language is not an instance of it -- no writer produces that shape, and
it is exactly what bridge_isolation, capability_isolation and
influence_classification plant -- so it is read as uninformative and
the language comparison decides, as before. The snapshot for a project
with no package runs the identical pre-change statement.

Docs: README 'Languages' section, CLI reference, plugin trust
--first-party and reserved ids; the language-profile guide's reserved
plain ids paragraph. lang_literal_gate loses its now-stale template
exemption.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Three registries graded the new surface and went RED, correctly:

* bless_registry: COSI_BLESS_HOST_EPOCH registered, with the waiver that
  says why a fixture fingerprint whose bless already refuses a moved
  fingerprint without an epoch bump is not a corpus ratchet.
* generation_policy_registry: plugin_packages pins `host_epoch`, argued as
  a property of the binary that derived a key's rows, not of a generation.
* ignored_test_reachability: ruby_package_overhead is a measurement
  recorded in _prdoc/records, waived with the gates that do grade it.
* parallel_precompile's wrapper counts only `--precompile`; discovery now
  also runs the host once per package to warm its grammar cache.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
A package may declare [module_map] inputs = ["composer.json", ...] and
export module_map(input_ptr, input_len) -> i64 from its extractor.wasm: a
PURE function from the files it claims plus the declared manifest files to
per-file module paths.

- abi: module_map codec — the canonical input (CIMM, sorted files,
  manifests) and validate_module_map (ascending unique file indices within
  the input, non-empty control-free UTF-8 modules, string/decoded-byte
  budgets, exact drain). Compiles for wasm32 and runs on i686.
- worker: REQUEST_KIND_MODULE_MAP calls the guest's second export under the
  same fresh store, fuel, epoch and memory ceilings as extract (the two
  entry points share one invoke path). An older worker refuses the kind.
- supervisor: extract and module_map share one serve() (admission, spawn,
  outside-kill retry, health); interpret_module_map reuses the envelope.
- package: [module_map] declaration (plain file NAMES, 1..8, unique),
  hashed into extraction_identity only when present.
- indexer: module_map::build_input (bounded; withheld files COUNTED),
  per-(worker key, content hash) cache, PackageHost::module_map.
- guest-kit: module_map Input/Writer (no_std, depends on the SDK alone) and
  psr4::map, a complete PSR-4 hook with a minimal no-alloc JSON reader.

Proof: tests/psr4_vs_builtin.rs runs psr4::map natively through the
parent's validator over the pinned php-guzzle — 131 files mapped, 129
graded against the builtin PHP extractor's declared type, 0 disagreements.
module_map_hook.rs drives the plumbing through a real worker (WAT guest):
answer, validation refusal, cache hit by pointer, missing declaration,
missing export.

NOT WIRED: the resolver does not consume module maps; moving PHP (or
Python/Rust/TS) resolution onto them is a bind-for-bind parity change of
its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
- `project_overview.language_ownership` carries only what an agent can
  act on: `not_enabled` and owner rows (with the enabled list and the
  reading beside them), an owner switch, and on the one start that made
  it the enablement decision. `{}` is the reported steady state;
  `availability: unavailable` the unreported one. The same renderer
  serves `code-index://stats` (disclosure_derivation_registry G2).
- Paid for under overview_payload_budget_e2e's pinned content reserve by
  removing the one sentence of `activation::SEMANTICS` that described the
  block's own absence: it rode only inside the block, and the absent
  state already renders as `availability: unavailable` with its own
  sentence. Saturated content 4047 -> 4028 with the new block included.
- The withheld-grant census joins the producer only when a package has
  ever produced (reader_epoch_e2e: a never-activated index runs no
  generation predicate).
- Fixtures that share one project between two starts, or that assert a
  clean git tree, now carry an explicit `[languages]` decision or the
  enablement marker: the first-start pass is an event of whichever start
  meets an undecided project first (mcp_smoke daemon parity,
  link_payload_scaling_e2e, answer_provenance_e2e); the settle-barrier
  population gains language_ownership_e2e.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
m0072 moved CURRENT_VERSION to 72, and package_baseline records the schema
version it was measured under as a CONDITION: the run refused the blessed
states ('written under 71, this run is 72'). That baseline may not be
re-blessed by this lane, so the table is created on the first declaration
by profiles::ensure_table and every reader probes for it (absent = the
builtin answer, which is what such an index resolved under). A project
whose packages declare nothing never has the table and its schema is
unchanged.

generation_policy_registry could see neither a lazily-created table (its
source scan reads migrations, its live scan a freshly migrated database)
— the hole it exists to close. It now carries LAZY_OBJECTS: each entry's
creator is EXERCISED before sqlite_master is read, must spell CREATE TABLE
IF NOT EXISTS, and the object is registered like any other
(declared_profiles: PromotionRestamped by write_declared, with its
promotion window recorded in PROMOTION_DOES_NOT_RESTAMP_YET).

Corpus suites (ratchet, mutation, stage, cost, ruby_package_parity,
package_baseline) green with COSI_CORPUS_REQUIRE=1 and no baseline moved;
precision_gate 7/7, phantoms 0.

Also: the guest kit's docs say it depends on the SDK alone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The import axis still demanded that the wire-id normalisation fire on
every import row. Since reserved plain ids the package leg's imports
already carry the plain id, so the harness asserts that no import row
carries the wire id and every one carries the plain id -- the same
assertion the symbol projection makes. Measured over ruby-sinatra:
the resolved projection is identical, producers differ.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Operator decision on the Phase-1 heads-up: the first daemon or MCP start
must not write into the project root.

- An automatic start with no decision writes the detected languages to
  `.code-index/languages.toml`, inside the index state dir. The reply
  says what was enabled, where it was written (`written_to`) and how to
  change it (`change_with`).
- Only `code-index init` and `code-index languages enable|owner` write
  `.code-index.toml`.
- Precedence: `.code-index.toml`'s `[languages] enabled` always wins
  (`languages::effective`); the state file is read only while it has no
  `enabled` key. Every routing reader (package set, planned activation,
  reactivation witness, daemon disclosure, CLI) goes through it, and
  `language_ownership.enabled_source` says which is in force.
- The upgrade marker and the add-once upgrade rule stay and write to the
  state dir. Over an explicit config list a start writes nothing: the
  config wins, and an indexed language it omits is disclosed as
  `language_not_enabled` (the builtin keeps indexing it).
- Reverted the answer_provenance_e2e fixture change that papered over
  the root write; it passes unchanged.
- New e2e: a first start in a git-initialised fixture leaves every byte
  outside `.code-index/` identical and `git status --porcelain` empty.
- Docs: README "Languages" (who writes where, precedence), the
  language-profile guide.

tests/packages/ruby/plugin.toml's comment still describes wire-id rows;
it is packed into the package, so editing it moves the pinned digest,
and it is left for the next package version bump.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The first-party Ruby package's cold index sat at 1.52-1.83x the builtin's
CPU. Attributed per phase (ruby_package_overhead.rs, CPU of the whole
process tree) and cut in three places:

* EXTRACTOR .cwasm CACHE (`precompile_cached`): the `--precompile`
  subprocess (~80 ms) runs once per (package digest, plugin-host build)
  instead of every daemon start. Same verified-manifest scheme as the
  grammar cache; the verified module is COPIED into the private staging
  directory, so a worker still maps only what this process staged. The
  engine flags are covered by the build key: they are compiled into the
  host binary, which the key hashes, and the worker's own deserialize
  refuses a foreign configuration.
* ONE READ, TWO HASHES at discovery (`Store::get_classified_bytes`,
  `prepare_verified`): activation used to re-read the stored package and
  hash it twice more after the store had verified it — four sha256 passes
  over 2 MB. Discovery 62 -> 42 ms.
* THE POOL GROWS WITH DEMAND (`LaneGrowth::PRODUCTION`, base 4, +1 lane
  per 128 answered requests): every lane that takes a job forks its own
  worker (~60 ms CPU each in a contended cold start), and all twelve lanes
  took jobs on a 153-file tree. Measured index-pass CPU: 1.09x with one
  lane, 1.19x with four, 1.66x with twelve; wall best at four to eight.
  Explicit widths (`from_set_with_lanes`) stay eager.

Result, first-party package, ruby-sinatra, medians of 11, two runs:
cold CPU 1.17 / 1.26x, cold WALL 1.11 / 1.15x (was 1.59 / 1.34x before
these three, same method).

Tests, mutations RUN: extractor_cache.rs (verified branch disabled -> RED
"a second start ran the extractor compile again"; any-manifest trusted ->
RED "a tampered cache entry was staged"); lane_growth.rs (gate removed ->
RED 4 workers on a one-lane pool; close() removed -> RED, the drop hangs;
served_one() removed -> RED never grew; `active` never grows -> RED).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Every discovery now runs `collect_stale_caches` after it has warmed or
verified its own entries. Removed: a `<package digest>` directory whose
package is no longer INSTALLED (nothing can look it up again); an entry of
ANOTHER host build whose manifest was last used more than
STALE_CACHE_AFTER (7 days) ago; any entry with no manifest. A verified use
stamps the manifest's mtime, so age means "unused", not "old".

Not approval-keyed, deliberately: the caches serve every project on the
machine and an approval is one project's.

Test cache_gc.rs, mutations RUN: the uninstalled arm disabled -> RED; the
age threshold multiplied by 1000 -> RED; the call removed from discovery
-> RED ("an uninstalled package's cache survived").

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Re-recorded through its own switch (COSI_PACKAGE_BLESS=1 with a reason),
not edited. The verdict is `conditions_only` with an empty diff: every
recorded projection number is unchanged, and the only moved fields are the
schema condition (71 -> 73, m0072/m0073) and the bless bookkeeping
(reason, identities, superseded reasons).

ruby-package-cost.json was NOT re-recorded: its re-measure moved recorded
numbers (see the lane R report), so the file was restored byte-for-byte.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
# Conflicts:
#	crates/indexer/src/packages.rs
# Conflicts:
#	crates/indexer/src/approval.rs
#	crates/indexer/src/packages.rs
The integration made `get_classified_bytes` return
`anchor.first_party || is_first_party_key(key)`. Lane P's tests anchor
their fixture key `--first-party` so a package may claim the reserved id
`ruby`, and that flag then exempted lane R's THIRD-party legs from the
general influence pass: first_party_influence recorded a third-party
container builtin-equivalent, and package_cost_attribution's positive
control saw 0 general-pass executions.

Restore lane R's rule: the exemption is `Store::is_first_party_key`
(compiled-in FIRST_PARTY_ANCHORS, widened only by
`with_first_party_keys`). An operator's `--first-party` anchor keeps
meaning what lane P built it for - permission to claim a reserved id -
and never switches off the pass. Both meanings are now documented at
`is_first_party_key` and `TrustAnchor::first_party`.

MUTATION (RUN): put `anchor.first_party ||` back. RESULT: RED in both
first_party_influence ("a third-party container was recorded
builtin-equivalent: [1]") and package_cost_attribution (POSITIVE
CONTROL FAILED, 0 general-pass executions).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
host_extraction_epoch fired: Ruby's host-derived fingerprint moved
(05316132.. -> 3e6e49ac..) under epoch 1 with unchanged package digests.

CAUSE, VERIFIED: lane P stamps a first-party package's rows with the
reserved plain id (`langid::row_lang`, `de.h-dv.ruby/ruby` -> `ruby`).
With `row_lang` mutated back to the identity, the gate passes against
the epoch-1 record unchanged, i.e. that is the ONLY moved input. Row
diff of the two dumps: 137 of 142 rows differ, and after substituting
`de.h-dv.ruby/ruby` -> `ruby` in the old dump the diff is empty — every
change is the `lang` column.

This is the gate's designed path: rows on operators' disks were written
with the wire id, so the epoch moves and `reconcile_extraction_epoch`
re-derives them. Re-recorded with COSI_BLESS_HOST_EPOCH=1; before the
bless the epoch-only bump was RED ("HOST_EXTRACTION_EPOCH is 2 and the
record was taken at 1"). The re-extraction of files derived under epoch
1 is graded by
`a_package_file_derived_under_another_epoch_is_re_extracted_and_nothing_else`,
which rewinds the stamp to HOST_EXTRACTION_EPOCH - 1 (now 1) and to
NULL and requires exactly the three package files to re-parse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The ruby/active rung moved resolved 2949 -> 2966 (+17), edges +4,
tier1b_name_import +8, tier1b_same_directory +7, tier3_import_boost +2.

CAUSE, VERIFIED: lane P stamps first-party package rows with the plain id
`ruby` (`langid::row_lang`). With `row_lang` reverted to the wire id the
suite passes against the previous record unchanged, so that is the only
moved input.

BINDS, not deltas. The package leg's active refs were dumped under both
rules and joined on (path, line, col, kind, occurrence): 20446 refs on
each side, 17 changed binds, all NEWLY resolved, 0 lost, 0 retargeted.
All 17 sit in test/yajl_test.rb, the one file the ladder un-shadows back
to `.rb`: its rows are builtin-produced (`ruby`), and under the wire id
they could not see the package-produced symbols (`de.h-dv.ruby/ruby`).
They bind mock_app/assert_body (test/test_helper) and yajl
(lib/sinatra/base.rb:810). Every one of the 17 is EQUAL to the builtin
leg's bind for the same ref — same target, same resolved_by rule —
measured against a builtin index of the suite's own builtin staging
tree. Package-vs-builtin bind disagreements fell 47 -> 30 and the
package leg's resolved count now equals the builtin's (2966).

Re-recorded with COSI_PACKAGE_BLESS=1 and a reason naming this.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
`cargo fmt --all -- --check` reported two hunks in
crates/indexer/src/packages.rs (~1913 and ~2007), both from the merge's
hand-resolved conflicts. Formatting only; no token changed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The registry keys a bound's disclosure on its NAME, workspace-wide.
Phase 1 added six unregistered bounds (the gate stops at the first, so
the log named only MAX_MAPPINGS) and two NAME COLLISIONS: module_map.rs
declared `MAX_MANIFEST_BYTES` and `ModuleMapCache::MAX_ENTRIES`, the
same names as code_index_package's refusing container bounds. The field
check resolves a name to the first file the scan reaches (indexer before
package), so the package rows' `reason + limit + limit_value + observed`
was being graded against module_map.rs, where no such field exists — and
a withholding bound was silently borrowing a refusing bound's disclosure.

Renamed the indexer pair (MAX_LAYOUT_MANIFEST_BYTES, MAX_CACHED_ANSWERS),
with a doc saying why a bound's name must be its own, and registered:
- MAX_GRAMMARS: Field(reason + limit + limit_value + observed) —
  validate_lanes refuses through Refusal::bound.
- MAX_MODULE_MAP_INPUTS: Field(reason + witness) — refuses with
  manifest.invalid_value and a witness naming the count and range, not
  through Refusal::bound.
- MAX_FILES, MAX_MANIFESTS, MAX_LAYOUT_MANIFEST_BYTES: Field(withheld) —
  build_input withholds and counts in BuiltInput::withheld.
- MAX_CACHED_ANSWERS: NotAgentVisible(capacity) — the cache clears, a
  miss re-asks a pure hook, no answer changes.
- MAX_MAPPINGS, MAX_TEXT (guest-kit psr4): NotAgentVisible(capacity) —
  fixed allocation-free buffers; a mapping past them is left unnamed,
  never named wrongly, and NO production path consumes a module-map
  answer yet (PackageHost::module_map and build_input have test callers
  only). The rows say they must become fields when the resolver reads one.

MUTATIONS (RUN): MAX_MAPPINGS's row renamed away -> RED "MAX_MAPPINGS
... is NOT in the disclosure REGISTRY"; the three input rows claiming
`withheld_files` -> RED "declared nowhere in [indexer]"; module_map's
old name restored -> RED on both tests (stale MAX_LAYOUT_MANIFEST_BYTES
row, and MAX_MANIFEST_BYTES's `limit_value` declared nowhere in indexer).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
the_fixture_set_covers_rubys_whole_fact_surface failed with "no ref
carries the `lexical_local` role". NOT a production regression:

- crates/plugins/src is byte-identical between 0bcdac1 and 0397b0c, and
  so are the checked-in Ruby .expected files, which render each ref's
  roles through ROLE_NAMES; ruby_expectations_are_byte_identical_to_the_
  generator passes at 0397b0c, so the live builtin's refs (roles included)
  are exactly what 0bcdac1 produced. The role census is identical on both
  sides: data_member_only 10, read 5, supertype 5, type_position 5,
  write 5.
- What moved is the VOCABULARY: lane Q (#112 phase 1) named the two
  REQUESTABLE bits, lexical_local and pytest_fixture, in ROLE_NAMES. The
  Ruby builtin never set either (Ruby's locals are the host's
  LocalDecision), and `project` projects under GRANTABLE_ROLES and panics
  on any builtin bit outside it.

So the check now iterates the names of the grant the set is projected
under, and asserts that filter keeps one name per granted bit.

MUTATIONS (RUN): the old unfiltered check -> RED "no ref carries the
`lexical_local` role"; the filter inverted to the requestable bits -> RED
"filtered down to [lexical_local, pytest_fixture]"; the Ruby builtin's
tp_roles ORs LEXICAL_LOCAL -> RED in `project` ("carries role bits
outside GRANTABLE_ROLES"), which is what makes a real regression of this
kind impossible to pass here.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
exec_copy_registry::a_file_that_installs_an_executable_locks_its_spawns_too
named crates/indexer/tests/corpus/mod.rs:278 and :306: `first_deleted_file`
and `head_sha` forked `git` with a bare `.output()` in a module that is
compiled into a test target that installs an executable. Both now go
through `code_index_test_support::exec::locked_output`, the same lock the
writer takes.

MUTATION (RUN): `head_sha` back to a bare `.output()` -> RED, naming
crates/indexer/tests/corpus/mod.rs:307.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
The band was refused on a CONDITION: measured under schema 71, binary at
73 (m0072, m0073). Re-measured, not edited, through the suite's bless
(COSI_RUBY_PKG_COST_BLESS=1, reason 'Phase 1: re-measured under schema
73').

NUMBERS (release, ruby-sinatra, 153 files):
                    record(s71)  0bcdac1    this tree (4 runs)
  package vm_step   30,656,396   30,093,721 30,083,210..30,092,091
  package fullscan     630,321      630,864    625,672..626,467
  sort / autoindex    396/22833    396/22833  396/22833
  builtin vm_step   17,053,958   17,090,476 17,068,543..17,072,396
  builtin fullscan         —        228,796    222,859
  wall ratio              144          141    141 144 142 134(written)

Every dimension is inside the old band (+5% / -12%). ATTRIBUTED:
- vm_step -1.9% against the record is NOT Phase 1: an isolated release
  run of 0bcdac1 (Phase 0 tip) already reads 30.09M. Phase 1 moves it by
  ~-0.02%, and reverting lane P's plain-id stamp moves it by nothing
  (30,092,091). The drift happened within band over Phase 0's 84 commits,
  where this gate passed it.
- fullscan -0.8% IS Phase 1, and it is shared: the builtin leg dropped by
  the same ~5.9k (228,796 -> 222,859), so it is a common-path saving, not
  package-path work.
- The first-party exemption does NOT apply here: this leg's key is not
  first party, and it pays the influence pass (package_cost_attribution:
  4 general-pass executions).
Load 3.6-6.6 through the runs; one full gate run was in progress in the
main checkout (not isolated).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
overview_payload_budget_e2e's saturated content read 4053 of 4050.

WHERE IT GREW, MEASURED: the saturated overview was dumped at 0bcdac1
(4047) and at this tree, and the bodies diffed. The only content
difference besides the build id, snapshot key and a top_referenced
reordering is lane P's `language_ownership.enablement` block, plus the
sentence lane P removed from activation::SEMANTICS to pay for it. Lane P
measured 4028 in 90e81d1; its later a8f77f1 then added `written_to` and
`change_with` to that block (automatic starts write .code-index/, and
the reply says where and how to change it) without re-measuring. Not
lanes Q or R.

Paid in activation::SEMANTICS, the site lane P already paid from:
"held in the plugin supervisor's health map and deliberately not
persisted" -> "held in memory and deliberately not persisted". The
supervisor's health map is an internal structure no agent can query;
the contrast with the on-disk, restart-surviving quarantine is what the
field needs, and it is intact. No verdict, field or disclosure changed.
The ceiling is unchanged: content 4053 -> 4046 (4 left); constant prose
1225 of 1500.

MUTATION (RUN): the tree before this edit is the mutation — the same
test RED at 4053 of 4050.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
ci: run the guest-kit corpus differentials under the require floor
Some checks failed
CI / cargo fmt (pull_request) Successful in 50s
CI / OSS corpus tier-3 scale (nightly) (pull_request) Has been skipped
CI / Grammar rebuild from source (nightly) (pull_request) Has been skipped
CI / CI lane wall-clock headroom (pull_request) Successful in 1m26s
CI / guest crates (fmt, clippy, doc) (pull_request) Failing after 1m40s
CI / cargo doc (intra-doc links) (pull_request) Successful in 5m40s
CI / cargo test (pull_request) Has been cancelled
CI / cargo test (daemon transport) (pull_request) Has been cancelled
CI / Plugin path cost + pool throughput (nightly) (pull_request) Has been cancelled
CI / cargo clippy (pull_request) Has been cancelled
CI / OSS corpus (tier 1) (pull_request) Has been cancelled
CI / cargo check (MSRV 1.98) (pull_request) Has been cancelled
CI / cargo check (windows-gnu) (pull_request) Has been cancelled
CI / cargo test (abi, 32-bit + wasm32) (pull_request) Has been cancelled
CI / cargo deny (pull_request) Has been cancelled
CI (Windows) / fmt + clippy + build + test (windows) (pull_request) Has been cancelled
25a18c301f
corpus_require_floor named two corpus-consuming binaries that no job
setting COSI_CORPUS_REQUIRE runs: guest-kit's `psr4_vs_builtin`
(php-guzzle) and `ruby_native_differential` (ruby-sinatra). In the plain
`test` job the corpus dir is unset, so their corpus halves never ran.

Both draw tier-1 repos, so they join the `corpus` job as their own step
— a separate `cargo test -p code-index-guest-kit`, since a `--test` name
must exist in the package it is passed with — guarded
`if: success() || failure()` like the other later grading steps.
Measured locally with the corpus and the floor: 0.80 s (131 files
mapped, 129 graded, 0 disagreements) and 0.91 s (152 files, 20065 refs),
--release, on a loaded box.

MUTATIONS (RUN): drop `--test ruby_native_differential` -> RED in
corpus_require_floor naming it; drop the step's `if:` -> RED in
ci_cadence::no_grading_step_is_masked_by_an_earlier_one.

NOT VERIFIED BY DISPATCH: this branch is not pushed, so the step has run
locally and not in CI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
guest/ruby: rustfmt two blank lines left by the guest-kit move
Some checks failed
CI / cargo fmt (pull_request) Successful in 50s
CI / OSS corpus tier-3 scale (nightly) (pull_request) Has been skipped
CI / Grammar rebuild from source (nightly) (pull_request) Has been skipped
CI / guest crates (fmt, clippy, doc) (pull_request) Successful in 1m28s
CI / CI lane wall-clock headroom (pull_request) Successful in 1m37s
CI / cargo doc (intra-doc links) (pull_request) Successful in 4m52s
CI / cargo deny (pull_request) Successful in 5m49s
CI / cargo test (abi, 32-bit + wasm32) (pull_request) Successful in 6m45s
CI / cargo check (MSRV 1.98) (pull_request) Successful in 7m1s
CI / cargo clippy (pull_request) Successful in 7m5s
CI / cargo check (windows-gnu) (pull_request) Successful in 7m8s
CI / OSS corpus (tier 1) (pull_request) Successful in 34m20s
CI / cargo test (pull_request) Successful in 36m54s
CI / cargo test (daemon transport) (pull_request) Successful in 10m25s
CI / Plugin path cost + pool throughput (nightly) (pull_request) Has been skipped
CI (Windows) / fmt + clippy + build + test (windows) (pull_request) Failing after 58m40s
ffb91adc3e
The guest crates live outside the workspace, so `cargo fmt --all` never
saw them; CI's guest-crates job (guest_gates.sh) did. Rebuilding with
build.sh reproduces extractor.wasm byte-for-byte (sha256 c3bf4717…),
so the package digest does not move.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
psr4_vs_builtin: hand the hook /-separated paths, as the host does
All checks were successful
CI / Grammar rebuild from source (nightly) (pull_request) Has been skipped
CI / CI lane wall-clock headroom (pull_request) Successful in 1m27s
CI / guest crates (fmt, clippy, doc) (pull_request) Successful in 1m28s
CI / cargo doc (intra-doc links) (pull_request) Successful in 5m10s
CI / cargo deny (pull_request) Successful in 5m50s
CI / cargo check (MSRV 1.98) (pull_request) Successful in 6m28s
CI / cargo test (abi, 32-bit + wasm32) (pull_request) Successful in 6m30s
CI / cargo clippy (pull_request) Successful in 6m32s
CI / cargo check (windows-gnu) (pull_request) Successful in 6m55s
CI / OSS corpus (tier 1) (pull_request) Successful in 30m41s
CI / cargo test (pull_request) Successful in 33m51s
CI / cargo test (daemon transport) (pull_request) Successful in 10m4s
CI / Plugin path cost + pool throughput (nightly) (pull_request) Has been skipped
CI (Windows) / fmt + clippy + build + test (windows) (pull_request) Successful in 1h6m7s
CI / cargo fmt (push) Successful in 50s
CI / OSS corpus tier-3 scale (nightly) (push) Has been skipped
CI / Grammar rebuild from source (nightly) (push) Has been skipped
CI / guest crates (fmt, clippy, doc) (push) Successful in 1m22s
CI / CI lane wall-clock headroom (push) Successful in 1m31s
CI / cargo doc (intra-doc links) (push) Successful in 5m6s
CI / cargo deny (push) Successful in 6m32s
CI / cargo test (abi, 32-bit + wasm32) (push) Successful in 6m33s
CI / cargo check (MSRV 1.98) (push) Successful in 6m39s
CI / cargo clippy (push) Successful in 6m49s
CI / cargo check (windows-gnu) (push) Successful in 6m59s
CI / OSS corpus (tier 1) (push) Successful in 33m8s
CI / cargo test (push) Successful in 53m40s
CI (Windows) / fmt + clippy + build + test (windows) (push) Successful in 1h11m8s
CI / cargo test (daemon transport) (push) Successful in 24m14s
CI / Plugin path cost + pool throughput (nightly) (push) Has been skipped
4ecf930e05
Native Windows CI mapped 0 of 4 fixture files: the test's own walk used
Path::display(), giving src\Client.php, which no src/ mapping matches.
Production is unaffected — module_map::build_input receives the host's
project-relative paths, which are /-separated. Linux cannot exercise
the difference; the Windows job is this change's check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
Author
Member

Merged: master was fast-forwarded to 4ecf930 after CI on that exact commit passed on Linux (run 5648) and native Windows (run 5647). The two fixes after the PR opened:

  • ffb91ad: rustfmt on the Ruby guest crate. The rebuilt extractor.wasm is byte-identical, so the package digest did not move.
  • 4ecf930: psr4_vs_builtin passes /-separated paths to the hook, as the host does. Production was unaffected.

I'm closing this PR by hand because Forgejo does not detect a fast-forward merge.

Merged: master was fast-forwarded to 4ecf930 after CI on that exact commit passed on Linux (run 5648) and native Windows (run 5647). The two fixes after the PR opened: - `ffb91ad`: rustfmt on the Ruby guest crate. The rebuilt `extractor.wasm` is byte-identical, so the package digest did not move. - `4ecf930`: `psr4_vs_builtin` passes `/`-separated paths to the hook, as the host does. Production was unaffected. I'm closing this PR by hand because Forgejo does not detect a fast-forward merge.
buildagent closed this pull request 2026-09-27 14:52:33 +02:00
All checks were successful
CI / cargo fmt (pull_request) Successful in 50s
CI / OSS corpus tier-3 scale (nightly) (pull_request) Has been skipped
CI / Grammar rebuild from source (nightly) (pull_request) Has been skipped
CI / CI lane wall-clock headroom (pull_request) Successful in 1m27s
CI / guest crates (fmt, clippy, doc) (pull_request) Successful in 1m28s
CI / cargo doc (intra-doc links) (pull_request) Successful in 5m10s
CI / cargo deny (pull_request) Successful in 5m50s
CI / cargo check (MSRV 1.98) (pull_request) Successful in 6m28s
CI / cargo test (abi, 32-bit + wasm32) (pull_request) Successful in 6m30s
CI / cargo clippy (pull_request) Successful in 6m32s
CI / cargo check (windows-gnu) (pull_request) Successful in 6m55s
CI / OSS corpus (tier 1) (pull_request) Successful in 30m41s
CI / cargo test (pull_request) Successful in 33m51s
CI / cargo test (daemon transport) (pull_request) Successful in 10m4s
CI / Plugin path cost + pool throughput (nightly) (pull_request) Has been skipped
CI (Windows) / fmt + clippy + build + test (windows) (pull_request) Successful in 1h6m7s
CI / cargo fmt (push) Successful in 50s
CI / OSS corpus tier-3 scale (nightly) (push) Has been skipped
CI / Grammar rebuild from source (nightly) (push) Has been skipped
CI / guest crates (fmt, clippy, doc) (push) Successful in 1m22s
CI / CI lane wall-clock headroom (push) Successful in 1m31s
CI / cargo doc (intra-doc links) (push) Successful in 5m6s
CI / cargo deny (push) Successful in 6m32s
CI / cargo test (abi, 32-bit + wasm32) (push) Successful in 6m33s
CI / cargo check (MSRV 1.98) (push) Successful in 6m39s
CI / cargo clippy (push) Successful in 6m49s
CI / cargo check (windows-gnu) (push) Successful in 6m59s
CI / OSS corpus (tier 1) (push) Successful in 33m8s
CI / cargo test (push) Successful in 53m40s
CI (Windows) / fmt + clippy + build + test (windows) (push) Successful in 1h11m8s
CI / cargo test (daemon transport) (push) Successful in 24m14s
CI / Plugin path cost + pool throughput (nightly) (push) Has been skipped

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
h-dv/code-index!308
No description provided.