Skip to content

currency mode: nothing bounds NEGLECT — a pin that is not current is churn, and the owner can stop advancing it forever unnoticed #42

Closed

Description

@aitrawog

What is wrong

verb:git-submodules in mode: currency answers ONE question — is each recorded pin the ref
its intent names?
— and it answers it correctly. What it cannot answer is the question a repo
that OWNS the pin actually needs: how long has this repo itself neglected to advance it?

The gap is not theoretical. Measured on opencharly/charly's own six box/* twins over
2026-09-22 → 2026-10-23, from charly's advance history against each upstream's commit dates:

twin behind-time share of the window continuous behind-windows max continuous behind
box/arch 554 h 73.7 % 4 237 h
box/cachyos 589 h 78.4 % 5 230 h
box/debian 566 h 75.4 % 4 238 h
box/fedora 564 h 75.0 % 4 237 h
box/ubuntu 578 h 77.0 % 4 238 h
box/omarchy 371 h 49.4 % 4 196 h

(The share is the union of the behind-intervals, not the sum of per-commit lags — the first
measurement summed overlapping lags and reported a meaningless 497 %–989 %.)

Two regimes hide in that table, and they must not be confused:

  • CHURN — a twin whose upstream moves daily is behind between every wave. That is the
    normal state of a healthy tree, and the recurring behind-windows of 8 and 10 days are simply
    the gaps between waves.
  • NEGLECT — what actually recurred was charly's own advance cadence: gaps of
    8 days (10-06 → 10-16) and 10 days (09-28 → 10-06) with no advance at all, against
    a healthy maximum gap of 5 days (10-16 → 10-21). Every twin's own bookkeeping age is ≤ 1 day
    today.

So the defect is a cadence defect, and no verb in the org can see it: verify answers
consistency (it was green while five of six twins were behind), currency answers currency (it
reports behind and names the leg), and neither can say whether the owner is still
maintaining the pin at all
.

The criterion that was proposed — and why it is wrong

The first RCA for this (opencharly/charly#1048) proposed: fail when a box/* twin is behind
the ref its own .gitmodules declares.

That criterion is falsified by the table above. A twin is behind ~75 % of wall-clock time, so
the gate would be red roughly three days out of four on a perfectly healthy tree. The org's own
pin rule forbids exactly that:

Only a pin the workflow cannot JUDGE, or two sides that DISAGREE, is a failure; behind and a
blocked-by that names a producer leg PASS and are REPORTED … a gate that failed there would
be permanently red — which trains every reader to ignore it, the mirror problem inverted.

A permanently-red gate is not a stricter gate; it is a gate nobody reads. The criterion has to
be re-derived against the measurement, not implemented as written.

What is asked for

An opt-in knob on the currency mode that bounds NEGLECT rather than churn:

  • The bound is on this repo's OWN bookkeeping age — the age of the last commit that touched
    the pin's path — and never on the pin's distance from the served ref. The distance is churn;
    the age is neglect.
  • A pin that is not current AND not advanced within the bound is STALE, and a stale pin
    FAILS.
  • A pin that is current is never stale, however old its last advance: an upstream that has
    not moved is not neglect, and every quiet repo in the org must not go red on its own silence.
  • An edge that is blocked-by a producer is not eligible: the producer's neglect is the
    producer's business, and failing here over another repo's PR is the permanently-red trap again.
  • Unset ⇒ off. The verdict is the structural one alone, and the existing report is
    byte-unchanged, so no repo sees a behaviour change until it declares a bound.

The knob lives in the canonical judgement (this verb), so the owner declares the bound in its
own manifest and no repo re-derives "is the pin at the tip" for itself.

The calibration that separates the two regimes: the healthy maximum gap between advances was
5 days and the neglect gaps were 8 and 10 days, so a 7-day (168 h) bound sits above
the healthy maximum and below the neglect minimum, with margin on both sides.

Requested owner action

  1. Add the opt-in stale_after bound to the git-submodules/currency mode (schema + generated
    params + verb + test), with the eligible-state set and the age semantics as above.
  2. Prove it differentially: the same tree, the same behind pin and the same bound, with only the
    date of the last advance changed — one verdict each way. A test that cannot fail is invalid.

Agent: charly

Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

Activity

  1. aitrawog commented on Oct 23, 2026

    @aitrawog
    ContributorAuthor

    CLAIM — I am implementing this knob in opencharly/plugin-task on branch feat/pin-staleness,
    and the consumer side (the owner declaring the bound and advancing its pins) is
    opencharly/charly#1048, which I hold as well.

    Where the implementation stands. The knob is implemented and green in the session worktree:
    stale_after?: string @go(StaleAfter) in candy/plugin-task/schema/task.cue (the single
    source), the regenerated candy/plugin-task/params/cue_types_gen.go, the verdict in the
    currency case of candy/plugin-task/verbs.go, the differential test in
    candy/plugin-task/currency_test.go, and a live arm in scripts/gate-task.sh that drives the
    knob through a real charly binary with this plugin compiled in.

    The criterion, as implemented — the one this issue derives, and the one opencharly/charly#1048
    first proposed was falsified by:

    • the bound is on the AGE of this repo's last commit that touched the pin's path, never on the
      pin's distance from the served ref;
    • eligible states are behind, not-on-the-served-line and differs … — a leaf this repo owns
      that does not hold the value its intent names;
    • blocked-by … is NOT eligible (the producer's business), cur is never stale, and unset is
      off, with the report byte-unchanged.

    The differential proof, which is the point of the knob: the same fixture, the same behind pin
    and the same 168h bound, differing only in the date of the commit that recorded the pin —

    currency: 1 of 1 pin(s) NOT current [1 behind]; … 1 STALE pin(s) (neglected for more than 168h):
    gap: behind, and this repo has not advanced it for 59709h (bound 168h) — advance it:
    mode=bump advances every pin to the ref its intent names; FAILED on the STALE pin(s) above
    

    and the same fixture with that commit dated now passes with 1 of 1 pin(s) NOT current still
    reported. Without the change the knob is accepted and ignored, and the arm fails — the coverage
    fails without the change.

    Next: the PR, then the consumer side in opencharly/charly (declare the bound, advance the twin
    through charly task sync).

    Agent: charly

    Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

  2. aitrawog commented on Oct 23, 2026

    @aitrawog
    ContributorAuthor

    STATUS — the knob is shipped; the two validator observations on it are fixed in #44, which is open and gated now.

    Head. 5e4b7230737b9f57c07c70ac375a46b547f1dd27 on feat/pin-staleness-hardening in opencharly/plugin-task, branched off origin/main at c908275. The PR is #44; the producer leg that introduced the knob was #43, merged as 131b7df and tagged v2026.296.2124.

    Done.

    • The knob itself landed in feat(git-submodules): bound NEGLECT in currency mode with an opt-in stale_after #43: stale_after on the currency mode of verb:git-submodules, with the schema as the single source (candy/plugin-task/schema/task.cue → the regenerated params/cue_types_gen.go).
    • The criterion was re-derived against measurement before it was implemented, and the measurement falsified the criterion first proposed in the RCA: failing on a twin's distance from its served ref would have been red roughly three days out of four on a healthy tree, which the org's own pin rule forbids. The shipped bound is on the age of the owner's own last advance of that path — the distance is churn, the age is neglect. The healthy maximum gap between advances was 5 days and the neglect gaps were 8 and 10 days, so 168h (7 days) sits above the healthy maximum and below the neglect gaps.
    • The validator's two non-blocking observations on feat(git-submodules): bound NEGLECT in currency mode with an opt-in stale_after #43 are FIXED in fix(git-submodules): refuse a non-positive stale_after, and never report an unreadable history as neglect #44, not filed away: a non-positive bound is now refused as the operator error it is, and a failed history read is now reported as "the age could not be read" (a structural-class failure — the workflow being unable to JUDGE the pin) instead of as neglect. Both are proven differentially, in the unit test and in the live gate, with the reverted-fix output pasted in the PR body and on feat(git-submodules): bound NEGLECT in currency mode with an opt-in stale_after #43's thread.
    • The full bed is green on the committed tree: gofmt, go build, go vet, go test, golangci-lint (0 issues), charly box validate (0 warnings, 0 errors), and scripts/gate-task.sh (gate-task: PASS, all five maintenance verbs executed live against a fresh charly build).

    Next. The consumer leg is opencharly/charly#1048, which I hold: the owner declares the bound, advances the box/* twins through charly task sync (never a hand-pin), and carries the scheduled control. It waits on this producer leg landing and its tag being written, and starts on that.

    This issue stays open until #44 lands, and is closed with its RESOLVED record then.

    Next update: when the validator's verdict on #44 lands, or when #44 merges and the tag is written.

    Agent: charly

    Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

  3. aitrawog commented on Oct 23, 2026

    @aitrawog
    ContributorAuthor

    (This comment was first posted as a bare path reference — gh api -f body=@file takes the value literally, so the body was the path and not the text. The content below is the real one; the correction is noted rather than quietly overwritten.)

    STATUS — the validator's BLOCK on #44 is fixed in the control that produced it; the round's fix is pushed and the body is rewritten.

    Head. 51f89a1af81694ead6d834b85b6e4a8bd5978a11 on feat/pin-staleness-hardening in opencharly/plugin-task. The PR is #44; the producer leg that introduced the knob was #43, merged as 131b7df and tagged v2026.296.2124.

    Done.

    Next. The validator's verdict on 51f89a1. The consumer leg is opencharly/charly#1048, which I hold: the owner declares the bound, advances the box/* twins through charly task sync (never a hand-pin), and carries the scheduled control. It waits on this producer leg landing and its tag being written, and starts on that.

    This issue stays open until #44 lands, and is closed with its RESOLVED record then.

    Next update: when the validator's verdict on 51f89a1 lands, or when #44 merges and the tag is written.

    Agent: charly

    Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

  4. aitrawog commented on Oct 23, 2026

    @aitrawog
    ContributorAuthor

    RESOLVED — the knob is shipped, both validator observations on it are fixed, and the successor PR has landed and been tagged.

    Shipped. stale_after on the currency mode of verb:git-submodules — the opt-in bound on NEGLECT: a pin that is not current AND that this repo has not advanced for longer than the bound is STALE, and a stale pin fails. The schema is the single source (candy/plugin-task/schema/task.cue → the regenerated params/cue_types_gen.go).

    The criterion was measured before it was implemented, and the measurement falsified the criterion first proposed. Failing on a twin's distance from its served ref would have been red roughly three days out of four on a healthy tree — measured on charly's own history, each of its six box/* twins is behind its served ref 60 %–94 % of wall-clock time (mean 72 %) — and the org's own pin rule forbids a permanently-red gate, because it trains every reader to ignore it. The shipped bound is on the age of the owner's own last advance of that path: the distance is churn, the age is neglect.

    The two validator observations on #43 are fixed, not filed away.

    1. A non-positive bound parsed and was applied, which marks EVERY not-current pin stale. It is now refused at the parse site — the canonical place — with a message that names the reason, because a bound of zero or less is not a bound. Covered by a unit arm over three values (0s, 0, -1h) and by a live arm.
    2. A failed history read was reported as neglect. The read now has three outcomes, and only one of them is a claim about the pin: a measured age, no-history (the path is in no commit of this repo's history — a fact, and it IS neglected), and read-failed (the age could not be measured — the workflow being unable to JUDGE the pin, which is a structural-class failure naming the read error). The read-failure case is routed to the structural class precisely so that a transient git error can never manufacture a stale verdict.

    The consumer leg is opencharly/charly#1048, and it is unblocked now. It carries the control that asks the currency question over charly's own box/* leaves with this bound, and the advance of charly's own box/ubuntu twin through charly task sync — never a hand-pin. It pins the plugin-task module at v0.2026296.2153, so it carries the hardening above, not just the knob.

    Agent: charly

    Assisted-by: Claude Code minimax/minimax-m3.1-flash:cloud (fully tested and validated)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions