Lists maturation: typed datasets + List Search (goal 0011) - #11
Merged
Conversation
Grows List from a flat key/value map into a typed tabular dataset (docs/SPEC.md §3.2.2's reference review): Columns/Rows against ADR-0029's canonical typedfield.Field vocabulary, system-managed row audit fields (CreatedAt/UpdatedAt/Status, Expired excluded from matching by default), a new list-search node (multiple exact/fuzzy match parameters AND'd together, fuzzy via github.com/hbollon/go-edlib behind internal/adapters/fuzzymatch, Damerau-Levenshtein default), and in-place migration of pre-existing key/value Lists. list-lookup keeps working unchanged via list.DeriveEntries's derived flat view. Originates from the owner's parallel wt-lists worktree session (uncommitted WIP, harvested by diff since the branch carried no commits of its own) and reconciled onto main here: typedfield's Phase 1/2 convergence, entity-level CreatedAt/UpdatedAt, ADR-0028 validation, the confirmed-delete dialog, and InventoryList's updatedLabel all landed on main after the worktree branched, so Configure's Lists CRUD (create/update/row add/update/delete) was rewritten to carry main's persist-failure-revert discipline (docs/goals/0025 item 2) across every new row-level mutation, not just the two that already had it. Two seeded workflows (list-lookup + list-search) against the shared "Example: Country codes" List (typed code/name columns, one deliberately Expired row) prove the capability end-to-end — real DBOS Go tests plus e2e, registered in seedproof_test.go. New configure-lists.spec.ts exercises the Configure Column/Row editors and the list-search Inspector live through the canvas. CSV/JSON row import, a per-column Jaro-Winkler override, and full per-execution dataset-version snapshotting stay named, deliberately deferred future work (recorded in SPEC.md and the archived goal file). docs/SPEC.md §3.2.2 (Update), §3.3 (List row), §3.5 (Configure table) updated in the same change; goal 0011 archived, BACKLOG updated. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
alicoding
enabled auto-merge
August 12, 2026 02:30
# Conflicts: # docs/goals/archive/0011-lists-maturation.md
…nux CI) PR #11's three red shards traced to ONE cause (confirmed via CI artifacts): the new seed and the configure-lists authoring test both ended in apply-clipboard-write-text, which errors on Linux (no pbcopy/osascript) — the exact environment class the earlier triage established. Fixed by REMOVING the unneeded node (neither test needs clipboard I/O to prove list-search; the seed now ends at list-search itself, the same warn-only Process-leaf shape list-lookup's sibling seed already uses). Shard 2 was a fail-fast cancellation casualty (50/50 green — matrix fail-fast:false rides the next PR); the codeexec blip was the documented pre-existing flake class, evidenced, untouched. Takeover note: the delegated agent stopped silently mid-verification (third such instance tonight); fix verified and landed by the orchestrator directly. The recurring 'linker contention' commit-gate failures root-caused to DISK FULL, not contention — corrected in the session memory. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft
5 tasks
alicoding
added a commit
that referenced
this pull request
Aug 15, 2026
…ers (task #11) configureservice_export.go adds Export/Import for all three Configure- authored, reusable entity types, mirroring compositionservice_export.go's established design exactly: a dedicated wire-shape type per entity (never the domain type directly), ID always omitted (import always mints a new entity via the existing Create* method -- ADR-0013's Duplicate precedent), deterministic JSON by construction. Secrets are excluded from HTTPRequest export by construction, not a field-stripping step to remember: httprequest.HTTPRequest carries no secret field at all (ADR-0007), and AuthConfig/JOSEConfig's own doc comments were checked directly, not assumed, before including them wholesale -- every field on OAuth2Config/HMACConfig/OAuth1Config/ JOSEConfig is genuinely non-secret (ClientSecret, signing keys, ConsumerSecret/TokenSecret, and Mill's own JOSE private key all live in the OS keychain exclusively). A real test (TestExportImportHTTPRequest_RoundTrips_NeverCarriesASecret) proves this by actually setting a secret, exporting, and asserting it's absent from the output and from the imported copy's own keychain entry -- not just asserted from reading the struct definition. Frontend: Export/Import UI added to all three Configure views (ConfigureRequests/Lists/MCPServers.tsx), matching Composition's own per-row Export IconButton + header Import button + hidden file input pattern. The Blob+anchor download mechanism itself was about to be written a third time, so it's extracted to shared/downloadJSON.ts (one pure, stateless function) -- Composition's own already-shipped, already- tested inline version is deliberately left as-is rather than retrofitted, since churning tested code for a marginal DRY gain isn't worth the re-verification cost. Verified: 13 new Go tests (round-trip + secret-exclusion for HTTPRequest, round-trip + determinism for List, round-trip for MCPServer, unknown-ID/invalid-JSON/missing-field rejection for all three) plus the full existing suite, all passing with -race. Full frontend check suite (tsc, eslint, boundaries, vitest) clean. New e2e spec (configure-export-import.spec.ts, 4 tests including the real API-key seeded example's secret-non-leak assertion) run twice in a row per .claude/rules/testing.md, both clean; existing configure-requests.spec.ts re-run as a regression check, unaffected. 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
…nux CI) PR #11's three red shards traced to ONE cause (confirmed via CI artifacts): the new seed and the configure-lists authoring test both ended in apply-clipboard-write-text, which errors on Linux (no pbcopy/osascript) — the exact environment class the earlier triage established. Fixed by REMOVING the unneeded node (neither test needs clipboard I/O to prove list-search; the seed now ends at list-search itself, the same warn-only Process-leaf shape list-lookup's sibling seed already uses). Shard 2 was a fail-fast cancellation casualty (50/50 green — matrix fail-fast:false rides the next PR); the codeexec blip was the documented pre-existing flake class, evidenced, untouched. Takeover note: the delegated agent stopped silently mid-verification (third such instance tonight); fix verified and landed by the orchestrator directly. The recurring 'linker contention' commit-gate failures root-caused to DISK FULL, not contention — corrected in the session memory. 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
Lists maturation: typed datasets + List Search (goal 0011)
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
Listfrom a flat key/value map into a typed tabular dataset (SPEC §3.2.2's reference review):Columns []typedfield.Field/Rows []Rowbuilt directly against ADR-0029's canonical vocabulary — no parallel schema system.CreatedAt/UpdatedAt/Status,Active/Expired) are platform-owned struct fields, never a user-declared Column; noCreatedBy/UpdatedBy(Mill is single-user forever, §3.7). Expired rows excluded from matching by default, uniform across exact and fuzzy, with a per-stepincludeExpiredopt-in.list-searchnode: multiple match parameters (column + literal-or-attr:<name>value + exact/fuzzy, AND'd together), fuzzy viagithub.com/hbollon/go-edlib(MIT) behindinternal/adapters/fuzzymatch, Damerau-Levenshtein default — exact match stays plain equality, never routed through the fuzzy library. Output is the fixed-by-construction typed Object{results, matched, first_match, match_count, list_id}.list-lookupkeeps working completely unchanged vialist.DeriveEntries's derived flat view.list.MigrateLegacyEntries, synthesizedkey/valueColumns) on first load — same precedent as ADR-0016's connector-key migration.ConfigureLists.tsx) authors the typed Column schema and a schema-generated Row editor.Provenance
Harvested from the owner's parallel
wt-listsworktree session (uncommitted WIP — the branch itself carried no commits, only working-tree changes, harvested by diff and never checked out directly) and reconciled ontomainhere:typedfield's Phase 1/2 convergence (goal 0013), entity-levelCreatedAt/UpdatedAt, ADR-0028 validation, the confirmed-delete dialog, andInventoryList'supdatedLabelall landed onmainafter the worktree branched. Configure's Lists CRUD (create/update + the new row-level add/update/delete) was rewritten to carrymain's persist-failure-revert discipline (docs/goals/0025 item 2) across every mutation, not just the two methods that already had it pre-harvest.Test plan
go vet ./...go test . ./internal/... -race -cover— all greengolangci-lint run— 0 issuesscripts/check-loc.sh/scripts/check-rules-frontmatter.sh— cleango build .) and server build (CGO_ENABLED=0 go build -tags server .) — both succeednpm run lint/npm run boundaries/npx vitest run(178/178) /npx tsc --noEmit— all clean--workers=1, matchingplaywright.config.ts's own documented flakiness class)example-list-lookup-workflow,example-list-search-workflow) against the shared "Example: Country codes" List (typed code/name columns, one deliberately Expired row) proven end-to-end: real-DBOS Go tests +seed-completeness.spec.tse2e, registered inseedproof_test.goconfigure-lists.spec.tsexercises the Configure Column/Row editors and thelist-searchnode's Inspector live through the canvasconfigure-export-import.spec.tsround-trips a List's typed columns/rowswails3 generate bindings)Deliberately deferred (named, not silently dropped)
CSV/JSON row+schema import, a per-column Jaro-Winkler override, and full per-execution dataset-version snapshotting (today's
list_idon thelist-searchoutput is the goal's own named minimum evidence bar).🤖 Generated with Claude Code
https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft