hermes-task-framework 1.3.0__tar.gz → 1.5.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.
Files changed (85) hide show
  1. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/PKG-INFO +1 -1
  2. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/README.md +8 -6
  3. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/SKILL.md +13 -0
  4. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/scripts/task_integrity.py +30 -0
  5. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +251 -0
  6. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-cross-machine-sync/references/on-demand-interactive-task-windows.md +33 -0
  7. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_checkpoint_sync.py +369 -0
  8. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-cross-machine-sync/scripts/task_git_sync.py +32 -0
  9. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/SKILL.md +28 -2
  10. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-framework/docs/SUBTASKS.md +32 -0
  11. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/manage_task.py +281 -22
  12. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_api.py +31 -2
  13. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +15 -14
  14. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-framework/scripts/task_symlink_policy.py +101 -0
  15. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-framework/scripts/task_write_lock.py +101 -0
  16. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-framework/scripts/todo_lifecycle.py +267 -0
  17. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/update-index.py +90 -8
  18. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/TASK.md +7 -0
  19. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/run.py +9 -6
  20. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-nested-subtask-lifecycle/SKILL.md +42 -0
  21. hermes_task_framework-1.5.0/hermes_task_framework/skills/task-requirement-intake/SKILL.md +78 -0
  22. hermes_task_framework-1.5.0/hermes_task_framework/tests/test_nested_subtasks.py +147 -0
  23. hermes_task_framework-1.5.0/hermes_task_framework/tests/test_task_checkpoint_sync.py +516 -0
  24. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_integrity.py +19 -0
  25. hermes_task_framework-1.5.0/hermes_task_framework/tests/test_task_model_contract.py +71 -0
  26. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_root_resolution.py +22 -0
  27. hermes_task_framework-1.5.0/hermes_task_framework/tests/test_task_symlink_policy.py +91 -0
  28. hermes_task_framework-1.5.0/hermes_task_framework/tests/test_todo_lifecycle.py +124 -0
  29. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/PKG-INFO +1 -1
  30. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/SOURCES.txt +14 -1
  31. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/pyproject.toml +1 -1
  32. hermes_task_framework-1.3.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +0 -67
  33. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/LICENSE +0 -0
  34. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/__init__.py +0 -0
  35. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/compact-directory-memory/SKILL.md +0 -0
  36. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/compact-directory-memory/scripts/manage_directory_context.py +0 -0
  37. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-archaeology/SKILL.md +0 -0
  38. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-artifact-integrity/references/task-root-relocation.md +0 -0
  39. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-aware-project-work/SKILL.md +0 -0
  40. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-context-storage/SKILL.md +0 -0
  41. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
  42. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
  43. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
  44. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
  45. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
  46. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
  47. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
  48. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/code-review-session.md +0 -0
  49. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
  50. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/composites/software-dev.md +0 -0
  51. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/document-analysis-workflow.md +0 -0
  52. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/file-safety-lesson.md +0 -0
  53. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/generating-output-documents.md +0 -0
  54. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/code-write.md +0 -0
  55. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/document-write.md +0 -0
  56. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/info-search.md +0 -0
  57. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/operations/web-research.md +0 -0
  58. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/policy-time-metadata.md +0 -0
  59. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
  60. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-format-validation.md +0 -0
  61. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-hash-naming.md +0 -0
  62. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-lifecycle-example.md +0 -0
  63. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-overview-discovery.md +0 -0
  64. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/analysis.md +0 -0
  65. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/external-audit.md +0 -0
  66. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/references/task-types/video-production-pipeline.md +0 -0
  67. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
  68. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/convert_md_to_pdf.py +0 -0
  69. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
  70. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
  71. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-framework/templates/MEMORY.md +0 -0
  72. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-discipline/SKILL.md +0 -0
  73. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
  74. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/references/task-5d5a1a-recovery-example.md +0 -0
  75. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +0 -0
  76. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-lifecycle-portability/references/output-model-design.md +0 -0
  77. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
  78. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
  79. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_directory_context.py +0 -0
  80. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_skill_ownership.py +0 -0
  81. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_api.py +0 -0
  82. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework/tests/test_task_git_sync.py +0 -0
  83. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
  84. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.0}/hermes_task_framework.egg-info/top_level.txt +0 -0
  85. {hermes_task_framework-1.3.0 → hermes_task_framework-1.5.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.0
3
+ Version: 1.5.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
- 6 skills bundled from [task-framework](https://github.com/nuffin/task-framework):
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 TASK_MEMORY.md |
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 + TASK_MEMORY.md, and works through the checklist.
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
- ├── TASK_MEMORY.md ← auto-appended decision log
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,251 @@
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.1.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
+ ## Remote execution model contract
34
+
35
+ Every task that may be explicitly dispatched to another machine has one
36
+ immutable-at-dispatch provider/model pair. Store it in canonical task metadata:
37
+
38
+ ```json
39
+ {
40
+ "extensions": {
41
+ "remote_execution": {
42
+ "provider": "openai-codex",
43
+ "model": "gpt-5.6-terra-900k",
44
+ "provider_source": "explicit",
45
+ "model_source": "explicit"
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ### Creation-time capture
52
+
53
+ The task creation API resolves both fields once, independently but with the same
54
+ source order:
55
+
56
+ 1. explicit `manage_task.py create --model <model> --provider <provider>`;
57
+ 2. Hermes launch-scoped `HERMES_MODEL` / `HERMES_INFERENCE_MODEL` and
58
+ `HERMES_TUI_PROVIDER` / `HERMES_INFERENCE_PROVIDER`;
59
+ 3. current profile configuration, then global configuration, `model.default`
60
+ and `model.provider`.
61
+
62
+ The creating conversation must pass both `--model` and `--provider` when it has
63
+ a temporary per-conversation override that is not exported into the process
64
+ environment. Do not defer this decision until remote dispatch: the controller
65
+ may later use a different provider/model pair.
66
+
67
+ ### Dispatch-time resolution
68
+
69
+ An explicit dispatch provider/model pair may override the stored creation pair
70
+ only when the controller records both values in the dispatch request. Otherwise
71
+ dispatch uses `extensions.remote_execution.provider` and `.model`. The receipt,
72
+ `LaunchSpec`, and result manifest must all record the same resolved pair. A
73
+ missing provider or model is a blocked dispatch; never silently fall back to the
74
+ executor's local provider or model defaults.
75
+
76
+ The task-specific runtime overlay starts Hermes with its receipt-derived pair:
77
+
78
+ ```text
79
+ hermes -p <executor-profile> -m <resolved-model> --provider <resolved-provider>
80
+ ```
81
+
82
+ Generic SSH/tmux runtime remains model-agnostic and never reads task metadata.
83
+
84
+ ## Preconditions
85
+
86
+ 1. Confirm the exact task root and Git repository status.
87
+ 2. Ensure the remote/branch were explicitly configured.
88
+ 3. Regenerate `README.md` and `TASKS.md` before commit.
89
+ 4. Check for nested repositories and machine-specific artifacts.
90
+ 5. Never push unless the user explicitly requests it.
91
+
92
+ ## Track/ignore policy
93
+
94
+ 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.
95
+
96
+ ## Sync sequence
97
+
98
+ 1. Fetch and inspect divergence.
99
+ 2. Default to fast-forward only; stop on divergence for explicit reconciliation.
100
+ 3. Resolve append-only CHANGELOG conflicts by preserving both chronological entries.
101
+ 4. Resolve MEMORY conflicts semantically: retain current stable facts, remove stale duplicates.
102
+ 5. Run task index regeneration and directory-context verification.
103
+ 6. Commit task changes with task hash/name in the message.
104
+ 7. Push only after explicit authorization.
105
+
106
+ ## Fallbacks
107
+
108
+ Use `task-lifecycle-portability` export/import for one-task transfer, offline movement, or when the task root is not a shared Git repository.
109
+
110
+ ## Permanent tooling
111
+
112
+ ```bash
113
+ python3 scripts/task_git_sync.py status [--tasks-root <path>]
114
+ python3 scripts/task_git_sync.py pull --tasks-root <path> --remote <remote> --branch <branch> --execute
115
+ python3 scripts/task_git_sync.py push --tasks-root <path> --remote <remote> --branch <branch> --execute
116
+ ```
117
+
118
+ 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.
119
+
120
+ ## Opt-in unattended checkpoint sync (Linux)
121
+
122
+ The separate `sync` command is for a tasks-only Git root whose owner has authorized
123
+ automatic commits and normal pushes. It never creates a repository, rewrites remote
124
+ history, changes signing policy, starts an LLM, claims task completion, or installs a
125
+ scheduler. Run it from outside the task root using an external hourly scheduler
126
+ (for example a systemd user timer, independent of the Hermes gateway).
127
+
128
+ ```bash
129
+ python3 scripts/task_git_sync.py sync --config /absolute/path/task-sync.json --execute
130
+ ```
131
+
132
+ The JSON config has these exact fields (unknown fields are rejected):
133
+
134
+ ```json
135
+ {
136
+ "tasks_root": "/absolute/path/to/tasks",
137
+ "remote": "origin",
138
+ "remote_url": "ssh://git@example.invalid/owner/tasks.git",
139
+ "branch": "main",
140
+ "node_id": "node-one",
141
+ "auto_push": true,
142
+ "cooperative_writers": true,
143
+ "max_blob_bytes": 10485760,
144
+ "command_timeout_seconds": 30,
145
+ "network_retries": 2
146
+ }
147
+ ```
148
+
149
+ All fields except the last three are required. `tasks_root` must resolve to the exact
150
+ repository top-level and the current branch must be `main`. Both fetch and push URL
151
+ lists must contain exactly the literal `remote_url`; SCP-style spellings are not
152
+ normalized or repaired. `auto_push` and `cooperative_writers` must be JSON `true`.
153
+ The latter is the owner's declaration that uncoordinated writers are excluded; it
154
+ is **not evidence** that arbitrary applications have acquired a lock. No config
155
+ or real tasks root is modified by installation of this implementation.
156
+
157
+ ### On-demand interactive task windows
158
+
159
+ When an explicitly named task needs human GPG/MFA/login interaction on an executor,
160
+ follow `references/on-demand-interactive-task-windows.md`. It defines the optional
161
+ `hermes-runtime:task-<hash6>` convention. Ordinary checkpoint runs remain noninteractive
162
+ and do not create a tmux window.
163
+
164
+ ### Transaction and history
165
+
166
+ 1. Acquire the nonblocking shared root writer lease; refuse known live Python/Hermes
167
+ processes whose `/proc/<pid>/cwd` is inside the task root, including idle sessions.
168
+ Shell CWD alone is not considered evidence of a writer. The process is never killed.
169
+ 2. Refuse interrupted merge/rebase/cherry-pick/revert and busy index operations.
170
+ 3. Snapshot the original index and then all working changes with a private index.
171
+ Git ignores are respected; tracked deletions are checkpointed. In an isolated
172
+ worktree, commit staged state first and remaining changes second. Normal `git commit`
173
+ honors hooks and configured signing; failure blocks sync, **never** falls back
174
+ unsigned. Checkpoint tree changes made by hooks also require review.
175
+ 4. Keep local `refs/task-sync/<node>-<transaction>/{before,staged,working,integrated}`.
176
+ Rebase actual checkpoint/user commits, not a squashed synthetic replacement.
177
+ Only conflicting root `README.md`/`TASKS.md` are mechanically discarded in favor
178
+ of the integrated version. Any task-content conflict stops for human recovery in
179
+ the isolated worktree; main is not put into rebase conflict.
180
+ 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.
181
+ Repeated idle runs after first normalization do not create commits.
182
+ 6. Push without force. A rejected push retries bounded fetch/rebase from the original
183
+ checkpoint. Verify the exact remote head after push. Before promotion compare
184
+ the local content/index/HEAD snapshot again, check incoming ignored/untracked
185
+ obstructions, and use checked index/worktree transitions, never `reset --hard` or
186
+ `clean`. New edits cause a refusal; an already successful push may still remain
187
+ published when a later local promotion is refused.
188
+
189
+ New/modified candidate blobs are size-limited; unchanged historical large blobs are
190
+ not rejected. New/changed credential filenames, numbered `.git.N` backup content and
191
+ nested gitlinks are refused. This filename denylist is **not a secret scanner**.
192
+ Unchanged historical backups/gitlinks and ignored historical content are not scanned
193
+ recursively. Snapshots hash tracked/eligible untracked regular files in chunks, with
194
+ a deadline, rather than traversing ignored build trees or nested repository contents.
195
+
196
+ ### Writer coordination and limits
197
+
198
+ `task-framework/scripts/task_write_lock.py` supplies `task_writer_lock(root)` and
199
+ `child_lease(root)` for cooperating tools. The canonical `manage_task.cmd_*` commands,
200
+ JSON API `command_*` functions, and direct index-generator CLI participate. The index
201
+ subprocess receives a verified inherited lease descriptor to avoid self-deadlock.
202
+ Imported low-level file-writing helpers, standalone todo-lifecycle operations,
203
+ third-party plugins, editors, shell writes, and older deployed framework copies are
204
+ **not universally covered**. They must be quiesced or explicitly wrapped in the same
205
+ lease. An idle live Hermes task session can therefore block every hourly run until
206
+ its owner moves/exits it; the scheduler must report that as skipped/blocked, not
207
+ success. There is no bypass flag for the live-process guard.
208
+
209
+ Git/filesystem promotion is not an atomic operation against arbitrary writers.
210
+ Fingerprints reduce the race window but cannot eliminate edits between checking and
211
+ writing. Do not enable automatic promotion where the cooperative-writer assumption
212
+ cannot be enforced. Mount failures, special files, malicious hooks, or processes that
213
+ escape their process group are outside the safe operating contract.
214
+
215
+ Each Git command/network operation is bounded (default 30 seconds, maximum 60),
216
+ with at most `network_retries + 1` push attempts (maximum 6) and an approximately
217
+ 180-second transaction budget. Timed-out Git commands kill only their owned process
218
+ group, including ordinary SSH/GPG/hook descendants. An external systemd service
219
+ should additionally use `TimeoutStartSec=180` and `KillMode=control-group` as the
220
+ whole-process-tree backstop. No global process kill is used.
221
+
222
+ ### Recovery and deployment
223
+
224
+ The shared Git common directory holds untracked `task-sync/` state: `writer.lock`,
225
+ `journal.json` (latest state), append-only per-transaction event JSON, original index
226
+ backup during promotion, and retained isolated recovery worktrees. Conflict,
227
+ interrupted promotion, or dirty recovery worktree phases block later automatic runs.
228
+ Do not delete recovery data to make a timer green. Inspect the recorded refs/index,
229
+ resolve in the isolated worktree, and verify local and remote contents before an
230
+ operator archives the latest journal to acknowledge recovery. Immutable event
231
+ records and backup refs should remain available; there is no automatic retention
232
+ pruning. Clean failed-push worktrees are removed and the next run may retry while
233
+ checkpoint refs and event records remain.
234
+
235
+ Deploy a versioned full release snapshot without overwriting existing checkout or
236
+ remote repositories. All writers requiring coordination need the corresponding new
237
+ writer implementation; a standalone scheduler-only update is not full coordination.
238
+ Unattended GPG/pinentry or authentication failure is a **blocked signing/authentication
239
+ result**, not a reason to disable signing or alter remote URLs. Report scheduler
240
+ installation, fixture tests, real-node signing, and real-node synchronization as
241
+ separate evidence; none implies the others.
242
+
243
+ For older deployed framework revisions, a minimal adoption backport is the new
244
+ `task_write_lock.py`, the `manage_task.py` command wrappers plus inherited-lease
245
+ subprocess change, equivalent `task_api.py` wrappers **only where that file already
246
+ exists**, and the index-generator lease/atomic-write safety changes. Apply that diff
247
+ against the exact preserved deployed revision after a clean applicability check;
248
+ do not replace unrelated receipt APIs or entire source files with newer versions.
249
+ Revalidate that revision's own suite and an external lock-contention test before
250
+ enabling `cooperative_writers`. Existing in-memory Python imports require session
251
+ 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.