Skip to content

feat(mecatui): bound schedule browsing on the shared surface - #2398

Open
jbeda wants to merge 2 commits into
mainfrom
impl/2173-schedule-surface
Open

jbeda wants to merge 2 commits into
mainfrom
impl/2173-schedule-surface

Conversation

@jbeda

@jbeda jbeda commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Move /schedule onto the shared, conversation-filling surface lifecycle, consistent with /sessions.
  • Bound schedule inventory and run history; put configuration, state, and complete lifecycle warnings in a separate scrollable schedule-details view. Back preserves parent selection and scroll position.
  • Correlate asynchronous results with their surface and request, serialize mutations, and reject snapshots predating a mutation. Preserve existing filtering, form validation, actions, and claimed/in-flight/deletion-pending distinctions.
  • Preserve atomic read-only fire-transcript handoff without changing the active chat or draft; populate Sessions inventory when returning from a directly opened transcript.
  • Keep hints truthful under remapped keys and document reusable placement, navigation, geometry, and ownership guidelines in docs/tui.md.

Closes #2173.
Relates to #1742, #1589, #2237, and #555. These umbrellas remain open.

Current status

Published PR head: 8354d4af5c3bb6eed6a81c3ebdb4af91948c54f8. The two existing schedule commits were explicitly rebased without conflicts onto origin/main at c4a043fd01eaa0f9649090a74d74ab3cee5f0e23 and pushed with an exact force-with-lease. Focused TUI suites passed after the rebase.

The approved creator-usability slice is implemented locally but remains uncommitted and unpushed. It is not part of the published PR diff yet. It adds:

  • Multiline/paste editing, conventional read-only checkbox, independent Create/Cancel, contextual guidance, retained failed input, and intentional draft abandonment.
  • Supported timing aliases including Run every day at 9am, local cron/clock validation, zoned next-occurrence estimates, and authoritative server next-run feedback.
  • Searchable city/IANA timezone selection with exact accepted identity, UTC default, pinned generated catalog, and standard embedded timezone-rule fallback. Backend invalid-zone and embedded-zone behavior remains unchanged.
  • Geometry-safe modal paste/private-widget routing, preserving root chat drafts and unrelated reconnect/catalog processing. Root input/render fixtures exercise every focus state at 80×24 and 100×30; these are automated fixtures, not manual visual qualification.
  • Server-default/possible plan-model and separate-run-history disclosure, plus the existing conversational scheduling alternative.

Local checks passed: focused UI/client/parser/catalog/generator suites; integrated task test; full post-fix task test:race; final task lint; task build; offline demo with tool call, permission ask/approval, and result; task docs; task site:build; git diff --check. The final test-only cross-surface editor-ownership negative control also passed focused race and lint checks. Four-axis final-tree review and targeted fix rechecks have no remaining findings.

Manual terminal qualification of the rebuilt client remains outstanding. No live/paid scheduled runs were created or fired. The creator slice is not claimed ready for merge or published on this PR head.

Deferred work was deduplicated: model picker #2414, form timing assistance #2415, shared prompt-editor core #2416, advanced policy #2417, timezone preference #2418, and server-zone/preview contract #2419. Existing #2149 and #700 received the terminal editing and explicit form-to-chat binding follow-ups, respectively. Toolchain checklist #2403 and checksum issue #807 remain separate.

Manual-test follow-up and drive-by bug fixes

  • Fill-height regression: the surface received the full conversation height but returned only its occupied rows, leaving the ghosted input and application footer above the terminal bottom. All nonzero schedule views now fill the offered height, including sparse lists, details, create/delete views, and compact fallback. Tests assert exact body height and application-footer placement.
  • Drive-by: non-mutating schedule creation was rejected by the server. Both the old and refactored forms omitted permission mode, which resolved to default; the existing server contract requires non-mutating schedules to use plan. Creation now explicitly sends plan when changes are disallowed and canonical default when allowed, and displays the effective mode in the form. Client request tests verify both mode/mutating combinations on the wire.
  • Drive-by: failed creation discarded the user's input. Both implementations cleared the form before the create RPC finished. The form now retains all values and focus while pending and after failure, displays errors inline, and lets the user correct and retry. It clears and returns automatically only after a correlated successful response. Pending input is blocked to prevent duplicate submissions; explicit Back/Close remains intentional abandonment and does not cancel an already submitted operation. Tests cover both mutating states, correction/retry, retained chat draft, and late success/failure after Back.

A comparison with the pre-refactor implementation found no additional refactor-introduced action, async-ownership, transcript-handoff, or list-rendering regressions. It also identified two older input defects outside these fixes: bracketed paste is dropped in schedule fields, and create-field cursor blink messages are not routed back to the field. Both are now fixed in the local, unpushed creator slice described above; the published browser/follow-up commits did not fix them.

Verification

Original surface implementation

  • Passed focused UI, bounded, client, and schedparse suites.
  • Passed task test, task test:race, and task lint.
  • Reran affected UI/bounded/client suites with race detection after the final hint refinement.
  • Passed task docs, task site:build, and offline go run ./cmd/mecademo (tool call, permission ask/approval, and result).
  • Passed git diff --check.
  • Independent Spec, Standards, Test adequacy, and Domain reviews completed; findings were resolved and incrementally rechecked. No unresolved findings.

Manual-test follow-up fixes (3a3617a49)

  • Passed the focused UI subpackage, client, and schedparse suites.
  • Passed task test, task lint, and task test:race; the final follow-up changes passed the full race suite.
  • Passed task build; rebuilt bin/mecatui for continued manual verification.
  • Passed the offline demo with tool call, permission ask/approval, and final result, plus git diff --check.
  • Independent Spec, Standards, Test adequacy, and Domain review is clear after rechecks. Empty-inventory/no-match fill coverage and actual default-mode request serialization coverage were strengthened during review.

The first full-test attempt on the original implementation hit the harness's long managed temporary-path limit for Unix sockets. Focused startup tests and full suites passed with the system temporary directory; the follow-up full gates used system temporary storage as well.

API compatibility

UI-only change. Existing RPCs, scheduler semantics, pagination, retention, and stored data are unchanged. The create-form fix supplies the existing permission-mode field consistently with existing server validation; it changes no authorization contract and grants no new permissions. The local creator slice uses existing trigger/timezone/mode fields with client-side validation; server invalid-zone and embedded-zone contracts remain unchanged. No wire API major/feature changes, exported engine Go API changes, stored-data migrations, or upgrade sequencing required.

Plan

The full approved implementation plan is preserved in the plan comment. Verification above records actual results for the original implementation and the committed follow-up fixes; verification in that plan describes the intended checks.

@jbeda

jbeda commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor Author

Approved schedule browser and creator implementation plans

  • Slugs: 2173-schedule-surface, additive schedule-create-usability.
  • Local provenance: .scratch/2173-schedule-surface-plan.md and .scratch/schedule-create-usability-plan.md.
  • Existing branch: impl/2173-schedule-surface; base: main.
  • Browser's historical start: e17d8394d365a94fb79258fa02a940625bfb99ff.
  • Explicitly authorized rebase onto: c4a043fd01eaa0f9649090a74d74ab3cee5f0e23.
  • New creator slice's immutable implementation/review start: 8354d4af5c3bb6eed6a81c3ebdb4af91948c54f8.
  • Verified published PR head at archive update: 8354d4af5c3bb6eed6a81c3ebdb4af91948c54f8. The creator slice is local/uncommitted, not claimed published on that head.
  • Updated: 2026-10-18T21:11:42.407369+00:00.
  • SHA-256 of the full combined plan below: 718caea521f638909759ef42dae457dff1d3585a5c8dcbf10824d32fa262c748.

Amendment and verification status

The operator explicitly authorized rebasing, pushing the rebased existing commits, and executing the last session's approved creator plan. That supersedes the historical planning-session-only/no-rebase notes retained verbatim below; it does not authorize merging. The existing archive is extended in place, not duplicated.

The creator adds supported timing phrases and local validation/preview, searchable explicit timezone selection with a pinned generated catalog, multiline/paste editing, conventional read-only checkbox, explicit Create/Cancel, persistent focused-field help, error retention, execution/result disclosure, and the existing conversational scheduling alternative. It retains the browser hierarchy and all named deferrals. The normal-terminal implementation uses a focused-field viewport and a review view rather than requiring all help and fields to fit simultaneously.

Verification sections below are intentions, not a claim all checks or manual terminal qualification are complete. Current execution evidence is maintained locally; the PR description will distinguish published code from uncommitted work. No private credentials, logs, or reviewer transcripts are included. Both selected plans are preserved in full.


/schedule bounded surface plan

Slug: 2173-schedule-surface
Implementation branch: impl/2173-schedule-surface
Base branch: main

Issue: #2173. Umbrellas: #1742 / #1589 (bounded), #2237 / #555 (surface).

Goal and scope

Move /schedule onto the existing one-active-modal surface contract and bound its inventory and fire-history lists. Use the full-conversation browser placement of /sessions. Preserve scheduling semantics and existing form/action/filter/transcript behavior, except the explicitly approved split of inspect into runs and details. Add reusable contributor guidelines to docs/tui.md during implementation rather than a migration log.

The operator confirmed fill placement and the three browsing levels: schedule inventory → runs → schedule details. Fire transcript activation remains a separate transition to the existing Sessions transcript viewer, not a fourth nested schedule level.

Implementation is authorized by the operator's follow-up request. The operator also requested rebasing onto origin/main and checking incoming session changes before implementation. The resulting implementation/review baseline is e17d8394d365a94fb79258fa02a940625bfb99ff; the impact assessment and execution evidence live in .scratch/2173-schedule-surface/run.md. No commit, push, PR, or merge authority is implied.

Pre-change behavior

  • cmd/mecatui/ui/schedule.go:597 and :685 ignore offered width/height and render every schedule/fire. This is a legacy conversation-body takeover, not a true fill surface or a parent-framed card.
  • /sessions explicitly selects modalPlacementFill (sessions_surface.go:364) and uses a bounded inventory, preserving the main header/prompt/footer. The surface interface supports this placement generically; docs/tui.md:123-145 describes Sessions as an example, not an exclusive exception.
  • Legacy schedule state/routes live on Model (model.go, view.go, update.go); surface.go defines the existing ownership, render-offer, wheel-capture, teardown and narrow-intent seams.
  • Schedule names address RPCs; use Schedule.Spec.Name for schedule identity. Fire rows use ScheduleFire.ID; SessionID is only the linked transcript target.
  • Filter is explicitly focused by /; bare c/p/r/f/d remain action shortcuts. r means resume, not refresh. Escape exits filter focus, clears a retained filter, then closes. Inspect/create/confirmation back out to inventory.
  • Fire transcript activation currently drops schedule browsing and calls the existing read-only Sessions transcript path; it must not rebind the active chat (schedule.go:461-481). Do not add a return-to-schedule stack without a separate decision.
  • Async result messages have no surface/request ownership. In particular, any delayed ScheduleFiresMsg can replace current fires (schedule.go:538). UI-local command wrappers can tag results without changing client/protobuf contracts.
  • Inspect metadata is substantial (schedule.go:685-774); wrapping spec/state/warnings can consume the entire height even before the fire list. This is a material layout risk, not solved by bounding fire rows alone.

Proposed approach

  • Allocate schedule state dynamically on open into m.modal, capture dependencies, and move open-lifetime state/key/result logic there. Keep panel/inspect/create/confirmation views within the same surface. Reuse parent placement and generic dispatch; remove only legacy schedule branches/field.
  • Use fill placement matching Sessions across the schedule surface's local views, not a new card/compositor mechanism. The parent retains the header/prompt/footer and sizes the conversation offer.
  • Use two bounded.List controls with stable IDs, presentListRow, shared navigation translation, physical-row budgets, overflow indicators, keyboard reveal, and wheel scrolling without cursor movement. Preserve list selection/window when backing out and across compatible refresh/filter/reorder. If selected identity disappears, apply the existing bounded fallback and test which item actions now target.
  • Surface-local instance/request tags distinguish list, inspect spec, fires, create and action results. Validate expected schedule/action as well as the active request, invalidate inspect results on view changes, and drop results after close/reopen. Track reads relative to mutations: invalidate snapshots started before a mutation, refresh authoritatively after successful mutation, and do not discard a still-owned mutation completion merely because another request started. Use a single pending mutation within the open surface to avoid duplicate/overlapping action submissions; navigation/close remain available. Preserve user-visible success/error feedback for owned actions; cancellation is not rollback of a server mutation.
  • Emit narrow intents for root-owned effects: existing read-only transcript loading and root status notices as needed. The transcript intent must atomically close Schedule, invoke the existing Sessions transcript loader, and terminate common post-dispatch processing; opening Sessions and then letting the old closed flag run would close the replacement surface. Transcript Escape continues to the Sessions inventory, not back to Schedule. Test through real dispatch including load failure. Do not make generic routing inspect concrete schedule state.
  • Keep create/delete semantics; geometry safeguards and compact close-only behavior must prevent invisible submit/delete actions. No new bounded browsing widgets for forms.

Confirmed interaction contract

  1. Schedule inventory: bounded schedule selection, existing action shortcuts and explicit / filter focus. Choose opens the selected schedule's runs. The normal close action exits filter focus first, then clears a retained filter, then closes the surface. Selection and window survive returns from child views.
  2. Runs: bounded fire history plus a concise schedule/lifecycle summary. Preserve enabled/paused, deletion pending, claimed, in-flight, and overdue/termination-unconfirmed meaning. A discoverable Details action opens the same schedule's details; it must not activate or change the selected fire. Choose or bare t opens the selected fire's existing read-only transcript when available. Missing replayer/session ID gives existing non-destructive feedback. Close returns to inventory, retaining the selected schedule/filter/window.
  3. Schedule details: a bounded.Viewport for existing schedule configuration/state and complete lifecycle warnings, not details of the selected fire. Keep the existing 120-character prompt preview; a full prompt feature is outside this slice. Shared line/page/top/end navigation and wheel affect the detail viewport only. Close returns to runs, retaining fire identity/window. Reset detail offset on a different inspected schedule; reflow/clamp on resize so Up works immediately after overscroll. Choose must not accidentally open a transcript or submit an action from this view.

Fill placement applies to all three browsing levels. At insufficient geometry show the bounded close/back fallback, consume unrelated input, disable hidden activation and preserve state for a later larger offer. At zero width/height return no body/hits and still allow close. The compact close action closes the surface directly, consistent with existing surface fallbacks; normal-size close follows the hierarchy above.

The implementation chooses a Details chord consistent with the captured keymap and existing bare-action conventions, and exposes its actual hint. Do not give the same chord two actions in the same focus context. Test that configured Choose/Close/navigation bindings remain authoritative. This is a local key choice, not a new UI-wide keymap framework.

No material operator decisions remain open.

Acceptance and implementation outline

  1. Acceptance fixtures (first): extend schedule/UI tests at real dispatch seams with many schedules/fires, normal/wide/narrow/short/zero offers, long wrapped rows and lifecycle warnings. Capture the approved inventory → runs → details flow and existing fire-transcript exit. Completion: tests demonstrate today's missing geometry/lifecycle behavior without source-text assertions; use offline/test-owned fixtures.
  2. Surface lifecycle + root handoff (integrate together): in schedule.go (or a focused schedule surface file), dynamically allocate surface state and move keys/async state/teardown onto it; remove schedule-owned state/routes from model.go, view.go, and update.go. Implement UI-local owner/request wrappers and narrow transcript/status intents through existing applySurfaceIntent/stopSurfaceDispatch. Preserve open gating, form/action/filter semantics; update surface_arch_test.go narrowly. Completion: focused schedule/lifecycle/structural tests pass, including close/reopen, mutation/read cross-ordering, and real-dispatch transcript replacement/load failure/Escape with unchanged active chat and prompt.
  3. Bounded inventory, runs and details (depends on 2): use two lists plus the details viewport; shared presentListRow/navigation; fill placement and approved back hierarchy. Measure rendered chrome, reserve summary/footer/indicators, and keep content/hits within the offer across resize. Preserve lifecycle distinctions without inferring termination. Completion: geometry, keyboard endpoint/page, wheel ownership, identity/window preservation, details overscroll/reflow, compact hidden-action suppression and Details-key discoverability tests pass. Selection disappearance must follow tested bounded fallback; subsequent action/transcript targets the visible selected ID, never an obsolete index.
  4. Integration ordering regressions (depends on 2–3): add deterministic tests for out-of-order lists, spec/fires A after B, repeated inspect of the same name, switching runs/details during an owned request, list-before-create arriving after success, action completion versus relist, failures, pending-mutation gating, close/reopen and late results after transcript replacement. Completion: no obsolete result mutates a new view/surface or erases confirmed state; owned errors remain visible without rolling back durable operations.
  5. Consistency docs + final qualification (depends on 2–4): update docs/tui.md with only reusable discoveries: fill versus card criteria, metadata-separated bounded browsing, normal back hierarchy and preserved parent windows, owned async/atomic surface replacement, and compact no-hidden-actions. Preserve deliberate filter/action differences rather than redesign Sessions. Add a concise owning user-doc explanation of the new runs/details navigation after activating tech-writer and reading user-docs/AGENTS.md. Completion: docs describe implemented behavior, not this plan; focused tests and integrated gates below pass.

Verification and delivery

  • Focused schedule/UI/bounded and direct transcript/placement/structural regression tests during iteration.
  • Integrated task test once; task docs for Markdown; before PR readiness task lint and task test:race, each with a 600-second timeout, plus offline go run ./cmd/mecademo showing tool call, permission ask/approval and result.
  • Implementation panel-review compares against the implementation branch's recorded start commit (not an invented OID or moving main). Target fixes incrementally against the last reviewed candidate.
  • API compatibility: proposed UI-only ownership/presentation change, no wire/protobuf/client message changes, no major/feature bump. Confirm during implementation; stop for approval if a conforming remote client contract would change. Stored scheduler data and API semantics stay unchanged.
  • Review complete: initial fresh-context critique identified mutation/read cross-ordering and atomic transcript replacement corrections; these were incorporated. After the operator confirmed fill placement and the three-level hierarchy, the same reviewer rechecked the amended design against source and reported no remaining blocker, important, or advisory correctness/requirements gaps. The ignored plan was unavailable in its worktree, so both passes used authoritative inlined plan content/amendments. Review is design evidence only: no implementation or tests have run.
  • No commit/push/PR/merge implied. When an implementation PR is explicitly requested, retain the latest publishable ignored plan as one verified full plan comment and use non-closing umbrella references; only completed child scope may close mecatui: bound /schedule inventory and fire-history browsing #2173.

Schedule create usability — implementation plan

Slug: schedule-create-usability
Implementation branch: existing impl/2173-schedule-surface (finish PR #2398; do not create a new branch)
Base branch: main
PR: #2398
Published starting commit: 3a3617a4960dc93fa4489b41645c7d45cdfb7b98
Original browser plan: .scratch/2173-schedule-surface-plan.md; this is the additive create-usability slice.

Goal and confirmed scope

Make creation usable before #2398 merges: enter a real task, understand timing/execution policy, submit deliberately, and correct errors without losing work. Joe will clear the session and request implementation next; do not start implementation in this planning session.

Joe confirmed:

  • Finish the usable minimum in the same PR, not a new broad scheduler project.
  • Supported timing phrases plus cron, not arbitrary English or an automatic model call inside the form.
  • Explicit timezone, default visibly UTC, with a proper searchable autocomplete so users need not know IANA spelling.
  • Defer model picker; clearly disclose deployment-default/plan-mode policy.
  • Multiline prompt and paste now. Defer extracting the session prompt editor.
  • Defer editing existing schedules (a painful but explicit cut), advanced policies, and automatic form-to-chat delivery.
  • Restore field-entry guidance and clear focus lost by refactoring. Old trigger placeholder showed cron (e.g. 0 9 * * *) or NL (e.g. every 30 minutes); focused row used accent styling. Help must remain available after typing, not only as a disappearing placeholder.
  • Tell users ordinary chat can already manage schedules via Schedule/ScheduleQuery. Deferred model-assisted timing means a new automatic interpretation call inside the form, not today's conversational workflow.
  • No new CI jobs, network checks, container tests, toolchain matrix, or material long-pole expansion.

Existing seams and constraints

  • cmd/mecatui/ui/schedule.go owns form/focus/rendering and mutation/result correlation. Preserve failed-form retention, explicit Back/Close, stale-result rejection, and root fill placement.
  • cmd/mecatui/schedparse/schedparse.go is the existing fixed-pattern compiler. Calendar one-shots use supplied now.Location; the caller currently sends unmatched strings as unchecked cron.
  • Backend engine/adapter/cronparse/cronparse.go uses existing github.com/robfig/cron/v3.ParseStandard and Next, rejecting zero next occurrence. Use the installed dependency client-side; no production ui import of engine/host packages. Test client/backend parity outside ui production.
  • internal/adapter/scheduler/scheduler.go:LoadLocation already supports IANA zones but silently falls back to UTC on failure. Reject invalid form input locally; do not tighten that server contract in this slice.
  • cmd/mecatui/client/schedule.go already carries Timezone, Mode/Mutating and response.NextFireAt. No validate/preview RPC exists; do not add one.
  • ui/prompttextarea is intentionally session-specific. Use installed upstream Bubbles textarea directly, not root attachments/staging/history/submission state.
  • ui/update.go:onPaste/prepareSurfaceInput/dispatchSurfaceMsg: bracketed paste bypasses surface dispatch today. Route generically to the open focused surface using a fresh geometry offer. Unhandled modal paste still drops, never reaches the hidden chat; no schedule-type assertions in shared dispatch.
  • Form creation is out-of-band: no current-chat origin/model inheritance. Placement is server-owned; results stay in schedule runs/transcripts. No workspace or delivery-routing field.

Minimum form and input contract

Controls: Name (single-line), Prompt (bounded multiline textarea with internal scrolling), Timing (single-line plus supported examples), Timezone (searchable autocomplete default UTC), Read-only (checked checkbox), separate focusable Create and Cancel.

  • Checked read-only sends Mutating=false/Mode=plan; unchecked sends true/default. Space toggles the focused checkbox; Enter on it cannot submit. Explain unchecked uses ordinary permissions, not automatic approval.
  • Tab/Shift+Tab move focus. Textarea arrows edit its cursor; Enter inserts prompt newlines. Enter activates a focused button; a single-line Enter may advance but never implicitly submits from the last input. Respect captured/remapped action bindings and make hints truthful for current focus.
  • Esc/Cancel intentionally abandons the form. Pending freezes edits/duplicates except explicit Back/Close, which cannot cancel a submitted operation. Local/RPC failure preserves every value and focus; automatic clear/return happens only after correlated success.
  • Bracketed paste preserves normal insertion/selection replacement and normalizes existing terminal newline encoding. Single-line widgets apply their normal single-line policy. Paste is never interpreted as shortcuts, never changes active chat or stages root prompt placeholders/media. Route cursor/blink messages to the active field.
  • Bound textarea and suggestion-list geometry. Compact/zero offers prevent hidden actions/paste; preserve draft on resize. Keep header, ghosted chat prompt, and footer anchored.
  • Show field-local errors or interpretation: cron/macro or absolute instant, timezone/offset, dated next occurrence. Label local preview as an estimate; after Create show server-returned authoritative NextFireAt. Invalid/incomplete input disables Create with a visible reason. Revalidate at submission and freeze submitted values.
  • Static model disclosure: server default; read-only runs may use the configured plan model. Do not name an inferred exact model. Explain separate run sessions and history-based results.
  • Short non-actionable inventory/create hint: users can ask in chat to schedule/manage tasks, with a concrete read-only weekday/timezone example. Docs distinguish chat tool-origin result delivery from form-created run history, and scheduling availability/normal permissions/backend validation. Do not auto-send examples, call a model inside the form, or add a chat handoff workflow.

Searchable autocomplete and timezone catalog

Build a thin reusable field over installed Bubbles textinput plus existing bounded.List, not a general form framework or new Huh dependency. Built-in textinput suggestions alone are prefix-only/inline and do not satisfy city search.

  • Caller supplies stable value, display label, and search text. Control owns only query, bounded matches/window/selection, focus/acceptance, and geometry; timezone knowledge stays with caller. No speculative migration of other controls.
  • Search case-insensitively by city/display text and canonical identifier; underscores match spaces (Los Angeles finds America/Los_Angeles). No geolocation, country/abbreviation inference, online service, or remembered preferences.
  • Up/Down select visible matches; Enter accepts without submitting the form. Tab navigates form focus, not Bubbles' default accept-suggestion binding. Esc first dismisses an open suggestion list without cancelling the form; a subsequent Esc follows normal form Back.
  • Typing/paste edits the query. Only a selected/accepted option or a fully valid typed identifier yields a valid timezone. Editing to an invalid query invalidates the field; never silently submit the previous UTC/accepted value behind new unmatched text. Selection alone does not commit. Preserve query and committed/draft identities correctly through resize, blur/refocus, and errors.
  • Accepted form identifiers: explicit UTC, or slash-qualified identifiers present in the pinned Go timezone archive and loadable by Go (including existing database aliases). Exclude bare abbreviation-like names and host-dependent Local. Display/store the exact identifier; do not invent alias canonicalization. Labels may render underscores as spaces. Validate typed/pasted values by the same rule as options.
  • Default UTC is an actual valid value, not a hidden fallback. All accepted zones are searchable; no small hand-picked list.

Catalog maintenance is generated and checked in, not generated on every build. No new Go module. Concrete ownership: internal/timezonegen/cmd/timezonegen is a stdlib-only generator; output is cmd/mecatui/internal/timezones/zones.json, bundled by that narrow client package. Store sorted UTC+slash-qualified names, source Go version, and SHA-256 of source GOROOT/lib/time/zoneinfo.zip. Generator reads the root go.mod Go directive as the selected source-toolchain version (the same version existing CI setup-go provisions), requires that exact active runtime version, and fails with an actionable GOTOOLCHAIN command on mismatch rather than generating from an arbitrary newer local Go.

Add task generate:timezones running GOWORK=off go run ./internal/timezonegen/cmd/timezonegen --output cmd/mecatui/internal/timezones/zones.json. Have existing task generate invoke it before downstream generation/verification; no new CI step/job. Maintenance selects the matching toolchain explicitly: currently GOTOOLCHAIN=go1.27.2 task generate:timezones (replace version after a deliberate root Go upgrade). Only this task writes the generated asset. Document that generation can require the selected toolchain to be installed, unlike ordinary tests/builds which support newer Go; do not solve all local toolchain selection or #807 here.

Existing package tests always check sorted unique names, accepted identifier shape, UTC presence, metadata integrity, and successful zone loading, without rewriting anything. Freshness compares generated metadata/names/archive fingerprint against the declared root CI toolchain only when the active runtime equals that declared version. On that path (including existing pinned CI), stale/missing provenance or contents FAIL with the regeneration command: bumping root Go without refreshing the asset cannot silently skip CI freshness. On supported newer ambient local Go, run all internal/semantic checks and explicitly report that source-archive freshness requires the declared toolchain; do not compare a different toolchain's rules/archive or fail ordinary tests just because it is newer. No automatic download, new job, or duplicate generator compilation in tests. Test both matching-source and newer-runtime decision paths with injected version metadata, including a negative control proving a declared Go bump with stale provenance fails the matching CI path.

Runtime rules are separate: standard time/tzdata fallback linked by the client timezone-data leaf (cmd/mecatui/internal/timezones, or an equivalently narrow existing client leaf selected during implementation) and by internal/adapter/scheduler for host consumers. The catalog contains names, not rules. Go may prefer system zoneinfo; catalog regeneration alone never updates DST rules. Avoid reimplementing Go's loader merely to force a test seam. Verify loading/DST semantics and catalog validity in ordinary isolated tests; use archive TZif data with time.LoadLocationFromTZData for deterministic rule fixtures where needed. Rely on the standard library's embedded-fallback contract rather than adding privileged/container fallback proofs to CI.

Maintenance runbook issue filed: #2403. Related existing #807 covers exact local toolchain/generator checksum isolation. This slice adds only generator maintenance notes; do not implement the broad upgrade runbook.

Finite timing grammar and interpretation

Preserve old intended meanings and document exact examples; optional leading run applies only to supported phrases, case-insensitively. Trim surrounding whitespace, do not consume arbitrary trailing prose.

Accepted family Output/meaning
every N minutes (1–59), singular unit where existing */N * * * *
every N hours (1–23), singular unit where existing 0 */N * * *
every hour 0 * * * *
daily at H[:MM] [am/pm], new alias every day at ... local-zone daily cron
every weekday at ... local-zone cron Monday–Friday
every monday/.../sunday at ... local-zone weekday cron; use parser-valid Sunday numbering (0), not invalid 7
next monday/.../sunday H[:MM] [am/pm] existing next matching future weekday/time in chosen zone
in N minute/hour/day/week(s) existing positive elapsed duration from now; reject overflow/past results
tomorrow at H[:MM] [am/pm] next calendar day/time in chosen zone
raw five-field cron and supported @ macros existing backend grammar, validated locally

12-hour inputs require 1–12; 24-hour inputs 0–23; minutes 0–59. Preserve existing intended supported forms; do not expand into noon/holidays/arbitrary sentences/date grammars. Existing cron-aligned interval semantics remain (not a new elapsed @every rewrite); expose actual cron interpretation so alignment is visible.

  • Unknown prose/bad clocks/cron/impossible dates produce actionable local errors, not a raw server parser dump. Backend cadence/governance restrictions still produce authoritative RPC errors with retained form.
  • The timezone field is authoritative in this form. Reject both CRON_TZ= and legacy TZ= prefixes in raw input with guidance to use the timezone control. These are outside the form's documented five-field/macro grammar; do not change backend/API support. Test both prefixes, leading whitespace, and case variants at the local-validation/fallback boundary so hidden zone overrides cannot survive.
  • Submit recurring cron plus chosen IANA name unchanged, not a fixed UTC cron offset; preserve local wall-clock schedules through DST.
  • Calendar one-shots use now.In(chosenLocation), then submit an absolute instant (UTC encoding fine). Duration-relative forms remain elapsed durations. Preserve underlying Go/cron DST gap/fold behavior, display actual offset/instant; do not invent new ambiguity policy.
  • One validated result carries trigger, zone, preview and field errors for both preview and Create, with a controlled clock. Revalidate time-sensitive one-shots at Create; relative previews are estimates, not promises of a frozen pre-submit instant. Match backend ParseStandard/Next strict-after and impossible-expression behavior for supported grammar.

Dependency-ordered tasks and completion

  1. Timing/zones/catalog. Reuse phrase compiler and cron dependency; add bounded validation/preview, finite aliases/clock checks, timezone leaf, generator/check task, and standard tzdata ownership. Completion: reported phrases, old intended forms, UTC/IANA/DST/one-shots work; bad/embedded-zone input never calls Create; catalog generation deterministic and fresh; parser/next-time parity tests pass without engine imports into ui production.
  2. Autocomplete component. Compose textinput/bounded.List with stable label/value identity, substring search, explicit acceptance, remappable/truthful keys, paste and zero/compact geometry. Completion: city query finds correct zone, highlighted-but-unaccepted option cannot submit, invalid edited query cannot retain a hidden old value, resize/blur preserve appropriate state, no generic framework/dependency.
  3. Form controls + safe input routing (integrate together). Upstream multiline textarea, timezone autocomplete, checkbox, explicit buttons, focus-aware guidance, generic geometry-prepared paste/cursor routing. Completion: real Model.Update proves multiline/paste/selection replacement, no hidden-chat leakage, checkbox separate from submission, pending/compact protection, full failed-draft retention and intentional Back.
  4. Preview/disclosure/request integration. Bound preview/errors and persistent examples, expose model/result policy and chat-management alternative, serialize existing fields, show authoritative saved next fire. Completion: correction/retry sends intended exact spec once, preserves chat identity/draft, and new hints cannot create operations or promise form-to-chat delivery.
  5. Qualification/docs/issues. Use tech-writer/user-docs guidance for non-obvious grammar/timezone/model/chat-vs-form behavior. Check duplicates before filing approved follow-ups. No architecture rewrite unless a stable claim becomes false. Manual terminal qualification remains necessary; no paid/live firing without explicit authority.

Verification and CI budget

No new jobs, network/container checks, extra toolchain matrix, redundant generator compilation/invocation on critical path, or unbounded UI matrices. Catalog freshness and ID validation belong to existing package tests. Keep UI fixtures offline and deterministic. Measure added package-test cost during implementation; reduce fixture work rather than weakening behavioral assertions.

Local exploratory Go 1.27.2 probe: 598 raw archive entries / 9,102 serialized bytes; enumeration/sort/serialization 0.14–0.92ms; all-name validation about 100ms first observed, 2.8ms warmed. Filtered selectable list will be smaller. These are workload timings, not measured CI wall time/compilation. Probe: .scratch/schedule-timezone-cost-probe.go (ignored research, not production).

Required checks:

  • Real input/dispatch tests: keyboard, multiline paste/selection replacement, pasted shortcuts negative controls, timezone acceptance/query identity, truthful hints, Space checkbox, explicit buttons, local/RPC error correction, retained focus/values, pending duplicate prevention, Back/late responses.
  • Geometry: wide/narrow/short/zero, long prompt/errors/matches, internal scrolling, resize restore, no hidden activation, exact fill and root-footer placement. Help remains accessible after populated input; test affordances not arbitrary documentation prose.
  • Timing/catalog: exact reported phrases, old intended forms including Sunday, clock/overflow/prose/cron errors, impossible dates, typo/UTC/alias/bare abbreviation policy, summer/winter local 9am, calendar and elapsed one-shots in nonlocal zone, expired Create, both embedded TZ prefixes, strict-after/impossible parity, deterministic generation/check, rule loading/DST.
  • Actual client request serialization: chosen Timezone/trigger/Mode/Mutating, absent inferred model/origin overrides; authoritative returned NextFireAt presentation. Chat-management disclosure must not imply automatic form model invocation or current-chat delivery.
  • Focused suites while iterating; integrated task test once; task lint, task test:race (600-second full-gate timeouts), task build, offline demo with tool call/approval/result; task docs for Markdown changes. Use system temp for known long Unix-socket paths.
  • Four-axis incremental implementation review against this slice's actual recorded start, not moving main; targeted fix rechecks. No rebase without explicit request.
  • Rebuilt-client manual smoke: multiline pasted task, city autocomplete, zoned daily preview, invalid local field, controlled RPC rejection/correction/retry, saved next run, checkbox/Create separation, narrow/resize/Back. Render tests are not visual verification.

Deferred backlog and delivery

Only Go-upgrade checklist #2403 was filed during planning. During implementation, check existing issues then file/update focused follow-ups for: edit-existing without state reset; model picker/effective plan-aware identity; automatic model-assisted timing inside form; reusable prompt-editor core; advanced policies/budgets/timeouts/retries/context; explicit form-to-chat completion binding; remembered timezone preferences; separate server fail-closed invalid-zone behavior/preview parity (wire assessment). Relate these to #2398; do not duplicate #2403 or #807.

API compatibility: existing fields/RPCs, no wire major/feature, exported engine API, storage migration, or authorization change. UI accepts explicit zones and validates before requests; backend embedded-zone/invalid-zone contracts remain unchanged. Standard embedded data improves rule availability, not rejection semantics. Stop for separate assessment/approval if stricter server behavior becomes necessary.

No implementation, branch change, commit, push, or PR-plan publication in this session. Joe requested the checklist issue and plan/handoff update, then will clear and start implementation. On implementation authorization, archive the approved combined browser/create scope in the PR's existing single plan comment (verify mapping/privacy/conflicts/readback); current archived comment still contains only the older browser plan. Keep existing comment, don't create another plan dump.

Review status

Initial fresh-context critique found embedded-zone precedence, vague timezone-name policy, tzdata ownership/proof, and finite grammar gaps. Revised plan resolves them with: authoritative timezone field/reject both prefixes; UTC+archive slash-ID membership; concrete client/server link owners and no new CI proof infrastructure; finite grammar table; one preview/submission result. User subsequently confirmed searchable reusable autocomplete, checked-in toolchain-derived catalog, explicit maintenance generation, and no CI-job/long-pole expansion.

Two incremental design rechecks completed. The first identified source-toolchain freshness versus supported newer local Go and provisional generator ownership. The final recheck accepted the root-declared-source CI check/newer-local diagnostic split, negative controls, concrete generator/asset/task, and prefix-boundary cases: no remaining blocker, important, or advisory design findings. Reviewer worked from the supplied scope-complete brief and complete changed sections because ignored scratch was unavailable in its checkout; this is design evidence only, not implementation/tests/runtime proof. Reviewer agent ID: subagent-a8de2895d7051b91e83af3a871c9b90c-call_ZoJZqX2FRUvJ5OY9x8w0UBXt.

No material product questions remain open. Capture any new load-bearing discovery during implementation instead of silently expanding scope. Joe requested session clear/handoff now and implementation in the next session; retain this plan and use plan-implement after that explicit implementation request.

jbeda and others added 2 commits October 18, 2026 12:40
Move schedule inventory, run history, and schedule details into a fill
surface with bounded controls, stable selection, and owned async results.
Preserve read-only transcript handoff and document reusable TUI conventions.

Co-authored-by: Mecatl <noreply@mecatl.dev>
Explicitly submit plan mode for read-only schedules and default mode for mutating schedules, showing the effective mode in the create form. Retain form values and focus until creation succeeds, prevent duplicate pending submissions, and cover retry and explicit Back behavior.

Co-authored-by: Mecatl <noreply@mecatl.dev>

This branch was successfully deployed

1 active (outdated) deployment
Preview — 945ea0f3 Deployed Oct 17, 2026 by vercel[bot]
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.

mecatui: bound /schedule inventory and fire-history browsing

1 participant