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.
Files changed (84) hide show
  1. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/PKG-INFO +1 -1
  2. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/README.md +8 -6
  3. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-artifact-integrity/SKILL.md +13 -0
  4. {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
  5. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +200 -0
  6. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-cross-machine-sync/references/on-demand-interactive-task-windows.md +33 -0
  7. hermes_task_framework-1.4.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.4.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.4.0}/hermes_task_framework/skills/task-framework/SKILL.md +28 -2
  10. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/docs/SUBTASKS.md +32 -0
  11. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/manage_task.py +214 -21
  12. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task_api.py +31 -2
  13. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task_ref.py +15 -14
  14. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/task_symlink_policy.py +101 -0
  15. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/task_write_lock.py +101 -0
  16. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-framework/scripts/todo_lifecycle.py +267 -0
  17. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/update-index.py +90 -8
  18. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/TASK.md +7 -0
  19. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/run.py +9 -6
  20. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-nested-subtask-lifecycle/SKILL.md +42 -0
  21. hermes_task_framework-1.4.0/hermes_task_framework/skills/task-requirement-intake/SKILL.md +78 -0
  22. hermes_task_framework-1.4.0/hermes_task_framework/tests/test_nested_subtasks.py +147 -0
  23. hermes_task_framework-1.4.0/hermes_task_framework/tests/test_task_checkpoint_sync.py +516 -0
  24. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_integrity.py +19 -0
  25. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_root_resolution.py +22 -0
  26. hermes_task_framework-1.4.0/hermes_task_framework/tests/test_task_symlink_policy.py +91 -0
  27. hermes_task_framework-1.4.0/hermes_task_framework/tests/test_todo_lifecycle.py +124 -0
  28. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/PKG-INFO +1 -1
  29. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/SOURCES.txt +13 -1
  30. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/pyproject.toml +1 -1
  31. hermes_task_framework-1.3.0/hermes_task_framework/skills/task-cross-machine-sync/SKILL.md +0 -67
  32. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/LICENSE +0 -0
  33. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/__init__.py +0 -0
  34. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/compact-directory-memory/SKILL.md +0 -0
  35. {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
  36. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-archaeology/SKILL.md +0 -0
  37. {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
  38. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-aware-project-work/SKILL.md +0 -0
  39. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-context-storage/SKILL.md +0 -0
  40. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-external-repos-pattern/SKILL.md +0 -0
  41. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/CHANGELOG.md +0 -0
  42. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/TASKS.md +0 -0
  43. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/architecture.dot +0 -0
  44. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/architecture.svg +0 -0
  45. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/docs/product-requirements.md +0 -0
  46. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/auto-runner.md +0 -0
  47. {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
  48. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/composites/research.md +0 -0
  49. {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
  50. {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
  51. {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
  52. {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
  53. {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
  54. {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
  55. {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
  56. {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
  57. {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
  58. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/references/research-workflow.md +0 -0
  59. {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
  60. {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
  61. {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
  62. {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
  63. {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
  64. {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
  65. {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
  66. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/__init__.py +0 -0
  67. {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
  68. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/scripts/task-runner.sh +0 -0
  69. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/CHANGELOG.md +0 -0
  70. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-framework/templates/MEMORY.md +0 -0
  71. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-discipline/SKILL.md +0 -0
  72. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-edge-cases/SKILL.md +0 -0
  73. {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
  74. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-lifecycle-portability/SKILL.md +0 -0
  75. {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
  76. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-timestamp-convention/SKILL.md +0 -0
  77. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/skills/task-tracker/SKILL.md +0 -0
  78. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_directory_context.py +0 -0
  79. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_skill_ownership.py +0 -0
  80. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_api.py +0 -0
  81. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework/tests/test_task_git_sync.py +0 -0
  82. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/dependency_links.txt +0 -0
  83. {hermes_task_framework-1.3.0 → hermes_task_framework-1.4.0}/hermes_task_framework.egg-info/top_level.txt +0 -0
  84. {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.0
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
- 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,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.