Conversations on the new loop (M2.5, PR A)
CI / checks (push) Skipped
CI / checks (pull_request) Successful in 5m14s

- A top-level message that isn't a command is a conversation: an item in
  the `general` scope (L1), threaded under the operator's message, with
  `reply`, `ask_human` and `propose_work` and no workspace or execution.
  `general` is reserved as a project name.
- propose_work(project, title, summary) goes up as a revision for a
  go-ahead (or a plain yes). On go, under a file_issue intent (L8), the
  record issue is opened in that project, assigned to the agent and
  carrying the new item's ID; its item starts agreed, with the summary as
  its brief. Intake skips any issue carrying an ID (D1); boot adopts a
  filed issue or puts the proposal up again.
- A conversation closes after tuning.loop.general_idle_s (default a day);
  a reply in its thread then starts a new conversation that continues it.

Tests: conversation unit tests (threading, filing, the marker, an unknown
project, changes, idle close and continuation, a plain yes, both crash
cases, commands at top level); the walk with conversations, proposals and
idle time, and records_have_one_item / conversations_do_no_work; an e2e
conversation that becomes filed work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPQ14L3LikyefCMFBqEbKE
This commit is contained in:
dongarandClaude Opus 5.5 committed 2026-10-06 04:26:40 +00:00
1 parent d70d43a8cc
commit 3f54896f65
14 files changed
+811 -35

No files matched your search

+6
View File
@@ -68,6 +68,7 @@ PROJECT_OPTIONAL = (
# never in a command the model chose.
VISIBILITY = ("commands", "harness")
_PROJECT_NAME = re.compile(r"[a-z0-9][a-z0-9-]*")
GENERAL = "general" # the scope of conversation not about a project (L1)
_REFERENCE = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
@@ -116,6 +117,8 @@ class LoopTuning:
# A restatement on a `bug` issue proceeds after this long without a
# reply (D1).
bug_grace_s: float = 600.0
# A `general` conversation closes after this long without a message (L1).
general_idle_s: float = 86400.0
@dataclass(frozen=True)
@@ -247,6 +250,9 @@ def _parse_projects(
if not isinstance(name, str) or not _PROJECT_NAME.fullmatch(name):
errors.append(f"{where}: a project name is lowercase letters, digits, -")
continue
if name == GENERAL:
errors.append(f"{where}: `{GENERAL}` is the scope of conversations (L1)")
continue
if not isinstance(entry, dict):
errors.append(f"{where}: must be a mapping")
continue