@pennixrv/trellis 0.7.1-beta.10 → 0.7.1-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/migrations/manifests/0.7.1-beta.11.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.6.json +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +1 -1
- package/dist/templates/trellis/scripts/common/task_continuity.py +10 -19
- package/package.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.7.1-beta.11",
|
|
3
|
+
"description": "收敛 handoff ownership 为一次性交接",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "移除消费后 ownership reconcile 入口;consume 成功后 ownership generation 固定,普通任务修改、提交、阶段推进和交付收尾不再改写交接归属。保留 sealed-source 在 claim 前的 continuity digest 恢复核对,以及只读 transfer status --include-current。",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No migration required. Existing consumed ownership records remain readable and are not rewritten."
|
|
9
|
+
}
|
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
"recommendMigrate": true,
|
|
6
6
|
"changelog": "任务保留 planning、in_progress、completed、cancelled 四个状态;目录随原生生命周期操作同步迁移。完成任务按月份保存,取消任务单独保存。任务发现、父子关系、上下文、状态栏、初始化和平台模板统一使用新布局;旧布局需要显式迁移。",
|
|
7
7
|
"migrations": [],
|
|
8
|
-
"migrationGuide": "## 任务目录迁移\n\n先运行 trellis update --migrate 更新脚本与工作流,并审查自定义文件冲突。更新模板不会自动移动任务。随后运行 python3 .trellis/scripts/task.py migrate-layout 预览,确认后运行 python3 .trellis/scripts/task.py migrate-layout --apply。planning、in_progress、cancelled 任务进入同名子目录;completed 任务进入 completed/YYYY-MM,旧 archive 的月份保持不变。task.json.status 是唯一状态事实,迁移不授予历史任务实施权限,也不重写历史正文或交接包。\n\n无效 JSON、重名、未关闭的委派资源、待交接所有权或不安全路径会阻止整个迁移。先处理明确的输入问题,再重新预览。发生中断时运行 python3 .trellis/scripts/task.py migrate-layout --recover,核验后重新预览;不要手工移动目录或删除事务记录。迁移会同步改写机器引用和当前会话指针;若连续性摘要变化,按原生 phase 接纳和 transfer
|
|
8
|
+
"migrationGuide": "## 任务目录迁移\n\n先运行 trellis update --migrate 更新脚本与工作流,并审查自定义文件冲突。更新模板不会自动移动任务。随后运行 python3 .trellis/scripts/task.py migrate-layout 预览,确认后运行 python3 .trellis/scripts/task.py migrate-layout --apply。planning、in_progress、cancelled 任务进入同名子目录;completed 任务进入 completed/YYYY-MM,旧 archive 的月份保持不变。task.json.status 是唯一状态事实,迁移不授予历史任务实施权限,也不重写历史正文或交接包。\n\n无效 JSON、重名、未关闭的委派资源、待交接所有权或不安全路径会阻止整个迁移。先处理明确的输入问题,再重新预览。发生中断时运行 python3 .trellis/scripts/task.py migrate-layout --recover,核验后重新预览;不要手工移动目录或删除事务记录。迁移会同步改写机器引用和当前会话指针;若连续性摘要变化,按原生 phase 接纳和 capture 重新核对当前事实。handoff ownership 在 consume 后完成转移,后续普通任务修改不更新交接代次;需要查看现场时使用只读 transfer status --include-current,保留历史交接摘要。",
|
|
9
9
|
"aiInstructions": "完整检查迁移预览、四种状态及月份分布、任务数量、父子关系和当前会话路径。只修复有证据的历史输入错误,不把 archive 中未完成任务伪造为 completed。使用原生生命周期入口完成或取消任务,不手写运行状态。保护项目配置、历史正文、二进制附件和不可变交接包。迁移后再次预览应为空,并运行 task.py list、current、phase show 和项目检查验证现场。"
|
|
10
10
|
}
|
|
@@ -33,7 +33,7 @@ The read-only trellis task progress command summarizes planning, in_progress, co
|
|
|
33
33
|
|
|
34
34
|
旧平铺目录和 `archive/YYYY-MM` 只能作为显式迁移输入。先运行 `task.py migrate-layout` 预览,再使用 `--apply`;已完成 archive 任务保留原月份,非终态任务按真实状态归位,不伪造完成。`--recover` 恢复未完成事务。重复名称、无效 JSON、符号链接、未知执行资源或交接所有权冲突会阻止迁移;不跳过记录。
|
|
35
35
|
|
|
36
|
-
移动同步 JSON/JSONL 机器引用、会话指针及 runtime 哈希索引;终态操作在同一事务中清除本任务 runtime。历史正文和正式交接包保留。内容绑定的接受证据或 continuity digest
|
|
36
|
+
移动同步 JSON/JSONL 机器引用、会话指针及 runtime 哈希索引;终态操作在同一事务中清除本任务 runtime。历史正文和正式交接包保留。内容绑定的接受证据或 continuity digest 失效后,通过原生重新接受和 capture 核对,不静默重授历史授权。handoff ownership 在 `consume` 后完成转移;后续普通任务修改不更新交接代次,需要查看现场时使用只读 `transfer status --include-current`。文档中的 `<task-dir>` 指包含状态及必要月份的真实任务相对路径。
|
|
37
37
|
|
|
38
38
|
## Context manifests
|
|
39
39
|
|
|
@@ -385,17 +385,13 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
385
385
|
if not PACKAGE_DIGEST_RE.fullmatch(args.expected_continuity_digest):
|
|
386
386
|
raise ContinuityError("TASK_TRANSFER_INVALID_INPUT", "expected continuity digest must be a SHA-256 digest")
|
|
387
387
|
if args.expected_continuity_digest != current_digest:
|
|
388
|
-
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "current task projection differs from the explicitly
|
|
388
|
+
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "current task projection differs from the explicitly checked snapshot")
|
|
389
389
|
# Recovery acknowledges a live target snapshot without rebasing the source package.
|
|
390
|
-
|
|
390
|
+
recovered_claim = (
|
|
391
391
|
operation == "claim" and record["status"] == "sealed"
|
|
392
392
|
and args.expected_continuity_digest == current_digest
|
|
393
393
|
)
|
|
394
|
-
|
|
395
|
-
operation == "reconcile" and record["status"] == "consumed"
|
|
396
|
-
and args.expected_continuity_digest == current_digest
|
|
397
|
-
)
|
|
398
|
-
if operation != "release" and current_digest != record.get("target_continuity_digest", record["continuity_digest"]) and not (reconciled_claim or reconciled_target):
|
|
394
|
+
if operation != "release" and current_digest != record.get("target_continuity_digest", record["continuity_digest"]) and not recovered_claim:
|
|
399
395
|
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "task continuity changed during ownership transfer")
|
|
400
396
|
if operation != "release" and _route_snapshot(data) != record["route"]:
|
|
401
397
|
raise ContinuityError("TASK_TRANSFER_STALE_ROUTE", "task phase, route, input, or implementation authorization changed")
|
|
@@ -403,13 +399,10 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
403
399
|
if operation in {"seal", "retire"}:
|
|
404
400
|
if context_key != record["source_session_id"] or current_path not in {record["task_path"], None}:
|
|
405
401
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "source session no longer owns the current task")
|
|
406
|
-
elif operation in {"claim", "consume"
|
|
402
|
+
elif operation in {"claim", "consume"}:
|
|
407
403
|
if context_key == record["source_session_id"]:
|
|
408
404
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "target must be a different direct session")
|
|
409
|
-
if operation == "
|
|
410
|
-
if not reconciled_target or context_key != record["target_session_id"] or current_path != record["task_path"]:
|
|
411
|
-
raise ContinuityError("TASK_TRANSFER_WITHHELD", "only the consumed target may reconcile its explicitly pinned projection")
|
|
412
|
-
elif operation == "release":
|
|
405
|
+
if operation == "release":
|
|
413
406
|
if record["status"] not in {"quiescing", "sealed", "recovery_required"} or record["target_session_id"] is not None:
|
|
414
407
|
raise ContinuityError("TASK_TRANSFER_INVALID_TRANSITION", "ownership can be released only before target claim")
|
|
415
408
|
if context_key != record["source_session_id"] or current_path not in {None, record["task_path"]}:
|
|
@@ -422,9 +415,7 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
422
415
|
path.unlink(missing_ok=True)
|
|
423
416
|
print(json.dumps({"status": "released", "generation": record["generation"], "task_id": task_id}, ensure_ascii=False, sort_keys=True))
|
|
424
417
|
return 0
|
|
425
|
-
if operation == "
|
|
426
|
-
record.update({"target_continuity_digest": current_digest, "generation": record["generation"] + 1})
|
|
427
|
-
elif operation == "seal" and record["status"] == "quiescing":
|
|
418
|
+
if operation == "seal" and record["status"] == "quiescing":
|
|
428
419
|
record.update({"status": "sealed", "generation": record["generation"] + 1})
|
|
429
420
|
elif operation == "retire" and record["status"] in {"sealed", "retiring", "recovery_required"}:
|
|
430
421
|
if record["status"] == "sealed":
|
|
@@ -440,14 +431,14 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
440
431
|
write_json(path, record)
|
|
441
432
|
raise ContinuityError("TASK_TRANSFER_RECOVERY_REQUIRED", "source pointer remains after retirement")
|
|
442
433
|
record["status"] = "ready"
|
|
443
|
-
elif operation == "claim" and (record["status"] in {"ready", "claiming", "recovery_required", "claimed"} or
|
|
434
|
+
elif operation == "claim" and (record["status"] in {"ready", "claiming", "recovery_required", "claimed"} or recovered_claim):
|
|
444
435
|
if record["status"] in {"claiming", "recovery_required"} and record["target_session_id"] != context_key:
|
|
445
436
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "only the recorded target session can recover a claim")
|
|
446
437
|
if current_path not in {None, record["task_path"]}:
|
|
447
438
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "target session already has a current task")
|
|
448
439
|
if record["status"] in {"ready", "sealed"}:
|
|
449
440
|
record.update({"status": "claiming", "generation": record["generation"] + 1, "target_session_id": context_key})
|
|
450
|
-
if
|
|
441
|
+
if recovered_claim:
|
|
451
442
|
record["target_continuity_digest"] = current_digest
|
|
452
443
|
if not write_json(path, record):
|
|
453
444
|
raise ContinuityError("TASK_TRANSFER_WRITE_FAILED", "could not record target claim")
|
|
@@ -497,12 +488,12 @@ def add_continuity_commands(subparsers) -> None:
|
|
|
497
488
|
capture.add_argument("--expected-source-digest", required=True, help="Current captured digest, or absent")
|
|
498
489
|
|
|
499
490
|
transfer = subparsers.add_parser("transfer", help="Transfer task ownership between direct sessions")
|
|
500
|
-
transfer.add_argument("transfer_action", choices=("status", "quiesce", "seal", "retire", "claim", "consume", "release"
|
|
491
|
+
transfer.add_argument("transfer_action", choices=("status", "quiesce", "seal", "retire", "claim", "consume", "release"))
|
|
501
492
|
transfer.add_argument("--task-id", required=True)
|
|
502
493
|
transfer.add_argument("--task-path", required=True)
|
|
503
494
|
transfer.add_argument("--package-id", required=True)
|
|
504
495
|
transfer.add_argument("--package-digest", required=True)
|
|
505
|
-
transfer.add_argument("--expected-continuity-digest", help="Expected live projection
|
|
496
|
+
transfer.add_argument("--expected-continuity-digest", help="Expected live projection for sealed-source claim recovery")
|
|
506
497
|
transfer.add_argument("--expected-generation", type=int)
|
|
507
498
|
transfer.add_argument("--include-current", action="store_true", help="Include the live task projection and its digest in transfer status")
|
|
508
499
|
transfer.add_argument("--explicit-user-request", action="store_true")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pennixrv/trellis",
|
|
3
|
-
"version": "0.7.1-beta.
|
|
3
|
+
"version": "0.7.1-beta.11",
|
|
4
4
|
"description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"inquirer": "^9.3.7",
|
|
35
35
|
"undici": "^6.21.0",
|
|
36
36
|
"zod": "^4.4.2",
|
|
37
|
-
"@pennixrv/trellis-core": "0.7.1-beta.
|
|
37
|
+
"@pennixrv/trellis-core": "0.7.1-beta.11"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/js": "^9.18.0",
|