hermes-task-framework 1.0.0__py3-none-any.whl
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.0.0.dist-info/METADATA +7 -0
- hermes_task_framework-1.0.0.dist-info/RECORD +56 -0
- hermes_task_framework-1.0.0.dist-info/WHEEL +5 -0
- hermes_task_framework-1.0.0.dist-info/licenses/LICENSE +1 -0
- hermes_task_framework-1.0.0.dist-info/top_level.txt +1 -0
- task-framework/__init__.py +2 -0
- task-framework/skills/task-external-repos-pattern/SKILL.md +165 -0
- task-framework/skills/task-framework/CHANGELOG.md +45 -0
- task-framework/skills/task-framework/SKILL.md +1365 -0
- task-framework/skills/task-framework/docs/TASKS.md +3 -0
- task-framework/skills/task-framework/docs/architecture.dot +65 -0
- task-framework/skills/task-framework/docs/architecture.svg +188 -0
- task-framework/skills/task-framework/docs/product-requirements.md +65 -0
- task-framework/skills/task-framework/references/auto-runner.md +32 -0
- task-framework/skills/task-framework/references/composites/code-review-session.md +30 -0
- task-framework/skills/task-framework/references/composites/research.md +45 -0
- task-framework/skills/task-framework/references/composites/software-dev.md +41 -0
- task-framework/skills/task-framework/references/document-analysis-workflow.md +95 -0
- task-framework/skills/task-framework/references/file-safety-lesson.md +38 -0
- task-framework/skills/task-framework/references/generating-output-documents.md +166 -0
- task-framework/skills/task-framework/references/operations/code-write.md +30 -0
- task-framework/skills/task-framework/references/operations/document-write.md +185 -0
- task-framework/skills/task-framework/references/operations/info-search.md +27 -0
- task-framework/skills/task-framework/references/operations/web-research.md +57 -0
- task-framework/skills/task-framework/references/policy-time-metadata.md +137 -0
- task-framework/skills/task-framework/references/research-workflow.md +220 -0
- task-framework/skills/task-framework/references/task-format-validation.md +25 -0
- task-framework/skills/task-framework/references/task-hash-naming.md +40 -0
- task-framework/skills/task-framework/references/task-lifecycle-example.md +122 -0
- task-framework/skills/task-framework/references/task-overview-discovery.md +31 -0
- task-framework/skills/task-framework/references/task-types/analysis.md +60 -0
- task-framework/skills/task-framework/references/task-types/external-audit.md +56 -0
- task-framework/skills/task-framework/references/task-types/video-production-pipeline.md +77 -0
- task-framework/skills/task-framework/scripts/__init__.py +0 -0
- task-framework/skills/task-framework/scripts/__pycache__/manage_task.cpython-312.pyc +0 -0
- task-framework/skills/task-framework/scripts/__pycache__/task_ref.cpython-312.pyc +0 -0
- task-framework/skills/task-framework/scripts/__pycache__/update-index.cpython-311.pyc +0 -0
- task-framework/skills/task-framework/scripts/__pycache__/update-index.cpython-312.pyc +0 -0
- task-framework/skills/task-framework/scripts/convert_md_to_pdf.py +236 -0
- task-framework/skills/task-framework/scripts/manage_task.py +442 -0
- task-framework/skills/task-framework/scripts/task-runner.sh +42 -0
- task-framework/skills/task-framework/scripts/task_ref.py +119 -0
- task-framework/skills/task-framework/scripts/update-index.py +293 -0
- task-framework/skills/task-framework/templates/TASK.md +114 -0
- task-framework/skills/task-framework/templates/TASK_MEMORY.md +27 -0
- task-framework/skills/task-framework/templates/run.py +187 -0
- task-framework/skills/task-lifecycle-edge-cases/SKILL.md +84 -0
- task-framework/skills/task-lifecycle-edge-cases/references/task-5d5a1a-recovery-example.md +67 -0
- task-framework/skills/task-lifecycle-portability/SKILL.md +128 -0
- task-framework/skills/task-lifecycle-portability/references/design-session-20260611.md +32 -0
- task-framework/skills/task-lifecycle-portability/references/output-model-design.md +55 -0
- task-framework/skills/task-lifecycle-portability/references/pipeline-output-transition.md +41 -0
- task-framework/skills/task-lifecycle-portability/references/task-recovery-5d5a1a-example.md +27 -0
- task-framework/skills/task-lifecycle-portability/references/task-recovery-procedure.md +63 -0
- task-framework/skills/task-timestamp-convention/SKILL.md +72 -0
- task-framework/skills/task-tracker/SKILL.md +80 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Unified task runner — single entry point for all phases.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
./run.py — auto: find first unchecked item & execute
|
|
7
|
+
./run.py phase<N> — run specific phase
|
|
8
|
+
./run.py list — show checklist status
|
|
9
|
+
|
|
10
|
+
Auto mode (no args):
|
|
11
|
+
1. Read TASK.md checklist, find first unchecked item
|
|
12
|
+
2. If it's a BREAK, mark it done, continue to next Phase(s)
|
|
13
|
+
3. Execute Phase(s) until next BREAK or end of list
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import sys, os, subprocess, re
|
|
17
|
+
|
|
18
|
+
TASK_DIR = os.path.dirname(os.path.abspath(__file__))
|
|
19
|
+
TASK_MD = os.path.join(TASK_DIR, "TASK.md")
|
|
20
|
+
SCRIPTS = os.path.join(TASK_DIR, "scripts")
|
|
21
|
+
VENV_PY = os.path.expanduser("~/.venvs/playwright/bin/python")
|
|
22
|
+
PY = VENV_PY if os.path.exists(VENV_PY) else sys.executable
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ── Phase runners ──
|
|
26
|
+
|
|
27
|
+
def run_script(name, label):
|
|
28
|
+
path = os.path.join(SCRIPTS, name)
|
|
29
|
+
if not os.path.exists(path):
|
|
30
|
+
print(f" ❌ Script not found: {path}")
|
|
31
|
+
return False
|
|
32
|
+
print(f"\n{'='*60}")
|
|
33
|
+
print(f" {label}")
|
|
34
|
+
print(f"{'='*60}")
|
|
35
|
+
return subprocess.run([PY, path], cwd=TASK_DIR).returncode == 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def phase1():
|
|
39
|
+
return run_script("gen_tts.py", "Phase 1: Text-to-Speech")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def phase2():
|
|
43
|
+
return run_script("record_timeline.py", "Phase 2: Browser Recording")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def phase3():
|
|
47
|
+
# Example: inline call with generate_timeline_chart
|
|
48
|
+
cmd = [
|
|
49
|
+
PY, "-c",
|
|
50
|
+
"import sys, os; "
|
|
51
|
+
"sys.path.insert(0, os.path.expanduser('~/.hermes/personal-suite/skills/video-audio-compositing/scripts')); "
|
|
52
|
+
"from utils.timeline import generate_timeline_chart; "
|
|
53
|
+
"generate_timeline_chart('COMPOSITING.md', 'timeline-chart-xxxxxx/timeline_chart.txt', format='both')"
|
|
54
|
+
]
|
|
55
|
+
print(f"\n{'='*60}")
|
|
56
|
+
print(f" Phase 3: Timeline Chart Preview")
|
|
57
|
+
print(f"{'='*60}")
|
|
58
|
+
return subprocess.run(cmd, cwd=TASK_DIR).returncode == 0
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def phase4():
|
|
62
|
+
return run_script("composite.py", "Phase 4: Compositing")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def phase5():
|
|
66
|
+
print(" ⏭ Phase 5 not implemented yet — skipping")
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def phase6():
|
|
71
|
+
print(" ⏭ Phase 6 not implemented yet — skipping")
|
|
72
|
+
return True
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def phase7():
|
|
76
|
+
print(" ⏭ Phase 7 not implemented yet — skipping")
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
PHASES = {
|
|
81
|
+
"phase1": phase1, "phase2": phase2, "phase3": phase3,
|
|
82
|
+
"phase4": phase4, "phase5": phase5, "phase6": phase6, "phase7": phase7,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# ── TASK.md checklist helpers ──
|
|
87
|
+
|
|
88
|
+
def parse_checklist():
|
|
89
|
+
with open(TASK_MD) as f:
|
|
90
|
+
lines = f.readlines()
|
|
91
|
+
items = []
|
|
92
|
+
for i, line in enumerate(lines):
|
|
93
|
+
m = re.match(r'^- \[([ x])\] (.+)', line)
|
|
94
|
+
if m:
|
|
95
|
+
items.append((i, m.group(2), m.group(1) == 'x'))
|
|
96
|
+
return items, lines
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def mark_done(line_idx, lines):
|
|
100
|
+
lines[line_idx] = lines[line_idx].replace("- [ ]", "- [x]", 1)
|
|
101
|
+
with open(TASK_MD, 'w') as f:
|
|
102
|
+
f.writelines(lines)
|
|
103
|
+
return lines
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def find_phase_num(text):
|
|
107
|
+
m = re.search(r'Phase (\d+)', text)
|
|
108
|
+
return int(m.group(1)) if m else None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def is_break(text):
|
|
112
|
+
return text.strip().startswith("BREAK")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# ── Auto mode ──
|
|
116
|
+
|
|
117
|
+
def run_auto():
|
|
118
|
+
items, lines = parse_checklist()
|
|
119
|
+
unchecked = [(idx, txt) for idx, txt, chk in items if not chk]
|
|
120
|
+
if not unchecked:
|
|
121
|
+
print("✅ All checklist items completed!")
|
|
122
|
+
return True
|
|
123
|
+
|
|
124
|
+
idx, txt = unchecked[0]
|
|
125
|
+
print(f"📋 First unchecked: {txt}")
|
|
126
|
+
|
|
127
|
+
if is_break(txt):
|
|
128
|
+
print(f" → Marking BREAK complete, continuing...")
|
|
129
|
+
lines = mark_done(idx, lines)
|
|
130
|
+
items, lines = parse_checklist()
|
|
131
|
+
unchecked = [(idx, txt) for idx, txt, chk in items if not chk]
|
|
132
|
+
if not unchecked:
|
|
133
|
+
print("✅ All items completed after marking BREAK!")
|
|
134
|
+
return True
|
|
135
|
+
idx, txt = unchecked[0]
|
|
136
|
+
print(f" → Next: {txt}")
|
|
137
|
+
|
|
138
|
+
for idx, txt in unchecked:
|
|
139
|
+
if is_break(txt):
|
|
140
|
+
print(f" ⏸ Stopping at BREAK: {txt}")
|
|
141
|
+
break
|
|
142
|
+
pnum = find_phase_num(txt)
|
|
143
|
+
if pnum is None:
|
|
144
|
+
print(f" ⏭ Skipping (not a phase): {txt}")
|
|
145
|
+
continue
|
|
146
|
+
phase_key = f"phase{pnum}"
|
|
147
|
+
fn = PHASES.get(phase_key)
|
|
148
|
+
if fn is None:
|
|
149
|
+
print(f" ❌ No runner for {phase_key}")
|
|
150
|
+
continue
|
|
151
|
+
print(f"\n ▶ Running {phase_key}...")
|
|
152
|
+
ok = fn()
|
|
153
|
+
if not ok:
|
|
154
|
+
print(f" ❌ {phase_key} failed!")
|
|
155
|
+
return False
|
|
156
|
+
lines = mark_done(idx, lines)
|
|
157
|
+
|
|
158
|
+
print(f"\n{'='*60}")
|
|
159
|
+
print(f" ✅ Auto-run complete")
|
|
160
|
+
print(f"{'='*60}")
|
|
161
|
+
return True
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def cmd_list():
|
|
165
|
+
items, _ = parse_checklist()
|
|
166
|
+
for _, txt, chk in items:
|
|
167
|
+
status = "✅" if chk else "⬜"
|
|
168
|
+
print(f" {status} {txt}")
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def main():
|
|
172
|
+
if len(sys.argv) >= 2:
|
|
173
|
+
cmd = sys.argv[1].strip().lower()
|
|
174
|
+
if cmd == "list":
|
|
175
|
+
cmd_list()
|
|
176
|
+
return 0
|
|
177
|
+
fn = PHASES.get(cmd)
|
|
178
|
+
if fn:
|
|
179
|
+
return 0 if fn() else 1
|
|
180
|
+
print(f"❌ Unknown: {cmd}\n")
|
|
181
|
+
print(__doc__)
|
|
182
|
+
return 1
|
|
183
|
+
return 0 if run_auto() else 1
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
if __name__ == "__main__":
|
|
187
|
+
sys.exit(main())
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-lifecycle-edge-cases
|
|
3
|
+
description: "Edge-case operations for task lifecycle — TASK.md recovery after loss, task resurrection from artifacts, conflict resolution when phase directories diverge from checklist."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
author: Hauzer S. Lee
|
|
6
|
+
license: MIT
|
|
7
|
+
category: software-development
|
|
8
|
+
platforms: [linux, macos]
|
|
9
|
+
metadata:
|
|
10
|
+
hermes:
|
|
11
|
+
tags: ['task', 'lifecycle', 'recovery', 'edge-case']
|
|
12
|
+
related_skills: ['task-framework', 'browser-screen-record-task']
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Task Lifecycle Edge Cases
|
|
16
|
+
|
|
17
|
+
Recurring edge cases in task lifecycle management that the standard `task-framework` workflow doesn't explicitly cover.
|
|
18
|
+
|
|
19
|
+
## TASK.md Recovery
|
|
20
|
+
|
|
21
|
+
When a task directory exists but `TASK.md` is missing (deleted, never created, or corrupted), reconstruct it from surrounding artifacts.
|
|
22
|
+
|
|
23
|
+
### Step 1: Gather evidence
|
|
24
|
+
|
|
25
|
+
| Source | What it tells you |
|
|
26
|
+
|--------|------------------|
|
|
27
|
+
| `.hermes-task.json` | hash, name, outputs, dependencies |
|
|
28
|
+
| `TASK_MEMORY.md` | last session's state, what was done, what broke, what's next |
|
|
29
|
+
| `input/REQUIREMENTS.md` | original task spec |
|
|
30
|
+
| `output/` | all generated files |
|
|
31
|
+
| `.hermes-task.json` → `outputs` | what was produced and where |
|
|
32
|
+
| Root index (`tasks/index.md`) | aggregated status from last index run |
|
|
33
|
+
|
|
34
|
+
### Step 2: Identify task type and load its governing skill
|
|
35
|
+
|
|
36
|
+
**Critical step.** Do NOT guess phase structure from memory or from other similar tasks.
|
|
37
|
+
|
|
38
|
+
1. Scan directory for type-signature files — `REQUIREMENTS.md` + `RECORDING.md` + `COMPOSITING.md` → video-production task
|
|
39
|
+
2. Load the relevant skill that defines this task type:
|
|
40
|
+
- Video production → `browser-screen-record-task`
|
|
41
|
+
- Document analysis → `task-framework`'s `references/task-types/`
|
|
42
|
+
- Other → the skill used to create the task (check `.hermes-task.json` or TASK_MEMORY.md)
|
|
43
|
+
3. Read the skill's phase/流程/Checklist structure carefully before writing
|
|
44
|
+
|
|
45
|
+
### Step 3: Map evidence to skill's phase structure
|
|
46
|
+
|
|
47
|
+
Cross-reference:
|
|
48
|
+
- Phase directories in `output/` → confirm each matches a skill-defined phase
|
|
49
|
+
- Outputs in `.hermes-task.json` → map each to the right phase's product (note: paths may be `output/...` prefixed for post-migration tasks)
|
|
50
|
+
- TASK_MEMORY.md execution notes → confirm sequence matches skill's defined order
|
|
51
|
+
- Parallel deps → check `和 X 同步进行` annotations match skill's flow table
|
|
52
|
+
|
|
53
|
+
### Step 4: Write TASK.md
|
|
54
|
+
|
|
55
|
+
Follow the skill's template for:
|
|
56
|
+
- `## Skills` — list all skills the task type requires
|
|
57
|
+
- `## Data Flow` — map skill's phase table to actual directory hashes
|
|
58
|
+
- `## Checklist` — use skill's standard phase names and dependency annotations
|
|
59
|
+
- Mark all verified-completed items `[x]`
|
|
60
|
+
- Add `## Notes` for task-specific context
|
|
61
|
+
|
|
62
|
+
Key formatting:
|
|
63
|
+
- Phase dirs use `<short-name>-<hash6>/` (from actual dirs or .hermes-task.json)
|
|
64
|
+
- Checklist `()` uses descriptive phase names only, no hashes
|
|
65
|
+
- `和 Phase X 同步进行` for parallel phases
|
|
66
|
+
- `等待 Phase X 完成` for dependency phases
|
|
67
|
+
|
|
68
|
+
### Step 5: Verify before marking done
|
|
69
|
+
|
|
70
|
+
Only mark phases `[x]` if the output artifact exists and can be verified (stat the file). If a phase was in-progress but incomplete, mark `[ ]` and note in `## Notes`.
|
|
71
|
+
|
|
72
|
+
### Step 6: Update indexes
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python3 ~/.hermes/skills/software-development/task-framework/scripts/update-index.py
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Pitfalls
|
|
79
|
+
|
|
80
|
+
- 🔴 **Do NOT infer phase structure from prior conversation** — load the domain skill that governs this task type and use its defined phases
|
|
81
|
+
- 🔴 **Do NOT invent phases that don't match the skill's template** — subtitle-compositing and timeline-chart-preview are NOT separate phases in the standard video-production skill; they are subsumed by composite
|
|
82
|
+
- 🔴 **Do NOT reorder phases from the skill's defined sequence** — the skill encodes the correct dependency graph (e.g., timeline-composer must run after TTS to know audio durations, but before recording)
|
|
83
|
+
- 🔴 **Always verify material existence** — directory existing ≠ phase completed; check for the actual output file
|
|
84
|
+
- 🔴 **BREAK placement** — keep BREAKs exactly where the skill template places them, not where you guess they should be
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Health Sales Demo (5d5a1a) Recovery
|
|
2
|
+
|
|
3
|
+
Task: `20260605-233355.health-sales-demo-5d5a1a` — 主动健康销售管理系统演示视频
|
|
4
|
+
|
|
5
|
+
## How TASK.md was lost (historical)
|
|
6
|
+
|
|
7
|
+
1. Original TASK.md existed on June 7 with full 76-line checklist
|
|
8
|
+
2. During pipeline bug fixing, exclusion-based cleanup was used (instead of `rm -rf output/`)
|
|
9
|
+
3. TASK.md was accidentally caught in the cleanup
|
|
10
|
+
4. Discovered missing on June 10 — agent noted it was gone but did NOT regenerate
|
|
11
|
+
5. Remained missing until June 11 when user explicitly asked for it
|
|
12
|
+
|
|
13
|
+
## Recovery process (June 11)
|
|
14
|
+
|
|
15
|
+
1. Searched filesystem — no TASK.md found in task directory
|
|
16
|
+
2. Read REQUIREMENTS.md → extracted timeline, language, viewport
|
|
17
|
+
3. Read TASK_MEMORY.md (was already a symlink, content preserved) → knew last state (completed)
|
|
18
|
+
4. Listed directory contents → found all phase dirs (tts-6d3e4c, compositing-6d3e4c, etc.)
|
|
19
|
+
5. Read `.hermes-task.json` → outputs mapping confirmed all phases done
|
|
20
|
+
6. Loaded `browser-screen-record-task` skill → got canonical phase decomposition
|
|
21
|
+
7. Compared first draft against skill's template:
|
|
22
|
+
- Missing Phase 2 (timeline-composer)
|
|
23
|
+
- Wrong phase order (TTS→record→cover→subtitle instead of parallel)
|
|
24
|
+
- Extra phases (subtitle-compositing, timeline-chart-preview are subsumed by composite)
|
|
25
|
+
8. Fixed TASK.md to match skill's phase structure, all items marked [x]
|
|
26
|
+
|
|
27
|
+
## Subsequent migration to symlink + output/ model
|
|
28
|
+
|
|
29
|
+
After recovery, the task was migrated to the new structure:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
tasks/<ts>.<name>-5d5a1a/
|
|
33
|
+
├── TASK.md → ~/.hermes/personal/tasks/5d5a1a/task.md
|
|
34
|
+
├── TASK_MEMORY.md → ~/.hermes/personal/tasks/5d5a1a/memory.md
|
|
35
|
+
├── .hermes-task.json → ~/.hermes/personal/tasks/5d5a1a/meta.json
|
|
36
|
+
├── input/ ← REQUIREMENTS.md + images/
|
|
37
|
+
└── output/ ← all generated files
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Migration commands:
|
|
41
|
+
```bash
|
|
42
|
+
# Create per-hash personal dir + symlinks
|
|
43
|
+
python3 manage_task.py init 5d5a1a
|
|
44
|
+
|
|
45
|
+
# Move user source files to input/
|
|
46
|
+
mkdir -p input && mv REQUIREMENTS.md input/ && mv images/ input/
|
|
47
|
+
|
|
48
|
+
# Move generated files to output/
|
|
49
|
+
mkdir -p output && mv RECORDING.md COMPOSITING.md IMAGE_SLIDESHOW.md output/
|
|
50
|
+
mv tts-*/ image-slideshow-*/ subtitle-gen-*/ browser-video-recording-*/ output/
|
|
51
|
+
|
|
52
|
+
# Verify clean worked
|
|
53
|
+
python3 pipeline.py --clean # only removes output/, leaves input/ + symlinks
|
|
54
|
+
|
|
55
|
+
# Re-export (now clean, no output/ in archive)
|
|
56
|
+
python3 manage_task.py export 5d5a1a
|
|
57
|
+
# → tar.gz contains: input/ + personal-tasks/5d5a1a/ only
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Lessons
|
|
61
|
+
|
|
62
|
+
- When you discover TASK.md is missing: regenerate IMMEDIATELY, don't defer
|
|
63
|
+
- Always verify recovered TASK.md against the governing skill's phase template
|
|
64
|
+
- Symlink for TASK.md + .hermes-task.json prevents permanent loss from cleanup
|
|
65
|
+
- Pipeline cleanup MUST use output/ boundary, never exclusion-based deletion
|
|
66
|
+
- For tasks with a hash, `manage_task.py relink <hash>` is the fastest recovery path
|
|
67
|
+
- `manage_task.py init <hash>` creates all three symlinks in one command
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-lifecycle-portability
|
|
3
|
+
description: Task migration, export/import, and symlink-protection model. Covers tar.gz
|
|
4
|
+
snapshots, double-git repos for metadata + execution history, and manage.py lifecycle
|
|
5
|
+
commands (export/import/rebuild/relink).
|
|
6
|
+
version: 1.0.0
|
|
7
|
+
category: software-development
|
|
8
|
+
platforms:
|
|
9
|
+
- linux
|
|
10
|
+
- macos
|
|
11
|
+
author: Hauzer S. Lee
|
|
12
|
+
license: MIT
|
|
13
|
+
metadata:
|
|
14
|
+
hermes:
|
|
15
|
+
tags:
|
|
16
|
+
- migration
|
|
17
|
+
- software-development
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Task Lifecycle & Portability
|
|
22
|
+
|
|
23
|
+
Manages task portability between machines and protects task metadata from destructive cleanup.
|
|
24
|
+
|
|
25
|
+
## Symlink Protection Model
|
|
26
|
+
|
|
27
|
+
Three core files in each task directory are symlinks to `~/.hermes/personal/tasks/<hash>/`:
|
|
28
|
+
|
|
29
|
+
| Symlink (in task dir) | Target | Content |
|
|
30
|
+
|----------------------|--------|---------|
|
|
31
|
+
| `TASK.md` | `task.md` | Checklist, status, goal |
|
|
32
|
+
| `TASK_MEMORY.md` | `memory.md` | Decision log, per-session notes |
|
|
33
|
+
| `.hermes-task.json` | `meta.json` | Hash, outputs, dependencies |
|
|
34
|
+
|
|
35
|
+
Personal storage layout:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
~/.hermes/personal/tasks/<hash>/
|
|
39
|
+
├── .git/ ← tracks task.md, memory.md, meta.json history
|
|
40
|
+
├── task.md ← TASK.md actual content
|
|
41
|
+
├── memory.md ← TASK_MEMORY.md actual content
|
|
42
|
+
└── meta.json ← .hermes-task.json actual content
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Protection guarantees:**
|
|
46
|
+
- `rm -rf` entire task dir → symlinks break, actual data in `~/.hermes/personal/tasks/<hash>/` remains
|
|
47
|
+
- Pipeline exclusion-based cleanup can't reach symlinked files
|
|
48
|
+
- `manage.py relink <hash>` restores broken symlinks in seconds
|
|
49
|
+
- Each `<hash>/` has independent git repo for metadata change history
|
|
50
|
+
|
|
51
|
+
## Cleanup Discipline
|
|
52
|
+
|
|
53
|
+
**Only `output/` is safe to delete:**
|
|
54
|
+
- `task_reset --hard` = `rm -rf output/` + reset checkbox
|
|
55
|
+
- Never use exclusion-based deletion (`find . -not -name 'X' -delete` or positive-listing `rm -rf tts-*/ RECORDING.md ...`) — these always miss something or catch too much
|
|
56
|
+
- All generated files (RECORDING.md, COMPOSITING.md, IMAGE_SLIDESHOW.md, SUBTITLE_SPEC.md, phase directories, logs) live in `output/`
|
|
57
|
+
|
|
58
|
+
## Directory Boundary
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
tasks/<ts>.<name>-<hash6>/
|
|
62
|
+
├── TASK.md (symlink)
|
|
63
|
+
├── TASK_MEMORY.md (symlink)
|
|
64
|
+
├── .hermes-task.json (symlink)
|
|
65
|
+
├── input/ ← SOURCE: user-provided, NEVER delete
|
|
66
|
+
└── output/ ← GENERATED: pipeline owns this, safe to rm -rf
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Export / Import / Rebuild
|
|
70
|
+
|
|
71
|
+
`manage.py <hash>` commands:
|
|
72
|
+
|
|
73
|
+
| Command | What it does |
|
|
74
|
+
|---------|-------------|
|
|
75
|
+
| `export <hash>` | Follow symlinks, resolve to real content, produce `tasks/<ts>.<name>-<hash>.tar.gz` (excludes `output/`, includes `input/` + symlink targets + both `.git/` dirs) |
|
|
76
|
+
| `import <tar.gz>` | Extract to `tasks/` directory, create `~/.hermes/personal/tasks/<hash>/` if needed, restore all symlinks |
|
|
77
|
+
| `rebuild <hash>` | Find latest `<hash>.tar.gz` in `tasks/`, extract, restore symlinks. Generates semantic directory name from TASK.md title |
|
|
78
|
+
| `relink <hash>` | Recreate broken symlinks for an existing task directory (no tarball needed) |
|
|
79
|
+
|
|
80
|
+
**Actual script path:**
|
|
81
|
+
`~/.hermes/skills/software-development/task-framework/scripts/manage_task.py`
|
|
82
|
+
|
|
83
|
+
Usage: `python3 <path> <command> <arg>`
|
|
84
|
+
|
|
85
|
+
**Export behavior:**
|
|
86
|
+
- `tar --dereference` — follows symlinks, archive contains actual content, not symlink paths
|
|
87
|
+
- Output dir is excluded (it's regenerable via pipeline)
|
|
88
|
+
- Both git repos (`tasks/<dir>/.git/` and `~/.hermes/personal/tasks/<hash>/.git/`) are preserved in the tarball
|
|
89
|
+
- Import restores both git histories independently
|
|
90
|
+
|
|
91
|
+
**Cross-machine strategy:**
|
|
92
|
+
- No automatic merge — each machine's copy is an independent task
|
|
93
|
+
- To combine work from two machines: create a new task referencing both via `ref:<hash>/...`
|
|
94
|
+
- `tar.gz` in git repo acts as backup even if personal `~/.hermes/personal/tasks/` is not pushed
|
|
95
|
+
|
|
96
|
+
## Old Tasks (Migration)
|
|
97
|
+
|
|
98
|
+
- Tasks already in `input/` + `output/` model: add symlinks via `manage.py init`
|
|
99
|
+
- Legacy flat tasks (REQUIREMENTS.md + images/ at root, no input/output): migrate if feasible; abandon if too old
|
|
100
|
+
- When you discover TASK.md is missing: **regenerate immediately from TASK_MEMORY.md + directory artifacts**, do not skip or defer
|
|
101
|
+
|
|
102
|
+
## TASK.md Recovery (Edge Cases)
|
|
103
|
+
|
|
104
|
+
See `references/task-recovery-procedure.md` for the full step-by-step TASK.md recovery process when the file is missing or corrupted. Key principles:
|
|
105
|
+
|
|
106
|
+
1. **Check the symlink target first** — if the hash-based personal backup exists, recovery is instant via `manage_task.py relink <hash>`
|
|
107
|
+
2. **Identify task type** — scan for signature files (REQUIREMENTS.md + RECORDING.md etc.) and load the task-type's governing skill for its defined phase structure
|
|
108
|
+
3. **Map evidence to phases** — cross-reference output directories, `.hermes-task.json` outputs, and TASK_MEMORY.md against the skill's phase templates
|
|
109
|
+
4. **Write TASK.md** following the skill's template — don't invent phases, don't reorder
|
|
110
|
+
|
|
111
|
+
🔴 Critical: Do NOT guess phase structure from memory. Load the domain skill that governs this task type and use its defined phases. Verify each phase's output file exists before marking complete.
|
|
112
|
+
|
|
113
|
+
**Real-world example:** `references/task-recovery-5d5a1a-example.md` — step-by-step walkthrough of recovering 5d5a1a's TASK.md after it was lost to exclusion-based cleanup.
|
|
114
|
+
|
|
115
|
+
## Pipeline Output Model (Design)
|
|
116
|
+
|
|
117
|
+
See `references/output-model-design.md` for the full 2026-06-11 design rationale. The core decisions:
|
|
118
|
+
|
|
119
|
+
- **Output isolation**: All generated artifacts in `output/`, user materials in `input/`
|
|
120
|
+
- **Cleanup strategy**: `rm -rf output/` — never exclusion-based deletion
|
|
121
|
+
- **Symlink protection**: Three metadata files symlinked to personal storage `~/.hermes/personal/tasks/<hash>/`
|
|
122
|
+
- **Manage tool**: `manage_task.py` with `init/export/import/rebuild/relink/reindex` commands
|
|
123
|
+
|
|
124
|
+
**Implementation:** `references/pipeline-output-transition.md` — detailed diff of what changed in pipeline.py to adopt the output/ model.
|
|
125
|
+
|
|
126
|
+
## Pitfalls
|
|
127
|
+
| `ref:` resolution breaks when hash-only (no directory match) | `.hermes-task.json` outputs should use `ref:hash/output_name` format. Directory must contain the hash in its name for `ref:` glob resolution. |
|
|
128
|
+
| Pipeline writes spec files to task root instead of output/ | All generated specs (RECORDING.md, COMPOSITING.md, IMAGE_SLIDESHOW.md, SUBTITLE_SPEC.md) belong in `output/`. Update pipeline scripts if they write to root. |
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Session Design Notes: Task Portability
|
|
2
|
+
|
|
3
|
+
From a session with Hauzer on 2026-06-11 discussing TASK.md loss and recovery architecture.
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Pipeline exclusion-based deletion (`rm -rf tts-*/ RECORDING.md ...` or `find . -not -name 'REQUIREMENTS.md' -delete`) destroyed TASK.md because it was a regular file in the task root. User observed: "你那次是排除式删除,把除了 REQUIREMENTS.md 之外的很多有用的文件都删光了."
|
|
8
|
+
|
|
9
|
+
## Solution Architecture (agreed design)
|
|
10
|
+
|
|
11
|
+
Three-layer protection:
|
|
12
|
+
|
|
13
|
+
1. **Directory isolation** — input/ (source) + output/ (generated) boundary. Pipeline only touches output/. Never exclusion-based deletion at task root.
|
|
14
|
+
|
|
15
|
+
2. **Symlink protection** — TASK.md, TASK_MEMORY.md, .hermes-task.json all symlink to `~/.hermes/personal/tasks/<hash>/`. rm -rf task dir only breaks symlinks; actual content survives.
|
|
16
|
+
|
|
17
|
+
3. **Tar.gz portability** — `manage.py export` creates self-contained snapshot with resolved content (no symlinks in archive), both git repos preserved. `import` restores symlinks on target machine.
|
|
18
|
+
|
|
19
|
+
## Key Decisions
|
|
20
|
+
|
|
21
|
+
- output/ excluded from tar.gz (regenerable)
|
|
22
|
+
- tar.gz committed to git (storage cost accepted)
|
|
23
|
+
- Cross-machine: no merge, independent tasks. Manual fusion via ref: links.
|
|
24
|
+
- `~/.hermes/personal/tasks/<hash>/` git can bare-run (no upstream). Metadata tracking > remote collaboration.
|
|
25
|
+
- Old tasks: migrate if feasible, abandon if not.
|
|
26
|
+
|
|
27
|
+
## Manage.py Split
|
|
28
|
+
|
|
29
|
+
| Responsibility | Skill | Commands |
|
|
30
|
+
|---|---|---|
|
|
31
|
+
| Note-taking (observer, file-agnostic) | task-memory | append, read |
|
|
32
|
+
| Lifecycle (orchestrator, manages locations) | task-framework | init, relink, export, import, rebuild, reindex |
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Pipeline Output Model Design (resolved 2026-06-11)
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
Pipeline cleanup using exclusion-based deletion (`find ... -not -name X -exec rm`) or positive-listing always either misses cleanup targets or accidentally deletes user files. Task-framework metadata files (TASK.md, TASK_MEMORY.md, .hermes-task.json) mixed with pipeline artifacts in the same directory — cleanup couldn't distinguish them.
|
|
6
|
+
|
|
7
|
+
## Solution
|
|
8
|
+
|
|
9
|
+
### Directory isolation
|
|
10
|
+
|
|
11
|
+
All generated artifacts go into `output/` subdirectory; `input/` holds user source materials:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
tasks/<ts>.<name>-<hash6>/
|
|
15
|
+
├── TASK.md → symlink to ~/.hermes/personal/tasks/<hash>/task.md
|
|
16
|
+
├── TASK_MEMORY.md → symlink
|
|
17
|
+
├── .hermes-task.json → symlink
|
|
18
|
+
├── input/
|
|
19
|
+
│ ├── REQUIREMENTS.md
|
|
20
|
+
│ └── images/
|
|
21
|
+
└── output/
|
|
22
|
+
├── tts-<hash6>/
|
|
23
|
+
├── RECORDING.md
|
|
24
|
+
├── COMPOSITING.md
|
|
25
|
+
└── compositing-<hash6>/output.mp4
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Cleanup strategy
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# pipeline.py --clean
|
|
32
|
+
rm -rf output/
|
|
33
|
+
|
|
34
|
+
# task_reset --hard
|
|
35
|
+
rm -rf output/ + reset checkboxes
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Both entry points clean `output/` uniformly, never conflicting.
|
|
39
|
+
|
|
40
|
+
### Symlink protection
|
|
41
|
+
|
|
42
|
+
Three core files all symlinked to `~/.hermes/personal/tasks/<hash>/`. A `rm -rf` of the entire task directory only removes symlinks — actual data is preserved.
|
|
43
|
+
|
|
44
|
+
### Manage tool
|
|
45
|
+
|
|
46
|
+
`manage_task.py` commands (in task-framework skill's `scripts/`):
|
|
47
|
+
|
|
48
|
+
| Command | Action |
|
|
49
|
+
|---------|--------|
|
|
50
|
+
| `init <hash>` | Create per-hash directory + three symlinks |
|
|
51
|
+
| `export <hash>` | Package tar.gz (dereference symlinks) |
|
|
52
|
+
| `import <file>` | Restore from tar.gz |
|
|
53
|
+
| `rebuild <hash>` | Find hash tar.gz and import |
|
|
54
|
+
| `relink <hash>` | Rebuild symlinks |
|
|
55
|
+
| `reindex` | Rebuild ~/.hermes/personal/tasks/index.md |
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Pipeline Output Model Transition (2026-06-11)
|
|
2
|
+
|
|
3
|
+
## What Changed
|
|
4
|
+
|
|
5
|
+
`video-production-pipeline/scripts/pipeline.py` was updated to write all generated files to `output/` instead of the task root.
|
|
6
|
+
|
|
7
|
+
| Before | After |
|
|
8
|
+
|--------|-------|
|
|
9
|
+
| RECORDING.md at root | output/RECORDING.md |
|
|
10
|
+
| COMPOSITING.md at root | output/COMPOSITING.md |
|
|
11
|
+
| IMAGE_SLIDESHOW.md at root | output/IMAGE_SLIDESHOW.md |
|
|
12
|
+
| SUBTITLE_SPEC.md at root | output/SUBTITLE_SPEC.md |
|
|
13
|
+
| tts-<hash6>/ at root | output/tts-<hash6>/ |
|
|
14
|
+
| image-slideshow-* at root | output/image-slideshow-*/ |
|
|
15
|
+
| subtitle-gen-* at root | output/subtitle-gen-*/ |
|
|
16
|
+
| browser-video-recording-* at root | output/browser-video-recording-*/ |
|
|
17
|
+
| compositing-* at root | output/compositing-*/ |
|
|
18
|
+
| REQUIREMENTS.md at root | input/REQUIREMENTS.md (fallback to root) |
|
|
19
|
+
| `--clean` positive-list | `rm -rf output/` |
|
|
20
|
+
|
|
21
|
+
## Why
|
|
22
|
+
|
|
23
|
+
Exclusion-based deletion (`find . -not -name 'REQUIREMENTS.md' -delete` or positive-listing `rm -rf tts-*/ RECORDING.md ...`) destroyed TASK.md because it was a regular file in the task root. The structural fix: put all generated files in a single `output/` directory, then cleaning is simply `rm -rf output/`.
|
|
24
|
+
|
|
25
|
+
## Migration
|
|
26
|
+
|
|
27
|
+
Existing pipeline tasks were migrated manually:
|
|
28
|
+
1. Created `input/` + `output/` dirs
|
|
29
|
+
2. Moved REQUIREMENTS.md + images/ into `input/`
|
|
30
|
+
3. Moved all generated specs and phase dirs into `output/`
|
|
31
|
+
4. TASK.md, TASK_MEMORY.md, .hermes-task.json converted to symlinks
|
|
32
|
+
|
|
33
|
+
## TTS cache location
|
|
34
|
+
|
|
35
|
+
TTS cache scanning now looks in `output/` directory for existing tts-* dirs:
|
|
36
|
+
- Old: `os.listdir(TASK_DIR)` → look for tts-* at root
|
|
37
|
+
- New: `os.listdir(os.path.join(TASK_DIR, OUTPUT_DIR))` → look for tts-* under output/
|
|
38
|
+
|
|
39
|
+
## script path
|
|
40
|
+
|
|
41
|
+
`~/.hermes/skills/media/video-production-pipeline/scripts/pipeline.py`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# TASK.md Recovery: 5d5a1a (2026-06-11)
|
|
2
|
+
|
|
3
|
+
## Scenario
|
|
4
|
+
|
|
5
|
+
Agent asked to find TASK.md for task `5d5a1a`. It didn't exist — deleted weeks earlier by exclusion-based cleanup. Only TASK_MEMORY.md and the phase directories remained.
|
|
6
|
+
|
|
7
|
+
## Process
|
|
8
|
+
|
|
9
|
+
1. **Identify task type** — scanned task directory. Found REQUIREMENTS.md + RECORDING.md + COMPOSITING.md → video production pipeline task.
|
|
10
|
+
2. **Load governing skill** — `skill_view('browser-screen-record-task')` for the phase template.
|
|
11
|
+
3. **Map evidence to phases** — cross-referenced phase directories (tts-6d3e4c/, browser-video-recording-6d3e4c/, compositing-6d3e4c/) against the skill's phase table.
|
|
12
|
+
4. **Read TASK_MEMORY.md** — confirmed final output: 174.1s / 5.8MB, all phases complete.
|
|
13
|
+
5. **Write TASK.md** following skill's template — used the correct phase decomposition (1a/b/c parallel → 2 timeline-composer → 3 recording → 4 composite), matching `browser-screen-record-task`'s defined phases.
|
|
14
|
+
6. **Verify** — checked output files exist, status = completed.
|
|
15
|
+
|
|
16
|
+
## Key Lesson
|
|
17
|
+
|
|
18
|
+
Phase structure must come from the domain skill, not guessed from memory or prior conversation. When I initially wrote the TASK.md, I used my own phase order (missing timeline-composer, wrong parallel grouping). The user corrected: "你可以参考 browser-screen-record-task 这个 skill,看看你的 phases 分解的对不对".
|
|
19
|
+
|
|
20
|
+
## Recovery from symlink protection (future)
|
|
21
|
+
|
|
22
|
+
If TASK.md was a symlink and the personal backup exists:
|
|
23
|
+
```bash
|
|
24
|
+
# Instant recovery:
|
|
25
|
+
manage_task.py relink 5d5a1a
|
|
26
|
+
```
|
|
27
|
+
No content reconstruction needed — the symlink target `~/.hermes/personal/tasks/5d5a1a/task.md` still has the content.
|