mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-07-07 20:32:12 +02:00
Canonicalize workbench theme storage
This commit is contained in:
parent
5c4948cc2e
commit
c5e7708911
4 changed files with 99 additions and 35 deletions
|
|
@ -12,8 +12,8 @@ Verified source roots:
|
||||||
- Effect v4 subtree: `/home/elpresidank/YeeBois/projects/beep-effect2/.repos/effect-v4`
|
- Effect v4 subtree: `/home/elpresidank/YeeBois/projects/beep-effect2/.repos/effect-v4`
|
||||||
- Installed Effect beta used by this workspace: `ts/node_modules/effect`
|
- Installed Effect beta used by this workspace: `ts/node_modules/effect`
|
||||||
|
|
||||||
Current signal counts from `ts/packages` after the 2026-06-02 legacy consumer
|
Current signal counts from `ts/packages` after the 2026-06-02 workbench theme
|
||||||
facade slice:
|
storage slice:
|
||||||
|
|
||||||
| Signal | Count |
|
| Signal | Count |
|
||||||
| --- | ---: |
|
| --- | ---: |
|
||||||
|
|
@ -21,8 +21,8 @@ facade slice:
|
||||||
| `Effect.runPromiseWith` | 0 |
|
| `Effect.runPromiseWith` | 0 |
|
||||||
| `Effect.cached` | 0 |
|
| `Effect.cached` | 0 |
|
||||||
| `Layer.succeed` | 12 |
|
| `Layer.succeed` | 12 |
|
||||||
| `Map<` | 88 |
|
| `Map<` | 38 |
|
||||||
| `WebSocket` | 74 |
|
| `WebSocket` | 72 |
|
||||||
| `new Map` | 60 |
|
| `new Map` | 60 |
|
||||||
| `toPromiseRequestor` | 0 |
|
| `toPromiseRequestor` | 0 |
|
||||||
| `makeAsyncProcessor` | 19 |
|
| `makeAsyncProcessor` | 19 |
|
||||||
|
|
@ -132,6 +132,12 @@ Notes:
|
||||||
`Effect.runPromise`, `receive(`, `new Promise`, and `setTimeout` counts
|
`Effect.runPromise`, `receive(`, `new Promise`, and `setTimeout` counts
|
||||||
dropped because the old blocking facade loop and its test timer shim were
|
dropped because the old blocking facade loop and its test timer shim were
|
||||||
removed.
|
removed.
|
||||||
|
- The workbench theme storage slice stopped mirroring `themeAtom` into the
|
||||||
|
legacy `tg-theme` localStorage key. The canonical
|
||||||
|
`trustgraph-workbench-theme-v1` value remains owned by `Atom.kvs` over
|
||||||
|
`BrowserKeyValueStore.layerLocalStorage`; the first-paint host script reads
|
||||||
|
that JSON-encoded key before React mounts and falls back to `tg-theme` only
|
||||||
|
for legacy installs.
|
||||||
- A focused broker-backend scout found no remaining P0 broker runtime rewrite
|
- A focused broker-backend scout found no remaining P0 broker runtime rewrite
|
||||||
after the producer, NATS, consumer concurrency, rate-limit, and
|
after the producer, NATS, consumer concurrency, rate-limit, and
|
||||||
request-response stop slices. `PubSubBackend` remains an intentional
|
request-response stop slices. `PubSubBackend` remains an intentional
|
||||||
|
|
@ -1332,14 +1338,40 @@ Notes:
|
||||||
- `cd ts && bun run test`
|
- `cd ts && bun run test`
|
||||||
- `git diff --check`
|
- `git diff --check`
|
||||||
|
|
||||||
|
### 2026-06-02: Workbench Theme KeyValueStore Slice
|
||||||
|
|
||||||
|
- Status: migrated and root-verified.
|
||||||
|
- Completed:
|
||||||
|
- `themeClassAtom` no longer writes the legacy `tg-theme` localStorage
|
||||||
|
mirror. Theme persistence stays in the canonical
|
||||||
|
`trustgraph-workbench-theme-v1` `Atom.kvs` entry backed by
|
||||||
|
`BrowserKeyValueStore.layerLocalStorage`.
|
||||||
|
- The pre-paint host script in `index.html` now restores the canonical
|
||||||
|
JSON-encoded theme key before React mounts, with `tg-theme` retained only
|
||||||
|
as a legacy fallback.
|
||||||
|
- Workbench QA now asserts that changing theme writes the canonical key and
|
||||||
|
does not recreate `tg-theme`.
|
||||||
|
- Verification:
|
||||||
|
- `cd ts && bun run check`
|
||||||
|
- `cd ts && bun run workbench:qa`
|
||||||
|
- `cd ts && bun run build`
|
||||||
|
- `cd ts && bun run test`
|
||||||
|
- `cd ts && bun run lint`
|
||||||
|
|
||||||
## Subagent Findings To Preserve
|
## Subagent Findings To Preserve
|
||||||
|
|
||||||
- MCP/workbench:
|
- MCP/workbench:
|
||||||
- Make the Effect MCP server the canonical implementation. The old stdio
|
- Make the Effect MCP server the canonical implementation. The old stdio
|
||||||
server should remain only as compatibility while parity is needed.
|
server should remain only as compatibility while parity tests and an
|
||||||
|
Effect `McpServer.layerStdio` entrypoint are missing. Do not delete
|
||||||
|
`server.ts` until stdio `tools/list`/`tools/call` parity is proved.
|
||||||
- Workbench BaseApi atoms can move toward `AtomRpc` or `AtomHttpApi` after
|
- Workbench BaseApi atoms can move toward `AtomRpc` or `AtomHttpApi` after
|
||||||
the client API is less Promise-first.
|
the client API is less Promise-first.
|
||||||
- MCP env is now Config-backed; continue that policy for future MCP settings.
|
- MCP env is now Config-backed; continue that policy for future MCP settings.
|
||||||
|
- Workbench persistent theme storage is now canonicalized through
|
||||||
|
`Atom.kvs`/`BrowserKeyValueStore`. Remaining workbench `localStorage`
|
||||||
|
matches are legacy migration fallbacks, QA assertions, or the pre-paint
|
||||||
|
host script.
|
||||||
- Flow stateful services:
|
- Flow stateful services:
|
||||||
- Config service, KnowledgeCore service, FlowManager, and Librarian
|
- Config service, KnowledgeCore service, FlowManager, and Librarian
|
||||||
ref-backed state slices are complete. Follow-up service work should focus
|
ref-backed state slices are complete. Follow-up service work should focus
|
||||||
|
|
@ -1410,6 +1442,11 @@ Notes:
|
||||||
text, token counts, streaming final usage, and rate-limit mapping. The
|
text, token counts, streaming final usage, and rate-limit mapping. The
|
||||||
local provider layer-construction cleanup is complete; remaining provider
|
local provider layer-construction cleanup is complete; remaining provider
|
||||||
work is adapter/parity work, not `Layer.succeed` cleanup.
|
work is adapter/parity work, not `Layer.succeed` cleanup.
|
||||||
|
- The next provider PR should add a small `effect/unstable/ai/LanguageModel`
|
||||||
|
to TrustGraph `LlmProvider` adapter and prove it with fake
|
||||||
|
`LanguageModel` parts before migrating Claude. Direct OpenAI, Azure, and
|
||||||
|
OpenAI-compatible swaps are no-ops until Responses-vs-Chat-Completions
|
||||||
|
parity is proven.
|
||||||
- FalkorDB scoped lifecycle is complete for triples query/store. Use the
|
- FalkorDB scoped lifecycle is complete for triples query/store. Use the
|
||||||
fakeable client/graph factory pattern from that slice for future storage
|
fakeable client/graph factory pattern from that slice for future storage
|
||||||
client tests.
|
client tests.
|
||||||
|
|
@ -1473,12 +1510,16 @@ Notes:
|
||||||
- `effect/unstable/ai/LanguageModel`, `effect/unstable/ai/EmbeddingModel`,
|
- `effect/unstable/ai/LanguageModel`, `effect/unstable/ai/EmbeddingModel`,
|
||||||
Effect AI OpenAI/Anthropic provider layers.
|
Effect AI OpenAI/Anthropic provider layers.
|
||||||
- Rewrite shape:
|
- Rewrite shape:
|
||||||
- Add an Effect AI adapter layer beside the current `LlmProvider` contract
|
- Add an Effect AI `LanguageModel` to `LlmProvider` adapter beside the
|
||||||
before flipping any public provider interface.
|
current `LlmProvider` contract before flipping any public provider
|
||||||
- Use Effect AI provider layers only where parity is proven.
|
interface.
|
||||||
- Keep OpenAI-compatible/Azure-compatible behavior behind parity tests
|
- Prove `LlmResult`, streaming `text-delta` plus final `finish.usage`,
|
||||||
because current code uses chat-completions style APIs while the Effect
|
`AiError.RateLimitError` mapping, and missing-token config behavior with
|
||||||
OpenAI language model is Responses API backed.
|
fake Effect `LanguageModel` tests.
|
||||||
|
- Claude is the first plausible provider migration after the adapter.
|
||||||
|
- Do not directly swap OpenAI, Azure, or OpenAI-compatible providers yet:
|
||||||
|
current TrustGraph code uses Chat Completions/local-server semantics while
|
||||||
|
`@effect/ai-openai` is Responses API backed.
|
||||||
- Tests:
|
- Tests:
|
||||||
- Provider parity for `LlmResult`, final streaming chunk token counts, 429
|
- Provider parity for `LlmResult`, final streaming chunk token counts, 429
|
||||||
mapping, missing-token config failures, and OpenAI-compatible local-server
|
mapping, missing-token config failures, and OpenAI-compatible local-server
|
||||||
|
|
@ -1490,34 +1531,37 @@ Notes:
|
||||||
- First blocker slice complete: MCP now builds under strict tsgo and the
|
- First blocker slice complete: MCP now builds under strict tsgo and the
|
||||||
stdio server has an Effect-backed compatibility implementation.
|
stdio server has an Effect-backed compatibility implementation.
|
||||||
- Remaining shape:
|
- Remaining shape:
|
||||||
- Decide whether the old SDK/Zod stdio compatibility surface should stay as
|
- Keep the old SDK/Zod stdio compatibility surface for now.
|
||||||
a wrapper or be removed.
|
- Add an Effect stdio entrypoint with `McpServer.layerStdio`, then prove
|
||||||
- Add parity tests before deleting any public entry point.
|
`tools/list` and `tools/call` parity before deleting any public entry
|
||||||
|
point or dropping `zod`/server-side MCP SDK dependencies.
|
||||||
|
- Pay special attention to `text_completion`: legacy calls the TrustGraph
|
||||||
|
gateway, while the Effect server currently uses an Effect AI
|
||||||
|
`LanguageModel`/OpenAI layer.
|
||||||
- Tests:
|
- Tests:
|
||||||
- `cd ts && bun run --cwd packages/mcp build`
|
- `cd ts && bun run --cwd packages/mcp build`
|
||||||
- Root `cd ts && bun run check`
|
- Root `cd ts && bun run check`
|
||||||
|
|
||||||
### P2: Tighten Workbench Platform And Reactivity Usage
|
### No-op: Workbench Theme And Browser Storage Boundaries
|
||||||
|
|
||||||
- TrustGraph evidence:
|
- Status: migrated and documented no-op for remaining direct browser matches.
|
||||||
- `ts/packages/workbench/src/atoms/workbench.ts`
|
- Evidence:
|
||||||
- Remaining direct browser state includes `localStorage` and DOM theme
|
- `settingsAtom`, `themeAtom`, `flowIdAtom`, and `conversationAtom` are
|
||||||
inspection.
|
canonical `Atom.kvs` entries backed by
|
||||||
- Effect primitives:
|
`BrowserKeyValueStore.layerLocalStorage`.
|
||||||
- `BrowserKeyValueStore.layerLocalStorage`,
|
- The only remaining `tg-theme` production read is a legacy migration
|
||||||
`BrowserKeyValueStore.layerSessionStorage`, `BrowserHttpClient`,
|
fallback. The pre-paint `index.html` script is a host boundary that restores
|
||||||
`Clipboard`, `AtomRpc`, `AtomHttpApi`, `AtomRegistry`, `AsyncResult`,
|
the canonical key before React and Effect services mount.
|
||||||
`Reactivity`.
|
- Graph canvas DOM/class inspection remains render-only host behavior.
|
||||||
- Rewrite shape:
|
- Rule:
|
||||||
- Leave workbench out of the next backend/runtime rewrite wave.
|
- Do not reopen workbench `localStorage` as a backend/runtime blocker unless
|
||||||
- Move persistent UI state through browser platform services later.
|
a canonical `Atom.kvs` entry is bypassed or a legacy migration fallback can
|
||||||
- Tests:
|
be intentionally removed.
|
||||||
- `cd ts && bun run workbench:qa`
|
|
||||||
|
|
||||||
## Recommended PR Order
|
## Recommended PR Order
|
||||||
|
|
||||||
1. Effect AI provider adapter cleanup.
|
1. Effect AI `LanguageModel` to `LlmProvider` adapter, then Claude parity.
|
||||||
2. MCP parity/deletion decision and workbench platform polish.
|
2. MCP Effect stdio parity and canonicalization.
|
||||||
|
|
||||||
## No-Op Rules
|
## No-Op Rules
|
||||||
|
|
||||||
|
|
@ -1539,6 +1583,12 @@ Do not flag these as rewrite blockers without additional proof:
|
||||||
- JSON stringification in tests or wire-contract fixtures. Production JSON
|
- JSON stringification in tests or wire-contract fixtures. Production JSON
|
||||||
encode/decode should prefer schema codecs when the encoded form can be
|
encode/decode should prefer schema codecs when the encoded form can be
|
||||||
preserved.
|
preserved.
|
||||||
|
- Workbench pre-paint `index.html` storage reads are host-boundary code used
|
||||||
|
before React and Effect services mount. Keep canonical workbench state in
|
||||||
|
`Atom.kvs`/`BrowserKeyValueStore`, and treat old-key reads as migration
|
||||||
|
fallbacks unless backward compatibility is intentionally dropped.
|
||||||
|
- The old MCP SDK/Zod stdio server is compatibility code until an Effect stdio
|
||||||
|
entrypoint plus parity tests prove the same public protocol behavior.
|
||||||
- Client `newableFactory` assertions that preserve vendored callable-plus-new
|
- Client `newableFactory` assertions that preserve vendored callable-plus-new
|
||||||
API facades are compatibility boundaries unless the public constructor API is
|
API facades are compatibility boundaries unless the public constructor API is
|
||||||
intentionally redesigned.
|
intentionally redesigned.
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,16 @@
|
||||||
<script>
|
<script>
|
||||||
// Restore theme preference before first paint
|
// Restore theme preference before first paint
|
||||||
(function() {
|
(function() {
|
||||||
var theme = localStorage.getItem('tg-theme');
|
var rawTheme = localStorage.getItem('trustgraph-workbench-theme-v1');
|
||||||
|
var theme = null;
|
||||||
|
if (rawTheme === '"light"') {
|
||||||
|
theme = 'light';
|
||||||
|
} else if (rawTheme === '"dark"') {
|
||||||
|
theme = 'dark';
|
||||||
|
}
|
||||||
|
if (theme !== 'dark' && theme !== 'light') {
|
||||||
|
theme = localStorage.getItem('tg-theme');
|
||||||
|
}
|
||||||
if (theme === 'light') {
|
if (theme === 'light') {
|
||||||
document.body.classList.remove('dark');
|
document.body.classList.remove('dark');
|
||||||
document.body.classList.add('light');
|
document.body.classList.add('light');
|
||||||
|
|
|
||||||
|
|
@ -447,8 +447,8 @@ function legacyConversation(): ConversationState {
|
||||||
|
|
||||||
function defaultTheme(): Theme {
|
function defaultTheme(): Theme {
|
||||||
if (typeof localStorage !== "undefined") {
|
if (typeof localStorage !== "undefined") {
|
||||||
const saved = localStorage.getItem("tg-theme");
|
const legacyTheme = localStorage.getItem("tg-theme");
|
||||||
if (saved === "dark" || saved === "light") return saved;
|
if (legacyTheme === "dark" || legacyTheme === "light") return legacyTheme;
|
||||||
}
|
}
|
||||||
if (typeof document !== "undefined" && document.documentElement.classList.contains("light")) {
|
if (typeof document !== "undefined" && document.documentElement.classList.contains("light")) {
|
||||||
return "light";
|
return "light";
|
||||||
|
|
@ -773,7 +773,6 @@ export const themeClassAtom = Atom.make((get) => {
|
||||||
document.documentElement.classList.toggle("light", theme === "light");
|
document.documentElement.classList.toggle("light", theme === "light");
|
||||||
document.body.classList.toggle("light", theme === "light");
|
document.body.classList.toggle("light", theme === "light");
|
||||||
document.body.classList.toggle("dark", theme === "dark");
|
document.body.classList.toggle("dark", theme === "dark");
|
||||||
localStorage.setItem("tg-theme", theme);
|
|
||||||
}
|
}
|
||||||
return theme;
|
return theme;
|
||||||
}).pipe(Atom.keepAlive);
|
}).pipe(Atom.keepAlive);
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,12 @@ test("settings persist view state and drive feature switches", async ({ page })
|
||||||
|
|
||||||
await page.getByText("Theme").click();
|
await page.getByText("Theme").click();
|
||||||
await expect.poll(() => page.evaluate(() => document.body.classList.contains("light"))).toBe(true);
|
await expect.poll(() => page.evaluate(() => document.body.classList.contains("light"))).toBe(true);
|
||||||
|
await expect.poll(() =>
|
||||||
|
page.evaluate(() => ({
|
||||||
|
canonical: window.localStorage.getItem("trustgraph-workbench-theme-v1"),
|
||||||
|
legacy: window.localStorage.getItem("tg-theme"),
|
||||||
|
}))
|
||||||
|
).toEqual({ canonical: "\"light\"", legacy: null });
|
||||||
|
|
||||||
await page.getByLabel("Create collection").click();
|
await page.getByLabel("Create collection").click();
|
||||||
const collectionDialog = page.getByRole("dialog", { name: "Create Collection" });
|
const collectionDialog = page.getByRole("dialog", { name: "Create Collection" });
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue