Skip to content

fix(id): writer-set expansion — expand/expand_retro over group grants (spec slice 2 of #3789) #4998

Open

Description

@dezren39

Writer-set spec §9 item 2 (doc/2026-10-30T13-15-00Z_design_id_writer_set_view_projection/). Slice 1 shipped as #3854 (pkgs/id/src/idset.rs: WriterSet, Entry in all three shapes, Excludes, Rescind, canonical body encode/decode, sign/decode round-trip). This slice adds the expansion both later slices stand on.

Goal

expand(set_version)       -> BTreeSet<AuthorKey>
expand_retro(set_version) -> BTreeSet<AuthorKey>   // same expansion, narrowed to entries carrying include_history

Spec §3.1 and §4.1: retro is per entry, so one flagged friend must never import another's backlog — expand_retro(v) ⊆ expand(v) is an invariant, not an approximation.

What already exists (verified at main 0467ae0f9, 2026-11-03)

  • pkgs/id/src/idset.rs — the types and their canonical encoding; Entry carries include_history on every shape (§D3). Its module doc still ends: "This module is types and encoding only. Expansion, projection, and counting are later slices of the spec's routing (§9 items 2–4) and are deliberately absent here."
  • pkgs/id/crates/id-core/src/membership/layer.rs:442 — Membership::access_at_position(after, group, who), the frontier-aware membership check; :424 — is_member_at(author, group, &after).
  • pkgs/id/src/groups/delegation.rs:312 — a caller of access_at_position; pkgs/id/src/event_store/resolve.rs:397 — a caller of is_member_at.
  • pkgs/id/src/groups/fold.rs — the fold the two above read.

What to build

  • Expansion must go through is_member_at / access_at_position, not members(group) (§4.5): the answer is pinned to the set version's frontier, and a live roster is not that.
  • One-level flattening of group entries (§D3/D4) — a group entry expands to its members at the frontier; a nested group reference is not followed.
  • role_min filtering (§D3): include a member whose role is at least the entry's role_min at that frontier.
  • Exclusions win (§D6): an exclusion entry removes its target from the expansion regardless of which entry admitted them.
  • Cache, if any, keyed (group, frontier, R) (§4.5) — and the fork-invalidation test below is what proves the key is right.

Tests (spec §9 item 2, verbatim requirements)

  1. Expansion is order-independent — the same set with entries permuted yields the same set of authors.
  2. Expansion is frontier-pinned — a membership change after the set version does not change expand(v).
  3. Expansion is unchanged by a later group-membership event — the reproducibility guarantee, as a test with the event written after the pin.
  4. expand_retro(v) ⊆ expand(v), and a set with no include_history entries has expand_retro empty while expand is not.
  5. Fork proof that invalidates a group grant changes the expansion, and the cache does not serve the stale one (§4.5) — the test finding 23 added.
  6. role_min filters at the frontier: a member who holds the role later is not admitted by an earlier version.
  7. An exclusion removes an admitted writer, and an exclusion of someone never admitted is a no-op rather than an error.

Out of scope

AuthorEvents/retrieval (slice 3), Projection/counted/uncounted/validity_now (slice 4), public/sentinel views (Q1 on #2031), abuse budgets (#2032), app/UI rendering.

Acceptance

cargo test -p id green with tests 1–7 above present and named; no counting path enabled (expand* returns authors only); pkgs/id/src/idset.rs's module doc updated to say expansion now lives beside the types.

Parent: #3789 · spec §9 item 2 · slice 1: #3854

Acceptance criteria

  • expand(set_version) and expand_retro(set_version) exist in the id writer-set module, expanding through is_member_at / access_at_position pinned to the set version's frontier — never a live members(group) roster.
  • Group entries flatten exactly one level (§D3/D4); expand_retro(v) ⊆ expand(v) holds and is test-pinned, including the case where one entry's include_history must not import another entry's backlog.
  • The idset module doc no longer states that expansion is absent; slice 3/4 consumers can call both functions without further expansion work.

Activity

  1. added
    enhancementNew feature or request
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    priority/normalOrdinary priority (the default)
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Nov 3, 2026
  2. added
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    on Nov 3, 2026
  3. developing-today commented on Nov 3, 2026

    @developing-today
    Contributor

    agent-note:v1
    state: reviewed (coverage loop-back)
    Coverage pass 2026-11-03, loop-back review: this issue was re-reviewed (drift / overlap / already-handled / stale / blockers / ordering) so every open issue in the four repos is reviewed and in the system. Verdict: still live; in the system as task 8240 (small_fix, queued, priority 40) in repo-health-code-pilot. Labels audited and synced. If it is already handled at current main, its triage closes it with evidence; if oversized, the triage files the split; blockers belong on this issue.
    Found via: coverage loop-back 2026-11-03

  4. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    Intake review

    Fleet monitor intake (Duty 2), 2026-11-03. (Posted on a delayed retry: the original comment attempts were rejected by a GitHub secondary rate limit; the label/body enrichment itself landed during the run.)

    • Related: code#3789 (the writer-set spec; this is §9 item 2), code#3854 (slice 1 — the types/encoding this builds on, shipped).
    • Still live: the filer verified at main 0467ae0 that pkgs/id/src/idset.rs is still types-and-encoding only; expansion is deliberately absent pending this slice.
    • Notes: the body is a complete spec slice (goal signatures, the frontier-pinning constraint, the retro invariant). Acceptance criteria appended in the standard section; labels normalized (kind/priority/effort/status were missing).
    • Risks: the subtle failure is a live-roster read sneaking in where a frontier-pinned read is required — the criteria name the two sanctioned entry points explicitly.
    • Suggested approach: implement against Membership::access_at_position as the body directs; property-test the retro subset invariant.
  5. dezren39 commented on Nov 3, 2026

    @dezren39
    MemberAuthor

    agent-note:v1
    state: handed_off
    task: task 8240 (small_fix, repo-health-code-pilot, lane code-l)
    worker: repo-worker-code
    issue: #4998

    Execution lane assigned: opencode. Handed off to the laptop claimant, which launches the bound session to drive task 8240 (writer-set expansion, spec slice 2 of #3789) to its terminal state.

  6. removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 3, 2026
  7. 34 remaining items

  8. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    queue/claimedQueue worker lifecycle state
    and removed
    queue/claimedQueue worker lifecycle state
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 4, 2026
  9. dezren39 commented on Nov 4, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

  10. added
    queue/queuedHas a queued task in the Supabase agent queue
    and removed
    queue/claimedQueue worker lifecycle state
    work/assignedQueue worker lifecycle state
    on Nov 5, 2026
  11. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · handed off

    • Agent: fleet monitor
    • Session / ID: task 8240 (repo-fleet-monitor run 2026-11-05T15:45:48Z)
    • Found via: monitor reconciliation (Duty 1 claim/label divergence repair)
    • Task: Claim evaporated: task 8240 is queued and unclaimed in repo-health-code-pilot, but this issue still carried claimed labels. Labels reset to the truth (queue/queued + status/needs-triage). Checkpoint stage at evaporation: handed_off.
    • Expectation: The next lane claim picks task 8240 up from the queued state and posts a fresh pickup note.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/mMedium: expected under 1 day of workenhancementNew feature or requestkind/enhancementmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedmeta/protocol-gapAn agent touched this without the required agent-note comment or labels; needs review of whypriority/normalOrdinary priority (the default)queue/queuedHas a queued task in the Supabase agent queuesource/creatorFiled by the new-issue-creator jobstatus/needs-triageNot yet triaged; needs a first looktask/triageQueue packet kind: verify, reproduce, and route this itemuser/reviewNeeds a user-look: Muse reviews first and handles what it can

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions