Skip to content

Lists maturation: typed datasets + List Search (goal 0011) - #11

Merged
alicoding merged 3 commits into
mainfrom
goal/0011-lists
Aug 12, 2026
Merged

Lists maturation: typed datasets + List Search (goal 0011)#11
alicoding merged 3 commits into
mainfrom
goal/0011-lists

Conversation

@alicoding

Copy link
Copy Markdown
Owner

Summary

  • Grows List from a flat key/value map into a typed tabular dataset (SPEC §3.2.2's reference review): Columns []typedfield.Field / Rows []Row built directly against ADR-0029's canonical vocabulary — no parallel schema system.
  • System-managed row audit fields (CreatedAt/UpdatedAt/Status, Active/Expired) are platform-owned struct fields, never a user-declared Column; no CreatedBy/UpdatedBy (Mill is single-user forever, §3.7). Expired rows excluded from matching by default, uniform across exact and fuzzy, with a per-step includeExpired opt-in.
  • New list-search node: multiple match parameters (column + literal-or-attr:<name> value + exact/fuzzy, AND'd together), fuzzy via github.com/hbollon/go-edlib (MIT) behind internal/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-lookup keeps working completely unchanged via list.DeriveEntries's derived flat view.
  • In-place migration of pre-existing key/value Lists (list.MigrateLegacyEntries, synthesized key/value Columns) on first load — same precedent as ADR-0016's connector-key migration.
  • Configure's Lists tab (ConfigureLists.tsx) authors the typed Column schema and a schema-generated Row editor.

Provenance

Harvested from the owner's parallel wt-lists worktree session (uncommitted WIP — the branch itself carried no commits, only working-tree changes, harvested by diff and never checked out directly) and reconciled onto main here: typedfield's Phase 1/2 convergence (goal 0013), entity-level CreatedAt/UpdatedAt, ADR-0028 validation, the confirmed-delete dialog, and InventoryList's updatedLabel all landed on main after the worktree branched. Configure's Lists CRUD (create/update + the new row-level add/update/delete) was rewritten to carry main'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 green
  • golangci-lint run — 0 issues
  • scripts/check-loc.sh / scripts/check-rules-frontmatter.sh — clean
  • Desktop build (go build .) and server build (CGO_ENABLED=0 go build -tags server .) — both succeed
  • Frontend: npm run lint / npm run boundaries / npx vitest run (178/178) / npx tsc --noEmit — all clean
  • Full Playwright e2e suite: 156/156 (two pre-existing, unrelated cross-worker-contention flakes reproduced-clean under --workers=1, matching playwright.config.ts's own documented flakiness class)
  • Two seeded workflows (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.ts e2e, registered in seedproof_test.go
  • New configure-lists.spec.ts exercises the Configure Column/Row editors and the list-search node's Inspector live through the canvas
  • configure-export-import.spec.ts round-trips a List's typed columns/rows
  • Bindings regenerated (wails3 generate bindings)
  • Docs: SPEC.md §3.2.2 (Update note), §3.3 (List row), §3.5 (Configure table) updated in the same change; goal 0011 archived with a Delivered note; BACKLOG.md updated

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_id on the list-search output is the goal's own named minimum evidence bar).

🤖 Generated with Claude Code

https://claude.ai/code/session_01FYwojT8GdUbYSoggbvEFft

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
alicoding enabled auto-merge August 12, 2026 02:30
alicoding and others added 2 commits August 11, 2026 22:35
# 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
@alicoding
alicoding merged commit d3a3132 into main Aug 12, 2026
16 checks passed
@alicoding
alicoding deleted the goal/0011-lists branch August 12, 2026 03:20
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)
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