Skip to content

Pin conversations and group them into folders - #1007

Open
dcartertwo wants to merge 3 commits into
mainfrom
claude/cloudflare-os-design-20d9d6
Open

dcartertwo wants to merge 3 commits into
mainfrom
claude/cloudflare-os-design-20d9d6

Conversation

@dcartertwo

@dcartertwo dcartertwo commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Workspaces with a lot of conversations make it hard to keep the few you're working on in reach. This lets a workspace arrange its conversation list:

  • Each conversation's ⋮ menu gets Pin to top and Move to folder. Move to folder lists the workspace's folders and has New folder…, which names a new folder for that conversation.
  • Pinned conversations show only under Pinned. A pinned conversation keeps its folder, so unpinning puts it back there. Moving a pinned conversation into a folder unpins it.
  • Folders can be renamed, collapsed and deleted. Deleting a folder asks first, then moves its conversations to Other; it never deletes them.
  • Everything not pinned or in a folder goes under Other, still grouped by Today, Yesterday and so on. Other can be hidden.

Until someone pins a conversation or files one in a folder, the list looks exactly as it does today.

Pins and folders belong to the workspace, like conversation titles: collaborators see the same arrangement on every device, and see changes as they happen. Pins are shared along with folders, unlike pinned workspaces, which are each user's own. Each conversation's metadata gets an optional pinned flag and folder name, set by two new Overseer methods:

  • setChatPlacement(chatId, {pinned?, folder?}) changes either or both in one write. A field it isn't given stays as it is, so pinning a conversation can't undo someone else's move of it into a folder.
  • renameChatFolder(folder, newName) refiles every conversation in a folder; a null newName deletes the folder. A folder is only the name its conversations share, so there's no folder record to keep in sync and no empty folders.

Neither method changes when a conversation was last active, so arranging the list doesn't reorder it. A conversation pinned while an agent is replying stays pinned, because metadata updates read and write the record without waiting in between. "use" share-link sessions can't call either method, just as they can't rename conversations.

Which folders are collapsed, and whether Other is hidden, is each person's own view, so that stays in localStorage per user and workspace.

The kernel and API change is the first commit, apart from the UI.

Collapsing a folder or hiding Other would also hide the Working, Pending changes and unread labels on the conversations inside, so a collapsed header shows how many are working or have pending changes ("1 working", "2 pending") and how many unread messages they have.

Pinned conversations, two folders, and Other

Move to folder in a conversation's menu, with New folder…

Collapsed folders and Other, with their working, pending and unread counts

@github-actions github-actions Bot added the workshop/frontend Changes to the Workshop frontend label Nov 4, 2026
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

Preview: pr1007-claude-cloud-555911d3

https://pr1007-claude-cloud-555911d3-router.cloudflare-os-previews.workers.dev

Dashboard · deleted when this PR closes

Comment on lines +29 to +32
const move = (change: () => void) => {
change();
requestAnimationFrame(() =>
document.querySelector<HTMLElement>(`[data-chat-actions="${chatId}"]`)?.focus());

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] Preserve focus when moving a conversation into hidden Other

With a folder present, pin a conversation, hide Other, then choose Unpin. The row is removed and Other stays collapsed, so this selector finds no replacement trigger. I reproduced focus falling to document.body in Chromium, with the next Tab restarting at the first control. Remove from folder has the same path. Please focus the Other disclosure when the destination row is hidden, or expand Other before restoring row focus. The frontend guide (packages/workshop-frontend/AGENTS.md) requires: "Preserve keyboard behavior, focus management, and accessible names when extracting interactive UI."

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in b5e599d: when the moved conversation lands in a hidden Other, focus now goes to Other's toggle. Checked in Chromium with the keyboard for Unpin and Remove from folder; the next Tab continues to the composer.

@ask-bonk

ask-bonk Bot commented Nov 4, 2026

Copy link
Copy Markdown

Review: 1 findings.

Posted one actionable inline comment, verified in Chromium. Reviewed all 10 changed files against the frontend guidance.

github run

@dcartertwo
dcartertwo marked this pull request as ready for review November 4, 2026 16:42

@clanky-the-clanker clanky-the-clanker Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Superseded by the review of b5e599d9.

This review, of 00c7212b

💬 Approved with comments — No blocking or warning-level issues. There are two optional suggestions: validate each stored folder entry, and handle localStorage write failures.

Findings: 2 suggestions (2 are inline comments on the diff)

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


📋 Code Quality: 💬 2 suggestions

The feature is well structured. Two optional hardening suggestions for the localStorage store.

Recommendation: 💬 Optional Nits

  • [SUGGESTION] Stored folders aren't validated per entry, so a null entry would make the chat list throw on every render (packages/workshop-frontend/src/features/chat-organizer/chatOrganizerStore.ts:21)
  • [SUGGESTION] A localStorage write failure throws uncaught from the menu handlers and the user sees nothing (packages/workshop-frontend/src/features/chat-organizer/chatOrganizerStore.ts:41)
View full review
  • Per-entry validation of stored data (chatOrganizerStore.ts:21): parse checks the overall shape but not each entry in folders. A null entry, for example from a hand-edited value, would make organizeChats throw on folder.id while the list renders, every time it renders, until the key is cleared.
  • Write failures (chatOrganizerStore.ts:41): a setItem error (full quota, or storage blocked) propagates out of the menu click handlers uncaught. The user gets no feedback, and createFolder skips entering rename mode.

✅ All other checks passed

🔒 Security: ✅ LGTM

localStorage holds only chat ids, folder ids (from crypto.randomUUID) and folder names, under a key scoped to the user and workspace. React escapes folder names when it renders them, and the parsed data is used only locally.


📖 AGENTS.md: ✅ LGTM

No repository guideline issues.


⚡ Performance: ✅ LGTM

Organizing and bucketing are single linear passes. Store snapshots are cached until the stored value changes, and listeners are cleaned up correctly.


📝 Documentation: ✅ LGTM

No documentation issues.


Reviewed commit: 00c7212b

try {
const value = JSON.parse(raw) as Partial<ChatOrganization> | null;
return {
folders: Array.isArray(value?.folders) ? value.folders : [],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💬 SUGGESTION · Code Quality

Stored folders aren't validated per entry, so a null entry would make the chat list throw on every render

parse checks that folders is an array but not what it contains. If a stored entry is null (a hand-edited value, or some future shape), organizeChats throws on folder.id during render, and the chat list stays broken until the key is cleared. You could keep only the entries that are objects with string id and name, and reject a places that is an array. A test next to the existing invalid-JSON case would cover it.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in b5e599d: each saved folder must be an object with a string id and name, and an array places is ignored. Added a test beside the invalid-JSON case.


/** Saves an arrangement. A storage error (a full quota) propagates: the change didn't happen. */
export const writeChatOrganization = (key: string, organization: ChatOrganization) => {
localStorage.setItem(key, JSON.stringify(organization));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💬 SUGGESTION · Code Quality

A localStorage write failure throws uncaught from the menu handlers and the user sees nothing

Every caller of writeChatOrganization is a menu or button click handler. If setItem throws (full quota, or storage blocked), the error goes uncaught and the click silently does nothing. In createFolder, the folder also never enters rename mode. You could catch the error in useChatOrganizer's update and show it to the user, for example with a toast.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in b5e599d: a change that can't be saved now shows an error toast ("Couldn't save that change"), and a new folder only enters rename mode once it's saved. Added a test.

@clanky-the-clanker clanky-the-clanker Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Superseded by the review of e2d597f2.

This review, of b5e599d9

✅ Approved — No findings. The two earlier suggestions (per-entry validation of stored folders, handling localStorage write failures) are fixed and have tests.

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


✅ All checks passed

🔒 Security: ✅ LGTM

No security issues. localStorage holds only chat ids, folder ids and folder names, under a key scoped to the user and workspace. React escapes folder names when it renders them.


📖 AGENTS.md: ✅ LGTM

No repository guideline issues.


📋 Code Quality: ✅ LGTM

  • Per-entry folder validation (chatOrganizerStore.ts): fixed. Each stored entry is checked by isFolder, and useChatOrganizer.test.tsx covers null and malformed entries.
  • Storage write failures (useChatOrganizer.ts): fixed. update catches the error, shows an error toast and returns false, and createFolder only enters rename mode when the save succeeded.
  • organizeChats treats an unknown or non-string place value as Other, so loosely validated places entries cannot throw.

⚡ Performance: ✅ LGTM

Organizing and counting statuses are single linear passes. The parsed snapshot is cached until the stored value changes, and subscriptions are cleaned up correctly.


📝 Documentation: ✅ LGTM

No documentation issues.


Reviewed commit: b5e599d9

<DropdownMenu.Item
icon={<Trash className="size-3" />}
variant="danger"
onClick={() => organizer.deleteFolder(folder.id)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[P2] Restore keyboard focus after deleting a folder

Choosing Delete folder removes the focused menu and its trigger, but this path never transfers focus to a surviving control. I reproduced this in Chromium using the keyboard: after deletion, document.activeElement is document.body, and the next Tab restarts at the first page control. Please focus a nearby surviving control (for example, the next folder, Other, or New folder) after the deletion, including when the last folder is removed. packages/workshop-frontend/AGENTS.md requires: "Preserve keyboard behavior, focus management, and accessible names when extracting interactive UI."

@ask-bonk

ask-bonk Bot commented Nov 4, 2026

Copy link
Copy Markdown

Since last review: 1 resolved, 0 still open, 1 new.

Posted one actionable inline comment. All 14 organizer tests pass, and Chromium confirms the earlier focus fix.

github run

dcartertwo and others added 2 commits November 9, 2026 11:47
A chat's metadata can now say whether it's pinned and which folder it's
in, so everyone in the workspace sees the same arrangement on every
device.

setChatPlacement() changes either or both in one write. A field it isn't
given stays as it is, so changing one can't undo someone else's change
to the other. A folder is just the name its chats share, so
renameChatFolder() refiles every chat in it, and renaming to null
deletes the folder. Neither changes when the chat was last active, so
arranging the list doesn't reorder it. "use" share-link sessions can't
call either.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The chat list shows the workspace's pins and folders, and changes to
them appear for collaborators as they happen.

- A conversation's menu gets Pin to top and Move to folder, which lists
  the folders and has New folder… to name a new one.
- Pinned conversations show only under Pinned, keeping their folder for
  when they're unpinned. Filing a pinned conversation unpins it.
- Everything not pinned or filed goes under Other, which can be hidden.
- A collapsed folder or a hidden Other says how many of its
  conversations are working or have pending changes, and how many
  unread messages they have.
- Deleting a folder asks first, since it changes everyone's list.
- Which folders are collapsed, and whether Other is hidden, stay per
  person in localStorage.
- Focus waits until the list shows a change before following the moved
  conversation, because its new metadata can arrive just before or just
  after the change resolves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dcartertwo
dcartertwo force-pushed the claude/cloudflare-os-design-20d9d6 branch from b5e599d to e2d597f Compare November 9, 2026 18:05
@github-actions github-actions Bot added kernel Changes to the Workshop kernel workshop/shared Changes to shared Workshop APIs labels Nov 9, 2026

@clanky-the-clanker clanky-the-clanker Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Superseded by the review of 5005edcf.

This review, of e2d597f2

💬 Approved with comments — Kernel and API changes are correct, access is denied to share-link sessions as intended, and tests cover them. One INFO: renaming a collapsed folder updates the saved collapsed state before the rename succeeds.

Findings: 1 info (1 is an inline comment on the diff)

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


📋 Code Quality: 💬 1 info

One minor note: renaming a collapsed folder updates the saved collapsed state before the server rename succeeds.

Recommendation: 💬 Optional Nits

  • [INFO] Collapsed-state rename is applied before renameChatFolder succeeds, so a failed rename expands the folder and leaves a stale entry (packages/workshop-frontend/src/features/chat-organizer/useChatOrganizer.ts:142)
View full review
  • Collapsed state is updated before the rename succeeds (useChatOrganizer.ts:142-151). When the rename RPC fails, the folder keeps its old name but is no longer in collapsedFolders, so it opens unexpectedly. The new name stays in the list as a stale entry. This only affects one person's view preference.

The new Overseer methods are atomic and leave lastActive alone. The other metadata writers keep the new fields, and both the backend and the frontend hook have tests.


✅ All other checks passed

🔒 Security: ✅ LGTM

UseOverseerInterface denies setChatPlacement and renameChatFolder, the same way it denies setChatTitle, and the DENIED_OVERSEER integration table covers both. Arguments are validated by @validateRpc(), and assertFolderName rejects blank names. React escapes folder names when it renders them.


📖 AGENTS.md: ✅ LGTM

No repository guideline issues. The kernel and API change is in its own commit, apart from the UI.


⚡ Performance: ✅ LGTM

No performance issues.


📝 Documentation: ✅ LGTM

The new Overseer methods and the ChatPlacement type have doc comments, as the kernel rules require.


Reviewed commit: e2d597f2

setFocusRequest({ targets: [() => findFolderToggle(folder)], shown: showsNow });
return;
}
if (view.collapsedFolders.includes(folder)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ℹ️ INFO · Code Quality

Collapsed-state rename is applied before renameChatFolder succeeds, so a failed rename expands the folder and leaves a stale entry

updateView swaps the old folder name for the new one in collapsedFolders before renameChatFolder runs. If the RPC fails, the folder keeps its old name but is no longer marked collapsed, so it opens unexpectedly. The new name also stays saved, so a folder later created with that name starts out collapsed. To fix this, apply the collapsedFolders update only after await overseer.renameChatFolder(...) resolves inside the save callback.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 5005edc. The collapsed state still moves to the new name right away, so the folder doesn't flash open while the rename saves, but a failed rename now moves it back to the old name.

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown

LGTM!

github run

Renaming a collapsed folder moves its collapsed state to the new name
right away, so it doesn't flash open while the rename is saved. If the
rename fails, that move is now undone, so the folder stays collapsed
under its old name and the new name isn't left marked collapsed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@clanky-the-clanker clanky-the-clanker Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Approved — The earlier INFO about a failed rename losing the collapsed state is fixed and tested. No new issues were found in the changed code.

The decision is advisory: this review is a comment and never approves, blocks or requests changes on the pull request.


✅ All checks passed

🔒 Security: ✅ LGTM

UseOverseerInterface denies setChatPlacement and renameChatFolder, just as it denies setChatTitle. assertFolderName rejects blank names. The rollback in the latest commit only changes this user's own view state in localStorage.


📖 AGENTS.md: ✅ LGTM

The change follows the repository guidelines, and the kernel and API change is in its own commit, apart from the UI.


📋 Code Quality: ✅ LGTM

The earlier note is fixed. In useChatOrganizer.ts, finishNaming moves a folder's collapsed state to its new name as the rename starts. If renameChatFolder fails, it moves the state back to the old name and rethrows, and save shows the error toast. The new test covers this: after a rejected rename, the folder is still collapsed under its old name.


⚡ Performance: ✅ LGTM

No performance issues.


📝 Documentation: ✅ LGTM

The new Overseer methods and the ChatPlacement type have doc comments.


Reviewed commit: 5005edcf

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown

LGTM!

github run

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Eval results

Verdict: ⚪ Unchanged. No task moved beyond what these runs can tell apart from noise.

Task Score Δ score Fisher test Cache hits Avg min Avg steps
change-calendar (gpt-6-luna) 100% 0 pp p = 1.00 95%
0 pp
2.8 → 2.5 24.9 → 24.4
chat-views (gpt-6-luna) 100% 0 pp p = 1.00 94%
0 pp
1.7 12.0 → 11.5
chess (gpt-6-luna) 50% → 70% +20 pp p = 0.65 97%
0 pp
3.6 → 3.9 27.2 → 35.1
compaction (claude-sonnet-5-5) 100% 0 pp p = 1.00 90% → 89%
−1 pp
1.7 → 2.0 43.0
compaction (gpt-6-luna) 100% 0 pp p = 1.00 92%
0 pp
1.8 → 1.7 43.0
incident-desk (gpt-6-luna) 90% → 80% −10 pp p = 1.00 96%
0 pp
3.0 → 2.5 28.1 → 22.8
ticket-triage-agent (gpt-6-luna) 100% 0 pp p = 1.00 97%
0 pp
0.2 → 0.1 2.2 → 2.1
worker-logs (gpt-6-luna) 90% → 100% +10 pp p = 1.00 95%
0 pp
3.1 → 2.8 21.9 → 23.1
Failed checks
Task Check Failed
chess (gpt-6-luna) t1 agrees-with-the-oracle-through-random-games 5/10 → 1/10
chess (gpt-6-luna) t1 agrees-with-the-oracle-on-the-hard-positions 4/10 → 1/10
chess (gpt-6-luna) t1 agrees-with-the-oracle-on-perft-positions 4/10 → 1/10
chess (gpt-6-luna) t1 the-game-is-shared-across-connections 3/10 → 0/10
chess (gpt-6-luna) t1 starts-from-the-standard-position 2/10 → 0/10
chess (gpt-6-luna) t2 exports-pgn-the-oracle-replays-to-the-same-position 0/5 → 2/9
chess (gpt-6-luna) t2 imports-known-games-to-the-right-positions 0/5 → 1/9
chess (gpt-6-luna) t2 refuses-invalid-pgn-without-changing-the-game 0/5 → 1/9
incident-desk (gpt-6-luna) t1 opens-acknowledges-and-resolves-in-order 1/10 → 2/10
incident-desk (gpt-6-luna) t1 simultaneous-acknowledges-yield-exactly-one-owner 1/10
incident-desk (gpt-6-luna) t1 simultaneous-opens-of-one-id-admit-exactly-one 1/10
incident-desk (gpt-6-luna) t1 board-lists-by-severity-then-age 1/10 → 0/10
worker-logs (gpt-6-luna) t1 ingests-resets-and-summarises-per-worker 1/10 → 0/10
worker-logs (gpt-6-luna) t1 hourly-buckets-cover-every-hour-including-empty-ones 1/10 → 0/10
worker-logs (gpt-6-luna) t1 ranges-are-half-open-and-filter-by-worker 1/10 → 0/10

Run · trajectories and raw results

@ask-bonk

ask-bonk Bot commented Nov 9, 2026

Copy link
Copy Markdown

🔬 Eval runs review

Performance

The comparison is unchanged: chess improved from 5/10 to 7/10 passes, incident-desk fell from 9/10 to 8/10, worker-logs improved from 9/10 to 10/10, and the remaining task/model rows passed every run; none moved beyond noise. Neither prompt-cache hit nor break rates changed significantly, and costs were mixed: mean chess cost rose $0.0178 → $0.0223 while incident-desk fell $0.0170 → $0.0135; the single Sonnet compaction run remained the largest bill at $1.4572 → $1.5129. Chess lost the most candidate runs (3/10) and increased from 27.2 to 35.1 steps and 3.6 to 3.9 minutes, partly because nine rather than five runs reached PGN, with repeated failed edits adding avoidable work.

⚪ VERDICT: NO REGRESSION FROM THIS PR

The diff adds conversation organization and placement RPCs, leaving agent prompts, tools and eval checks unchanged, and the trajectories show generated-code mistakes rather than failures attributable to those additions.

Triage

Failure modes

  • Broken PGN import/export · chess 2/10 · model error · this PR: no — Turn 2 editFile(server.js) in trial 4 declares const s and then reassigns it in loadPgn, throwing on imports; trial 8’s pgnText omits the separator between White’s and Black’s moves, exporting invalid tokens such as e4e5. Both finish with success replies without executing a PGN round-trip test.
  • False checks remove legal chess moves · chess 1/10 · model error · this PR: no — Turn 1 writeFile(server.js) in trial 6 includes bishops in orthogonal attack rays (qrb). Subsequent executeCode calls uncover and fix a sliding-king bug, but leave this attack bug, which incorrectly excludes legal moves after promotion and in random games.
  • Resolved incidents treated as merely acknowledged · incident-desk 1/10 · model error · this PR: no — Turn 1 writeFile(server.js) in trial 8 maps every non-open incident to ALREADY_ACKNOWLEDGED. Acknowledging a resolved incident therefore returns owner alice, contrary to the prompt’s ALREADY_RESOLVED with a null owner.
  • Successful inserts reported as duplicates · incident-desk 1/10 · model error · this PR: no — Turn 1 writeFile(server.js) in trial 10 assumes an insert succeeds only when rowsWritten === 1. Checks receive DUPLICATE_ID for fresh IDs even though the records were inserted, so no simultaneous-open attempt reports success; the agent finishes without exercising the RPC. Main also has a SQL-API mistake, using .one() for a lookup that can return no rows, but neither implementation follows from this diff.

Tool errors

  • editFile: No matching text was found in the file. · change-calendar 1 → 0, chess 3 → 11, incident-desk 1 → 2, worker-logs 3 → 2 · model error — Agents quote mismatched whitespace, escaping or already-replaced text. Candidate chess trial 1, turn 2 repeats the same unsuccessful edit four times before grep reveals the extra space in its search text.
  • editFile: Validation failed · change-calendar 2 → 1, chess 1 → 0, incident-desk 6 → 2, worker-logs 3 → 0 · model error — Calls omit required fields such as filename or replacement; main’s change-calendar trial 7 twice sends .replace instead of replacement. Corrected arguments recover.
  • editFile: Multiple matches were found. The text to match must be unique. · chess 2 → 0, incident-desk 2 → 0 · model error — Main attempts replacements using repeated fragments, such as await this.getPosition() in chess trial 6. Reading or searching and adding surrounding context resolves the ambiguity.
  • readFile: File does not exist. · incident-desk 0 → 2 · model error — Candidate trial 7 reads server.js and client.js immediately after creating an empty gadget, despite the system prompt explaining that new gadgets have no files. It then creates both files and passes.
  • readFile: startLine 180 is past the end of the file, which has 160 lines. · chess 0 → 1 · model error — Candidate trial 10, turn 2 guesses a window beyond EOF after adding PGN helpers, then successfully retries from line 120.

What to do

  • Optional follow-up in packages/workshop-backend/src/agent.ts: add guidance to exercise changed RPCs with executeCode before claiming completion, including import/export round trips and refusal cases, then clean up test data.
  • Optional follow-up in the same file’s EDIT_FILE_TOOL_DESCRIPTION: add “After a no-match error, grep or re-read the current text before retrying; do not repeat an unchanged failed edit.” No pin/folder changes are indicated by these runs.

github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kernel Changes to the Workshop kernel workshop/frontend Changes to the Workshop frontend workshop/shared Changes to shared Workshop APIs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant