LearnNewsExamplesServices
Frontmatter
id16312
titleThe tenantRepoSync starvation invariant is prose-only: starvedAfterMs must exceed backoffCapMs, and nothing checks it
stateClosed
labels
bugai
assigneesneo-kimi-iris
createdAtAug 2, 2026, 1:20 AM
updatedAtAug 2, 2026, 3:46 AM
githubUrlhttps://github.com/neomjs/neo/issues/16312
authorneo-opus-grace
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 2, 2026, 3:46 AM

The tenantRepoSync starvation invariant is prose-only: starvedAfterMs must exceed backoffCapMs, and nothing checks it

neo-opus-grace
neo-opus-grace commented on Aug 2, 2026, 1:20 AM

Context

Surfaced in my Approve+Follow-Up review of PR #16307 (merged 2026-08-01T23:18:26Z, resolving #16224). I left the follow-up to the author; @neo-kimi-iris has been unreachable all day (route published at 17:00:15 against a receiver booted 16:21:18, so outside its snapshot until a host-side reload), so she almost certainly never saw the review. Filing it rather than letting it fall through that gap.

Live latest-open sweep at 2026-08-02T00:10Z; search sweep on starvedAfterMs / backoffCapMs / ordering invariant across all states returned no equivalent.

The Problem

#16307 added two orchestrator.tenantRepoSync leaves whose JSDoc states a required ordering between them:

starvedAfterMs"Must exceed backoffCapMs so a lane whose capped retries keep failing (fresh attempts, visible failed sweeps) stays quiet — a stale suppression means a wedged lane, not an ordinary outage."

Both are env-overridable (NEO_ORCHESTRATOR_TENANT_REPO_SYNC_BACKOFF_CAP_MS, NEO_ORCHESTRATOR_TENANT_REPO_SYNC_STARVED_AFTER_MS). Nothing rejects, warns about, or pins the ordering — grepping for the two symbols co-occurring outside comments finds only the destructure at the call site.

The shipped defaults are correct (2h cap, 6h floor), so nothing is wrong today. The gap is that the requirement lives only in prose.

Consequence when inverted, i.e. starvedAfterMs < backoffCapMs: a lane in ordinary capped backoff can cross the duration floor and emit a heal-ledger event for a transient mirror or credential outage — exactly the false positive the JSDoc's reasoning exists to prevent. The fix still works; the alert stops meaning what it claims, which is how alerts get ignored.

The Architectural Reality

  • ai/configBase.mjs — the two leaves, declarative and ADR-0019-clean; the constraint is a relationship between them, which no single leaf can express.
  • ai/daemons/orchestrator/scheduling/tenantRepoSync.mjsdetectStarvedTenantSync consumes starvedAfterMs; isRepoDue consumes backoffCapMs. Neither sees the other, correctly: they are independent pure predicates.
  • The existing spec suite covers each knob thoroughly in isolation (cap clamping, cross-restart purity, 0 back-compat on both, status-vs-event separation). No test asserts the relationship.

The Fix

(Prescription — inference, not observation.)

Cheapest sufficient version: a spec asserting the shipped defaults satisfy starvedAfterMs > backoffCapMs, so a future tuning that inverts them turns the suite red. This is a config-invariant test, not new runtime behaviour.

Stronger, and the author's call: validate the ordering where the values are resolved, and log a warning rather than throwing — an operator who deliberately inverts them should not lose the sync lane over an alerting preference.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
starvedAfterMs / backoffCapMs ordering the leaf JSDoc in #16307 the documented ordering is mechanically checked never throw — a misordered pair degrades alert quality, it does not break sync; warn and proceed the JSDoc already states it; the check makes it enforceable inverting the pair fails a test rather than silently degrading the heal-ledger signal

Acceptance Criteria

  • A test fails if the shipped defaults are ever changed so that starvedAfterMs <= backoffCapMs.
  • If runtime validation is added, a misordered pair warns and proceeds — it never throws and never disables the sync lane.
  • starvedAfterMs: 0 (event disabled) remains exempt, since 0 is the documented disable value and is not a misordering.

Out of Scope

  • The backoff and starvation mechanics themselves — shipped and specced in #16307.
  • A general config-invariant framework. If several cross-leaf relationships accumulate, that becomes its own proposal; one instance does not justify machinery.

Avoided Traps

  • Throwing on a misordered pair. It would convert an alerting-quality preference into a hard failure of the sync lane, which is a strictly worse outcome than the noisy alert it prevents.
  • Treating starvedAfterMs: 0 as a violation. 0 disables the event by design (#16307 specs this explicitly) and must stay exempt.
  • Pushing the check into either pure predicate. They are independent by design and neither should learn about the other; the relationship belongs where the values are resolved.

Related

  • #16224 / PR #16307 — the lane that introduced both leaves
  • #16223 — sibling never-advancing-state ticket in the same family
  • ADR-0019 — config SSOT authority; the leaves themselves are compliant, this is about a relationship no single leaf can hold

Origin Session ID: 713db0da-2239-44ea-ba5b-931be90d34fc

Retrieval Hint: query_raw_memories("starvedAfterMs must exceed backoffCapMs unenforced ordering invariant tenantRepoSync").

tobiu referenced in commit 2c7f40a - "feat(ai): guard the tenant-repo-sync starved-leaf ordering (#16312) (#16327)" on Aug 2, 2026, 3:46 AM
tobiu closed this issue on Aug 2, 2026, 3:46 AM