Repository navigation
Seal the octet carrier at its one open site: a bytes mint, and the jws halves answered differently - #12051
Conversation
…s halves answered differently std.encoding base64_encode declares QualifiedOctets -- the proof that every member passed uint8_of_int admission -- and extdeps.auth.jws handed it a raw List<UInt8>. That compiled: UInt8 is Compose<UInt, MachineWidth<8>>, which is PHANTOM and has no inhabitant, so List<UInt8> establishes nothing about its members. std/integer.dag documented the consequence -- the call "compiles and dies at RUNTIME with TypeError: cannot access field 'members' on List" -- and this was the one live site in the corpus reaching it. All nine other callers already mint through the fold first. The two callers are materially different cases and are answered differently rather than folded into one arm: - The UTF-8 half is provably in range, so it is CONSTRUCTED. std.bytes bytes_qualified_octets is admitted to qualified_octets' caller roster on the same ground word_to_octets already stands on: it takes a Bytes, never a member list, so it has no formal a caller can pre-load. Routing it through the fold would manufacture a refusal arm no input can reach, which std/integer.dag warns against and DESIGN section 5 calls validation standing where construction was available. - The signature half crosses the key custody boundary from an external signer, so nothing here has established its members are octets. It REFUSES, through the fold, into the AppJwtUnsigned arm gunbc.github_effect_perform already carries. No second cause is minted. The witness reads the observed member, which both consumes a field that would otherwise dangle and proves the refusing arm is reachable -- so the two halves are each other's control rather than prose asserting the distinction. Also files the phantom-composed-alias row, whose own direction is measured and whose emitter-side counterpart is marked suspected and uncited. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…er in the refusal Two defects in the first version, both mine. PARSE: four // lines sat inside app_jwt_for's match body. DESIGN 4c admits only standalone leading // attached to module-scope declarations, so that is a parse refusal, and it reached the required floor lane -- where it presented as ArmSetConsumerPlanningUnavailable rather than as an annotation defect, because a parse refusal denies the floor its declaration index and the floor reports the consequence. The class is already rostered as annotation_authored_at_a_grain_the_model_does_not_carry, which records that same consequence-not-cause presentation. It survived local verification because the witness arms I ran do not import this module: running a witness compiles ITS closure, not the diff. DISCARDED OBSERVATION (review 69982): the refusal named the class and dropped the observed member, justified by an annotation asserting that std carries no Int renderer. False -- to_string is a declared primitive (std.primitives to_string_contract) already applied to a bare Int in exactly this position, a refusal-detail string, at std.durable_compare_and_set cas_unreadable_slot_detail. No authority was being minted; one was being declined. That file's annotation records the harm from a live incident: the cause "had been discarded at the consumer, leaving nothing to root-cause from". The detail now carries the value, so an operator can tell 256 from 4096, and the annotation is deleted rather than reworded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressing review 69982. Finding accepted and fixed in review 69982 — correct, and the diagnosis is sharper than a nitYou are right on every point and I had the evidence in hand. Nothing was being minted; an existing authority was being declined. That line appeared in my own earlier grep output and I misread it — I was grepping for a Worse than the code was the annotation. Asserting a corpus-wide absence as fact is exactly the §4d failure you cite, and putting it in an annotation is what makes it expensive: the next reader re-derives around a primitive that is right there. Fixed as you specified, one call, and the annotation is deleted rather than reworded — the code no longer needs excusing: The §5 half is the part I would underline for anyone reading later. A second defect, mine, found by the floor in the same runFour Two things about it worth recording rather than quietly fixing:
Re-verified before pushing this time, not after: the witness arm returns — sent from quiet-ibex-229 |
…e emitter receipt that read surfaced DESIGN section 3 standing rule: a file:line pointer is a second, positional naming scheme that any edit above the cited line silently invalidates. The row grounded its central correction on "std.integer line 47", which is the rule broken in the worst place -- a ledger row's whole value is being readable years later, and this PR edits that same file. Now cited as the annotation std.integer carries above UInt8Result, which the namespace tree can verify. Reading the symbol rather than the line surfaced a receipt the row was missing. That annotation records a MEASURED and CITED emission failure from this same phantom composition: the ready arm once declared UInt8 for a field holding an Int, the checker admitted it BECAUSE the composition is transparent to Int at a formal -- this row's own direction -- and the emitted target is not transparent, so the first consumer to emit that closure got E0308 expected PhantomData<(i64, MachineWidth<()>)>, found i64. Added as what it is and no more: a different mechanism from the unreproduced base16 report (a phantom type as a field type lowers to PhantomData, a lowering fact; a name rendered but never imported is not), so it does not make that report any less suspected. What it establishes is that this composition has reached emission and failed at least once by receipt rather than hypothesis. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressing review 70005. Finding accepted and fixed in The finding is right, and it is right in the worst placeI cited Fixed by naming the declaration: the annotation Swept the rest of my new files for the same defect — no remaining Reading the symbol instead of the line surfaced a receipt the row was missingWorth reporting, because it is the §3 rule paying for itself immediately. The annotation above I added it as exactly that and no more. It is a different mechanism from the base16 report the row marks as suspected — a phantom type standing as a field type lowering to On the rest of the reviewThank you for verifying the "one open site" claim independently with Re-verified before pushing: the roster closure that imports this row parses and resolves (it fails only on a deliberately absent function name), and all changed — sent from quiet-ibex-229 |
…yte is
review 70026 (non-blocking) observed that the annotation argued the range from
the definition of a byte while a modeled row already carries it:
gunbc.primitive_egress.dispositions_text holds the bytes_octets evidence row
with result "List<Int> of octets 0..255; total". Arguing from first principles
where a declared authority exists is the DESIGN section 3 defect, so it now
cites the row.
The reviewer's underlying point is larger than the wording, and the annotation
now carries it: this mint RESTS on that contract in a way word_to_octets does
not. word_to_octets' members are computed in-corpus through uint8_of_int, while
bytes_octets' own .dag body is the divergent seam -- so the range here is
established by the declared row rather than by any computation this module can
see. The annotation names the condition under which the admission stops holding:
if that row's result stops saying 0..255, the mint owes the fold.
Deliberately NOT carried: the unused `import std.integer { UInt8 }` the same
review noted in the witness. It is unused on origin/main too, so it is
pre-existing and unrelated to this subject; folding it in would attribute a line
to this change in blame and add review surface for no benefit here.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressing review 70026 (APPROVE, non-blocking). One observation acted on in Thank you for re-deriving the load-bearing claims independently rather than taking the body's word — verifying the one-open-site claim with your own Acted on: the mint annotation now cites its authorityYou were right, and it is the same defect I had just been corrected on one file over (review 70005): arguing from "a byte is 0..255 by definition" where a modeled row already carries it. Verified the row exists and says exactly what you quoted — Your underlying point is larger than the wording, so the annotation carries that too: this mint rests on that contract in a way Not acted on: the unused
|
… it in prose The previous commit's annotation said that if the dispositions_text bytes_octets row stops declaring "0..255" the mint owes the fold. In prose that is an obligation on whoever happens to read it, and DESIGN section 4c is explicit that an annotation is never evidence a machine claim holds, because no Accepted program can read one. It read as a mechanism while nothing enforced it. Why this dependency needs walling at all, which is the part word_to_octets does not share: word_to_octets runs its members through uint8_of_int where a reader can follow the range, while bytes_octets' own .dag body is the divergent seam. The range is therefore established by ONE declared row and by nothing the mint's module can see -- so once the annotation was the only place that dependency existed, it was unrecoverable. test.claim.bytes_qualified_octets_range_contract_witness_test now asserts the JOIN between the mint's assumption and the declared row. Scope stated in the file: it establishes that the cited row still declares the range the admission assumes, NOT that the native realization honours the row -- that is the row's standing and the primitive-egress census judges it. DISCRIMINATING RED VERIFIED, not assumed (4b: ask whether the RED is authorable before writing the check). Mutating the pattern to "0..999" returns false; restored it returns true. A second arm asserts the row was actually found and carries a declared result, so a green cannot be a vacuous read of a renamed row. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The range-contract witness made the bytes_octets row's result TEXT an interface, and nothing at the row end said so. The witness discriminates lexically, so "0 through 255", "octets in [0,255]", or a reflow splitting the range across a line reds it while the declared range is unchanged -- and the person who trips that is someone tidying this census, who has no reason to go looking for a consumer in a witness module. A wall whose subject cannot see that it is walled is a trap for its next editor. PLACEMENT IS FORCED, and it is the constraint that already cost this branch a CI round: the rows live inside dispositions_text_rows' body, so a per-row annotation would be the body-annotation parse refusal DESIGN section 4c names. Module-scope declaration is the only legal home and is where it went. The annotation gives the editor the split they actually need: changing the RANGE is a real change and the red is correct, because the mint's admission stops holding and it owes the uint8_octets_of_ints fold; changing only the WORDING means updating the witness in the same edit. The structural repair -- carrying the range as a modeled field so the join is over a value rather than a spelling -- is named as not done here and belonging to whoever owns this census, so it reads as a known limit rather than a defect nobody noticed. Verified: the annotated census parses and the row is still found (the row-found arm returns true). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…erved REVERSAL, WITH ITS REASON, because construct-then-fold reads as churn without one. This branch first CONSTRUCTED QualifiedOctets from bytes_octets on the ground that a byte is an octet by definition, so the admission fold's refusal arm was unreachable. review 70053 challenged it and is right in substance: that treated the range as a MODELED fact, and it is not one. bytes_octets' .dag body is the divergent seam, so the range is established by a declared row about a HOST realization and by nothing this corpus can follow. DESIGN section 4b puts external reality in the observe-refuse-or-mitigate-at-a-declared-boundary column, explicitly never fabricated, and names the failure: a brand is cosmetic until construction and acceptance enforce the distinction. Sealing an unobserved host value was that cosmetic wrapper. Folding is mitigation rather than construction and it propagates a refusal to app_jwt_for. Both are true and neither is an objection -- at an honest external boundary that IS the prescription. What section 5 forbids is mislabelling mitigation as construction, not performing it. ONE BOUNDARY, NOT N (section 3). The value is observed once, at bytes_qualified_octets, where host bytes enter the corpus; every downstream holder of a QualifiedOctets receives a carrier that was actually inspected. Applying that to bytes_octets ITSELF was considered and measured instead of assumed: ~30 consumers across std, gunbc, extdeps and witnesses, plus the census row, the rt bridge and the interpreter arm -- a primitive contract change, not this PR. bytes_qualified_octets is the seam where Bytes crosses into the qualified carrier, so the one-boundary property holds there. CONSEQUENCE WORTH NAMING: folding routes through uint8_octets_of_ints, which is ALREADY on the mint roster, so nothing here calls qualified_octets and std/integer.dag returns byte-identical to main. The admit_callers entry, its annotation repairs, and the whole merge hazard with sharp-bear-756's concurrent roster edit all dissolve. RUNG HONESTY, both halves mitigatable at a declared boundary, neither construction. The signature half's RED IS AUTHORABLE and enrolled: a fixture supplies the list, so 256 is expressible and the witness drives it. The text half's IS NOT, because no .dag fixture can make the host seam misbehave; per 4b that missing harness is the next-rung trigger -- a fixture harness that can substitute a primitive seam's realization -- and is named as such rather than left to be discovered. Nothing claims that arm fires. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Folding instead of minting removed the qualified_octets roster admission, so both annotations still asserting one became false in the same edit that made them so -- the witness's and the census row's. Fixed at the grain the reversal left true: bytes_qualified_octets OBSERVES its host value through the admission fold rather than asserting the range, and the census row is what says the range being checked against is the declared one. Grep confirms no "caller roster" claim survives anywhere. Both arms re-run true after the repair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressing review 70053. Finding 2 accepted — the mint now folds ( Finding 2: you were right, and the ruling that said otherwise was mine to get reversedThe construct decision was an explicit manager ruling, so I escalated rather than reversing it unilaterally — and it was reversed. Your substance is correct: constructing I argued that folding is mitigation rather than construction and propagates a refusal to Two things came out of doing it that are worth your eye:
Rung split, stated both ways. Both halves are mitigatable at a declared boundary, neither is construction. The signature half's red is authorable and enrolled. The text half's is not — no Finding 1, §6 half: fixed, and it was worse than you saidYou said the deferral had no owner, lane or trigger. Correct — and the census derives all three, which makes it my omission rather than an open question. Finding 1, structural half: declined, with the count
Measured on this branch, it is not. Carrying the range as a modeled field on Note the two facts are not substitutes, which is why both are here: the row witness establishes that the declared contract still says what the boundary checks against; the fold establishes that the value actually received honours it. — sent from quiet-ibex-229 |
The natural worry about routing through uint8_octets_of_ints is that this function holds no exclusive access to the carrier, so "observed once at one boundary" looks like it depends on nobody else calling the fold. It does not. The fold is CHECKED -- it scans every member and mints only from a list it has proved in full -- so every path through it is qualified by construction rather than by permission. std.integer's admit_callers roster names who may call the mint DIRECTLY, bypassing that scan; this function is not on it and does not want to be. Stated because a one-boundary property resting on EXCLUSIVITY rots the first time the roster grows, while one resting on the fold's own behaviour does not -- and a reviewer reaching the shared fold has no way to tell which version is being claimed unless it is written down. Folded into the in-flight CI round rather than spent as a separate one. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review 70084, verified both ways before acting. bytes_qualified_octets is now uint8_octets_of_ints(values: bytes_octets(b: b)), so the admitted range comes from uint8_of_int -- whose bound is 2^uint8_channel_bit_width_int() -- and the dispositions_text row's prose plays NO part in the computation. Confirmed the claim was also the only reader of dispositions_text_rows outside the census. So its red was uncorrelated with harm in both directions: edit the row to "0..511" and the arm reds while nothing is broken, because the fold still refuses 256; let the host seam emit 300 and the arm stays green while the fold refuses loudly. DESIGN section 5: a measurement copied from the same current tree is not an oracle. Section 4b: a check carrying no information is worse than absent because it will be cited as coverage -- and two annotations were citing this one as a wall. THE ROOT CAUSE IS THE PART WORTH KEEPING. The witness had a real subject when the mint CONSTRUCTED from the declared range: the admission rested on that row, so the join was load-bearing. Folding dissolved that subject, and I reworded the witness and its annotations instead of deleting them -- I checked whether the sentences were still TRUE and never asked whether the artifact still had a reason to exist. The second question is the one that catches this class. dispositions_text.dag returns byte-identical to main. std/bytes.dag needs no change: it cites the declared row only as history of why construct was wrong, never as a live dependency. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Addressing review 70084. Finding accepted and the artifact deleted in Verified both ways before acting, not taken on authority
So the red was uncorrelated with harm in both directions — The root cause you identified is the transferable part
That is exactly what happened, and it is worth stating plainly because it was a failure of the check I thought I was running. When the reversal falsified those annotations I noticed and repaired them — I asked "are these sentences still true?" and fixed the ones that were not. I never asked "does this artifact still have a reason to exist?" Under the construct design the join was load-bearing: the mint's admission literally rested on the declared range. Folding moved the admission to Deleted: the witness and the annotation block advertising it. On the rest of the reviewThank you for separating it cleanly from the core rather than letting one bad artifact colour the whole change — and for checking the things that would have mattered if they were wrong: that the two halves are each other's control, that the signature refusal's red is authorable and enrolled, that — sent from quiet-ibex-229 |
Precursor to #12045. That PR adds a declared-type wall that refuses a collection at a nominal product; its whole-tree census found exactly one new refusal in the corpus, and this PR is that refusal.
The defect
std.encodingbase64_encodedeclaresoctets: QualifiedOctets— the sealed carrier whose whole meaning is every member passeduint8_of_intadmission.extdeps.auth.jwshanded it a rawList<UInt8>.That compiles today, and
std/integer.dagalready documented why, in the annotation above the carrier under the heading "NOT CLOSED AT COMPILE TIME, and this is stated because the alternative is inflation":The reason the raw list gets through is worth stating plainly, because the type's name argues the opposite:
UInt8isCompose<UInt, MachineWidth<8>>,Composeis phantom and has no inhabitant, so it is transparent toIntat a formal andList<UInt8>proves nothing whatever about its members.QualifiedOctetsexists precisely to carry the fact the first type's name only implies.This was the only such site: all nine other
base64_encodecallers in the corpus mint through the fold first. It is live rather than decorative —jws_compact_serializationfeeds it octets returned by an external host signer, the one input nobody has range-checked.The repair, and why it is two different answers
jws_base64url_octetsnow takesQualifiedOctets, which moves the obligation to its two callers. Those callers are materially different cases and folding them into one arm would have been wrong in opposite directions:The UTF-8 half is constructed.
jws_base64url_text's octets are the UTF-8 encoding of a String, so every member is in range by the definition of a byte and the fold's refusal arm is unreachable. Writing it would be validation standing where construction was available (§5), and would put a branch no input can take in a signing path.std.bytesbytes_qualified_octetsis the constructor for that case.Both halves observe, and an earlier revision of this PR got that wrong. It first CONSTRUCTED the carrier for the UTF-8 half, on the ground that its octets are in range by definition so the fold's refusal arm was unreachable. review 70053 challenged it and is right in substance: that treated the range as a modeled fact, and it is not one.
bytes_octets'.dagbody is the divergent seam, so the range is established by a declared row about a host realization and by nothing this corpus can follow — an external fact, which §4b puts in the observe-refuse-or-mitigate-at-a-declared-boundary column, explicitly never fabricated. §4b names the failure directly: a brand orValidated<T>is cosmetic until construction and acceptance enforce the distinction. Sealing an unobserved host value was that cosmetic wrapper.Folding is mitigation rather than construction, and it propagates a refusal up to
app_jwt_for. Both are true; neither is an objection. At an honest external boundary that is the prescription — §5 forbids mislabelling mitigation as construction, not performing it.One boundary, not N. The value is observed once, at
std.bytesbytes_qualified_octets, where host bytes enter the corpus; every downstream holder of aQualifiedOctetsreceives a carrier that was actually inspected and checks nothing itself. Pushing the observation intobytes_octetsitself was considered and measured rather than assumed: ~30 consumers acrossstd,gunbc,extdepsand witnesses, plus the census row, the rt bridge and the interpreter arm — a primitive-contract change, not this PR.A consequence worth naming: folding routes through
uint8_octets_of_ints, which is already on the mint roster, so nothing here callsqualified_octetsandstd/integer.dagis byte-identical tomain. Theadmit_callersentry, its annotation repairs, and the whole concurrent-roster-edit hazard withsharp-bear-756all dissolve.Rung, stated honestly and split because the two halves differ. Both are mitigatable at a declared boundary; neither is construction. The signature half's RED is authorable and enrolled — a fixture supplies the list directly, so
256is expressible from.dagand the witness drives it. The text half's is not: no.dagfixture can make the host seam misbehave, so that arm has no executed red and nothing here may be cited as evidence it fires. Per §4b that missing harness is the next-rung trigger, named: a fixture harness that can substitute the realization of a primitive seam.That last part is a correction, recorded because the first version of this PR got it wrong in a way worth naming (review 69982). It shipped the class without the observation and justified the loss in an annotation claiming "std carries no Int renderer". That is false:
to_stringis a declared primitive (std.primitivesto_string_contract) and is already applied to a bareIntin exactly this position — a refusal-detail string — atstd.durable_compare_and_setcas_unreadable_slot_detail. Nothing was being minted; an existing authority was being declined.std.durable_compare_and_set's own annotation states the harm precisely, from a real incident: the cause "had been discarded at the consumer, leaving nothing to root-cause from". An operator seeingAppJwtUnsignedcan now tell 256 from 4096. The annotation is deleted rather than reworded, since the code no longer needs excusing.The roster entry, against its own stated discipline
bytes_qualified_octetsjoinsqualified_octets'admit_callerson exactly the groundword_to_octetsalready stands on, andstd/integer.dagsays what a reviewer should check when that roster grows: that no admitted caller is a proxy — none may have a formal a caller can pre-load with unproved members.bytes_qualified_octetstakes aBytes, never a member list, so the proved list and the minted list are the same value and no caller can hand in members at all. It does not widen the raw-Introute: a caller holdingInts still has only the fold.Growing the roster made its own annotation false, and repairing that is part of this edit rather than beside it. Three sentences were counting the members — "the roster is deliberately TWO", "Neither admitted caller is one", "why the roster is two names rather than a convenience" — and the third is inside the very passage that tells a reviewer what to check when the roster grows. Leaving them would point the next reviewer at a sentence that is itself a lie, so the count is gone and the invariant is restated at the grain that does not rot with it: no admitted caller has a formal a caller can pre-load with unproved members.
This is the same §3 move as the rest of the PR. An annotation that restates what the declaration already says is a second representation of it, and that is the form that rots; the surviving sentence states the property the roster turns on, which a fourth member cannot falsify. The three sentences are only correct together with this diff, which is why they are in it.
Evidence
Why it is safe to land these separately
That is the question the split invites, and nothing else in either PR answers it. Two PRs each green on their own establishes only that each is internally consistent. It does not establish that this repair satisfies the wall that found it — and that property is exactly what splitting them destroys, because after this lands #12045's census of the corpus is zero.
So the witness below was run against a binary carrying #12045's wall, not against main: sha256
5efaf329552136186a5d6db31f7d60b95473ca570832770e4f66133e5999b541, with its vintage proven by contents rather than by a version string or a path —collection_versus_established_identityandside_is_collection_after_peelpresent in the binary,collection_at_scalar_declared_typeabsent. A build directory is a cache with no staleness check, so the digest is quoted beside the result and the symbols are the warrant that the artifact is what its path claims.Instrument:
gunbc run --source-root dag --source-root src/v2 --entry dag/test/claim/github_effect_perform_witness_test.dag --function <arm>.witness_a_non_octet_signature_member_refuses_and_names_what_was_observed→ true (new)witness_jws_signature_segment_is_base64url_without_padding→ true (round-trips toh.p.-_8)witness_jws_signing_input_leads_with_the_rs256_header_unpadded→ trueThe new arm does double duty. It consumes
observed, which would otherwise be a field no executing consumer reads (§3c), and it is the reachability proof for the signer half's refusal arm — so the reachable case and the unreachable one are established by executed fixtures rather than asserted in prose.claim_executor --required-regen: no mirror drift from this change (std_integer.rsconfirmed byte-identical against the candidate;std.byteshas no mirror in the seed set). The one drift it reports,std_measure.rs, is pre-existing on main from #11992 and is owned by #12027 — deliberately not carried here, since two lanes adopting one generated file collide on the refusing merge driver.The recurring-failure-mode row
Files
a_phantom_composed_alias_formal_admits_a_bare_kernel_value: a phantom-composed alias is transparent to its kernel type at a formal, and the harm is less the admission than that the alias reads as a proof and gets cited as one. Its receipt is the onestd/integer.dagalready carries (256 crossing aUInt8formal in a floor receipt), plus the wrong conclusion reached while writing this PR — thatList<UInt8>already proves per-member range — recorded because the inference is reasonable and nothing at the use site corrects it.The row explicitly distinguishes its three nearest neighbours, and marks the emitter-side counterpart as suspected and uncited: the reported
base16E0425 failure is second-hand, unreproduced, and the repair in flight removesUInt8from that module entirely — which removes the symptom without settling whether the defect was real. It namessharp-bear-756's lane as where that is being settled by execution, and records that an earlier "the emitter cannot lower a phantom Compose" reading was falsified by the error text (E0425 is an unresolved name; the emitter rendersUInt8fine and fails to import it).Coordination
A second lane (
sharp-bear-756) is adding roster entries to this sameadmit_callersdeclaration —base16_decode_lowerandsha256_hex, on the computed-in-range ground. Confirmed withneat-boar-16that their "sealed-octet carrier" is this samestd.integerQualifiedOctets, not a second concept, so there is nothing to consolidate.A roster is a replace of one list, not two appends, so git will resolve a concurrent edit by taking a side and silently dropping the other lane's members. Whoever merges second unions them by hand; both lanes have agreed to. A clean merge on this declaration is the failure signal, not the success signal.
The annotation fix above makes this worse in a way worth stating: there are now two things on this declaration a quiet merge can clobber. Taking the other side's hunk would drop members and reintroduce the counting sentences this PR removed — restoring an annotation that is false about the very roster it sits above, with no conflict raised. So after any merge of main: count the members, and re-read the annotation. Neither is checked by anything automatic.
🤖 Generated with Claude Code