@pennixrv/trellis 0.7.0-beta.51 → 0.7.0-beta.52
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.
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.7.0-beta.52",
|
|
3
|
+
"description": "修复日期任务的交接围栏,并拒绝携带正式 ownership 记录的任务改名",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "交接围栏使用 task.json 中的规范任务 ID 查询记录,覆盖原生日期目录;任何正式 ownership 记录均阻止任务改名,避免留下失效的路径和身份引用。",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "无任务 schema、ownership 记录格式或历史迁移。没有正式 ownership 存储的普通任务保留原行为;已有交接记录的任务需保持原名。"
|
|
9
|
+
}
|
|
@@ -416,20 +416,35 @@ def status(root: Path | None, task_id: str, handoff_id: str, core_digest: str) -
|
|
|
416
416
|
return _public(record)
|
|
417
417
|
|
|
418
418
|
|
|
419
|
+
def _task_ownership_records(root: Path, task_path: Path) -> tuple[dict[str, str], list[Path]]:
|
|
420
|
+
"""Discover records by validated JSON identity, never by directory name."""
|
|
421
|
+
runtime = root / ".trellis" / ".runtime"
|
|
422
|
+
ownership = runtime / "handoff-ownership"
|
|
423
|
+
if runtime.is_symlink() or ownership.is_symlink():
|
|
424
|
+
raise OwnershipError("ownership record directory is unsafe")
|
|
425
|
+
if not ownership.exists():
|
|
426
|
+
return {}, []
|
|
427
|
+
relative = task_path.relative_to(root).as_posix()
|
|
428
|
+
try:
|
|
429
|
+
task = _task_snapshot(root, relative)
|
|
430
|
+
records_dir = _record_path(root, task["id"], "record-probe").parent
|
|
431
|
+
except ContinuationError as exc:
|
|
432
|
+
raise OwnershipError(str(exc)) from exc
|
|
433
|
+
return task, sorted(records_dir.glob("*.json"))
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def assert_task_rename_allowed(root: Path, task_path: Path) -> None:
|
|
437
|
+
"""A rename cannot preserve the immutable identity of a formal handoff."""
|
|
438
|
+
_task, records = _task_ownership_records(root, task_path)
|
|
439
|
+
if records:
|
|
440
|
+
raise OwnershipError("refusing rename: task has formal handoff ownership records")
|
|
441
|
+
|
|
442
|
+
|
|
419
443
|
def assert_task_mutation_allowed(root: Path, task_path: Path) -> None:
|
|
420
444
|
"""Reject ordinary task pointer changes that would bypass ownership fencing."""
|
|
445
|
+
task, records = _task_ownership_records(root, task_path)
|
|
421
446
|
actor = resolve_context_key()
|
|
422
|
-
|
|
423
|
-
task_id = _safe_task_id(task_path.name)
|
|
424
|
-
except ContinuationError:
|
|
425
|
-
return
|
|
426
|
-
relative = task_path.relative_to(root).as_posix()
|
|
427
|
-
records_dir = root / ".trellis" / ".runtime" / "handoff-ownership" / task_id
|
|
428
|
-
if records_dir.is_symlink():
|
|
429
|
-
raise OwnershipError("ownership record directory is unsafe")
|
|
430
|
-
if not records_dir.is_dir():
|
|
431
|
-
return
|
|
432
|
-
for record_path in sorted(records_dir.glob("*.json")):
|
|
447
|
+
for record_path in records:
|
|
433
448
|
if not actor:
|
|
434
449
|
raise OwnershipError("no_direct_session_identity: task has handoff ownership fencing")
|
|
435
450
|
if record_path.is_symlink():
|
|
@@ -437,7 +452,7 @@ def assert_task_mutation_allowed(root: Path, task_path: Path) -> None:
|
|
|
437
452
|
record = _decode(record_path)
|
|
438
453
|
if record["state"] not in STATES:
|
|
439
454
|
continue
|
|
440
|
-
if record["task"]["path"] !=
|
|
455
|
+
if record["task"]["path"] != task["path"] or record["task"]["id"] != task["id"]:
|
|
441
456
|
raise OwnershipError("ownership record task path does not match current task")
|
|
442
457
|
if record.get("consumer_session_id") == actor:
|
|
443
458
|
continue
|
|
@@ -1095,6 +1095,13 @@ def cmd_rename(args: argparse.Namespace) -> int:
|
|
|
1095
1095
|
)
|
|
1096
1096
|
return 1
|
|
1097
1097
|
|
|
1098
|
+
from .ownership_record import assert_task_rename_allowed
|
|
1099
|
+
try:
|
|
1100
|
+
assert_task_rename_allowed(repo_root, task_dir)
|
|
1101
|
+
except (OSError, ValueError) as exc:
|
|
1102
|
+
print(colored(f"Error: {exc}", Colors.RED), file=sys.stderr)
|
|
1103
|
+
return 1
|
|
1104
|
+
|
|
1098
1105
|
old_name = task_dir.name
|
|
1099
1106
|
date_prefix, _old_slug = _split_date_prefix(old_name)
|
|
1100
1107
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pennixrv/trellis",
|
|
3
|
-
"version": "0.7.0-beta.
|
|
3
|
+
"version": "0.7.0-beta.52",
|
|
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.0-beta.
|
|
37
|
+
"@pennixrv/trellis-core": "0.7.0-beta.52"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/js": "^9.18.0",
|