@teamscala/service-boot 0.23.290 → 0.23.292
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -501,10 +501,31 @@ async function reconcileTaskList(
|
|
|
501
501
|
laneSession: string,
|
|
502
502
|
list: TaskListItem[],
|
|
503
503
|
source?: string,
|
|
504
|
+
pane?: string | null,
|
|
504
505
|
): Promise<{ task_id: string; created: number; updated: number; deleted: number; total: number }> {
|
|
505
|
-
|
|
506
|
+
// BIND ON DEMAND — this path used to `resolveLaneTask` and THROW, which is the
|
|
507
|
+
// exact failure `ensureLaneTask` was written to make impossible (see its
|
|
508
|
+
// comment above, and the operator ruling it cites: "ALL lanes can comply,
|
|
509
|
+
// there is no reason why a tab cant comply"). The backstop existed and this
|
|
510
|
+
// caller did not use it, so a lane whose SessionStart bindLane never ran — or
|
|
511
|
+
// ran while the endpoint was failing — could never sync a task list again.
|
|
512
|
+
// Not degraded: permanently, with a 404 on every attempt and no path to
|
|
513
|
+
// self-recover.
|
|
514
|
+
//
|
|
515
|
+
// Measured 2026-07-30: a live lane reported ~9 consecutive failures over
|
|
516
|
+
// hours and correctly diagnosed its own lane-compliance flag as a false
|
|
517
|
+
// positive it was structurally unable to clear. The gate was telling it to
|
|
518
|
+
// keep a task list while the only endpoint that records one refused it.
|
|
519
|
+
//
|
|
520
|
+
// A lane calling this HAS everything the bind needs — its session, its pane,
|
|
521
|
+
// its list — so requiring a prior call is a sequencing demand with no
|
|
522
|
+
// informational content. `friction-is-a-stop-condition`: the compliant path
|
|
523
|
+
// must exist, or the rule is unenforceable against the lane it binds.
|
|
524
|
+
const task = await ensureLaneTask(prisma, laneSession, pane ?? null);
|
|
506
525
|
if (!task) {
|
|
507
|
-
throw new Error(
|
|
526
|
+
throw new Error(
|
|
527
|
+
`Could not bind a task for session ${laneSession} — the create failed and no existing task was found.`,
|
|
528
|
+
);
|
|
508
529
|
}
|
|
509
530
|
const existing = await prisma.work_items.findMany({
|
|
510
531
|
where: { parent_id: task.id, kind: "subtask" },
|
|
@@ -701,8 +722,13 @@ export const taskSyncRouter = {
|
|
|
701
722
|
const { ORPCError } = await import("@orpc/server");
|
|
702
723
|
throw new ORPCError("BAD_REQUEST", { message: "upsertFromTaskList requires lane_session" });
|
|
703
724
|
}
|
|
725
|
+
// The pane rides in the header the callers already send (`x-lane-pane`),
|
|
726
|
+
// and it is what lets the on-demand bind link the new task to the lane's
|
|
727
|
+
// run. Absent, the bind still succeeds — it just cannot link the run — so
|
|
728
|
+
// this is passed opportunistically rather than required.
|
|
729
|
+
const headerPane = (context as Context).headers?.get("x-lane-pane") ?? null;
|
|
704
730
|
try {
|
|
705
|
-
const res = await reconcileTaskList(prisma, session, list, body.source);
|
|
731
|
+
const res = await reconcileTaskList(prisma, session, list, body.source, headerPane);
|
|
706
732
|
log.info("[task-sync] upsertFromTaskList reconciled", { session, ...res });
|
|
707
733
|
return res;
|
|
708
734
|
} catch (err) {
|