Phase 0 of pluggable languages: route language rules through profiles, measure costs, add gates #306
No reviewers
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
h-dv/code-index!306
Loading…
Reference in a new issue
No description provided.
Delete branch "p0-integration"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 0 prepares the move of all languages into plugin packages: it measures the costs, adds the gates and makes rules follow the language profile. It does not move any language yet. Built-in results are unchanged: a bind-by-bind comparison over all nine corpus repositories found 0 differences among 1,140,041 refs.
Runtime and measurement (lane M)
oom_score_adj=1000, so the Linux OOM killer picks a worker before the daemon._prdoc/records/P0-real-grammar-costs.md). Ruby takes about 175 ms to start cold and uses 35 MB for the first worker.Language rules follow the profile (lane N)
lang_profile. There were 57 literals at 41 sites; there are now 0, andlang_literal_gateenforces that with no allowlist.temp.lang_profiletable built in Rust.langfilter given as a profile name (lang: "ruby") also matches packaged languages that use that profile.Gates and release infrastructure (lane O)
wchar.hshim PHP needs.package_paritygate driven by aLangSpec, with two new axes.COSI_CORPUS_LANGSruns the corpus ratchets for selected languages only.Integration fixes
<pkg>/csharplanguage enter the builtin C# partial-class and reclassification rules. It had no language key and no capability gate, so a packagedWidgetcould merge with a builtinWidget.lang, and the partial-class join requires the same language on both sides.member_candidateortype_position_candidate.parallel_precompiletest and the package fixture's host build now take the fork lock.plugin_activation_cli's bench no longer inheritsCOSI_CORPUS_DIR. The inherited variable made it reconcile every corpus checkout and read another root's log line.ruby_package_local_decisionsdeclarespackage_parity, which the fixture now imports.Validation
agent_task_benchpassed.pf.lang = c.langis removed.pc_callspicks the enclosing class from ungated symbols. This would only matter with two producers in one file, which no shipped path creates yet.🤖 Generated with Claude Code
https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu
tests/grammars/build-tree-sitter.sh is the recipe the three per-grammar scripts each claimed to be a copy of. It takes every value as a required flag (crate, version, crate sha256, source subdir, export symbol, wasm sha256, output name); a default would be one grammar's value applied to another. `--wasm-sha256 -` builds an UNPINNED grammar, prints its digest and exits 3, never 0. build-tree-sitter-{xml,ruby,svelte}.sh keep their provenance blocks and their plain VERSION=/CRATE_SHA256=/WASM_SHA256= assignments, because grammar_provenance.rs and ruby_kind_table.rs read those lines, and then exec the recipe. shim/wchar.h: tree-sitter-php 0.24.2's common/scanner.h includes it and calls nothing from it, so it declares types only (wint_t, and wchar_t via stddef.h) - no function a WasmStore could refuse to import. Verified: * ruby, xml, svelte rebuilt through the wrappers: all three byte-identical (ruby c54cc209...e3d8, the checked-in sha). * php 0.24.2 (php/src, tree_sitter_php) builds unpinned: 71c92662...ce51, 15 imports, every function among them (calloc/free/malloc/realloc, memcpy/memcmp, iswspace/iswxdigit/ iswdigit/iswalnum) already in the svelte/ruby import sets. * grammar_provenance 7/7, ruby_kind_table 6/6. Mutations (run, restored, md5-verified): * remove shim/wchar.h -> php build RED: 'wchar.h' file not found. * xml wrapper --src-subdir xml/src -> dtd/src -> RED, digest mismatch. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmucrates/plugin-host/tests/real_grammar_costs.rs (#[ignore], linux) packs tests/packages/{ruby,svelte,timeline,xaml} and the JSON fixture and reports extractor precompile, grammar JIT wall/CPU, cold start (+worker CPU), warm trip, RSS/Pss and marginal Pss. Ruby: ~175 ms cold (~135 ms of it the grammar JIT), ~35 MiB Pss, ~28 MiB marginal - 14x the JSON fixture's 2 MiB. Recorded in _prdoc/records/P0-real-grammar-costs.md. packages.rs docs (75 us / 18.4 ms / 2-3 MiB) now state they are the JSON fixture grammar's and give the Ruby figures, including that the pool's IDLE_WORKER_MARGINAL_PSS_CEILING_KIB understates a Ruby worker ~3.4x. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmuaxis-B W2. `ruby_package_parity.rs`'s mechanism moves to `crates/indexer/tests/package_parity/`, parameterized by `LangSpec` (builtin lang, package id/lang/dir, shadow extension, qualified-name separator, grant) and per-repo `RepoParity` (floors, pinned resolved count, staged/qname floors). `RUBY` carries the numbers the Ruby file pinned, unchanged: 1260 / 20446 / 231 / 2700 floors, 2966 on both legs. `ruby_package_parity.rs` keeps its history and its test name and now calls `package_parity::run(&RUBY)`; `ruby_parity_fixture` is a facade over the same recipe so its three other callers did not change. Assertions are the old ones in the old order. Added: * imports ROW FOR ROW (path, module, alias, bound_name, lang, start_line) - the old gate had only a count(*) floor per leg; * `corpus::project_symbol_detail`: signature and doc hashes, attr_start_line, is_extension - columns SYM_SQL omits (it must, for cross-binary comparisons). Signature is required populated on the builtin leg (1170/1260); doc and attr_start_line are COUNTED and printed, and for Ruby both are 0 - so those two columns are graded only as "both legs leave it NULL", and the control line says so. `[[displaces]]` was NOT adopted: using it would mean packing a rewritten plugin.toml (not the shipped digest) and would re-admit Gemfile (moving FLOOR_REFS and the pinned resolved count). Reasons in the module doc. Ruby parity: GREEN, executed=1, controls=5, 3.4 s. Mutations (run; guest rebuilt with build.sh, whose unmodified output was first confirmed byte-identical; restored by cp, md5-verified): * guest import encoder alias Some("zz") on each file's first import: RED at the import rows, "231 builtin rows, 231 package rows" - the old count floor and the resolution equality both held. * signature_until_body one byte short: RED at symbol detail. * compare returns 0: RED, "examined 0 row pairs against 20446". Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmuaxis-C W1. `COSI_CORPUS_LANGS=ruby,php` (corpus.toml `lang` spelling) narrows corpus_ratchet, corpus_cost, corpus_stage, corpus_tier3_ratchet and package_parity to those languages' repos, through ONE door: `corpus::Coverage::select`. `corpus::tier` stays unfiltered. Why it cannot weaken a full run: * unset/empty/whitespace = no filter; `select` is then the identity; * a value naming nothing (unknown lang, only commas) PANICS at `Coverage::new`, naming it and the known languages (#259's shape); * deselected repos are recorded in the coverage manifest (`lang_filter`, `deselected`), never counted executed or unavailable, and every filtered run prints `FILTERED RUN ... This is NOT a full corpus run.`; * a selected repo that is unavailable still fails under REQUIRE; only "the filter selected none of this suite's repos" is excused, and that state is unreachable without a filter; * corpus_lang_filter.rs fails if ci.yml's `corpus` or `corpus-scale` job ever sets the variable. A filtered bless is PARTIAL by construction: every ratchet writer already merges (per-repo replace), and the filter note now goes into the committed control roster, naming the rows kept unmeasured. Baselines are NOT split. baseline.json must not move (CLAUDE.md), and per-repo rows already make a per-language bless touch only its rows; the one shared line two concurrent per-language blesses would conflict on is the `_blessed` block. Splitting that is a decision about bless history, not a cheap mechanical change. ci.yml: a comment block documents the per-language job shape (its own workflow with `paths:` or an `if:` over changed files - both unmeasured on this Forgejo), and the three rules. No job, step or env line changed. Tests: corpus_lang_filter 6/6, corpus_require_floor 11/11, ci_cadence 9/9. End to end: COSI_CORPUS_LANGS=ruby corpus_ratchet GREEN executed=1 with the six others named as deselected; =rubyy RED naming it; COSI_CORPUS_LANGS=ruby corpus_tier3_ratchet GREEN, "selected none". Mutations (run, restored, md5-verified): * drop the unknown-language check -> RED a_filter_that_names_nothing * select_by_lang drops the deselected half -> RED ...loses_nothing * empty value parses as Some(empty) -> RED no_value_is_no_filter * COSI_CORPUS_LANGS: "ruby" in the corpus job env -> RED, naming it Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmu`a_no_daemon_mcp_startup_preserves_the_active_generations_package_record` failed in the full gate run with "the startup reconcile stat-skipped everything", and the failure's own stderr says why: the `single-process reconcile complete` line it stopped on read `files_seen: 206, parse_count: 0`, beside `index_path{root=/home/master/.cache/cosi-corpus/js-express}`. The gate run sets COSI_CORPUS_DIR for its corpus suites. The spawned `code-index-mcp` inherited it and (#191) linked every pinned corpus checkout, and `--no-daemon` reconciles each linked root, each emitting its own `complete` line that names no root. The helper stopped at whichever finished first. NOT Phase 0 and not W7: neither file changed since v0.32.2, where both the test and `corpus_links` already existed. Reproduced with COSI_CORPUS_DIR set: 2 of 3 runs RED, including the sibling `a_rollback_across_an_mcp_startup_…` ("did not run"). The helper now removes COSI_CORPUS_DIR and COSI_WORKTREES_DIR (#238, the same shape) from the server, and refuses any `index_path` line naming a root other than the bench, so a future leak fails by name instead of grading another project's stats. Nothing the tests assert is weakened. With COSI_CORPUS_DIR set: 5/5 green. MUTATION (RUN): drop both `env_remove`s, COSI_CORPUS_DIR set, 10 runs. RESULT: RED in 7 of 10 ("reconciled a root other than this bench"); the 3 survivors are runs whose own reconcile reported first — the race the leak is, which is why the precondition is removed rather than detected. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0126PDDLB4wNHxKXvWM1VNmuMerged: master was fast-forwarded to
0bcdac1after CI on that exact commit passed on Linux (run 5634) and native Windows (run 5633). The Windows failure from the first run is fixed in0bcdac1: the two new isolation tests now holdRESOLVE_HOOK_LOCK. I'm closing this PR by hand because Forgejo does not detect a fast-forward merge.Pull request closed