Skip to content

Seed floor: collection vs nominal product is judged as a relation, not a property of one side - #12045

Merged
briansrls merged 9 commits into
mainfrom
session/quiet-ibex-229
Sep 23, 2026
Merged

briansrls merged 9 commits into
mainfrom
session/quiet-ibex-229

Conversation

@gunbai-bot

@gunbai-bot gunbai-bot Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

The defect

v1.compiler.infer declared_type_inhabitance admitted a collection at a nominal product formal, and a nominal product at a collection formal — in both directions. That is below the DESIGN §4b floor ("values inhabit declared types"), so it is a below-baseline safety regression, not a rung.

Discovered by loyal-swift-608 while honestly demoting lively-bat-737's construction-level wall: a record mintable only by a quiet builder was defeated by handing the interpreter a List<String> carrying --quiet. The construction wall was real; the declared-type wall beneath it was open, so the value never had to be minted.

The grid (measured, main ff2110b)

declared produced main
Rec { a: String } List<String> ADMITTED (body ran)
List<Int> Rec ADMITTED (body ran)
Int String refused
Int List<Int> refused
Rec Other (second record) refused
Rec Optional<Int> refused

The four refusals are what make the two admissions a measurement: the direct-call inhabitance seam demonstrably runs on these fixtures, so the admission was a hole in the relation and not an unreached position.

The chain (§6b, re-derived — not a symptom patch)

For declared=Rec, produced=List<String>, every arm of declared_type_inhabitance in order: optional arm no; declared not generic; both names resolve; declared_realizes_as_kernel_numeric no; collection_at_scalar_declared_type refuses only when the declared base peels to a kernel scalar and Rec does not → false; record_at_scalar_needs_identity wants a kernel declared → no; coproduct_at_record_declared_type wants a coproduct produced → no; refinement_inhabitance Absent; kernel_value_declared_type_mismatch false because the actual is not a kernel; coproduct_payload_where_parent_required no; nominal_product_inhabitance_refusal asks nominal_product_head_name(produced), which answers "" for a collection by design → none. The relation then reaches its terminal arm, Inhabits, by fallthrough. The dual falls through identically with the roles swapped.

The earliest unjustified boundary is that the relation's terminal arm is acceptance while no arm ever judged collection-vs-nominal-product disjointness — every arm keys on the kind of one side.

The repair

collection_at_scalar_declared_type is replaced (not joined by a sibling — a second relation keyed on products is exactly the fork the rostered row exists to prevent) with collection_versus_established_identity: exactly one side is an element/keyed collection after peel_nominal_alias_identity on both sides, and the other side is an established non-generic identity — kernel scalar, nominal product or coproduct via expected_type_head_exposure.

Peeling both sides is what the replaced predicate's own comment warned about without acting on: type Words = List<String> and FreeMonoid<T>-arriving-as-List<T> are collections whose outer node is not List, and refusing either against the other would be a fabricated refusal.

"Established" is the guard, not an extra: a generic formal, a type variable, a callable, and an opaque or stuck head all resolve to no identity this relation may contradict, so none of them refuses here — they fall through to the arms that own them.

New refusal reason RefusedCollectionAtEstablishedIdentity. RefusedKernelAtStructured does not denote record-vs-collection, and the reason is named for the relation rather than for one side. It surfaces through declared_type_obligation_diags at every wired DeclaredTypePosition — no per-position code.

Evidence

Instrument: gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/declared_type_inhabitance_direct_call_witness_test.dag --function <arm>, against a locally built target/release/gunbc, sha256 5efaf329552136186a5d6db31f7d60b95473ca570832770e4f66133e5999b541. Vintage proven by contents, not by path: collection_versus_established_identity and side_is_collection_after_peel are present in the binary and collection_at_scalar_declared_type is absent.

Five arms added to the existing witness home, all returning true:

  • RED w_a_collection_at_a_nominal_product_formal_is_refused — the List<String> at Rec specimen
  • RED w_a_nominal_product_at_a_collection_formal_is_refused — the dual (exercises the other branch; a produced-side-only relation leaves this admitted)
  • positive w_a_collection_and_a_product_each_at_their_own_formal_are_admitted
  • positive w_a_collection_at_an_alias_of_that_collection_is_admitted — guards the peel
  • RED w_a_smuggled_argv_list_at_a_builder_minted_record_is_refused — the loyal-swift discovery shape, enrolled as a claim on the DECLARED type so lively-bat-737's next-rung trigger can fire on it

Mutation control — and the result that matters most was not the one asked for

Run as one remote dispatch on a single runner: the relation forced to false in the mirror, rebuilt, the reds re-run.

The headline is the last row.

arm relation active relation disabled
w_list_typed_value_at_non_empty_str_argument_is_refused — pre-existing true false
w_a_collection_at_a_nominal_product_formal_is_refused true false
w_a_nominal_product_at_a_collection_formal_is_refused true false
w_a_smuggled_argv_list_at_a_builder_minted_record_is_refused true false
w_a_raw_octet_list_at_the_qualified_octets_carrier_is_refused true false

w_list_typed_value_at_non_empty_str_argument_is_refused is not new. It is List<String> at a NonEmptyStr formal — the case the deleted collection_at_scalar_declared_type used to carry. It goes red under the mutation, which means the old predicate's own discriminating red now flows through collection_versus_established_identity.

That establishes this is a REPLACEMENT and not a sibling, and it is the one thing that could not have been established by argument. §3's replacement rule is that X and Y must not both answer for one fact, and the way it usually fails is precisely this: a new predicate lands beside the old one, both green, and nobody notices there are two authorities until a consolidation lane finds them later. Reading the two predicates side by side tells you only that they look equivalent. The old control's red flowing through the new relation tells you the old authority is gone and the new one carries its discriminating power — so the fork is closed by construction rather than by the author's say-so.

The other four rows are the ordinary obligation: each new red is discriminating, none green for a reason other than this relation.

§4b(4) consequence, stated because it cuts against the instinct to tidy. A climb deletes the lower-rung production machinery and keeps the evidence. So collection_at_scalar_declared_type going away is correct, and w_list_typed_value_at_non_empty_str_argument_is_refused must not be retired as redundant now that its predicate is gone — it is now the regression control proving the replacement still covers what the old predicate covered. This diff leaves it untouched (no - lines against it). If it is later proposed for removal as dead weight, the measurement above is the answer.

On the census instruments

The reading comes from the required floor lane, over that lane's closure — which is not the whole tree. A side gunbc test //gunbc/instruments:compile-clean-diagnostic-census was attempted on the same dispatch and was OOM-killed on the runner (exit 137), so it is named here as attempted-and-unobserved rather than quoted from a partial log.

The floor lane is the gate the corpus is held to, so a reading from it is the strongest single instrument available here — but it is not corpus-wide, and an earlier revision of this section said it was.

Correction, against my own earlier wording. This section previously described the floor's closure as "the whole-tree closure" and said the floor "outranks" the side census. The first is false and the second overstates: the regen round runs a v2 self-compile phase whose closure the floor never reaches, and merging main surfaced a second refusal there, in v1.compiler.emit_rust. Two populations, one of them reported as the tree. That second refusal was a false one with its own root cause — the with meaning fork, repaired in this PR — but the scope error stands on its own and is filed as gunbc.recurring_failure_mode.a_census_is_cited_wider_than_the_closure_its_instrument_compiles. Counts in this PR are stated with the closure that produced them named.

Two corrections to the brief's recipe, measured not inferred

  1. gunbc compile --dependency-pool-index primary-precedence --source-root dag --source-root src/v2 --target dag no longer runs: it refuses at admission with WholeCorpusCompileRepositoryUndeclared (wants --repository + --measured-root-demands), and it also requires --output-dir. A first census attempt using that recipe reported zero diagnostics on both sides — a refusal, not a reading. The rostered instrument above is used instead.
  2. A git checkout <main-sha> control silently no-ops on the BuildBuddy runner (fatal: invalid reference) — the mirror carries tracked files, not git history. The controlled arm is therefore the in-place mutation, which isolates this relation more tightly than a main checkout would anyway.

The v2 path (scope bound, measured rather than left open)

v2.std.inhabitance declared_type_inhabitance is a different relation in kind — find_witness over preservation_rule_exact_structural_equality_zip_fold, which reads both sides — so this repair does not transfer. What is measured there, by capturing the diagnostics infer() returns (not by reading the classifier): neither direction reaches that fold, and the two directions stop at different earlier boundaries.

declared produced outcome reasons captured
nominal product collection Accepted infer_grounding_not_derived, inhabitance_undecidable_argument_type_not_derived
collection nominal product Accepted infer_grounding_not_derived, inhabitance_undecidable_generic_formal

Correction. An earlier version of this section said both directions exit as UndecidableGenericFormal. That was read off inhabitance_node_undecidable_reason, not observed, and the floor refuted it: the product-formal direction stops one boundary earlier, because the supplied collection argument has no derived value type. The claim now asserts the reason actually captured.

v2 therefore does not silently answer Inhabits the way the seed did; it counts the obligation as undecidable and the route advances — the standing of gunbc.recurring_failure_mode argument_type_obligation_absent_while_the_route_advances, not a second class, and not the same one-relation change. Two frontier arms enrolled in the existing infer_application_argument_inhabitance witness so the cell is no longer unmeasured.

The row

Appended as measured specimen three to gunbc.recurring_failure_mode.declared_type_wall_keyed_on_the_value_being_a_kernel — both directions, the six-cell grid, the chain. No second row minted. The specimen states explicitly that this repair is not that row's next-rung trigger: the trigger names one judgment from the formal's declaration for an actual of any kind, sufficient for removing the offending-value guards entirely, and this removes one such guard while kernel_value_declared_type_mismatch and the head-name arms still stand. The row stays open.

Not in this PR

Record-update (with) controls — mutation measured

At head 279ec56043, claim_batch built locally (--functions w_a_record_update_inhabits_its_bases_declared_type,w_a_genuine_map_at_a_product_formal_still_refuses):

arm repair active product branch of the with arm disabled in the mirror (if false && ...)
w_a_record_update_inhabits_its_bases_declared_type PASS FAIL
w_a_genuine_map_at_a_product_formal_still_refuses PASS PASS

The false-refusal control depends on the receiver-keyed arm, and the Map discriminator stays green either way, so admitting the record update didn't weaken the wall.

emit-build is red on main for the same reason

emit-build fails on this head with E0573 at emitted src/std_integer.rs:162 (MachineWidth<PointerWidth>: PointerWidth resolves to a variant, not a type). Main fails the same way on 7a9b29aeb0, fb859cfa35, 0be62879ed and 45286d61c0. This diff touches neither std.integer nor v1.compiler.emit_rust. The defect is already owned outside this PR, and emit-build isn't in the required aggregator (needs: compiler, clippy, floor).

Regen round on the merged head

claim_executor --required-regen --source-root dag --source-root src/v2, run after merging main (which includes #12051): v1_compiler_infer.rs regenerates byte-identical to the committed mirror. The round's FAIL line names three drifted files that this branch does not touch and that are identical to main's: std_integer.rs, std_machine_constraints.rs (the PointerWidth shape behind main's emit-build red) and v1_compiler_infer_resolve.rs (a hand-carried bootstrap patch). They are not installed here: that drift belongs to main.

🤖 Generated with Claude Code

Brian Searls and others added 2 commits September 22, 2026 04:10
…t a property of one side

v1.compiler.infer declared_type_inhabitance admitted a List<String> at a
Rec { a: String } formal and a Rec at a List<Int> formal, in both directions,
by reaching its terminal Inhabits arm through fallthrough: every arm keyed on
the kind of ONE side, so the collection/nominal-product pair was judged by no
arm at all.

Replaces collection_at_scalar_declared_type with
collection_versus_established_identity: exactly one side is an element or
keyed collection after peel_nominal_alias_identity on BOTH sides, and the
other side is an established non-generic kernel scalar, nominal product or
coproduct by expected_type_head_exposure. Adds the refusal reason
RefusedCollectionAtEstablishedIdentity, since RefusedKernelAtStructured does
not denote record-vs-collection.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s specimen is being repaired

The whole-tree census found exactly one natural red for this relation:
extdeps.auth.jws handing a raw List<UInt8> to base64_encode's QualifiedOctets
formal. A precursor PR repairs that site, which would leave this wall with no
executed red at all -- DESIGN 4b(1) rung inflation arriving from the direction
where the corpus got healthier rather than where the check got weaker.

4b's top-rung question settles it: a state unrepresentable in the ACCEPTED
corpus is still representable as source handed to the compiler by a fixture.
The specimen therefore moves into the witness as an authored red over the real
std.integer / std.encoding declarations, with a positive control over the same
carrier and callee so an arm that refused every argument cannot pass it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Automated notice flagged 2 failing checks. Investigated: this red is the change working, and it is resolved by landing #12051 first — not by a fix pushed here.

The two failures are one fact. floor refuses at a single site, and witnesses is the aggregator that reports floor's conclusion (a required lane concluded failure (compiler=success clippy=success)), so it carries no independent finding. clippy and compiler pass.

The floor refusal, identical in all three floor steps and unchanged by the most recent commit:

dag/extdeps/auth/jws.dag:35:39: error: value does not inhabit its declared type
  at the direct call argument for parameter 'octets':
  declared 'Product(QualifiedOctets)',
  produced 'Container(List,Node(UInt8<...>))'

That is the one new refusal this PR's wall produces across the whole tree, and it is a real latent defect rather than a false positive: std.encoding base64_encode declares the sealed carrier QualifiedOctets, extdeps.auth.jws hands it a raw List<UInt8>, and std/integer.dag already documents that exact hole as open — "handing a raw List where the carrier is declared still type-checks … dies at RUNTIME with TypeError: cannot access field 'members' on List". All nine other base64_encode callers in the corpus mint through the fold first.

The repair is #12051, which is this PR's precursor and lands first. Landing order is being handled by neat-boar-16; once #12051 is in, main is merged here and the lane goes green with no diff from this branch.

Two things this PR deliberately does not do, recorded so the red is not mistaken for unfinished work:

Because the precursor removes the corpus specimen, this PR carries its discriminating red as an authored fixture instead (w_a_raw_octet_list_at_the_qualified_octets_carrier_is_refused, with a positive control over the same carrier and callee) — see the "why is it safe to land the two separately" section in the body, which also records that #12051's witness was run against a binary carrying this wall, with its vintage proven by contents.

@gunbai-bot

gunbai-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Addressing review 69974. Both findings verified against the current head; one is correct and already answered, one rests on a premise that is factually wrong. No commit for either — reasons below.

Finding 1 — the jws site: correct, and it is the ordering case

Your dichotomy is the right one and the answer is the first horn, stated plainly: the wall fires, the corpus refuses on the required lane right now, and the repair lands strictly before this PR.

The dependent PR is #12051. It was already named in the body before this review (the "Not in this PR" and "why is it safe to land these separately" sections) and in an earlier comment; landing order is held by neat-boar-16. Evidence it is exactly the one site: this wall's whole-tree census produced exactly one new refusal, and the floor lane names the same jws.dag:35:39 in all three of its steps, unchanged across my last commit.

On the second horn — whether the claimed climb is established — it is, and by two independent things rather than the one you cite:

  1. the live site, which the required floor lane refuses by execution;
  2. an authored fixture, w_a_raw_octet_list_at_the_qualified_octets_carrier_is_refused, with a positive control over the same carrier and the same callee.

The fixture is not belt-and-braces. Once #12051 repairs the live site this PR's census is zero new refusals, and a wall landing with no executed red is §4b(1) rung inflation arriving from the direction where the corpus got healthier rather than where the check got weaker. §4b's own top-rung rule licenses the fixture: a state unrepresentable in the accepted corpus is still representable as source handed to the compiler, and a compiler is precisely a thing whose regression probes are invalid programs.

One measurement worth having, since the split is what your finding is really about: #12051's jws witness was run against a binary carrying this PR's wall — vintage proven by contents (collection_versus_established_identity present, collection_at_scalar_declared_type absent), not by a path — and all three arms pass. Two PRs each green alone would not have shown that.

Finding 2 — the mirror indentation: the premise does not hold

the entire rest of this emitted file is cargo fmt-clean

Measured on this branch, that is not the case, and the flush-left block is not introduced by this diff:

flush-left ^ reason: flush-left ^})
origin/main 8 46
this head 9 47

My change adds exactly one of each, shaped identically to 46 siblings the emitter already produces on main — including, four lines above the one you flagged, UndecidableProducedIdentityErased, and immediately below it RefusedKernelAtStructured, the very arm mine replaced.

cargo fmt --all --check (gunbc.repo_self_build repo_self_clippy_command's sibling, run by the generated pre-commit hook) exits 0 on this tree, with no rustfmt exclusion for the mirror. So the file is fmt-clean by the gate's own verdict; rustfmt declines to reflow these deeply-nested struct literals rather than rejecting them.

What is true underneath, and worth naming even though it is not mine: the emitter produces sub-optimally indented output for struct literals at this nesting depth, and rustfmt silently leaves them. That is a pre-existing characteristic of v1.compiler.emit_rust, which this brief explicitly bars me from touching (another lane is serializing it), and a mirror is never hand-edited — the diff must be exactly what the emitter produced from the .dag, or the mirror stops being a projection of its authority. Editing it to satisfy this finding would be the defect, not the fix.

— sent from quiet-ibex-229

Brian Searls and others added 5 commits September 22, 2026 19:38
# Conflicts:
#	src/v1/stage0/src/v1_compiler_infer.rs
…ope class it exposed

Merging main surfaced a SECOND refusal from this wall, and it was a FALSE one:
v1.compiler.emit_rust `with(emit_info, { movable: tco_movable })`, where
EmitGraphInfo really does declare `movable`. The site is correct code.

ROOT, per the ruling: `with` is TWO contracts on one spelling. v1.compiler.method
registers it as the MAP operation -- three parameters returning a Map -- while
the corpus also uses the record-update form `with(base, { field: value })`
returning the base's product. Inference knew only the first, typed the record
update as a Map, and the wall then refused Map-at-product correctly on a premise
it had been handed wrongly. DESIGN section 3: a materially different contract
needs a materially different name, and the name is already forked across
v1.compiler.method, v1.compiler.languages, v1.compiler.parse and
v1.compiler.emit_rust.

THE DISCRIMINATOR IS THE RECEIVER'S TYPE, not the arity and not the call's
spelling. Keyed-collection receiver means map insert; established product
receiver means record update returning that product; anything unestablished
keeps the registry's answer, so the arm narrows nothing it cannot decide. An
arity test would have greened the four sites that exist and lied about the fifth,
because arity is a property of the call rather than of the operation. The arm is
REACHED by name through 04_infer's existing func_name chain, which is that file's
established shape; only the decision is type-keyed.

NOT SETTLED, recorded in the annotation as an open reading: whether these are one
concept -- functional update of a keyed structure at two scales -- in which case
section 2's horizontal move makes the answer a single polymorphic `with` rather
than two names. This makes the two contracts distinguishable, which is the
prerequisite for either answer.

NOT narrowing the wall to admit Map-at-product, which would have unblocked this
faster: that encodes inference's defect into the wall where nothing declares it,
leaves the wall permanently weaker, and is the rostered
symptom_link_patched_without_the_earliest_unjustified_boundary.

ALSO FILES a_census_is_cited_wider_than_the_closure_its_instrument_compiles. My
own "exactly one new refusal across the whole tree" was true of the required
floor's closure and not of the corpus: regen's v2 self-compile is outside it, and
that is where the second site lived. The row carries why it survived checking --
the refusal cannot appear until the wall is IN THE BUILT BINARY, so every earlier
regen ran an instrument that could not refuse.

Mirror: v1_compiler_infer.rs regenerated and confirmed at its fixed point (it
drops off the drift list on the following round, self-compile clean). The four
mirrors still drifting are NOT mine and are deliberately untouched, byte-identical
to main: std_integer, std_machine_constraints and v1_compiler_emit are main's own
stale mirrors from the kind-reflection and SHA-256 commits, and
v1_compiler_infer_resolve carries a block labelled TRANSIENT BOOTSTRAP PATCH that
the candidate would REMOVE -- another lane is mid-bootstrap there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…made obligatory

Adopting the regenerated v1_compiler_infer.rs added main's
enclosing_declared_type_param_names to InferScope, and main's OWN
v1_compiler_emit.rs -- already stale against main's .dag -- does not set it.
Regenerating my mirror is what turned that latent inconsistency into E0063.

MY EARLIER RULE WAS RIGHT AS A PRINCIPLE AND WRONG AS AN APPLICATION. "Adopt
only the mirror you own" treats generated mirrors as independent units, and they
are not: InferScope's DEFINITION lives in infer's mirror while two of its
CONSTRUCTORS live in emit's. The narrower correct rule is to adopt what the file
you own requires for coherence and nothing beyond it. That is two lines in
v1_compiler_emit.rs, taken verbatim from the regen candidate.

The coupling also reaches HAND-WRITTEN Rust: src/v1/stage0/src/bin/
infer_semantics_witness.rs constructs InferScope directly at one of its three
sites (the other two use struct-update syntax and inherit the field). That is not
a mirror, so it is edited rather than regenerated.

STILL NOT TOUCHING v1_compiler_infer_resolve.rs. It carries a block labelled
TRANSIENT BOOTSTRAP PATCH that the regen candidate would delete, another lane is
mid-bootstrap on it, and nothing in this failure implicates it.

VERIFICATION GAP THIS EXPOSED, which is the transferable part: I had been
validating with `cargo build --bin claim_executor`, which compiles one crate
graph. DESIGN's Building-and-checks section states that
`cargo clippy --all-targets` is the ONLY command that compiles the
integration-test and example targets, so a red there is invisible to every other
step -- and the witness bin above is exactly such a target. Both new sites were
found by running that command locally, and it now runs before the push rather
than after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…scriminator

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…nly a comment

Review 70238: the open reading (unify into one functional update, or split the
name) lived only in a // annotation. It now sits on the existing meaning_fork row
with rung, ceiling, next trigger and controls, citing the registry and the
receiver-keyed arm by declaration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@gunbai-bot

gunbai-bot Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

Review 70238, finding 1: the with fork is now a typed specimen on the existing gunbc.recurring_failure_mode.meaning_fork row (no new row). It records the hidden-state form, what was done (the receiver-keyed arm), rung MITIGATABLE, ceiling STRUCTURALLY IMPOSSIBLE, the next trigger (decide unify-vs-split in v2), and both controls. Evidence refs: v1.compiler.infer_method::builtin_function_registry and v1.compiler.infer::infer_tier2b_builtin_with_kernel_diags. Finding 2 (the hand-written Rust field initializer) needs no action, as the review says.

— sent from quiet-ibex-229

Brian Searls and others added 2 commits September 22, 2026 22:44
…-product cell

The floor refuted the claim that both directions exit as UndecidableGenericFormal:
that was read off the classifier, not observed. Captured diagnostics show the
product-formal direction stops one boundary earlier, at
inhabitance_undecidable_argument_type_not_derived, because the supplied collection
argument has no derived value type. The dual does reach
inhabitance_undecidable_generic_formal.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@briansrls
briansrls added this pull request to the merge queue Sep 23, 2026
Merged via the queue into main with commit 9fd525e Sep 23, 2026
4 of 5 checks passed
@briansrls
briansrls deleted the session/quiet-ibex-229 branch September 23, 2026 02:49
gunbai-bot Bot pushed a commit that referenced this pull request Sep 23, 2026
…instead of retiring it (review 70608)

Its trigger also named the four-parameter kind-inhabitance fold. The seed still carries the
three-parameter type_arg_kind_inhabitance (no kind_inhabitant_matches_resolved) and the
single-arm WidthResolution alias, so only the value-position half was discharged by #12045.
The row now cites those two seed declarations, with the trigger restated for them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant