feat(overseer): query_open_loops — "what am I forgetting?" lens - #100
feat(overseer): query_open_loops — "what am I forgetting?" lens#100heavygee wants to merge 2 commits into
Conversation
Adds a read-only neglect-axis lens, orthogonal to inbox urgency. A cold open loop = a session whose latest status-bearing worker event is not `done` (needs_decision/needs_review/blocked/failed/stalled) and was never closed by a later `completed`. Self-populating from AGENT_NOTIFY_SUMMARY; no operator triage. - shared: 8th tool query_open_loops (name, zod schema, catalog, OpenAI params, OverseerOpenLoop return type); isNoOpAction + openLoopBucket + open-loop event-type constants. - store: latestWorkerStatusPerSession — one indexed query, latest status-bearing worker event per session (progress excluded; completed is the only closer). - entity: queryOpenLoops — drops closed loops, nulls no-op actions (status!=done is the strong filter, action is a tiebreak), buckets waiting_on_you before half_finished, coldest-first; identity survives session deletion via payload. - converse: brain-facing projection thins the rows; system prompt gains a two-axes section (urgency vs neglect) and fixes priority direction (lower-is-higher — the 27B called p50 "highest"). - docs: implementation notes + the zero-code weekly-digest converse prompt. - tests: entity lens (ordering/closed/no-op/filters), projection, shared helpers; tool count 7 -> 8. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 74acbcc030
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const limit = Math.min(Math.max(args.limit ?? 50, 1), 100) | ||
|
|
||
| const loops: OverseerOpenLoop[] = [] | ||
| for (const event of this.events.latestWorkerStatusPerSession()) { |
There was a problem hiding this comment.
Scope open loops to the caller namespace
On a multi-user hub, this reads the global EventStore without receiving or filtering by the authenticated request's namespace, so /overseer/tools/query_open_loops and the converse tool can mix another namespace's session names, projects, summaries, and actions into the response. Pass c.get('namespace') through the Overseer dispatch and restrict candidate events to sessions owned by that namespace.
AGENTS.md reference: AGENTS.md:L145-L149
Useful? React with 👍 / 👎.
| ORDER BY e.ts ASC | ||
| LIMIT ? |
There was a problem hiding this comment.
Apply the cap after excluding closed sessions
Once the database contains more than 500 sessions with status-bearing events, this selects only the 500 oldest per-session statuses before the caller removes completed rows and applies bucket/project filters. Consequently, newer open decisions can disappear permanently—potentially returning an empty lens if the oldest 500 sessions are closed—and the reported counts are also truncated. Build the latest-per-session set first, exclude closers/apply filters, then limit the resulting open loops in the requested presentation order.
Useful? React with 👍 / 👎.
| WHERE source_kind = 'worker' | ||
| AND related_session_id IS NOT NULL | ||
| AND event_type IN ( | ||
| 'needs_decision', 'needs_review', 'blocked', 'failed', 'stale', 'completed' |
There was a problem hiding this comment.
Let inferred completion close an open loop
When a session ends with reason === 'completed' but its last agent output lacks a parseable AGENT_NOTIFY_SUMMARY, OverseerEventRecorder.onSessionEnd deliberately records a later completed event with sourceKind: 'system'. This worker-only predicate ignores that definitive closer, leaving any earlier blocked, failed, or decision event surfaced as abandoned forever. Include system-generated completion fallback events when selecting the latest closing status while continuing to restrict opening statuses to worker events.
Useful? React with 👍 / 👎.
…prompt (H1/H2/H4) Ingest-peer handoff (PR #99 -> converse/entity layer): - H2: two-level `detail: 'lean'|'full'` (default lean) on every context tool, threaded into projectToolResultForBrain(tool, result, detail). Closes the coverage gap — get_session_state, get_session_recent_output (raw terminal text capped at 280 chars in lean; was a token bomb), and get_worker_health (signal trail dropped in lean) now have lean projections. `full` returns raw rows, still bounded by limit/n + the outer char clamp. No token-budget engine. - H1: system-prompt rule — for a specific inbox item, pull explain_priority then query_events{sessionId} for that session's backlog as salience before answering. - H4: confirmed query_open_loops spans all non-deleted sessions (active AND archived); never filters on session.active. Documented in the method + doc. - H3: query_session_actions reader deferred (0 dispositions today). Tests: session-state/recent-output/worker-health lean projections, detail:full passthrough; pass-through test moved to explain_priority. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Follow-up commit
Gates: |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6792bea07e
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** Transcript chunk → role + capped text (raw terminal output would blow the window). */ | ||
| function projectChunk(chunk: unknown): Record<string, unknown> { | ||
| const o = isObj(chunk) ? chunk : {} | ||
| return { role: o.role, at: o.createdAt, text: truncate(o.text, LEAN_CHUNK_TEXT_MAX) } |
There was a problem hiding this comment.
Preserve the tail of truncated transcript chunks
When a worker chunk exceeds 280 characters, the default lean projection retains only its prefix. This repository's agent contract requires the status, action, and summary line at the end of every response (shared/src/overseerEvents.ts:14-18), so get_session_recent_output now routinely hides both that machine summary and often the worker's conclusion, causing the Overseer to answer from incomplete context unless the model happens to request detail: "full". Preserve a head-and-tail slice or explicitly retain the final summary line within the same cap.
Useful? React with 👍 / 👎.
…onverse
8th read-only Overseer tool query_open_loops ("what am I forgetting?"
cold-open-loops lens) + system-prompt two-axes framing and priority
lower-is-higher fix. Additive store query, no DDL/schema-version step.
Sits immediately after feat/overseer-text-converse (stacked on it).
Rebuilt + verified on :3006 at driver tip 002e32dcc; live query_open_loops
trace confirmed.
Co-authored-by: Cursor <cursoragent@cursor.com>
Stacked on
feat/overseer-text-converse. Implements the validated cold open-loops / "what am I forgetting?" lens (design + live evidence indocs/plans/2026-07-31-overseer-forgotten-open-loops-lens.md).Why
The inbox answers urgency ("what needs me now?"). This adds the orthogonal neglect axis ("what have I abandoned?"). It is self-populating from the
AGENT_NOTIFY_SUMMARYevery worker turn already emits — no operator triage (the operator has disposed 0/174 inbox items, so "old + undisposed" is useless as a filter; the self-disposition signal is the substitute).What's in it
query_open_loops(8th read-only tool). Substrate = rawevents(richer than the coalesced inbox), one indexed query for the latest status-bearing worker event per session (progressexcluded — it doesn't close an operator-owed decision;completedis the only closer).status != done. A no-opaction("none"/"complete"/…) is nulled but the loop still surfaces — action is a tiebreak, not the filter (kills the 108 Tier-B false positives from the spec correction).waiting_on_you(a decision the operator owes) presented beforehalf_finished, each coldest-first.Not here (follow-ups)
stale-row sweep (build-path step 3) — coordinate with the inbox-ingest lane. (Worker-only filter already keeps thosesource_kind=systemrows out of the lens.)Tests / gates
bun typecheckclean (hub + web).isNoOpAction/openLoopBucket; updated tool count 7→8.Made with Cursor