修复 Trellis 空闲态治理检查(07-26-trellis-upgrade-project-audit)

这个提交包含在:
2026-07-26 18:12:24 +08:00
父节点 1e624c9482
当前提交 70aa24ecbf
共修改 4 个文件,包含 31 行新增和 2 行删除
@@ -101,6 +101,8 @@ rollback and future self-maintenance.
one deliberately. one deliberately.
- Post-upgrade dry-run changes manifest again -> investigate template hash drift - Post-upgrade dry-run changes manifest again -> investigate template hash drift
before commit. before commit.
- No active task directory after the last task is archived -> valid idle state;
governance still checks core files, docs registration and sensitive content.
- Only documented project overlays remain modified -> acceptable after task and - Only documented project overlays remain modified -> acceptable after task and
governance gates pass. governance gates pass.
@@ -118,6 +120,8 @@ rollback and future self-maintenance.
- Assert CLI and project versions match the intended target. - Assert CLI and project versions match the intended target.
- Compile every Python file under `.trellis/scripts` and `.codex/hooks`. - Compile every Python file under `.trellis/scripts` and `.codex/hooks`.
- Exercise task validate/current/list and run Trellis governance. - Exercise task validate/current/list and run Trellis governance.
- Archive the final active task and assert Trellis governance still passes with
zero active task directories.
- Confirm no `.new`, backup, bytecode or out-of-scope platform assets are - Confirm no `.new`, backup, bytecode or out-of-scope platform assets are
tracked; finish with `git diff --check`. tracked; finish with `git diff --check`.
@@ -16,6 +16,7 @@
- 四个冲突文件按设计完成合并,保留 Trellis-first overlay、Codex inline、禁止自动提交和 `workspace/` 忽略规则;未保留 `.new` 冲突副本。 - 四个冲突文件按设计完成合并,保留 Trellis-first overlay、Codex inline、禁止自动提交和 `workspace/` 忽略规则;未保留 `.new` 冲突副本。
- 0.6.9 runtime 的 `task_context.py` 包含 Python 3.9.2 不接受的嵌套多行 f-string;已改为先构造 warning 再着色,并通过全量 compileall 与 task runtime 验证。 - 0.6.9 runtime 的 `task_context.py` 包含 Python 3.9.2 不接受的嵌套多行 f-string;已改为先构造 warning 再着色,并通过全量 compileall 与 task runtime 验证。
- 升级后 dry-run 不再改变 manifest,仅报告 `task_context.py`、`config.yaml`、`.gitignore`、`workflow.md`、`AGENTS.md` 五个有意保留的项目定制。 - 升级后 dry-run 不再改变 manifest,仅报告 `task_context.py`、`config.yaml`、`.gitignore`、`workflow.md`、`AGENTS.md` 五个有意保留的项目定制。
- 归档最后一个 active task 后,治理检查器曾将正常 idle 状态误报为 `tasks.none`;已调整为允许零个 active task,并增加单元回归测试。
### 升级前版本与差异 ### 升级前版本与差异
+19 -2
查看文件
@@ -97,6 +97,19 @@ export function contextManifestPolicy(dispatchMode) {
}; };
} }
export function activeTaskDirectoryPolicy(taskCount) {
if (taskCount === 0) {
return {
valid: true,
detail: "0 active task directories; idle state after archive is valid",
};
}
return {
valid: true,
detail: `${taskCount} task directories have Trellis contracts`,
};
}
function checkContextManifest(result, taskDir, action, dispatchMode) { function checkContextManifest(result, taskDir, action, dispatchMode) {
const relativePath = `${taskDir}/${action}.jsonl`; const relativePath = `${taskDir}/${action}.jsonl`;
const policy = contextManifestPolicy(dispatchMode); const policy = contextManifestPolicy(dispatchMode);
@@ -209,7 +222,8 @@ function checkTaskContracts(result) {
const config = exists(".trellis/config.yaml") ? readText(".trellis/config.yaml") : ""; const config = exists(".trellis/config.yaml") ? readText(".trellis/config.yaml") : "";
const dispatchMode = /dispatch_mode:\s*sub-agent\b/.test(config) ? "sub-agent" : "inline"; const dispatchMode = /dispatch_mode:\s*sub-agent\b/.test(config) ? "sub-agent" : "inline";
if (taskDirs.length === 0) { if (taskDirs.length === 0) {
fail(result, "tasks.none", "no .trellis/tasks/* directories found"); const policy = activeTaskDirectoryPolicy(taskDirs.length);
pass(result, "task_contracts", policy.detail);
return; return;
} }
@@ -251,7 +265,10 @@ function checkTaskContracts(result) {
checkContextManifest(result, dir, "implement", dispatchMode); checkContextManifest(result, dir, "implement", dispatchMode);
checkContextManifest(result, dir, "check", dispatchMode); checkContextManifest(result, dir, "check", dispatchMode);
} }
if (result.status === "PASS") pass(result, "task_contracts", `${taskDirs.length} task directories have Trellis contracts`); if (result.status === "PASS") {
const policy = activeTaskDirectoryPolicy(taskDirs.length);
pass(result, "task_contracts", policy.detail);
}
} }
function checkDocsRegistration(result) { function checkDocsRegistration(result) {
@@ -24,4 +24,11 @@ describe("Trellis context manifest governance", () => {
rejectSeedRows: true, rejectSeedRows: true,
}); });
}); });
it("accepts the idle state after the last active task is archived", () => {
expect(governance.activeTaskDirectoryPolicy(0)).toEqual({
valid: true,
detail: "0 active task directories; idle state after archive is valid",
});
});
}); });