Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ea34e58
Reuse observed Goal narrative in progressive first reads
loopx-agent Nov 7, 2026
e9e7f48
refactor(feedback): load retained-state adapters on reward use
cocolord Nov 8, 2026
8f3cc15
docs(rfc): record feedback adapter loading correctness
cocolord Nov 8, 2026
6d1b388
docs(rfc): qualify feedback successor after transaction merge
cocolord Nov 8, 2026
b0c42af
refactor(kernel): decouple declaration codecs from lease runtime
cocolord Nov 8, 2026
131bafa
docs(rfc): qualify lexical scope loading and backend parity
cocolord Nov 8, 2026
3b27598
test(browser): anchor the conversation quiet-time clock
cocolord Nov 8, 2026
57b783e
style(kernel): remove the trailing blank codec line
cocolord Nov 8, 2026
928a11e
Merge current main for combined small-kernel validation
cocolord Nov 8, 2026
40b8e5e
Merge feedback loading fix into small-kernel proposal
cocolord Nov 8, 2026
f90a5f2
Reconcile clock component with current main implementation
cocolord Nov 8, 2026
8ff7ab3
fix(kernel): preserve property paths for scalar staged reads
cocolord Nov 8, 2026
b26cf57
docs(rfc): record combined loading and real-path qualification
cocolord Nov 8, 2026
7858d96
Load report construction providers only when consumed
cocolord Nov 8, 2026
532ecfd
Record report composition isolation and qualification limits
cocolord Nov 8, 2026
92a5f22
fix(workspace): read complete evidence from original Task sources
loopx-agent Nov 8, 2026
0cbc922
Merge main and retain Goal reuse in the shared context reader
loopx-agent Nov 8, 2026
c63a8f9
Merge pull request #8361 from loopx-project/codex/product-engineer-20…
loopx-agent Nov 8, 2026
a2341e4
Merge main after original Task evidence readback update
loopx-agent Nov 8, 2026
a4ce613
Read synthetic delegation Host input as UTF-8
cocolord Nov 8, 2026
fe36f31
Merge pull request #8323 from loopx-project/codex/progressive-goal-na…
loopx-agent Nov 8, 2026
107c4fa
Record synthetic Host UTF-8 recovery qualification
cocolord Nov 8, 2026
0119b66
Integrate original report loading proposal with current main
cocolord Nov 8, 2026
9f2df5c
Integrate reviewed scope codec and feedback loading repairs
cocolord Nov 8, 2026
251d7ea
Integrate synthetic Host UTF-8 recovery repair
cocolord Nov 8, 2026
d643f2a
docs(rfc): qualify integrated loading and recovery paths
cocolord Nov 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/presentation/dashboard/src/data/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ export async function readTodoRequest(goalId: string, todoId: string, signal: Ab
const result = z.object({
ok: z.literal(true), goal_id: z.string(), todo_id: z.string(), text: z.string(),
status: z.string(), archive_state: z.string(), updated_at: z.string().nullable(),
evidence: z.string().nullable().optional(), note: z.string().nullable().optional(),
review_basis: todoUpdateSourceBasisSchema.optional(),
source_access: z.object({namespace: z.string()}).optional(),
}).parse(await requestJson<unknown>(url, {signal}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import type { LarkGoalConnection } from "../../data/chat";
import { localizedGoalState, localizedSessionStatus, useWorkspaceI18n } from "./i18n";
import { formatCostUsd, formatDurationMs, formatTokenCount, formatUsageValue, hasGoalUsage } from "./personal-workspace-model";
import { TeamPlanResult } from "./team-plan-result";
import { TaskRequest } from "./task-request";
import { TaskRequest, taskRequestIdentity, type TaskRequestRead } from "./task-request";
import { parseTodoResumeCondition } from "./todo-resume-condition";
import { MarkdownText } from "./markdown";
import { formatMonitorDate } from "./monitor-readback";
Expand Down Expand Up @@ -145,6 +145,7 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
const { locale, t } = useWorkspaceI18n();
const [correction, setCorrection] = useState("");
const [diagnosticsOpen, setDiagnosticsOpen] = useState(false);
const [taskRequestRead, setTaskRequestRead] = useState<TaskRequestRead | null>(null);
const [repositoryCopyState, setRepositoryCopyState] = useState<"idle" | "copied" | "error">("idle");
const [runDrawerTab, setRunDrawerTab] = useState<"record" | "details">("record");
const [runActions, setRunActions] = useState<Record<string, RunActionStates>>({});
Expand Down Expand Up @@ -355,6 +356,15 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
: selection.item.goalId;
const taskNamespace = goals.find(goal => goal.goalId === goalId)?.sourceAccess?.namespace;
const taskReadable = !readOnly || Boolean(taskNamespace);
const taskDetail = selection.kind === "todo" && taskRequestRead?.phase === "ready"
&& taskRequestRead.identity === taskRequestIdentity(selection.item, taskReadable, taskNamespace)
? taskRequestRead.detail : null;
// Missing fields retain compatibility with older/local request-only readers;
// an explicit null from the current source clears an outdated display hint.
const taskEvidence = taskDetail && "evidence" in taskDetail ? taskDetail.evidence
: selection.kind === "todo" ? selection.item.evidence : undefined;
const taskNote = taskDetail && "note" in taskDetail ? taskDetail.note
: selection.kind === "todo" ? selection.item.note : undefined;
const contextLabel = selection.kind === "attention" ? selection.item.goalTitle ?? t("drawer.currentGoal")
: selection.kind === "todo" ? selection.item.goalTitle
: selection.kind === "run" ? selection.item.goalTitle
Expand Down Expand Up @@ -684,7 +694,7 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
) : selection.item.detailMode ? (
<section className="personal-task-inspector-summary">
<p className="personal-proposal-explainer">{t(selection.item.detailMode === "missing_current" ? "drawer.taskReadbackUnavailable" : "drawer.taskRequestOnly")}</p>
<TaskRequest todo={selection.item} local={taskReadable} sourceNamespace={taskNamespace} />
<TaskRequest todo={selection.item} local={taskReadable} sourceNamespace={taskNamespace} onRead={setTaskRequestRead} />
</section>
) : (
<>
Expand All @@ -697,6 +707,7 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
<span>{selection.item.taskClass === "advancement_task" ? t("drawer.taskAdvancement") : selection.item.taskClass ?? t("drawer.taskOrdinary")}</span>
</div>
<TaskRequest key={`${taskNamespace}:${selection.item.goalId}:${selection.item.todoId}`} todo={selection.item} local={taskReadable} sourceNamespace={taskNamespace}
onRead={setTaskRequestRead}
onSave={!readOnly && !todoCompleted ? callbacks.onSaveTodoRequirements : undefined} />
{selection.item.status === "blocked" ? <section aria-label={t("drawer.blockedReason")} className="personal-task-blocked-reason" data-recorded={selection.item.blockedReason ? "true" : "false"}>
<h4><AlertCircle size={14} />{t("drawer.blockedReason")}</h4>
Expand Down Expand Up @@ -788,13 +799,13 @@ export function ContextDrawer({ agents, attentionHistory = [], onSelectAttention
<small>{t("drawer.taskCompletionProofHint")}</small></label>
</details>}
</div> : null}
{selection.item.evidence ? <section className="personal-detail-card" aria-label={t("drawer.evidence")}>
{taskEvidence ? <section className="personal-detail-card" aria-label={t("drawer.evidence")}>
<h4>{t("drawer.evidence")}</h4>
<MarkdownText text={selection.item.evidence} />
<MarkdownText text={taskEvidence} />
</section> : null}
{selection.item.note ? <details className="personal-detail-card" aria-label={t("drawer.notes")}>
{taskNote ? <details className="personal-detail-card" aria-label={t("drawer.notes")}>
<summary>{t("drawer.notes")}</summary>
<MarkdownText text={selection.item.note} />
<MarkdownText text={taskNote} />
</details> : null}
{todoCompleted ? <div className="personal-task-completed-note"><Check size={16} /><span><strong>{t("drawer.taskCompletedTitle")}</strong><small>{t("drawer.taskCompletedNote")}</small></span></div> : null}
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useEffect, useId, useRef, useState} from "react";
import {useCallback, useEffect, useId, useRef, useState} from "react";
import {compactPythonWhitespace, compactPythonTextLength} from "../../../../../../loopx/control_plane/coordination/python_whitespace.ts";
import {readTodoRequest} from "../../data/chat";
import type {TodoUpdateSourceBasis} from "../../data/action-source-basis";
Expand All @@ -7,18 +7,25 @@ import {MarkdownText} from "./markdown";
import type {PersonalWorkspaceCallbacks, WorkspaceActionPreview, WorkspaceTodo} from "./personal-workspace-model";

// Per-open read/draft state, never a second Task or execution owner.
type RequestRead = {identity: string} & (
export type TaskRequestRead = {identity: string} & (
| {phase: "loading" | "error"; detail?: never}
| {phase: "ready"; detail: Awaited<ReturnType<typeof readTodoRequest>>}
);

export function TaskRequest({todo, local, sourceNamespace, onSave}: {todo: WorkspaceTodo; local: boolean; sourceNamespace?: string;
export function taskRequestIdentity(todo: WorkspaceTodo, local: boolean, sourceNamespace?: string) {
return JSON.stringify([local, sourceNamespace, todo.goalId, todo.sourceTodoId, todo.requestText,
todo.status, todo.evidence, todo.note]);
}

export function TaskRequest({todo, local, sourceNamespace, onSave, onRead}: {todo: WorkspaceTodo; local: boolean; sourceNamespace?: string;
onRead?: (read: TaskRequestRead) => void;
onSave?: PersonalWorkspaceCallbacks["onSaveTodoRequirements"]}) {
const {locale, t} = useWorkspaceI18n();
const countId = useId();
const sourceId = todo.sourceTodoId;
const identity = JSON.stringify([local, sourceNamespace, todo.goalId, sourceId, todo.requestText, todo.status]);
const [read, setRead] = useState<RequestRead | null>(null);
const identity = taskRequestIdentity(todo, local, sourceNamespace);
const [read, setRead] = useState<TaskRequestRead | null>(null);
const publishRead = useCallback((value: TaskRequestRead) => {setRead(value); onRead?.(value);}, [onRead]);
const [retry, setRetry] = useState(0);
const [draft, setDraft] = useState<{text: string; basis: TodoUpdateSourceBasis} | null>(null);
const [pending, setPending] = useState<WorkspaceActionPreview | null>(null);
Expand All @@ -32,10 +39,10 @@ export function TaskRequest({todo, local, sourceNamespace, onSave}: {todo: Works
useEffect(() => {
if (!local || !sourceId) return;
const controller = new AbortController();
setRead({identity, phase: "loading"});
publishRead({identity, phase: "loading"});
void readTodoRequest(todo.goalId, sourceId, controller.signal, sourceNamespace).then(detail => {
if (controller.signal.aborted) return;
setRead({identity, phase: "ready", detail});
publishRead({identity, phase: "ready", detail});
if (rebaseRef.current && detail.review_basis) {
const basis = detail.review_basis;
setDraft(previous => previous ? {...previous, basis} : null);
Expand All @@ -44,10 +51,10 @@ export function TaskRequest({todo, local, sourceNamespace, onSave}: {todo: Works
}
rebaseRef.current = false;
}).catch(() => {
if (!controller.signal.aborted) setRead({identity, phase: "error"});
if (!controller.signal.aborted) publishRead({identity, phase: "error"});
});
return () => controller.abort();
}, [identity, local, retry, todo.goalId, sourceId, sourceNamespace]);
}, [identity, local, retry, todo.goalId, sourceId, sourceNamespace, publishRead]);
const current = local && read?.identity === identity ? read : null;
const detail = current?.phase === "ready" ? current.detail : null;
const editable = local && onSave && todo.claimedBy && sourceId && detail?.review_basis
Expand All @@ -68,7 +75,7 @@ export function TaskRequest({todo, local, sourceNamespace, onSave}: {todo: Works
}
const observed = await readTodoRequest(todo.goalId, sourceId, new AbortController().signal, sourceNamespace);
if (!mounted.current) return;
setRead({identity, phase: "ready", detail: observed});
publishRead({identity, phase: "ready", detail: observed});
if (observed.text !== text) throw new Error(t("drawer.requirementsReadbackChanged"));
setDraft(null); setPending(null); setSaved(true);
} catch (error) {
Expand Down
Loading
Loading