Skip to content

test(continuation): add static allowlist for session-keyed volatile Maps (#441 / swim-39 OV-5)#505

Merged
ronan-dandelion-cult merged 1 commit intocael/325-canonical2from
frond-scribe/441-volatile-map-allowlist-test
May 2, 2026
Merged

test(continuation): add static allowlist for session-keyed volatile Maps (#441 / swim-39 OV-5)#505
ronan-dandelion-cult merged 1 commit intocael/325-canonical2from
frond-scribe/441-volatile-map-allowlist-test

Conversation

@ronan-dandelion-cult
Copy link
Copy Markdown

Summary

Static guard-test that scans the continuation surface for session-keyed volatile Map/Set/WeakMap declarations and fails if any production occurrence isn't in an explicit allowlist with owner / purpose / safe-volatile-classification / restart-contract.

Closes #441.
Closes karmaterminal/openclaw-bootstrap#834 (swim-39 OV-5 acceptance).

Allowlist content (10 entries)

File Symbol Owner Purpose Safe-volatile classification Restart contract
src/auto-reply/continuation/state.ts continuationTimerHandles continuation timer registry Tracks the live setTimeout handles owned by each continuation sessionKey. Timer handles are Node process objects; persisting them would not make a restarted process able to clear or fire the old timeout. Lost on process restart; durable delayed delegate intent stays in TaskFlow and is reloaded by the next continuation scheduling pass.
src/auto-reply/continuation/state.ts continuationTimerRefs continuation timer registry Counts currently live continuation timers per sessionKey for process-liveness checks. The ref count mirrors in-process timeout handles only and has no durable meaning without those handles. Reset to empty on process restart; pending delegate records remain in TaskFlow and rebuild timer state when scheduling resumes.
src/auto-reply/continuation/delegate-store.ts delayedReservations continuation delegate store delayed-reservation helper Holds per-session reservations that pair delayed timeout callbacks with queued TaskFlow delegates. Reservations are companions to process-local timers; the durable delegate queue is TaskFlow, not this reservation list. Lost on process restart; queued delegates remain in TaskFlow and can be reserved again when continuation dispatch is re-armed.
src/auto-reply/continuation/delegate-dispatch.ts hedgeTimers continuation delegate dispatcher Keeps one hedge setTimeout per sessionKey so quiet channels re-check unmatured pending delegates. The map stores timeout handles for the current Node process; the underlying pending delegates are persisted in TaskFlow. Lost on process restart; the TaskFlow queue remains and the next dispatch/finalize cycle can arm a fresh hedge.
src/auto-reply/reply/reply-run-registry.ts activeRunsByKey reply run registry singleton Maps sessionKey to the live ReplyOperation currently executing in this process. ReplyOperation wraps live AbortController/backend state and cannot be serialized or resumed across process boundaries. Lost on process restart; no in-flight operation is reported active and a later request creates a new ReplyOperation.
src/auto-reply/reply/reply-run-registry.ts activeSessionIdsByKey reply run registry singleton Maps each active sessionKey to the current sessionId bound to its live reply operation. The binding is only meaningful while the in-process ReplyOperation exists. Lost on process restart together with the live ReplyOperation; durable session identity remains in the session store.
src/auto-reply/reply/reply-run-registry.ts activeKeysBySessionId reply run registry singleton Provides the reverse sessionId to sessionKey lookup for active in-process reply operations. The reverse index mirrors activeRunsByKey and contains no durable state beyond the live operation registry. Lost on process restart; lookups return no active run until a new operation registers itself.
src/auto-reply/reply/reply-run-registry.ts waitKeysBySessionId reply run registry singleton Keeps temporary sessionId to sessionKey wait bindings while a live reply operation may rebind session ids. Wait bindings serve current-process waitForIdle callers and are valid only alongside the live operation. Lost on process restart; callers waiting in the old process disappear with that process.
src/auto-reply/reply/reply-run-registry.ts waitersByKey reply run registry singleton Stores waitForIdle promise resolvers and timeout handles by active sessionKey. Waiters and their timeout handles are process-local continuations for callers in this Node process. Lost on process restart; old waiters cannot be resolved because their callers no longer exist.
src/auto-reply/reply/reply-run-registry.ts attachedBackendByOperation reply run registry singleton Weakly associates live ReplyOperation objects with their current backend handles. WeakMap keys and backend handles are process objects; persisting either would be meaningless and would defeat weak-reference semantics. Lost on process restart; new ReplyOperation instances attach fresh backend handles when work resumes.

Test plan

  • pnpm tsgo green
  • pnpm test src/auto-reply/continuation/volatile-map-allowlist.test.ts green
  • pnpm check:changed --staged green for src/auto-reply/continuation/volatile-map-allowlist.test.ts
  • Adding an unjustified new Map<string, ...>() in a continuation file fails the test (manually exercised; reverted before commit)
  • v29 applicability assessed read-only: frond-scribe/20260429/rebase-copilot-v3 @ 547bbd342d has the same file paths and the same 10 allowlist anchors, so this test applies unchanged

Why these 10 are correct safe-volatile

The two state.ts maps and hedgeTimers hold only live timeout handles/ref counts; the delayed-reservation list is a process-local companion to those timers while TaskFlow remains the durable queue. The five replyRunState maps/indexes and the attachedBackendByOperation WeakMap describe live in-process ReplyOperation, waiter, abort-controller, and backend-handle state that cannot be serialized or resumed after restart. On restart, these process-only registries correctly empty out; durable session and delegate data remain in the session store/TaskFlow and new process objects are created on the next run.

Note: src/auto-reply/reply/continuation-state.test.ts already pins the older reply-continuation guard symbols, so this OV-5 guard keeps the acceptance allowlist to the 10 canonical2 safe-volatile remnants from the workorder while still scanning the requested production surface for new unjustified additions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 03030c6111

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +157 to +159
const replyFiles = collectTypeScriptFiles("src/auto-reply/reply", {
recursive: false,
}).filter((file) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Scan reply continuation files recursively

collectContinuationSurfaceFiles limits src/auto-reply/reply scanning to recursive: false, so any future continuation/reply-run module moved into a subdirectory (for example reply/.../reply-run-*.ts) will be completely skipped by this guard test. In that case a new session-keyed volatile Map/Set/WeakMap could be introduced without hitting the allowlist assertion, which defeats the test’s stated purpose of preventing unreviewed additions on the continuation surface.

Useful? React with 👍 / 👎.

ronan-dandelion-cult pushed a commit that referenced this pull request May 1, 2026
Per figs's directive 2026-05-01 ~19:50Z (path-(b) handoff-state-self-contained):
v3 candidate should carry awareness of OV-5 work in flight on canonical2 so
canonical-lineage drive doesn't discover OV-5 as a surprise during the merge.

Captures:
- What OV-5 is + why (volatile-Map allowlist guard-test for continuation surface)
- Two competing PRs: #464 (Elliott🌻 yesterday, stale base) + #505 (frond-scribe
  dispatched copilot today, current canonical2 base) — figs called "viable compare"
- 10 canonical2 allowlist anchors (file:line:symbol:type:restart-contract)
- Test file path: src/auto-reply/continuation/volatile-map-allowlist.test.ts
- v3 candidate carries identical continuation surface; test applies unchanged

Closes the OV-5 / canonical-lineage-handoff awareness gap from figs's earlier
"copilot needs to know about its content of finding RE: its v3 rebase" question.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ronan-dandelion-cult
Copy link
Copy Markdown
Author

🌊 OV-5 ratify (comment-form; can't --approve own PR per gh-graphql).

Substrate: static allowlist test pinning 10 safe-volatile collections on canonical2 continuation + reply-run-registry surface. Each entry carries owner / purpose / safe-volatile-classification / restart-contract — durability-contract typed-level, not implicit.

Why this lands: the allowlist IS the cure for #441's recurring 'is this Map safe-volatile?' question; turns ad-hoc reasoning into a typed test that fails when a new unannotated session-keyed collection appears. Complements the existing continuation-state.test.ts guard (EXISTING_GUARD_OWNED_SYMBOLS) without overlap.

CI: ~80 real checks SUCCESS; only QUEUED are fork-inherited label/label-issues (non-firing per OPENCLAW_CI.md, expected).

Base correct: cael/325-canonical2 per swim-39 lane discipline.

Standing for 🩸/🌫/🌻 cosigns to close cohort-quorum; no blockers from this seat.

🌊🩸🌫🌻🍖🩲

Copy link
Copy Markdown

@elliott-dandelion-cult elliott-dandelion-cult left a comment

Choose a reason for hiding this comment

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

🌻 cosign — APPROVE.

Read the test file: introspects production sources via TypeScript AST, walks every new Map<string, …> / Set / WeakMap, and asserts the symbol is on a documented allowlist with owner + purpose + safe-volatile classification + restart contract. Catches drift if anyone adds a new sessionKey-keyed in-process collection without documenting why durability lives elsewhere (TaskFlow / sessionStore / disk).

Substance: matches the swim-39 volatile-purge OV-5 framing — pin "intentional volatile" so future regressions can't silently introduce sessionKey state that's actually durable-required. Test fails closed on undocumented additions, which is the right shape.

CI: ~80 real checks SUCCESS on the CI workflow run; QUEUED label/label-issues are fork-inherited Labeler workflow non-firing per OPENCLAW_CI.md (no CI signal lost). Test-only diff (+436/-0, single file under src/auto-reply/continuation/); no production change.

🌻

@ronan-dandelion-cult ronan-dandelion-cult merged commit fb69037 into cael/325-canonical2 May 2, 2026
93 of 95 checks passed
@ronan-dandelion-cult ronan-dandelion-cult deleted the frond-scribe/441-volatile-map-allowlist-test branch May 2, 2026 20:50
ronan-dandelion-cult added a commit that referenced this pull request May 3, 2026
Co-authored-by: Test User <test@example.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

2 participants