Problem Statement
The widget has no user settings. Preferences like color scheme are either hardcoded or resolved purely from the host page, and nothing a user chooses survives a reload. There is no place in the UI to view or change preferences, and no persistence story for "I want this in every project" versus "only in this project".
Solution
A settings system with two persistence layers and a settings panel inside the widget shell:
- A global layer at
~/.conciv/conciv.db (per developer, machine-wide).
- A project layer in the existing per-project state database under the project's
.conciv directory.
- Read-time layered resolution: project value wins, else global value, else the code-defined default. Databases are never merged; "merge" is per-key precedence at read time.
- A settings panel in the widget where the user changes settings, sees which layer each value comes from, applies a value globally, or resets to default.
v1 ships exactly one setting: scheme (auto | light | dark), where auto means the existing host-scheme detection ladder. The schema, resolution ladder, contract, and panel are built so that adding a future setting is a registry entry plus its control mapping in the panel, with no storage migration.
User Stories
- As a widget user, I want to switch the widget between light, dark, and auto scheme, so that the widget matches my preference rather than only the host page.
- As a widget user, I want my scheme choice to persist across reloads and dev-server restarts, so that I do not re-toggle it every session.
- As a widget user, I want a settings change to apply to the current project by default, so that experimenting in one project does not affect my other projects.
- As a widget user, I want an explicit "apply globally" action on a setting, so that a preference I always want follows me to every project on this machine.
- As a widget user, I want "apply globally" to also clear the project override, so that the global value visibly takes effect immediately instead of being shadowed.
- As a widget user, I want to see, per setting, whether the value comes from this project, from my global settings, or is the default, so that I understand why the widget looks the way it does.
- As a widget user, I want a "reset" action per setting, so that I can return to the default (or let the global value show through) without knowing the default's value.
- As a widget user, I want the settings panel reachable from the widget shell, so that I do not need to leave the page I am working on.
- As a widget user, I want a settings change to apply immediately in the open widget, so that I can see the effect without reloading.
- As a widget user with
scheme = auto, I want the widget to keep following the host page / OS scheme live, so that auto genuinely means automatic.
- As a widget user, I want an invalid or corrupted stored value to be ignored rather than break the widget, so that a bad entry can never wedge the UI.
- As a developer on the conciv codebase, I want every settings change recorded as an immutable history row, so that changes are auditable and revertable later.
- As a developer on the conciv codebase, I want defaults to live only in code, so that shipping a new default reaches every user who never overrode that setting.
- As a developer on the conciv codebase, I want adding a new setting to require only a registry entry (key, zod schema, default) and a panel row, so that settings do not accrue migrations.
- As a widget user running several projects at once, I want concurrent dev servers to share the global database safely, so that two open projects never corrupt my settings.
- As a widget user, I want to ask the chat agent to change a setting (e.g. "switch the widget to dark"), so that I can configure conciv conversationally without opening the panel.
- As a widget user, I want an agent-made settings change to apply to the open widget immediately, so that the conversation result is visible without a reload.
- As a widget user, I want history to show whether a change came from me or the agent, so that I can audit conversational changes.
- As a widget user, I want unknown keys in my settings files (from a newer or older conciv) to be preserved and ignored, so that switching versions never breaks or destroys settings.
Implementation Decisions
Storage: layered JSONC files (design v2, derived from VS Code / Chromium / GSettings / Zed reference studies; supersedes the sqlite design).
- Project layer:
<stateRoot>/.conciv/settings.json — committable, so teams share project settings via git (like .vscode/settings.json). Global layer: ~/.conciv/settings.json.
- Per layer, exactly one settings file is honored, discovered in fixed order:
settings.jsonc then settings.json (if both exist, .jsonc wins and a warning is surfaced). settings.jsonc is JSONC — comments welcome, and programmatic writes preserve them (minimal-diff edits). settings.json is plain commentless JSON; a commented .json follows the malformed-file path. When neither exists, the first programmatic write creates settings.jsonc. There is no executable/TS settings form. Content is nested by owner namespace: {"appearance": {"scheme": "dark"}} — a contributor owns a top-level object, so key collisions are impossible across owners and duplicate registration is rejected at registry time.
- Defaults are NEVER written to disk (all reference systems): reset = delete the key; a file contains only explicit overrides.
- Unknown keys are preserved on read and write (forward/backward compatibility); a parse error is surfaced as a visible problem and never destroys the file or crashes resolution (fall through to lower layers/defaults).
- Reading/layering composes proven libraries behind one thin engine seam: c12 loads the two layers (accessed individually via its layers API — its deep merge is unused, resolution stays per-key with provenance) and its watch drives auto-reload; raw file bytes are read alongside to compute each layer's content revision (hash) for optimistic concurrency and watcher-echo dedupe. Writes are minimal-diff edits preserving the user's formatting (jsonc-parser edit primitives over the plain-JSON file), persisted with write-file-atomic; the cross-process lock on the global file uses proper-lockfile with explicit stale/retry configuration. The store surface follows conf-style patterns (per-key change notification, a migrations slot in the registry for later) without using conf itself, whose plain-JSON writes would destroy comments.
- Single writer per server, with cross-process safety for the shared global file: within a server all writes serialize through one queue; the GLOBAL file (shared by every concurrently running dev server) is additionally guarded by a lock file, and every global mutation rereads the file under the lock before editing. Writes persist atomically (temp file in the same directory, flush, rename, permissions preserved; symlinked settings files are written through to their target). Writes are immediate, never debounced — only watcher reloads/notifications are debounced.
- A file that currently fails to parse REJECTS writes (error returned to the client) rather than risking clobbering user content; reads keep serving the last-known-good values for that layer until the file parses again.
- The server watches both files (c12 watch, wrapped where echo-dedupe/stale-reload guards need it): external hand edits propagate live through the same settings-changed event as RPC writes. The watcher dedupes the server's own write echoes by content revision and guards against stale reload completions.
Settings key registry in the protocol package: namespaced groups — each group declares its owner namespace, per-key zod schema, default, label and description. v1 registers appearance.scheme (auto | light | dark, default auto). Adding a setting is a registry entry only. Duplicate key or namespace registration is an error.
Resolution and provenance: server-side resolver over the layers: project ?? global ?? registry default, per key, with invalid and absent values distinguished (an invalid stored value is reported as such, not silently identical to unset). One read endpoint serves everything: per key, the effective value, the controlling layer (project | global | default), each layer's value and validity, and each layer's revision. The UI's badges and scope menu consume this endpoint; there is no separate get-vs-inspect pair.
Write rule (v3, final): ordinary control edits ALWAYS write the project layer — an edit in one project can never mutate other projects. Global changes happen only through the explicit "Apply to all projects" action. When an edit overrides an inherited global value, the transition is made legible: the badge visibly transitions GLOBAL to PROJECT and the footer states what happened. "Apply to all projects" is a SINGLE server operation (applyGlobally): the server locks the global file, validates, writes global, clears the project override, appends one history entry with a shared operation id, and emits one settings-changed event — clients never sequence multi-layer writes themselves. All mutations carry the expected revision of their target layer; a stale client receives a conflict and refetches.
History sidecar (best-effort): each layer directory carries an append-only settings-audit.jsonl (project: under .conciv/, gitignored; global: under ~/.conciv/): one line per change {ts, actor, scope, key, from, to, opId} (compound operations share an opId) with actor user (panel), agent (chat), or file (external hand edit detected by the watcher). The sidecar is never read for resolution and is best-effort (a lost line is not an integrity failure); it exists for attribution and a future revert UI. Corrupt or missing lines are skipped on read. The global sidecar is written under the same cross-process lock as the global settings file.
Contract: a new settings oRPC procedure group beside the existing groups:
get → all registered keys: effective value + controlling layer.
inspect → per-layer values for all registered keys (powers provenance UI).
set {key, value, scope: 'project' | 'global'} → validated against the registry schema, written by the server to the target file.
clear {key, scope} → deletes the key from the target file.
history {key} → recent sidecar entries for the key (both layers).
applyGlobally {key, value, expectedRevisions} → the compound global operation described above.
The raw set procedure is single-layer and dumb. Compound behavior lives in the panel: the "Apply to all projects" menu action performs set on global followed by clear on project, so the global value is not shadowed by a stale project override.
Settings panel: a net-new view inside the widget shell (not a native page), reachable from the panel chrome. v1 contains one Appearance section with a scheme control (auto / light / dark as scheme preview tiles). Per setting, the provenance badge IS the scope control: a click target styled as the badge (PROJECT / GLOBAL / DEFAULT) opening a small menu with the scope actions — "Apply to all projects", "Use global value" (only when a project override shadows a global one), "Reset to default". There are no separate text-link actions.
Writes follow provenance: an edit saves to the layer the resolved value currently lives in — source global means the edit updates the global layer (and the badge stays GLOBAL); source project or default means the edit saves to the project layer. The footer sentence follows the active source ("Changes save automatically to this project" / "... to all projects"). When the source is global, the badge menu additionally offers "Set for this project only" for deliberately forking a project override.
Settings apply and persist immediately — there is no Save button anywhere. Feedback: the change takes visible effect at once (optimistic, with rollback and a visible inline error + retry on failure) and the provenance badge shows the new source. No transient saved indicators. While settings load, controls render in the widget's pending/skeleton vocabulary. The footer line reads "Changes save automatically to this project". Built from Ark primitives and the --chat-* token language; visual design is being iterated on a design canvas and the approved mockup governs the final look.
Agent access: settings reach the chat agent through the approved agent-API surface design (every oRPC procedure auto-exposes to the agent catalog with AgentMeta) — no bespoke settings tool is written; the settings group IS the agent surface once that lane lands. Same core service, same registry validation, same append-only log, no second write path. Audit lines record the actor (user for panel writes, agent for agent-initiated writes, file for external edits), derived from the caller context. Gating follows the approval axis: settings.get/history are readonly (free); settings.set/clear/applyGlobally ask like any other mutating procedure.
Live propagation: any settings write (panel or agent) emits a settings-changed notification on the existing server-to-widget event stream; the widget refetches resolved settings and re-applies them immediately. Required so an agent-initiated change (e.g. "switch to dark") repaints the open widget without a reload.
Scheme application: the panel writes through set; the widget applies the resolved scheme by flipping the existing .light / .dark scheme classes on the widget root (the mechanism landed by the scheme lane). auto removes the explicit class and defers to the existing detection ladder, which stays live. The resolved settings load with widget hydration.
Settings navigation: the settings view is a layout route with each tab a nested child route (matching the panel's existing nested-route structure): a settings layout renders the left nav rail and an outlet; appearance is the first child (index redirects to it). Left vertical nav rail plus independently scrolling content pane, ported from the user's reference settings page (playful dashboard) and restyled to the widget's design language: compact single-line rows (12.5px, 4px radius, quiet text that lifts on hover/active) with a mono uppercase microlabel above the group. The active indicator is the widget's existing trace-rail concept: a hairline glyph spine in an 18px gutter with arms branching to each tab row and a corner elbow into the last, plus a live accent segment on the spine marking the active tab.
Responsive layout (container queries): the settings view adapts to the PANEL's size via CSS container queries — the panel root already declares container-type: size — never viewport media queries. Below a narrow threshold (~26rem inline size) the vertical nav rail flips to a horizontal top tab strip: the live accent indicator becomes a sliding underline driven by the same clip-path transition on the horizontal axis, and content spans full width below the strip. Fluid internal sizing may use container query units (cqi). The phone sheet inherits this behavior automatically since it is container-driven.
Panel header: the settings header uses the session-rail two-line pattern — mono uppercase "SETTINGS" microlabel above a title that is the ACTIVE section name, updating on tab navigation. Section pages carry no in-content header (no card title/description); content starts at the first field.
Control motion (ui-kit-system, not view-local): the segmented control's selected indicator and the switch thumb animate as proper layout animations inside the ui-kit-system primitives (Ark SegmentGroup exposes indicator position via CSS variables — animate those; same treatment for Switch), so every consumer gets the motion. Tab-content changes get a subtle enter transition (fade + small translate) in the widget's existing transition vocabulary. All motion honors reduced-motion (snap, no slide).
Rail rendering rules (from the spike, load-bearing): no rail pixel may be painted by both colors — the gray layer carries a complement clip that excludes the live band (accent is never composited over gray, which blends at antialiased edges and arcs); arms start at the spine stroke's outer edge, never inside its stroke band; band edges land on plain spine a full stroke clear of any arm (entered arm fully gray, active arm fully accent, elbow included when last); stroke centerlines snap to half-integers so 1px strokes land on whole pixels at DPR 1 and 2. The rail is orientation-aware: a CSS custom property set by the container query selects the vertical or horizontal plan, and one generic clip polygon drives the slide on either axis.
Styling landmine: the widget's generated utility layer is emitted last in the app stylesheet, so utility classes in markup outrank hand-written rules at equal specificity — any property a container query must override has to live in the stylesheet, not as a utility class in the markup.
Tab indicator animation: the live accent segment slides along the spine between tabs using the trace rail's existing clip-path transition mechanism (reuse the shipped rail component/mechanics rather than a new indicator), which already snaps instead of sliding under reduced motion.
Dependency: stacks on the scheme-mechanism lane (light-dark token pairs + scheme classes) merging first; this spec's scheme values are meaningless without it.
New ui-kit-system primitives: the segmented control (SegmentGroup) and radio-group primitives introduced for this surface are real additions to ui-kit-system (Ark-based, with stories, following the package's existing component conventions), including the animated selected-indicator via Ark's indicator position CSS variables and the switch thumb motion — not view-local one-offs.
Testing Decisions
- Tests assert external behavior only: what a procedure returns, what lands in the files, and what the widget visibly does — never engine internals.
- Two seams, both existing:
- Procedure seam: the
settings group through a real server against real files in temp directories (temp project state root plus a temp fake home; never the developer's real ~/.conciv). Coverage centers on file and multi-process failure modes: default resolution and project-over-global with controlling-layer correctness; clear-fallthrough; unknown keys in files preserved across programmatic writes; jsonc comment and formatting survival through set/clear/applyGlobally; both-files-present warning; commented-.json write rejection; invalid value reported as invalid while resolution falls through; malformed file → last-known-good reads plus write REJECTION until fixed; atomic replacement (no observable empty/partial file); TWO server processes mutating the global file concurrently under the lock without lost updates; watcher self-echo dedupe (a server write causes exactly one settings-changed); external hand edit → event plus history entry with actor file; stale-revision mutation → conflict; applyGlobally end state plus single event plus shared opId; registry rejecting a bad set; symlinked settings file written through; file permissions preserved.
- Widget integration seam: real browser against the prebuilt bundle — open settings, flip scheme, class lands on the widget root, survives reload, apply-to-all-projects and reset flows, auto follows a host scheme flip live, second widget repaints via live propagation, optimistic rollback plus visible error on a blocked write.
- No engine-internal unit seam; the procedure seam owns resolution coverage.
- Whiteboard suite untouched and not run locally, per repo policy.
Out of Scope
- Any setting beyond
scheme (model/harness defaults, appearance extras, behavior knobs come later as registry entries).
- Revert/history UI (the
history procedure ships; its UI does not).
- Agent-initiated settings beyond the registry keys (the tool can only touch registered settings).
- Per-session settings or any session identity in the schema.
- Value-semantic migration hooks in the registry.
- Multi-user identity; settings are developer-local by design.
- Physical merging or syncing of the two databases.
- History retention caps.
- Renaming or restructuring existing host-contract markers.
Further Notes
- The layered model deliberately mirrors git config (local wins over global) and the
~/.claude / project .claude convention: ~/.conciv for the machine layer was chosen to match, and a ~/.conciv directory precedent already exists in the codebase (dev endpoint registry).
- Writes are rare human actions; the append-only log's read cost is one indexed newest-row lookup per key.
- Visual design of the panel continues on the design canvas; implementation should not start on the panel's final styling until the mockup is approved, though storage/contract work is unblocked immediately.
Problem Statement
The widget has no user settings. Preferences like color scheme are either hardcoded or resolved purely from the host page, and nothing a user chooses survives a reload. There is no place in the UI to view or change preferences, and no persistence story for "I want this in every project" versus "only in this project".
Solution
A settings system with two persistence layers and a settings panel inside the widget shell:
~/.conciv/conciv.db(per developer, machine-wide)..concivdirectory.v1 ships exactly one setting:
scheme(auto|light|dark), whereautomeans the existing host-scheme detection ladder. The schema, resolution ladder, contract, and panel are built so that adding a future setting is a registry entry plus its control mapping in the panel, with no storage migration.User Stories
scheme = auto, I want the widget to keep following the host page / OS scheme live, so that auto genuinely means automatic.Implementation Decisions
Storage: layered JSONC files (design v2, derived from VS Code / Chromium / GSettings / Zed reference studies; supersedes the sqlite design).
<stateRoot>/.conciv/settings.json— committable, so teams share project settings via git (like.vscode/settings.json). Global layer:~/.conciv/settings.json.settings.jsoncthensettings.json(if both exist,.jsoncwins and a warning is surfaced).settings.jsoncis JSONC — comments welcome, and programmatic writes preserve them (minimal-diff edits).settings.jsonis plain commentless JSON; a commented.jsonfollows the malformed-file path. When neither exists, the first programmatic write createssettings.jsonc. There is no executable/TS settings form. Content is nested by owner namespace:{"appearance": {"scheme": "dark"}}— a contributor owns a top-level object, so key collisions are impossible across owners and duplicate registration is rejected at registry time.Settings key registry in the protocol package: namespaced groups — each group declares its owner namespace, per-key zod schema, default, label and description. v1 registers
appearance.scheme(auto | light | dark, defaultauto). Adding a setting is a registry entry only. Duplicate key or namespace registration is an error.Resolution and provenance: server-side resolver over the layers:
project ?? global ?? registry default, per key, with invalid and absent values distinguished (an invalid stored value is reported as such, not silently identical to unset). One read endpoint serves everything: per key, the effective value, the controlling layer (project | global | default), each layer's value and validity, and each layer's revision. The UI's badges and scope menu consume this endpoint; there is no separate get-vs-inspect pair.Write rule (v3, final): ordinary control edits ALWAYS write the project layer — an edit in one project can never mutate other projects. Global changes happen only through the explicit "Apply to all projects" action. When an edit overrides an inherited global value, the transition is made legible: the badge visibly transitions GLOBAL to PROJECT and the footer states what happened. "Apply to all projects" is a SINGLE server operation (
applyGlobally): the server locks the global file, validates, writes global, clears the project override, appends one history entry with a shared operation id, and emits one settings-changed event — clients never sequence multi-layer writes themselves. All mutations carry the expected revision of their target layer; a stale client receives a conflict and refetches.History sidecar (best-effort): each layer directory carries an append-only
settings-audit.jsonl(project: under.conciv/, gitignored; global: under~/.conciv/): one line per change{ts, actor, scope, key, from, to, opId}(compound operations share an opId) with actoruser(panel),agent(chat), orfile(external hand edit detected by the watcher). The sidecar is never read for resolution and is best-effort (a lost line is not an integrity failure); it exists for attribution and a future revert UI. Corrupt or missing lines are skipped on read. The global sidecar is written under the same cross-process lock as the global settings file.Contract: a new
settingsoRPC procedure group beside the existing groups:get→ all registered keys: effective value + controlling layer.inspect→ per-layer values for all registered keys (powers provenance UI).set {key, value, scope: 'project' | 'global'}→ validated against the registry schema, written by the server to the target file.clear {key, scope}→ deletes the key from the target file.history {key}→ recent sidecar entries for the key (both layers).applyGlobally {key, value, expectedRevisions}→ the compound global operation described above.The raw
setprocedure is single-layer and dumb. Compound behavior lives in the panel: the "Apply to all projects" menu action performsseton global followed byclearon project, so the global value is not shadowed by a stale project override.Settings panel: a net-new view inside the widget shell (not a native page), reachable from the panel chrome. v1 contains one Appearance section with a scheme control (
auto/light/darkas scheme preview tiles). Per setting, the provenance badge IS the scope control: a click target styled as the badge (PROJECT / GLOBAL / DEFAULT) opening a small menu with the scope actions — "Apply to all projects", "Use global value" (only when a project override shadows a global one), "Reset to default". There are no separate text-link actions.Writes follow provenance: an edit saves to the layer the resolved value currently lives in — source
globalmeans the edit updates the global layer (and the badge stays GLOBAL); sourceprojectordefaultmeans the edit saves to the project layer. The footer sentence follows the active source ("Changes save automatically to this project" / "... to all projects"). When the source is global, the badge menu additionally offers "Set for this project only" for deliberately forking a project override.Settings apply and persist immediately — there is no Save button anywhere. Feedback: the change takes visible effect at once (optimistic, with rollback and a visible inline error + retry on failure) and the provenance badge shows the new source. No transient saved indicators. While settings load, controls render in the widget's pending/skeleton vocabulary. The footer line reads "Changes save automatically to this project". Built from Ark primitives and the
--chat-*token language; visual design is being iterated on a design canvas and the approved mockup governs the final look.Agent access: settings reach the chat agent through the approved agent-API surface design (every oRPC procedure auto-exposes to the agent catalog with AgentMeta) — no bespoke settings tool is written; the
settingsgroup IS the agent surface once that lane lands. Same core service, same registry validation, same append-only log, no second write path. Audit lines record the actor (userfor panel writes,agentfor agent-initiated writes,filefor external edits), derived from the caller context. Gating follows the approval axis:settings.get/historyare readonly (free);settings.set/clear/applyGloballyask like any other mutating procedure.Live propagation: any settings write (panel or agent) emits a settings-changed notification on the existing server-to-widget event stream; the widget refetches resolved settings and re-applies them immediately. Required so an agent-initiated change (e.g. "switch to dark") repaints the open widget without a reload.
Scheme application: the panel writes through
set; the widget applies the resolved scheme by flipping the existing.light/.darkscheme classes on the widget root (the mechanism landed by the scheme lane).autoremoves the explicit class and defers to the existing detection ladder, which stays live. The resolved settings load with widget hydration.Settings navigation: the settings view is a layout route with each tab a nested child route (matching the panel's existing nested-route structure): a settings layout renders the left nav rail and an outlet; appearance is the first child (index redirects to it). Left vertical nav rail plus independently scrolling content pane, ported from the user's reference settings page (playful dashboard) and restyled to the widget's design language: compact single-line rows (12.5px, 4px radius, quiet text that lifts on hover/active) with a mono uppercase microlabel above the group. The active indicator is the widget's existing trace-rail concept: a hairline glyph spine in an 18px gutter with arms branching to each tab row and a corner elbow into the last, plus a live accent segment on the spine marking the active tab.
Responsive layout (container queries): the settings view adapts to the PANEL's size via CSS container queries — the panel root already declares
container-type: size— never viewport media queries. Below a narrow threshold (~26rem inline size) the vertical nav rail flips to a horizontal top tab strip: the live accent indicator becomes a sliding underline driven by the same clip-path transition on the horizontal axis, and content spans full width below the strip. Fluid internal sizing may use container query units (cqi). The phone sheet inherits this behavior automatically since it is container-driven.Panel header: the settings header uses the session-rail two-line pattern — mono uppercase "SETTINGS" microlabel above a title that is the ACTIVE section name, updating on tab navigation. Section pages carry no in-content header (no card title/description); content starts at the first field.
Control motion (ui-kit-system, not view-local): the segmented control's selected indicator and the switch thumb animate as proper layout animations inside the ui-kit-system primitives (Ark SegmentGroup exposes indicator position via CSS variables — animate those; same treatment for Switch), so every consumer gets the motion. Tab-content changes get a subtle enter transition (fade + small translate) in the widget's existing transition vocabulary. All motion honors reduced-motion (snap, no slide).
Rail rendering rules (from the spike, load-bearing): no rail pixel may be painted by both colors — the gray layer carries a complement clip that excludes the live band (accent is never composited over gray, which blends at antialiased edges and arcs); arms start at the spine stroke's outer edge, never inside its stroke band; band edges land on plain spine a full stroke clear of any arm (entered arm fully gray, active arm fully accent, elbow included when last); stroke centerlines snap to half-integers so 1px strokes land on whole pixels at DPR 1 and 2. The rail is orientation-aware: a CSS custom property set by the container query selects the vertical or horizontal plan, and one generic clip polygon drives the slide on either axis.
Styling landmine: the widget's generated utility layer is emitted last in the app stylesheet, so utility classes in markup outrank hand-written rules at equal specificity — any property a container query must override has to live in the stylesheet, not as a utility class in the markup.
Tab indicator animation: the live accent segment slides along the spine between tabs using the trace rail's existing clip-path transition mechanism (reuse the shipped rail component/mechanics rather than a new indicator), which already snaps instead of sliding under reduced motion.
Dependency: stacks on the scheme-mechanism lane (light-dark token pairs + scheme classes) merging first; this spec's scheme values are meaningless without it.
New ui-kit-system primitives: the segmented control (SegmentGroup) and radio-group primitives introduced for this surface are real additions to ui-kit-system (Ark-based, with stories, following the package's existing component conventions), including the animated selected-indicator via Ark's indicator position CSS variables and the switch thumb motion — not view-local one-offs.
Testing Decisions
settingsgroup through a real server against real files in temp directories (temp project state root plus a temp fake home; never the developer's real~/.conciv). Coverage centers on file and multi-process failure modes: default resolution and project-over-global with controlling-layer correctness; clear-fallthrough; unknown keys in files preserved across programmatic writes; jsonc comment and formatting survival through set/clear/applyGlobally; both-files-present warning; commented-.json write rejection; invalid value reported as invalid while resolution falls through; malformed file → last-known-good reads plus write REJECTION until fixed; atomic replacement (no observable empty/partial file); TWO server processes mutating the global file concurrently under the lock without lost updates; watcher self-echo dedupe (a server write causes exactly one settings-changed); external hand edit → event plus history entry with actorfile; stale-revision mutation → conflict;applyGloballyend state plus single event plus shared opId; registry rejecting a bad set; symlinked settings file written through; file permissions preserved.Out of Scope
scheme(model/harness defaults, appearance extras, behavior knobs come later as registry entries).historyprocedure ships; its UI does not).Further Notes
~/.claude/ project.claudeconvention:~/.concivfor the machine layer was chosen to match, and a~/.concivdirectory precedent already exists in the codebase (dev endpoint registry).