Skip to content

feat(web): add Directory and Settings workspaces - #705

Merged
wesm merged 6 commits into
kenn-io:mainfrom
salmonumbrella:feat/issue-639-web-directory
Sep 4, 2026
Merged

feat(web): add Directory and Settings workspaces#705
wesm merged 6 commits into
kenn-io:mainfrom
salmonumbrella:feat/issue-639-web-directory

Conversation

@salmonumbrella

@salmonumbrella salmonumbrella commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Add a Directory workspace with durable-person search, contact and organization filters, last-contact controls, stable pagination, responsive person detail, profile maintenance, and history.
  • Add bounded relationship networks, identity and imported-relationship review queues, explicit merge/split handling, person tracking, and a privacy-gated fact ledger.
  • Add CardDAV account, address-book role, sync/history, conflict, and person-publication surfaces.
  • Add a self-describing Settings workspace to Web and TUI for safe daemon, source, search, enrichment, and attachment-download controls.
  • Add owner-only, write-only provider credential management with independent optimistic revisions. Stored values never cross the API response boundary.
  • Add named Exa and SixtyFour enrichment policies, text and visual embedding configuration, and future-only large-group attachment rules. Host bootstrap credentials and sensitive paths remain read-only or outside the generic editor.

Why

Msgvault already had rich People and daemon configuration models, but maintaining them required the CLI, config files, or direct API calls. These workspaces make the safe, routine controls available in the browser and TUI while preserving provenance, optimistic concurrency, restart visibility, and secret boundaries.

Usage

Directory and Reviews are top-level Web workspaces. Select a person to inspect or maintain their profile, employment, relationships, network, files, tracking, publication, and merge history.

Settings is also a top-level Web workspace; press , in the TUI to open the keyboard-only Settings screen. Provider keys can be added, replaced, or removed without revealing their values. Save endpoint and model changes before credential changes when both revisions have moved. Named provider policy remains Web-only; the TUI exposes its current state as read-only.

Attachment controls affect future downloads only. A size limit of 0 is unlimited, while a positive participant cap skips conversations larger than that cap.

Closes #639

Screenshots

Settings

Embedding providers, write-only credentials, and restart metadata:

Search and embedding settings

Named Exa and SixtyFour person-enrichment policies:

Person enrichment settings

Future-only attachment rules by source, conversation scope, participant count, and size:

Attachment download settings

Directory and person workspace

Directory list, search, last-contact controls, and stable sorting:

Directory list

Structured profile, service-scoped observation, tracking, and CardDAV publication state:

Structured person profile

Responsive person drawer:

Responsive person overview

Profile editing, history, custom fields, employment, relationships, and bounded network:

Profile editing

Profile history

Custom attribute field

Organizations and employment

Typed relationships

Bounded network

Review and identity flows

Identity review queue

Binding conflict

Merge decision

Merge history

Split merged person

Imported relationship review

Fact ledger

Fact evidence history

CardDAV and operation history

CardDAV account and operations

CardDAV conflict and history

CardDAV person publication

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 2 times, most recently from 6f89076 to 14f2506 Compare August 29, 2026 09:45
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (14f2506)

Verdict: 3 medium-severity issues require attention; no concrete security issues were identified.

Medium

  • internal/api/carddav.go:485-525; internal/api/openapi.go:243-247 — Existing CardDAV response fields are removed or replaced, potentially breaking 2.x clients despite only a minor schema update. Preserve compatibility fields or version and document the replacement contract with a major-version change.

  • web/src/lib/carddav/publication-controller.svelte.ts:154-191; web/src/lib/carddav/conflicts-controller.svelte.ts:240-302carddav_unavailable is treated as successful reconciliation. Propagate unavailable as an unknown/error outcome, and only report reconciliation after confirming valid server state.

  • internal/store/person_directory.go:203-226,726-729 — Directory cursors validate display-name keys but omit match_quality, which is also part of the ordering and can cause skipped or duplicated results. Validate the full ordering tuple or bind cursors to a projection revision.


Reviewers: 2 done | Synthesis: codex, 8s | Total: 24m18s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 2 times, most recently from c0a8ebf to 55b1029 Compare August 29, 2026 11:31
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (55b1029)

Verdict: Code is generally clean; one low-severity inconsistency was identified but omitted per the requested threshold.

No Medium, High, or Critical findings.


Reviewers: 2 done | Synthesis: codex, 5s | Total: 35m34s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch from 55b1029 to 75bf0c0 Compare August 29, 2026 12:18
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (75bf0c0)

Verdict: One medium-severity issue was identified; no critical or high-severity findings were reported.

Medium

  • Network projection can be incomplete or order-dependentinternal/store/person_network.go:150-169, 214-243
    The edge limit is applied per frontier node before filtering, deduplicating, hydrating ordering metadata, and applying public ordering. A high-degree earlier node can consume the limit and exclude a later candidate that should rank higher.
    Suggested fix: Build and filter the complete layer candidate set across all frontier nodes, hydrate ordering fields, deduplicate candidates, then apply deterministic ordering and the bounded limit.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 15m36s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 2 times, most recently from c1741cc to c3d785c Compare August 29, 2026 13:14
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (c3d785c)

Verdict: No Medium, High, or Critical findings identified.

Reviewers found no material security issues. One Low-severity pagination issue was reported but omitted per the requested threshold.


Reviewers: 2 done | Synthesis: codex, 5s | Total: 14m52s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch from c3d785c to 9d3c381 Compare August 29, 2026 14:08
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (9d3c381)

Verdict: One medium-severity issue requires attention.

  • Mediuminternal/store/carddav_publication.go:294-307: Nullable address_book_id and href columns are scanned into non-nullable int64 and string values. Valid unassigned publication rows can therefore cause scan errors and make the publication-status API fail. Scan into sql.NullInt64/sql.NullString and handle missing values as unpublished/unassigned without querying the address-book table.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 22m4s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch from 9d3c381 to 5d9074e Compare August 29, 2026 15:23
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (5d9074e)

Verdict: One medium-severity issue found; no critical or high-severity issues.

Medium

  • internal/store/person_network.go:201-247 — The edge budget is applied to raw adjacency IDs before hydration and public sorting. High-degree nodes may consume the source window with employment edges and omit relationship edges that should rank earlier under the advertised (hop, kind, label, node ID, edge ID) ordering. Apply the limit after deduplication and public sorting, or implement the bounded query with that ordering while retaining a separate work guard.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 21m43s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 2 times, most recently from 7abc72a to 73db967 Compare August 29, 2026 15:41
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (73db967)

Verdict: Medium-risk issues found: failed refreshes can leave stale CardDAV and tracking data actionable.

Medium

  • web/src/lib/carddav/publication-controller.svelte.ts:185-212 — Failed publication-state refreshes preserve stale data while mutations remain enabled. Mark state unknown or require a successful, non-loading refresh before publish/unpublish.

  • web/src/lib/directory/person-tracking-controller.svelte.ts:166-191 — Failed tracking refreshes leave the previous tracking value actionable. Disable mutations or mark tracking state unknown when refresh fails.

  • web/src/lib/carddav/conflicts-controller.svelte.ts:125-136,217-237 — Failed conflict-detail reloads retain stale details while resolution remains enabled. Gate resolution on a current successful detail load.

  • web/src/lib/carddav/controller.svelte.ts:286-345 — Failed status refreshes preserve stale status while canSync remains enabled. Mark status unknown or require no loading/error state before syncing.

  • web/src/lib/carddav/controller.svelte.ts:297-307,359-383 — Failed book refreshes leave stale books displayed and actionable, potentially allowing role updates against obsolete IDs. Mark books unknown and disable role controls during loading or errors.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 40m6s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch from 73db967 to 0628e0a Compare August 29, 2026 19:09
@salmonumbrella salmonumbrella changed the title feat(web): add Directory workspace feat(web): add Directory and Settings workspaces Aug 29, 2026
@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch from 0628e0a to 6a7540c Compare August 29, 2026 19:22
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (6a7540c)

Verdict: One medium-severity state-consistency issue remains.

Medium

  • internal/api/settings.go:500-527, internal/api/settings_suppression.go:47 — Enabling enrichment persists the generated suppression credential before API-key confirmation and configuration validation complete. A rejected or stale settings update can mutate credential state despite the request failing. Defer persistence until validation and update succeed, or add transactional rollback handling.

Reviewers: 2 done | Synthesis: codex, 9s | Total: 15m50s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 4 times, most recently from f52d2fe to 56f16aa Compare August 29, 2026 20:17
@roborev-ci

roborev-ci Bot commented Aug 29, 2026

Copy link
Copy Markdown

roborev: Combined Review (56f16aa)

Verdict: One medium-severity robustness issue identified; no high or critical findings.

Medium

  • internal/providercredentials/store.go:105-107EndpointOrigin accesses parsed.Scheme before checking the URL parse error. Malformed input can yield a nil URL and panic the request handler instead of returning validation feedback. Check err and parsed == nil before dereferencing.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 32m30s

@salmonumbrella
salmonumbrella force-pushed the feat/issue-639-web-directory branch 3 times, most recently from 253dd22 to a1f5c68 Compare August 29, 2026 21:00
@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (de3dd10)

Verdict: Medium-severity issue found; no concrete security issues identified.

Medium

  • web/src/lib/components/settings/SettingsWorkspace.svelte:173-181 — Saving settings updates only local workspace state. Theme, density, and default search mode are not refreshed at the app level, so changes do not take effect during the current session. Notify the parent after saving and update the app-level settings.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 18m45s

@wesm

wesm commented Sep 2, 2026

Copy link
Copy Markdown
Member

I did a full review pass and pushed 21 follow-up commits on top of de3dd10. CI checks, lint, OpenAPI generation, the web suite, and the full Go suite (SQLite plus a PostgreSQL 17 run of the store tests) pass locally. Summary of what changed and why:

Store

  • The Directory projection now installs with a single migration. The three chained ledger entries, the guarded ADD COLUMN, and the legacy trigger drops only made sense if earlier versions had shipped; none had, and an upgrade from main would have rebuilt the projection three times.
  • The person network edge budget is charged once per distinct unseen edge and truncation follows the public ordering. Before, each hop re-read the previous hop's edges from the other endpoint, so truncated fired and edges were dropped well below the 250/500 caps.
  • Dirty Directory rows are claimed with DELETE ... RETURNING before rebuilding. Two concurrent refreshes on PostgreSQL could hit a unique violation and fail an unrelated write.
  • Partial indexes for the running and succeeded run states. The operations status poll was full-scanning sync_runs, which is never pruned.
  • Read-only stores skip the projection refresh hook; the organizations dirty trigger matches across dialects; the case folder is built per call.

API and CLI

  • Stored vector and multimodal credentials are severed when their endpoint moves to a different origin, the same as the tasks integration. A stale origin-bound key used to break vector init on restart and could not be removed.
  • DELETE /settings/provider-credentials/{id} no longer requires the provider to still be in config, so a key for a removed provider can be cleaned up.
  • Removed the variadic optional-parameter shims in the daemon composition. Tests now build the same credential-store gate the daemon builds instead of an env-only fallback.
  • Removed the unreachable 409 on operation history and the dead confirm_api_key_restart request field. server.api_key is read-only in the browser.
  • CardDAV: a startup credential read failure is now logged; the sync run row is finished in a defer so a recovered panic cannot leave it running.
  • API schema is 2.14.0, not 3.1.0. Everything since v0.19.3 is unreleased, so the CardDAV shape change is recorded inside the existing 2.x breaking-change entry rather than opening a 3.x line.

Web

  • Directory text filters are debounced and commit with history replace. Each keystroke used to send a request, clear the list, and push a history entry.
  • Selecting a person no longer fetches employments, relationships, and network twice.
  • CardDAV account save and test show the server's error message again.
  • Dropped the directoryCursor URL compat stripping for a key that never shipped.

Docs and repo

  • Changelog, web UI, configuration, and TUI docs cover Settings, stored credentials, the , key, and the 2.14.0 schema. Reverted the .roborev.toml review agent change; that is a repo-level decision separate from this feature.

@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (c35f71b)

Verdict: Two medium-severity issues require attention; otherwise, no material security regressions were identified.

Medium

  • Configuration commit can leave stale credentials: internal/api/settings.go:527-531; internal/api/settings_enrichment.go:152-157
    Configuration is committed before origin-bound credential deletion. If cleanup fails, the old credential remains stored and may reactivate if the endpoint is later restored. Make the update and cleanup recoverably atomic, or retry cleanup using the committed endpoint and stored credential origin.

  • Invalid provider kinds can be persisted: internal/api/settings_enrichment.go:123-129
    Disabled providers bypass kind validation, allowing missing or arbitrary kinds to be stored. Since provider kind is immutable, such providers can never be enabled. Always validate stable identity and require a valid kind while retaining relaxed policy validation for disabled providers.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 28m4s

@wesm

wesm commented Sep 2, 2026

Copy link
Copy Markdown
Member

Both roborev findings on c35f71b are addressed in 4cf3393:

  • Stale credentials after a failed cleanup. Severing now decides from the committed config and the stored key's origin instead of from the edits in one request. Any later settings PATCH or provider PUT removes a stored key whose origin no longer matches the configured endpoint, so an interrupted cleanup, a concurrent credential write, or a host edit of config.toml is repaired by the next write rather than left on disk.
  • Invalid provider kinds. Kind is validated on every provider PUT regardless of enabled, so a disabled provider can no longer be created with an empty or unknown kind that could never be enabled.

Both have regression tests that fail on the previous head.

@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (4cf3393)

Verdict: One medium-severity concurrency issue requires attention; no material security issues were found.

Medium

  • internal/store/person_enrichment_work.go:326ClaimWork performs a read followed by an update in a deferred SQLite transaction. Concurrent commits can cause the snapshot upgrade to fail with SQLITE_BUSY, aborting the worker pass and leaving work unclaimed. Use BEGIN IMMEDIATE or the store’s bounded contention-retry policy.

Reviewers: 2 done | Synthesis: codex, 8s | Total: 46m36s

@wesm
wesm force-pushed the feat/issue-639-web-directory branch from 4cf3393 to 39e5675 Compare September 2, 2026 20:27
@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (39e5675)

Verdict: One high-severity and two medium-severity issues require attention.

High

  • Credential binding does not cover polling endpoints
    Location: internal/api/settings_credentials.go:188-191, cmd/msgvault/cmd/provider_credentials.go:40-50, internal/personenrichment/sixtyfour.go:185-186,512-519
    Credential checks cover only the provider’s primary endpoint. If PollEndpoint changes hosts, the stored API key may be sent to the new host. Bind credentials to both endpoint origins, require them to match, or invalidate credentials when either changes.

Medium

  • Provider update can leave stale credentials after a failed cleanup
    Location: internal/api/settings_enrichment.go:135-162
    The new provider configuration is persisted before stale-credential deletion. If deletion fails, the handler returns an error while leaving the new configuration and old credential on disk, without marking restart or reconciliation as required. Make the transition transactional or persist retryable cleanup state and mark reconciliation pending.

  • Directory refresh can lose concurrent dirty-marker updates
    Location: internal/store/person_directory_projection.go:243-257
    The refresh transaction reads the dirty marker before claiming or deleting it. Concurrent writes can cause snapshot-upgrade failures or allow an older snapshot to delete the only dirty marker, leaving the projection stale. Claim the marker under an appropriate write lock or use a separate retried refresh transaction that preserves concurrent notifications.


Reviewers: 2 done | Synthesis: codex, 9s | Total: 34m11s

@roborev-ci

roborev-ci Bot commented Sep 2, 2026

Copy link
Copy Markdown

roborev: Combined Review (86fe32d)

Verdict: Two medium-severity issues identified; no critical or high-severity findings.

Medium

  • web/src/lib/directory/profile-controller.svelte.ts:206-226 — Name and contact-point profile changes do not refresh the Directory list, leaving search results and membership stale after adding or removing the only matching value. Request a Directory page refresh when these values change.

  • web/src/lib/directory/entity-controller.svelte.ts:259-355 — Organization and employment mutations update only detail-local state, leaving Directory summaries and organization filters stale. Add a callback to reconcile or refresh loaded Directory pages after these mutations.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 40m4s

@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (38344e5)

Verdict: Two medium-severity issues remain in credential and provider repair flows.

Medium

  • internal/api/carddav.go:284-296 — Replacing a password fails when the existing credential file is corrupt, unreadable, or unavailable, despite the API/UI presenting re-entry as a repair path. Permit replacement when a new password is supplied while preserving the old credential if later validation or persistence fails.

  • internal/api/settings_enrichment.go:251-270 — Disabled providers skip validation, but settings serialization still unconditionally calls CredentialEndpoint(). Incomplete or legacy-invalid disabled-provider configuration can therefore make all settings requests fail, blocking UI repair. Serialize such providers without requiring endpoint or credential resolution and provide an explicit repair path for invalid configurations.


Reviewers: 2 done | Synthesis: codex, 7s | Total: 44m5s

@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (70695fc)

Verdict: One medium-severity issue identified; no critical or high-severity findings.

Medium

  • TUI settings save can partially fail when updating an endpoint and credential together
    Location: cmd/msgvault/cmd/tui_settings.go:239-256, 316-337
    Updating both can replace the stored credential and return a new Credential-Etag, but the TUI discards it and uses the stale ETag for the subsequent credential update, causing a conflict and partial save. Propagate the PATCH response’s Credential-Etag or prevent simultaneous endpoint and credential edits.

Reviewers: 2 done | Synthesis: codex, 9s | Total: 37m58s

@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (083d5bd)

Verdict: One medium-severity issue found; no critical or high-severity issues.

Medium

  • internal/api/carddav.go:265 — Disabling an existing CardDAV account still requires a reusable or newly supplied password. If its stored credential is unavailable or invalid, the account cannot be disabled or rescheduled. Allow disabling and reconciling scheduled jobs without credential validation; require credentials only when enabling or changing connection identity.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 55m10s

@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (08649ff)

Verdict: One medium-severity issue found; otherwise, no concrete security vulnerabilities were identified.

Medium

  • internal/api/settings.go:718-725 — Stale-credential cleanup only checks two fixed vector credential IDs. Named enrichment credentials (people.enrichment/<name>) can remain on disk after a provider is removed or renamed through host config edits, since provider PUT cleanup cannot target a nonexistent provider. Enumerate dynamic enrichment credential IDs and remove those lacking a matching committed provider or having a mismatched endpoint origin.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 27m54s

@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (e7b89c3)

Verdict: No Medium, High, or Critical issues found.

Reviewers found no actionable findings at or above Medium severity.


Reviewers: 2 done | Synthesis: codex, 5s | Total: 38m43s

Add the durable-person Directory, relationship network, profile maintenance,
and CardDAV publication workflows.

Expose safe daemon, embedding, enrichment, source, and future
attachment-download controls in Web and TUI Settings. Keep provider
credentials write-only, origin-bound, independently versioned, and stored
outside config.toml.

- docs: record Directory, Settings, and API 2.14.0 in changelog
- fix(web): drop directoryCursor URL compat stripping
- fix(web): stop sending confirm_api_key_restart on settings save
- fix(web): show the server's reason when a CardDAV save or test fails
- perf(web): fetch each Directory person section once per selection
- fix(web): debounce Directory text filters and keep rows while reloading
- docs: describe Settings, stored provider credentials, and TUI shortcuts
- refactor(api): split settings PATCH commit and rollback into helpers
- fix(api): sever stored provider credentials when their endpoint moves
- fix(api): drop unreachable operation 409 and API-key restart confirmation
- fix(api): publish the Directory and Settings work as schema 2.14.0
- fix(carddav): finish the sync run row even when the pull panics
- fix(api): log why a saved CardDAV credential could not be loaded
- refactor(cli): require the credential-store lookups the daemon builds
- test(web): drop confirm_api_key_restart from browser settings spec
- Install the Directory projection with one migration
- Charge the person network edge budget once per distinct edge
- Claim dirty Directory people before rebuilding their rows
- Index running and successful runs for operation lane status
- Tidy Directory projection and operation run helpers
- Rebind the employment lookup in the network truncation test
- fix(api): retry stale credential severing and always validate kind
- fix(store): retry contended enrichment claims
- fix(settings): keep provider credentials bound and recoverable
- fix(web): refresh Directory after detail mutations
- fix(settings): keep credential repair paths available
- fix(tui): carry credential token across settings saves
- fix(carddav): allow credential-free disable
- fix(settings): clean up orphaned provider credentials

Generated with Claude Code (claude-fable-5-1)
Generated with Codex
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Wes McKinney <wesmckinn+git@gmail.com>
@wesm
wesm force-pushed the feat/issue-639-web-directory branch from e7b89c3 to 99518ae Compare September 3, 2026 12:27
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (99518ae)

Verdict: One medium-severity reliability issue identified; no material security issues found.

Medium

  • internal/store/carddav_sync_runs.go:153 — Terminalizing a CardDAV sync and pruning old history occur in the same transaction. If pruning fails, the terminal-state update rolls back, leaving the completed run marked running and potentially blocking subsequent syncs until startup recovery.
    • Suggested fix: Commit the terminal transition independently, then perform retention pruning separately or on a best-effort basis.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 1h23m37s

A completed CardDAV run must release the active-run constraint even when
history retention fails. Rolling both operations back together left finished
work marked as running and blocked the next sync until startup recovery.

Keep retention cleanup retryable and best effort after the terminal state is
durable. Apply the same boundary to startup recovery so it cannot recreate the
same blockage.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (689a0fb)

Verdict: One medium-severity production integration issue was identified.

Medium

  • cmd/msgvault/cmd/serve.go:601-604, 1180-1236: The production storeAPIAdapter does not implement api.PersonNetworkStore. As a result, requests to /api/v1/people/{id}/network return 503 person_network_unavailable.
    • Fix: Add a GetPersonNetworkContext pass-through method and a compile-time api.PersonNetworkStore assertion to storeAPIAdapter.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 44m30s

The person network route worked with a direct store in API tests but the
daemon supplies its production adapter. Without the network capability on
that adapter, every real request returned a service-unavailable response.

Keep a compile-time interface check beside the other daemon capabilities so
future adapter drift fails during the build instead of at runtime.

Generated with Codex
Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (5170d93)

Verdict: One medium-severity issue requires attention; no concrete security issues were identified.

Medium

  • web/src/lib/components/settings/CardDAVAccountSettings.svelte:22-29 — Form state is initialized from settings only once and is not resynchronized when the parent refreshes its settings prop. Subsequent saves may submit stale CardDAV URL, username, schedule, enabled state, or persisted-password metadata and overwrite newer configuration. Synchronize refreshed settings while preserving intentional dirty edits, or add optimistic-concurrency checks (ETag/If-Match), plus a prop-refresh behavior test.

Reviewers: 2 done | Synthesis: codex, 6s | Total: 49m44s

Settings reloads replace the parent state, but the CardDAV form kept the
values captured when it was first mounted. Reconcile refreshed values into
untouched fields while preserving intentional local edits, including the
latest stored-password metadata.

Generated with Codex

Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (d6e58cb)

Verdict: One medium-severity configuration initialization bug found.

  • Mediumcmd/msgvault/cmd/person_enrichment.go:51: personEnrichmentEnvironmentLookup(cfg) captures cfg while it is still nil during package initialization. After configuration loads, suppression commands remain bound to the nil configuration and cannot read the persisted suppression key, failing as “not set.” Resolve the lookup at command execution time or use a configuration accessor.

Reviewers: 2 done | Synthesis: codex, 7s | Total: 25m25s

Person enrichment commands are registered before Cobra loads the user
configuration. Read the active configuration when a command needs its
suppression key so stored credentials remain available at execution time.

Stored people-provider credentials are intentionally unsupported on Windows.
Keep the combined proxy lookup test on platforms that implement that store.

Generated with Codex

Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 3, 2026

Copy link
Copy Markdown

roborev: Combined Review (e1a9d7a)

Verdict: One medium-severity issue found; no high or critical issues reported.

Medium

  • CardDAV account disablingweb/src/lib/components/settings/CardDAVAccountSettings.svelte:128-133, 164-166
    The UI rejects a blank password when disabling an existing account, preventing accounts with missing or invalid stored credentials from being disabled even though the API supports credential-free disablement. Allow blank passwords when disabling without changing the base URL or username, and apply the same condition to the field’s required state.

Reviewers: 2 done | Synthesis: codex, 8s | Total: 1h25m42s

An unavailable stored credential prevented users from disabling a CardDAV
account in the web form, even though the API supports a config-only update.
Allow an unchanged account to be saved while disabled without a password.

Continue to require a password for connection tests, identity changes, and
re-enabling. Preserve the actual credential state after a config-only save.

Generated with Codex

Co-authored-by: Codex <noreply@openai.com>
@roborev-ci

roborev-ci Bot commented Sep 4, 2026

Copy link
Copy Markdown

roborev: Combined Review (4f286d1)

No issues found.


Reviewers: 2 done | Synthesis: codex | Total: 52m40s

@wesm
wesm merged commit a589bf1 into kenn-io:main Sep 4, 2026
25 checks passed
@wesm

wesm commented Sep 4, 2026

Copy link
Copy Markdown
Member

@salmonumbrella merged, I didn't have time to really QA this much, but I figure whatever doesn't work we can fix

wesm added a commit that referenced this pull request Sep 4, 2026
msgvault.io previously served only the reference docs at the domain root, with no page that says what msgvault is or where it is going. This gives the site the same three-tier shape as the other kenn-io project sites, in a carbon monochrome design matched to the existing msgvault mark:

- **`/`** — marketing page: msgvault as the system of record for your communications and relationships. Covers every shipped source, the people layer, hybrid search, the Docbank-backed document and visual lanes, the interface surfaces, and the verify → stage → gated-execute → restore deletion model. An install matrix offers the macOS/Linux script, Homebrew, and Windows PowerShell journeys with per-row copy buttons.
- **`/guide/`** — a nine-stop archive lifecycle tour (Capture → Preserve → Resolve → Curate → Understand → Search → Analyze → Act → Own), each stop pairing copy with a hand-drawn SVG diagram or a capture from the reviewed Enron fixture, and deep-linking into the docs.
- **`/docs/`** — the existing Zensical docs, now built into a subpath by the same `vercel-build.sh`.

The Directory/Reviews/Settings workspaces from #705 appear only as a labelled "in development" placeholder; nothing unshipped is claimed as current. Both new tiers ship exact Markdown twins (`/index.md`, `/guide.md`) and an `llms.txt` index for machine readers.

Moving the docs under `/docs/` changes every published URL, so `vercel.json` adds permanent redirects for all legacy paths (top-level pages, `usage/`, `guides/`, `architecture/`, `search/`, and the hydrated asset directories), and absolute internal links in the docs sources now carry the `/docs/` prefix. `check_built_site.py` validates the combined site and `check_vercel_redirects.py` enforces the full redirect table; `bash scripts/check-docs.sh` passes end to end.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


Co-authored-by: Wes McKinney <wesm@users.noreply.github.com>
@salmonumbrella

Copy link
Copy Markdown
Contributor Author

@wesm agree. it's ugly anyway, in the future the frontend needs to be redesigned.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Add a web Directory workspace to view and edit person attributes

2 participants