Skip to content

feat(studio): show the proposal that wrote a memory fact - #2229

Merged
kantord merged 2 commits into
mainfrom
feat/studio-memory-proposal-2223
Oct 14, 2026
Merged

kantord merged 2 commits into
mainfrom
feat/studio-memory-proposal-2223

Conversation

@peppescg

@peppescg peppescg commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This is the memory fact detail's Proposal row from the Studio alignment (#2210), a follow-up to #1854 (#2222). It's the top of a GitHub stack: #2225 → #2226 → #2228 → this PR. Review and merge from the bottom.

The daemon already reports which learning proposal wrote a memory revision (UserModelRevision.source_proposal_id), but Studio's BFF dropped it. The BFF now maps it, the memory revision contract carries it, and the fact detail shows the prototype's Proposal row when it's set.

What changed

  • Contract (apps/contracts/src/schemas/knowledge.ts): memoryRevisionSchema gains sourceProposalId: z.string(), an empty string when no proposal wrote the revision. This is the same shape as sourceSessionId. openapi.json and types.gen.ts are regenerated with task studio:generate; nothing else in the contract changes.
  • BFF (apps/server/src/mecatl/knowledge.ts): memoryRevisionFromSdk() maps sourceProposalId from the SDK's UserModelRevision, for the current revision and for every history entry.
  • UI (apps/web/src/features/memory/memory-fact-detail.tsx): the prototype's Proposal row, between Source session and Updated. The label is "Proposal", the value is the proposal id in monospace, and it links to Settings → Learning (/workspace/settings/learning) with the same hover:underline link as Source session. The row is absent when the id is empty.

Visible change

A fact written by a learning proposal gets one more row in Details, Proposal, with the proposal id. The rows below it move down by one row. A fact with no proposal looks exactly as before.

Differences from the prototype

None for the row itself: same label, position, monospace value, link styling, and link target (/workspace/settings/learning) as stack-08's memory-fact-detail.tsx.

No regression

Pinning tests

2e9fd6256 was committed first, alone, and passes on #2225's head (db98c3768) and on this branch:

  • server/src/mecatl/knowledge.test.ts, "maps a memory revision's provenance for the current value and its history": an exact toEqual of what getMemory maps from an SDK revision, for the current value and one history entry, plus the request sent to the SDK. There was no adapter test for getMemory before.
  • web/src/features/memory/memory-fact-detail.test.tsx, "orders the Details rows key first and the update time last": the exact row labels, in order, for a fact with a source session.

Modified tests

  • The pinning adapter test's expected objects each gain sourceProposalId ("proposal-7" for the current revision, "" for the history entry). The test was written to catch fields the mapping drops, and now it also checks the new field. No expectation was removed.
  • The fact detail test's detail() fixture gains sourceProposalId: "" on each revision, which the contract type now requires. No assertion changes.
  • The route test's fake getMemory returns sourceProposalId: "proposal-7", which the type now requires.

New tests

  • Route and contract (server/src/routes/knowledge.test.ts): GET /api/v1/user-memory/{key} serves sourceProposalId, the body round-trips through memoryDetailResponseSchema, an empty string is accepted, and a revision without the field is rejected.
  • Fact detail: the Proposal row links proposal-7 to /workspace/settings/learning in monospace, between Source session and Updated; and without a proposal there is no Proposal row.
  • Each new test fails without the change. With the mapping and the schema field removed, the adapter and route tests fail; with the row disabled, the fact detail test fails.

The mecated --mock integration test isn't extended: its memory coverage reads the list and a missing key's 404, not revision fields.

Gate counts

Gate #2225 (db98c3768) This branch
Biome 408 files 408 files
Typecheck, generated-check clean clean
Server Vitest 24 files / 187 tests 24 files / 189 tests
Web Vitest 123 files / 769 tests 123 files / 772 tests
task studio:browser (desktop + mobile Chromium) 54 passed 54 passed

Screenshots

Captured with a throwaway Playwright journey (Chromium, not committed) with offline BFF routes, a fixed clock (2026-09-25 12:30 UTC), Europe/Rome, and en-US, at desktop 1280×800 and mobile 390×844, in light and dark. Both trees get the same BFF responses, including sourceProposalId; the before tree ignores it. Before is #2225's head (db98c3768), after is this PR's head (299a1b3). They were captured before the stack was linearised onto #2228. That rebase was conflict-free, and neither #2226 nor #2228 touches the memory fact detail. The gates were rerun on the rebased head. Each tree was captured twice and the two runs matched pixel for pixel. Every pair was pixel-compared; red boxes mark the regions that differ. Desktop composites are downscaled to 1600px wide.

Fact written by a learning proposal

Intended change: a Proposal row between Source session and Updated, the proposal id in monospace, linking to Settings → Learning. Everything below it moves down one row.

Fact written by a learning proposal, desktop light

Dark and mobile

Desktop, dark:

Fact written by a learning proposal, desktop dark

Mobile, light:

Fact written by a learning proposal, mobile light

Mobile, dark:

Fact written by a learning proposal, mobile dark

Fact with no proposal

No change: the revision's sourceProposalId is empty, so there is no Proposal row. Before and after are identical pixel for pixel.

Fact with no proposal, desktop light

Dark and mobile

Desktop, dark:

Fact with no proposal, desktop dark

Mobile, light:

Fact with no proposal, mobile light

Mobile, dark:

Fact with no proposal, mobile dark

Re-verification (2026-10-11)

Re-verified at this head: no change needed.

Closes #2223

🤖 Generated with Claude Code

@peppescg peppescg self-assigned this Oct 9, 2026
@peppescg
peppescg force-pushed the feat/studio-memory-proposal-2223 branch from 299a1b3 to 4019116 Compare October 9, 2026 15:33
@peppescg
peppescg changed the base branch from feat/studio-foundation-2204 to feat/studio-shell-search-2205 October 9, 2026 15:33
@peppescg
peppescg added this pull request to stack #2227 October 9, 2026 15:33
@peppescg
peppescg force-pushed the feat/studio-memory-proposal-2223 branch from 4019116 to 22ccd78 Compare October 11, 2026 14:57
@kantord
kantord force-pushed the feat/studio-memory-proposal-2223 branch 2 times, most recently from 242b112 to 3a96992 Compare October 13, 2026 10:54
@peppescg
peppescg force-pushed the feat/studio-memory-proposal-2223 branch from 3a96992 to 53d3b42 Compare October 14, 2026 08:44
Base automatically changed from feat/studio-shell-search-2205 to main October 14, 2026 08:58
peppescg and others added 2 commits October 14, 2026 11:59
Pin what the BFF maps from the SDK's UserModelRevision today, for the
current value and its history, and the order of the fact detail's
Details rows, before the Proposal row is added.

Refs #2223

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The BFF now maps sourceProposalId from the SDK's UserModelRevision, and
the memory revision contract carries it as a string that is empty when no
proposal wrote the revision, like sourceSessionId. The fact detail page
shows the prototype's Proposal row, in monospace, only when the field is
set, linking to Settings > Learning (no route addresses one proposal).

Closes #2223

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@kantord
kantord force-pushed the feat/studio-memory-proposal-2223 branch from 53d3b42 to d5aa7be Compare October 14, 2026 08:59
@kantord
kantord merged commit 5e27be4 into main Oct 14, 2026
1 check passed
@kantord
kantord deleted the feat/studio-memory-proposal-2223 branch October 14, 2026 08:59
@kantord

kantord commented Oct 14, 2026

Copy link
Copy Markdown
Member

Post-merge review follow-ups for this PR. No blockers or regressions found. Items worth tracking were filed here:

Other candidate findings were checked and dropped as not worth tracking.

Fully or partially written by an AI agent.

@toolhive-release-app toolhive-release-app Bot mentioned this pull request Oct 20, 2026
2 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Studio memory: show the Proposal row on the fact detail (map sourceProposalId)

2 participants