@pennixrv/trellis 0.7.1-beta.1 → 0.7.1-beta.11
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.
- package/README.md +10 -7
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/ablate.d.ts.map +1 -1
- package/dist/commands/ablate.js +2 -0
- package/dist/commands/ablate.js.map +1 -1
- package/dist/commands/backup.d.ts +4 -0
- package/dist/commands/backup.d.ts.map +1 -0
- package/dist/commands/backup.js +75 -0
- package/dist/commands/backup.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +687 -676
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/mem.d.ts.map +1 -1
- package/dist/commands/mem.js +49 -7
- package/dist/commands/mem.js.map +1 -1
- package/dist/commands/task-progress.d.ts +1 -0
- package/dist/commands/task-progress.d.ts.map +1 -1
- package/dist/commands/task-progress.js +67 -25
- package/dist/commands/task-progress.js.map +1 -1
- package/dist/commands/teamwork/spawn.d.ts.map +1 -1
- package/dist/commands/teamwork/spawn.js +5 -2
- package/dist/commands/teamwork/spawn.js.map +1 -1
- package/dist/commands/teamwork/teammate-adapters.d.ts +0 -2
- package/dist/commands/teamwork/teammate-adapters.d.ts.map +1 -1
- package/dist/commands/teamwork/teammate-adapters.js +0 -2
- package/dist/commands/teamwork/teammate-adapters.js.map +1 -1
- package/dist/commands/teamwork/teammate-launch.d.ts +1 -2
- package/dist/commands/teamwork/teammate-launch.d.ts.map +1 -1
- package/dist/commands/teamwork/teammate-launch.js +0 -2
- package/dist/commands/teamwork/teammate-launch.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +92 -84
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +589 -591
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/workflow.d.ts.map +1 -1
- package/dist/commands/workflow.js +94 -89
- package/dist/commands/workflow.js.map +1 -1
- package/dist/configurators/shared.js +2 -2
- package/dist/configurators/shared.js.map +1 -1
- package/dist/constants/paths.d.ts +3 -10
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +4 -13
- package/dist/constants/paths.js.map +1 -1
- package/dist/migrations/manifests/0.7.1-beta.10.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.11.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.2.json +522 -0
- package/dist/migrations/manifests/0.7.1-beta.3.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.4.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.5.json +9 -0
- package/dist/migrations/manifests/0.7.1-beta.6.json +10 -0
- package/dist/migrations/manifests/0.7.1-beta.7.json +8 -0
- package/dist/migrations/manifests/0.7.1-beta.8.json +481 -0
- package/dist/migrations/manifests/0.7.1-beta.9.json +191 -0
- package/dist/templates/claude/hooks/statusline.py +9 -4
- package/dist/templates/common/bundled-skills/trellis-decision-space-construction-and-merging/workflow.md +2 -0
- package/dist/templates/common/bundled-skills/trellis-goal-exploration-and-requirements/workflow.md +2 -0
- package/dist/templates/common/bundled-skills/trellis-meta/SKILL.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/add-project-local-conventions.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-context-loading.md +5 -5
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/change-task-lifecycle.md +3 -2
- package/dist/templates/common/bundled-skills/trellis-meta/references/customize-local/overview.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/context-injection.md +1 -1
- package/dist/templates/common/bundled-skills/trellis-meta/references/local-architecture/task-system.md +8 -2
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/cli-quick-reference.md +2 -1
- package/dist/templates/common/bundled-skills/trellis-session-insight/references/triggering-patterns.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-solution-design-and-convergence/workflow.md +4 -0
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/mcp-setup.md +4 -2
- package/dist/templates/common/bundled-skills/trellis-spec-bootstrap/references/spec-task-planning.md +2 -2
- package/dist/templates/common/bundled-skills/trellis-teamwork/SKILL.md +7 -2
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/coordination-modes.md +3 -1
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-configuration-matrix.md +11 -6
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teammate-processes.md +50 -6
- package/dist/templates/common/bundled-skills/trellis-teamwork/references/teamwork-plan-construction.md +49 -9
- package/dist/templates/common/commands/start.md +2 -2
- package/dist/templates/common/skills/before-dev.md +1 -4
- package/dist/templates/common/skills/break-loop.md +7 -18
- package/dist/templates/common/skills/check.md +7 -11
- package/dist/templates/common/skills/{brainstorm.md → request-intake.md} +5 -3
- package/dist/templates/common/skills/update-spec.md +5 -7
- package/dist/templates/omp/extensions/trellis/index.ts.txt +1 -1
- package/dist/templates/opencode/lib/session-utils.js +301 -209
- package/dist/templates/snow/SNOW.md +1 -1
- package/dist/templates/trellis/agents/teammate.md +16 -5
- package/dist/templates/trellis/index.d.ts +3 -0
- package/dist/templates/trellis/index.d.ts.map +1 -1
- package/dist/templates/trellis/index.js +6 -0
- package/dist/templates/trellis/index.js.map +1 -1
- package/dist/templates/trellis/scripts/add_session.py +1 -1
- package/dist/templates/trellis/scripts/common/active_task.py +3 -47
- package/dist/templates/trellis/scripts/common/paths.py +9 -25
- package/dist/templates/trellis/scripts/common/safe_commit.py +8 -8
- package/dist/templates/trellis/scripts/common/task_context.py +11 -51
- package/dist/templates/trellis/scripts/common/task_continuity.py +63 -64
- package/dist/templates/trellis/scripts/common/task_layout.py +80 -0
- package/dist/templates/trellis/scripts/common/task_move.py +345 -0
- package/dist/templates/trellis/scripts/common/task_store.py +28 -55
- package/dist/templates/trellis/scripts/common/task_utils.py +23 -19
- package/dist/templates/trellis/scripts/common/task_workflow.py +72 -31
- package/dist/templates/trellis/scripts/common/tasks.py +6 -5
- package/dist/templates/trellis/scripts/hooks/linear_sync.py +13 -3
- package/dist/templates/trellis/scripts/task.py +41 -23
- package/dist/templates/trellis/workflow.md +4 -2
- package/dist/utils/ablation-store.d.ts +1 -1
- package/dist/utils/ablation-store.d.ts.map +1 -1
- package/dist/utils/ablation-store.js +58 -24
- package/dist/utils/ablation-store.js.map +1 -1
- package/dist/utils/asset-backup.d.ts +138 -0
- package/dist/utils/asset-backup.d.ts.map +1 -0
- package/dist/utils/asset-backup.js +514 -0
- package/dist/utils/asset-backup.js.map +1 -0
- package/dist/utils/atomic-write.d.ts +1 -1
- package/dist/utils/atomic-write.d.ts.map +1 -1
- package/dist/utils/atomic-write.js +6 -3
- package/dist/utils/atomic-write.js.map +1 -1
- package/dist/utils/manifest-prune.d.ts +3 -1
- package/dist/utils/manifest-prune.d.ts.map +1 -1
- package/dist/utils/manifest-prune.js +7 -3
- package/dist/utils/manifest-prune.js.map +1 -1
- package/dist/utils/template-hash.d.ts +5 -4
- package/dist/utils/template-hash.d.ts.map +1 -1
- package/dist/utils/template-hash.js +24 -17
- package/dist/utils/template-hash.js.map +1 -1
- package/package.json +2 -2
|
@@ -25,7 +25,7 @@ In Snow:
|
|
|
25
25
|
2. Dispatch implement/check/research (project agents under `.snow/agents/`). Prefer prompt first line:
|
|
26
26
|
|
|
27
27
|
```text
|
|
28
|
-
Active task: .trellis/tasks/<
|
|
28
|
+
Active task: .trellis/tasks/<task-dir>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
3. Optional: `/trellis-continue` / `/trellis-finish-work`, or `skill-execute` on `trellis-*` skills.
|
|
@@ -16,11 +16,11 @@ assignment,
|
|
|
16
16
|
the paths are:
|
|
17
17
|
|
|
18
18
|
```text
|
|
19
|
-
.trellis/tasks/<task>/teammates/<batch>/<assignment>/brief.json
|
|
20
|
-
.trellis/tasks/<task>/teammates/<batch>/<assignment>/queue.json
|
|
21
|
-
.trellis/tasks/<task>/teammates/<batch>/<assignment>/attempts/<attempt-id>/claim.json
|
|
22
|
-
.trellis/tasks/<task>/teammates/<batch>/<assignment>/attempts/<attempt-id>/worklog.md
|
|
23
|
-
.trellis/tasks/<task>/teammates/<batch>/<assignment>/attempts/<attempt-id>/report.json
|
|
19
|
+
.trellis/tasks/<task-dir>/teammates/<batch>/<assignment>/brief.json
|
|
20
|
+
.trellis/tasks/<task-dir>/teammates/<batch>/<assignment>/queue.json
|
|
21
|
+
.trellis/tasks/<task-dir>/teammates/<batch>/<assignment>/attempts/<attempt-id>/claim.json
|
|
22
|
+
.trellis/tasks/<task-dir>/teammates/<batch>/<assignment>/attempts/<attempt-id>/worklog.md
|
|
23
|
+
.trellis/tasks/<task-dir>/teammates/<batch>/<assignment>/attempts/<attempt-id>/report.json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
Read the brief and claim, append material progress to the assigned worklog,
|
|
@@ -34,6 +34,10 @@ and create exactly one pending report for this attempt using this JSON shape:
|
|
|
34
34
|
"assignment_id": "<assignment-id>",
|
|
35
35
|
"attempt_id": "<attempt-id>",
|
|
36
36
|
"teammate_id": "<teammate-id>",
|
|
37
|
+
"plan_hash": "<plan_hash from the claim>",
|
|
38
|
+
"phase_id": "<phase_id from the frozen plan>",
|
|
39
|
+
"task_route_revision": "<task_route_revision from the frozen plan>",
|
|
40
|
+
"input_revision": "<input_revision from the frozen plan>",
|
|
37
41
|
"status": "complete",
|
|
38
42
|
"source_digest": "<source_digest from the brief>",
|
|
39
43
|
"evidence": [
|
|
@@ -46,6 +50,13 @@ and create exactly one pending report for this attempt using this JSON shape:
|
|
|
46
50
|
}
|
|
47
51
|
```
|
|
48
52
|
|
|
53
|
+
Copy the assigned identity and revisions from the coordinator's frozen launch
|
|
54
|
+
context and claim. Do not infer them from another task, plan, or attempt. If
|
|
55
|
+
that context is missing, report the blocker instead of inventing values.
|
|
56
|
+
|
|
57
|
+
These are role and result requirements, not a claim of sandbox isolation.
|
|
58
|
+
Available coding tools do not authorize product implementation in this role.
|
|
59
|
+
|
|
49
60
|
Use only the explicit context paths frozen in the brief. Do not read or use
|
|
50
61
|
ambient personal, session, or cross-project memory, including memory tools or
|
|
51
62
|
environment-provided memory. If the host exposes such memory and cannot isolate
|
|
@@ -25,6 +25,8 @@ export declare const commonDeveloper: string;
|
|
|
25
25
|
export declare const commonGitContext: string;
|
|
26
26
|
export declare const commonTaskQueue: string;
|
|
27
27
|
export declare const commonTaskUtils: string;
|
|
28
|
+
export declare const commonTaskLayout: string;
|
|
29
|
+
export declare const commonTaskMove: string;
|
|
28
30
|
export declare const commonActiveTask: string;
|
|
29
31
|
export declare const commonCliAdapter: string;
|
|
30
32
|
export declare const commonConfig: string;
|
|
@@ -50,6 +52,7 @@ export declare const initDeveloperScript: string;
|
|
|
50
52
|
export declare const taskScript: string;
|
|
51
53
|
export declare const getContextScript: string;
|
|
52
54
|
export declare const addSessionScript: string;
|
|
55
|
+
export declare const linearSyncScript: string;
|
|
53
56
|
export declare const workflowMdTemplate: string;
|
|
54
57
|
export declare const configYamlTemplate: string;
|
|
55
58
|
export declare const gitignoreTemplate: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,kBAAkB,QAE9B,CAAC;AACF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAcH,eAAO,MAAM,WAAW,QAAsC,CAAC;AAG/D,eAAO,MAAM,UAAU,QAA6C,CAAC;AACrE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,eAAe,QAA8C,CAAC;AAC3E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,cAAc,QAA8C,CAAC;AAC1E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,YAAY,QAA2C,CAAC;AACrE,eAAO,MAAM,QAAQ,QAAuC,CAAC;AAC7D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,QAAwC,CAAC;AAC/D,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,WAAW,QAA0C,CAAC;AACnE,eAAO,MAAM,iBAAiB,QAAiD,CAAC;AAChF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,kBAAkB,QAE9B,CAAC;AACF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,oBAAoB,QAEhC,CAAC;AACF,eAAO,MAAM,qBAAqB,QAEjC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AACF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,eAAO,MAAM,eAAe,QAA+C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAC9E,eAAO,MAAM,gBAAgB,QAAgD,CAAC;AAG9E,eAAO,MAAM,kBAAkB,QAA2C,CAAC;AAC3E,eAAO,MAAM,mBAAmB,QAA4C,CAAC;AAC7E,eAAO,MAAM,UAAU,QAAkC,CAAC;AAC1D,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAAyC,CAAC;AACvE,eAAO,MAAM,gBAAgB,QAA+C,CAAC;AAG7E,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,kBAAkB,QAA8B,CAAC;AAC9D,eAAO,MAAM,iBAAiB,QAAgC,CAAC;AAC/D,eAAO,MAAM,qBAAqB,QAAoC,CAAC;AAGvE,eAAO,MAAM,qBAAqB,QAAqC,CAAC;AACxE,eAAO,MAAM,8BAA8B,QAE1C,CAAC;AACF,eAAO,MAAM,wBAAwB,QAEpC,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA6CnD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASlD"}
|
|
@@ -35,6 +35,8 @@ export const commonDeveloper = readTemplate("scripts/common/developer.py");
|
|
|
35
35
|
export const commonGitContext = readTemplate("scripts/common/git_context.py");
|
|
36
36
|
export const commonTaskQueue = readTemplate("scripts/common/task_queue.py");
|
|
37
37
|
export const commonTaskUtils = readTemplate("scripts/common/task_utils.py");
|
|
38
|
+
export const commonTaskLayout = readTemplate("scripts/common/task_layout.py");
|
|
39
|
+
export const commonTaskMove = readTemplate("scripts/common/task_move.py");
|
|
38
40
|
export const commonActiveTask = readTemplate("scripts/common/active_task.py");
|
|
39
41
|
export const commonCliAdapter = readTemplate("scripts/common/cli_adapter.py");
|
|
40
42
|
export const commonConfig = readTemplate("scripts/common/config.py");
|
|
@@ -61,6 +63,7 @@ export const initDeveloperScript = readTemplate("scripts/init_developer.py");
|
|
|
61
63
|
export const taskScript = readTemplate("scripts/task.py");
|
|
62
64
|
export const getContextScript = readTemplate("scripts/get_context.py");
|
|
63
65
|
export const addSessionScript = readTemplate("scripts/add_session.py");
|
|
66
|
+
export const linearSyncScript = readTemplate("scripts/hooks/linear_sync.py");
|
|
64
67
|
// Configuration files
|
|
65
68
|
export const workflowMdTemplate = readTemplate("workflow.md");
|
|
66
69
|
export const configYamlTemplate = readTemplate("config.yaml");
|
|
@@ -84,6 +87,8 @@ export function getAllScripts() {
|
|
|
84
87
|
scripts.set("common/git_context.py", commonGitContext);
|
|
85
88
|
scripts.set("common/task_queue.py", commonTaskQueue);
|
|
86
89
|
scripts.set("common/task_utils.py", commonTaskUtils);
|
|
90
|
+
scripts.set("common/task_layout.py", commonTaskLayout);
|
|
91
|
+
scripts.set("common/task_move.py", commonTaskMove);
|
|
87
92
|
scripts.set("common/active_task.py", commonActiveTask);
|
|
88
93
|
scripts.set("common/cli_adapter.py", commonCliAdapter);
|
|
89
94
|
scripts.set("common/config.py", commonConfig);
|
|
@@ -110,6 +115,7 @@ export function getAllScripts() {
|
|
|
110
115
|
scripts.set("task.py", taskScript);
|
|
111
116
|
scripts.set("get_context.py", getContextScript);
|
|
112
117
|
scripts.set("add_session.py", addSessionScript);
|
|
118
|
+
scripts.set("hooks/linear_sync.py", linearSyncScript);
|
|
113
119
|
return scripts;
|
|
114
120
|
}
|
|
115
121
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1107,7 +1107,7 @@ def _auto_commit_workspace(repo_root: Path) -> str:
|
|
|
1107
1107
|
|
|
1108
1108
|
commit_msg = get_session_commit_message(repo_root)
|
|
1109
1109
|
# Resolve the current task so staging is scoped to its dir only. The ref
|
|
1110
|
-
# is ``.trellis/tasks/<
|
|
1110
|
+
# is ``.trellis/tasks/<task-dir>`` — pass the bare task name.
|
|
1111
1111
|
current = get_current_task(repo_root)
|
|
1112
1112
|
if current:
|
|
1113
1113
|
task_name = Path(current).name
|
|
@@ -201,54 +201,10 @@ def normalize_task_ref(task_ref: str) -> str:
|
|
|
201
201
|
|
|
202
202
|
|
|
203
203
|
def resolve_task_ref(task_ref: str, repo_root: Path) -> Path | None:
|
|
204
|
-
"""
|
|
204
|
+
"""Use the canonical status-layout resolver shared by all task readers."""
|
|
205
|
+
from .paths import resolve_task_ref as resolve
|
|
205
206
|
|
|
206
|
-
|
|
207
|
-
rather than imported because this module is loaded standalone — hooks add
|
|
208
|
-
it to `sys.path` directly — so it stays zero-relative-import on purpose.
|
|
209
|
-
"""
|
|
210
|
-
normalized = normalize_task_ref(task_ref)
|
|
211
|
-
if not normalized:
|
|
212
|
-
return None
|
|
213
|
-
|
|
214
|
-
try:
|
|
215
|
-
root = repo_root.resolve()
|
|
216
|
-
except OSError:
|
|
217
|
-
return None
|
|
218
|
-
|
|
219
|
-
path_obj = Path(normalized)
|
|
220
|
-
if path_obj.is_absolute():
|
|
221
|
-
candidate = path_obj
|
|
222
|
-
elif normalized.startswith(f"{DIR_WORKFLOW}/"):
|
|
223
|
-
candidate = root / path_obj
|
|
224
|
-
else:
|
|
225
|
-
candidate = root / DIR_WORKFLOW / DIR_TASKS / path_obj
|
|
226
|
-
|
|
227
|
-
# Both sides are resolved because repo_root itself may sit behind a
|
|
228
|
-
# symlink (/tmp on macOS does), and resolve() is what collapses `..`
|
|
229
|
-
# instead of leaving it for a lexical relative_to() to wave through.
|
|
230
|
-
try:
|
|
231
|
-
resolved = candidate.resolve()
|
|
232
|
-
workflow_real = (root / DIR_WORKFLOW).resolve()
|
|
233
|
-
except OSError:
|
|
234
|
-
return None
|
|
235
|
-
|
|
236
|
-
try:
|
|
237
|
-
resolved.relative_to(root)
|
|
238
|
-
return resolved
|
|
239
|
-
except ValueError:
|
|
240
|
-
pass
|
|
241
|
-
|
|
242
|
-
# `.trellis` may itself be a symlink into a store outside the repo (#567).
|
|
243
|
-
# The workflow dir's own real location is then a second legitimate
|
|
244
|
-
# containment base; a ref that escapes BOTH bases is still refused. Map
|
|
245
|
-
# back to the in-repo (lexical) form so callers store a repo-relative ref.
|
|
246
|
-
try:
|
|
247
|
-
rel = resolved.relative_to(workflow_real)
|
|
248
|
-
except ValueError:
|
|
249
|
-
return None
|
|
250
|
-
|
|
251
|
-
return root / DIR_WORKFLOW / rel
|
|
207
|
+
return resolve(task_ref, repo_root)
|
|
252
208
|
|
|
253
209
|
|
|
254
210
|
def _runtime_sessions_dir(repo_root: Path) -> Path:
|
|
@@ -344,35 +344,19 @@ def resolve_task_ref(task_ref: str, repo_root: Path | None = None) -> Path | Non
|
|
|
344
344
|
else:
|
|
345
345
|
candidate = root / DIR_WORKFLOW / DIR_TASKS / path_obj
|
|
346
346
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
# repo_root itself may sit behind a symlink (/tmp on macOS does).
|
|
350
|
-
try:
|
|
351
|
-
resolved = candidate.resolve()
|
|
352
|
-
workflow_real = (root / DIR_WORKFLOW).resolve()
|
|
353
|
-
except OSError:
|
|
354
|
-
return None
|
|
347
|
+
from .task_layout import task_status_dir
|
|
348
|
+
from .task_move import require_settled_layout
|
|
355
349
|
|
|
356
350
|
try:
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
# containment base: a ref through that link never left the workflow tree.
|
|
365
|
-
# A ref that escapes BOTH bases (traversal, absolute path elsewhere, a
|
|
366
|
-
# task dir symlinked out of the tree) is still refused.
|
|
367
|
-
try:
|
|
368
|
-
rel = resolved.relative_to(workflow_real)
|
|
369
|
-
except ValueError:
|
|
351
|
+
require_settled_layout(root)
|
|
352
|
+
tasks = root / DIR_WORKFLOW / DIR_TASKS
|
|
353
|
+
if task_status_dir(candidate, tasks) is None:
|
|
354
|
+
return None
|
|
355
|
+
relative = candidate.resolve().relative_to(tasks.resolve())
|
|
356
|
+
return tasks / relative
|
|
357
|
+
except (ValueError, OSError, RuntimeError):
|
|
370
358
|
return None
|
|
371
359
|
|
|
372
|
-
# Map back to the in-repo (lexical) form so callers store the same
|
|
373
|
-
# repo-relative ref as in the non-symlinked layout.
|
|
374
|
-
return root / DIR_WORKFLOW / rel
|
|
375
|
-
|
|
376
360
|
|
|
377
361
|
def get_current_task(
|
|
378
362
|
repo_root: Path | None = None,
|
|
@@ -70,7 +70,7 @@ def safe_trellis_paths_to_add(
|
|
|
70
70
|
Included:
|
|
71
71
|
- .trellis/workspace/<developer>/journal-*.md
|
|
72
72
|
- .trellis/workspace/<developer>/index.md
|
|
73
|
-
- .trellis/tasks/<
|
|
73
|
+
- .trellis/tasks/<task-dir>/ (ONLY the current task dir when
|
|
74
74
|
``task_name`` is passed)
|
|
75
75
|
|
|
76
76
|
Excluded (intentionally — these must not be staged):
|
|
@@ -112,16 +112,16 @@ def safe_trellis_paths_to_add(
|
|
|
112
112
|
# Narrow scope — ONLY the current task directory.
|
|
113
113
|
# Never iterdir() all tasks: parallel-window dirty task dirs must not
|
|
114
114
|
# leak into the session auto-commit.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
from .task_utils import find_task_by_name
|
|
116
|
+
active_task = find_task_by_name(task_name, tasks_dir)
|
|
117
|
+
if active_task is not None and active_task.is_dir():
|
|
118
|
+
paths.append(active_task.relative_to(repo_root).as_posix())
|
|
118
119
|
return paths
|
|
119
120
|
|
|
120
121
|
# Wide scope (no task_name): each direct task directory.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
paths.append(f"{DIR_WORKFLOW}/{DIR_TASKS}/{child.name}")
|
|
122
|
+
from .task_layout import iter_task_dirs
|
|
123
|
+
for child in iter_task_dirs(tasks_dir):
|
|
124
|
+
paths.append(child.relative_to(repo_root).as_posix())
|
|
125
125
|
|
|
126
126
|
return paths
|
|
127
127
|
|
|
@@ -29,7 +29,7 @@ from .config import get_context_injection_limits
|
|
|
29
29
|
from .git import branch_exists_locally
|
|
30
30
|
from .io import read_json
|
|
31
31
|
from .log import Colors, colored
|
|
32
|
-
from .paths import
|
|
32
|
+
from .paths import FILE_TASK_JSON, get_repo_root
|
|
33
33
|
from .task_utils import resolve_task_dir
|
|
34
34
|
|
|
35
35
|
# Extensions that look like code rather than spec/research docs. Entries with
|
|
@@ -195,54 +195,6 @@ def _is_exempt_from_code_file_warning(file_path: str, task_rel: str) -> bool:
|
|
|
195
195
|
return False
|
|
196
196
|
|
|
197
197
|
|
|
198
|
-
def _resolve_context_entry_path(
|
|
199
|
-
file_path: str, repo_root: Path, task_dir: Path | None
|
|
200
|
-
) -> Path | None:
|
|
201
|
-
"""Bind historical self-references to their archive copy without fallback."""
|
|
202
|
-
repo_path = repo_root / file_path
|
|
203
|
-
if task_dir is None:
|
|
204
|
-
return repo_path
|
|
205
|
-
|
|
206
|
-
try:
|
|
207
|
-
task_parts = task_dir.resolve().relative_to(repo_root.resolve()).parts
|
|
208
|
-
except ValueError:
|
|
209
|
-
return repo_path
|
|
210
|
-
|
|
211
|
-
archive_prefix = (DIR_WORKFLOW, DIR_TASKS, "archive")
|
|
212
|
-
if len(task_parts) != 5 or task_parts[:3] != archive_prefix:
|
|
213
|
-
return repo_path
|
|
214
|
-
|
|
215
|
-
year_month = task_parts[3]
|
|
216
|
-
if (
|
|
217
|
-
len(year_month) != 7
|
|
218
|
-
or year_month[4] != "-"
|
|
219
|
-
or not year_month[:4].isdigit()
|
|
220
|
-
or not year_month[5:].isdigit()
|
|
221
|
-
):
|
|
222
|
-
return repo_path
|
|
223
|
-
|
|
224
|
-
historical_root = f"{DIR_WORKFLOW}/{DIR_TASKS}/{task_dir.name}"
|
|
225
|
-
posix_path = file_path.replace("\\", "/")
|
|
226
|
-
if posix_path == historical_root:
|
|
227
|
-
relative_parts: tuple[str, ...] = ()
|
|
228
|
-
elif posix_path.startswith(f"{historical_root}/"):
|
|
229
|
-
relative_path = posix_path[len(historical_root) + 1 :]
|
|
230
|
-
if relative_path.endswith("/"):
|
|
231
|
-
relative_path = relative_path[:-1]
|
|
232
|
-
relative_parts = tuple(relative_path.split("/")) if relative_path else ()
|
|
233
|
-
if any(part in ("", ".", "..") for part in relative_parts):
|
|
234
|
-
return None
|
|
235
|
-
else:
|
|
236
|
-
return repo_path
|
|
237
|
-
|
|
238
|
-
try:
|
|
239
|
-
archive_root = task_dir.resolve()
|
|
240
|
-
resolved_path = task_dir.joinpath(*relative_parts).resolve()
|
|
241
|
-
resolved_path.relative_to(archive_root)
|
|
242
|
-
except (OSError, RuntimeError, ValueError):
|
|
243
|
-
return None
|
|
244
|
-
return resolved_path
|
|
245
|
-
|
|
246
198
|
|
|
247
199
|
def _validate_jsonl(jsonl_file: Path, repo_root: Path, task_dir: Path | None = None) -> int:
|
|
248
200
|
"""Validate a single JSONL file.
|
|
@@ -268,7 +220,7 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path, task_dir: Path | None = N
|
|
|
268
220
|
task_rel = ""
|
|
269
221
|
if task_dir is not None:
|
|
270
222
|
try:
|
|
271
|
-
task_rel = task_dir.
|
|
223
|
+
task_rel = task_dir.absolute().relative_to(repo_root.absolute()).as_posix()
|
|
272
224
|
except ValueError:
|
|
273
225
|
task_rel = ""
|
|
274
226
|
|
|
@@ -323,7 +275,15 @@ def _validate_jsonl(jsonl_file: Path, repo_root: Path, task_dir: Path | None = N
|
|
|
323
275
|
continue
|
|
324
276
|
|
|
325
277
|
real_entries += 1
|
|
326
|
-
full_path =
|
|
278
|
+
full_path = repo_root / file_path
|
|
279
|
+
normalized = file_path.replace("\\", "/")
|
|
280
|
+
if task_dir is not None and task_rel and (normalized == task_rel or normalized.startswith(task_rel + "/")):
|
|
281
|
+
try:
|
|
282
|
+
if ".." in Path(normalized).parts:
|
|
283
|
+
raise ValueError("Traversal in task reference")
|
|
284
|
+
full_path.resolve().relative_to(task_dir.resolve())
|
|
285
|
+
except (ValueError, OSError, RuntimeError):
|
|
286
|
+
full_path = None
|
|
327
287
|
if entry_type == "directory":
|
|
328
288
|
if full_path is None or not full_path.is_dir():
|
|
329
289
|
print(f" {colored(f'{file_name}:{line_num}: Directory not found: {file_path}', Colors.RED)}")
|
|
@@ -385,17 +385,13 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
385
385
|
if not PACKAGE_DIGEST_RE.fullmatch(args.expected_continuity_digest):
|
|
386
386
|
raise ContinuityError("TASK_TRANSFER_INVALID_INPUT", "expected continuity digest must be a SHA-256 digest")
|
|
387
387
|
if args.expected_continuity_digest != current_digest:
|
|
388
|
-
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "current task projection differs from the explicitly
|
|
388
|
+
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "current task projection differs from the explicitly checked snapshot")
|
|
389
389
|
# Recovery acknowledges a live target snapshot without rebasing the source package.
|
|
390
|
-
|
|
390
|
+
recovered_claim = (
|
|
391
391
|
operation == "claim" and record["status"] == "sealed"
|
|
392
392
|
and args.expected_continuity_digest == current_digest
|
|
393
393
|
)
|
|
394
|
-
|
|
395
|
-
operation == "reconcile" and record["status"] == "consumed"
|
|
396
|
-
and args.expected_continuity_digest == current_digest
|
|
397
|
-
)
|
|
398
|
-
if operation != "release" and current_digest != record.get("target_continuity_digest", record["continuity_digest"]) and not (reconciled_claim or reconciled_target):
|
|
394
|
+
if operation != "release" and current_digest != record.get("target_continuity_digest", record["continuity_digest"]) and not recovered_claim:
|
|
399
395
|
raise ContinuityError("TASK_TRANSFER_STALE_CONTINUITY", "task continuity changed during ownership transfer")
|
|
400
396
|
if operation != "release" and _route_snapshot(data) != record["route"]:
|
|
401
397
|
raise ContinuityError("TASK_TRANSFER_STALE_ROUTE", "task phase, route, input, or implementation authorization changed")
|
|
@@ -403,13 +399,10 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
403
399
|
if operation in {"seal", "retire"}:
|
|
404
400
|
if context_key != record["source_session_id"] or current_path not in {record["task_path"], None}:
|
|
405
401
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "source session no longer owns the current task")
|
|
406
|
-
elif operation in {"claim", "consume"
|
|
402
|
+
elif operation in {"claim", "consume"}:
|
|
407
403
|
if context_key == record["source_session_id"]:
|
|
408
404
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "target must be a different direct session")
|
|
409
|
-
if operation == "
|
|
410
|
-
if not reconciled_target or context_key != record["target_session_id"] or current_path != record["task_path"]:
|
|
411
|
-
raise ContinuityError("TASK_TRANSFER_WITHHELD", "only the consumed target may reconcile its explicitly pinned projection")
|
|
412
|
-
elif operation == "release":
|
|
405
|
+
if operation == "release":
|
|
413
406
|
if record["status"] not in {"quiescing", "sealed", "recovery_required"} or record["target_session_id"] is not None:
|
|
414
407
|
raise ContinuityError("TASK_TRANSFER_INVALID_TRANSITION", "ownership can be released only before target claim")
|
|
415
408
|
if context_key != record["source_session_id"] or current_path not in {None, record["task_path"]}:
|
|
@@ -422,9 +415,7 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
422
415
|
path.unlink(missing_ok=True)
|
|
423
416
|
print(json.dumps({"status": "released", "generation": record["generation"], "task_id": task_id}, ensure_ascii=False, sort_keys=True))
|
|
424
417
|
return 0
|
|
425
|
-
if operation == "
|
|
426
|
-
record.update({"target_continuity_digest": current_digest, "generation": record["generation"] + 1})
|
|
427
|
-
elif operation == "seal" and record["status"] == "quiescing":
|
|
418
|
+
if operation == "seal" and record["status"] == "quiescing":
|
|
428
419
|
record.update({"status": "sealed", "generation": record["generation"] + 1})
|
|
429
420
|
elif operation == "retire" and record["status"] in {"sealed", "retiring", "recovery_required"}:
|
|
430
421
|
if record["status"] == "sealed":
|
|
@@ -440,14 +431,14 @@ def transfer_task_ownership(args: argparse.Namespace) -> int:
|
|
|
440
431
|
write_json(path, record)
|
|
441
432
|
raise ContinuityError("TASK_TRANSFER_RECOVERY_REQUIRED", "source pointer remains after retirement")
|
|
442
433
|
record["status"] = "ready"
|
|
443
|
-
elif operation == "claim" and (record["status"] in {"ready", "claiming", "recovery_required", "claimed"} or
|
|
434
|
+
elif operation == "claim" and (record["status"] in {"ready", "claiming", "recovery_required", "claimed"} or recovered_claim):
|
|
444
435
|
if record["status"] in {"claiming", "recovery_required"} and record["target_session_id"] != context_key:
|
|
445
436
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "only the recorded target session can recover a claim")
|
|
446
437
|
if current_path not in {None, record["task_path"]}:
|
|
447
438
|
raise ContinuityError("TASK_TRANSFER_WITHHELD", "target session already has a current task")
|
|
448
439
|
if record["status"] in {"ready", "sealed"}:
|
|
449
440
|
record.update({"status": "claiming", "generation": record["generation"] + 1, "target_session_id": context_key})
|
|
450
|
-
if
|
|
441
|
+
if recovered_claim:
|
|
451
442
|
record["target_continuity_digest"] = current_digest
|
|
452
443
|
if not write_json(path, record):
|
|
453
444
|
raise ContinuityError("TASK_TRANSFER_WRITE_FAILED", "could not record target claim")
|
|
@@ -497,12 +488,12 @@ def add_continuity_commands(subparsers) -> None:
|
|
|
497
488
|
capture.add_argument("--expected-source-digest", required=True, help="Current captured digest, or absent")
|
|
498
489
|
|
|
499
490
|
transfer = subparsers.add_parser("transfer", help="Transfer task ownership between direct sessions")
|
|
500
|
-
transfer.add_argument("transfer_action", choices=("status", "quiesce", "seal", "retire", "claim", "consume", "release"
|
|
491
|
+
transfer.add_argument("transfer_action", choices=("status", "quiesce", "seal", "retire", "claim", "consume", "release"))
|
|
501
492
|
transfer.add_argument("--task-id", required=True)
|
|
502
493
|
transfer.add_argument("--task-path", required=True)
|
|
503
494
|
transfer.add_argument("--package-id", required=True)
|
|
504
495
|
transfer.add_argument("--package-digest", required=True)
|
|
505
|
-
transfer.add_argument("--expected-continuity-digest", help="Expected live projection
|
|
496
|
+
transfer.add_argument("--expected-continuity-digest", help="Expected live projection for sealed-source claim recovery")
|
|
506
497
|
transfer.add_argument("--expected-generation", type=int)
|
|
507
498
|
transfer.add_argument("--include-current", action="store_true", help="Include the live task projection and its digest in transfer status")
|
|
508
499
|
transfer.add_argument("--explicit-user-request", action="store_true")
|
|
@@ -524,35 +515,39 @@ def clear_task_runtime_records(repo_root: Path, task_path: str) -> bool:
|
|
|
524
515
|
@contextlib.contextmanager
|
|
525
516
|
def active_task_change_guard(repo_root: Path, context_key: str, task_path: str, *, clearing: bool):
|
|
526
517
|
"""Serialize pointer writes with ownership changes for the same task."""
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
with _record_lock(path):
|
|
534
|
-
try:
|
|
535
|
-
record = _load_owner(path)
|
|
536
|
-
except ContinuityError:
|
|
537
|
-
yield False
|
|
538
|
-
return
|
|
539
|
-
if record is None:
|
|
540
|
-
yield True
|
|
541
|
-
return
|
|
542
|
-
data, _ = read_json_checked(directory / FILE_TASK_JSON)
|
|
543
|
-
task_id = (data.get("id") or data.get("name")) if isinstance(data, dict) else None
|
|
544
|
-
if task_id != record["task_id"] or record["task_path"] != task_path:
|
|
518
|
+
from .task_move import layout_lock, require_settled_layout
|
|
519
|
+
|
|
520
|
+
with layout_lock(repo_root):
|
|
521
|
+
require_settled_layout(repo_root)
|
|
522
|
+
directory = resolve_task_dir(task_path, repo_root)
|
|
523
|
+
if directory is None:
|
|
545
524
|
yield False
|
|
546
525
|
return
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
526
|
+
task_path = directory.relative_to(repo_root).as_posix()
|
|
527
|
+
path = _ownership_path(repo_root, task_path)
|
|
528
|
+
with _record_lock(path):
|
|
529
|
+
try:
|
|
530
|
+
record = _load_owner(path)
|
|
531
|
+
except ContinuityError:
|
|
532
|
+
yield False
|
|
533
|
+
return
|
|
534
|
+
if record is None:
|
|
535
|
+
yield True
|
|
536
|
+
return
|
|
537
|
+
data, _ = read_json_checked(directory / FILE_TASK_JSON)
|
|
538
|
+
task_id = (data.get("id") or data.get("name")) if isinstance(data, dict) else None
|
|
539
|
+
if task_id != record["task_id"] or record["task_path"] != task_path:
|
|
540
|
+
yield False
|
|
541
|
+
return
|
|
542
|
+
if clearing:
|
|
543
|
+
allowed = record["status"] == "consumed" and record["target_session_id"] == context_key
|
|
544
|
+
elif record["status"] == "ready":
|
|
545
|
+
allowed = context_key != record["source_session_id"] and task_path == record["task_path"]
|
|
546
|
+
elif record["status"] == "consumed":
|
|
547
|
+
allowed = context_key == record["target_session_id"] and task_path == record["task_path"]
|
|
548
|
+
else:
|
|
549
|
+
allowed = record["status"] in {"claiming", "claimed"} and context_key == record["target_session_id"] and task_path == record["task_path"]
|
|
550
|
+
yield allowed
|
|
556
551
|
|
|
557
552
|
|
|
558
553
|
def assert_task_mutation_allowed(repo_root: Path, task_dir: Path, data: dict) -> None:
|
|
@@ -578,21 +573,25 @@ def assert_task_identity_change_allowed(repo_root: Path, task_dir: Path, data: d
|
|
|
578
573
|
@contextlib.contextmanager
|
|
579
574
|
def task_mutation_guard(repo_root: Path, task_dir: Path, data: dict):
|
|
580
575
|
"""Hold the ownership lock across a single task mutation."""
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
with
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
576
|
+
from .task_move import layout_lock, require_settled_layout
|
|
577
|
+
|
|
578
|
+
with layout_lock(repo_root):
|
|
579
|
+
require_settled_layout(repo_root)
|
|
580
|
+
task_path = task_dir.relative_to(repo_root).as_posix()
|
|
581
|
+
path = _ownership_path(repo_root, task_path)
|
|
582
|
+
with _record_lock(path):
|
|
583
|
+
try:
|
|
584
|
+
record = _load_owner(path)
|
|
585
|
+
except ContinuityError:
|
|
586
|
+
yield False
|
|
587
|
+
return
|
|
588
|
+
if record is None:
|
|
589
|
+
yield True
|
|
590
|
+
return
|
|
591
|
+
task_id = (data.get("id") or data.get("name")) if isinstance(data, dict) else None
|
|
592
|
+
if task_id != record["task_id"] or record["task_path"] != task_path:
|
|
593
|
+
yield False
|
|
594
|
+
return
|
|
595
|
+
active = resolve_active_task(repo_root)
|
|
596
|
+
allowed = record["status"] == "consumed" and active.context_key == record["target_session_id"] and active.task_path == task_path
|
|
597
|
+
yield allowed
|