Skip to content

feat(runtime): bump session.local_t via ResolveSessionForEnvelope (closes §M13 sub-program)#33

Merged
MSD21091969 merged 2 commits intomasterfrom
feat/m13-bump-session-local-t-resolver
Apr 25, 2026
Merged

feat(runtime): bump session.local_t via ResolveSessionForEnvelope (closes §M13 sub-program)#33
MSD21091969 merged 2 commits intomasterfrom
feat/m13-bump-session-local-t-resolver

Conversation

@MSD21091969
Copy link
Copy Markdown
Collaborator

Summary

Closes the §M13 sub-program session-actor-agent-lookup flagged at T=168 in kb/research/kernel/20260417-t187-kernel-proper.md line 430, surfaced concretely at T=174 ~00:45 CEST during Phase A on hp-laptop: 24 acknowledged Cowork rewrites left session:sam.laptop-cowork-workspace.local_t = 0 because the agent-actor inferred-session path skipped bumpSessionLocalT entirely.

The old guard in Apply (~line 173) and ApplyProgram (~line 293) only fired when env.Actor was itself a session node. Agent actors — which became the common case once §M11 inferred-session resolution went live — fell through.

What changes

  • New helper Runtime.resolveSessionForBump wraps operad.ResolveSessionForEnvelope and returns (sessionURN, ok) for the three resolver outcomes that should tick local_t (Explicit, Inferred, ActorIsSession), and ("", false) for the three that shouldn''t (Ambiguous, ExplicitMismatch, Absent).
  • Apply swaps the actorNode.TypeID == "session" check for resolveSessionForBump.
  • ApplyProgram swaps dedupe-by-actor for dedupe-by-resolved-session — a batch where two agents both occupy one session ticks once, batches spanning sessions tick each session once.

Multi-session safety is intrinsic: ambiguous envelopes never reach the bump path because §M11 rejects upstream; if one slips through (defensive), the resolver returns Ambiguous → no bump. Kernel-actor system-internal envelopes (sweep, infrastructure ADDs through the allowlist) take Absent → no bump, which is correct (they''re not session-scoped work).

Files

  • internal/kernel/runtime.go — 3 hunks: Apply path, ApplyProgram path, new helper after bumpSessionLocalT
  • internal/kernel/m13_localt_test.go — new file, 7 tests

Test plan

7 cases in m13_localt_test.go, all passing:

  • TestApply_BumpsLocalT_ActorIsSession — regression check on existing session-actor path
  • TestApply_BumpsLocalT_ActorIsAgent_Inferredthe fix — single-session agent emits without explicit SessionURN; reverse-lookup resolves; local_t ticks
  • TestApply_BumpsLocalT_ActorIsAgent_Explicit — agent emits with matching env.SessionURN
  • TestApply_NoBump_Ambiguous — agent occupies two sessions; gate rejects; neither session ticks
  • TestApply_NoBump_KernelActor — kernel-actor allowlist envelope; no session ticks
  • TestApplyProgram_DedupesBySessionNotActor — two-agent batch on one session ticks once
  • TestApplyProgram_DedupesAcrossAgents — two-agent batch on two sessions, each ticks once

go test ./... clean across all packages.

Post-merge — rebuild + restart sequence

  1. Z440 (Wolfram): rebuild moos-kernel binary; restart 4 kernels (hp-z440.{primary, lola, menno, moos}) via federation startup script. Verify each /healthz reports new binary tip.
  2. hp-laptop (Guido): pull moos-kernel; rebuild; swap binary; restart kernel:hp-laptop.primary. Verify /healthz + log replay clean.
  3. Verification emit: any single inferred-session rewrite from any agent should tick its session''s local_t. Cleanest test: a tiny MUTATE on the agent itself by claude-cowork on either host.
  4. Doctrine close: amend kb/research/kernel/20260417-t187-kernel-proper.md §M13 paragraph with this PR''s merge SHA + verification log_seq.

Cross-references

🤖 Generated with Claude Code

Closes the §M13 sub-program `session-actor-agent-lookup` flagged at T=168.
Surfaced concretely T=174 ~00:45 by Guido during Phase A: 24 acknowledged
Cowork rewrites left session:sam.laptop-cowork-workspace.local_t = 0
because the agent-actor inferred path skipped bumpSessionLocalT entirely.

The old code in Apply (~line 173) and ApplyProgram (~line 293) only
incremented local_t when env.Actor was itself a session node. Agent
actors — which is the common case once §M11 inferred-session resolution
went live — fell through. Result: heartbeat dead for almost all work.

Fix uses operad.ResolveSessionForEnvelope (already used by §M11 gate) to
identify the resolved session URN regardless of:
  - session-as-actor (existing path, regression-checked)
  - agent-as-actor with single has-occupant (inferred — the new fix path)
  - agent-as-actor with explicit env.SessionURN

Multi-session safety intrinsic: ResolveSessionAmbiguous returns no
session URN, so no bump fires. The §M11 gate would have rejected the
envelope upstream regardless.

Kernel-actor system-internal envelopes (sweep emissions, infrastructure
ADDs through allowlist) take ResolveSessionAbsent → no bump. Correct;
they aren't session-scoped work.

ApplyProgram dedupe key changes from env.Actor to the resolved session
URN, so a batch where two agents both occupy the same session ticks
local_t once (not twice).

Tests added in m13_localt_test.go cover all 7 paths:
  - actor-is-session (regression)
  - agent-actor inferred (the fix)
  - agent-actor explicit
  - ambiguous (gate rejects, no bump)
  - kernel-actor allowlist (no bump)
  - batch dedupe by session, not actor
  - batch ticks each session once when actors span sessions

go test ./... clean.

Doctrine note in ffs0: kb/research/kernel/20260417-t187-kernel-proper.md
§M13 paragraph cites this PR as the closing path (T=175 update).
Plan: ~/.claude/plans/valiant-kindling-sunrise.md Phase E.2.
Design: ffs0/dev/scripts/ops/t175-e2-kernel-pr-bump-session-local-t.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 25, 2026 13:13
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes session local_t not incrementing for agent-actor rewrites by resolving the session context via operad.ResolveSessionForEnvelope (explicit/inferred/session-actor), and applies the bump once per resolved session in batched programs.

Changes:

  • Add Runtime.resolveSessionForBump to map resolver outcomes to a (sessionURN, ok) decision for bumping local_t.
  • Update Runtime.applyWithOptions to bump local_t based on resolved session context (not just actorNode.TypeID == "session").
  • Update Runtime.ApplyProgram to dedupe bumps by resolved session (not by actor), and add §M13-focused tests.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
internal/kernel/runtime.go Uses session-context resolver to decide local_t bumps in Apply and ApplyProgram, and dedupes bumps by session in batches.
internal/kernel/m13_localt_test.go Adds tests covering explicit/inferred/session-actor bumping and batch dedupe behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/kernel/runtime.go Outdated
Comment on lines 171 to 177
// §M13: increment local_t for the resolved session, regardless of
// whether actor is the session itself, an agent inferring via
// has-occupant, or an explicit env.SessionURN. Closes the
// session-actor-agent-lookup sub-program from T=168.
if sessionURN, ok := rt.resolveSessionForBump(env); ok {
rt.bumpSessionLocalT(sessionURN)
}
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resolveSessionForBump() is called after rt.state has already been advanced to next. Because ResolveSessionForEnvelope depends on has-occupant relations, an envelope that changes occupancy (e.g., UNLINK/LINK rotating the actor out of their only session) can cause the post-apply resolver to return Absent/Ambiguous and skip bumping local_t, even though the envelope was accepted under §M11 using the pre-state session context. Consider resolving the session to bump against the pre-apply state (e.g., compute sessionURN before fold.Evaluate/rt.state=next and then bump using that captured URN).

Copilot uses AI. Check for mistakes.
Comment thread internal/kernel/runtime.go Outdated
Comment on lines 290 to 305
// §M13: bump local_t once per unique resolved session in the batch.
// Dedupe by session URN (not by actor) so a single batch from one agent
// to one session counts as one tick, while multi-session batches tick
// each session once. Closes the session-actor-agent-lookup sub-program
// from T=168.
{
seen := make(map[graph.URN]bool)
seenSessions := make(map[graph.URN]bool)
for _, env := range envelopes {
if !seen[env.Actor] {
seen[env.Actor] = true
if actorNode, ok := rt.state.Nodes[env.Actor]; ok && actorNode.TypeID == "session" {
rt.bumpSessionLocalT(env.Actor)
}
sessionURN, ok := rt.resolveSessionForBump(env)
if !ok {
continue
}
if !seenSessions[sessionURN] {
seenSessions[sessionURN] = true
rt.bumpSessionLocalT(sessionURN)
}
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApplyProgram resolves sessions for bumping local_t against rt.state after the whole program has been folded and committed. If the program itself rotates/changes has-occupant relations for the actor(s), the post-commit state can resolve to a different session (or Absent) than the session context that §M11 validated against the batch-initial state, leading to wrong/missed local_t bumps. Suggest collecting the sessions-to-bump during the preflight (or by resolving against the batch-initial state used for §M11) and then bumping those captured session URNs once after commit.

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +160
// TestApply_NoBump_Ambiguous — agent occupying TWO sessions emits without
// explicit SessionURN. §M11 gate rejects upstream (returns Ambiguous);
// neither session ticks. Defensive double-check at the bump path.
func TestApply_NoBump_Ambiguous(t *testing.T) {
rt := newM13Runtime(t)
agentURN := graph.URN("urn:moos:agent:claude")
sessionA := graph.URN("urn:moos:session:sam.a")
sessionB := graph.URN("urn:moos:session:sam.b")
injectOccupancy(rt, sessionA, agentURN, "agent")
injectOccupancy(rt, sessionB, agentURN, "agent")

env := graph.Envelope{
RewriteType: graph.ADD,
Actor: agentURN,
NodeURN: "urn:moos:program:x",
TypeID: "program",
}
if _, err := rt.Apply(env); err == nil {
t.Fatalf("ambiguous liveness should reject; got nil error")
}
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn’t a regression test covering an envelope/program that modifies occupancy (e.g., UNLINK/LINK rotating the actor out of their only has-occupant session) where session resolution can change between pre- and post-apply state. Adding a test that asserts the originating session still gets its local_t tick would help prevent subtle future breakage.

Copilot uses AI. Check for mistakes.
Addresses Copilot review on PR #33: resolveSessionForBump was running
after rt.state had been advanced to `next`, so envelopes that rotate
has-occupant (e.g. UNLINK + re-LINK) would resolve against post-state
and miss/misroute the bump despite §M11 having validated against
pre-state.

Apply path: capture sessionURN BEFORE rt.state = next; bump after
commit using captured URN.

ApplyProgram path: capture all sessions-to-bump against batch-initial
rt.state (mirroring §M11 preflight semantics — both checkLivenessM11
and the bump now resolve against the same view), populate the dedupe
set, then bump after commit.

Adds TestApply_BumpsLocalT_RotatesOccupancy: emits an UNLINK that
removes the only has-occupant edge between actor and session and
asserts that local_t still ticks. Without the pre-state capture the
test fails with local_t=0 because post-state resolution returns Absent.

Test registry extended with WF19 declaration so UNLINK validation
passes; mirrors v3.10+ D19.1 grammar_fragment shape minimally.

go test ./... clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MSD21091969
Copy link
Copy Markdown
Collaborator Author

Thanks @copilot — caught a real correctness bug. Pushed 069aafa addressing all three points:

Pre-vs-post state for resolution (lines 177 + 305): Both Apply and ApplyProgram now resolve session URNs against the pre-apply state, matching §M11 preflight semantics. Apply captures bumpSessionURN before the rt.state = next assignment. ApplyProgram populates sessionsToBump against rt.state (batch-initial, same view §M11 used) before commit, then bumps after commit.

Regression test for occupancy rotation: added TestApply_BumpsLocalT_RotatesOccupancy. Emits an UNLINK of the actor''s only has-occupant edge and asserts that local_t still ticks. Without the pre-state capture, the test fails because post-apply resolution returns Absent (the edge is gone). With the fix, the originating session gets credited.

The test registry needed a minimal WF19 declaration so the UNLINK validates — added with AllowedRewrites: [LINK, UNLINK] and has-occupant/is-occupant-of ports. Mirrors the v3.10+ D19.1 grammar_fragment shape.

go test ./... clean. PR ready for human review.

Copy link
Copy Markdown

Copilot AI commented Apr 25, 2026

Thanks @copilot — caught a real correctness bug. Pushed 069aafa addressing all three points:

Pre-vs-post state for resolution (lines 177 + 305): Both Apply and ApplyProgram now resolve se...

Confirmed — 069aafa is correct. Pre-state capture lands in both Apply (before rt.state = next) and ApplyProgram (before rt.state = nextState). TestApply_BumpsLocalT_RotatesOccupancy fails without the fix and passes with it. go test ./... clean across all packages.

@MSD21091969 MSD21091969 merged commit b1de4ff into master Apr 25, 2026
1 check passed
@MSD21091969 MSD21091969 deleted the feat/m13-bump-session-local-t-resolver branch April 25, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants