Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d6a0720
docs(vocabulary): the collaboration protocol and its task-unit sub-pr…
wefio Sep 20, 2026
ebc053d
docs(vocabulary): the names get one index, with their owners
wefio Sep 20, 2026
44318cb
docs(concept-map): the Task Board row points at a heading that exists
wefio Sep 20, 2026
a16571b
docs(vocabulary): where the words came from, and one stale sentence a…
wefio Sep 20, 2026
ef33abc
docs(decisions): the program answers legality, and nothing else
wefio Sep 20, 2026
4db6140
docs(decisions): read the legality record together with board governance
wefio Sep 20, 2026
9832bf6
docs(design): inventory the umbrella - its parts, the gaps, and the d…
wefio Sep 20, 2026
d93ae39
docs(decisions): the frame, its data format, and its storage
wefio Sep 21, 2026
9e1041d
docs(decisions): answer the frame record's review - mapping, seam, tw…
wefio Sep 21, 2026
41867e5
docs(decisions): mechanism, not policy - the division both records ne…
wefio Sep 21, 2026
4cfbe11
docs(design): a layered model, with the checks that decide whether it…
wefio Sep 22, 2026
9ea3296
docs(design): fill in checks a and b, and correct the display stage t…
wefio Sep 22, 2026
d56270d
Give the board entry two faces, and the read face one home
wefio Sep 22, 2026
23d8644
The store gets its own board
wefio Sep 23, 2026
dcc611a
The current-value case was a stopwatch
wefio Sep 23, 2026
e31fe77
The type gate had ten diagnostics; it now has none
wefio Sep 23, 2026
9fb3beb
One home for the identity of work
wefio Sep 23, 2026
95fb3bc
The completeness ledger stops over-claiming in both directions
wefio Sep 23, 2026
996e339
The shared answer says why, not just which
wefio Sep 23, 2026
f11fdb4
The board's read answers why, not only which
wefio Sep 23, 2026
d7519e9
The continuation is a constraint the plan declares
wefio Sep 23, 2026
dbf10ac
Two teeth had stopped pointing at their rule
wefio Sep 23, 2026
56a4f60
Merge remote-tracking branch 'origin/main' into docs/vocabulary-task-…
wefio Sep 24, 2026
f74859e
A tooth is an operator over a selector, not a copy of a line
wefio Sep 24, 2026
fcbf87f
The anchors pass joins the static contract
wefio Sep 24, 2026
ed5b9f8
Five teeth retire where a check already states their rule
wefio Sep 24, 2026
4f50675
Six more teeth retire where one case states their rules
wefio Sep 24, 2026
fff284e
Third group: two teeth retire, and four links were wrong
wefio Sep 25, 2026
a810324
The insertion-shaped residue retires where a check states its rule
wefio Sep 25, 2026
337cc00
59 teeth become operator-and-selector, and three gaps in the vocabula…
wefio Sep 25, 2026
b515f94
Seven operators the catalogues name, and 18 more teeth converted
wefio Sep 25, 2026
7691291
Every tooth is derived now: the last 15 convert, and the register has…
wefio Sep 25, 2026
41768c8
docs(decisions): the derived-mutants record is implemented, not proposed
wefio Sep 25, 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
3 changes: 2 additions & 1 deletion .pi/extensions/nmg/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import { memoryDisclosureEntries } from "../../../src/integration/search-projection.ts";
import { PI_BOARD_ACTIONS, PI_REMEMBER_ACTIONS } from "../../../src/integration/tool-contract.ts";
import { TASK_BOARD_VERDICTS } from "../../../src/core/types.ts";
import { boardEntryView } from "../../../src/core/board-entry-view.ts";
import { resolveSkillOptPolicyChannels } from "../../../src/lab/skillopt-policy.ts";
import type {
ActiveGraphBudget,
Expand Down Expand Up @@ -263,171 +264,171 @@
}
});

pi.on("before_agent_start", async (event, ctx) => {
const sessionId = ctx.sessionManager.getSessionId();
const isNewUserTurn = recallFlow.beginTurn(
sessionId,
piUserTurnKey(ctx.sessionManager, event.prompt),
);
await beginDisclosureTurn(sessionId, isNewUserTurn);
const completionNudge = popCompletionNudge(event.prompt);
// Pi re-enters before_agent_start after tool results. A completed graph may
// already exist at that point, but it must be reviewed on the next user
// turn, not consumed by an internal tool loop from the same prompt.
const pendingFeedback = isNewUserTurn ? controllerShadow.pendingFeedback(sessionId) : null;
if (pendingFeedback) await controllerShadow.feedbackNudgeShown(sessionId, pendingFeedback);
const feedbackNudge = pendingFeedback
? renderDisclosure(nmgPrompts.shadow_feedback_nudge, {
active_graph_id: pendingFeedback.activeGraphId,
semantic_task_id: pendingFeedback.semanticTaskId,
})
: "";
const pendingClaimOutcome = isNewUserTurn
? controllerShadow.pendingClaimOutcome(sessionId)
: null;
if (pendingClaimOutcome) {
await controllerShadow.claimOutcomeNudgeShown(sessionId, pendingClaimOutcome);
}
const claimOutcomeNudge = pendingClaimOutcome
? renderDisclosure(nmgPrompts.shadow_claim_outcome_nudge, {
active_graph_id: pendingClaimOutcome.activeGraphId,
semantic_task_id: pendingClaimOutcome.semanticTaskId,
memory_ids: pendingClaimOutcome.memoryIds.join(","),
})
: "";
const nudge = [completionNudge, feedbackNudge, claimOutcomeNudge].filter(Boolean).join("\n");
let reasoningCheckpoint = "";
if (labToolsEnabled) {
try {
const status = (await invokeDaemon(await connection(), "lab", {
action: "status",
capability: "reasoning_workspace",
sessionId,
})) as { activation?: unknown };
if (status.activation) {
const consumed = (await invokeDaemon(await connection(), "lab", {
action: "invoke",
capability: "reasoning_workspace",
sessionId,
operation: "consume_checkpoint",
input: { maxNodes: 24, maxChars: 6_000 },
})) as { output?: { text?: string } | null };
reasoningCheckpoint = consumed.output?.text ?? "";
}
} catch (error) {
reasoningCheckpoint = `Reasoning workspace unavailable: ${message(error)}`;
}
}
let runtimeAgContext = "";
try {
const result = (await invoke("sessionActiveGraph", {
action: "snapshot",
sessionId,
})) as {
snapshot?:
import("../../../src/core/session-active-graph.ts").SessionActiveGraphSnapshot | null;
};
runtimeAgContext = renderSessionActiveGraphSurface(result.snapshot ?? null);
} catch {
// Search and ordinary Pi operation continue without optional working state.
}
const runtimeContext = [runtimeAgContext, reasoningCheckpoint].filter(Boolean).join("\n");
const recallRequest = taskWindow.prepare(sessionId, event.prompt);
const dynamicContext = composeNmgContextMessage("", "", nudge, runtimeContext);
if (!recallRequest) {
return {
systemPrompt: composeNmgSystemPrompt(event.systemPrompt),
...(dynamicContext
? {
message: {
customType: "nmg-context",
content: dynamicContext,
display: true,
details: { count: 0 },
},
}
: {}),
};
}
try {
let context = (await invoke("search", {
query: recallRequest.query,
projectDir: projectDirectory(),
sessionId,
maxTier: Math.min(configuredAutoRecallTier(), recallRequest.maxTier) as MemoryTier,
limit: Math.min(configuredAutoRecallLimit(), recallRequest.limit),
initialEvidenceTarget: configuredInitialTarget(),
strongHitTopGap: configuredStrongHitTopGap(),
strongHitInitialTarget: configuredStrongHitInitialTarget(),
secondPass: qpp2Mode === "active",
graphHops: Math.min(1, recallRequest.graphHops),
tieredDisclosure: true,
// Tell the daemon this is an automatic recall decision: it stages the
// injected graph for online learning (explicit nmg_search stays unstaged).
autoRecall: true,
})) as MemoryContext;
if (controllerRerankMode === "active") {
context = await controllerShadow.rerank(context);
}
const fullContext = context;
if (qpp2Mode === "active") {
context = await applyLearnedFold(context, controllerShadow, qpp2RetainedMass, false);
}
const recalled = await formatDisclosedContext(sessionId, context, "header");
await controllerShadow.retrieval(fullContext, sessionId, "automatic", recalled);
// Automatic recall is still a retrieval trace. Keep it in the per-turn
// attribution window so agent_end can distinguish surfaced evidence from
// candidates that were merely injected.
agentAttributionFlow.note(sessionId, fullContext);
const recordCount = (recalled.match(/memory=/g) ?? []).length;
const searchNudge = formatSearchRecommendation(context, recommendationMode);
const recallContext = composeNmgContextMessage(
recalled,
"",
[
nudge,
searchNudge,
recordCount > 0 ? recallFeedbackAffordance(fullContext.activeGraph?.id) : "",
]
.filter(Boolean)
.join("\n"),
runtimeContext,
);
return {
systemPrompt: composeNmgSystemPrompt(event.systemPrompt),
...(recallContext
? {
message: {
customType: "nmg-context",
content: recallContext,
display: true,
details: { count: recordCount },
},
}
: {}),
};
} catch (error) {
const errorContext = composeNmgContextMessage(
"",
`NMG unavailable: ${message(error)}`,
nudge,
runtimeContext,
);
return {
systemPrompt: composeNmgSystemPrompt(event.systemPrompt),
...(errorContext
? {
message: {
customType: "nmg-context",
content: errorContext,
display: true,
details: { count: 0 },
},
}
: {}),
};
}
});

Check notice on line 431 in .pi/extensions/nmg/index.ts

View check run for this annotation

codefactor.io / CodeFactor

.pi/extensions/nmg/index.ts#L267-L431

Complex Method

// TUI 折叠开关:nmg-context 默认折叠为一个 [nmg-context] chip,避免每轮刷屏。
// /nmg-recall 切换展开(走 pi 默认渲染:label + 全文)。状态只影响后续渲染
Expand All @@ -448,210 +449,210 @@
// NMG 总二级/三级菜单。早期只有独立的 /nmg-recall 命令;现在统一收编到
// /nmg 下,保留 /nmg-recall 作为别名。状态只影响后续渲染与重开会话后的
// 历史恢复;已渲染的历史消息不重绘(pi 无公开 force-rerender)。
const nmgMenuHandler = async (args: string, ctx: ExtensionCommandContext): Promise<void> => {
const parts = args.trim().split(/\s+/).filter(Boolean);
const [sub, ...rest] = parts;
const restText = rest.join(" ");
switch (sub) {
case "recall": {
recallCollapsed = !recallCollapsed;
ctx.ui.notify(
recallCollapsed
? "nmg-context 已折叠(只显示 [nmg-context] chip)"
: "nmg-context 已展开(显示召回全文)",
"info",
);
return;
}
case "wake": {
const current = readWakeConfig();
const [wcmd, wvalRaw] = restText.split(/\s+/);
const wval = Number(wvalRaw);
switch (wcmd) {
case "on":
writeWakeConfig({ ...current, enabled: true });
ctx.ui.notify("黑板唤醒已开启(世界频道+活跃频道有新 open 条目会唤醒)", "info");
return;
case "off":
writeWakeConfig({ ...current, enabled: false });
ctx.ui.notify("黑板唤醒已关闭", "info");
return;
case "status": {
const budgetText = current.budget === 0 ? "不限制" : `${current.budget}/天`;
const cooldownText =
current.cooldownMs === 0 ? "无" : `${Math.round(current.cooldownMs / 60_000)} 分`;
ctx.ui.notify(
`黑板唤醒:${current.enabled ? "开" : "关"} · 预算 ${budgetText} · 冷却 ${cooldownText} · 轮询 ${Math.round(current.intervalMs / 1_000)} 秒 · 世界广播 ${current.worldBroadcast ? "开" : "关"}`,
"info",
);
return;
}
case "budget": {
if (!Number.isFinite(wval)) {
ctx.ui.notify("用法:/nmg wake budget N(0=不限制)", "warning");
return;
}
const budget = Math.max(0, Math.min(100, Math.round(wval)));
writeWakeConfig({ ...current, budget });
ctx.ui.notify(`黑板唤醒预算已设为 ${budget === 0 ? "不限制" : `${budget}/天`}`, "info");
return;
}
case "cooldown": {
if (!Number.isFinite(wval)) {
ctx.ui.notify("用法:/nmg wake cooldown M(0=无冷却)", "warning");
return;
}
const cooldownMs = wval === 0 ? 0 : Math.max(30_000, Math.round(wval * 60_000));
writeWakeConfig({ ...current, cooldownMs });
ctx.ui.notify(
`黑板唤醒冷却已设为 ${cooldownMs === 0 ? "无" : `${Math.round(cooldownMs / 60_000)} 分钟`}`,
"info",
);
return;
}
case "interval": {
if (!Number.isFinite(wval)) {
ctx.ui.notify("用法:/nmg wake interval S(秒,最小 5)", "warning");
return;
}
const intervalMs = Math.max(5_000, Math.round(wval * 1_000));
writeWakeConfig({ ...current, intervalMs });
ctx.ui.notify(`黑板唤醒轮询已设为 ${Math.round(intervalMs / 1_000)} 秒`, "info");
return;
}
case "world": {
// 无参切换;0 关、1 开。
const world =
wvalRaw === "0" ? false : wvalRaw === "1" ? true : !current.worldBroadcast;
writeWakeConfig({ ...current, worldBroadcast: world });
ctx.ui.notify(
world
? "已开启世界频道协作广播:协作类新条目会同时发到世界频道拉其他 agent"
: "已关闭世界频道协作广播",
"info",
);
return;
}
default: {
const enabled = !current.enabled;
writeWakeConfig({ ...current, enabled });
ctx.ui.notify(enabled ? "黑板唤醒已开启" : "黑板唤醒已关闭", "info");
}
}
return;
}
default: {
// 无参数 → 交互式选择菜单(可上下键选,不用手敲);未知子命令 → 总览提示。
if (parts.length === 0) {
await nmgInteractiveMenu(ctx);
return;
}
const wake = readWakeConfig();
const budgetText = wake.budget === 0 ? "不限制" : `${wake.budget}/天`;
const cooldownText =
wake.cooldownMs === 0 ? "无" : `${Math.round(wake.cooldownMs / 60_000)} 分`;
ctx.ui.notify(
`NMG 菜单:/nmg recall(召回折叠 ${recallCollapsed ? "开" : "关"}) · /nmg wake on|off|status|budget N|cooldown M|interval S(唤醒 ${wake.enabled ? "开" : "关"},预算 ${budgetText},冷却 ${cooldownText},轮询 ${Math.round(wake.intervalMs / 1_000)} 秒)`,
"info",
);
}
}
};

Check notice on line 560 in .pi/extensions/nmg/index.ts

View check run for this annotation

codefactor.io / CodeFactor

.pi/extensions/nmg/index.ts#L452-L560

Complex Method
// 交互式选择菜单:/nmg(无参数)弹出可上下键选的列表,避免手敲。带参数的
// 形式(/nmg recall、/nmg wake on 等)仍保留,供脚本/快捷路径直接调用。
const nmgInteractiveMenu = async (ctx: ExtensionCommandContext): Promise<void> => {
if (!ctx.hasUI || typeof ctx.ui.select !== "function") {
ctx.ui.notify(
"当前模式不支持交互菜单,可用:/nmg recall、/nmg wake on|off|status|budget N|cooldown M|interval S",
"warning",
);
return;
}
const choice = await ctx.ui.select(
"NMG 控制台",
nmgMenuOptions(recallCollapsed, readWakeConfig()),
);
if (!choice) return; // 用户取消
if (choice.startsWith("召回")) {
await nmgMenuHandler("recall", ctx);
return;
}
if (choice.startsWith("唤醒:开启/关闭")) {
const onOff = await ctx.ui.select("黑板唤醒", ["开启", "关闭"]);
if (!onOff) return;
const current = readWakeConfig();
writeWakeConfig({ ...current, enabled: onOff === "开启" });
ctx.ui.notify(onOff === "开启" ? "黑板唤醒已开启" : "黑板唤醒已关闭", "info");
return;
}
if (choice.startsWith("唤醒:世界广播")) {
const onOff = await ctx.ui.select("世界频道协作广播", ["开启", "关闭"]);
if (!onOff) return;
const current = readWakeConfig();
writeWakeConfig({ ...current, worldBroadcast: onOff === "开启" });
ctx.ui.notify(
onOff === "开启"
? "已开启世界频道协作广播:协作类新条目会同时发到世界频道拉其他 agent"
: "已关闭世界频道协作广播",
"info",
);
return;
}
if (choice.startsWith("唤醒:参数设置")) {
// 挡位选择(select 预设值,避免随意输入溢出/无效);自由数字只走命令形式
// (/nmg wake budget N 等,已有 clamp)。
const param = await ctx.ui.select("唤醒参数", ["每日上限", "冷却", "轮询"]);
if (!param) return;
const current = readWakeConfig();
if (param === "每日上限") {
const option = await ctx.ui.select("每日唤醒上限", ["不限制", "3/天", "8/天", "15/天"]);
if (!option) return;
const budget = option === "不限制" ? 0 : Number(option.split("/")[0]);
writeWakeConfig({ ...current, budget });
ctx.ui.notify(`黑板唤醒预算已设为 ${budget === 0 ? "不限制" : `${budget}/天`}`, "info");
} else if (param === "冷却") {
const option = await ctx.ui.select("冷却", ["无", "10 分钟", "30 分钟", "1 小时"]);
if (!option) return;
const cooldownMs =
option === "无"
? 0
: option === "10 分钟"
? 600_000
: option === "30 分钟"
? 1_800_000
: 3_600_000;
writeWakeConfig({ ...current, cooldownMs });
ctx.ui.notify(
`黑板唤醒冷却已设为 ${cooldownMs === 0 ? "无" : `${Math.round(cooldownMs / 60_000)} 分钟`}`,
"info",
);
} else if (param === "轮询") {
const option = await ctx.ui.select("轮询", ["5 秒", "30 秒", "60 秒", "5 分钟"]);
if (!option) return;
const intervalMs =
option === "5 秒"
? 5_000
: option === "30 秒"
? 30_000
: option === "60 秒"
? 60_000
: 300_000;
writeWakeConfig({ ...current, intervalMs });
ctx.ui.notify(`黑板唤醒轮询已设为 ${Math.round(intervalMs / 1_000)} 秒`, "info");
}
return;
}
{
const wake = readWakeConfig();
const budgetText = wake.budget === 0 ? "不限制" : `${wake.budget}/天`;
const cooldownText =
wake.cooldownMs === 0 ? "无" : `${Math.round(wake.cooldownMs / 60_000)} 分`;
ctx.ui.notify(
`NMG:召回折叠 ${recallCollapsed ? "开" : "关"} · 唤醒 ${wake.enabled ? "开" : "关"}(预算 ${budgetText},冷却 ${cooldownText},轮询 ${Math.round(wake.intervalMs / 1_000)} 秒)`,
"info",
);
}
};

Check notice on line 655 in .pi/extensions/nmg/index.ts

View check run for this annotation

codefactor.io / CodeFactor

.pi/extensions/nmg/index.ts#L563-L655

Complex Method
// 菜单项带当前状态,进子菜单前就看得见:召回折叠/展开、唤醒开/关、参数当前值。
// 预算/冷却支持 0(不限制/无),轮询保持最小 5 秒(0 无意义)。
const nmgMenuOptions = (
Expand Down Expand Up @@ -2998,7 +2999,7 @@
entryIds: [entry.id],
})) as { delivered: string[]; suppressed: boolean };
if (worldCheck.delivered.includes(entry.id)) return false;
const excerpt = entry.content.length > 140 ? `${entry.content.slice(0, 140)}…` : entry.content;
const excerpt = boardEntryView(entry.content, 140);
const label = kindLabel(entry.kind);
const broadcast = `[NMG board 协作广播] 频道 ${entry.taskId} 有 #${entry.id} 未认领的${label}(open):${excerpt}。有空的 agent 可用 nmg_board read taskId=${entry.taskId} 查看详情、claim 认领处理。`;
await invoke("taskBoard", {
Expand Down
4 changes: 2 additions & 2 deletions .pi/extensions/nmg/ooo-execution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import {
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
import { createHash } from "node:crypto";
import { Type } from "typebox";
import { snapshotPrompt, type SnapshotInput } from "../../../src/integration/ooo-execution.ts";
import { workDigestOf } from "../../../src/integration/work-identity.ts";
import type { FrozenPatchWork } from "../../../src/integration/ooo-patch.ts";
import {
ARTIFACT_TOOL,
Expand All @@ -36,7 +36,7 @@ import {
* that agree on the spec can still differ here, and an instrument version stops being a matter of prose:
* the report carries the digest the run actually used. */
function digestOf(input: SessionRunInput): string {
return createHash("sha256").update(JSON.stringify(input)).digest("hex").slice(0, 12);
return workDigestOf(input).slice(0, 12);
}

/** Pi-only execution adapter. Selection, ownership and acceptance are not model decisions.
Expand Down
3 changes: 3 additions & 0 deletions agent-context.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ routes:
- build
- package:check
- check
- mutation:anchors
- check:lock
- lint
- format:check
Expand Down Expand Up @@ -232,6 +233,7 @@ routes:
- src/integration/ooo-fusion-plan.ts
- src/integration/ooo-mutation.ts
- src/integration/ooo-patch.ts
- src/integration/ooo-runner.ts
- src/integration/check-ticket.ts
- src/integration/check-runner.ts
- src/integration/ooo-session-facts.ts
Expand All @@ -241,6 +243,7 @@ routes:
- src/integration/task-semantics-interleavings.ts
- src/integration/task-semantics-model.ts
- src/integration/task-semantics.ts
- src/integration/work-identity.ts
owners:
- docs/design/ooo-execution-bootstrap.md
- docs/design/task-unit-semantics.md
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ Split fusion planning into two clocks, and write down neither as a plan.
- **Online** is one move about the current session: _admit_ the next legal successor or _close_ the
session, naming the condition that closed it. A fused session is irreversible, so no move may
rewrite it.
- **Repair-first**: the default is to continue; only a declared change (rejected verdict, cancellation,
an unmet dependency, a declared external wait that is not ready) may end a session early. Repairing
keeps every commitment intact and decides only about what has not run.
- **Repair-first**: continuing is a constraint the plan declares rather than this design's default - a
plan that enables nothing runs one unit per session ([the program answers
legality](2026-09-20-the-program-answers-legality.md), step 3, landed 2026-09-23) - and, once enabled,
only a declared change (rejected verdict, cancellation, an unmet dependency, a declared external wait
that is not ready) may end a session early. Repairing keeps every commitment intact and decides only
about what has not run.
- **Baseline**: a move is not revisited while its facts hold, and the same plan plus the same facts
yield the same move, ties broken by plan order. Without determinism two runs of one plan are not
comparable, which is what the arms need.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
把融合规划拆成两个时钟,并且**两边都不写成一个计划**。

- **在线**只做关于当前会话的**一个动作**:*接纳*下一个合法后继,或*关闭*会话并说出关闭它的那个条件。已融合的会话**不可撤销**,所以任何动作都不能改写它。
- **修复优先**:默认是继续;只有**声明的变化**(裁决被拒、取消、依赖未达成、声明的外部等待未就绪)才能提前结束会话。修复保留全部已作出的承诺,只决定**尚未运行**的部分。
- **修复优先**:延续是一条**由计划声明的约束**,不是本设计的默认——什么都没启用的计划就是每个会话一个单元([程序只回答合法性](2026-09-20-the-program-answers-legality.zh-CN.md) 的第 3 步,2026-09-23 落地);启用后,只有**声明的变化**(裁决被拒、取消、依赖未达成、声明的外部等待未就绪)才能提前结束会话。修复保留全部已作出的承诺,只决定**尚未运行**的部分。
- **基线**:事实不变时,动作不重议;同样的计划加同样的事实得到同样的动作,平局按计划顺序打破。没有确定性,同一计划的两次运行就不可比——而两臂正需要可比。
- **成本模型在线只读**:离线拟合、按运行冻结。查询优化器也是在边界上拿已收集的统计**重新规划**,而**绝不在查询中途重新收集统计**。
- **离线**从一个**乐观投影**计算上限,并把投影喂给**同一个** `sharedSessionLegal`,让五条条件只有一个归属:下界来自**最小链覆盖**(Dilworth:等于最大反链,用 `单元数 − 最大匹配`(关系传递闭包上的二分图匹配)算出),可行界来自**按声明上限的贪心 list scheduling**。
Expand Down
Loading
Loading