Skip to content

Goal 0011 design section (research delivered; build gated on wt-lists decision) - #10

Merged
alicoding merged 1 commit into
mainfrom
docs/0011-design
Aug 12, 2026
Merged

Goal 0011 design section (research delivered; build gated on wt-lists decision)#10
alicoding merged 1 commit into
mainfrom
docs/0011-design

Conversation

@alicoding

Copy link
Copy Markdown
Owner

Records the Lists-maturation research verdicts into the goal file. The build is gated on an owner decision: the wt-lists worktree holds a near-complete parallel implementation — rebase-and-land vs rebuild-from-research is theirs to call.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft

go-edlib adopted for fuzzy (with the industry-default-is-exact framing
correction); fixed-by-construction output shape; rows without
CreatedBy (§3.2.4's verdict); snapshot-per-execution reframed from
correctness gap to audit evidence (DBOS checkpointing already
guarantees replay stability — Temporal-precedent-confirmed); storage
stays settings-store with a named SQLite trigger. Flags the gating
owner decision: wt-lists' near-complete parallel implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
@alicoding
alicoding enabled auto-merge August 12, 2026 01:56
@alicoding
alicoding merged commit f86bc27 into main Aug 12, 2026
13 checks passed
alicoding added a commit that referenced this pull request Aug 12, 2026
… 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
alicoding deleted the docs/0011-design branch August 12, 2026 17:44
alicoding added a commit that referenced this pull request Aug 13, 2026
… second pass, ls-lint node_modules ignore (BACKLOG 10/11) (#53)

resizable-table.spec.ts (Standing #10, third recurrence post-hardening):
structural fix, not another timeout bump. test.describe.configure({
mode: 'serial' }) makes the file's never-interleave requirement
explicit; waitForStableBoundingBox polls for the handle's bounding box
being IDENTICAL across two consecutive reads (a non-null box mid-reflow
was always possible the old check couldn't see); the synthesized drag
moved from one batched page.mouse.move(..., { steps: N }) call to
discrete, individually-awaited moves (browsers coalesce rapid
pointermove events within one CDP command -- real, documented browser
behavior via PointerEvent.getCoalescedEvents()); two more expect.polls
wait for the drag's actual DOM effect and the localStorage persist to
land before the next step depends on them. A keyboard-resize
alternative was checked and ruled out -- shared/ResizableTable.tsx has
no keyboard path at all, building one would be a feature addition, not
a test fix. Verified via 5 separate fresh playwright invocations
(10/10) -- an in-process --repeat-each stress loop turned out to be a
self-confounding methodology (accumulating same-worker degradation,
not representative of a real CI run).

golangci-lint second pass (Standing #11b, named future work in goal
0028): gocritic/prealloc/contextcheck/sqlclosecheck enabled. Triaged to
zero findings on both build-tag variants -- 14 real findings fixed (1
gocritic assignOp, 13 prealloc slice-capacity hints), zero
contextcheck/sqlclosecheck findings, no rule needed a scoped tweak or
nolint suppression this pass.

.ls-lint.yml (Standing #11c): root ignore: list gains node_modules,
with a comment on the root-scoped-recursive-rule interaction that
caused the original gap.


Claude-Session: https://claude.ai/code/session_018pkViCNAuZp2vBv2K9AbUh

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
alicoding added a commit that referenced this pull request Aug 13, 2026
… second pass, ls-lint node_modules ignore (BACKLOG 10/11) (#53)

resizable-table.spec.ts (Standing #10, third recurrence post-hardening):
structural fix, not another timeout bump. test.describe.configure({
mode: 'serial' }) makes the file's never-interleave requirement
explicit; waitForStableBoundingBox polls for the handle's bounding box
being IDENTICAL across two consecutive reads (a non-null box mid-reflow
was always possible the old check couldn't see); the synthesized drag
moved from one batched page.mouse.move(..., { steps: N }) call to
discrete, individually-awaited moves (browsers coalesce rapid
pointermove events within one CDP command -- real, documented browser
behavior via PointerEvent.getCoalescedEvents()); two more expect.polls
wait for the drag's actual DOM effect and the localStorage persist to
land before the next step depends on them. A keyboard-resize
alternative was checked and ruled out -- shared/ResizableTable.tsx has
no keyboard path at all, building one would be a feature addition, not
a test fix. Verified via 5 separate fresh playwright invocations
(10/10) -- an in-process --repeat-each stress loop turned out to be a
self-confounding methodology (accumulating same-worker degradation,
not representative of a real CI run).

golangci-lint second pass (Standing #11b, named future work in goal
0028): gocritic/prealloc/contextcheck/sqlclosecheck enabled. Triaged to
zero findings on both build-tag variants -- 14 real findings fixed (1
gocritic assignOp, 13 prealloc slice-capacity hints), zero
contextcheck/sqlclosecheck findings, no rule needed a scoped tweak or
nolint suppression this pass.

.ls-lint.yml (Standing #11c): root ignore: list gains node_modules,
with a comment on the root-scoped-recursive-rule interaction that
caused the original gap.


Claude-Session: https://claude.ai/code/session_018pkViCNAuZp2vBv2K9AbUh

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
alicoding added a commit that referenced this pull request Aug 13, 2026
… second pass, ls-lint node_modules ignore (BACKLOG 10/11)

resizable-table.spec.ts (Standing #10, third recurrence post-hardening):
structural fix, not another timeout bump. test.describe.configure({
mode: 'serial' }) makes the file's never-interleave requirement
explicit; waitForStableBoundingBox polls for the handle's bounding box
being IDENTICAL across two consecutive reads (a non-null box mid-reflow
was always possible the old check couldn't see); the synthesized drag
moved from one batched page.mouse.move(..., { steps: N }) call to
discrete, individually-awaited moves (browsers coalesce rapid
pointermove events within one CDP command -- real, documented browser
behavior via PointerEvent.getCoalescedEvents()); two more expect.polls
wait for the drag's actual DOM effect and the localStorage persist to
land before the next step depends on them. A keyboard-resize
alternative was checked and ruled out -- shared/ResizableTable.tsx has
no keyboard path at all, building one would be a feature addition, not
a test fix. Verified via 5 separate fresh playwright invocations
(10/10) -- an in-process --repeat-each stress loop turned out to be a
self-confounding methodology (accumulating same-worker degradation,
not representative of a real CI run).

golangci-lint second pass (Standing #11b, named future work in goal
0028): gocritic/prealloc/contextcheck/sqlclosecheck enabled. Triaged to
zero findings on both build-tag variants -- 14 real findings fixed (1
gocritic assignOp, 13 prealloc slice-capacity hints), zero
contextcheck/sqlclosecheck findings, no rule needed a scoped tweak or
nolint suppression this pass.

.ls-lint.yml (Standing #11c): root ignore: list gains node_modules,
with a comment on the root-scoped-recursive-rule interaction that
caused the original gap.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018pkViCNAuZp2vBv2K9AbUh
alicoding added a commit that referenced this pull request Aug 15, 2026
New capability: CompositionService.ExportWorkflow/ImportWorkflow
(compositionservice_export.go) plus a real Composition UI affordance
(Export IconButton per workflow row, an Import button + hidden file
input near New workflow) -- not backend-only, per explicit direction
that this needs to be visibly checkable in the running app, not just
git history.

Design decision the goal itself required in writing: exportedWorkflow
deliberately omits the workflow's own ID and BuiltIn flag from the wire
shape. ImportWorkflow always mints a brand-new workflow via the
existing CreateWorkflow (same validation bar as a hand-composed one --
ResolveNodeDefaults, ValidateGraph, no import-specific leniency),
matching ADR-0013's Duplicate precedent (copying an entity means a new
identity, never resurrecting the old one) -- so importing the same file
twice, or into a different Mill instance, can never silently collide
with or overwrite an existing workflow. Node/Edge IDs inside the graph
ARE preserved as-is (relative references, not global identity).

Stable/deterministic by construction, not extra bookkeeping: every
exported field is already-stored data, never regenerated on save
(confirmed by reading idgen.go directly), and Go's encoding/json
guarantees deterministic struct-field and sorted-map-key ordering. Two
exports of an unchanged workflow are therefore byte-identical -- proven
by a real test (TestExportWorkflow_IsDeterministic), not just asserted
-- which is the concrete answer to n8n's own documented git-diff-noise
problem researched earlier this session.

Two files crossed the 500-line limit adding this (CompositionView.tsx,
composition.spec.ts) -- split along real seams, not truncated:
TestRunDialog extracted to its own file (self-contained, zero shared
state with its parent beyond props, same pattern this repo already
uses); the three new e2e tests moved to composition-export-import.spec.ts
with their own workflowRow helper, matching the existing convention of
5 other spec files each keeping their own copy rather than a shared
helpers file that doesn't otherwise exist here.

Verified: 11 new Go tests (round-trip, new-ID-never-reused, two
independent imports from the same file, determinism, ID/BuiltIn
omitted from the wire shape, unknown-ID/invalid-JSON/empty-label/
invalid-graph-shape rejection, Attributes application) plus the full
existing suite, all passing with -race. Full frontend check suite
(tsc, eslint, boundaries, vitest) clean. Both e2e spec files run twice
in a row per .claude/rules/testing.md's explicit discipline for tests
creating persisted entities -- 17/17 both times, confirming cleanup
actually works, not just once. Regenerated bindings also carry two
small doc-comment-only updates trailing from task #3's Go comment
edits (configureservice.ts, models.ts) -- no functional change.

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
…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
… 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
… second pass, ls-lint node_modules ignore (BACKLOG 10/11) (#53)

resizable-table.spec.ts (Standing #10, third recurrence post-hardening):
structural fix, not another timeout bump. test.describe.configure({
mode: 'serial' }) makes the file's never-interleave requirement
explicit; waitForStableBoundingBox polls for the handle's bounding box
being IDENTICAL across two consecutive reads (a non-null box mid-reflow
was always possible the old check couldn't see); the synthesized drag
moved from one batched page.mouse.move(..., { steps: N }) call to
discrete, individually-awaited moves (browsers coalesce rapid
pointermove events within one CDP command -- real, documented browser
behavior via PointerEvent.getCoalescedEvents()); two more expect.polls
wait for the drag's actual DOM effect and the localStorage persist to
land before the next step depends on them. A keyboard-resize
alternative was checked and ruled out -- shared/ResizableTable.tsx has
no keyboard path at all, building one would be a feature addition, not
a test fix. Verified via 5 separate fresh playwright invocations
(10/10) -- an in-process --repeat-each stress loop turned out to be a
self-confounding methodology (accumulating same-worker degradation,
not representative of a real CI run).

golangci-lint second pass (Standing #11b, named future work in goal
0028): gocritic/prealloc/contextcheck/sqlclosecheck enabled. Triaged to
zero findings on both build-tag variants -- 14 real findings fixed (1
gocritic assignOp, 13 prealloc slice-capacity hints), zero
contextcheck/sqlclosecheck findings, no rule needed a scoped tweak or
nolint suppression this pass.

.ls-lint.yml (Standing #11c): root ignore: list gains node_modules,
with a comment on the root-scoped-recursive-rule interaction that
caused the original gap.


Claude-Session: https://claude.ai/code/session_018pkViCNAuZp2vBv2K9AbUh

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant