docs(plan): v0.2 adds Pi subagents and scheduled-jobs board

Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
rootandClaude Fable 5.1 committed 2026-09-28 02:09:39 +08:00
1 parent b839ed4056
commit 16ab2a6c84
97 files changed
+592 -648

No files matched your search

@@ -25,7 +25,7 @@ export const TASK_KIND_LABEL: Record<TaskKind, string> = {
refreshMetadata: "刷新元数据",
reconcileLibrary: "资产对账",
cleanupDownloadCache: "清理缓存",
resolveDecision: "处理待办",
resolveDecision: "处理待处理事项",
agentRun: "Agent 运行",
};
@@ -76,7 +76,7 @@ export function BusinessTasks() {
body: op === "cancel" ? { reason: "控制台取消" } : {},
}).then((d) => Task.parse(d)),
onSuccess: (_t, v) => {
toast.success(v.op === "cancel" ? "已请求取消(只停后续步骤)" : "已重新排队");
toast.success(v.op === "cancel" ? "已请求取消,后续步骤不再执行" : "已重新排队");
void qc.invalidateQueries({ queryKey: ["tasks"] });
},
onError: (e) => toast.error(errorMessage(e)),
@@ -218,7 +218,7 @@ export function BusinessTasks() {
empty={{
icon: ListTodo,
title: kind || status ? "没有符合筛选的任务" : "还没有业务任务",
description: "提交下载、核验入库、缓存清理、对账等后台工作都会记在这里。",
description: "提交下载、核验入库、清理缓存、对账等后台任务会出现在这里。",
}}
/>
</>
@@ -61,7 +61,7 @@ export function BusinessTimeline({ events, loading }: { events: TimelineEvent[];
/>
)}
{loading && events.length ? (
<p className="text-meta text-muted-foreground">正在补全关联的整理记录与引擎 run…</p>
<p className="text-meta text-muted-foreground">正在加载关联的整理记录与引擎运行…</p>
) : null}
</div>
);
@@ -63,8 +63,8 @@ function VolumeBar({ v }: { v: Volume }) {
<span>保留锁定 {formatBytes(v.retentionLockedBytes, 1)}</span>
</div>
<div className="mt-1.5 flex flex-wrap gap-1.5">
{pct === null ? <StatusBadge tone="warning">读不到磁盘状态:已保守阻止新提交</StatusBadge> : null}
{tripped ? <StatusBadge tone="destructive">已达硬阈值:停止新提交</StatusBadge> : null}
{pct === null ? <StatusBadge tone="warning">读不到磁盘状态,已暂停新提交</StatusBadge> : null}
{tripped ? <StatusBadge tone="destructive">已达硬阈值,停止新提交</StatusBadge> : null}
{v.pausedByThreshold ? <StatusBadge tone="warning">阈值暂停 {v.pausedByThreshold} 个任务</StatusBadge> : null}
</div>
{v.notes.map((n) => (
@@ -93,12 +93,12 @@ export function CacheVolumes() {
</Button>
}
title="按规则清理下载缓存?"
description="只清理已核验入库、且满足保留期与 H&R 的纳管任务;未纳管任务与未满足保留的不会动。"
description="只清理已入库、且满足保留期与 H&R 的纳管任务,其余不动。"
confirmLabel="清理"
onConfirm={() =>
cleanup.mutateAsync(undefined).then(
(r) => {
toast.success(`已排队清理任务 ${r.taskId.slice(0, 8)}`, { description: "后台执行,结果见任务详情" });
toast.success(`已排队清理任务 ${r.taskId.slice(0, 8)}`, { description: "结果见任务详情" });
navigate(`/console/engine/${r.runId}`);
},
(e) => toast.error(errorMessage(e)),
@@ -111,7 +111,7 @@ export function CacheVolumes() {
) : vols.error ? (
<ErrorState plain error={vols.error} onRetry={() => void vols.refetch()} />
) : !vols.data.length ? (
<p className="text-caption text-muted-foreground">还没有下载器缓存卷的统计(未配置下载器或尚未巡检)。</p>
<p className="text-caption text-muted-foreground">还没有缓存卷数据。配置下载器并完成巡检后显示。</p>
) : (
<div className="grid gap-5 md:grid-cols-2">
{vols.data.map((v) => (
@@ -66,14 +66,14 @@ export function PauseResume({ t, size = "sm" }: { t: DownloadTask; size?: "sm" |
variant="outline"
size={size}
disabled={act.isPending}
title={threshold ? "阈值暂停:缓存回落到恢复水位后由系统自动恢复" : undefined}
title={threshold ? "阈值暂停,缓存回落到恢复水位后自动恢复" : undefined}
onKeyDown={(e) => e.stopPropagation()}
onClick={(e) => {
e.stopPropagation();
act.mutate(
{ id: t.id, action: canResume ? "resume" : "pause" },
{
onSuccess: () => toast.success(canResume ? "已恢复" : "已暂停(记为用户暂停,阈值恢复不会自动恢复它)"),
onSuccess: () => toast.success(canResume ? "已恢复" : "已暂停。阈值回落时不会自动恢复"),
onError: (err) => toast.error(errorMessage(err)),
},
);
@@ -128,13 +128,12 @@ export function DownloadDetail({ task }: { task: DownloadTask }) {
{task.pauseReason === "threshold" ? (
<p className="flex items-start gap-2 rounded-lg border border-warning/40 bg-warning/10 px-3 py-2 text-caption text-warning">
<ShieldAlert className="mt-0.5 h-3.5 w-3.5 shrink-0" />
缓存卷达到硬阈值,系统暂停了这个纳管任务。水位回落到恢复线后自动恢复;手动恢复、优先级或 Agent
推荐都不能绕过阈值。
缓存卷达到硬阈值,任务已暂停,水位回落到恢复线后自动恢复。手动恢复和 Agent 都不能绕过阈值。
</p>
) : null}
{task.noProgressSince ? (
<p className="rounded-lg border border-warning/40 bg-warning/10 px-3 py-2 text-caption text-warning">
自 {formatDateTime(task.noProgressSince)} 起无进展。系统只记录证据并汇入待处理,不会自动换源。
自 {formatDateTime(task.noProgressSince)} 起无进展,已记入待处理,不会自动换源。
</p>
) : null}
@@ -44,9 +44,7 @@ function PlanPreview({ planId }: { planId: string }) {
return (
<p className="rounded-lg border border-border px-3 py-2 text-caption text-muted-foreground">
计划已生成(planId {planId.slice(0, 8)}…),
{isMissingEndpoint(art.error)
? "计划内容不存在或已过期,请重新预览。"
: `读取计划失败:${errorMessage(art.error)}`}
{isMissingEndpoint(art.error) ? "计划已过期,重新预览。" : `读取计划失败:${errorMessage(art.error)}`}
</p>
);
}
@@ -65,7 +63,7 @@ function PlanPreview({ planId }: { planId: string }) {
<div className="space-y-2">
<div className="flex flex-wrap gap-1.5">
<StatusBadge>{org.operation}</StatusBadge>
{org.crossDevice ? <StatusBadge tone="warning">跨卷:Copy → 核验 → 删源</StatusBadge> : null}
{org.crossDevice ? <StatusBadge tone="warning">跨卷:复制 → 核验 → 删源</StatusBadge> : null}
{org.requiresApproval ? <StatusBadge tone="warning">需要审批</StatusBadge> : null}
{blocking.length ? <StatusBadge tone="destructive">阻塞冲突 {blocking.length}</StatusBadge> : null}
</div>
@@ -155,7 +153,7 @@ export function OrganizeDialog({
const pathErr = Object.entries(e).find(([k]) => k.startsWith("sourcePaths"));
setErrors({
...e,
paths: pathErr ? "每行一个以 / 开头的绝对路径" : e[""] ? "填写源路径(每行一个绝对路径)" : "",
paths: pathErr ? "每行一个以 / 开头的绝对路径" : e[""] ? "填写源路径" : "",
});
return;
}
@@ -165,8 +163,8 @@ export function OrganizeDialog({
if (dryRun) {
setPlanId(r.planId);
if (r.truncated)
toast.warning("源目录文件数超过上限,预览只含前一部分文件", {
description: "正式执行会被拒绝,请把源路径缩小到具体的作品目录",
toast.warning("源目录文件过多,预览只含前一部分", {
description: "执行会被拒绝。把源路径缩小到具体的作品目录",
});
} else {
toast.success("已发起整理");
@@ -183,9 +181,7 @@ export function OrganizeDialog({
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-xl">
<DialogHeader>
<DialogTitle>手动整理</DialogTitle>
<DialogDescription>
先预览计划(不动文件),确认后再执行。Move 跨卷时按 Copy → 核验 → 删源执行。
</DialogDescription>
<DialogDescription>先预览计划(不动文件),确认后执行。跨卷移动按复制 → 核验 → 删源执行。</DialogDescription>
</DialogHeader>
<div className="grid gap-1">
{downloadTaskId ? (
@@ -266,7 +262,7 @@ export function OrganizeDialog({
<FormField
label="动作"
htmlFor="org-action"
hint={action === "move" ? "Move 需要对应授权" : "保留源文件(做种不受影响)"}
hint={action === "move" ? "移动需要对应授权" : "保留源文件,不影响做种"}
>
<Select
value={action}
@@ -279,8 +275,8 @@ export function OrganizeDialog({
<SelectValue />
</SelectTrigger>
<SelectContent position="popper">
<SelectItem value="copy">Copy</SelectItem>
<SelectItem value="move">Move</SelectItem>
<SelectItem value="copy">复制</SelectItem>
<SelectItem value="move">移动</SelectItem>
</SelectContent>
</Select>
</FormField>