Repository navigation
feat: the web app talks to the reception (08b) - #55
Conversation
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…on 7) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… item by hand, the shell for the conversation and memory pages (08b task 1) An item written by hand is the operator's act: its source is its own MEMORY_NOTED, so the event schema stays. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…f the new pages (08b task 2) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…Operator preferences pages (08b tasks 3, 4) One box with the project picker, the live conversation with its buttons (a tap always answers the newest decision, so older replies' taps turn off), a conversation's own page, and memory with add, edit, pin, done and forget. Checked in headless Chromium against a scripted reception. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… allowed place (08b) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…seen projects respect the cap, one abort listener per screen (review of 08b) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
| "to remember") | ||
| if scope_name == memory.OPERATOR_SCOPE and body.kind != "preference": | ||
| raise ApiError(400, "INVALID", "the operator's memory holds preferences only") | ||
| data_class = "internal" if scope_name == memory.OPERATOR_SCOPE else project_class(server, scope_name) |
There was a problem hiding this comment.
Minor: any slug passes the scope check here. A slug that isn't in ooat.toml and has never been used (e.g. a typo in /memory/dcos) quietly falls back to internal through project_class and creates a new memory scope. That scope can hold a pinned item that steers later tasks. Consider accepting only the slugs that GET /projects would offer this operator, or operator. The class check itself is correct: note() raises the class for personal data, and require_within checks the raised class.
| listed = {slug: {"project": slug, "data_class": p.data_class, "write": p.write, "folders": len(p.folders)} | ||
| for slug, p in server.config.projects.items() if views.within(p.data_class, who.max_data_class)} | ||
| for event in server.runtime().ledger.events(types=["TASK_SUBMITTED", "CONVERSATION_MESSAGE"]): | ||
| slug = event["body"].get("project") |
There was a problem hiding this comment.
Nit (performance): every home page load reads all TASK_SUBMITTED and CONVERSATION_MESSAGE events in the ledger, from every operator. That is fine for F1 with SQLite, but it grows with the ledger. If it becomes a hotspot, filter by actor in the query, or move this to a view (db-dev).
| function addForm() { | ||
| const kind = h("select", {"aria-label": t("memory.kind")}, ...(preferences ? ["preference"] : ADDABLE) | ||
| .map((name) => h("option", {value: name}, t(/** @type {import("../i18n.js").Key} */ (`memory.kind.${name}`))))); | ||
| const text = h("textarea", {rows: "2", required: true, maxlength: "2000", placeholder: t("memory.addText"), |
There was a problem hiding this comment.
Nit: "2000" copies memory.MAX_TEXT here and at line 91. If the server limit changes, the UI will drift from it without any error. A named constant (or a comment pointing at memory.MAX_TEXT) would make the link visible.
|
Verdict: looks good. Only minor suggestions, posted inline. Security invariants: hold
Suggestions (minor)
Docs: Open point for the owner: the I could not run pytest in this environment. The tests I read ( |
Plan 08b (
tasks/f1-skeleton/task-08-reception/plan-08b.md, includes the plan commits of #54 with the owner's change to decision 7)./conversations/{id}(read only unless open on the web); the server's redaction notice links there./memory/{scope}and Operator preferences/memory/operator: add, edit, pin, done, forget.GET /api/v1/projects(names only, within the cap) andPOST /api/v1/memory/{scope}(web session only, secret scan, refused when personal data would raise the item above the author's cap; source = its own MEMORY_NOTED, so the event schema is unchanged).Tests: full suite 1424 passed / 7 skipped;
npm run check,npm test(35). Checked in headless Chromium against a scripted reception (send/Enter, options, Yes/Differently with a correction, Forget, project switch, memory add/refusal/pin, phone width). Reviewer: approve; its suggestions are applied.Open point for the owner:
operatormemory is one scope per server, not per operator (as since 08a); fine for a single owner.🤖 Generated with Claude Code