feat: release 2.2.0 with PC-only tests and execution reliability fixes
Unify task scheduling and queue state, fix Android input and observation recovery, preserve screenshot paths, and match final review criteria accurately. Add collapsible copyable results and publish the complete 2.1.0 upgrade notes. Validation: 3189 backend tests, 326 frontend tests, 24 client tests, production build and quality gates.
This commit is contained in:
1 parent
fac4ccb760
commit
be6e8c2abd
79 files changed
+2263
-277
No files matched your search
+24
-7
@@ -716,7 +716,23 @@ async def _process_plan_write(
|
||||
new top-level completions. ``declared_intent`` marks surgical update_note
|
||||
edits, which are exempt from the full-rewrite hand-slip check.
|
||||
"""
|
||||
rejection_message = check_plan_mutation_rejections(content_before, content_after, state)
|
||||
before_snapshot = parse_plan(content_before)
|
||||
after_snapshot = parse_plan(content_after)
|
||||
before_counts = Counter(before_snapshot.milestone_texts)
|
||||
duplicate_milestones = [
|
||||
text
|
||||
for text, count in Counter(after_snapshot.milestone_texts).items()
|
||||
if count > max(1, before_counts[text])
|
||||
]
|
||||
rejection_message = (
|
||||
"Your task_plan write was not applied because it introduced duplicate top-level "
|
||||
f"milestones: {duplicate_milestones!r}. When using update_note, replacement must "
|
||||
"only replace target; do not copy milestones outside target into replacement. "
|
||||
"Expand target if replacing the whole section. Use distinct milestone titles "
|
||||
"for intentionally repeated phases. Read the current plan before correcting it."
|
||||
if duplicate_milestones
|
||||
else check_plan_mutation_rejections(content_before, content_after, state)
|
||||
)
|
||||
if rejection_message is None and not declared_intent:
|
||||
rejection_message = check_unintended_rewrite(content_before, content_after)
|
||||
if rejection_message:
|
||||
@@ -724,10 +740,13 @@ async def _process_plan_write(
|
||||
if task_plan_path.exists():
|
||||
task_plan_path.write_text(content_before, encoding="utf-8")
|
||||
|
||||
# Guidance, not a tool failure: status stays "success" so the Operator
|
||||
# loop treats it as feedback content (matching the legacy envelope
|
||||
# behavior where the SystemMessage carried no error status).
|
||||
return ToolMessage(content=rejection_message, tool_call_id="", status="success")
|
||||
# Duplicate writes are failed edits: defer dependent actions and let the
|
||||
# Operator correct the target. Preserve legacy feedback for other guards.
|
||||
return ToolMessage(
|
||||
content=rejection_message,
|
||||
tool_call_id="",
|
||||
status="error" if duplicate_milestones else "success",
|
||||
)
|
||||
|
||||
# Deterministic check-line guard (Operator-source writes only; the Planner
|
||||
# node writes through unwrapped tools and retains revision authority):
|
||||
@@ -748,8 +767,6 @@ async def _process_plan_write(
|
||||
# nor retired (the ledger, the harvest gate and the run outcome all key a
|
||||
# check item by ``(kind, text)``). Only a line whose ``(kind, text)`` is
|
||||
# gone from the new plan counts as dropped.
|
||||
before_snapshot = parse_plan(content_before)
|
||||
after_snapshot = parse_plan(content_after)
|
||||
missing = missing_check_items(before_snapshot, after_snapshot)
|
||||
unprotected = getattr(ctx, "guidance_unprotected_checks", None)
|
||||
if not isinstance(unprotected, set):
|
||||
|
||||
Reference in new issue
Block a user