Activity dock: unique row ids for per-chunk extraction jobs (fix duplicate React keys)
The live import surfaced it: a multi-chunk notes source renders N feed rows, and the dock keyed every one by run.chatId (the SOURCE id) — React's duplicate-key error. The activity feed also dropped run.part entirely, so all chunk rows showed identical titles. - ActivityExtraction/FeedExtraction gain a unique row id (source id, or 'src#i' for chunk jobs) and part; the feed title carries the '· i/N' suffix like the graph status route - toBlobs keys extraction blobs by the row id; chatId stays the SOURCE id (identity for links) - abort sends the SOURCE id for extractions — aborting any chunk row kills the source's whole fan-out (the new source-wide abort), and the abort route's id charset rejects '#' anyway - new pin: one source, two chunk rows → two blobs, distinct ids, same chatId; dock fixtures updated
This commit is contained in:
@@ -54,7 +54,10 @@ export interface FeedJob extends JobRecord {
|
||||
}
|
||||
|
||||
export interface FeedExtraction {
|
||||
/** Unique row id (source id, or "src#i" for chunk jobs). */
|
||||
id: string;
|
||||
chatId: string;
|
||||
part?: { i: number; n: number };
|
||||
title: string;
|
||||
phase: string;
|
||||
startedAt: number;
|
||||
@@ -145,7 +148,8 @@ export function useActivity(): { feed: ActivityFeed; refresh: () => void } {
|
||||
|
||||
/** One dock blob: the unified view of a job or an extraction run. */
|
||||
export interface ActivityBlob {
|
||||
/** job.id, or the extraction's chatId (one run per chat). */
|
||||
/** job.id, or the extraction job's row id (source id, or "src#i" for
|
||||
* chunk jobs — unique per row, per-chunk jobs owner 2026-09-28). */
|
||||
id: string;
|
||||
process: "job" | "extraction";
|
||||
/** Sub-agent kind for jobs (extraction blobs have none). */
|
||||
@@ -219,7 +223,7 @@ export function toBlobs(feed: ActivityFeed): ActivityBlob[] {
|
||||
});
|
||||
for (const run of feed.extractions) {
|
||||
blobs.push({
|
||||
id: run.chatId,
|
||||
id: run.id,
|
||||
process: "extraction",
|
||||
phase: run.phase,
|
||||
title: run.title,
|
||||
@@ -292,7 +296,10 @@ export function ActivitySection({ feed: feedProp }: { feed?: ActivityFeed }) {
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
kind: blob.process === "job" ? "job" : "extraction",
|
||||
id: blob.id,
|
||||
// Extractions abort by SOURCE id (chatId) — chunk rows
|
||||
// share one source and die together (the route's id
|
||||
// charset rejects the "#" of a "src#i" job key anyway).
|
||||
id: blob.process === "job" ? blob.id : blob.chatId,
|
||||
}),
|
||||
});
|
||||
if (!res.ok) {
|
||||
|
||||
Reference in New Issue
Block a user