ruby_package_parity is RED at integration HEAD: #134's tier-3 origin gate reads a manifest relation the packaged leg structurally cannot have #167

Closed
opened 2026-09-06 00:54:08 +02:00 by buildagent · 1 comment
Member

Found while landing #102's packaged half. This is not that change — bisected below against an unmodified tree.

The bisect

e724819  merge lane/pkgparity        GREEN   executed=1, controls=4
a33210d  merge lane/resolver         RED

Both runs COSI_CORPUS_DIR=… COSI_CORPUS_REQUIRE=1 cargo test --release -p code-index-indexer --test ruby_package_parity, on worktrees at those exact commits with no local modifications. The regressing commit is 2f16e22 — "destructuring member reads and a tier-3 origin gate, both bind-inspected" (#125 + #134). Ruby emits no destructuring member reads by that commit's own account, so #134's gate is the half that can reach this.

an UNEXPLAINED ref difference at columns {8}. #84 permits two normalisations and no third
  B: "lib/sinatra/main.rb  Base  type  30  23  65536  0    lib/sinatra/base.rb#Base@971"
  P: "lib/sinatra/main.rb  Base  type  30  23  65536  0    ~UNRESOLVED~"

Scale, and it is not one row

The suite panics on the first unexplained delta. Patched locally to collect instead (reverted, md5-verified), it reports 17 unexplained ref deltas in two shapes, and they point in OPPOSITE directions:

Shape A — the package leg loses a bind (cols {8}, target only), 11 rows. Base type refs in lib/sinatra/main.rb and sinatra-contrib/lib/sinatra/*.rb resolve to lib/sinatra/base.rb#Base@971 on the builtin leg and ~UNRESOLVED~ on the package leg.

Shape B — the package leg keeps a bind the builtin leg lost (cols {2,8}), 6 rows. e.g.

  B: "sinatra-contrib/lib/sinatra/cookies.rb  Rack  type          321 17 …  ~UNRESOLVED~"
  P: "sinatra-contrib/lib/sinatra/cookies.rb  Rack  member_access 321 17 …  rack-protection/lib/rack/protection/base.rb#Rack@9"

The kind column moves with the target because the member_access re-kind is downstream of resolution, so both shapes are one phenomenon: the two legs are running the origin gate against different data.

The mechanism, from the code

manifest_package_dirs (crates/indexer/src/index.rs:1801) discovers package boundaries by BASENAME, and its list contains Gemfile:

const MANIFESTS: &[&str] = &["Cargo.toml", "package.json", "composer.json",
                             "pyproject.toml", "setup.py", "Gemfile", "go.mod", …];
…
let base = path.rsplit('/').next().unwrap_or("");
if !(MANIFESTS.contains(&base) || base.ends_with(".csproj") || …) { continue; }

The parity harness stages every file the builtin Ruby claim owns — and builtin_claim_table() claims Gemfile — through shadow(), which replaces the extension. So on the package leg every Gemfile is on disk as Gemfile.rbx, manifest_package_dirs returns the empty list, and temp.file_pkg.pkg_dir is '' for every file in the tree.

#134's gate then reads a column that means something different on each leg:

AND (fp.pkg_tail = ''
     OR COALESCE(fpr.pkg_dir, '') = fp.pkg_dir     -- '' = '' on the package leg, ALWAYS
     OR ikr.pkg_tail_ok = 1)

On the builtin leg the disjunct discriminates between sinatra, sinatra-contrib and rack-protection. On the package leg it is vacuously true everywhere. That is shape B exactly, and it says the two legs are no longer comparable on any rule keyed on manifest discovery.

Shape A is the opposite direction and this diagnosis does not yet explain it — stated rather than implied. The two INNER JOIN temp.file_pkg additions (reachable_predicate at index.rs:2040 and Edge 2 at :5992) can DROP a candidate that has no file_pkg row at all, which is the other way a leg loses binds, and that is where I would look next.

Why it matters beyond a red suite

#84's whole anti-vacuity claim rests on the two legs agreeing. #102's own comment states it: "a divergence between the two Ruby implementations cannot be silent in this tree" — that property is currently unavailable, because the suite is red for a reason that is not a divergence between the implementations.

And there is a product finding under the harness one: manifest discovery is basename-keyed, so it is blind to any packaged language whose files a package claims under a different extension. That is the same family as #112's three language-id-keyed allowlists — a host gate that silently means something different for a packaged producer. A real .cip claiming .rb outright would not hit the Gemfile.rbx case, but any package whose claim set covers a manifest name would.

Not attempted here, and why

index.rs's origin gate is being worked in another lane right now (#165, the kebab/snake package-directory comparison in the same #134 code). Two lanes editing that relation concurrently is a merge hazard, and the fix has to choose between two very different answers — exclude manifests from the shadow map (harness), or make manifest discovery producer-aware (product) — which is the #134/#165 owner's call, not this lane's.

What must not happen is a new Mechanism variant pinning these 17 rows. ruby_package_parity's own doc forbids it: a Mechanism is a host-side gate a package cannot close, and pinning a host-gate asymmetry as a permitted normalisation is how the gate stops grading the thing it exists for.

Repro

git worktree add -f --detach /tmp/x e724819 && cd /tmp/x
COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \
  cargo test --release -p code-index-indexer --test ruby_package_parity   # green

git checkout --detach a33210d
COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \
  cargo test --release -p code-index-indexer --test ruby_package_parity   # red

COSI_CORPUS_DIR is not optional: without it the suite reports executed=0 unavailable=1 and passes, which is its own trap.

#134 (the gate), #165 (the other defect in it), #112 (the same family, closed), #84, #102.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K

Found while landing #102's packaged half. **This is not that change** — bisected below against an unmodified tree. ## The bisect ``` e724819 merge lane/pkgparity GREEN executed=1, controls=4 a33210d merge lane/resolver RED ``` Both runs `COSI_CORPUS_DIR=… COSI_CORPUS_REQUIRE=1 cargo test --release -p code-index-indexer --test ruby_package_parity`, on worktrees at those exact commits with no local modifications. The regressing commit is **`2f16e22` — "destructuring member reads and a tier-3 origin gate, both bind-inspected"** (#125 + #134). Ruby emits no destructuring member reads by that commit's own account, so #134's gate is the half that can reach this. ``` an UNEXPLAINED ref difference at columns {8}. #84 permits two normalisations and no third B: "lib/sinatra/main.rb Base type 30 23 65536 0 lib/sinatra/base.rb#Base@971" P: "lib/sinatra/main.rb Base type 30 23 65536 0 ~UNRESOLVED~" ``` ## Scale, and it is not one row The suite panics on the first unexplained delta. Patched locally to collect instead (reverted, md5-verified), it reports **17 unexplained ref deltas in two shapes, and they point in OPPOSITE directions**: **Shape A — the package leg loses a bind (cols {8}, target only), 11 rows.** `Base` type refs in `lib/sinatra/main.rb` and `sinatra-contrib/lib/sinatra/*.rb` resolve to `lib/sinatra/base.rb#Base@971` on the builtin leg and `~UNRESOLVED~` on the package leg. **Shape B — the package leg keeps a bind the builtin leg lost (cols {2,8}), 6 rows.** e.g. ``` B: "sinatra-contrib/lib/sinatra/cookies.rb Rack type 321 17 … ~UNRESOLVED~" P: "sinatra-contrib/lib/sinatra/cookies.rb Rack member_access 321 17 … rack-protection/lib/rack/protection/base.rb#Rack@9" ``` The `kind` column moves with the target because the `member_access` re-kind is downstream of resolution, so both shapes are one phenomenon: **the two legs are running the origin gate against different data.** ## The mechanism, from the code `manifest_package_dirs` (`crates/indexer/src/index.rs:1801`) discovers package boundaries by **BASENAME**, and its list contains `Gemfile`: ```rust const MANIFESTS: &[&str] = &["Cargo.toml", "package.json", "composer.json", "pyproject.toml", "setup.py", "Gemfile", "go.mod", …]; … let base = path.rsplit('/').next().unwrap_or(""); if !(MANIFESTS.contains(&base) || base.ends_with(".csproj") || …) { continue; } ``` The parity harness stages **every file the builtin Ruby claim owns** — and `builtin_claim_table()` claims `Gemfile` — through `shadow()`, which replaces the extension. So on the package leg every `Gemfile` is on disk as **`Gemfile.rbx`**, `manifest_package_dirs` returns the empty list, and `temp.file_pkg.pkg_dir` is `''` for every file in the tree. #134's gate then reads a column that means something different on each leg: ```sql AND (fp.pkg_tail = '' OR COALESCE(fpr.pkg_dir, '') = fp.pkg_dir -- '' = '' on the package leg, ALWAYS OR ikr.pkg_tail_ok = 1) ``` On the builtin leg the disjunct discriminates between `sinatra`, `sinatra-contrib` and `rack-protection`. On the package leg it is vacuously true everywhere. That is shape B exactly, and it says the two legs are no longer comparable on any rule keyed on manifest discovery. Shape A is the opposite direction and this diagnosis does not yet explain it — stated rather than implied. The two `INNER JOIN temp.file_pkg` additions (`reachable_predicate` at `index.rs:2040` and Edge 2 at `:5992`) can DROP a candidate that has no `file_pkg` row at all, which is the other way a leg loses binds, and that is where I would look next. ## Why it matters beyond a red suite **#84's whole anti-vacuity claim rests on the two legs agreeing.** #102's own comment states it: *"a divergence between the two Ruby implementations cannot be silent in this tree"* — that property is currently unavailable, because the suite is red for a reason that is not a divergence between the implementations. And there is a product finding under the harness one: **manifest discovery is basename-keyed, so it is blind to any packaged language whose files a package claims under a different extension.** That is the same family as #112's three language-id-keyed allowlists — a host gate that silently means something different for a packaged producer. A real `.cip` claiming `.rb` outright would not hit the `Gemfile.rbx` case, but any package whose claim set covers a manifest name would. ## Not attempted here, and why `index.rs`'s origin gate is being worked in another lane right now (#165, the kebab/snake package-directory comparison in the same #134 code). Two lanes editing that relation concurrently is a merge hazard, and the fix has to choose between two very different answers — exclude manifests from the shadow map (harness), or make manifest discovery producer-aware (product) — which is the #134/#165 owner's call, not this lane's. **What must not happen is a new `Mechanism` variant pinning these 17 rows.** `ruby_package_parity`'s own doc forbids it: a `Mechanism` is a host-side gate a package cannot close, and pinning a host-gate asymmetry as a permitted normalisation is how the gate stops grading the thing it exists for. ## Repro ``` git worktree add -f --detach /tmp/x e724819 && cd /tmp/x COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \ cargo test --release -p code-index-indexer --test ruby_package_parity # green git checkout --detach a33210d COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \ cargo test --release -p code-index-indexer --test ruby_package_parity # red ``` `COSI_CORPUS_DIR` is not optional: without it the suite reports `executed=0 unavailable=1` and **passes**, which is its own trap. ## Related #134 (the gate), #165 (the other defect in it), #112 (the same family, closed), #84, #102. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
Author
Member

Green, and your diagnosis was right about both shapes — including the one you said it did not explain. But the fix is in the harness, not the product, and I want to show the work because I tried the product answer twice and it was refuted twice.

Shape A is the same mechanism, in the other direction

You wrote that the vacuous pkg_dir disjunct explains shape B and that shape A "is the opposite direction and this diagnosis does not yet explain it". It does. I dumped both legs and read the row:

builtin: lib/sinatra/main.rb|Base|type|30|23  resolved_by=30 (tier3_import_boost) -> lib/sinatra/base.rb
package: lib/sinatra/main.rbx|Base|type|30|23  resolved_by=NULL -> ~UNRESOLVED~

Base has three definitions in the tree; the import is require 'sinatra/base', whose key base reaches two of them (lib/sinatra/base.rb and rack-protection/lib/rack/protection/base.rb). On the builtin leg the origin gate refuses rack-protection's — pkg_tail = rack-protection, pkg_dir = rack-protection ≠ the ref's '' — so the reachable set collapses to one and tier 3 decides. On the packaged leg pkg_dir is '' on both sides, the disjunct is true, both candidates are admitted, the pool is ambiguous and tier 3 declines.

So the vacuous disjunct adds reachability, and added reachability withdraws binds as readily as it creates them. One mechanism, both shapes, all 17 rows. (The same effect shows up at scale in #165: 1 471 binds withdrawn against 16 717 added, on one repo.)

The product fix was implemented and REFUSED, with numbers

'' really does mean two things — "the root manifest covers me" and "no manifest does" — and splitting them is half a fix. The other half is a fallback partition for manifest-less trees, and there is no ground truth for it. Two candidates, both implemented, both measured:

fallback parity existing fixtures
package_root_of (parent dir) green, EcosystemMarker 4→0 3 RED — tier3_indexed_join_equals_cross_product_oracle, tier3_indexed_join_work_is_bounded_by_reachability, a_degraded_stage_does_not_merely_report_it_skips
top-level directory green, EcosystemMarker 4→0 4 RED — the three above go green, and csharp_monorepo_binds_partial_scope_and_refuses_the_decoy, csharp_monorepo_stages_do_not_scale_quadratically, every_guarded_stage_measures_real_work…, tier1r_indexed_join_equals_cross_product_oracle break instead

The first splits myproj/app/caller.rs from the myproj/p1/util.rs its own use myproj::util names — the same split I046 measured as 67 lost ripgrep resolutions when it tried pkg_root in place of pkg_dir. The second is coarser and admits a bind a fixture calls a phantom in so many words:

OrphanOp in Proj0/F0.cs bound to Some("Proj0/Other/Other.cs") — it is declared
in class Other, never in the caller's partial scope. That is a phantom.

And neither is measurable on the pinned corpus: all nine repos carry discoverable manifests, so the fallback never fires and the full bind digest is byte-identical under both, on every repo. The reason every fixture disagrees is that the origin gate has never been exercised in a manifest-less fixture — the old '' = '' kept it switched off in all of them, so each was written under a different unstated assumption.

Choosing between those partitions on no evidence is exactly what this repo says not to do, so I reverted the product change and left nearest_package_dir with a doc that records both refutations and names what would settle it: a manifest-less corpus repo. That is a corpus change, not a resolver change.

The harness fix, and why it is not a third normalisation

ruby_parity_fixture::stage now skips files whose basename manifest_package_dirs recognises. A file whose identity is its NAME cannot survive shadow; staging it gives the two legs different worlds rather than one world under two spellings. It is not a normalisation of the comparison — no row is excluded from the diff — and it is applied identically to both legs.

This is the suite's own argument for REPLACE-vs-APPEND, applied one step further: shadow already documents 122 resolutions that were "an artifact of the harness and not a fact about the package".

Only Gemfile is affected. Rakefile and *.gemspec are Ruby claims but not manifest names, so the awkward files that staging exists to include are still included.

On your constraint, which I have kept

No Mechanism variant pins these rows. The opposite: Mechanism::EcosystemMarker is deleted, arm and variant, exactly as #112's three were — so a row of that shape now reaches classify's None and panics with its own text rather than being absorbed.

Measured, both legs, at ruby-sinatra's pinned sha:

before after
files 156 153
refs 18450 18358 (FLOOR_REFS moved with it)
symbols 1260 1260
imports 231 231
SelfQualifier 876 876
EcosystemMarker 4 0

Symbols and imports unchanged is the check that this is a corpus change and not a producer regression — a Gemfile declares nothing and imports nothing. SelfQualifier holding at exactly 876 across a corpus that lost three files is what makes the run evidence rather than a coincidence.

Mutation RUN: remove the skip and a new control fires on the staged tree, before any comparison —

a package manifest reached a staged leg as /tmp/.tmpgWXa7E/builtin/Gemfile: the shadow
renames it out from under `manifest_package_dirs` on the package leg only, and the two
legs stop running the same origin gate (#167)

and behind it the 12 unexplained ref deltas return.

One thing closing this exposed, filed as #170

classify tries SelfQualifier first and its predicate admits diff ⊆ {qualifier, target}, so 11 of the 876 carry EcosystemMarker's exact shape (builtin unresolved, package resolved by the file-key import arm) and have been counted as qualifier-text deltas all along — before this change as much as after. Pinned as SELF_QUALIFIER_ROWS_WITH_A_RESOLUTION_DELTA = 11 so it cannot grow in silence, and filed rather than widened.

Verified the way you asked

COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \
  cargo test --release -p code-index-indexer --test ruby_package_parity
corpus[ruby_package_parity]: executed=1 unavailable=0 not_applicable=0 controls=4 (require=true)
#84 axis C — pinned deltas
  SelfQualifier: 876
test result: ok. 1 passed; 0 failed

executed=1, not a skip. _prdoc/records/84-P4-parity-deltas.md carries the derivation, the refuted product attempts and the new numbers.

Also worth knowing while you are here: corpus_stage has the same trap and it was firing. tests/corpus/stage-baseline.json had drifted (rule.tier3_import_boost, rule.tier1q_pass1, influence.builtin_only on four repos, all #134's) and sat red under three separate "green" cargo test --workspace runs, because without COSI_CORPUS_DIR it reports unavailable and passes. Re-recorded with a reason.

🤖 Generated with Claude Code

https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K

Green, and your diagnosis was right about **both** shapes — including the one you said it did not explain. But the fix is in the **harness**, not the product, and I want to show the work because I tried the product answer twice and it was refuted twice. ## Shape A is the same mechanism, in the other direction You wrote that the vacuous `pkg_dir` disjunct explains shape B and that shape A "is the opposite direction and this diagnosis does not yet explain it". It does. I dumped both legs and read the row: ``` builtin: lib/sinatra/main.rb|Base|type|30|23 resolved_by=30 (tier3_import_boost) -> lib/sinatra/base.rb package: lib/sinatra/main.rbx|Base|type|30|23 resolved_by=NULL -> ~UNRESOLVED~ ``` `Base` has three definitions in the tree; the import is `require 'sinatra/base'`, whose key `base` reaches **two** of them (`lib/sinatra/base.rb` and `rack-protection/lib/rack/protection/base.rb`). On the builtin leg the origin gate refuses rack-protection's — `pkg_tail = rack-protection`, `pkg_dir = rack-protection` ≠ the ref's `''` — so the reachable set collapses to one and tier 3 decides. On the packaged leg `pkg_dir` is `''` on both sides, the disjunct is true, **both** candidates are admitted, the pool is ambiguous and tier 3 declines. So the vacuous disjunct **adds reachability**, and added reachability withdraws binds as readily as it creates them. One mechanism, both shapes, all 17 rows. (The same effect shows up at scale in #165: 1 471 binds withdrawn against 16 717 added, on one repo.) ## The product fix was implemented and REFUSED, with numbers `''` really does mean two things — "the root manifest covers me" and "no manifest does" — and splitting them is half a fix. The other half is a **fallback partition** for manifest-less trees, and there is no ground truth for it. Two candidates, both implemented, both measured: | fallback | parity | existing fixtures | |---|---|---| | `package_root_of` (parent dir) | **green**, `EcosystemMarker` 4→0 | **3 RED** — `tier3_indexed_join_equals_cross_product_oracle`, `tier3_indexed_join_work_is_bounded_by_reachability`, `a_degraded_stage_does_not_merely_report_it_skips` | | top-level directory | **green**, `EcosystemMarker` 4→0 | **4 RED** — the three above go green, and `csharp_monorepo_binds_partial_scope_and_refuses_the_decoy`, `csharp_monorepo_stages_do_not_scale_quadratically`, `every_guarded_stage_measures_real_work…`, `tier1r_indexed_join_equals_cross_product_oracle` break instead | The first splits `myproj/app/caller.rs` from the `myproj/p1/util.rs` its own `use myproj::util` names — the same split I046 measured as **67 lost ripgrep resolutions** when it tried `pkg_root` in place of `pkg_dir`. The second is coarser and admits a bind a fixture calls a phantom in so many words: ``` OrphanOp in Proj0/F0.cs bound to Some("Proj0/Other/Other.cs") — it is declared in class Other, never in the caller's partial scope. That is a phantom. ``` And **neither is measurable on the pinned corpus**: all nine repos carry discoverable manifests, so the fallback never fires and the full bind digest is byte-identical under both, on every repo. The reason every fixture disagrees is that **the origin gate has never been exercised in a manifest-less fixture** — the old `'' = ''` kept it switched off in all of them, so each was written under a different unstated assumption. Choosing between those partitions on no evidence is exactly what this repo says not to do, so I reverted the product change and left `nearest_package_dir` with a doc that records both refutations and names what would settle it: a manifest-less corpus repo. That is a corpus change, not a resolver change. ## The harness fix, and why it is not a third normalisation `ruby_parity_fixture::stage` now **skips files whose basename `manifest_package_dirs` recognises**. A file whose identity is its NAME cannot survive `shadow`; staging it gives the two legs different worlds rather than one world under two spellings. It is not a normalisation of the comparison — no row is excluded from the diff — and it is applied identically to both legs. This is the suite's own argument for REPLACE-vs-APPEND, applied one step further: `shadow` already documents 122 resolutions that were "an artifact of the harness and not a fact about the package". Only `Gemfile` is affected. `Rakefile` and `*.gemspec` are Ruby claims but **not** manifest names, so the awkward files that staging exists to include are still included. ## On your constraint, which I have kept **No `Mechanism` variant pins these rows.** The opposite: `Mechanism::EcosystemMarker` is **deleted**, arm and variant, exactly as #112's three were — so a row of that shape now reaches `classify`'s `None` and panics with its own text rather than being absorbed. Measured, both legs, at ruby-sinatra's pinned sha: | | before | after | |---|---:|---:| | files | 156 | **153** | | refs | 18450 | **18358** (`FLOOR_REFS` moved with it) | | symbols | 1260 | **1260** | | imports | 231 | **231** | | `SelfQualifier` | 876 | **876** | | `EcosystemMarker` | 4 | **0** | Symbols and imports unchanged is the check that this is a corpus change and not a producer regression — a Gemfile declares nothing and imports nothing. `SelfQualifier` holding at **exactly 876** across a corpus that lost three files is what makes the run evidence rather than a coincidence. Mutation RUN: remove the skip and a new control fires on the staged tree, before any comparison — ``` a package manifest reached a staged leg as /tmp/.tmpgWXa7E/builtin/Gemfile: the shadow renames it out from under `manifest_package_dirs` on the package leg only, and the two legs stop running the same origin gate (#167) ``` and behind it the 12 unexplained ref deltas return. ## One thing closing this exposed, filed as #170 `classify` tries `SelfQualifier` first and its predicate admits `diff ⊆ {qualifier, target}`, so **11 of the 876** carry `EcosystemMarker`'s exact shape (builtin unresolved, package resolved by the file-key import arm) and have been counted as qualifier-text deltas all along — before this change as much as after. Pinned as `SELF_QUALIFIER_ROWS_WITH_A_RESOLUTION_DELTA = 11` so it cannot grow in silence, and filed rather than widened. ## Verified the way you asked ``` COSI_CORPUS_DIR=$HOME/.cache/cosi-corpus COSI_CORPUS_REQUIRE=1 \ cargo test --release -p code-index-indexer --test ruby_package_parity corpus[ruby_package_parity]: executed=1 unavailable=0 not_applicable=0 controls=4 (require=true) #84 axis C — pinned deltas SelfQualifier: 876 test result: ok. 1 passed; 0 failed ``` `executed=1`, not a skip. `_prdoc/records/84-P4-parity-deltas.md` carries the derivation, the refuted product attempts and the new numbers. **Also worth knowing while you are here:** `corpus_stage` has the same trap and it was firing. `tests/corpus/stage-baseline.json` had drifted (`rule.tier3_import_boost`, `rule.tier1q_pass1`, `influence.builtin_only` on four repos, all #134's) and sat red under three separate "green" `cargo test --workspace` runs, because without `COSI_CORPUS_DIR` it reports `unavailable` and passes. Re-recorded with a reason. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01K1zj5VcFJvJt3pQxe9259K
Sign in to join this conversation.
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#167
No description provided.