okstra 0.165.0 → 0.165.1
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/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -112,9 +112,26 @@ def _data_to_report_path(data_path: Path) -> Path:
|
|
|
112
112
|
return final_report_markdown_path(data_path)
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
def _project_id_of(task_key: str) -> str:
|
|
116
|
+
"""`project-id:task-group:task-id` 의 첫 세그먼트, 아니면 "".
|
|
117
|
+
|
|
118
|
+
A follow-up belongs to its parent's project by construction, so the new
|
|
119
|
+
task-key inherits that segment rather than re-deriving it from disk.
|
|
120
|
+
Omitting it is not a cosmetic difference: `okstra_project.state.
|
|
121
|
+
parse_task_key` raises on a two-segment key, and every catalog reader goes
|
|
122
|
+
through it, so one `<group>/<id>` entry took down `list_project_tasks` for
|
|
123
|
+
the whole project — the wizard could not render its first screen.
|
|
124
|
+
"""
|
|
125
|
+
parts = (task_key or "").split(":")
|
|
126
|
+
if len(parts) != 3 or not all(parts):
|
|
127
|
+
return ""
|
|
128
|
+
return parts[0]
|
|
129
|
+
|
|
130
|
+
|
|
115
131
|
def _spawn_one(
|
|
116
132
|
*,
|
|
117
133
|
project_root: Path,
|
|
134
|
+
project_id: str,
|
|
118
135
|
task_group: str,
|
|
119
136
|
parent_task_key: str,
|
|
120
137
|
parent_report_relative: str,
|
|
@@ -151,7 +168,7 @@ def _spawn_one(
|
|
|
151
168
|
origin = row["origin"].strip()
|
|
152
169
|
priority = (row.get("priority") or "P1").strip()
|
|
153
170
|
ticket_id = (row.get("ticketId") or "").strip()
|
|
154
|
-
new_task_key = f"{task_group}
|
|
171
|
+
new_task_key = f"{project_id}:{task_group}:{new_task_id}"
|
|
155
172
|
now = dt.datetime.now(dt.timezone.utc).isoformat()
|
|
156
173
|
|
|
157
174
|
spawned_meta: dict = {
|
|
@@ -257,6 +274,16 @@ def main(argv: list[str]) -> int:
|
|
|
257
274
|
print(f"data.json not found: {args.data_file}", file=sys.stderr)
|
|
258
275
|
return 1
|
|
259
276
|
|
|
277
|
+
project_id = _project_id_of(args.parent_task_key)
|
|
278
|
+
if not project_id:
|
|
279
|
+
print(
|
|
280
|
+
f"--parent-task-key must be project-id:task-group:task-id, got "
|
|
281
|
+
f"{args.parent_task_key!r} — a spawned task-key derived from it "
|
|
282
|
+
"would break every catalog read in this project.",
|
|
283
|
+
file=sys.stderr,
|
|
284
|
+
)
|
|
285
|
+
return 1
|
|
286
|
+
|
|
260
287
|
try:
|
|
261
288
|
data = json.loads(args.data_file.read_text(encoding="utf-8"))
|
|
262
289
|
except json.JSONDecodeError as exc:
|
|
@@ -297,6 +324,7 @@ def main(argv: list[str]) -> int:
|
|
|
297
324
|
continue
|
|
298
325
|
status, info = _spawn_one(
|
|
299
326
|
project_root=args.project_root,
|
|
327
|
+
project_id=project_id,
|
|
300
328
|
task_group=args.task_group,
|
|
301
329
|
parent_task_key=args.parent_task_key,
|
|
302
330
|
parent_report_relative=parent_report_relative,
|
|
@@ -4658,10 +4658,16 @@ def _sim_advance(state: WizardState, prompt: Prompt) -> None:
|
|
|
4658
4658
|
STEP_BY_ID[prompt.step].submit(state, _sim_answer(prompt))
|
|
4659
4659
|
if prompt.step not in state.answered:
|
|
4660
4660
|
state.answered.append(prompt.step)
|
|
4661
|
-
except WizardError:
|
|
4662
|
-
#
|
|
4663
|
-
#
|
|
4664
|
-
#
|
|
4661
|
+
except (WizardError, PrepareError):
|
|
4662
|
+
# 기본답이 거부되는 두 경우만 전진 처리한다 — 입력 검증이 막는 드문 text
|
|
4663
|
+
# 분기(WizardError), 그리고 답 자체는 유효하나 대상의 도메인 상태가 그
|
|
4664
|
+
# 경로를 막는 경우(PrepareError). 후자가 빠져 있어 승인 게이트가
|
|
4665
|
+
# `blocked-by-disagreement` 인 계획이 최신 태스크이면 위저드가 첫 화면조차
|
|
4666
|
+
# 내지 못하고 죽었다: 시뮬레이터가 기본 경로를 따라가다 그 계획을 고르고,
|
|
4667
|
+
# 진행률 라벨 하나 때문에 run 전체가 시작 불가가 됐다. 시뮬레이션은 분모
|
|
4668
|
+
# 추정이므로 막힌 경로를 만나면 그 화면을 지났다고 치고 계속 세면 된다.
|
|
4669
|
+
# KeyError/AttributeError 등 실제 버그는 여전히 삼키지 않고 그대로
|
|
4670
|
+
# 전파시켜, progress 라벨이 그럴듯하게 틀리는 대신 시끄럽게 실패한다.
|
|
4665
4671
|
members = prompt.questions if prompt.kind == "pick_group" else [prompt]
|
|
4666
4672
|
for p in members:
|
|
4667
4673
|
if p.step not in state.answered:
|