flower-trellis 0.6.10-beta.4 → 0.6.10-beta.5
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.
|
@@ -603,6 +603,17 @@ def _tracked_files(repo_root: Path, relative: str) -> list[str]:
|
|
|
603
603
|
return sorted(path for path in output.split("\0") if path)
|
|
604
604
|
|
|
605
605
|
|
|
606
|
+
def _untracked_files(repo_root: Path, relative: str) -> list[str]:
|
|
607
|
+
"""列出路径下可由 Git 纳管的未跟踪文件。"""
|
|
608
|
+
code, output, error = run_git(
|
|
609
|
+
["ls-files", "-z", "--others", "--exclude-standard", "--", relative],
|
|
610
|
+
cwd=repo_root,
|
|
611
|
+
)
|
|
612
|
+
if code != 0:
|
|
613
|
+
raise TaskLifecycleError("git-ls-files-failed", error.strip() or "无法读取未跟踪文件")
|
|
614
|
+
return sorted(path for path in output.split("\0") if path)
|
|
615
|
+
|
|
616
|
+
|
|
606
617
|
def _run_git_with_index(
|
|
607
618
|
args: list[str],
|
|
608
619
|
repo_root: Path,
|
|
@@ -1245,18 +1256,36 @@ def _write_batch_and_commit(
|
|
|
1245
1256
|
message: str,
|
|
1246
1257
|
baseline: dict[str, str],
|
|
1247
1258
|
transaction: dict[str, Any],
|
|
1259
|
+
*,
|
|
1260
|
+
commit_paths: Optional[list[str]] = None,
|
|
1261
|
+
expected_files: Optional[set[str]] = None,
|
|
1248
1262
|
) -> str:
|
|
1249
|
-
"""原子写入一批 JSON,并以可恢复的精确 pathspec 创建本地提交。
|
|
1263
|
+
"""原子写入一批 JSON,并以可恢复的精确 pathspec 创建本地提交。
|
|
1264
|
+
|
|
1265
|
+
Args:
|
|
1266
|
+
repo_root: Git worktree 根目录。
|
|
1267
|
+
writes: 需要原子写入的 JSON 路径与数据。
|
|
1268
|
+
message: maintenance commit 消息。
|
|
1269
|
+
baseline: 事务开始时固定的完整分支引用与 HEAD。
|
|
1270
|
+
transaction: 操作类型与可恢复结果。
|
|
1271
|
+
commit_paths: 可选的精确提交 pathspec;缺省仅提交写入的 JSON。
|
|
1272
|
+
expected_files: 可选的预期提交文件全集;缺省等于写入的 JSON。
|
|
1273
|
+
|
|
1274
|
+
Returns:
|
|
1275
|
+
新创建并绑定到固定分支的提交哈希。
|
|
1276
|
+
"""
|
|
1250
1277
|
originals = [(path, path.read_bytes()) for path, _ in writes]
|
|
1251
1278
|
relative_paths = [_task_relative(repo_root, path) for path, _ in writes]
|
|
1279
|
+
exact_paths = relative_paths if commit_paths is None else commit_paths
|
|
1280
|
+
exact_files = set(relative_paths) if expected_files is None else expected_files
|
|
1252
1281
|
try:
|
|
1253
1282
|
for path, data in writes:
|
|
1254
1283
|
if not write_json(path, data):
|
|
1255
1284
|
raise TaskLifecycleError("write-failed", f"无法写入:{_task_relative(repo_root, path)}")
|
|
1256
1285
|
return _commit_exact_paths(
|
|
1257
1286
|
repo_root,
|
|
1258
|
-
|
|
1259
|
-
|
|
1287
|
+
exact_paths,
|
|
1288
|
+
exact_files,
|
|
1260
1289
|
message,
|
|
1261
1290
|
baseline,
|
|
1262
1291
|
transaction,
|
|
@@ -1337,18 +1366,31 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1337
1366
|
return result
|
|
1338
1367
|
timestamp = migration_time or utc_now()
|
|
1339
1368
|
writes: list[tuple[Path, dict[str, Any]]] = []
|
|
1369
|
+
commit_paths: list[str] = []
|
|
1370
|
+
expected_files: set[str] = set()
|
|
1340
1371
|
for task_dir, data in legacy_dirs:
|
|
1341
1372
|
task_json = task_dir / FILE_TASK_JSON
|
|
1342
1373
|
task_ref = _task_relative(repo_root, task_dir)
|
|
1374
|
+
task_json_ref = f"{task_ref}/{FILE_TASK_JSON}"
|
|
1343
1375
|
path_status = _git_path_status(repo_root, [task_ref])
|
|
1376
|
+
status_records = _status_records(path_status)
|
|
1344
1377
|
dirty_paths = {
|
|
1345
1378
|
path
|
|
1346
|
-
for _, paths in
|
|
1379
|
+
for _, paths in status_records
|
|
1347
1380
|
for path in paths
|
|
1348
1381
|
}
|
|
1382
|
+
tracked_files = _tracked_files(repo_root, task_ref)
|
|
1383
|
+
untracked_files = set(_untracked_files(repo_root, task_ref)) if not tracked_files else set()
|
|
1384
|
+
entirely_untracked = bool(
|
|
1385
|
+
status_records
|
|
1386
|
+
and not tracked_files
|
|
1387
|
+
and task_json_ref in untracked_files
|
|
1388
|
+
and dirty_paths == untracked_files
|
|
1389
|
+
and all(git_status == "??" for git_status, _ in status_records)
|
|
1390
|
+
)
|
|
1349
1391
|
runner_owned = (
|
|
1350
1392
|
task_ref in recorded
|
|
1351
|
-
and dirty_paths == {
|
|
1393
|
+
and dirty_paths == {task_json_ref}
|
|
1352
1394
|
and (
|
|
1353
1395
|
recorded_digests.get(task_ref) == _task_json_sha256(task_json)
|
|
1354
1396
|
or _legacy_runner_bookkeeping_matches(
|
|
@@ -1359,10 +1401,10 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1359
1401
|
)
|
|
1360
1402
|
)
|
|
1361
1403
|
)
|
|
1362
|
-
if path_status and not runner_owned:
|
|
1404
|
+
if path_status and not runner_owned and not entirely_untracked:
|
|
1363
1405
|
result["deferred"].append({"task": task_ref, "reason": "candidate-dirty"})
|
|
1364
1406
|
continue
|
|
1365
|
-
if not
|
|
1407
|
+
if not tracked_files and not entirely_untracked:
|
|
1366
1408
|
result["deferred"].append({"task": task_ref, "reason": "candidate-untracked"})
|
|
1367
1409
|
continue
|
|
1368
1410
|
next_data = dict(data)
|
|
@@ -1375,7 +1417,7 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1375
1417
|
task_dir,
|
|
1376
1418
|
repo_root,
|
|
1377
1419
|
next_data,
|
|
1378
|
-
delivery_verified=not path_status or runner_owned,
|
|
1420
|
+
delivery_verified=not path_status or runner_owned or entirely_untracked,
|
|
1379
1421
|
closed_at=timestamp,
|
|
1380
1422
|
)
|
|
1381
1423
|
if evaluated["status"] == "error":
|
|
@@ -1389,6 +1431,13 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1389
1431
|
result["deferred"].append({"task": task_ref, "reason": "unknown-task-status"})
|
|
1390
1432
|
continue
|
|
1391
1433
|
writes.append((task_json, next_data))
|
|
1434
|
+
if entirely_untracked:
|
|
1435
|
+
# 新任务尚无 HEAD 基线,只能把可纳管的完整目录作为一个原子任务记录提交。
|
|
1436
|
+
commit_paths.append(task_ref)
|
|
1437
|
+
expected_files.update(untracked_files)
|
|
1438
|
+
else:
|
|
1439
|
+
commit_paths.append(task_json_ref)
|
|
1440
|
+
expected_files.add(task_json_ref)
|
|
1392
1441
|
result["migrated"].append({"task": task_ref, "closeout": migration_status})
|
|
1393
1442
|
if writes:
|
|
1394
1443
|
result["commit"] = _write_batch_and_commit(
|
|
@@ -1397,6 +1446,8 @@ def reconcile_legacy_tasks(repo_root: Path, migration_time: Optional[str] = None
|
|
|
1397
1446
|
"chore(task): reconcile closeout metadata",
|
|
1398
1447
|
baseline,
|
|
1399
1448
|
{"operation": "reconciliation", "result": result},
|
|
1449
|
+
commit_paths=commit_paths,
|
|
1450
|
+
expected_files=expected_files,
|
|
1400
1451
|
)
|
|
1401
1452
|
return result
|
|
1402
1453
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"syncedAt": "2026-09-
|
|
2
|
+
"syncedAt": "2026-09-23T09:09:05.054Z",
|
|
3
3
|
"syncedFrom": "vendor/skill-garden",
|
|
4
|
-
"sourceCommit": "
|
|
4
|
+
"sourceCommit": "c2a53653f8b80d2ff96ab6d340aec31f54eae864",
|
|
5
5
|
"common": {
|
|
6
6
|
"codexSkills": [
|
|
7
7
|
"aliyun-ops",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flower-trellis",
|
|
3
|
-
"version": "0.6.10-beta.
|
|
3
|
+
"version": "0.6.10-beta.5",
|
|
4
4
|
"description": "一键安装/升级 Trellis 并自动融合 skill-garden 强化包(默认 Claude + agents)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -70,9 +70,9 @@
|
|
|
70
70
|
"commit-and-tag-version": "^12.7.3"
|
|
71
71
|
},
|
|
72
72
|
"flowerReleaseNotes": {
|
|
73
|
-
"version": "0.6.10-beta.
|
|
73
|
+
"version": "0.6.10-beta.5",
|
|
74
74
|
"source": "CHANGELOG.md",
|
|
75
|
-
"body": "### ✨ 新功能 Features\n\n* **
|
|
75
|
+
"body": "### ✨ 新功能 Features\n\n* **task:** 支持精确收敛未跟踪旧任务 ([4b6871e](https://github.com/SilentFlower/flower-trellis/commit/4b6871e22abc3566aca9373d480ab9bf3d516bd3))",
|
|
76
76
|
"truncated": false
|
|
77
77
|
},
|
|
78
78
|
"optionalDependencies": {
|