Skip to content
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
58678d9
docs(adr): 0002 frontend vertical-slice decomposition for apps/web go…
Jul 6, 2026
e09dc49
refactor(web): decompose MemorySection into a features/memory vertica…
Jul 6, 2026
0d8e4ed
fix(web): satisfy noUncheckedIndexedAccess in slice-boundary guard
Jul 6, 2026
1a881a1
fix(web): roll back master memory toggle on failed PATCH and enforce …
Jul 8, 2026
f2bdd1a
fix(web): roll back per-hook memory flag on thrown PATCH and enforce …
Jul 8, 2026
711f483
chore: re-trigger CI (flaky UI P0 entry-chrome-flows timeout, unrelat…
Jul 8, 2026
06aef19
fix(web): restore memory extractions after transport failures
Jul 13, 2026
10c0f13
fix(web): surface memory read failures
Jul 13, 2026
dc0da4a
fix(web): restore memory history when rollback reload fails
Jul 14, 2026
f99ea0a
fix(web): preserve memory state on auxiliary read failures
Jul 14, 2026
6bca511
test(web): cover connector load error fixture
Jul 14, 2026
7f15665
fix(web): reconcile overlapping memory deletes
Jul 14, 2026
d2e62d7
chore: drop stale apps/telemetry-worker lockfile importer after rebas…
Jul 14, 2026
06eadee
fix(web): resolve three async race conditions in the memory hooks
Jul 14, 2026
9c7ed8c
fix(web): harden memory hook staleness tracking against same-id and d…
Jul 14, 2026
9c84473
fix(web): close SSE handler-error masking and slice-boundary guard by…
Jul 14, 2026
ca8e269
test(web): close memory-slice coverage gaps to 100% branches/function…
Jul 14, 2026
e05c1cd
fix(web): fix the general invariant behind the last four review findings
Jul 14, 2026
8cbe142
fix(web): route memory connector discovery through the one existing t…
Jul 14, 2026
323ca9f
chore: re-trigger CI (flaky Playwright visual settings-workspace avat…
Jul 14, 2026
e8a5f83
fix(web): route reload()/reloadExtractions() through the same stalene…
Jul 14, 2026
554d8d9
fix(web): close five more guard bypasses codex found on an exhaustive…
Jul 14, 2026
28d38c1
fix(web): restore dropped extraction-hook fixes; land the config hydr…
Jul 14, 2026
e9f19fd
docs(adr): scope the slice-boundary guard as best-effort, not adversa…
Jul 14, 2026
81320e7
fix(web): extract a shared async-commit-guard, fix config/connector r…
Jul 14, 2026
4af2df6
fix(web): return preserved state (not []) from a rejected reloadExtra…
Jul 14, 2026
6e0e447
fix(web): invalidate the config hydration guard at write-settle too
Jul 15, 2026
88f9219
fix(web): reconcile extraction rows by content progression, not recep…
Jul 15, 2026
429ac90
refactor(web): rewrite useMemoryExtractions around one explicit order…
Jul 15, 2026
245a465
chore: re-trigger CI (flaky HomeView community-filter-decouple test, …
Jul 15, 2026
38d38e7
refactor(web): split the extraction-history store out of its hook
Jul 15, 2026
075b757
fix(web): fail fast on malformed 2xx entry reads; recognize JS-backed…
Jul 15, 2026
0381410
fix(web): split connector catalogue/status guards; fail fast on malfo…
Jul 15, 2026
1f62b41
refactor(web): rebuild slice-boundary guard's resolution on ts.resolv…
Jul 15, 2026
72f93f7
fix(web): route memory providers through shared required-field readers
Jul 15, 2026
f65eea0
fix(web): fix 3 nettee bugs, 2 self-found races, push memory-slice co…
Jul 15, 2026
d695f1e
fix(web): harden memory async state and response handling
Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ Follow the root `AGENTS.md` first. This file only records module-level boundarie
- `apps/desktop`: Electron shell. Desktop does not guess the web port; it reads runtime status through sidecar IPC and opens the reported web URL.
- `apps/packaged`: Thin packaged Electron runtime entry. It starts packaged daemon/web sidecars, registers the `od://` entry protocol, and delegates desktop host behavior to `apps/desktop`.

## apps/web frontend refactors (vertical slices)

Before decomposing a large `apps/web` component (a "god-component" like `SettingsDialog`, `MemorySection`, or `ChatComposer`), read `docs/adr/0002-frontend-vertical-slice-decomposition.md` first. It is the canonical design for this work and covers: the layer split (wire DTOs → `packages/contracts`, transport adapters → `apps/web/src/providers/`, in-slice `features/<slice>/` owning ports + rules + hooks + dumb components + barrel), the injected-port hook paradigm, and the **Testing & coverage strategy for a slice** subsection (how to reach ≥95% honestly — classify each "unreachable" branch and apply the matching fix instead of `/* v8 ignore */`).

The `MemorySection` slice (`apps/web/src/features/memory/`) is the worked canary; mirror its structure. A detailed session working-log for that canary lives in the branch's `ADS-project-knowledge/reports/*-memory-slice-*handoff.md` when present.

## Daemon layout

- `apps/daemon/src/` contains only daemon app source.
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
"@types/node": "20.19.39",
"@types/react": "18.3.28",
"@types/react-dom": "18.3.7",
"@vitest/coverage-v8": "4.1.6",
"jsdom": "29.1.1",
"postcss": "8.5.15",
"tailwindcss": "4.3.0",
Expand Down
19 changes: 2 additions & 17 deletions apps/web/src/components/MemoryModelInline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import type {
MemoryExtractionProvider,
MemoryListResponse,
} from '@open-design/contracts';
import { patchMemoryExtractionConfig } from '../providers/memory';
import type { AgentModelOption, ApiProtocol, ExecMode } from '../types';
import {
SUGGESTED_MODELS_BY_PROTOCOL,
Expand Down Expand Up @@ -170,22 +171,6 @@ async function fetchMemoryExtraction(): Promise<MemoryExtractionMaskedConfig | n
}
}

async function saveMemoryExtraction(
extraction: MemoryExtractionConfigShape | null,
): Promise<MemoryExtractionMaskedConfig | null | undefined> {
const resp = await fetch('/api/memory/config', {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ extraction }),
});
if (!resp.ok) return undefined;
const json = (await resp.json()) as {
enabled: boolean;
extraction: MemoryExtractionMaskedConfig | null;
};
return json.extraction ?? null;
}

export function MemoryModelInline({
mode,
apiProtocol,
Expand Down Expand Up @@ -313,7 +298,7 @@ export function MemoryModelInline({
) => {
setBusy(true);
try {
const result = await saveMemoryExtraction(next);
const result = await patchMemoryExtractionConfig(next);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

patchMemoryExtractionConfig() now throws when a 2xx /api/memory/config response is missing the required extraction field, but persist() still treats this call as if it only returned undefined on failure. That leaves both save paths here broken on the new contract: the click handlers (await persist(...)) reject out of the React event with no user-visible failure state, and the debounced BYOK re-sync below (void persist(...)) becomes an unhandled promise rejection. The provider test added in this PR already locks in the thrown malformed-success case, so this caller needs to absorb it explicitly. Please wrap the await in try/catch, keep the prior config on rejection, surface a local failure state instead of throwing out of the component, and add a component test that drives a malformed-success response through both a user-triggered save and the debounced re-sync path.

🔁 Powered by Looper · runner=reviewer · agent=codex · An autonomous AI dev team for your GitHub repos.

if (result !== undefined) {
setConfig(result);
// Skip the "Saved!" flash on background re-syncs (provider
Expand Down
6 changes: 4 additions & 2 deletions apps/web/src/components/MemoryProfilePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ function parseProfileBody(body: string): Record<string, string> {
for (const line of lines) {
const m = /^\s*-\s*([^:]+):\s*(.*)$/.exec(line);
if (!m) continue;
const rawLabel = (m[1] ?? '').trim().toLowerCase();
const value = (m[2] ?? '').trim();
// Both groups are guaranteed present once `.exec` matches (`[^:]+` requires
// ≥1 char; `.*` always yields a string), so the captures are non-null here.
const rawLabel = m[1]!.trim().toLowerCase();
const value = m[2]!.trim();
const field = PROFILE_FIELDS.find((f) => f.label.toLowerCase() === rawLabel);
if (field) out[field.label] = value;
}
Expand Down
Loading
Loading