From 7c8c606d73fbf9375275f23e2dc43c9c6c804935 Mon Sep 17 00:00:00 2001 From: laptran Date: Sat, 13 Jun 2026 21:37:13 -0400 Subject: [PATCH] Consolidate task location: always .automaton/tasks/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both framework and project tasks now live in .automaton/tasks/. Every project has a .automaton/ directory, so no need for special scope-based path logic. Removed the dual-convention gap. Changes: - Dashboard _find_tasks_dir() and _get_review_path() always use {root}/.automaton/tasks/ — no scope branching - migrate-project.sh: moves tasks/ -> .automaton/tasks/ during upgrade - Orchestrator prompt: all task path references updated to {project}/.automaton/tasks/ --- .../ui/__pycache__/app.cpython-314.pyc | Bin 23201 -> 22621 bytes automaton/dashboard/ui/app.py | 29 ++-------------- prompts/orchestrate.md | 32 ++++++------------ scripts/migrate-project.sh | 22 ++++++------ 4 files changed, 25 insertions(+), 58 deletions(-) diff --git a/automaton/dashboard/ui/__pycache__/app.cpython-314.pyc b/automaton/dashboard/ui/__pycache__/app.cpython-314.pyc index 2aa5b2674ac1cf41e867311f0d8fe97ab4989ee4..810391ef7ee784963ebd0e0c612f219360ca5ad6 100644 GIT binary patch delta 1986 zcmZXVeN0nV6u{qk{h;O3I8c-#g<5Q()Z(a4M28}10b9NXGLr#;s#T!uwhlozNl+(Q z%yegem@RQLn+p!y(3d7#G8dL@iPLPWOB&saV@o#Yh;Dnn{7jm7 zk&h}t&`s5Ip--q6Y6M%{J)`uEPV=WvL?{#@A4{DyR9mwBd5KDjT~H&OL8-zA3Yfb z@Pf2Iqe=zaFp{+#_F}v#2Rd+t$pi;+i>VP_ltQL7O+3|SRmq~=!JfsdmTEYQcI&$I zS!Gkk|5uHE>jIZGIl9yWvfp2?tnT#g4$fXRnRgR<2)%@dvZdtjAw8|es*f(jMv7(v zZ3Lq&k=1lo>^b`0ht8b!FeLTn+ypp}+id6HRW#>$xV$SbY`e&P0w#_-eZsV$*3`kI z!c(RRWjwR^z2s1CREObJY3f1li&bVL4%-hF^Hx#0**2Po=H+{<9O`Ramg8zl>s_S{ zO=Ygi(tDeJzt#ZnHz7M4(;cg{N(sgIl*44al9YC=J(83&*bqr780m>56^|BNE~&j# zQX5IC4eM(0nxhO}L0kSZxFj{@_p9J}{Jx-e&YM(0IpGjt0Lz@~=A0sNh%ihzjK`b? z*n=Ubg$Iv0WzZ@f!}#F}8(f!ethfq1-T9Rct&%Q;|E|n}0n9ASO^G^EmUjpt!n@c~ zSOVAaWZ_RBq0w#@Cj|_xIKmNXUl*GNbuDIx4wfl<;MeaRXT&>!tIDSSh7dYIEqIqBFl_0_nLOF*V z;|qEjzr3>6r(EJ_eM9tzaa)vrS1*AkT^zKjh8V$bF**p?nW16pv$!%j^Ykk&2SE}JiAg^gpu1>CLxQkgzyG|nvE$ZrtJ7AeP$CL zC0Gc`6A*)&Ycjs^qUR`D=hJJ0iumR9glLwHL&7nWKG<-c=OsAKGh}N1;eJl(OOFM_ zy8`}E)xDbBWlb0Jbp~g$P^v^i0-5w%nZKiHtFPV5fNzskJ?ohI@pN?wIB~N2ER5gJ zzgD_X17aLSwsd&A{cIr!}U|5!J)@IlE7XbO?1Wc?(?;LsCWV67BW{|IOkNc|wA z2g@3c>t@*U{yfO~c7JP2`*wdI=sS|ZRdMWRGXFyO zl`uh=B&aA?EMXoYjc|($w+X*<1h`5lW_k1-dxuD{;@IXj35t{v%@js!wiaz0IC20 delta 2537 zcmai#e@t6d6vyva%_z9Fu_S-^v5ihlq>oFF8`XxeGu2^lC_~2qwinDlw6&fgPV{=RUi z+aC_~X!@{U?Nptzme>|zic4(^^(tZlYrt8Cpy~?*nbW{pc{d)3`ZT?og^qlZzb6n3 z_``~-(O%WSee$yT11r+2hK}&wF!|P4^vNlNcIdF*Ku2KGzLu)+zCD-T4>9{5IuU8evFgp3qf@6z zu3~ls?##c1z6o#T*9)1+9M^c>MQ#uEKu6>-d!W zs5XvYhsi~fgj(h}ksHI;$<;5XohlF(7~E);YA8&kr$a^2Ly}j5v7#yqmz~b5;|j{+ z3X;9@0?C+$yz&CQ=oaZk*T~v0XY7>YMS6xCw4|0=ue)}aug>kM6|&&c5+AY<-CGeo zh!BUW=g315up-{lEWUk8*vFK+f(m50oCdMKBj9o62EH<1cdRmM&Y#MUnoFkLQS+uZ zC&UnxYr6c@i;$01i#Z{QW9QM(hc-E%wC@ zgRj)VV~&<;)F39#Kzmss{X6nV*=Llm!Zs8e#44v@YC{ekhxaxVERQ>4@Rty$5vL%j zd<*>>n##YUuRy45jbxIXrps6fxE3dQga#);20)5vT^ ziqK{P+Jz23!YK(01dgX)Gqi>Jdcvw^L;>JkAtWf`s|nme+?Ls}siu@# zBX`y)55P0~R_=aMeqJ(*9D@8aEqB!CviNQ>LBM(d6wJH3!JFg>#)R@FuKRRHi+mXoS zD>QsD1CF}CrPUB>n4)L-_s)j$ZC1J^vSXV>m;Zx73D25QL@Rc?q0^ITUN{2aDWO~7 zjOStcG>>4{OWfHLIdrpwN-~A1#{N`v*Yv$C*b}}SrKD+Ps)0LlZ7;lrZ)@5@J#f5f zhF-W5Yj9sJDxILI@}2`xwD+^1#M^XkeH4ME-C;9#|=4kjhd+|)aLJM52@kc zgEnqTVn5>WJmM$B&xl_TMvRq$$UxW-zu~~|h(9<~ZX(rjMJN7+Rzx*`w`vKtoVIwR zbMS@tRz2(BCaB%D#x1g3g8s>fVGd313aVjE?o{@x@n43hi3oZDor)3SPBd0_hXQ?F hN);=B$gZ)h+hn?AHhtYs1REC*869->JmC;i{snoDdx8J} diff --git a/automaton/dashboard/ui/app.py b/automaton/dashboard/ui/app.py index c823f6e..da28e5e 100644 --- a/automaton/dashboard/ui/app.py +++ b/automaton/dashboard/ui/app.py @@ -106,24 +106,8 @@ class DashboardHandler(SimpleHTTPRequestHandler): @staticmethod def _find_tasks_dir(project_root: Path) -> Path | None: - from ..core.scope import detect_scope - _, scope = detect_scope() - # Framework mode: tasks live inside .automaton/ - # Project mode: tasks live at project root, .automaton/tasks/ may be a symlink - if scope == "framework": - candidates = [ - project_root / ".automaton" / "tasks", - project_root / "tasks", - ] - else: - candidates = [ - project_root / "tasks", - project_root / ".automaton" / "tasks", - ] - for p in candidates: - if p.exists() and p.is_dir(): - return p - return candidates[0] + tasks_dir = project_root / ".automaton" / "tasks" + return tasks_dir if tasks_dir.exists() else tasks_dir def _serve_tasks(self): project_root = find_automaton_root() @@ -220,14 +204,7 @@ class DashboardHandler(SimpleHTTPRequestHandler): project_root = find_automaton_root() if not project_root or not self._validate_task_name(task_name): return None - candidates = [ - project_root / ".automaton" / "tasks" / task_name / self.REVIEW_FILE, - project_root / "tasks" / task_name / self.REVIEW_FILE, - ] - for p in candidates: - if p.exists(): - return p - return candidates[0] + return project_root / ".automaton" / "tasks" / task_name / self.REVIEW_FILE def _get_review_status(self, task_name: str) -> dict: review_path = self._get_review_path(task_name) diff --git a/prompts/orchestrate.md b/prompts/orchestrate.md index 657aca4..cd9c7ec 100644 --- a/prompts/orchestrate.md +++ b/prompts/orchestrate.md @@ -19,7 +19,7 @@ Specifically: 7. {project}/.automaton/extensions/contracts/*.md — Additive extensions loaded after global contracts 8. ~/.automaton/scripts/*.sh — Always from global framework 9. {project}/.automaton/extensions/scripts/*.sh — Additive extensions loaded before global scripts (pre-processing) -10. {project}/tasks/ — Project tasks (project mode) or ~/.automaton/tasks/ (framework mode) +10. {project}/.automaton/tasks/ — Task folders (both framework and project mode) ## VRAM Detection @@ -193,7 +193,7 @@ while task is not in terminal state: #### Continue from existing tasks If the user says "orchestrate" or "continue" with no new task description, the Orchestrator should: -1. Scan all tasks in the tasks/ directory, including sub-task folders under `tasks/{parent-task}/subtasks/` +1. Scan all tasks in `{project}/.automaton/tasks/`, including sub-task folders under `{project}/.automaton/tasks/{parent-task}/subtasks/` 2. Find the most advanced task (the one closest to completion) — **prioritize sub-tasks over parent tasks** (because the parent depends on the sub-tasks) 3. When choosing among sub-tasks in the same wave, prioritize those in later phases (e.g., Bug Find over Research) because they are closer to completion 4. Drive that task through the remaining phases @@ -201,10 +201,7 @@ If the user says "orchestrate" or "continue" with no new task description, the O #### New tasks from user input If {task-description} contains a description for a NEW task, the Orchestrator MUST: 1. Generate a kebab-case task name from the description (e.g., "add user auth" → `add-user-auth`) -2. Create the task folder. Task location depends on scope: - - **Framework mode** (inside ~/.automaton/): `~/.automaton/tasks/{task-name}/` - - **Project mode** (in a user project): `{project}/tasks/{task-name}/` - The dashboard reads from both locations automatically. +2. Create the task folder at `{project}/.automaton/tasks/{task-name}/` (empty — no artifact files) 3. **Immediately drive it to completion** using the auto-execution loop Note: `IMPLEMENTATION.md` is the artifact produced by the implementation phase, not the Orchestrator. Do not pre-create it. **Do not create an empty `IMPLEMENTATION.md` for new tasks** — this is inconsistent with the Orchestrator's own rule and can confuse the Research phase. @@ -240,9 +237,7 @@ In manual mode, the Orchestrator only **reports** the current state and the next ## State Determination -Examine the tasks/ directory and determine the state of each task folder. The tasks directory is: -- **Framework mode**: `~/.automaton/tasks/` -- **Project mode**: `{project}/tasks/` Check from the most advanced state backward. **Important**: Always check that artifacts are non-empty before considering them as indicators of task state. +Examine the `{project}/.automaton/tasks/` directory and determine the state of each task folder. Check from the most advanced state backward. **Important**: Always check that artifacts are non-empty before considering them as indicators of task state. 1. Has `VERDICT.md` with `PASS` (non-empty) → **Complete** 2. Has `VERDICT.md` with `NEEDS_REVIEW` or `FAIL` (non-empty) → **Human Intervention** @@ -325,23 +320,16 @@ When a parent task has a `DECOMPOSITION.md` file, the Orchestrator must create s ### Sub-Task Folder Structure -When decomposing, the Orchestrator creates sub-tasks under the parent task's `subtasks/` folder: +When decomposing, the Orchestrator creates sub-tasks under the parent task's `subtasks/` folder inside `{project}/.automaton/tasks/`: ``` -tasks/parent-task/ → Parent task (Research → Decomposition → complete) +{project}/.automaton/tasks/parent-task/ → Parent task (Research → Decomposition → complete) SPEC.md DECOMPOSITION.md subtasks/ - subtask-a/ → Sub-task (full lifecycle independently) - SPEC.md - DESIGN.md - IMPLEMENTATION.md - BUG_REPORT.md - ADVERSARIAL_BUG_REPORT.md - DOC_REVIEW.md - VERDICT.md + subtask-a/ → Sub-task (full lifecycle independently) + ... subtask-b/ - SPEC.md ... ``` @@ -356,11 +344,11 @@ When a parent task reaches the **Decomposition** phase (has `SPEC.md` and `DECOM 5. **Verify VRAM constraints**: - For each sub-task, check if its estimated peak context (from DECOMPOSITION.md) is within the max peak context. - If a sub-task exceeds the VRAM limit, create a warning and suggest splitting it further. -6. **Check for existing sub-task folders**: For each sub-task, check if the folder `tasks/{parent-task-name}/subtasks/{sub-task-name}/` already exists. If it does, skip the creation and report that the sub-task has already been created. This prevents duplicate sub-tasks when the Orchestrator is invoked multiple times. +6. **Check for existing sub-task folders**: For each sub-task, check if the folder `{project}/.automaton/tasks/{parent-task-name}/subtasks/{sub-task-name}/` already exists. If it does, skip the creation and report that the sub-task has already been created. This prevents duplicate sub-tasks when the Orchestrator is invoked multiple times. 7. **Check for DECOMPOSITION.md updates**: Compare the DECOMPOSITION.md with the existing sub-task folders. If the DECOMPOSITION.md has been updated (new sub-tasks added or existing sub-tasks removed), update the sub-task folders accordingly: - For new sub-tasks, create the folders. - For removed sub-tasks, report the orphaned sub-tasks and delete the folders. -8. **Create sub-task folders** under `tasks/{parent-task-name}/subtasks/{sub-task-name}/`: +8. **Create sub-task folders** under `{project}/.automaton/tasks/{parent-task-name}/subtasks/{sub-task-name}/`: - For each sub-task, create an empty folder (no files yet — the sub-task starts at the Research phase). - The sub-task name should be a kebab-case version of the sub-task description from DECOMPOSITION.md. 9. **Create a VRAM_CONFIG.md** file for each sub-task with the VRAM configuration (see VRAM Config Propagation section). diff --git a/scripts/migrate-project.sh b/scripts/migrate-project.sh index 7554bb7..5982eb7 100755 --- a/scripts/migrate-project.sh +++ b/scripts/migrate-project.sh @@ -107,16 +107,18 @@ while IFS= read -r -d '' project_file; do fi done < <(find "$PROJECT_AUTOMATON" -maxdepth 1 -type f -name "*.md" -o -name "*.sh" -print0 2>/dev/null || true) -# Task directory — old model stored tasks in .automaton/tasks/, new model uses tasks/ -OLD_TASKS="$PROJECT_AUTOMATON/tasks" -NEW_TASKS="$PROJECT_DIR/tasks" -if [ -d "$OLD_TASKS" ] && [ ! -e "$NEW_TASKS" ]; then - echo " MOVING .automaton/tasks/ → tasks/ (tasks should be at project root)" - mv "$OLD_TASKS" "$NEW_TASKS" - MOVED+=(".automaton/tasks/ -> tasks/") -elif [ -d "$OLD_TASKS" ] && [ -d "$NEW_TASKS" ]; then - echo " NOTICE Both .automaton/tasks/ and tasks/ exist — keeping both" - KEPT+=(".automaton/tasks/ and tasks/") +# Task directory — tasks always live in .automaton/tasks/ for all modes +ROOT_TASKS="$PROJECT_DIR/tasks" +AUTOMATON_TASKS="$PROJECT_AUTOMATON/tasks" +if [ -d "$ROOT_TASKS" ] && [ ! -e "$AUTOMATON_TASKS" ]; then + echo " MOVING tasks/ → .automaton/tasks/ (tasks belong in .automaton/)" + mv "$ROOT_TASKS" "$AUTOMATON_TASKS" + MOVED+=("tasks/ -> .automaton/tasks/") +elif [ -d "$ROOT_TASKS" ] && [ -d "$AUTOMATON_TASKS" ]; then + echo " MERGING tasks/ contents into .automaton/tasks/" + cp -r "$ROOT_TASKS"/* "$AUTOMATON_TASKS/" 2>/dev/null || true + rm -rf "$ROOT_TASKS" + MOVED+=("tasks/ merged -> .automaton/tasks/") fi # Report