升级Trellis并精简项目知识(09-05-trellis-upgrade-rule-cleanup)
更新Trellis适配、任务归属与归档失败恢复,精简技能入口和知识读取规则。 保留权限、证据、事务及独立复核合同;纳入对应任务及已完成复核记录。 仅本地提交,不启用新平台适配,不推送或部署。
这个提交包含在:
@@ -648,17 +648,15 @@ def resolve_active_task(
|
||||
platform_input: dict[str, Any] | None = None,
|
||||
platform: str | None = None,
|
||||
*,
|
||||
allow_single_session_fallback: bool = True,
|
||||
allow_single_session_fallback: bool = False,
|
||||
allow_environment_context: bool = True,
|
||||
) -> ActiveTask:
|
||||
"""Resolve the active task from session runtime state only.
|
||||
|
||||
A stale session task is returned as stale. Missing context identity or a
|
||||
missing/empty session context falls back to single-session inference: if
|
||||
exactly one session file exists in the runtime, return its task with
|
||||
source_type="session-fallback" — covers pull-based platform sub-agents
|
||||
(copilot, gemini, qoder) that don't inherit the parent's session id. ≥2
|
||||
files or 0 files yield ActiveTask(None) — refuses to guess across windows.
|
||||
Missing or unmatched session identity does not infer ownership from the
|
||||
number of session files. Pull-based child-agent callers that cannot inherit
|
||||
a parent identity must opt into the compatibility fallback explicitly.
|
||||
"""
|
||||
context_key = resolve_context_key(
|
||||
platform_input,
|
||||
|
||||
@@ -196,6 +196,40 @@ def _report_write_failure(path: Path) -> None:
|
||||
)
|
||||
|
||||
|
||||
def _restore_child_links(unlinked: dict[Path, str | None]) -> None:
|
||||
"""Put back the parent links this archive attempt already removed.
|
||||
|
||||
The unlink loop below walks a parent's children one at a time, so a
|
||||
failure part-way through leaves the earlier children carrying
|
||||
``parent: null`` while their parent is still in the active tree. That is
|
||||
the mirror image of the dangling reference the same loop already refuses
|
||||
to create, and nothing repairs it later either, so undo it before the
|
||||
failure is reported.
|
||||
|
||||
Restoring is best-effort: if a child cannot be written back, name it so
|
||||
the caller can re-link it by hand instead of guessing which one broke.
|
||||
"""
|
||||
broken: list[str] = []
|
||||
for child_json, original_parent in unlinked.items():
|
||||
child_data, _ = read_json_checked(child_json)
|
||||
if child_data is None:
|
||||
broken.append(child_json.parent.name)
|
||||
continue
|
||||
child_data["parent"] = original_parent
|
||||
if not write_json(child_json, child_data):
|
||||
broken.append(child_json.parent.name)
|
||||
if broken:
|
||||
print(
|
||||
colored(
|
||||
f"Warning: could not restore the parent link on: {', '.join(broken)}. "
|
||||
"Re-link each one with `python3 .trellis/scripts/task.py "
|
||||
"add-subtask <parent> <child>`.",
|
||||
Colors.RED,
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Sub-agent platform detection + JSONL context files
|
||||
# =============================================================================
|
||||
@@ -1332,7 +1366,13 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
||||
# missing from the active set are treated as completed.
|
||||
task_children = data.get("children", [])
|
||||
|
||||
# If this is a parent, clear parent field in all children
|
||||
# If this is a parent, clear parent field in all children.
|
||||
# Remember each link removed so a later failure in this loop can
|
||||
# put it back (see _restore_child_links). Keyed by the child's
|
||||
# task.json: a `children` list that names the same child twice
|
||||
# would otherwise record a second, already-cleared snapshot and
|
||||
# the restore would write that `null` back over the real parent.
|
||||
unlinked_children: dict[Path, str | None] = {}
|
||||
if task_children:
|
||||
for child_name in task_children:
|
||||
child_dir_path = find_task_by_name(child_name, tasks_dir)
|
||||
@@ -1351,13 +1391,29 @@ def cmd_archive(args: argparse.Namespace) -> int:
|
||||
file=sys.stderr,
|
||||
)
|
||||
continue
|
||||
# Only the first visit to a child records its original
|
||||
# parent: a `children` list naming the same child twice
|
||||
# would otherwise snapshot the already-cleared value and
|
||||
# the restore would write that `null` back over the link.
|
||||
first_visit = child_json not in unlinked_children
|
||||
original_parent = child_data.get("parent")
|
||||
if first_visit:
|
||||
unlinked_children[child_json] = original_parent
|
||||
child_data["parent"] = None
|
||||
if not write_json(child_json, child_data):
|
||||
# Stop before the move: a child pointing at a
|
||||
# parent that has left .trellis/tasks/ is a
|
||||
# dangling reference nothing repairs later.
|
||||
# Put back the children already unlinked above —
|
||||
# their parent is staying, so losing the link the
|
||||
# other way round is just as unrecoverable.
|
||||
# Retrying is safe — every step so far is
|
||||
# idempotent.
|
||||
if first_visit:
|
||||
# The link never came off, so there is nothing
|
||||
# to put back for this child.
|
||||
del unlinked_children[child_json]
|
||||
_restore_child_links(unlinked_children)
|
||||
_report_write_failure(child_json)
|
||||
print(
|
||||
f"Not archived: {_repo_relative_path(task_dir, repo_root)} is "
|
||||
|
||||
在新工单中引用
屏蔽一个用户