hermes-task-framework 1.3.0__tar.gz → 1.4.0__tar.gz
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.
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/PKG-INFO +1 -1
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/README.md +8 -6
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-artifact-integrity/SKILL.md +13 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-artifact-integrity/scripts/task_integrity.py +30 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +200 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-cross-machine-sync/references/on-demand-interactive-task-windows.md +33 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_checkpoint_sync.py +369 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_git_sync.py +32 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/SKILL.md +28 -2
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/docs/SUBTASKS.md +32 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/manage_task.py +214 -21
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task_api.py +31 -2
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +15 -14
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/task_symlink_policy.py +101 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/task_write_lock.py +101 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/todo_lifecycle.py +267 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/update-index.py +90 -8
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/TASK.md +7 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/run.py +9 -6
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-nested-subtask-lifecycle/SKILL.md +42 -0
- hermes_task_framework-1.4.0/hermes_task_framework/skills/task-requirement-intake/SKILL.md +78 -0
- hermes_task_framework-1.4.0/hermes_task_framework/tests/test_nested_subtasks.py +147 -0
- hermes_task_framework-1.4.0/hermes_task_framework/tests/test_task_checkpoint_sync.py +516 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_integrity.py +19 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_root_resolution.py +22 -0
- hermes_task_framework-1.4.0/hermes_task_framework/tests/test_task_symlink_policy.py +91 -0
- hermes_task_framework-1.4.0/hermes_task_framework/tests/test_todo_lifecycle.py +124 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/PKG-INFO +1 -1
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/SOURCES.txt +13 -1
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/pyproject.toml +1 -1
- hermes_task_framework-1.3.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +0 -67
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/LICENSE +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/__init__.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/compact-directory-memory/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/compact-directory-memory/scripts/manage_directory_context.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-archaeology/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-artifact-integrity/references/task-root-relocation.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-aware-project-work/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-context-storage/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/composites/code-review-session.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/composites/software-dev.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/document-analysis-workflow.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/file-safety-lesson.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/generating-output-documents.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/operations/code-write.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/operations/document-write.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/operations/info-search.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/operations/web-research.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/policy-time-metadata.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-format-validation.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-hash-naming.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-lifecycle-example.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-overview-discovery.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-types/analysis.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-types/external-audit.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/task-types/video-production-pipeline.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/convert_md_to_pdf.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/MEMORY.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-discipline/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/references/task-5d5a1a-recovery-example.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-portability/references/output-model-design.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_directory_context.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_skill_ownership.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_api.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_git_sync.py +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/top_level.txt +0 -0
- {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hermes-task-framework
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: Structured task management skills for Hermes Agent — task creation, decomposition, stage gates
|
|
5
5
|
Project-URL: Source, https://github.com/nuffin/hermes-task-framework-pip
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -12,12 +12,13 @@ pip install hermes-task-framework
|
|
|
12
12
|
|
|
13
13
|
## What's Inside
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The package bundles the task-framework skill library from
|
|
16
|
+
[hermes-task-framework](https://github.com/nuffin/hermes-task-framework), including:
|
|
16
17
|
|
|
17
18
|
| Skill | What it does |
|
|
18
19
|
|-------|-------------|
|
|
19
20
|
| `task-framework` | Structured task lifecycle: directory, checklist, logs, scripts |
|
|
20
|
-
| `task-tracker` | Auto-update TASK.md checkboxes and
|
|
21
|
+
| `task-tracker` | Auto-update TASK.md checkboxes, MEMORY.md, and CHANGELOG.md |
|
|
21
22
|
| `task-timestamp-convention` | Naming rules for task directories |
|
|
22
23
|
| `task-lifecycle-edge-cases` | Recover lost TASK.md from artifacts |
|
|
23
24
|
| `task-lifecycle-portability` | Export/import tasks between machines |
|
|
@@ -41,7 +42,7 @@ Then in Hermes:
|
|
|
41
42
|
> 创建一个任务来分析这个 PDF
|
|
42
43
|
|
|
43
44
|
# The agent auto-detects task-framework, creates the directory,
|
|
44
|
-
# writes TASK.md +
|
|
45
|
+
# writes TASK.md + MEMORY.md + CHANGELOG.md, and works through the checklist.
|
|
45
46
|
```
|
|
46
47
|
|
|
47
48
|
## Task Structure
|
|
@@ -49,7 +50,8 @@ Then in Hermes:
|
|
|
49
50
|
```
|
|
50
51
|
tasks/20260717-154154.task-framework-extract-standalone-fad328/
|
|
51
52
|
├── TASK.md ← checklist with status + [ ] / [x]
|
|
52
|
-
├──
|
|
53
|
+
├── MEMORY.md ← durable task context
|
|
54
|
+
├── CHANGELOG.md ← chronological decision and change log
|
|
53
55
|
├── README.md ← goal, scope, key findings
|
|
54
56
|
├── .hermes-task.json ← hash, outputs, dependencies
|
|
55
57
|
├── input/ ← source files (never deleted)
|
|
@@ -59,7 +61,7 @@ tasks/20260717-154154.task-framework-extract-standalone-fad328/
|
|
|
59
61
|
## Development
|
|
60
62
|
|
|
61
63
|
```bash
|
|
62
|
-
git clone --recurse-submodules https://github.com/nuffin/hermes-task-framework.git
|
|
63
|
-
cd hermes-task-framework
|
|
64
|
+
git clone --recurse-submodules https://github.com/nuffin/hermes-task-framework-pip.git
|
|
65
|
+
cd hermes-task-framework-pip
|
|
64
66
|
pip install -e .
|
|
65
67
|
```
|
|
@@ -60,10 +60,23 @@ Report task hashes, roots, input/output inventories, manifest equality, context-
|
|
|
60
60
|
|
|
61
61
|
See `references/task-root-relocation.md` for the checklist.
|
|
62
62
|
|
|
63
|
+
## Symlink portability policy
|
|
64
|
+
|
|
65
|
+
`symlinks` and task-scope `post-flight` apply one reusable policy:
|
|
66
|
+
|
|
67
|
+
- allow only an existing relative target whose resolved path remains inside the same task root;
|
|
68
|
+
- reject absolute, missing, or task-escaping targets;
|
|
69
|
+
- reject a symlink at the task-root canonical files (`TASK.md`, `README.md`, `MEMORY.md`, `CHANGELOG.md`, `.hermes-task.json`);
|
|
70
|
+
- reject a symlink at tasks-root generated indexes (`README.md`, `TASKS.md`).
|
|
71
|
+
|
|
72
|
+
The commands are read-only and emit JSON. `post-flight` is the per-task integrity record used after task operations; it does not mutate a global task index.
|
|
73
|
+
|
|
63
74
|
## Permanent tooling
|
|
64
75
|
|
|
65
76
|
```bash
|
|
66
77
|
python3 scripts/task_integrity.py audit <hash-or-dir>
|
|
78
|
+
python3 scripts/task_integrity.py symlinks <hash-or-dir>
|
|
79
|
+
python3 scripts/task_integrity.py post-flight <hash-or-dir>
|
|
67
80
|
python3 scripts/task_integrity.py closure <hash-or-dir>
|
|
68
81
|
python3 scripts/task_integrity.py manifest <hash-or-dir> --output <manifest.json>
|
|
69
82
|
python3 scripts/task_integrity.py compare <source-dir> <destination-dir>
|
|
@@ -13,6 +13,7 @@ from pathlib import Path
|
|
|
13
13
|
TASK_SCRIPTS = Path(__file__).resolve().parents[2] / "task-framework" / "scripts"
|
|
14
14
|
sys.path.insert(0, str(TASK_SCRIPTS))
|
|
15
15
|
import task_api # pyright: ignore[reportMissingImports] # noqa: E402
|
|
16
|
+
from task_symlink_policy import inspect_task # pyright: ignore[reportMissingImports] # noqa: E402
|
|
16
17
|
|
|
17
18
|
CANONICAL = ("TASK.md", "README.md", "MEMORY.md", "CHANGELOG.md", ".hermes-task.json")
|
|
18
19
|
RELATION_KEYS = ("dependencies", "related", "supersedes", "requires", "required_by")
|
|
@@ -93,6 +94,11 @@ def audit(identifier: str) -> dict:
|
|
|
93
94
|
for dirname in ("input", "output", "scripts"):
|
|
94
95
|
if not (root / dirname).is_dir():
|
|
95
96
|
warnings.append(f"missing standard directory: {dirname}/")
|
|
97
|
+
symlinks = inspect_task(root)
|
|
98
|
+
errors.extend(
|
|
99
|
+
f"symlink policy: {entry['path']}: {entry['reason']}"
|
|
100
|
+
for entry in symlinks["rejected"]
|
|
101
|
+
)
|
|
96
102
|
return {
|
|
97
103
|
"task": description,
|
|
98
104
|
"ok": not errors,
|
|
@@ -100,6 +106,7 @@ def audit(identifier: str) -> dict:
|
|
|
100
106
|
"warnings": warnings,
|
|
101
107
|
"subsystems": subsystems,
|
|
102
108
|
"relations": relation_hashes(metadata),
|
|
109
|
+
"symlinks": symlinks,
|
|
103
110
|
"manifest_entries": len(build_manifest(root)),
|
|
104
111
|
}
|
|
105
112
|
|
|
@@ -123,6 +130,18 @@ def closure(identifier: str) -> dict:
|
|
|
123
130
|
return {"root": root["hash"], "tasks": list(seen.values()), "unresolved": sorted(set(unresolved))}
|
|
124
131
|
|
|
125
132
|
|
|
133
|
+
def post_flight(identifier: str) -> dict:
|
|
134
|
+
description = task_api.command_describe(identifier)
|
|
135
|
+
report = inspect_task(Path(description["path"]))
|
|
136
|
+
return {
|
|
137
|
+
"scope": "task",
|
|
138
|
+
"phase": "post-flight",
|
|
139
|
+
"task": {"hash": description["hash"], "path": description["path"]},
|
|
140
|
+
"ok": report["ok"],
|
|
141
|
+
"symlinks": report,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
|
|
126
145
|
def compare(source: Path, destination: Path) -> dict:
|
|
127
146
|
source_manifest = build_manifest(source.resolve())
|
|
128
147
|
destination_manifest = build_manifest(destination.resolve())
|
|
@@ -143,6 +162,10 @@ def main() -> int:
|
|
|
143
162
|
audit_parser.add_argument("identifier")
|
|
144
163
|
closure_parser = commands.add_parser("closure")
|
|
145
164
|
closure_parser.add_argument("identifier")
|
|
165
|
+
symlinks_parser = commands.add_parser("symlinks")
|
|
166
|
+
symlinks_parser.add_argument("identifier")
|
|
167
|
+
post_flight_parser = commands.add_parser("post-flight")
|
|
168
|
+
post_flight_parser.add_argument("identifier")
|
|
146
169
|
manifest_parser = commands.add_parser("manifest")
|
|
147
170
|
manifest_parser.add_argument("identifier")
|
|
148
171
|
manifest_parser.add_argument("--output", type=Path)
|
|
@@ -157,6 +180,13 @@ def main() -> int:
|
|
|
157
180
|
elif args.command == "closure":
|
|
158
181
|
result = closure(args.identifier)
|
|
159
182
|
code = 0 if not result["unresolved"] else 1
|
|
183
|
+
elif args.command == "symlinks":
|
|
184
|
+
description = task_api.command_describe(args.identifier)
|
|
185
|
+
result = inspect_task(Path(description["path"]))
|
|
186
|
+
code = 0 if result["ok"] else 1
|
|
187
|
+
elif args.command == "post-flight":
|
|
188
|
+
result = post_flight(args.identifier)
|
|
189
|
+
code = 0 if result["ok"] else 1
|
|
160
190
|
elif args.command == "manifest":
|
|
161
191
|
root = Path(task_api.command_describe(args.identifier)["path"])
|
|
162
192
|
result = build_manifest(root)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
---
|
|
2
|
+
author: Hermes Agent
|
|
3
|
+
category: software-development
|
|
4
|
+
description: Synchronize a configured task root across machines with Git while preserving task indexes and context integrity.
|
|
5
|
+
license: MIT
|
|
6
|
+
metadata:
|
|
7
|
+
hermes:
|
|
8
|
+
scenes: [hermes, coding, devops]
|
|
9
|
+
tags: [task-framework, git, cross-machine, synchronization, portability]
|
|
10
|
+
relations:
|
|
11
|
+
- type: depends_on
|
|
12
|
+
target: task-lifecycle-portability
|
|
13
|
+
properties: {reason: export/import remains the one-task fallback, strength: strong}
|
|
14
|
+
- type: complemented_by
|
|
15
|
+
target: task-artifact-integrity
|
|
16
|
+
properties: {reason: validates context after transport, strength: strong}
|
|
17
|
+
name: task-cross-machine-sync
|
|
18
|
+
platforms: [linux, macos]
|
|
19
|
+
version: 2.0.0
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# Task Cross-Machine Sync
|
|
23
|
+
|
|
24
|
+
Synchronizes the configured `$HERMES_TASKS_ROOT` as a Git repository. It contains no private repository name, fixed machine role, credentials, or mandatory remote.
|
|
25
|
+
|
|
26
|
+
## Configuration inputs
|
|
27
|
+
|
|
28
|
+
- task root from task-framework resolution;
|
|
29
|
+
- Git remote and branch supplied by the environment/user;
|
|
30
|
+
- optional machine role and conflict policy supplied outside this skill;
|
|
31
|
+
- optional Git LFS policy for large artifacts.
|
|
32
|
+
|
|
33
|
+
## Preconditions
|
|
34
|
+
|
|
35
|
+
1. Confirm the exact task root and Git repository status.
|
|
36
|
+
2. Ensure the remote/branch were explicitly configured.
|
|
37
|
+
3. Regenerate `README.md` and `TASKS.md` before commit.
|
|
38
|
+
4. Check for nested repositories and machine-specific artifacts.
|
|
39
|
+
5. Never push unless the user explicitly requests it.
|
|
40
|
+
|
|
41
|
+
## Track/ignore policy
|
|
42
|
+
|
|
43
|
+
Track task metadata, context, inputs, outputs, and verification artifacts unless the task repository policy says otherwise. Ignore environment rebuild products such as `.venv/`, `node_modules/`, caches, editor files, and sync-conflict metadata. Nested external repositories require an explicit policy; do not accidentally stage their object databases.
|
|
44
|
+
|
|
45
|
+
## Sync sequence
|
|
46
|
+
|
|
47
|
+
1. Fetch and inspect divergence.
|
|
48
|
+
2. Default to fast-forward only; stop on divergence for explicit reconciliation.
|
|
49
|
+
3. Resolve append-only CHANGELOG conflicts by preserving both chronological entries.
|
|
50
|
+
4. Resolve MEMORY conflicts semantically: retain current stable facts, remove stale duplicates.
|
|
51
|
+
5. Run task index regeneration and directory-context verification.
|
|
52
|
+
6. Commit task changes with task hash/name in the message.
|
|
53
|
+
7. Push only after explicit authorization.
|
|
54
|
+
|
|
55
|
+
## Fallbacks
|
|
56
|
+
|
|
57
|
+
Use `task-lifecycle-portability` export/import for one-task transfer, offline movement, or when the task root is not a shared Git repository.
|
|
58
|
+
|
|
59
|
+
## Permanent tooling
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
python3 scripts/task_git_sync.py status [--tasks-root <path>]
|
|
63
|
+
python3 scripts/task_git_sync.py pull --tasks-root <path> --remote <remote> --branch <branch> --execute
|
|
64
|
+
python3 scripts/task_git_sync.py push --tasks-root <path> --remote <remote> --branch <branch> --execute
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Mutation commands `pull` and `push` refuse dirty worktrees, missing remotes/branches, absent `--execute`, non-fast-forward pulls, and pushes when the remote is ahead. These strict commands still never create commits or repositories.
|
|
68
|
+
|
|
69
|
+
## Opt-in unattended checkpoint sync (Linux)
|
|
70
|
+
|
|
71
|
+
The separate `sync` command is for a tasks-only Git root whose owner has authorized
|
|
72
|
+
automatic commits and normal pushes. It never creates a repository, rewrites remote
|
|
73
|
+
history, changes signing policy, starts an LLM, claims task completion, or installs a
|
|
74
|
+
scheduler. Run it from outside the task root using an external hourly scheduler
|
|
75
|
+
(for example a systemd user timer, independent of the Hermes gateway).
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
python3 scripts/task_git_sync.py sync --config /absolute/path/task-sync.json --execute
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The JSON config has these exact fields (unknown fields are rejected):
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"tasks_root": "/absolute/path/to/tasks",
|
|
86
|
+
"remote": "origin",
|
|
87
|
+
"remote_url": "ssh://git@example.invalid/owner/tasks.git",
|
|
88
|
+
"branch": "main",
|
|
89
|
+
"node_id": "node-one",
|
|
90
|
+
"auto_push": true,
|
|
91
|
+
"cooperative_writers": true,
|
|
92
|
+
"max_blob_bytes": 10485760,
|
|
93
|
+
"command_timeout_seconds": 30,
|
|
94
|
+
"network_retries": 2
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
All fields except the last three are required. `tasks_root` must resolve to the exact
|
|
99
|
+
repository top-level and the current branch must be `main`. Both fetch and push URL
|
|
100
|
+
lists must contain exactly the literal `remote_url`; SCP-style spellings are not
|
|
101
|
+
normalized or repaired. `auto_push` and `cooperative_writers` must be JSON `true`.
|
|
102
|
+
The latter is the owner's declaration that uncoordinated writers are excluded; it
|
|
103
|
+
is **not evidence** that arbitrary applications have acquired a lock. No config
|
|
104
|
+
or real tasks root is modified by installation of this implementation.
|
|
105
|
+
|
|
106
|
+
### On-demand interactive task windows
|
|
107
|
+
|
|
108
|
+
When an explicitly named task needs human GPG/MFA/login interaction on an executor,
|
|
109
|
+
follow `references/on-demand-interactive-task-windows.md`. It defines the optional
|
|
110
|
+
`hermes-runtime:task-<hash6>` convention. Ordinary checkpoint runs remain noninteractive
|
|
111
|
+
and do not create a tmux window.
|
|
112
|
+
|
|
113
|
+
### Transaction and history
|
|
114
|
+
|
|
115
|
+
1. Acquire the nonblocking shared root writer lease; refuse known live Python/Hermes
|
|
116
|
+
processes whose `/proc/<pid>/cwd` is inside the task root, including idle sessions.
|
|
117
|
+
Shell CWD alone is not considered evidence of a writer. The process is never killed.
|
|
118
|
+
2. Refuse interrupted merge/rebase/cherry-pick/revert and busy index operations.
|
|
119
|
+
3. Snapshot the original index and then all working changes with a private index.
|
|
120
|
+
Git ignores are respected; tracked deletions are checkpointed. In an isolated
|
|
121
|
+
worktree, commit staged state first and remaining changes second. Normal `git commit`
|
|
122
|
+
honors hooks and configured signing; failure blocks sync, **never** falls back
|
|
123
|
+
unsigned. Checkpoint tree changes made by hooks also require review.
|
|
124
|
+
4. Keep local `refs/task-sync/<node>-<transaction>/{before,staged,working,integrated}`.
|
|
125
|
+
Rebase actual checkpoint/user commits, not a squashed synthetic replacement.
|
|
126
|
+
Only conflicting root `README.md`/`TASKS.md` are mechanically discarded in favor
|
|
127
|
+
of the integrated version. Any task-content conflict stops for human recovery in
|
|
128
|
+
the isolated worktree; main is not put into rebase conflict.
|
|
129
|
+
5. Reject absolute, missing, task-escaping, canonical-metadata, and root-index symlinks before generation; permit only an existing relative target whose resolved path stays within the same task root. Write root indexes by atomic regular-file replacement rather than opening existing targets for writing. Regenerate the root indexes **after** integration and commit only a changed tree. Each completed sync records a task-scope symlink post-flight in its transaction journal.
|
|
130
|
+
Repeated idle runs after first normalization do not create commits.
|
|
131
|
+
6. Push without force. A rejected push retries bounded fetch/rebase from the original
|
|
132
|
+
checkpoint. Verify the exact remote head after push. Before promotion compare
|
|
133
|
+
the local content/index/HEAD snapshot again, check incoming ignored/untracked
|
|
134
|
+
obstructions, and use checked index/worktree transitions, never `reset --hard` or
|
|
135
|
+
`clean`. New edits cause a refusal; an already successful push may still remain
|
|
136
|
+
published when a later local promotion is refused.
|
|
137
|
+
|
|
138
|
+
New/modified candidate blobs are size-limited; unchanged historical large blobs are
|
|
139
|
+
not rejected. New/changed credential filenames, numbered `.git.N` backup content and
|
|
140
|
+
nested gitlinks are refused. This filename denylist is **not a secret scanner**.
|
|
141
|
+
Unchanged historical backups/gitlinks and ignored historical content are not scanned
|
|
142
|
+
recursively. Snapshots hash tracked/eligible untracked regular files in chunks, with
|
|
143
|
+
a deadline, rather than traversing ignored build trees or nested repository contents.
|
|
144
|
+
|
|
145
|
+
### Writer coordination and limits
|
|
146
|
+
|
|
147
|
+
`task-framework/scripts/task_write_lock.py` supplies `task_writer_lock(root)` and
|
|
148
|
+
`child_lease(root)` for cooperating tools. The canonical `manage_task.cmd_*` commands,
|
|
149
|
+
JSON API `command_*` functions, and direct index-generator CLI participate. The index
|
|
150
|
+
subprocess receives a verified inherited lease descriptor to avoid self-deadlock.
|
|
151
|
+
Imported low-level file-writing helpers, standalone todo-lifecycle operations,
|
|
152
|
+
third-party plugins, editors, shell writes, and older deployed framework copies are
|
|
153
|
+
**not universally covered**. They must be quiesced or explicitly wrapped in the same
|
|
154
|
+
lease. An idle live Hermes task session can therefore block every hourly run until
|
|
155
|
+
its owner moves/exits it; the scheduler must report that as skipped/blocked, not
|
|
156
|
+
success. There is no bypass flag for the live-process guard.
|
|
157
|
+
|
|
158
|
+
Git/filesystem promotion is not an atomic operation against arbitrary writers.
|
|
159
|
+
Fingerprints reduce the race window but cannot eliminate edits between checking and
|
|
160
|
+
writing. Do not enable automatic promotion where the cooperative-writer assumption
|
|
161
|
+
cannot be enforced. Mount failures, special files, malicious hooks, or processes that
|
|
162
|
+
escape their process group are outside the safe operating contract.
|
|
163
|
+
|
|
164
|
+
Each Git command/network operation is bounded (default 30 seconds, maximum 60),
|
|
165
|
+
with at most `network_retries + 1` push attempts (maximum 6) and an approximately
|
|
166
|
+
180-second transaction budget. Timed-out Git commands kill only their owned process
|
|
167
|
+
group, including ordinary SSH/GPG/hook descendants. An external systemd service
|
|
168
|
+
should additionally use `TimeoutStartSec=180` and `KillMode=control-group` as the
|
|
169
|
+
whole-process-tree backstop. No global process kill is used.
|
|
170
|
+
|
|
171
|
+
### Recovery and deployment
|
|
172
|
+
|
|
173
|
+
The shared Git common directory holds untracked `task-sync/` state: `writer.lock`,
|
|
174
|
+
`journal.json` (latest state), append-only per-transaction event JSON, original index
|
|
175
|
+
backup during promotion, and retained isolated recovery worktrees. Conflict,
|
|
176
|
+
interrupted promotion, or dirty recovery worktree phases block later automatic runs.
|
|
177
|
+
Do not delete recovery data to make a timer green. Inspect the recorded refs/index,
|
|
178
|
+
resolve in the isolated worktree, and verify local and remote contents before an
|
|
179
|
+
operator archives the latest journal to acknowledge recovery. Immutable event
|
|
180
|
+
records and backup refs should remain available; there is no automatic retention
|
|
181
|
+
pruning. Clean failed-push worktrees are removed and the next run may retry while
|
|
182
|
+
checkpoint refs and event records remain.
|
|
183
|
+
|
|
184
|
+
Deploy a versioned full release snapshot without overwriting existing checkout or
|
|
185
|
+
remote repositories. All writers requiring coordination need the corresponding new
|
|
186
|
+
writer implementation; a standalone scheduler-only update is not full coordination.
|
|
187
|
+
Unattended GPG/pinentry or authentication failure is a **blocked signing/authentication
|
|
188
|
+
result**, not a reason to disable signing or alter remote URLs. Report scheduler
|
|
189
|
+
installation, fixture tests, real-node signing, and real-node synchronization as
|
|
190
|
+
separate evidence; none implies the others.
|
|
191
|
+
|
|
192
|
+
For older deployed framework revisions, a minimal adoption backport is the new
|
|
193
|
+
`task_write_lock.py`, the `manage_task.py` command wrappers plus inherited-lease
|
|
194
|
+
subprocess change, equivalent `task_api.py` wrappers **only where that file already
|
|
195
|
+
exists**, and the index-generator lease/atomic-write safety changes. Apply that diff
|
|
196
|
+
against the exact preserved deployed revision after a clean applicability check;
|
|
197
|
+
do not replace unrelated receipt APIs or entire source files with newer versions.
|
|
198
|
+
Revalidate that revision's own suite and an external lock-contention test before
|
|
199
|
+
enabling `cooperative_writers`. Existing in-memory Python imports require session
|
|
200
|
+
restart or remain unguarded; their live task CWD continues to block sync safely.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# On-Demand Interactive Task Windows
|
|
2
|
+
|
|
3
|
+
Use this convention only when an explicitly named remote task needs a human to
|
|
4
|
+
complete an interactive prerequisite, such as GPG pinentry, MFA, browser login,
|
|
5
|
+
or a hardware confirmation.
|
|
6
|
+
|
|
7
|
+
## Creation rule
|
|
8
|
+
|
|
9
|
+
Do not create an interactive window for ordinary automated work. Create or reuse
|
|
10
|
+
one only after the task identifies a concrete interaction requirement.
|
|
11
|
+
|
|
12
|
+
A remote runtime adapter owns SSH and tmux lifecycle. This task-framework
|
|
13
|
+
reference defines task-operation requirements only; it does not create sessions
|
|
14
|
+
itself and does not prescribe a role, suite, or external coordinator.
|
|
15
|
+
|
|
16
|
+
## Window identity
|
|
17
|
+
|
|
18
|
+
The task overlay derives one deterministic window identity from the canonical
|
|
19
|
+
task hash. Reuse the existing window for the same task. Never substitute a
|
|
20
|
+
generic shell window or create a second window for a different task identity.
|
|
21
|
+
|
|
22
|
+
## Operator handoff
|
|
23
|
+
|
|
24
|
+
1. Verify the selected executor, canonical task hash, and reason for interaction.
|
|
25
|
+
2. Resolve or reuse the receipt-derived task window.
|
|
26
|
+
3. Start an interactive shell or the minimal required command in that window.
|
|
27
|
+
4. Tell the operator the exact SSH attach command and the exact prompt they should
|
|
28
|
+
expect. Never request, capture, or transmit a secret.
|
|
29
|
+
5. After the operator completes the interaction, run a noninteractive readiness
|
|
30
|
+
probe from the same executor. For GPG, use cache-only signing so an absent cache
|
|
31
|
+
fails rather than opening an unattended pinentry.
|
|
32
|
+
6. Preserve the window for task audit and later operator attachment until the task
|
|
33
|
+
reaches a terminal state or the operator explicitly asks to close it.
|