Skip to content

Follow-up: deferred review findings from PR #945 #970

Description

@philcunliffe

Deferred from PR #945 (triage after 2 review rounds at head a4d4670046cf94aa1868c6b13065d9aa6ab033b9). One residual finding, reported as round 1 finding 1 and re-confirmed standing in round 2; recorded in llp/0289-sync-lane-asks-the-store-about-hidden-picks.decision.md §not-done.

Finding: on a degraded wizard run, a stale opt-out for a hidden central row makes the sync lane print "nothing syncs" while the daemon ships the row

The lane's hidden-pick check is the policy store's answer, not the export seam's:

  • src/core/cli/wizard/sync_scope.js:97-99: optedOutAll = new Set(optedOutClientSourceIds(existing)); a hidden pick with any standing local-only entry reads as withheld.
  • src/core/runtime/source_withhold.js:163: the seam withholds optedOutClientSourceIds(entries).filter((id) => !centralIds.has(id)), so an entry for a central-classified source is inert and the row ships anyway.

The equation "a hidden pick is non-central by construction" holds only while the wizard's locked set actually resolved. Two paths return [] deliberately:

  • computeLockedSafe swallows a layered-resolution failure (src/core/cli/wizard/index.js:1102, LLP 0182).
  • a converge timeout returns { status: 'ok', lockedSources: [] } (src/core/cli/wizard/join.js:112, LLP 0129).

Concrete run: the user writes hyp policy client <source> local-only before the org adopts the source (allowed; adoption never clears an existing entry). The org adopts it, so it is central and hidden. On a re-run whose locked resolution fails, the carried hidden row lands in candidatesHiddenIds, reads as withheld, and the lane prints "You picked nothing to record, so nothing syncs to your server." while the daemon, resolving the same config successfully at boot, drops the inert entry and exports every row. A false claim in the LLP 0188 #never-silent direction, on a privacy-facing screen.

Why it was deferred, not fixed

The wizard cannot subtract central ids on the very run where computing them is what failed, and it cannot treat "no locked rows at all" as the degraded signal, because that is also the healthy shape of an enrolled machine whose org configures nothing (the exact case LLP 0289's main test covers). The corner needs a resolution failure, a stale entry, and a hidden central row at once.

What would close it

Give the sync lane a way to tell "no central rows" from "could not tell": a signal from the orchestrator (e.g. a lockedResolved/degraded flag alongside lockedSources) that the lane can use to fall back to the conservative "still syncs" sentence when the central set is unknown. Introducing that signal is a design decision about the degraded path, so it should get its own LLP (extending LLP 0289 §not-done, and touching LLP 0182 / LLP 0129 rationale) before the code change.

Backlink: PR #945.

Metadata

Metadata

Assignees

No one assigned

    Labels

    neutral:fixDelegate this issue to neutral for an autonomous fix attempt (reproduce -> fix -> PR)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions