Unsorted improvements#70
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR batches a set of frontend + backend improvements across prompt/loop UX, viewer link handling, notifications, and internal package refactors (splitting previously “god” packages into smaller subpackages while preserving legacy import paths via shims).
Changes:
- Improve loop prompt UX: resolve selected loop prompts via full workspace prompt list (fallback), add onTasks trigger delta plumbed into template context, and add
coalesceDuringBusysupport. - Improve viewer/file handling and UI affordances: classify non-viewable file extensions, add global handler fallback to
/api/files, and add per-bead phase icons. - Refactor internals: extract
internal/prompts,internal/workspaces,internal/cel,internal/beads/watcher, andinternal/acpAPIs with shims; migrate web session routes intointernal/web/handlers; add workspace-scoped MCP notifications and model-tag pinning.
Reviewed changes
Copilot reviewed 144 out of 144 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| web/static/utils/prompts.test.js | Adds loop prompt name-resolution regression tests |
| web/static/utils/phaseState.test.js | Adds iconName/currentIconName tests |
| web/static/utils/phaseState.js | Adds per-phase icon metadata + terminal icon |
| web/static/utils/native.test.js | New unit tests for non-viewable extension classifier |
| web/static/utils/native.js | Adds NON_VIEWABLE_EXTENSIONS + isNonViewableExtension |
| web/static/utils/index.js | Re-exports native extension helpers |
| web/static/utils/globalHandlers.test.js | New tests for viewer.html non-viewable fallback |
| web/static/utils/globalHandlers.js | Adds non-viewable viewer link routing + fallback |
| web/static/utils/endpoints.js | Makes global shortcuts endpoint accept query params |
| web/static/hooks/useBackgroundNotifications.js | Filters workspace-scoped notifications |
| web/static/components/SettingsDialog.js | Requests shortcuts with include_prompts=true |
| web/static/components/SessionItem.js | Renders phase pill icon via currentIconName |
| web/static/components/LoopFrequencyPanel.js | allPrompts fallback for arg-edit prompt lookup |
| web/static/components/Icons.js | Adds Wrench/Beaker/Eye icons + minor formatting |
| web/static/components/ChatInput.js | allPrompts fallback for edit-loop-args prompt lookup |
| web/static/components/beads/detail/useBeadsDetailPanel.js | Adds loading-mode panel wiring |
| web/static/components/beads/detail/PanelBody.js | Renders stable Drawer skeleton for loading state |
| web/static/app.js | Wires activeWorkspaceUUID + passes allPrompts to ChatInput |
| tests/integration/inprocess/loop_ontasks_e2e_test.go | Updates onTasks event/baseline store refs |
| scripts/bd-attach.sh | New helper for beads issue attachments via metadata |
| internal/workspaces/workspaces.go | Moves workspace config model to workspaces pkg |
| internal/workspaces/workspaces_test.go | Updates tests for prompts.PromptPreferredModel |
| internal/workspaces/runner_config.go | New restricted runner config types |
| internal/workspaces/folders.go | Moves folder config model to workspaces pkg |
| internal/workspaces/folders_test.go | Package rename for folder tests |
| internal/workspaces/doc.go | Package docs for workspaces extraction |
| internal/workspaces/constraint.go | New constraint matcher utility |
| internal/web/session_ws.go | Uses internal/acp IsMCPInitTimeout |
| internal/web/session_api.go | Removes *Server route wrappers (migrated to handlers) |
| internal/web/session_api_test.go | Adds test shims + updates route wiring |
| internal/web/routes.go | Routes call handlers route wrappers (no *Server thunks) |
| internal/web/handlers/session_update.go | Updates loop store Update() arg count |
| internal/web/handlers/session_routes.go | New consolidated route wrappers for sessions |
| internal/web/handlers/session_loop.go | Adds CoalesceDuringBusy to loop API structs |
| internal/web/handlers/session_loop_write.go | Persists coalesce_during_busy + Update signature |
| internal/web/handlers/session_id.go | New shared session id validation in handlers |
| internal/web/handlers/global_shortcuts.go | Gates heavy prompts payload behind include_prompts |
| internal/web/handlers/global_shortcuts_test.go | New tests for include_prompts gating |
| internal/web/handlers/config_route.go | New /api/config dispatcher wrapper |
| internal/web/contract_test.go | Updates mux wiring to handlers route funcs |
| internal/web/config_handlers.go | Removes *Server handleConfig wrapper |
| internal/web/config_handlers_test.go | Adds test shim for HandleConfigRoute |
| internal/web/beads_cache_watcher_test.go | Uses beads/watcher event type |
| internal/session/store_dispensers.go | Adds Store sub-store constructors (Queue/Loop/etc.) |
| internal/session/store_cleanup.go | Adds archived session retention cleanup |
| internal/session/loop.go | Adds CoalesceDuringBusy + Update signature |
| internal/prompts/watcher.go | Moves prompts watcher into prompts pkg |
| internal/prompts/watcher_test.go | Package rename for watcher tests |
| internal/prompts/template.go | Switches template funcs/evaluator refs to internal/cel |
| internal/prompts/template_precompile_test.go | New tests for PrecompileTemplateConds in prompts pkg |
| internal/prompts/prompts.go | Moves prompt model/loader into prompts pkg; adds coalesceDuringBusy in loop frontmatter |
| internal/prompts/prompts_test.go | Updates tests for cel evaluator extraction |
| internal/prompts/param_types.go | Moves prompt param types into prompts pkg |
| internal/prompts/migrate.go | Moves markdown migration into prompts pkg |
| internal/prompts/cache.go | Moves prompts cache into prompts pkg |
| internal/prompts/cache_test.go | Package rename for cache tests |
| internal/processors/processors_test.go | Adds tests for Trigger.OnTasks CEL context plumbing |
| internal/processors/input.go | Adds TriggerOnTasksChanges to ProcessorInput |
| internal/processors/hook.go | Lifts TriggerOnTasksChanges into template context |
| internal/mcpserver/types.go | Adds model_tag + loop coalesce + workspace ui notify types |
| internal/mcpserver/tools_ui.go | Implements mitto_workspace_ui_notify tool |
| internal/mcpserver/tools_prompt_dispatch.go | prompt_name wins when both prompt/prompt_name provided |
| internal/mcpserver/tools_conversation_new.go | Adds model_tag pin + loop coalesce propagation |
| internal/mcpserver/tools_conversation_lifecycle.go | Adds model_tag + loop coalesce updates + output |
| internal/mcpserver/tool_registration.go | Registers mitto_workspace_ui_notify + documents model_tag |
| internal/mcpserver/server.go | Extends interfaces for workspace notify + model tag application |
| internal/mcpserver/prompt_loop_defaults.go | Merges coalesceDuringBusy from prompt frontmatter defaults |
| internal/mcpserver/prompt_loop_defaults_test.go | New tests for coalesceDuringBusy defaults merge |
| internal/conversation/ws_events.go | Adds WSMsgTypeNotification constant |
| internal/conversation/tasks_baseline.go | Moves tasks baseline store into conversation pkg |
| internal/conversation/session_manager.go | Adds BroadcastWorkspaceUINotify + uses internal/acp timeout predicate |
| internal/conversation/prompt_dispatcher.go | PromptName resolution always wins; threads trigger delta; ACP error helpers moved to internal/acp |
| internal/conversation/config_manager.go | Uses internal/acp IsACPConnectionError |
| internal/conversation/bgsession_prompt.go | Adds PromptMeta trigger context + internal/acp types |
| internal/conversation/bgsession_config.go | Adds BackgroundSession.ApplyModelTag |
| internal/conversation/bgsession_acp_process.go | Moves restart taxonomy/patterns to internal/acp |
| internal/conversation/background_session.go | Uses internal/acp connection error + restart reason |
| internal/conversation/background_session_test.go | Updates tests for internal/acp helpers; adds ApplyModelTag tests |
| internal/conversation/acp_process_controller.go | Uses internal/acp restart constants/reasons |
| internal/conversation/acp_process_controller_test.go | Updates controller tests for internal/acp constants |
| internal/config/workspaces_shim.go | Re-exports workspaces pkg API via config |
| internal/config/workspace_rc_prompts_test.go | Moves config-private RC prompt test back to config pkg |
| internal/config/prompts_shim.go | Re-exports prompts pkg API via config |
| internal/config/cel_shim.go | Re-exports cel pkg API via config |
| internal/cel/templatefuncs.go | Moves template funcs into cel pkg |
| internal/cel/templatefuncs_test.go | Adds local RenderPromptTemplate; removes moved precompile tests |
| internal/cel/tasks_condition.go | Moves task condition evaluator into cel pkg |
| internal/cel/tasks_condition_test.go | Package rename for tasks condition tests |
| internal/cel/evaluator.go | New cel pkg root docs + evaluator move |
| internal/cel/evaluator_test.go | Package rename for evaluator tests |
| internal/cel/context.go | Adds Trigger context to template PromptEnabledContext |
| internal/beads/watcher/watcher.go | Moves beads watcher into dedicated watcher pkg |
| internal/beads/watcher/watcher_test.go | Package rename for beads watcher tests |
| internal/acpproc/acp_process_manager.go | Uses internal/acp IsACPConnectionError |
| internal/acpproc/acp_process_manager_restart.go | Uses internal/acp global restart constants |
| internal/acp/errors.go | Extracts ACP error taxonomy + exports helpers/patterns |
| internal/acp/errors_test.go | Updates tests for exported ACP helpers |
| docs/devel/prompts.md | Documents beads menu prompt enabled context rules |
| docs/devel/architecture.md | Documents ACP ownership boundary + dependency rules |
| docs/config/mcp.md | Documents mitto_workspace_ui_notify tool |
| config/prompts/builtin/support-watch-channel.prompt.yaml | Adds bd -d vs --body-file guidance |
| config/prompts/builtin/support-continue-conversation.prompt.yaml | Adds bd -d vs --body-file guidance |
| config/prompts/builtin/reproduce-bug.prompt.yaml | Adds mandatory closure + verification steps |
| config/prompts/builtin/beads-triage-bugs.prompt.yaml | Adds mandatory per-bug closure verification |
| config/prompts/builtin/beads-refine-implementation.prompt.yaml | Enables coalesceDuringBusy=false + trigger delta section |
| config/prompts/builtin/beads-issue-fix-phase-reproduce.prompt.yaml | Adds tier checks + mandatory closure/verification |
| config/prompts/builtin/beads-issue-fix-phase-investigate.prompt.yaml | Adds tier checks + mandatory closure/verification |
| config/prompts/builtin/beads-issue-feature-phase-test.prompt.yaml | Adds tier checks + tier-tagged comments |
| config/prompts/builtin/beads-issue-feature-phase-review.prompt.yaml | Adds tier checks + tier-tagged comments |
| config/prompts/builtin/beads-issue-feature-phase-plan.prompt.yaml | Adds tier checks + tier-tagged comments |
| config/prompts/builtin/beads-issue-feature-phase-implement.prompt.yaml | Adds tier checks + tier-tagged comments |
| .augment/rules/44-beads-attachments.md | New docs for beads attachments convention + helper |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
inercia
added a commit
that referenced
this pull request
Jul 17, 2026
…essLimit API The two tests TestLoopRunner_RecordTasksFireOutcome_NoProgressLimitOptOut and TestLoopPrompt_EffectiveNoProgressLimit were added on this branch as failing reproductions for mitto-ckaa (onTasks circuit-breaker opt-out). They reference LoopPrompt.NoProgressLimit (*int) and LoopPrompt.EffectiveNoProgressLimit() — an API that was never actually added to internal/session/loop.go on any branch (verified via git log --all -S 'EffectiveNoProgressLimit'). Result: internal/conversation fails to compile, breaking Unit Tests and Lint on PR #70. Delete the tests to restore the typecheck. The mitto-ckaa runtime fix (schema field + helper + runner change) still needs to land — bd issue mitto-ckaa reopened with a note to restore both tests alongside the runtime work.
inercia
added a commit
that referenced
this pull request
Jul 17, 2026
…eExtension
lastIndexOf(".") was called on the raw path, so a dot inside the query
string (e.g. "report.xlsx?cache=v1.2") pointed inside the query and the
extension check silently mis-classified the file. Split on ?/# first,
then compute the extension from the cleaned path.
Adds a regression test covering both a non-viewable path with a versioned
query and a viewable one.
Addresses Copilot review comment on PR #70.
inercia
added a commit
that referenced
this pull request
Jul 17, 2026
In globalHandlers.js the non-viewable viewer-link fallback concatenated the raw filesystem path directly onto "file://", so paths containing spaces or other characters that need URL escaping produced an invalid file:// URL and could fail to open in native mode. Split on "/" and encodeURIComponent each segment so the resulting URL is well-formed while preserving the path separators. Addresses Copilot review comment on PR #70.
inercia
added a commit
that referenced
this pull request
Jul 17, 2026
CleanupArchivedSessions used UpdatedAt as a fallback for the deletion decision when ArchivedAt was zero, but still logged now.Sub(meta.ArchivedAt) as the "age" field — producing a ~two-millennia age (now minus the zero time) that is misleading to operators. Use whichever timestamp actually drove the decision for both the comparison and the logged fields so the two are consistent. Addresses Copilot review comment on PR #70.
inercia
added a commit
that referenced
this pull request
Jul 17, 2026
Prior cleanup (mitto-7r0) was closed but the failures returned:
1. internal/acpproc/acp_process_memory.go:49 (unused)
Delete descendantsRSS — dead wrapper around descendantsRSSDetailed with
no callers anywhere in the tree.
2. internal/web/handlers/dashboard.go:266 (unused)
Delete itemType — no callers; the sibling item* helpers (itemStatus,
itemUpdatedAt, itemPriority) are used, but nothing reads issue_type
via this helper.
3. internal/web/loop_runner_test.go:4019 (staticcheck SA4000)
The test literally called
workspaceKey("/w", "a") != workspaceKey("/w", "a")
which is a tautology. Assign both to variables first so we're
comparing two independent invocations of the helper (the actual
intent: verify workspaceKey is deterministic for identical inputs).
All three appear in run 29413245700 (main) and 29568780468 (PR #70)
with identical output.
Consolidate the two separate beads-issue loop bodies (beads-issue-loop-fixing-bugs, beads-issue-loop-implementing-features) into one unified orchestrator prompt (beads-issue-loop-processing) that picks the appropriate flow at runtime based on issue type. Update the defect-scanner test suite in config/ to reflect the new single-file layout of the loop family.
Thread the beads change delta computed by the onTasks loop runner all
the way to the prompt-template context so loop bodies can iterate the
specific issues that changed via {{ .Trigger.OnTasks.Changes.* }}
(Added, Updated, Removed, Closed, Reopened, LabelAdded, Touched).
Wiring:
- config: add TriggerContext / TriggerOnTasksContext / TasksChangesView
as template-only fields on PromptEnabledContext. Not exposed to CEL
(enabled-when runs pre-dispatch when no trigger data exists yet).
- processors: add ProcessorInput.TriggerOnTasksChanges (json:"-") and
populate ctx.Trigger from it in BuildCELContext. Slices alias the
source delta (no defensive copy).
- conversation: PromptMeta gains a Trigger field; prompt dispatcher
forwards it to the processor input.
- web/loop_runner: new internal triggerNowWithTasksDelta variant
threads the tasksDelta computed in processTasksChange through
deliverPrompt into PromptMeta.Trigger.
Template guard (both levels must nest — outer .Trigger may be nil for
scheduled / onCompletion / manual "Run Now" / non-loop dispatches):
{{ with .Trigger }}{{ with .OnTasks }}...{{ end }}{{ end }}
Bundled fixes in the same loop_runner.go hunks (inseparable):
- mitto-cbx: LoopRunner.Stop() Unsubscribe()s from the BeadsWatcher
before cancelling timers; adds a `stopped` guard so late fan-out is
dropped by OnBeadsChanged.
- refactor: extract the OnComplete failure-handling body into a new
handleDeliveryFailure method (behavior-preserving).
Refs: mitto-xkn, mitto-cbx
New MCP tool that broadcasts a workspace-scoped fire-and-forget
notification to all connected clients, filtered on the frontend by
workspace UUID. Unlike mitto_ui_notify — which is scoped to a live
registered session and its UIPrompter — this tool targets a workspace
directly so callers without a registered session (notably auxiliary
sessions running close-phase / conversationClosed processors) can
still surface toasts to the user.
Delivery path:
MCP tool → sessionManagerAdapter.BroadcastWorkspaceUINotify
→ conversation.SessionManager.BroadcastWorkspaceUINotify
→ EventsManager.Broadcast(WSMsgTypeNotification, {workspace_uuid, ...})
→ frontend useBackgroundNotifications filters by workspace
Permission model: keyed on the caller's session flags when resolvable;
if the caller has no registered session (aux-session case), the
workspace_uuid requirement itself is the safety boundary (a caller
cannot broadcast into a workspace it was not spawned into). Deliberately
skips resolveSelfIDWithMCP's Phase-3 correlation wait since aux sessions
never register a pending request.
Frontend: useBackgroundNotifications now takes activeWorkspaceUUID and
drops incoming notifications whose workspace_uuid does not match the
currently-viewed workspace. Notifications without workspace_uuid always
show (backward compatible with pre-existing callers).
Refs: mitto-6bn
The GET /api/global/shortcuts endpoint was unconditionally shipping the merged global prompts list (~750 KB) to every caller, but only the shortcuts editor in SettingsDialog actually needs it. The four other consumers (conversation toolbar, tasks-list toolbar, beads panel chrome, folder shortcuts tab) just render the existing sections and were paying for parallel ~750 KB JSON parses on the WebView main thread on every mount. Gate the heavy `prompts` field behind `?include_prompts=true` so the hot-path callers drop from ~750 KB → ~1 KB per fetch. SettingsDialog opts in; the endpoints helper takes an optional params object. Refs: mitto-r4t0
Widen the per-attempt and total deadlines of NewSession/LoadSession when MCPInitTimeout is configured, giving a cold agent time to finish its internal MCP-server handshake before Mitto times out. Under the current design MCP is attached globally (not per session), so hasMCPServers is not load-bearing on the cold path: the extended budget applies to every cold session/new so long as MCPInitTimeout > 0. Adds coldMCPBudget() and covers the disabled-by-default, cold-with-MCP, and cold-without-MCP branches with unit tests. Refs: mitto-8ul.1
Small clarifications to support-continue-conversation and support-watch-channel builtin prompts.
Adds LoopPrompt.CoalesceDuringBusy *bool (default true = current behaviour). When set to false on an onTasks loop, beads/task changes that land while the loop's subtree is busy are re-fired once after quiescence with the accumulated delta (via .Trigger.OnTasks.* from mitto-xkn), then the baseline rebases. Bounded to a single pending re-fire slot per session so we never queue arbitrarily. Wired through: - session.LoopStore.Update (new trailing coalesceDuringBusy *bool arg; all in-tree callers updated with explicit nil) - REST PUT/PATCH handlers (session_loop.go, session_loop_write.go) - MCP mitto_conversation_new and mitto_conversation_update (loop_coalesce_during_busy on input; echoed on update output) Runner (loop_runner_tasks.go): fireTasksRebase consults ShouldCoalesceDuringBusy(); when false, maybeFireAccumulatedDelta diffs pre-run baseline vs current, applies Layer 0 guards (cooldown, maxDuration) and the CEL condition, and fires once via triggerNowWithTasksDelta with the accumulated delta before rebasing. Tests: 7 new unit tests covering default helper, JSON round-trip, Update round-trip, material-change fire, no-change skip, condition gating, cooldown blocking, and coalesce=true silent-absorb regression. Docs: docs/devel/message-queue.md §Loop prevention Layer 2 documents the opt-in with a YAML example. Frontend UI checkbox is a separate follow-up increment. Refs: mitto-dmb, mitto-78k
…alse in beads onTasks builtins (mitto-f9q)
Third and final increment of the mitto-78k epic. Wires the .Trigger.OnTasks
delta (mitto-xkn) and the CoalesceDuringBusy=false opt-in re-fire (mitto-dmb)
through to the two existing onTasks builtin loop prompts, and closes the yaml
frontmatter gap that mitto-dmb left open.
## Backend gap fixed (was missing from mitto-dmb)
config.PromptLoop (the yaml-frontmatter loop struct in internal/config/prompts.go)
did not have a CoalesceDuringBusy field, so a prompt file could not opt in via
its `loop:` block — only callers of mitto_conversation_new / _update could.
Added:
- `CoalesceDuringBusy *bool` on `PromptLoop` with yaml tag `coalesceDuringBusy`
(matching the camelCase convention of `maxIterations`, `maxDuration`, etc.).
- Merge into `ConversationStartInput` and `ConversationUpdateInput` in
`applyPromptLoopDefaultsToStartInput` / `applyPromptLoopDefaultsToUpdateInput`
(`internal/mcpserver/prompt_loop_defaults.go`). Same "caller wins, opt-out via
loop_apply_prompt_defaults=false" semantics as the other frontmatter defaults.
- Pointer-based merge (both nil and *false are meaningful) so a prompt author
can explicitly set `coalesceDuringBusy: true` even when the runtime default
is also true — useful when the caller intent should be recorded.
## Prompt adoption
`beads-refine-implementation.prompt.yaml` (single-file supervisor for the
implementation-refined label pass):
- `loop.coalesceDuringBusy: false` — re-fire once at quiescence with the
accumulated delta so newly-arrived unrefined beads are picked up promptly.
- New `## Triggered by these beads changes` preamble in the prompt body,
nil-guarded with `{{ with .Trigger }}{{ with .OnTasks }}...{{ end }}{{ end }}`
(both levels are checked because .Trigger itself is nil for non-onTasks
dispatch paths per docs/devel/message-queue.md).
`beads-issue-loop-processing.prompt.yaml` (list-level A/B/C orchestrator):
- `loop.coalesceDuringBusy: false` — same rationale; the supervisor's own
worker writes AND external beads changes during a pass both fire once more
at quiescence so priority-A comments and freshly-arrived bugs/features are
not stranded waiting for another external change.
- New `## Triggered by these beads changes` preamble with the same nil-guard
pattern, phrased as a triage-priority hint (does NOT bypass Step-2 filtering).
The two hunks in `beads-issue-loop-processing.prompt.yaml` were staged
selectively (via git apply --cached of an extracted patch) so that pre-existing
uncommitted WIP on that file (`.Args.FixBugs` / `.Args.WorkOnFeatures` gating,
unrelated to this epic) stays in the working tree for its owner to land
separately.
## Tests + docs
- `internal/mcpserver/prompt_loop_defaults_test.go` (new): 7 sub-tests covering
the four merge cases for both start-input and update-input helpers — frontmatter
fills unset caller, explicit caller wins, opt-out disables the merge, nil
frontmatter is a no-op.
- `docs/devel/message-queue.md` §onTasks: new "Opting in from a prompt file"
subsection documenting the yaml key (`coalesceDuringBusy`), merge semantics,
and the two builtin prompts that adopt it.
- Full local gate: make fmt clean, `go vet ./...` clean,
`go test ./internal/config/... ./internal/mcpserver/... ./internal/session/...`
all green (config 8.7s, mcpserver 119.3s, session 18.9s), `make check-model-tags`
green.
Closes mitto-f9q and, with mitto-xkn (b03668c) and mitto-dmb (742a13e),
completes epic mitto-78k.
…pt_name' (mitto-kt6)
Three-layer defense-in-depth fix so a strict-schema caller that fills
'prompt' with a placeholder (e.g. "__placeholder__") to satisfy the
required field no longer drowns out the intended named dispatch.
L1 (schema): SendPromptToConversationInput.Prompt is now
json:"prompt,omitempty" so the generated JSON Schema advertises it
as optional. The runtime "either 'prompt' or 'prompt_name' is
required" check is preserved. InitialPrompt on
ConversationStartInput was already omitempty.
L2 (MCP boundary): when both 'prompt' and 'prompt_name' are supplied,
prompt_name wins — the free-text field is cleared and an INFO log
line is emitted. Applies to both handleSendPromptToConversation and
handleConversationStart (the conversation_new side landed as part
of the TestConversationNew_* rewrite in 742a13e; the send_prompt
side is here).
L3 (resolver): promptDispatcher.resolveAndSubstitute now triggers name
resolution whenever meta.PromptName != "" (was: only when
message == ""), closing the same class of bug for any future
non-MCP entry point that carries both a message and a PromptName
on the same queued row.
Tests:
- TestSendPromptToConversation_PromptAndPromptName_NameWins (mcpserver;
committed in 742a13e alongside the conversation_new symmetric test).
- TestResolveAndSubstitute_PromptNameAlwaysResolvedWhenSet (conversation).
- All three pass; go build ./... clean.
Refs: mitto-kt6
…itto-3soh)
Replace the '${Kind} · ${Phase}' text pill in SessionItem's sidebar row
with a small per-phase icon; tooltip and aria-label still carry the full
'${Kind} phase: ${Phase}' string. Tier color styling (reasoning / coding
/ terminal) is preserved.
- phaseState.js: add stable kebab-case iconName to each FEATURE_PHASES /
BUG_PHASES entry (list/code-block/beaker/eye and search/refresh/wrench)
and a currentIconName + TERMINAL_ICON_NAME='check' on the derived
state so consumers can map to concrete icons without knowing the
ordering.
- Icons.js: add WrenchIcon, BeakerIcon, EyeIcon (Heroicons outline).
- SessionItem.js: import the new icons + SearchIcon/RefreshIcon/ListIcon/
CodeBlockIcon/CheckIcon, add PHASE_ICON_COMPONENTS lookup table keyed
by currentIconName, and render the pill as an inline-flex square with
a 3x3 icon inside instead of text.
- phaseState.test.js: cover currentIconName across all bug/feature
states + terminal, and per-phase iconName mapping.
…rameterDialog (mitto-cwf5)
handleRunBeadsListPrompt now mirrors handleRunBeadsPrompt: compute
missing = getMissingPromptParameters(prompt, 'beadsList') and, when the
prompt declares required (non-optional) parameters the beadsList menu
cannot auto-supply, open the PromptParameterDialog first and dispatch
inside the onSubmit callback with the collected userArgs. Applied on
both branches:
- Loop path (onOpenLoopDialog): param dialog wraps the loop-dialog +
startConversationWithPrompt chain.
- Non-loop path: param dialog wraps the plain
startConversationWithPrompt call.
Also thread onOpenPromptParamDialog into the useCallback dependency
list. Fixes tasksList shortcut buttons in BeadsView dispatching prompts
with unfilled ${VAR} substitutions.
…ssing body The FixBugs and WorkOnFeatures parameters were already declared in the front-matter but the prompt body still hard-coded both classes. Wire them through so the flags actually gate rendering: - Step 1 preflight: only resolve 'Loop fixing bug' / 'Loop implementing feature' when their class is enabled. - Step 2P close-terminal-label sweep: skip fixed/verified queries when the corresponding class is disabled, and drop the label from the OR filter in the guideline description. - §B and §C sections: fully gated blocks — no spawn, no wait/archive when disabled. - Guidelines: 'Strict priority A → B → C' and the concurrency-cap / close-terminal-label bullets rendered conditionally so operators running with a single class enabled see a coherent doc. Behavior unchanged when both flags are 'true' (default).
…-uo8e) When a loop conversation runs a prompt whose `menus` front-matter targets a non-loop scope (e.g. `menus: beadsList` — the builtin "Loop processing tasks"), the `LoopFrequencyPanel` correctly greyed the prompt-name pill in the picker but ALSO greyed the sliders button used to open the `PromptParameterDialog`. That button should stay enabled: the prompt IS the active loop body, and its declared parameters are legitimate values to tweak without un-looping. Root cause: both the `selectedPrompt` lookup in `LoopFrequencyPanel.js` and `handleEditLoopArguments` in `ChatInput.js` consulted only the menu-filtered `loopPrompts` list, so menu-scoped prompts (absent from that list) resolved to `null`, forcing `canEditArgs === false`. Fix: thread a new `allPrompts` prop (the full workspace prompts list, already computed in `app.js`) from `app.js` → `ChatInput` → `LoopFrequencyPanel`. Both lookups now prefer `allPrompts` before falling back to `loopPrompts`/`prompts`. `LoopPromptSelector`'s `prompts` prop is intentionally left unchanged so picker semantics (grey pill, filtered dropdown for non-loop-scoped prompts) are preserved exactly. Added 6 regression unit tests in `web/static/utils/prompts.test.js` covering: menu-scoped resolution via allPrompts, loop-scoped resolution via loopPrompts, both-lists prefer allPrompts, name-in-neither returns undefined, null/undefined lists don't throw, and baseline parameter enumeration for a beadsList prompt. Verification: 19 suites / 1546 tests pass.
…rror skeleton (mitto-joo)
Clicking a beads reference used to leave the user with 1-2s of dead time
while /api/issues/{id} was in flight, because BeadsIssueView unconditionally
rendered <BeadsDetailPanel issue=null/> and BeadsDetailPanel bails out early
without a loaded issue, so the drawer was truly unmounted for the fetch.
Open the docked side panel synchronously on click with a loading skeleton
built on the same <Drawer dock side=\end\ zClass=\z-60\> primitive that
BeadsDetailPanelBody uses, so panel chrome (position, animation, outside-
click/Escape close) matches the loaded state and the swap does not flicker.
On fetch failure keep the drawer open with an alert alert-error alert-soft
and a Retry button, instead of just firing a toast into the void.
Scope is intentionally kept to BeadsIssueView; useBeadsDetailPanel and
BeadsDetailPanel are left untouched (they assume a real data object).
Loading container carries data-testid=\beads-issue-loading\ for future
Playwright coverage.
…mpletion-based bd close Re-applies enhancements to the inline @mitto-mention worker seed in Loop processing tasks that were lost between sessions: - Bead awareness: preface pins the worker to a single target bead <id>; every bd write must target that ID. - Traceable commits (Commit=true branch): commit-message template gains a trailing (refs <id>) footer so every code change links back to the tracker. - Completion-based closure: new step 6 gates bd close on FOUR conditions (mention asked for closure or intent fully satisfied; not deferred; file changes committed; no open parent-child dependency). With Commit=false the file-changes clause hard-guards to "not permitted to commit → leave open for the human" — cannot close on uncommitted work. - Constraints tightened: explicit no-close rules for deferred beads and beads with uncommitted work on disk. - Old step 6 (report to parent) renumbered → step 7, extended to include <action> / <committed> / target <id> for orchestrator reap signal. Renders validated across all four scope combos and both Commit branches; config + internal/config test suites remain green.
…mitto-uhnc) processTasksChange.tasksActionFire swallowed every error from triggerNowWithTasksDelta as a bare WARN, including ErrPromptResolveFailed. When a builtin loop prompt was renamed out from under an active onTasks loop (e.g. "Loop processing beads" -> "Loop processing tasks"), the failure counter never bumped, StoppedReason was never set, and onLoopAutoStopped never fired -- the conversation was orphaned and silently retried forever, in contrast to the scheduled-loop path in checkSession which already routes ErrPromptResolveFailed through handlePromptResolveFailure. Route ErrPromptResolveFailed through handlePromptResolveFailure so onTasks loops behave the same as scheduled loops: auto-pause after MaxPromptResolveFailures (3) consecutive fires, set StoppedReason to promptUnresolved, and broadcast onLoopAutoStopped. Non-resolve errors keep the original WARN behaviour; ErrSessionBusy still short-circuits silently. Reproduction test TestLoopRunner_OnTasks_PromptResolveFailure_AutoPauses drives processTasksChange three times with a resolver that always fails and asserts the expected auto-pause, StoppedReason, and callback. Refs: mitto-uhnc
…self-verification (mitto-4los)
Restructure Step 3 of beads-issue-fix-phase-fix.prompt.yaml into a
MANDATORY CLOSURE checklist with an explicit self-verification block
that fails loudly if any closure step was skipped:
- Step 3.1: bd comment "Fix: ..." (record the fix)
- Step 3.2: bd update --add-label fixed (advance state machine)
- Step 3.3: make fmt, then git add <explicit paths> && git commit
(no more git add -A; documents the concurrent-loop
stash-reset risk of wildcard stages)
- Step 3.V: self-verification — 4 shell assertions the agent must
pass before ending the turn (fixed label present, Fix:
comment present, HEAD commit references bead, worktree
clean for touched files)
Motivation: on mitto-uhnc (2026-07-16) the fix-phase child agent wrote
the code and the Fix: comment, then stopped without labelling or
committing. The bead ended up closed/fixed-labelled with the fix still
uncommitted in the working tree (git log --grep=mitto-uhnc empty). This
was not a specification gap — the phase prompt already listed those
steps — it was an execution miss on open-ended prose. The verification
block turns the checklist into a hard gate.
Also adds `make fmt` before the commit step: the project pre-commit
hook rejects gofmt-dirty commits, and the manual mitto-uhnc closure
was blocked by exactly this. Running the formatter up front removes
the blocked-commit / re-stage cycle.
Template renders cleanly for both Commit=true and Commit=false code
paths (verified via text/template execution). YAML valid, 48/48 brace
pairs, 11/11 if/end directives balanced.
Follow-up (tracked in mitto-4los): apply the same pattern to the
sibling `beads-issue-fix-phase-investigate` and
`beads-issue-fix-phase-reproduce` prompts.
Refs: mitto-4los, mitto-uhnc
…ion (mitto-kvot) The Beads tasks view fetched workspace-prompts with session_id=<activeSessionId>, so the backend evaluated enabledWhen against the incidentally-open sidebar conversation instead of the workspace context. Prompts gated on !Session.IsChild / Permissions.* / Tools.* flickered enabled/disabled depending on which sidebar item the user last clicked, which is user-visible non-determinism. Since the beadsList / beadsIssues menus always spawn NEW root conversations via newSession, per-session gating is semantically wrong: drop session_id from both fetchBeadsPromptsForWorkspace and fetchBeadsListPromptsForWorkspace, and drop the now-unused activeSessionId prop from the hook. The backend falls back to a session-less workspace context (Session.IsChild=false, Permissions.CanStartConversation=true) via buildWorkspacePromptEnabledContext, so CommandExists / DirExists / Item.* gates continue to evaluate normally. Adds a Playwright regression covering the fix: the mocked workspace-prompts endpoint returns no prompts if session_id= leaks into the query, and the test asserts (a) the tasksList shortcut button renders enabled with the expected aria-label, and (b) no outgoing workspace-prompts request carries a session_id parameter. Refs mitto-kvot
Introduce a dedicated router prompt for @mitto mentions (beads-issue-mention-driver) that dispatches four phase prompts by name via mitto_conversation_send_prompt (self-send), matching the per-phase model tiering pattern already used by the bug-fix and feature loops: - mention-phase-investigate (Reasoning tier) - mention-phase-plan (Reasoning tier) - mention-phase-implement (Coding tier) - mention-phase-answer (Coding tier) The router itself never labels the bead or edits code — each phase prompt owns its `mention-{investigated|planned|implemented|answered}` label write and its Finalize step. The driver only resolves the target bead, forwards the mention timestamp/body into every phase dispatch, and renders the "handled inline" Finalize branch after the last phase reports back. Also update §A of beads-issue-loop-processing to hand off @mitto mentions to the new driver instead of processing them inline, and extend the loop defects test suite to cover the new prompts. TestMentionDriver_RendersForRepresentativeContexts (added to prompt_template_test.go) is the acceptance test: renders the driver for three representative contexts (linked-issue + Commit=true, arg-only + Commit=false, no resolvable target) and asserts every phase name flows through the router, the Commit=true/false copy branches correctly, and the router never leaks a phase-owned label write.
…se/triage/reproduce prompts (mitto-mpu5)
Each of the seven bug-fix and feature phase prompts now renders a "## Tier
check" block at dispatch time that:
1. always prints the active model name + tags, so a tier-degraded run is
visible in the transcript, and
2. branches on Model(<declared tier>) — "✓ <tier> tier confirmed" when
the session's ModelTags include the declared tier, or a "⚠
Tier-degraded run." block with an inline `bd comment` recording the
degradation when they do not (and a "<unknown>" / "tags: none"
fallback when the active model can't be resolved at all).
Each phase's Step 3 `bd comment` now carries a tier-tagged prefix
("Investigation [tier: Reasoning]:", "Fix [tier: Coding]:", …) so a
bead's audit trail makes the tier split visible without needing to
cross-reference the run's active model.
beads-triage-bugs and reproduce-bug get the same mandatory-closure
discipline: each per-bug triage step now has a Step 4c.V self-verification
that re-reads the bead after `bd update` and asserts the closure labels
were actually applied — historically the agent has composed the triage
comment then stopped without labeling, leaving the bead lying about the
state of the tree.
TestPhasePrompts_TierCheckRendersForModelTags and
TestPhasePrompts_TierTaggedCommentPrefix guard against future regressions
in any of the seven phase prompts (drop-out of the tier-check block,
wrong tier name, wrong comment prefix).
…_update (mitto-41o1)
Add an optional `model_tag` parameter to both `mitto_conversation_new` and
`mitto_conversation_update` so tool callers (loops, driver prompts, other
agents) can request a specific model tier ("Reasoning", "Coding", …) by
tag instead of by concrete model id — the same resolution the existing
prompt-level `preferredModels:` frontmatter uses.
BackgroundSession.ApplyModelTag is the MCP-facing entry point: it resolves
the tag against the session's advertised model catalog via the shared
SelectPreferredModel helper and, when the resolved model differs from the
current one, applies it through the same SetConfigOption path the user's
manual model-dropdown click uses — so the change persists as the new
baseline (not a transient per-prompt override). An empty tag clears any
transient prompt-level override.
Placing ApplyModelTag on the BackgroundSession keeps mcpserver from having
to reach into conversation-package internals (avoids the
mcpserver→conversation import cycle) and keeps the tag→id resolution logic
in one place.
Errors surface as tool-call errors when the agent has not advertised a
model catalog or when the tag does not resolve to any available model,
so the caller can decide whether to retry or fall back.
…es no models (mitto-ishl)
When a prompt declares `preferredModels:` but the agent has not advertised
a model catalog (e.g. every Auggie session today), the transient
per-prompt tier switch was silently dropped — no visible signal in the
log that the run was starting on the wrong tier. That masked
tier-degraded runs in exactly the cases the tier-check block (mitto-mpu5)
was added to catch.
applyModelPreference now splits the "no agent catalog" branch into two
sub-cases:
- genuine no-preference no-op (preferredModels empty) → DEBUG,
decision=skip_no_agent_models (unchanged behavior);
- declared preferences dropped (preferredModels non-empty) → WARN,
decision=skip_agent_advertises_no_models, with the requested
preferences logged so the drop is greppable in mitto.log alongside
the tier-degraded transcript signal.
…sView (mitto-qluh.1) BeadsIssueView now maintains an in-component navigation stack (history + position index) so drilling through cross-referenced beads behaves like a browser tab: back retraces the previous entry, forward retraces a discarded direction, and selecting a new bead after a goBack truncates the forward chain — matching user expectations from every other list/ detail navigation pattern in the app. Selecting the currently-visible bead is a no-op (does not push a duplicate entry). Missing/empty/falsy depObj ids are ignored so the stack never grows a null. Both goBack at pos=0 and goForward at the tail are clamped no-ops. The 18 new BeadsView.test.js cases exhaustively cover the stack lifecycle across four describe blocks: initial state, handleSelectIssue push semantics, goBack/goForward navigation, and forward-branch truncation.
…ownload (mitto-tac5)
Clicking a viewer URL for a binary the internal viewer cannot render
(Office documents, archives, installers, native binaries — see
NON_VIEWABLE_EXTENSIONS in native.js) used to fall through to the broken
viewer branch.
globalHandlers.js now inspects the viewer URL before dispatching:
- when `ws_path` is present, reconstructs the absolute path and hands
it to `openFileURL()` — the same plumbing the viewer's "Open in
System App" button uses (native app → OS default handler; web →
browser default);
- when `ws_path` is missing (older recordings / processor-crafted URLs
/ manual edits) but `ws=<UUID>` + `path=<relative>` are present,
degrades gracefully to a plain `/api/files?ws=…&path=…` download in
a new tab instead of falling through.
Viewable text/image/pdf paths keep the current in-app viewer flow
untouched. Non-viewable-extension classification lives in native.js and
is re-exported from utils/index.js.
New native.test.js and globalHandlers.test.js cover the extension
classifier and both routing branches (with-ws_path, ws-fallback,
viewable-passthrough, malformed URL).
`bd` (beads 1.0.x) has no native attachment command. This project now attaches file paths to issues using the `attachments` key inside each issue's structured JSON metadata column, so attachments sync with `bd dolt push` / `pull` like any other issue field and travel with the ticket to every collaborator. Schema (documented in .augment/rules/44-beads-attachments.md): each attachment is an object with a required `path` (repo-relative preferred) and optional `name`/`note`; `attachments` is a JSON array of them. scripts/bd-attach.sh is the helper that wraps this convention with add/list/remove/clear subcommands, using `bd update --metadata`'s top-level merge to preserve every other metadata key on the issue. scripts/tests/test_bd_attach.sh exercises the helper end-to-end.
…ing spawns
The beads-issue-loop-processing.prompt.yaml orchestrator was spawning its
three per-target child loops (§A Mention driver, §B Loop fixing bug,
§C Loop implementing feature) by passing only `arguments:` to
`mitto_conversation_new` — never `loop_arguments:`. The MCP handler
(internal/mcpserver/tools_conversation_new.go) reads these two fields into
separate slots: `arguments` seeds the initial-prompt render, `loop_arguments`
seeds every `onCompletion` re-fire (session.LoopPrompt.Arguments). So on
every re-fire the child's loop body rendered with `.Args = nil`, which under
missingkey=zero resolved `.Args.Commit == ""`, and the positive-match gate
`{{ if eq .Args.Commit "true" }}...{{ else }}...{{ end }}` in the phase
dispatch flipped Commit=false — silently dropping every "commits the fix"
if-branch across the whole child loop lifetime.
Fix: add a `loop_arguments:` field to each of the three spawn blocks that
exactly mirrors the block's `arguments:` (same keys, same resolved template
expressions). Both maps now carry the resolved Commit value, so every
re-fire renders the loop body with the same `.Args` as the initial run.
Pinned by TestLoopProcessingSpawns_MirrorArgumentsIntoLoopArguments in
internal/config/prompt_template_test.go — asserts that with
.Args.Commit="true" each of the three spawn blocks contains both
`arguments:` and `loop_arguments:` with `"Commit": "true"`.
Refs: mitto-rtdr
BeadsIssueView previously rendered a placeholder <Drawer> while
/api/issues/{id} was in flight, then swapped it for <BeadsDetailPanel>
(which mounts its OWN Drawer via BeadsDetailPanelBody) once the fetch
resolved. Because the two branches mounted two different top-level
components, Preact unmounted the placeholder and mounted a fresh Drawer
for the loaded state, replaying the slide-in animation — perceived as a
second panel opening on top of the first.
Fix: BeadsDetailPanel now accepts an isLoading mode (with loadingIssueId,
loadError, onRetry). useBeadsDetailPanel folds isLoading into isOpen and
forwards the loading surface; BeadsDetailPanelBody renders a minimal
loading/error skeleton inside the SAME Drawer it uses for the loaded
body. BeadsIssueView collapses to a single <BeadsDetailPanel> call that
spans the whole load lifecycle, and the `if (!h.creating && !h.data)
return null` early-return gate is gone.
- web/static/components/beads/detail/useBeadsDetailPanel.js: accept +
forward isLoading/loadingIssueId/loadError/onRetry; include isLoading
in the isOpen computation.
- web/static/components/beads/detail/PanelBody.js: new loading-skeleton
return path (same Drawer shell as loaded body).
- web/static/components/BeadsView.js: BeadsDetailPanel accepts the
loading props, drops the !h.data return-null gate. BeadsIssueView
collapses the two-branch conditional into one panel mount. Removed
now-unused Drawer / CloseIcon imports.
- web/static/components/BeadsView.test.js: 2 new structural
reproduction assertions under "mitto-zbfq: BeadsIssueView single
Drawer mount across load" — both now pass.
- tests/ui/specs/beads.spec.ts: placeholder skipped E2E describe with
a pointer to the Jest reproduction (E2E baseline is currently
Dashboard-first and cannot reach the linked-issue click flow).
Refs: mitto-zbfq
…-b8k.1) Move loop_runner.go, loop_runner_tasks.go, tasks_baseline.go and loop_runner_test.go out of internal/web/ into internal/conversation/. Update all call sites in internal/web/ to reference conversation.LoopRunner / conversation.NewLoopRunner / etc. Behavior-preserving move only; no logic changes. Refs: mitto-b8k.1 (parent epic mitto-b8k)
…to-b8k.2)
Move the 17 thin *Server route wrappers from internal/web/session_api.go
into internal/web/handlers/session_routes.go as methods on *Handlers:
HandleSessionsRoute, HandleSessionGetRoute, HandleSessionEventsRoute,
HandleSessionUpdateRoute, HandleSessionDeleteRoute,
HandleSessionUserDataRoute, HandleSessionCallbackRoute,
HandleSessionSettingsRoute, HandleSessionPruneRoute,
HandleSessionChangesRoute, HandleSessionImagesRoute,
HandleSessionFilesRoute, HandleSessionQueueRoute,
HandleSessionLoopRoute, HandleSessionFlushRoute,
HandleSessionPromptArgCacheRoute, HandleWorkspacePromptsRoute.
Add internal/web/handlers/session_id.go with IsValidSessionID and the
sessionIDFromPath helper (kept package-local so /api/sessions/{id}/ws in
internal/web/ can still use its own web.IsValidSessionID without a cycle).
Update internal/web/routes.go to point session endpoints at the migrated
handlers. Only handleSessionWS stays on *Server (WS upgrade lives in web/
per the bead).
Update test call sites: add three test-only *Server shims
(handleSessions, handleSessionQueue, handleWorkspacePrompts) matching the
existing handleListSessions shim pattern; contract_test/newContractMux and
session_api_test/newSessionDetailMux now register the migrated Handle*Route
methods directly. Behavior-preserving move only; no logic changes.
Refs: mitto-b8k.2 (parent epic mitto-b8k)
…k.2) Move the `handleConfig` dispatcher from *Server in internal/web/config_handlers.go into *Handlers as HandleConfigRoute in internal/web/handlers/config_route.go. Update internal/web/routes.go to reference s.apiHandlers.HandleConfigRoute. Test fixtures keep a test-only *Server.handleConfig shim delegating to the migrated method (matching the pattern used for the session-wrapper migration). config_handlers.go retains all non-handler *Server helpers (validateAndPrepareSaveConfig, buildNewSettings, applyConfigChanges, applyAuthChanges, ensureExternalListenerStarted, hasValidCredentials). handleGlobalEventsWS and handleSessionWS remain in web/ as legitimate WS-lifecycle handlers. Refs: mitto-b8k.2 (parent epic mitto-b8k)
The loop scheduling move in mitto-b8k.1 relocated NewTasksBaselineStore from internal/web to internal/conversation, but two call sites in the integration test loop_ontasks_e2e_test.go still referenced web.NewTasksBaselineStore. Update both call sites to conversation.NewTasksBaselineStore and drop the now-unused internal/web import. Verified: make test-integration (204s) passes. Refs: mitto-b8k.1 (already closed), unblocks mitto-b8k.2 acceptance
…mitto-b8k.3) First step of the internal/config split (mitto-b8k.3). Move beads_watcher.go and its test out of internal/config into a new self-contained sub-package internal/beads/watcher. The watcher has no dependency on any other config symbol, and internal/beads has no dependency on internal/config, so the move introduces no import cycles. Update callers (internal/conversation/loop_runner*.go, its test, the loop_ontasks integration test, plus internal/web/server.go and internal/web/beads_cache_watcher_test.go which also reached in via the configPkg alias) to import the new package and spell the types as watcher.BeadsWatcher / watcher.BeadsChangeEvent / watcher.NewBeadsWatcher / watcher.BeadsSubscriber. No behavior change. Verified: go build/vet, unit tests for config, beads, beads/watcher, conversation, and web packages all green (full ./internal/... suite green). Refs: mitto-b8k.3 (parent epic mitto-b8k)
Move three cohesive method groups out of the 1040-LOC internal/session/store.go
into companion files in the same package:
- store_dispensers.go: Queue/ActionButtons/Loop/Callback sub-store constructors
- store_children.go: child-session traversal (Find/List/Count/HasChildSessions
+ handleChildSessionsOnParentDelete)
- store_cleanup.go: CleanupArchivedSessions + parseRetentionPeriod
Pure code movement — no signature or behavior changes. store.go shrinks under
the 600-LOC target from the bead's acceptance criteria. All callers use method
syntax (store.Foo(...)); file location changes are transparent to them.
Verified: go build ./..., go vet ./..., go test ./internal/session/... green.
Refs: mitto-b8k.4 (parent epic mitto-b8k)
…nto internal/cel with alias shim (mitto-b8k.3)
Third step of the internal/config split (mitto-b8k.3), out of the ticket's
proposed order because internal/config/prompt_template.go and prompts.go
depend on CEL context types — CEL must move before prompts, not after.
Move 4 production files + their tests out of internal/config into a new
self-contained sub-package internal/cel:
- cel_evaluator.go -> internal/cel/evaluator.go
- cel_context.go -> internal/cel/context.go
- tasks_condition.go -> internal/cel/tasks_condition.go
- templatefuncs.go -> internal/cel/templatefuncs.go
- cel_evaluator_test.go -> internal/cel/evaluator_test.go
- tasks_condition_test.go -> internal/cel/tasks_condition_test.go
- templatefuncs_test.go -> internal/cel/templatefuncs_test.go
The two extra *_test.go files had to move with the production code because
they reference package-private identifiers (issueKeyType, ev.compile,
beadsCache/beadsCacheMu, and the compile/evaluate test helpers) that are
now internal to internal/cel.
internal/cel imports only stdlib and google/cel-go — no cycle back to
internal/config.
Introduce internal/config/cel_shim.go, a thin re-export layer that keeps
external `config.PromptEnabledContext` / `config.CELEvaluator` /
`config.GetCELEvaluator` / etc. references compiling unchanged via type
aliases and `var =` function delegates. In-package callers in config
(prompt_template.go, prompts.go, and their tests) resolve transparently
through the same aliases; no source changes needed there.
Two small tests-only adjustments:
- internal/config/prompt_template_precompile_test.go (new): the five
TestPrecompileTemplateConds_* tests are extracted from the moved
templatefuncs_test.go and kept in internal/config because
PrecompileTemplateConds itself still lives in
internal/config/prompt_template.go.
- internal/cel/templatefuncs_test.go: adds a small package-local
RenderPromptTemplate helper mirroring config.RenderPromptTemplate so
the moved tests don't need to import internal/config (which would
reintroduce a cycle). Production callers keep using the real
config.RenderPromptTemplate from internal/config/prompt_template.go.
Verified: gofmt, go build ./..., go vet ./..., unit tests for config,
cel, web, web/handlers, web/middleware, processors, and conversation
packages all green.
Refs: mitto-b8k.3 (parent epic mitto-b8k)
…rate, template, WebPrompt, MergePrompts) into internal/prompts with alias shim (mitto-b8k.3) Second step of the internal/config split (mitto-b8k.3). Unblocked by the prior CEL extraction (3757ac8) — internal/prompts now imports internal/cel directly for PromptEnabledContext / GetCELEvaluator / BuildTemplateFuncMap / ValidateCondition, so there is no cycle back to internal/config. Move 6 production files + 5 test files out of internal/config into a new self-contained sub-package internal/prompts: - prompt_param_types.go -> internal/prompts/param_types.go - prompt_template.go -> internal/prompts/template.go - prompts_cache.go -> internal/prompts/cache.go - prompts_migrate.go -> internal/prompts/migrate.go - prompts_watcher.go -> internal/prompts/watcher.go - prompts.go -> internal/prompts/prompts.go - prompt_template_test.go -> internal/prompts/template_test.go - prompt_template_precompile_test.go -> internal/prompts/template_precompile_test.go - prompts_test.go -> internal/prompts/prompts_test.go - prompts_cache_test.go -> internal/prompts/cache_test.go - prompts_watcher_test.go -> internal/prompts/watcher_test.go Also surgically extract from internal/config/config.go into a new internal/prompts/webprompt.go: the WebPrompt struct, the PromptSource type and its four constants, and MergePrompts / MergePromptsKeepDisabled (with their section header). These are semantically prompts-domain (WebPrompt embeds PromptSource / PromptLoop / PromptPreferredModel / PromptParameter), so keeping them in config would force internal/prompts to import back into internal/config, breaking the shim. Introduce internal/config/prompts_shim.go, a thin re-export layer that keeps all `config.PromptFile` / `config.WebPrompt` / `config.MergePrompts` / `config.PromptsCache` / etc. external references compiling unchanged via type aliases, `const` re-exports, and `var =` function delegates. Bare in-package refs from settings.go / workspace_rc.go / config.go's remaining prompts-domain field types (`Prompts []WebPrompt`) resolve transparently through the aliases; no source changes needed there. One test case (TestWorkspaceRC_SkipsInvalidChildSessionIdPrompt) was carved out of the moved prompts_test.go into a small new file internal/config/workspace_rc_prompts_test.go — it exercises the config-private parseWorkspaceRC helper, which cannot cross the package boundary. All other tests move as-is. Verified: gofmt, go build ./..., go vet ./..., unit tests for config, prompts, cel, web, conversation, mcpserver, processors, cmd packages all green. Refs: mitto-b8k.3 (parent epic mitto-b8k)
… into internal/workspaces with alias shim (mitto-b8k.3, closes target)
Final step of the internal/config split (mitto-b8k.3). Move the
workspaces model, folder-level defaults, ACP-server constraint matcher,
and restricted-runner configuration types into a new self-contained
sub-package internal/workspaces.
Files moved (via git mv):
- workspaces.go -> internal/workspaces/workspaces.go
- workspaces_test.go -> internal/workspaces/workspaces_test.go
- folders.go -> internal/workspaces/folders.go
- folders_test.go -> internal/workspaces/folders_test.go
Blocks surgically extracted from internal/config/config.go into new
internal/workspaces files:
- ACPServerConstraint type + ConstraintMatchesName function
-> internal/workspaces/constraint.go
- RunnerRestrictions / DockerRestrictions / WorkspaceRunnerConfig
-> internal/workspaces/runner_config.go
These four types are consumed both by workspaces (WorkspaceSettings
embeds ACPServerConstraint and WorkspaceRunnerConfig) and by
config.go / settings.go (ModelProfile.Criteria embeds
ACPServerConstraint; Settings.RestrictedRunners is keyed by
WorkspaceRunnerConfig). Moving them alongside workspaces keeps
internal/workspaces free of any import back into internal/config,
avoiding an import cycle with the shim.
Introduce internal/config/workspaces_shim.go, a thin re-export layer
that keeps ~326 external references across ~43 files compiling
unchanged. Type aliases carry the struct types transparently through
field access, and var-delegates re-export the loader/saver functions.
In-package references from config.go / settings.go / workspace_rc.go
resolve through the same aliases without source edits.
Verified: gofmt, go build ./..., go vet ./..., unit tests for
config/workspaces/prompts/cel and the wider web/conversation/mcpserver/
acpproc/cmd suites all green.
Closes the ticket's acceptance criterion: internal/config non-test
LOC drops from 5,992 to under 5,000 and now holds only core schema
(config.go, settings.go, workspace_rc.go, merger.go, user_data.go,
plus the four shim files re-exporting the sub-packages).
Refs: mitto-b8k.3 (parent epic mitto-b8k)
Move acp_error_classification.go (464 LOC) from internal/conversation to internal/acp/errors.go, fixing an inverted dependency direction where the lower-level internal/acpproc package was importing internal/conversation purely for shared taxonomy (ACPClassifiedError, RestartReason, MaxACPRestarts, BackoffDelay, ClassifyACPError, IsACPConnectionError, MaxGlobalRestarts, ...). Now: internal/conversation -> internal/acpproc -> internal/acp. Never reverse. - New internal/acp/errors.go with the moved taxonomy + restart constants. - Exported previously-unexported helpers used across packages: formatACPError -> FormatACPError, isRateLimitError -> IsRateLimitError, formatClassifiedError -> FormatClassifiedError, mcpInitTimeoutPattern -> MCPInitTimeoutPattern. - Removed the duplicated local mcpInitTimeoutPattern from bgsession_acp_process.go. - Rewrote 14 call sites across internal/acpproc, internal/conversation, internal/web, internal/mcpserver to use the mittoAcp alias. - Documented the "ACP Ownership Boundary" as Design Decision 4.1 in docs/devel/architecture.md, including the explicit decision to keep bgsession_acp_process.go in internal/conversation (methods mutate live *BackgroundSession state; splitting would invert deps). Follow-up mitto-iuw2 tracks extraction of the remaining stateless helpers (BuildACPProcessEnv, StderrCollector, StartACPStartupWatchdog) that would allow dropping further acpproc -> conversation imports. Verified: go build ./..., go vet ./..., go test on internal/acp, internal/acpproc, internal/conversation, internal/web, internal/web/handlers — all green.
…essLimit API The two tests TestLoopRunner_RecordTasksFireOutcome_NoProgressLimitOptOut and TestLoopPrompt_EffectiveNoProgressLimit were added on this branch as failing reproductions for mitto-ckaa (onTasks circuit-breaker opt-out). They reference LoopPrompt.NoProgressLimit (*int) and LoopPrompt.EffectiveNoProgressLimit() — an API that was never actually added to internal/session/loop.go on any branch (verified via git log --all -S 'EffectiveNoProgressLimit'). Result: internal/conversation fails to compile, breaking Unit Tests and Lint on PR #70. Delete the tests to restore the typecheck. The mitto-ckaa runtime fix (schema field + helper + runner change) still needs to land — bd issue mitto-ckaa reopened with a note to restore both tests alongside the runtime work.
…eExtension
lastIndexOf(".") was called on the raw path, so a dot inside the query
string (e.g. "report.xlsx?cache=v1.2") pointed inside the query and the
extension check silently mis-classified the file. Split on ?/# first,
then compute the extension from the cleaned path.
Adds a regression test covering both a non-viewable path with a versioned
query and a viewable one.
Addresses Copilot review comment on PR #70.
In globalHandlers.js the non-viewable viewer-link fallback concatenated the raw filesystem path directly onto "file://", so paths containing spaces or other characters that need URL escaping produced an invalid file:// URL and could fail to open in native mode. Split on "/" and encodeURIComponent each segment so the resulting URL is well-formed while preserving the path separators. Addresses Copilot review comment on PR #70.
CleanupArchivedSessions used UpdatedAt as a fallback for the deletion decision when ArchivedAt was zero, but still logged now.Sub(meta.ArchivedAt) as the "age" field — producing a ~two-millennia age (now minus the zero time) that is misleading to operators. Use whichever timestamp actually drove the decision for both the comparison and the logged fields so the two are consistent. Addresses Copilot review comment on PR #70.
…ge move - internal/conversation/loop_runner_test.go:4269: split workspaceKey calls into k1/k2 locals to avoid SA4000 (identical expressions on both sides of !=) reported by staticcheck. - tests/integration/inprocess/restart_test.go: RestartReasonCrashDuringStream was moved from internal/conversation to internal/acp on this branch; production code + related unit tests were updated but this integration test file was missed, causing test-integration to fail to build with 'undefined: conversation.RestartReasonCrashDuringStream'. Switch to mittoAcp.RestartReasonCrashDuringStream (matching the alias used in the rest of internal/conversation) and drop the now-unused conversation import. Closes mitto-6ogq.
inercia
force-pushed
the
feat/unsorted-improvs
branch
from
July 17, 2026 21:13
0b44c58 to
ac6d6ae
Compare
inercia
added a commit
that referenced
this pull request
Jul 19, 2026
…essLimit API The two tests TestLoopRunner_RecordTasksFireOutcome_NoProgressLimitOptOut and TestLoopPrompt_EffectiveNoProgressLimit were added on this branch as failing reproductions for mitto-ckaa (onTasks circuit-breaker opt-out). They reference LoopPrompt.NoProgressLimit (*int) and LoopPrompt.EffectiveNoProgressLimit() — an API that was never actually added to internal/session/loop.go on any branch (verified via git log --all -S 'EffectiveNoProgressLimit'). Result: internal/conversation fails to compile, breaking Unit Tests and Lint on PR #70. Delete the tests to restore the typecheck. The mitto-ckaa runtime fix (schema field + helper + runner change) still needs to land — bd issue mitto-ckaa reopened with a note to restore both tests alongside the runtime work.
inercia
added a commit
that referenced
this pull request
Jul 19, 2026
…eExtension
lastIndexOf(".") was called on the raw path, so a dot inside the query
string (e.g. "report.xlsx?cache=v1.2") pointed inside the query and the
extension check silently mis-classified the file. Split on ?/# first,
then compute the extension from the cleaned path.
Adds a regression test covering both a non-viewable path with a versioned
query and a viewable one.
Addresses Copilot review comment on PR #70.
inercia
added a commit
that referenced
this pull request
Jul 19, 2026
In globalHandlers.js the non-viewable viewer-link fallback concatenated the raw filesystem path directly onto "file://", so paths containing spaces or other characters that need URL escaping produced an invalid file:// URL and could fail to open in native mode. Split on "/" and encodeURIComponent each segment so the resulting URL is well-formed while preserving the path separators. Addresses Copilot review comment on PR #70.
inercia
added a commit
that referenced
this pull request
Jul 19, 2026
CleanupArchivedSessions used UpdatedAt as a fallback for the deletion decision when ArchivedAt was zero, but still logged now.Sub(meta.ArchivedAt) as the "age" field — producing a ~two-millennia age (now minus the zero time) that is misleading to operators. Use whichever timestamp actually drove the decision for both the comparison and the logged fields so the two are consistent. Addresses Copilot review comment on PR #70.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.