Skip to content

fix(core): forking a child session launders it out of ancestor governance — fork is projected top-level (parent_id null), switches and descendants escape policies above the source (PR #468) #505

Closed

Description

@dezren39

Finding

PR #468's spec (§6.2, specs/2026-10-25-subsession-model-governance.md:272) states: "Session.fork creates a child whose parent is the source session." The implementation does the opposite, and the difference silently removes governance from a whole subtree:

  • packages/core/src/session/projector.ts (~:160-172): the forked session is inserted with parent_id: null (fork_session_id records the source instead), copying only the source's own subagent_policy.
  • The one-time fork check in packages/core/src/session.ts validates only the copied model at the fork instant, against the source as parent.

Consequences, all reachable by forking a child session (POST /api/session/:id/fork):

  1. The fork is top-level, so switchAgent/switchModel skip governance entirely — both guard on session.parentID (packages/core/src/session/session.ts, child-only checks added by PRs feat(core): subsession model governance #468/fix(core): run the governance check on switchAgent for child sessions #486). The fork can be switched to any excluded model unchecked.
  2. The fork's own children are governed only by the copied policy: the governance (and depth) walks follow parentID only and never traverse fork_session_id, so every ancestor policy above the source — e.g. the primary session's models/excludedModels — silently drops out of the subtree.

Forking a governed child therefore launders it out of its ancestor policies: same agent, same model, same history — minus the restrictions.

Evidence

Verified on current main ffbc856: projector insert (parent_id: null, fork_session_id: event.data.parentID, subagent_policy: parent.subagent_policy), spec §6.2 text, the fork-time check in packages/core/src/session.ts, and the parentID guards in packages/core/src/session/session.ts. Existing fork governance tests (packages/core/test/session-governance.test.ts:167/186) cover forking a session whose own policy excludes its model, and policy copying — not forking a child out from under a grandparent's policy.

Acceptance criteria

  • Forking a child session does not escape ancestor governance: either the fork stays linked into the governance chain (its switches and its descendants' creation are checked against the source's ancestors' policies as well as the copied policy), or the escape is an explicitly documented, spec-amended decision with the fork-time check applying the full ancestor chain.
  • A regression test forks a grandchild under a primary policy that excludes its model and shows the fork cannot be switched to — or launched with children on — the excluded model (or documents the accepted behavior if the design decision goes the other way).

Filed by the pr-diff-review job, 2026-10-26 run (addendum 3: governance reviewer final report reconciled and re-verified by the parent; this corrects the run's first-pass fork disposition, which had verified only the fork-time check).

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/highImportant and time-sensitive
    effort/mMedium: expected under 1 day of work
    meta/enrichedIntake review done: acceptance criteria, links, and normalized labels added
    on Oct 26, 2026
  2. dezren39 commented on Oct 26, 2026

    @dezren39
    Author

    Intake review

    Still live: verified by the filer on current main ffbc8562f2 (filed 2026-10-26, pr-diff-review): the fork projector still inserts parent_id: null. No open PR addresses fork governance. Acceptance criteria were already present, so the body is unchanged.

    Related: PR #468 (governance, spec §6.2 states the opposite of the implementation); PR #486 (child-only switch checks the fork escapes); sibling governance holes from the same review: #506, #507, #508.

    Notes / risks: this is a governance escape, not just a spec mismatch — forking a governed child drops every ancestor policy above the source for the fork and its whole descendant subtree. Priority set to high for that reason. The criteria allow either linking the fork into the chain or a documented spec amendment; the silent status quo is the one unacceptable outcome.

    Suggested approach: walk the governance chain through fork_session_id when parentID is null on a fork (or set the fork's parent linkage for governance purposes), and add the grandchild-fork regression test from the criteria.

    Labels normalized by the fleet monitor: kind/bug, source/pr-diff-review, status/needs-triage, priority/high, effort/m, meta/enriched.

  3. dezren39 commented on Oct 27, 2026

    @dezren39
    Author

    🤖 Agent note · state: triaged (verified still present)

    • Agent: issue sweep (issue-sweep-rotating)
    • Session / ID: issue-sweep-rotating run 2026-10-27 10:27 CDT
    • Found via: sweep triage — top unqueued priority/high item in the backlog
    • Task: Re-verify the finding against current main before any fix is attempted.
    • Expectation: effort/m fix remains owed; this note only confirms the bug is live.

    Verified on current main f1e0c58 (fresh read of the sandbox clone, fast-forwarded this run):

    • packages/core/src/session/projector.ts (~:158-172) — the fork insert still writes parent_id: null with fork_session_id: event.data.parentID, copying only the source session's own subagent_policy, agent, and model. Ancestor policies above the source still drop out of the forked subtree, and the fork's own switches still skip the child-only governance guards.

    Conclusion: NOT fixed upstream, no open PR touches projector.ts. Remains a valid priority/high fix; left in needs-triage for a queue/sweep fix run (effort/m, not a trivial self-solve). Related: #507 (same PR #468 governance family).

  4. dezren39 commented on Oct 28, 2026

    @dezren39
    Author

    State: triaged (verified still present) + fix routed
    Agent: issue sweep (issue-sweep-rotating)
    Session/ID: issue-sweep-rotating run 2026-10-28 16:24 CDT
    Found via: sweep triage — priority/high unqueued item in the backlog
    Task: re-verify on current main and route the fix (previous triages confirmed but never enqueued a fix packet)
    Expectation: effort/m fix via queue task 2725 (packet-505-fix.json, repo-health-opencode-pilot)

    Verified on current main 0b51b19 (fresh sandbox read): packages/core/src/session/projector.ts:160-161 still inserts the fork with parent_id: null and fork_session_id: event.data.parentID; the governance ancestor walk in model-governance.ts follows parentID only. Bug live, unchanged since the 2026-10-27 verification on f1e0c58. Packet pins the governance-link shape (spec 6.2 already decided) and the grandchild-fork regression test; the "document the escape" alternative was not routed.

  5. added
    queue/queuedHas a queued task in the Supabase agent queue
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedAn agent holds this item via the queue (claimed task)
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 28, 2026
  6. dezren39 commented on Oct 28, 2026

    @dezren39
    Author

    Progress (task 2725): PR #775 opened — #775

    Implemented the governance-link shape (the issue's option (a); projection untouched — forks still insert with parent_id: null):

    • model-governance.ts: the assertAllowed ancestor walk follows parentID ?? fork.sessionID, so policies above the source bind the fork and its descendants.
    • session/session.ts: switchAgent/switchModel gate on that governance parent, so a fork's own switches are checked against its source's chain; genuinely top-level sessions stay unchecked.
    • subagent-depth.ts: the depth walk follows the same link (a fork hop counts as one level).
    • New regression test in session-governance.test.ts: forking a grandchild under a grandparent test/haiku-only policy — the fork cannot switch to or launch children on test/opus, and can launch on test/haiku.

    Task 2725 is parked awaiting CI on PR #775; on green it merges and closes this issue.

  7. dezren39 commented on Oct 28, 2026

    @dezren39
    Author

    Triage confirmation — 2026-10-28 (queue task 2733)

    Packet numbering correction: queue task 2733 was enqueued as "triage #511", but its body, evidence, and context refs describe this issue, #505. #511 is an unrelated, already-merged PR ("apply Selection segment rules in v1 model migration", fixes #503) — verified via the GitHub API, search, and the web page on 2026-10-28. Nothing about #511 needs triage. The triage below is for #505, the issue the packet actually describes.

    Confirmed still live: #505 is OPEN; the fork-governance hole it describes is not fixed on current main (5e77693704) — the fix exists only on the unmerged PR branch below. Not a duplicate of #507 (model-less child checked vs run model; fix in flight as PR #776) or of closed siblings #506/#508 — distinct mechanism: fork projection sets parent_id: null, so governance walks that follow parentID only miss every ancestor policy above the fork's source, and the fork's own switchAgent/switchModel take the top-level gate.

    Priority confirmed: priority/high is correct — this is a governance escape for a fork and its whole descendant subtree, not a spec nit.

    Fix already in flight — no new fix task needed: PR #775 "fix(core): govern forks as children of their source session" (Fixes #505, branch queue/2725-505-fork-governance, mergeable, CI pending at triage time) is bound to queue task 2725, which holds this issue's queue/parked + work/assigned labels. Enqueuing another fix task would duplicate task 2725.

    Status label: status/needs-triage → status/in-progress to match the in-flight fix.


    agent-note:v1 · agent: repo-queue-worker-shift-opencode-b · task: 2733 · issue: #505 · action: triage-confirmed

  8. removed
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedAn agent holds this item via the queue (claimed task)
    on Oct 28, 2026
  9. dezren39 commented on Oct 28, 2026

    @dezren39
    Author

    Merged: PR #775 (merge commit a9452a1) — forks are now governed as children of their source session; session-governance suite 8/8 on the laptop including the new grandchild-fork regression. CI note: unit (linux/windows) failed only on two TUI permission-focus tests ('permission layers leave the focused peer's Enter and navigation alone until activated', 'permission rejection text keeps its draft and regains focus when its scope resumes') that fail identically on unrelated PR #776 — a pre-existing main issue, log-verified, not caused by this change. All other checks green (typecheck, build x2, nix-eval, affected packages).


    agent-note:v1 · agent: repo-queue-worker-shift-opencode-b · task: 2725 · issue: #505 · action: merged · pr: #775

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    effort/mMedium: expected under 1 day of workkind/bugSomething isn't working (prefixed form of the classic bug label)meta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/highImportant and time-sensitivesource/pr-diff-reviewFiled by the pr-diff-review job

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions