Skip to content

feat(task-bridge): task overlap classifier — independence detection layer (#1487 layer 1) - #1656

Merged
liususan091219 merged 2 commits into
sonichi:mainfrom
john-the-dev:feat/task-overlap-classifier-1487
Jun 13, 2026
Merged

liususan091219 merged 2 commits into
sonichi:mainfrom
john-the-dev:feat/task-overlap-classifier-1487

Conversation

@john-the-dev

Copy link
Copy Markdown
Collaborator

What

Adds src/task-overlap.ts — the classification layer needed before the
concurrent-execution step of #1487 can land.

Three exported helpers:

Export Purpose
classifyTaskCategory(body) keyword-based category: cancel | code | research | email | calendar | file | unknown
hasSerialDependency(body) detects "after you finish", "once it's done", etc.
areSafeToOverlap(raw1, raw2) combines both — single boolean for the loop to check

Independence rules (conservative first pass)

Per issue #1487's stated heuristics:

  • Serial-dependency language in either task → serialize
  • Either is CANCEL_INSTRUCTION → serialize (must interrupt in-flight work)
  • Either is code category → serialize (shared git / filesystem state)
  • Same category → serialize (may share external state — e.g. two email tasks could race)
  • Different non-code, non-cancel categories → safe to overlap (max 1 extra concurrent)

Tests

50 unit tests in tests/task-overlap.test.ts covering:

  • extractTaskBody delimiter handling (header fields must not skew classification)
  • classifyTaskCategory — 19 labelled cases including CANCEL_INSTRUCTION, contractions ("it's done"), plurals ("appointments")
  • hasSerialDependency — 6 positive / 5 negative cases including "once it's done"
  • areSafeToOverlap — 10 cases: research+email, code+anything, cancel+anything, serial dep, unknown pairs

What's next (layer 2)

A follow-up PR adds the orchestration: when the proactive-loop's task watcher emits a second TASK_FILE: notification while task N is in-flight, call areSafeToOverlap(task_N_body, task_N1_body) and, if true, start task N+1 as a background Agent rather than queuing it. Concurrency cap: 2 simultaneous tasks.

This PR is intentionally the classifier only so it can be reviewed and merged before the more complex orchestration change lands.

Closes #1487 (layer 1 of 2)

🤖 Generated with Claude Code

Adds `src/task-overlap.ts` with three exported helpers:
  - `classifyTaskCategory(body)` — keyword-based categorisation into
    cancel / code / research / email / calendar / file / unknown
  - `hasSerialDependency(body)` — detects "after you finish", "depends on",
    "once it's done", etc. — always serialises
  - `areSafeToOverlap(raw1, raw2)` — combines the above into a boolean
    the proactive-loop can use when deciding whether to start task N+1
    while task N is in-flight

Conservative rules (first pass per issue sonichi#1487):
  - Serial-dependency language in either task → serialize
  - Either is a CANCEL_INSTRUCTION → serialize (must interrupt)
  - Either is "code" category → serialize (shared git/filesystem state)
  - Same category → serialize (may share external state)
  - Different non-code, non-cancel categories → safe to overlap (1 max)

50 unit tests cover extractTaskBody, classifyTaskCategory,
hasSerialDependency, and areSafeToOverlap including header-field
non-interference (source: discord must not skew task-body classification).

The orchestration layer (proactive-loop using areSafeToOverlap to spawn a
concurrent background Agent) is a follow-up commit; this PR lands the
classifier independently so it can be reviewed and merged first.

Closes sonichi#1487 (layer 1 of 2)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@john-the-dev
john-the-dev force-pushed the feat/task-overlap-classifier-1487 branch from 604807b to 7f8501f Compare June 11, 2026 15:40

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Cold review by Sutando agents (review only — merge stays with the owner).

Should-fix (correctness vs. its own docstring) — extractTaskBody keeps the trailing headers and the injected tier-instructions block. The docstring says classifiers see "only the actual request text — not source: voice etc.", but the bridge writes headers on both sides of the body: discord-bridge.py:2966-2976 emits task: … then source:, channel_name:, guild_name:, user_id:, access_tier:, priority:, then the ===SUTANDO SYSTEM INSTRUCTIONS=== block for non-owner tiers. Slicing "from task: onward" strips only id:/timestamp: and feeds everything else to the regexes. Concretely:

  • channel_name/guild_name are user-influenced strings inside the classified text. The category check is first-match-wins (email → calendar → code → file → research), so a code task arriving from a guild named e.g. "inbox zero" matches the email regex first and classifies email — making it overlap-eligible against a research task. That's the unsafe direction (two concurrent tasks, one mutating git), not the conservative one.
  • Team/other-tier task files carry the whole codex instructions block; today's keywords happen not to trip on it, but that's luck, not a contract.

Fix is small: bound the slice at the next \nsource: line (every bridge writes source: immediately after the task text — discord-bridge.py:2967, and the chat-path/voice formats match), falling back to end-of-string. One regression test with a full real-shaped task file (headers both sides + tier block) would pin it.

Should-fix — hasSerialDependency only matches single-token subjects in the "once X is done" form. once\s+\S+('s?\s+|\s+is\s+)(done|complete|finished) matches "once it's done" but not "once the deploy is done" / "once the email is sent" (multi-word subject; also "sent" absent). Misses here flip serial intent to overlap — again the unsafe direction. once\s+.{1,40}?\s+is\s+(done|complete|finished|sent) (plus the existing 's form) closes the common cases.

Nits:

  • Stale comment: "'code' is last among specific categories" — it's third of five (email, calendar, code, file, research).
  • after finishing X doesn't match (finish\b vs "finishing"); cheap to add finish(ing)?.

The overall shape is right — conservative default, cancel/code always serialize, same-category serializes, 50 tests, and layer-splitting the classifier from orchestration is the correct call. Both should-fixes bias the unsafe direction though, which is exactly what this layer exists to prevent.

Merge-readiness: not quite — fix the extractTaskBody bound (and ideally the serial-dependency subject gap) before layer 2 consumes this; both are small, self-contained changes.

Stand: Echo Act IV (Mini)

@liususan091219

Copy link
Copy Markdown
Collaborator

Routing/dedup note: see #1666 (comment) — #1666 and this PR are one feature in two PRs; please consolidate to one (single concern, single tracking issue) and it needs the owner's feature go/no-go before depth review.

— Lucy (Mac Studio bot)

@liususan091219 liususan091219 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE.

  • Clean standalone layer: no behavior change yet (classifier + tests only), conservative defaults (serialize on same-category, code, cancel, or dependency language), and extractTaskBody respects the #982 header-forge boundary.
  • One suggested tightening before layer 3 wires this in: rule 5 currently lets unknown overlap with any different known category — but unknown means the keywords failed, and a missed code task overlapping an email task is the expensive failure. Treating unknown as non-overlappable would make the conservative claim complete.
  • Test suite covers the matrix well incl. negative dependency phrases.

@liususan091219

Copy link
Copy Markdown
Collaborator

Post-approve addendum (applies here and to the #1666 port): hasSerialDependency misses multi-word subjects — once\s+\S+ only allows ONE token before is done, so "once the report is done, email it" → no match → tasks may overlap when the user asked for ordering (the unsafe direction). Suggest once\s+.{1,40}?\s+is\s+(done|complete|finished) (bounded, non-greedy) + a multi-word test case in both suites.

@github-actions

Copy link
Copy Markdown
Contributor

@cla-assistant check

@liususan091219
liususan091219 merged commit d05c082 into sonichi:main Jun 13, 2026
3 checks passed
liususan091219 added a commit that referenced this pull request Jun 13, 2026
… 2) (#1666)

Python port of `src/task-overlap.ts` (PR #1656) so the proactive-loop
(Python) can call `are_safe_to_overlap(raw1, raw2)` without shelling out
to Node.  Same conservative heuristics: code/cancel always serialize;
serial-dependency language forces ordering; same-category serializes;
different non-code categories may overlap.

20 tests covering extract_task_body, classify_task_category,
has_serial_dependency, and are_safe_to_overlap — mirrors the TS test
suite so divergence between implementations surfaces immediately.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Susan Xueqing Liu <xliu127@stevens.edu>
liususan091219 added a commit that referenced this pull request Jun 13, 2026
…#1682)

* Revert "feat(task-bridge): task overlap classifier — Python port (#1487 layer 2) (#1666)"

This reverts commit 2e5334b.

* Revert "feat(task-bridge): add task overlap classifier (#1487 layer 1) (#1656)"

This reverts commit d05c082.
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.

feat(task-bridge): auto-overlap independent tasks instead of serializing

3 participants