Request lifecycle honesty: cancel_write, staleness tiers, badge fix (goal 0026) - #12
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
…e fix (goal 0026) Delivers all seven Mill-side items: a cancel_write MCP tool (the missing fourth verb alongside park/poll/resolve, ungated, a status distinct from denied); age-tiered staleness presentation with "expires in Nh" across Review, the MCPWriteApprovals banner, and the floating approval prompt; a requester-liveness heartbeat (LastPolledAt, shown only past a 5-minute no-noise bar); resolved MCP writes now durable in Review's Recently-resolved; Activity's MCP-write rows are expandable with a jump-to-workflow preview; and stuck-ENQUEUED runs get age emphasis plus Stop in WorkflowRunsPanel and Activity's runs explorer. Fixes a real bug found live: ResolveMCPWrite/CancelMCPWrite/the expiry sweep never fired the pending-changed signal at all, so the sidebar badge could hold a phantom count against an empty queue — traced to Wails3's own registered-event type check silently dropping an empty-struct payload against a typed event registration. Also: e2e's shard matrix gains fail-fast:false after a real incident (PR 11) where one shard's real failure cancelled two others mid-run before they could report their own verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
alicoding
enabled auto-merge
August 12, 2026 04:28
… self-cancel Owner-mandated framework, researched to the converged standard and found to need NAMING, not building: Scrum 2020's DoD (a formal artifact commitment) + Kanban University's ready-for-delivery pull criteria are the industry vocabulary for gates this repo already runs — consolidated into ONE always-loaded rule (.claude/rules/delivery-discipline.md) instead of four documents. Estimation deliberately excluded (negotiates team capacity a solo+agent loop doesn't have); peer review already settled by ADR-0034. Build-health becomes a ritual, not a wonder: gh pr checks --watch after opening a goal PR; gh run list -b main -L 1 at goal pickup. Tech debt: same queue, same DoR/DoD, never a shadow register. Plus the real config bug the research found: cancel-in-progress applied to main's post-merge runs too, so two quick merges cancelled the earlier one's verification (observed live: merges #1/#10) — now conditional per GitHub's own documented pattern; PR runs still supersede, main runs always complete. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
alicoding
added a commit
that referenced
this pull request
Aug 15, 2026
…y (task #12) Mill as MCP server for the first time -- §3.1 named three possible MCP roles (server, client, host) back when only client was ever built (§3.6, wrapping external tools as workflow nodes); this closes the server role, already locked as fine ("fits §1.1 cleanly with zero tension") since no LLM/agent loop runs inside Mill here -- an external agent's own host connects and reads, the same shape as httpconnector being an HTTP client. Does not touch or reopen the still-disputed MCP-host question. internal/adapters/mcpserving wraps modelcontextprotocol/go-sdk's server role (the same SDK Mill already depends on for the client role) behind a thin New()/Serve() pair -- new role, not a new dependency. millmcpservice.go registers two URIs per entity type: a plain index resource (mill://workflows, mill://requests, mill://lists, mill://mcpservers) listing every current ID/Label/Description, and a ResourceTemplate (mill://workflows/{id}, etc.) whose read returns the full definition by calling the SAME Export* methods tasks #10/#11 just built for the UI's own Export buttons -- one read-model, reused, not a second one built for this. Secrets stay excluded by the same construction Export* already guarantees, verified independently through this new code path rather than assumed inherited: a real test connects a genuine MCP client over real HTTP, sets a real secret on a real HTTPRequest, reads it back through mill://requests/{id}, and asserts the secret never appears in the wire response. Binds 127.0.0.1:8090 by default (MILL_MCP_ADDR overrides), loopback-only deliberately -- a new unauthenticated local listener, same conservative- default reasoning as the LAN-exposure question elsewhere in this repo. Runs in both desktop and server-mode builds (no build tag); confirmed the second listener doesn't conflict with server mode's existing :8080 via a real Playwright smoke run. A bind failure is logged, not fatal. Explicitly NOT built here, named directly rather than silently dropped: the write side (create/import via MCP Tools). §8's guardrail policy is still OPEN, and programmatic writes are a materially different risk than read-only exposure -- tracked as its own task (#13), not folded into this one. Verified: two new Go tests connect a genuine MCP client (the SDK's own client role, not a mock, not a direct call into the handler functions) over real HTTP -- resources/list returns the real registered set, resources/read against a just-created real workflow returns its real node data, an unknown ID returns a real resource-not-found error, and the secret-exclusion test above. Full existing suite passes with -race. go vet, golangci-lint (0 issues after fixing two real errcheck findings on deferred session.Close() -- fixed using this repo's own existing defer func() { _ = x.Close() }() convention, not suppressed), ls_lint, check-loc.sh all clean. Documented in docs/SPEC.md §3.6. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
alicoding
added a commit
that referenced
this pull request
Aug 15, 2026
Request lifecycle honesty: cancel_write, staleness tiers, badge fix (goal 0026)
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.
Summary
cancel_writeMCP tool: the missing fourth verb (park/poll/resolve/WITHDRAW) — the requesting client withdraws its own still-pending write;cancelledis a distinct outcome fromdenied, ungated, at-most-once.MCPWriteApprovalsbanner, and the floating approval prompt — one sharedshared/staleness.ts/StalenessBadge.tsx.check_write_statusnow recordsLastPolledAt, surfaced as a muted "requester last checked Nm ago" hint only past a 5-minute no-noise bar.ResolveMCPWrite/CancelMCPWrite/the expiry sweep never fired the pending-changed signal, so the sidebar badge could hold a phantom count against an empty queue. Root cause:main.go'sRegisterEvent[MCPWriteRequest]("mcp-write-approval")binds that event to an exact Go type — an empty-struct{}{}payload silently failed Wails3's own type check and was dropped. Fixed by emitting a zero-valueMCPWriteRequest.canExpand/resultmechanism) with a jump-to-workflowWorkflowHoverPreviewwhen the gated tool named an existing workflow.CancelRunaffordance inWorkflowRunsPaneland Activity's runs explorer.e2ejob's shard matrix gainsfail-fast: falseafter a real incident (PR 11, run 31557343422) where one shard's genuine failure cancelled two others mid-run before they reported their own verdict.Test plan
go vet/go vet -tags server— cleango test . ./internal/... -race -cover— all packages passgolangci-lint run— 0 issuesgo build .(desktop) andCGO_ENABLED=0 go build -tags server .— both cleanscripts/check-loc.sh/scripts/check-rules-frontmatter.sh— cleannpm run lint/npm run boundaries/npx tsc --noEmit/npx vitest run(197 tests) — cleanTestMCPWriteTools_CancelWrite_*,TestListRuns_EnqueuedRun_PresentationFieldsAndCancelPath(real DBOS ENQUEUED→CANCELLED path)mcp-write-cancel.spec.ts(3 tests: cancel lifecycle, phantom-badge deny repro, Activity jump-to-workflow),mcp-write-staleness.spec.ts(backdated-write age emphasis across all 3 surfaces)staleness.test.ts(15),enqueuedStale.test.ts(4)🤖 Generated with Claude Code
https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft