Commit Graph

8 Commits

Author SHA1 Message Date
LZH-YS1998 5e02364eb4 fix(company): make delegation review lifecycle durable 2026-07-14 10:52:22 +08:00
LZH-YS1998 4e7aa75ba5 fix(company): stop failed children from wedging the tree and soften the dispatch guard
Root-cause fix for the project-4444 class of deadlocks: any FAILED/
CANCELLED work item with downstream dependents used to block its whole
tree forever, because the advancement gate required all dependencies to
be APPROVED and nothing ever propagated or triaged failures.

Settlement mechanism (work_item_transition.py):
- compute doomed set (FAILED/CANCELLED seeds contagious through hard
  deps); settlement-released cards are treated as alive
- three-way advancement gate: all-approved (unchanged) / settled-with-
  failures releases the nearest decision-capable card (manager parent ->
  failure-triage synthesis turn, rollup delivery/aggregate -> READY)
  with an atomic dependency_settlement stamp; released cards never
  oscillate back
- claim/park/resume/dispatcher-tick all honor the stamp: runnable gates
  admit settled failed+stuck deps, parking excludes settled deps and
  re-arms triage when a failure raced the park, engine resume no longer
  re-locks released cards, the dispatcher tick releases rollup cards
  created after the failure
- settlement cascade: once the settled card is APPROVED, stuck children
  the manager did not rebuild are cancelled (transitive closure over
  stamped stuck seeds, retried until every cancel lands)
- info-class deps never block settlement; adaptive runnable gate now
  shares DEPENDENCY_CLASS_DEFAULT with the release gates

Dispatch guard (company_mode.py):
- NO_DELEGATION_JUSTIFICATION parsing tolerates markdown decoration
  (bold/lists/quotes/full-width colon) and rejects placeholder echoes
  across all artifact/metadata/content channels
- retries exhausted no longer FAILs the work item: dispatch is a soft
  constraint, so the turn output is accepted as normal completion and
  annotated via manager_dispatch_guard_unresolved; the reminder loop is
  unchanged, and the mutation flag is now reset per turn so one past
  delegation can never mute future reminders
- manager board context now surfaces failed/cancelled children with
  their preserved output and pending-cancellation stuck list so the
  triage turn can rebuild, accept partial results, or escalate

Self-produced delegation output goes through review (persisted fact,
single predicate):
- the DONE transition classifies what a dispatch/intake/plan turn
  actually delivered from store ground truth (live children => delegated,
  none => self_produced) and persists turn_output_kind/-source on the
  WorkItem
- is_manager_reviewable_turn honors the persisted marker, so the DONE
  routing, report spawn, report completion and recovery scans all read
  the same fact — this closes a pre-existing hole where
  NO_DELEGATION_JUSTIFICATION output auto-approved with no review at all
- escalation requires a real agent manager above; top seats reporting to
  the human owner keep auto-approve (covered by final delivery's human
  acceptance) instead of minting unclaimable review cards
- dispatcher tick reconciles reviewable cards stuck in
  AWAITING_MANAGER_REVIEW with no live report/review card by rebuilding
  the report card idempotently (legacy DBs, crash windows)

Verified: 4444 tasks.db replay unwedges end to end; real-store
report->review chain exercised without lifecycle mocks (mutation check
confirms the tests bite); full suite failure set identical to a
same-session HEAD baseline run (all remaining failures pre-existing or
environment flakes reproduced at HEAD).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:19:09 +08:00
LZH-YS1998 e3bed49811 perf(office-ui): stop per-token full-tree renders, sleep hidden Phaser loop, cut broadcast churn
Company-mode UIs became unusably slow once work items and transcripts
grew. Three presentation-layer fixes, none touching work progression:

- App.tsx: buffer assistant_delta/thinking_delta store writes per task
  and flush every 80ms instead of once per token; any non-delta event
  for the same task flushes first so draft/clearDraft ordering is
  byte-identical. The unconditional per-event setUiTick (whole-app
  re-render per websocket event) is now a 300ms trailing throttle.
- PhaserGame: display:none does not stop requestAnimationFrame, so the
  office scene kept burning CPU on every other page. The loop now
  sleeps when the office page is hidden and wakes (with a parent-bounds
  refresh) on return; bridge writes stay synchronous so no state is lost.
- Kanban collab_sync debounce 0.2s -> 1.5s: the broadcaster is a
  trailing coalescer, so the final board state still always ships; each
  fire is a full-project snapshot build, which at 5/s dominated backend
  CPU on large projects. CommsPanel poll 8s -> 30s (comms_state_dirty
  push already drives freshness) and its interval no longer pins a
  stale onRefresh closure.

Verified: tsc + vite build, App.test.tsx / workItemSessions structural
tests, backend suites (company_review_flow incl. debounce push test,
kanban_push_runtime, actor_runtime_company_mode, task_mode_contract,
work_item_transition — 119 green), and canvas_smoke e2e against a real
server with zero console errors.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 16:36:39 +08:00
LZH-YS1998 5aa57e69ee fix: release comms-blocking parks when replies arrive and give every role file authoring tools
Project 3333 forensics: env_engineer sent a blocking question to the CTO,
got a full reply 96s later, and still deadlocked the whole run — the
park half (`_park_for_blocking_comms`) had no wired consumer, so
WAITING_FOR_PEER work items could never be released.

Unpark path (root fix):
- The dispatcher loop now calls `_try_unpark_blocking_comms` each tick
  for parked, non-in-flight tasks; blocking replies land as durable
  inbox files, so the check is read-only until all replies are present.
- `_try_unpark_blocking_comms` accepts orphaned waits (peer_wait
  stripped by the legacy resolver while the work item stayed parked)
  and falls back to the park predicate itself: an empty unresolved
  blocking outbox releases the task, anything pending keeps it parked.
- `resolve_task_peer_wait` no longer touches comms_blocking waits (it
  flipped task.status without the work-item phase and stripped the
  peer_wait evidence); `_resume_peer_checkpoint` re-enters the company
  runtime for comms/orphaned waits and lets the dispatcher converge.

File tools (defaults changed at their declaration sites, honoring the
"empty tools = everything, explicit list = exactly that" contract):
- corporate builtin groups gain file_write/file_edit for coordination,
  QA, and data-acquisition roles.
- all shipped org YAML role tool lists gain the missing
  file_write/file_edit entries.
- coordination turn modes no longer strip file_write/file_edit at
  runtime — in-context content (briefs, matrices) must be persistable
  instead of getting trapped in blocking DM hand-offs.

Also includes the pending office_ui ws_handler change from the working
tree.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 21:06:39 +08:00
LZH-YS1998 447516d93c fix: converge approval park/resume through the work-item state machine
Four coordinated fixes for company runs permanently stalling around
tool-approval parks (project 1111 forensics + live reproduction):

- company executor dispatch loop now exits convergently when nothing is
  in flight and every remaining task waits on a human: immediate parked
  exit when all waiters have pending checkpoints, bounded stall ticks
  otherwise. Previously it polled sleep(5) forever, hanging the turn
  (observed 7.5h), never answering the user, and holding claims that
  blocked any later rescue turn.
- _resume_task_checkpoint routes work-item runtime tasks through the
  delegation state machine: release the human wait via the legal
  AWAITING_HUMAN -> READY recovery exit, clear stale claims, and hand
  the item back to the dispatcher (runtime-snapshot fallback when the
  checkpoint payload lacks a plan) instead of a detached single-agent
  re-run that never advances the work item phase.
- startup recovery reverse self-heal: an awaiting_human task whose park
  checkpoint was already resolved (human answered, resume cut off before
  the phase write) is reopened for dispatch instead of being preserved
  as a wait nobody can end. Runs before the metadata-plan gate since
  modern runs no longer carry the plan in task metadata.
- pause checkpoints record execution_mode from the durable
  work_item_runtime marker instead of volatile task metadata, which
  degraded to task_mode after a first resume and misrouted the next one.

Validated end-to-end on a live native-agent minimal-org run
(park -> approve -> re-park -> approve -> complete -> deliver, plus
synthesized crash-between-resolve-and-phase-write healed on restart).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 10:52:02 +08:00
LZH-YS1998 b4d28aefeb fix: stop misreading per-work-item spec as run-level company plan
Every completed company session's follow-up was answered with the canned
"Legacy company runtime run ... read-only" text (project 000, 2026-07-07).
Root cause: snapshot loaders read task.metadata work_item_runtime_plan as a
serialized run-level CompanyWorkItemRuntimePlan, but work-item tasks persist a
per-item assignment spec (projection_id/turn_type/summary/deliverables/...)
under that key. from_dict on the wrong shape silently yields an empty plan
(no projections, empty metadata), _runtime_uses_multi_team_org returns False,
and the resume path falls through to the legacy read-only branch.

Add is_serialized_company_work_item_runtime_plan (a run-level plan always
serializes with projections + runtime_model; a spec always carries
projection_id) and route all full-plan metadata reads through
serialized_company_plan_from_metadata, which skips wrong-shaped candidates so
the loaders fall back to the sample-metadata-constructed plan instead of an
empty one. Fixes existing DBs read-side; no data migration.

Verified: old path on the 000 shape classifies multi_team_org=False, new path
True; regression tests cover shape discrimination, snapshot classification,
and the follow-up never reporting legacy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 22:33:39 +08:00
LZH-YS1998 08e48c2f9c fix(logging): replace stdlib-style exc_info kwargs with loguru opt(exception=)
Loguru has no exc_info kwarg: extra kwargs are str.format() arguments, so
logger.error(f"...{e}", exc_info=True) forces .format() on the rendered
message — any error text containing braces (e.g. a JSON error body) raises
KeyError FROM the log call itself, escaping the surrounding except block and
killing the caller (observed: whole agent turns dying in benchmark runs).
The intended traceback was also never logged, since exc_info is not a loguru
feature.

Batch fix of all 143 sites across 11 files:
  logger.X(msg, exc_info=True) -> logger.opt(exception=True).X(msg)
  (one exc_info=exc site -> opt(exception=exc))
Messages are byte-identical; with the kwarg gone loguru never calls
.format(), so brace-containing f-string messages are inert.

Verified: AST post-conditions per file, py_compile, import smoke of all
modules, behavioral equivalence of the 3 patterns, full unit suite (1549
passed) with a failure set identical to the pristine tree (22 pre-existing,
zero regressions).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Also add pixel-agents to the README acknowledgements.
2026-07-03 20:07:37 +08:00
LZH-YS1998 d78931979d Initial commit 2026-07-01 17:56:31 +08:00