feat(tasks): the hand-off — SessionEnd releases a session's claims; the practice is written down (milestone 381 step 4)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m40s
CI & Build / Build & push image (push) Successful in 24s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 12s
CI & Build / TypeScript typecheck (push) Successful in 52s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m40s
CI & Build / Build & push image (push) Successful in 24s
Release is the mechanical half: scribe_session_end.sh sends the ending
session's id to /api/plugin/release-session, which releases the claims it
held. A tidy-up, not the guarantee (no SessionEnd on a crash; the lease
covers that), and skipped on /clear so SessionStart(clear) can still
hand the claimed work back.
Saying what happened is the half only the model can do. It is stated as a
practice where it is read: the using-scribe skill owns it ("Hand off before
this session's context stops existing", pinned in test_guidance_ownership),
the static context points at it for the wrap-up moment, and add_task_log's
docstring says a log claims the task. _INSTRUCTIONS is untouched.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -256,3 +256,30 @@ async def test_session_start_after_a_compaction_carries_the_claimed_task(users):
|
||||
owner, source="compact", session_id="sess-compact"))["context"]
|
||||
assert "claimed then compacted" in ctx
|
||||
assert "the migration is written" in ctx
|
||||
|
||||
|
||||
# --- step 4: the hand-off ----------------------------------------------------
|
||||
|
||||
def test_session_end_releases_claims_but_not_on_clear():
|
||||
"""The mechanical half of the hand-off. A /clear keeps the claim, because
|
||||
SessionStart(source=clear) pushes the claimed work straight back."""
|
||||
hooks = json.loads((ROOT / "plugin/hooks/hooks.json").read_text())["hooks"]
|
||||
commands = [h["command"] for b in hooks.get("SessionEnd", []) for h in b["hooks"]]
|
||||
assert any("scribe_session_end.sh" in c for c in commands)
|
||||
text = (ROOT / "plugin/hooks/scribe_session_end.sh").read_text()
|
||||
assert '[ "$reason" = "clear" ] && exit 0' in text
|
||||
assert "/api/plugin/release-session" in text
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
async def test_ending_a_session_releases_only_that_sessions_claims(users):
|
||||
owner, _ = users
|
||||
kept = await notes_svc.create_note(owner, title="other session's", status="in_progress")
|
||||
gone = await notes_svc.create_note(owner, title="ending session's", status="in_progress")
|
||||
await tc.bind_session(owner, kept.id, "sess-stays")
|
||||
await tc.bind_session(owner, gone.id, "sess-ends")
|
||||
|
||||
assert await tc.release_session(owner, "sess-ends") >= 1
|
||||
assert (await notes_svc.get_note(owner, gone.id)).claimed_at is None
|
||||
assert (await notes_svc.get_note(owner, kept.id)).claim_session == "sess-stays"
|
||||
assert await tc.release_session(owner, "") == 0
|
||||
|
||||
Reference in New Issue
Block a user