@pennixrv/trellis 0.6.40 → 0.6.43
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/dist/migrations/manifests/0.6.41.json +9 -0
- package/dist/migrations/manifests/0.6.42.json +9 -0
- package/dist/migrations/manifests/0.6.43.json +9 -0
- package/dist/templates/codex/hooks/session-start.py +12 -1
- package/dist/templates/opencode/lib/trellis-context.js +24 -4
- package/dist/templates/opencode/plugins/inject-workflow-state.js +16 -3
- package/dist/templates/shared-hooks/inject-subagent-context.py +6 -2
- package/dist/templates/shared-hooks/inject-workflow-state.py +7 -1
- package/dist/templates/trellis/scripts/common/active_task.py +28 -17
- package/dist/templates/trellis/scripts/common/continuation_record.py +1 -1
- package/dist/templates/trellis/scripts/task.py +18 -0
- package/dist/templates/trellis/workflow.md +14 -1
- package/package.json +2 -2
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.6.41",
|
|
3
|
+
"description": "Restores active-task visibility when a host context key has no persisted session file.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Fix:**\n- Projects one unique developer-owned resumable task as read-only workflow context when the host provides a context key whose session file is missing, while still refusing to guess when another session pointer exists.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No project migration is required. Existing projects can run the normal trellis update flow."
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.6.42",
|
|
3
|
+
"description": "Isolates unbound task resolution from unrelated session fallback inference.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Fix:**\n- Stop the default task resolver from borrowing a task from a sole unrelated session file, while preserving read-only unbound task and ambiguity projections.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No project migration is required. Existing projects can run the normal trellis update flow."
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.6.43",
|
|
3
|
+
"description": "Ships the isolated active-task resolver to the dogfood script tree.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Fix:**\n- Keep the dogfood and published active-task resolver scripts synchronized so isolated session fallback behavior is identical in development and installed projects.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"notes": "No project migration is required. Existing projects can run the normal trellis update flow."
|
|
9
|
+
}
|
|
@@ -229,6 +229,14 @@ def _resolve_task_dir(trellis_dir: Path, task_ref: str) -> Path:
|
|
|
229
229
|
|
|
230
230
|
def _get_task_status(trellis_dir: Path, hook_input: dict) -> str:
|
|
231
231
|
active = _resolve_active_task(trellis_dir, hook_input)
|
|
232
|
+
if active.source_type == "unbound_ambiguous":
|
|
233
|
+
candidates = "\n".join(f"- {candidate}" for candidate in active.candidate_paths)
|
|
234
|
+
return (
|
|
235
|
+
"Status: TASK BINDING AMBIGUOUS\n"
|
|
236
|
+
f"Candidates:\n{candidates}\n"
|
|
237
|
+
"Next: Review the candidates and run `python3 ./.trellis/scripts/task.py start <task>` "
|
|
238
|
+
"with the intended task once a direct session identity is available."
|
|
239
|
+
)
|
|
232
240
|
if not active.task_path:
|
|
233
241
|
return (
|
|
234
242
|
"Status: NO ACTIVE TASK\n"
|
|
@@ -383,7 +391,10 @@ def _build_compact_current_state(
|
|
|
383
391
|
lines.append(_format_git_state(repo_root))
|
|
384
392
|
|
|
385
393
|
active = _resolve_active_task(trellis_dir, hook_input)
|
|
386
|
-
if active.
|
|
394
|
+
if active.source_type == "unbound_ambiguous":
|
|
395
|
+
candidates = ", ".join(active.candidate_paths)
|
|
396
|
+
lines.append(f"Current task: ambiguous; candidates={candidates}; bind explicitly.")
|
|
397
|
+
elif active.task_path:
|
|
387
398
|
task_dir = _resolve_task_dir(trellis_dir, active.task_path)
|
|
388
399
|
status = "unknown"
|
|
389
400
|
task_json = task_dir / "task.json"
|
|
@@ -373,7 +373,8 @@ export class TrellisContext {
|
|
|
373
373
|
*
|
|
374
374
|
* Resolution order (mirrors Python `active_task.resolve_active_task`):
|
|
375
375
|
* 1. Lookup the runtime file for the input-derived context key. A known
|
|
376
|
-
* key is authoritative
|
|
376
|
+
* key is authoritative when it contains a task; a missing or empty
|
|
377
|
+
* context may use the guarded unbound-task projection.
|
|
377
378
|
* 2. If no context key is available and exactly one session runtime file
|
|
378
379
|
* exists locally, use it (`_resolveSingleSessionFallback`). Refuses to
|
|
379
380
|
* guess when 0 or ≥2 files exist so multi-window isolation holds.
|
|
@@ -393,6 +394,10 @@ export class TrellisContext {
|
|
|
393
394
|
stale: !taskDir || !existsSync(taskDir),
|
|
394
395
|
}
|
|
395
396
|
}
|
|
397
|
+
const unbound = this._resolveUnboundTask()
|
|
398
|
+
if (unbound) {
|
|
399
|
+
return unbound
|
|
400
|
+
}
|
|
396
401
|
return { taskPath: null, source: "none", stale: false }
|
|
397
402
|
}
|
|
398
403
|
|
|
@@ -456,7 +461,14 @@ export class TrellisContext {
|
|
|
456
461
|
} catch {
|
|
457
462
|
return null
|
|
458
463
|
}
|
|
459
|
-
if (sessionFiles.
|
|
464
|
+
if (sessionFiles.some(file => {
|
|
465
|
+
try {
|
|
466
|
+
const context = JSON.parse(readFileSync(join(sessionsDir, file), "utf-8"))
|
|
467
|
+
return Boolean(this.normalizeTaskRef(context?.current_task || ""))
|
|
468
|
+
} catch {
|
|
469
|
+
return false
|
|
470
|
+
}
|
|
471
|
+
})) return null
|
|
460
472
|
}
|
|
461
473
|
|
|
462
474
|
const developer = (process.env.TRELLIS_DEVELOPER || "").trim() || (() => {
|
|
@@ -486,8 +498,16 @@ export class TrellisContext {
|
|
|
486
498
|
return []
|
|
487
499
|
}
|
|
488
500
|
})
|
|
489
|
-
if (candidates.length
|
|
490
|
-
|
|
501
|
+
if (candidates.length === 0) return null
|
|
502
|
+
if (candidates.length === 1) {
|
|
503
|
+
return { taskPath: candidates[0], source: "unbound", stale: false }
|
|
504
|
+
}
|
|
505
|
+
return {
|
|
506
|
+
taskPath: null,
|
|
507
|
+
source: "unbound_ambiguous",
|
|
508
|
+
stale: false,
|
|
509
|
+
candidatePaths: candidates.sort(),
|
|
510
|
+
}
|
|
491
511
|
}
|
|
492
512
|
|
|
493
513
|
getCurrentTask(platformInput = null) {
|
|
@@ -143,6 +143,14 @@ function loadBreadcrumbs(directory) {
|
|
|
143
143
|
*/
|
|
144
144
|
function getActiveTask(ctx, platformInput = null) {
|
|
145
145
|
const active = ctx.getActiveTask(platformInput)
|
|
146
|
+
if (active.source === "unbound_ambiguous") {
|
|
147
|
+
return {
|
|
148
|
+
id: null,
|
|
149
|
+
status: "unbound_ambiguous",
|
|
150
|
+
source: active.source,
|
|
151
|
+
candidates: active.candidatePaths || [],
|
|
152
|
+
}
|
|
153
|
+
}
|
|
146
154
|
const taskRef = active.taskPath
|
|
147
155
|
if (!taskRef) return null
|
|
148
156
|
const taskDir = ctx.resolveTaskDir(taskRef)
|
|
@@ -170,12 +178,17 @@ function getActiveTask(ctx, platformInput = null) {
|
|
|
170
178
|
* "Refer to workflow.md for current step." line
|
|
171
179
|
* - no_task pseudo-status (id === null) → header omits task info
|
|
172
180
|
*/
|
|
173
|
-
function buildBreadcrumb(id, status, templates) {
|
|
181
|
+
function buildBreadcrumb(id, status, templates, candidates = []) {
|
|
174
182
|
let body = templates[status]
|
|
175
183
|
if (body === undefined) {
|
|
176
184
|
body = "Refer to workflow.md for current step."
|
|
177
185
|
}
|
|
178
|
-
let header
|
|
186
|
+
let header
|
|
187
|
+
if (status === "unbound_ambiguous") {
|
|
188
|
+
header = `Status: ${status}\nCandidates: ${candidates.join(", ")}`
|
|
189
|
+
} else {
|
|
190
|
+
header = id === null ? `Status: ${status}` : `Task: ${id} (${status})`
|
|
191
|
+
}
|
|
179
192
|
return `<workflow-state>\n${header}\n${body}\n</workflow-state>`
|
|
180
193
|
}
|
|
181
194
|
|
|
@@ -218,7 +231,7 @@ export default async ({ directory }) => {
|
|
|
218
231
|
const templates = loadBreadcrumbs(directory)
|
|
219
232
|
const task = getActiveTask(ctx, platformInput)
|
|
220
233
|
const breadcrumb = task
|
|
221
|
-
? buildBreadcrumb(task.id, task.status, templates, task.
|
|
234
|
+
? buildBreadcrumb(task.id, task.status, templates, task.candidates)
|
|
222
235
|
: buildBreadcrumb(null, "no_task", templates)
|
|
223
236
|
|
|
224
237
|
prependEphemeralText(messages, breadcrumb)
|
|
@@ -149,7 +149,7 @@ def get_current_task(
|
|
|
149
149
|
input_data: dict,
|
|
150
150
|
*,
|
|
151
151
|
platform: str | None = None,
|
|
152
|
-
allow_single_session_fallback: bool =
|
|
152
|
+
allow_single_session_fallback: bool = False,
|
|
153
153
|
allow_environment_context: bool = True,
|
|
154
154
|
require_existing: bool = False,
|
|
155
155
|
) -> str | None:
|
|
@@ -740,7 +740,11 @@ def main():
|
|
|
740
740
|
sys.exit(0)
|
|
741
741
|
|
|
742
742
|
# Get current task directory (research doesn't require it)
|
|
743
|
-
task_dir = get_current_task(
|
|
743
|
+
task_dir = get_current_task(
|
|
744
|
+
repo_root,
|
|
745
|
+
input_data,
|
|
746
|
+
allow_single_session_fallback=True,
|
|
747
|
+
)
|
|
744
748
|
|
|
745
749
|
# implement/check need task directory
|
|
746
750
|
if subagent_type in AGENTS_REQUIRE_TASK:
|
|
@@ -169,6 +169,9 @@ def get_active_task(
|
|
|
169
169
|
prompt.
|
|
170
170
|
"""
|
|
171
171
|
active = _resolve_active_task(root, input_data)
|
|
172
|
+
if active.source_type == "unbound_ambiguous":
|
|
173
|
+
candidates = ", ".join(active.candidate_paths)
|
|
174
|
+
return candidates, "unbound_ambiguous", active.source
|
|
172
175
|
if not active.task_path:
|
|
173
176
|
return None
|
|
174
177
|
|
|
@@ -379,7 +382,10 @@ def build_breadcrumb(
|
|
|
379
382
|
body = templates.get(status)
|
|
380
383
|
if body is None:
|
|
381
384
|
body = "Refer to workflow.md for current step."
|
|
382
|
-
|
|
385
|
+
if status == "unbound_ambiguous":
|
|
386
|
+
header = f"Status: {status}\nCandidates: {task_id}"
|
|
387
|
+
else:
|
|
388
|
+
header = f"Status: {status}" if task_id is None else f"Task: {task_id} ({status})"
|
|
383
389
|
return f"<workflow-state>\n{header}\n{body}\n</workflow-state>"
|
|
384
390
|
|
|
385
391
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"""Session-scoped active task resolution.
|
|
3
3
|
|
|
4
4
|
The user-facing concept is a single "active task". Trellis stores that pointer
|
|
5
|
-
per AI session/window under `.trellis/.runtime/sessions/`;
|
|
6
|
-
|
|
5
|
+
per AI session/window under `.trellis/.runtime/sessions/`; when the pointer is
|
|
6
|
+
missing, a unique developer-owned resumable task may be exposed read-only.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
@@ -168,6 +168,7 @@ class ActiveTask:
|
|
|
168
168
|
source_type: str
|
|
169
169
|
context_key: str | None = None
|
|
170
170
|
stale: bool = False
|
|
171
|
+
candidate_paths: tuple[str, ...] = ()
|
|
171
172
|
|
|
172
173
|
@property
|
|
173
174
|
def source(self) -> str:
|
|
@@ -648,17 +649,17 @@ def resolve_active_task(
|
|
|
648
649
|
platform_input: dict[str, Any] | None = None,
|
|
649
650
|
platform: str | None = None,
|
|
650
651
|
*,
|
|
651
|
-
allow_single_session_fallback: bool =
|
|
652
|
+
allow_single_session_fallback: bool = False,
|
|
652
653
|
allow_environment_context: bool = True,
|
|
653
654
|
) -> ActiveTask:
|
|
654
655
|
"""Resolve the active task from session runtime state only.
|
|
655
656
|
|
|
656
|
-
A stale session task is returned as stale.
|
|
657
|
-
|
|
658
|
-
task
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
657
|
+
A stale session task is returned as stale. Missing or unmatched session
|
|
658
|
+
identity does not infer ownership from the number of session files.
|
|
659
|
+
A unique developer-owned task, or an explicit ambiguous projection, may
|
|
660
|
+
still be exposed without writing a binding. Only the legacy inference from
|
|
661
|
+
one unrelated session file is opt-in; pull-based child agents use that
|
|
662
|
+
compatibility opt-in when they cannot inherit a parent session identity.
|
|
662
663
|
"""
|
|
663
664
|
context_key = resolve_context_key(
|
|
664
665
|
platform_input,
|
|
@@ -671,6 +672,9 @@ def resolve_active_task(
|
|
|
671
672
|
active = _active_from_ref(task_ref, repo_root, "session", context_key)
|
|
672
673
|
if active:
|
|
673
674
|
return active
|
|
675
|
+
unbound = _resolve_unbound_task(repo_root)
|
|
676
|
+
if unbound is not None:
|
|
677
|
+
return unbound
|
|
674
678
|
return ActiveTask(None, "none", context_key)
|
|
675
679
|
|
|
676
680
|
if allow_single_session_fallback:
|
|
@@ -678,9 +682,9 @@ def resolve_active_task(
|
|
|
678
682
|
if fallback is not None:
|
|
679
683
|
return fallback
|
|
680
684
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
685
|
+
unbound = _resolve_unbound_task(repo_root)
|
|
686
|
+
if unbound is not None:
|
|
687
|
+
return unbound
|
|
684
688
|
|
|
685
689
|
return ActiveTask(None, "none", context_key)
|
|
686
690
|
|
|
@@ -713,8 +717,10 @@ def _resolve_single_session_fallback(repo_root: Path) -> ActiveTask | None:
|
|
|
713
717
|
def _resolve_unbound_task(repo_root: Path) -> ActiveTask | None:
|
|
714
718
|
"""Expose one developer-owned task when no session pointer exists."""
|
|
715
719
|
sessions_dir = _runtime_sessions_dir(repo_root)
|
|
716
|
-
if sessions_dir.is_dir()
|
|
717
|
-
|
|
720
|
+
if sessions_dir.is_dir():
|
|
721
|
+
session_files = sorted(sessions_dir.glob("*.json"))
|
|
722
|
+
if any(_string_value((_read_json(session) or {}).get("current_task")) for session in session_files):
|
|
723
|
+
return None
|
|
718
724
|
|
|
719
725
|
from .paths import get_developer, get_tasks_dir
|
|
720
726
|
from .tasks import iter_active_tasks
|
|
@@ -728,11 +734,16 @@ def _resolve_unbound_task(repo_root: Path) -> ActiveTask | None:
|
|
|
728
734
|
for task in iter_active_tasks(get_tasks_dir(repo_root))
|
|
729
735
|
if task.assignee == developer and task.status in {"planning", "in_progress", "review"}
|
|
730
736
|
]
|
|
731
|
-
if len(candidates)
|
|
737
|
+
if len(candidates) == 0:
|
|
732
738
|
return None
|
|
733
739
|
|
|
734
|
-
|
|
735
|
-
|
|
740
|
+
task_paths = tuple(sorted(
|
|
741
|
+
task.directory.relative_to(repo_root).as_posix()
|
|
742
|
+
for task in candidates
|
|
743
|
+
))
|
|
744
|
+
if len(task_paths) == 1:
|
|
745
|
+
return ActiveTask(task_paths[0], "unbound", None)
|
|
746
|
+
return ActiveTask(None, "unbound_ambiguous", None, candidate_paths=task_paths)
|
|
736
747
|
|
|
737
748
|
|
|
738
749
|
def _utc_now() -> str:
|
|
@@ -125,7 +125,7 @@ def _file_digest(path: Path, maximum: int = MAX_EVIDENCE_BYTES) -> tuple[str, in
|
|
|
125
125
|
|
|
126
126
|
|
|
127
127
|
def _task_source(root: Path) -> dict[str, Any]:
|
|
128
|
-
active = resolve_active_task(root)
|
|
128
|
+
active = resolve_active_task(root, allow_single_session_fallback=True)
|
|
129
129
|
if not active.task_path:
|
|
130
130
|
raise NoDirectCurrentTask("no_direct_current_task")
|
|
131
131
|
if active.source_type == "session-fallback":
|
|
@@ -312,6 +312,11 @@ def cmd_finish(args: argparse.Namespace) -> int:
|
|
|
312
312
|
print(colored("Task exists but no direct session is bound; run task.py start first", Colors.YELLOW))
|
|
313
313
|
print(f"Task: {current}")
|
|
314
314
|
return 0
|
|
315
|
+
if active.source_type == "unbound_ambiguous":
|
|
316
|
+
print(colored("Multiple tasks exist but no direct session is bound; choose one before finishing", Colors.YELLOW))
|
|
317
|
+
for candidate in active.candidate_paths:
|
|
318
|
+
print(f"Candidate: {candidate}")
|
|
319
|
+
return 0
|
|
315
320
|
|
|
316
321
|
try:
|
|
317
322
|
assert_task_mutation_allowed(repo_root, repo_root / current)
|
|
@@ -367,6 +372,8 @@ def cmd_current(args: argparse.Namespace) -> int:
|
|
|
367
372
|
"source": active.source,
|
|
368
373
|
"stale": active.stale,
|
|
369
374
|
}
|
|
375
|
+
if active.candidate_paths:
|
|
376
|
+
payload["candidates"] = list(active.candidate_paths)
|
|
370
377
|
# Only present when the read failed, so the healthy shape is unchanged.
|
|
371
378
|
if read_error:
|
|
372
379
|
payload["error"] = read_error
|
|
@@ -374,6 +381,12 @@ def cmd_current(args: argparse.Namespace) -> int:
|
|
|
374
381
|
return 0 if active.task_path else 1
|
|
375
382
|
|
|
376
383
|
if args.source:
|
|
384
|
+
if active.source_type == "unbound_ambiguous":
|
|
385
|
+
print("Current task: (ambiguous)")
|
|
386
|
+
print("Source: unbound_ambiguous")
|
|
387
|
+
for candidate in active.candidate_paths:
|
|
388
|
+
print(f"Candidate: {candidate}")
|
|
389
|
+
return 1
|
|
377
390
|
print(f"Current task: {active.task_path or '(none)'}")
|
|
378
391
|
print(f"Source: {active.source}")
|
|
379
392
|
if active.stale:
|
|
@@ -384,6 +397,11 @@ def cmd_current(args: argparse.Namespace) -> int:
|
|
|
384
397
|
print(active.task_path)
|
|
385
398
|
return 0
|
|
386
399
|
|
|
400
|
+
if active.source_type == "unbound_ambiguous":
|
|
401
|
+
print("Multiple active tasks require explicit binding:")
|
|
402
|
+
for candidate in active.candidate_paths:
|
|
403
|
+
print(candidate)
|
|
404
|
+
|
|
387
405
|
return 1
|
|
388
406
|
|
|
389
407
|
|
|
@@ -76,7 +76,7 @@ python3 ./.trellis/scripts/task.py create-pr [name] [--dry-run]
|
|
|
76
76
|
|
|
77
77
|
> Run `python3 ./.trellis/scripts/task.py --help` to see the authoritative, up-to-date list.
|
|
78
78
|
|
|
79
|
-
**Current-task mechanism**: `task.py create` creates the task directory and (when session identity is available) auto-sets the per-session active-task pointer so the planning breadcrumb fires immediately. `task.py start` writes the same pointer (idempotent if already set) and flips `task.json.status` from `planning` to `in_progress`. State is stored under `.trellis/.runtime/sessions/`. If no context key is available from hook input, `TRELLIS_CONTEXT_ID`, or a platform-native session environment variable,
|
|
79
|
+
**Current-task mechanism**: `task.py create` creates the task directory and (when session identity is available) auto-sets the per-session active-task pointer so the planning breadcrumb fires immediately. `task.py start` writes the same pointer (idempotent if already set) and flips `task.json.status` from `planning` to `in_progress`. State is stored under `.trellis/.runtime/sessions/`. If no context key is available from hook input, `TRELLIS_CONTEXT_ID`, or a platform-native session environment variable, `task.py start` fails with a session identity hint because it cannot persist a session pointer; read-only context may still project one unique developer-owned resumable task. `task.py finish` deletes the current session file (status unchanged). `task.py archive <task>` writes `status=completed`, moves the directory to `archive/`, and deletes any runtime session files that still point at the archived task.
|
|
80
80
|
|
|
81
81
|
### Workspace System
|
|
82
82
|
|
|
@@ -123,6 +123,8 @@ python3 ./.trellis/scripts/get_context.py --mode phase --step <X.Y> # detailed
|
|
|
123
123
|
TAG ↔ PHASE scoping:
|
|
124
124
|
[workflow-state:no_task] → no active task; before Phase 1
|
|
125
125
|
[workflow-state:unbound_task] → one developer-owned task exists without a session pointer
|
|
126
|
+
[workflow-state:unbound_ambiguous] → multiple developer-owned tasks exist without a session pointer
|
|
127
|
+
[workflow-state:unbound_ambiguous-inline] → Codex inline variant of unbound_ambiguous
|
|
126
128
|
[workflow-state:task_error] → active task record is unreadable; repair it before continuing
|
|
127
129
|
[workflow-state:planning] → all of Phase 1 (status='planning')
|
|
128
130
|
[workflow-state:planning-inline] → Codex inline variant of Phase 1
|
|
@@ -196,6 +198,16 @@ Complex task: ask the user if you can create a Trellis task and enter the planni
|
|
|
196
198
|
An existing task is assigned to the current developer, but this shell has no direct Trellis session binding. Do not create a duplicate task. Continue reading and working from the existing task artifacts; before any lifecycle write or closure, run the native `task.py start <task>` command once a direct session identity is available. Never edit `.trellis/.runtime/sessions/` manually.
|
|
197
199
|
[/workflow-state:unbound_task]
|
|
198
200
|
|
|
201
|
+
<!-- Per-turn breadcrumb shown when multiple resumable tasks exist without a direct session binding. -->
|
|
202
|
+
|
|
203
|
+
[workflow-state:unbound_ambiguous]
|
|
204
|
+
Multiple active tasks belong to the current developer, but this shell has no direct Trellis session binding. Do not guess or create a duplicate task. Review the listed candidates and run `python3 ./.trellis/scripts/task.py start <task>` with the intended task once a direct session identity is available.
|
|
205
|
+
[/workflow-state:unbound_ambiguous]
|
|
206
|
+
|
|
207
|
+
[workflow-state:unbound_ambiguous-inline]
|
|
208
|
+
Multiple active tasks belong to the current developer, but this Codex session has no direct Trellis session binding. Do not guess or create a duplicate task. Review the listed candidates and run `python3 ./.trellis/scripts/task.py start <task>` with the intended task once a direct session identity is available.
|
|
209
|
+
[/workflow-state:unbound_ambiguous-inline]
|
|
210
|
+
|
|
199
211
|
<!-- Per-turn breadcrumb: shown when the active task record cannot be read. -->
|
|
200
212
|
|
|
201
213
|
[workflow-state:task_error]
|
|
@@ -702,6 +714,7 @@ All tag blocks live in the `## Phase Index` section above, immediately after eac
|
|
|
702
714
|
|---|---|
|
|
703
715
|
| No active task (before Phase 1) | `[workflow-state:no_task]` (after the Phase Index ASCII art) |
|
|
704
716
|
| One developer-owned task without a session binding | `[workflow-state:unbound_task]` (bind before lifecycle writes) |
|
|
717
|
+
| Multiple developer-owned tasks without a session binding | `[workflow-state:unbound_ambiguous]` / `[workflow-state:unbound_ambiguous-inline]` (choose and bind explicitly) |
|
|
705
718
|
| Active task record unreadable | `[workflow-state:task_error]` (repair the existing task before continuing) |
|
|
706
719
|
| All of Phase 1 (task created → ready for implementation) | `[workflow-state:planning]` (after Phase 1 summary) |
|
|
707
720
|
| Codex inline Phase 1 | `[workflow-state:planning-inline]` |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pennixrv/trellis",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.43",
|
|
4
4
|
"description": "AI capabilities grow like ivy — Trellis provides the structure to guide them along a disciplined path",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"inquirer": "^9.3.7",
|
|
35
35
|
"undici": "^6.21.0",
|
|
36
36
|
"zod": "^4.4.2",
|
|
37
|
-
"@pennixrv/trellis-core": "0.6.
|
|
37
|
+
"@pennixrv/trellis-core": "0.6.43"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@eslint/js": "^9.18.0",
|