feat(orgtrack): WorkItem + Routine PM protocol v1 (phases 0-8) - #683
Open
Neonforge98 wants to merge 33 commits into
Open
feat(orgtrack): WorkItem + Routine PM protocol v1 (phases 0-8)#683Neonforge98 wants to merge 33 commits into
Neonforge98 wants to merge 33 commits into
Conversation
Phase 0 of the WorkItem+Routine CLI protocol: JSON Schemas for the envelope, ExecutionContext, WorkItem, Routine and RoutineRun; success fixtures per command family plus one golden fixture per stable error code (18); decisions.md freezing mode/capability matrix, error-to-exit mapping, CLI carrier (separate console binary org2-pm aliased as org2), provider id namespace, hook naming, workspace manifest, ORGII_* env names and the pm_change_seq cross-process wake protocol; entry-point parity matrix scaffold. Fixtures are ajv-validated against the schemas. Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 1 of the Orgtrack PM protocol migration. Work Item Manager persona removed: builtin definition + prompt, registration (12 -> 11 builtin agents), the prompt.rs special case (the linked-work-item context now keys on session agent_role "custom" + work_item_id instead of agent identity), Create-with-AI default agent falls back to builtin:os, hero copy, docs, and E2E specs now exercise the same scenarios through the generic OS agent. Dead visual-automation surface removed after call-chain tracing: the AgentOrgs workflow editor cluster (AutomationWorkflowStep, editor content/nodes, EditPanel, DnD hooks, action catalog, flattenWorkflow), the shadowed AgentOrgs/types directory, AutomationTriggerConfig wizard variant, RulesMemoryEvolution automation types/detail views (page is markdown-rules-only now), the six dormant agent_automation_* Tauri commands plus their handler registrations and companion-md generation, and the never-functional packages/orgtrack npm stub. The Rust automation engine itself stays - it is live via the channel-enable path; its cron-path duplication is Phase 5 scope. The read-only workflowDragActiveAtom in GlobalDragDrop is recorded as Phase 8 cleanup. manage_work_item / manage_project are frozen (no new behavior) until their callers migrate to the unified work application service. Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2a of the Orgtrack PM protocol migration. New work_service module in project_management: the portable WorkItem state machine (open/in_progress/blocked/completed/failed/cancelled) with a legacy-status mapping and the full transition matrix from the frozen v1 contract, including the in_progress->open release edge. The atomic RMW choke point now threads AtomicServiceOptions: optimistic concurrency via expected_revision against local_version (typed PM_ERR:REVISION_CONFLICT sentinel), strict-vs-flag-only FSM validation on status changes, and an audit label/reason. Every mutation - UI patch, agent tool, sync merge, future CLI - commits an append-only pm_audit_events row and bumps the single-row pm_change_seq cross- process watermark in the same transaction (new DDL, plus pm_idempotency for the Phase 3 wire contract). acquire/release_execution_lock audit as work.claim / work.release - the local execution lock is the claim record the portable work.claim operation absorbs. New project_transition_work_item Tauri command exposes the strict service path; legacy paths stay fail-open with violations flagged in the audit stream until Phase 7 flips them. 12 new unit/integration tests; full crate suite 542 passing. Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2b (step 1) of the Orgtrack PM protocol migration. New canonical work.create application operation: a typed CreateWorkItemRequest DTO with a single Rust construction site for WorkItemFrontmatter, exposed as project_create_work_item / work_item_create_standalone and audited (work.create + pm_change_seq). Short-id allocation stays caller-side because collab orgs mint ids on the server. All five TS frontmatter-literal construction sites are gone: useWorkItemActions, createWorkItemFromDraft, useAiWorkItemCreator and ProjectPanelView kanban-add now call work.create; the AI creator's post-launch linked-session write, StandaloneWorkItemDetailPage and WorkItemPanelView switch from client-side merge + whole-row writeStandaloneWorkItem (lost-update race) to the atomic partial-update command. The whole-row write commands remain for sync/merge internals and E2E seeding only. Pre-commit hook ran. Total eslint: 18, total circular: 0
Phase 2b (step 2): the two agent-tool WorkItemFrontmatter construction sites (tool_infra create_work_item and manage_work_item standalone create) now build a CreateWorkItemRequest and call the audited work.create service handlers instead of assembling 32-field frontmatter literals and invoking the whole-row write. CreateWorkItemRequest gains the starred field the agent surface supports. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 2b (step 3): new public update_standalone_work_item_atomic closure API on the atomic choke point (same IMMEDIATE-transaction, history, audit, watermark and collab-bridge semantics as the partial path). The four manage_work_item standalone sites - update, delete, link_session, unlink_session - stop doing client-side read-modify-write followed by a whole-row write and run their existing mutation logic inside the transaction instead. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (vertical slice 1) of the Orgtrack PM protocol migration. New orgtrack-pm-cli crate producing the org2-pm console binary (PATH- aliased as org2 by the distribution; the GUI binary stays untouched per frozen decision 4). Implements the orgtrack/v1 wire contract: - success/error envelopes with the 18 stable error codes and the frozen error-to-exit table (PROJECT_MODE_REQUIRED=5, PERMISSION_DENIED=8); - context resolver: flags -> ORGII_* env -> .orgii/orgtrack.json manifest, mode default build, frozen mode-capability matrix (only project exposes the 12-capability mutation surface); - the eight work commands: list (--ready = portable open + unclaimed), show (revision + relations), create/update via the service, claim (execution-lock CAS composed with the strict open->in_progress transition, lock rolled back on rejection), transition (portable states only; in_progress is claim-only), note (append-only comment, audited as work.note), relate (typed edge in the new pm_relations table); - PM_ERR sentinel mapping onto stable wire codes; idempotent schema init on the canonical store path for entry-point init parity. work_service grows read_project_work_item_revision, note/relate handlers and the pm_relations DDL. Cross-process E2E drives the real binary against a sandbox store: external shell agent completes a work item end to end, pm_change_seq watermark advances per mutation, audit stream carries work.claim/note/relate/transition. Residuals documented in-code: dependsOn joins --ready with Phase 4, single-transaction claim replaces the compose when the claim handler lands, --idempotency-key dedup is the next slice. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (slice 2): work_service::run_idempotent guards operations by (actor, operation, scope, key) against the pm_idempotency table - an exact replay returns the stored response without re-executing, a different canonical request under the same key returns IDEMPOTENCY_CONFLICT (exit 4). The CLI create/claim/transition commands run their cores through the guard when --idempotency-key is supplied. E2E covers replayed claim (a re-run would trip INVALID_TRANSITION) and the conflict path. Post-commit record residual documented in-code. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 3 (slice 3): the mode enum had four divergent enumerations (Rust AgentExecMode, the caller-less agent_list_modes command returning a 4-entry catalog, the TS AGENT_EXEC_MODES picker, and MODE_LABELS with 5 entries missing wingman). agent_list_modes and its whole TS chain (rpc procedure, schema, wrapper, AgentExecModeConfig type) are deleted; MODE_LABELS is now derived from ALL_AGENT_EXEC_MODES so it can never drift again. AGENT_EXEC_MODES in sessionCreatorConfig.ts remains the single user-facing picker list, as a deliberate curated subset of the canonical wire-value set. Prerequisite for adding the product-mode axis without minting a fifth list. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 1) of the Orgtrack PM protocol migration.
New routine_service module: the portable Routine spec types mirror the
frozen routine.schema.json exactly (deny_unknown_fields makes model/
account/workspace/credential fields unparseable - the boundary the
legacy RoutineDefinition violated), with graph validation (step-id
shape/uniqueness, needs referencing, Kahn acyclicity, ${...} mapping
expressions resolving only to declared inputs/outputs of declared
dependencies, 5-field cron + timezone) and deterministic
canonicalization for the immutable snapshot hash.
routine.apply lands on the new pm_routines table: same canonical body
keeps the revision (idempotent), changed body bumps it, historic runs
untouched, audited + watermarked in the same transaction.
pm_routine_runs DDL ships alongside for the invoke slice. The frozen
Phase 0 fixture parses and validates through the real types - the
schema/fixture/implementation triangle closes in CI.
Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 2): routine.invoke snapshots the current revision into an immutable pm_routine_runs row, validates inputs against the snapshot contract (typed ROUTINE_INPUTS_INVALID sentinel), and materializes the graph through the canonical work.create handler: one root WorkItem from the substituted template, one generated child per step (instruction + input mappings + actor requirement rendered into the body), parented to the root. Dependencies and run provenance are durable typed relations (depends_on work://<scope>/<id>, generated_by run://<id>) in pm_relations - not a singleton routine_source. Manual and scheduler invocations share this single entry point. 12 service tests green; full crate suite passing. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 3): the org2 CLI gains the routine namespace - list/validate/apply/run/status/enable/disable per the frozen contract (cancel returns UNSUPPORTED_CAPABILITY until the Phase 5 cancel_requested machinery). Spec files parse as YAML or JSON through one parser; validation failures surface as INVALID_ARGUMENT with the structured violation list in details. routine run collects repeated --input k=v pairs. routine_service grows list/set_enabled and run_status: the durable view joins pm_routine_runs with the generated items (reverse generated_by lookup) and recomputes the overall status with the ordered projection from design section 11, including dependency-aware readiness through the depends_on relations. E2E: full lifecycle through the real binary - apply idempotency, materialized 3-step graph, claim + complete a step, projection stays running as downstream becomes ready. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 4 (slice 4, completing the phase): one-way additive converter from legacy RoutineDefinition rows to portable specs. CreateWorkItem and DirectSession modes become single-step routines (prompt = step instruction) with the legacy concurrency/catch-up policies mapped onto the portable enums; model/account/harness/workspace/agent targets are reported as execution-binding warnings for operator re-setup - the boundary the portable model enforces. UpdateExistingWorkItem routines are reported as skipped (portable --root-work runs land with Phase 5), and one-shot triggers convert to manual with the timestamp noted. Legacy rows stay untouched until the Phase 5 scheduler unification, so nothing can double-fire. convert_all() produces the written report the migration plan requires. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 1) of the Orgtrack PM protocol migration. The routine scheduler tick gains a portable pass: pm_routines schedule activations fire through the canonical routine.invoke - the same entry manual CLI runs use - with per-candidate watermarks, croner-evaluated due windows (UTC for now; the declared timezone rides in the spec), and the frozen concurrency semantics: an active run suppresses the fire with an audited routine.fire_suppressed event (skip/coalesce; queue suppresses too until pending-run dequeue lands), and a missing scope binding is audited rather than silently dropped. Startup now converts legacy RoutineDefinitions into portable specs and DISABLES the converted legacy rows in the same pass, so each routine is only ever driven by one scheduler - no double-fire window. The written conversion report lands at ~/.orgii/routine-conversion-report.json. CreateWorkItem routines carry their project slug over as the host-local default_scope binding (never part of the portable hash). A cross-process watermark poller notices pm_change_seq bumps from external writers (the org2 CLI) and emits the data-changed event so the UI refreshes (design 13.0). project_management suite: 555 green. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 2): the automation engine was the third parallel cron executor (routine scheduler, work-item scheduler, automation timer). Cron-triggered AutomationRules still load - no persisted-data break - but no longer spawn an execution loop; the audit-visible warn points at Routines as the recurring-execution home. The dead spawn_cron loop is deleted. Timer/scheduled-time/file-watch/webhook/channel triggers are untouched (they are event triggers, not recurring execution). Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 5 (slice 3, completing the phase): the session-terminal -> work-item completion chain is no longer a silent side effect. Every orchestrator transition through state_machine::mutate_work_item is now audited as an explicit work.transition carrying the reason "completion policy: orchestrator session terminal" - the durable form of the default completion policy from the design (§17), ready to grow an org-level off switch. Stale-signal rejection (§12.4): a terminal event from a session that no longer holds the item's execution claim returns the new TransitionResult::Ignored instead of completing a newer episode - late signals from superseded sessions cannot close current work. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 6 of the Orgtrack PM protocol migration. ProviderBinding leaves the extras_json blob: pm_provider_bindings is a relational table keyed (work_item_id, provider) with an index on (provider, external_id). apply_remote_merge dual-writes it in the same transaction as the legacy blob (identity can never split-brain), and find_by_external_ref takes the indexed path first, lazily backfilling pre-migration bindings it still finds via the legacy scan - the documented linear-scan pressure point is gone for synced projects. provider_host grounds the section-15 capability profiles in code: the bundled org2 provider (execution + provenance), the sync framework adapters surfaced as planning-profile providers (SyncAdapter/ AdapterDescriptor ARE the planning host interfaces; Phase 8 aligns the names), and the canonical external CLI providers as reference-only provenance. validate_session_ref enforces the frozen namespace at the CLI boundary: claim --session-ref and relate session:// targets reject hook short names (claude) with a pointer at the canonical id (claude_code). CLI E2E covers the rejection; 559 crate tests green. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 1) of the Orgtrack PM protocol migration. agent_sessions gains a product_mode column (build|plan|ask|project, NULL = build) via the established try_migrate ALTER path - the single source of truth for persistent WorkItem/Routine mutation intent, distinct from the runtime exec mode (AgentExecMode) per the frozen 5.2 split. The upsert preserves it against background refreshes (same posture as agent_exec_mode); update_product_mode is the explicit write path. The frozen resolver precedence is wired server-side: launching from a WorkItem forces project (rule 1), non-interactive routine invokes are project context (5.1), the explicit launch-time choice is validated against the closed enum, everything else stays NULL/build. Wire: SessionLaunchParams.productMode rides next to mode; AgentRunLaunchRequest carries it through every constructor. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 1): workflowDragActiveAtom could only be set by the visual workflow editor deleted in Phase 1 - since then it was a permanently-false input to GlobalDragDrop internal-drag detection. The atom, its store file, the ref plumbing through useGlobalDragDrop / useBrowserDragDrop and the isInternalDrag parameter are gone. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 2): the last code-side reference to the retired persona id is reworded; git grep for work-item-manager / the deleted workflow editor symbols / the drag atom now returns zero hits outside docs and locale files. Full suites green: project_management 559, orgtrack-pm-cli 6 (incl. cross-process binary E2E). Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 8 (slice 3): manage_project loses its duplicate work-item CRUD surface (list_items/read_item/create_item/update_item/delete_item) - the audit finding that deleting manage_work_item alone would not consolidate. The retired actions return structured guidance pointing at the manage_work_item equivalent instead of a trajectory-visible execution error, so agents self-correct in one step. manage_project keeps what manage_work_item does not have: project CRUD, members, global find, and start_item (orchestrator launch). The work-item param helpers - including the cron schedule entry point retired in Phase 5 - leave with the surface; the schema and LLM descriptions shrink to match. Pre-commit hook ran. Total eslint: 19, total circular: 0
The debug/test HTTP launch paths and the benchmark launcher construct SessionLaunchParams literally; they pass product_mode: None (= build) like every other default caller. Restores the full-workspace build after the Phase 7 product-mode axis landed. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 2): the productMode data path is now complete in both directions. Read: SessionAggregateRecord carries the column through every conversion site (CLI/imported sessions report None = build), the zod schema and the frontend Session type mirror it camelCase. Write: session_patch accepts productMode for agent sessions only, validated against the closed build|plan|ask|project enum on the Rust side so a typo can never silently grant or drop the Project mutation surface. The UI layer (ModePill entry, Project panel, Runs view) can now read session.productMode and patch it through the existing channel. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 3): the one user-visible mode selector gains Project as its fourth entry, per the frozen 5.2 split - the selector writes the PRODUCT mode and the runtime exec mode is derived (project -> build, identity otherwise). In-session selection patches both fields through the session aggregate; a Project session pins the pill to Project regardless of the derived exec mode, exits by picking Build/Plan/Ask (work items are never deleted or completed by leaving - only the mutation surface stops being exposed), and the reset-on-click shortcut is disabled so a stray click cannot silently drop Project mode. The creator/controlled pill variants stay exec-only until the Project bootstrap flow lands there. COMPOSER_MODES extends the canonical list - no fifth divergent catalog. Pre-commit hook ran. Total eslint: 19, total circular: 0
Phase 7 (slice 4): the frozen 5.1 capability boundary is now enforced at the tool-policy layer. ResolvedToolPolicy::with_modes composes the exec-mode overlay with a product-mode deny-delta: sessions whose product_mode is not project (build/plan/ask or unset) subtract manage_work_item and manage_project from whatever the agent definition allows. Deny-delta preserved - Project never grants tools beyond the definition, so switching modes cannot escalate an actor. Both composition consumers switch to with_modes: the per-turn executor (reading the session row like the prompt path does) and the effective-tools RPC. Work-item/routine-launched sessions resolve to project server-side, so orchestrator flows and Create-with-AI keep their tools; ordinary Build chat now follows the design - switch to Project to manage the persistent work graph. Pre-commit hook ran. Total eslint: 19, total circular: 0
The deny-delta gate on manage_work_item/manage_project left several launch paths without a way to reach product_mode=project: - SessionLaunchParams zod schema now accepts productMode, and the Create-Project-with-AI creator passes project explicitly (the flow's whole purpose is manage_project, but it launches without a workItemId so the resolver defaulted it to build). - agent_link_session_to_work_item now sets product_mode='project' — the same rule the launch resolver applies — so post-hoc-linked sessions stop getting the linked-work-item prompt with the tool denied. - Agent Org member sessions inherit project when launched from a work item (previously only the coordinator got it). - The subagent overlay re-applies the parent's product mode next to its exec mode, closing the delegation escalation through both the inherited-policy and fresh-registry dispatch paths. - debug_session_execute_tool now enforces the same with_modes composition as the LLM path instead of bypassing policy. - The channel behavioral rules and task-routing prompt sections only advertise the PM tools when the effective (policy-filtered) tool surface actually contains them. - E2E: the launchSession helper maps agentExecMode to the wire name 'mode' (previously silently dropped), and the two work-item-manager probes launch with productMode=project so they exercise the gated path. Pre-commit hook ran. Total eslint: 19, total circular: 0
Real-machine testing caught two gaps in the Phase 7 UI slice: - usePatchSession dropped productMode on the wire: the optimistic write, the change detector and the RPC patch object all skipped the field, so picking Project in the composer only persisted the derived exec mode. Forward it through all three. - The in-session ModePill was mounted with hideWhenDefault, which hides the pill for Build sessions — making the Project entry unreachable from exactly the state every new session starts in. Mount it always-visible in-session. Also give org2-pm a --help response that lists the command surface instead of 'Flag --help requires a value'. Pre-commit hook ran. Total eslint: 19, total circular: 0
The remaining §7.2 product surfaces: - Host-side root bootstrap: a Project session with no active WorkItem creates and links its root when the first non-empty user submission is accepted (never on mode switch or empty open), body preserving the original request, under a sessionRef-derived idempotency key so a create-then-link-failure retry replays the same root instead of duplicating it. - Track this / Convert to Project: new agent_track_session_as_project command switches the session to product_mode=project, derives the exec mode the way the composer picker does, invalidates Plan mode's snapshot/restore state (also done when session_patch flips a session to project) and creates-or-replays the root from the recorded first input. Surfaced as a session-header menu action next to Link Work Item. - Active WorkItem indicator: Project sessions show a shortId+status pill in the session context bar (now actually mounted above the transcript); clicking opens the real Work Item panel. Standalone bootstrap roots render as a static badge. - Runs surface: routine_service::list_runs + project_list_routine_runs / project_routine_run_status commands expose pm_routine_runs, with a Runs entry in the sidebar BROWSE section and an expandable run list showing each run's generated WorkItems and recomputed ordered status projection. Pre-commit hook ran. Total eslint: 19, total circular: 0
On-device Track-this runs hit a FOREIGN KEY failure: session rows carry scopes with no local orgs row — the implicit personal-org and cloud:<uuid> sidebar scopes — and the standalone store's org FK rejects both. The bootstrap now keeps the org only when it exists in the local orgs table and otherwise falls back to the NULL (personal) standalone scope, the same normalization WorkItemTool applies. Residual (documented): standalone short ids are per-scope, so a root created under the personal fallback can share a short id with an org-scoped item; each session resolves its link through its own org context. Pre-commit hook ran. Total eslint: 19, total circular: 0
Neonforge98
force-pushed
the
feat/orgtrack-pm-protocol
branch
from
August 5, 2026 18:48
b3d4049 to
993bad1
Compare
On-device the completion policy never landed: proof-of-work collection shells out to git (rev-parse, default-branch probe, diff stats) and ran INSIDE the work item's BEGIN IMMEDIATE transaction. On this machine a GUI-spawned git hangs (the same commands finish in ~1s from a shell), so the open transaction held the projects.db write lock indefinitely — starving the sync worker's gc, the org2-pm CLI (STORE_UNAVAILABLE) and every later completion attempt, while the execution lock stayed claimed and Start Agent stayed disabled. Split collect_proof_of_work into a subprocess-side collector that runs BEFORE the transaction opens and a pure in-memory apply step inside the mutator, and bound the collector with a 10s timeout so a sick git degrades to completing without diff stats instead of stalling the policy. Verified on-device: the timeout fired, the transition then committed in ~20ms (session terminal -> audited work.transition -> status completed, execution lock released). Pre-commit hook ran. Total eslint: 19, total circular: 0
Sending the Launchpad's AI work-item creator bounced the user back to the start page's Session tab with a cleared composer — the created item and its running agent were invisible until a toast minutes later. The post-launch handler reset createTarget to AGENT_SESSION, which the start page maps to the Session tab. - The AI work-item flow now opens/focuses the launched session's chat tab and makes it the active session; the active-WorkItem pill in the session context bar carries the created item. - Create-Project-with-AI gets the same treatment (it previously stayed on a blank creator with only the delayed toast). - The background-completion toast's copy says 'Open the Session' — it now has an actual Open Session action instead of being a dead end. Pre-commit hook ran. Total eslint: 19, total circular: 0
The draft-fill session launched whatever agent the composer had
selected — usually SDE, whose definition does not register
manage_work_item at all (the Work Item Manager persona retirement
assumed builtin:os would carry the flow, but nothing forced it). The
model then tool-searched, reported the tool missing and finished
without ever filling the draft.
The work-item launch context now carries an agentDefinitionId override
(builtin:os) for the fill session; the item's assignee is untouched.
Also stop linked sessions from narrating plumbing at the user
('This session is already linked to Work Item WI-0003…') — the
linked-work-item prompt block now says to apply the linkage silently.
Pre-commit hook ran. Total eslint: 20, total circular: 0
A follow-up like 'write me another work item' in a linked session overwrote the linked item's title and body wholesale (observed on-device: WI-0005 went from an alphabet-song request to a fabricated sample bug). The linked-work-item prompt block now scopes the update-not-duplicate rule to iterations on the ORIGINAL request and directs new/additional item requests to manage_work_item(create_item), matching the reference semantics in Multica where agent creates are always new issues (with a server-side duplicate guard) and existing issues are never repurposed. Pre-commit hook ran. Total eslint: 18, total circular: 0
Neonforge98
force-pushed
the
feat/orgtrack-pm-protocol
branch
from
August 6, 2026 06:25
4fc771b to
9402844
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
orgtrack/v1: WorkItem + Routine PM protocol — phases 0–8 on one branch
Implements the orgtrack/v1 project-management protocol end to end: a frozen wire
contract, a transactional work-item application service, the
org2-pmconsoleCLI, a portable Routine domain driven by a single scheduler, provider bindings,
the Project product-mode axis through session + UI, and the matching cleanup of
the legacy/duplicate paths. 31 commits, rebased on latest develop.
What's in here (by phase)
Phase 0 — frozen contract (
docs/orgtrack-pm-protocol/)(+ common defs), validated fixtures (6 success, 18 error), decision log and
parity matrix. The 18-error table is the canonical error→exit-code map
(
PROJECT_MODE_REQUIRED=5,PERMISSION_DENIED=8, …).Phase 1 — dead/duplicate product paths deleted
work-item-managerbuiltin agent removed (12→11), dead automation visualcluster (−7.6k lines), npm stub. The live Rust automation engine stays.
Phase 2 — work application service (
project_management::work_service)open/in_progress/blocked/completed/failed/cancelled, incl.the
in_progress→openrelease edge) with legacy status mapping.BEGIN IMMEDIATE) growsAtomicServiceOptions: OCC viaexpected_local_version(
PM_ERR:REVISION_CONFLICT), strict-FSM enforcement, operation label +reason; audit row (
pm_audit_events) andpm_change_seqwatermark bump inthe same transaction.
CreateWorkItemRequestsingle construction point,5 frontend frontmatter-assembly sites removed, standalone whole-row overwrite
replaced by an atomic closure API.
Phase 3 —
org2-pmCLI (separate console binary)orgtrack-pm-cli(binorg2-pm): JSON envelope in/out, 18 wireerror codes + exit table, context resolver (
ORGII_*env +.orgii/orgtrack.jsonmanifest), eightwork.*commands, idempotency keys(
pm_idempotencyreplay/conflict semantics).ALL_AGENT_EXEC_MODES).Phases 4–5 — portable Routine domain + unified runtime
routine_service: portable spec (deny_unknown_fields rejectsmodel/account/workspace leakage), Kahn cycle validation, idempotent
routine.applyrevisions,routine.invokematerializes root+children workitems through
work.createwithdepends_on/generated_byedges inpm_relations; orderedrun_statusprojection.disables converted legacy rows (anti-double-fire); the portable scheduler
pass is the only cron driver (legacy automation cron path deleted).
(
work.transition, reason "completion policy"), with stale-signal rejection(
TransitionResult::Ignoredwhenexecution_lock.active_session_idmismatches).
Phase 6 — provider bindings
pm_provider_bindingsrelation table (dual-write + index + lazy backfill),provider capability-profile registry, CLI session-ref namespace validation.
Phase 7 — Project product mode (§5.2 two-axis split)
agent_sessions.product_modecolumn (additive migration), resolver(work-item launch →
project, routine fire →project, explicit selectionvalidated closed-enum), threaded through
SessionLaunchParams/AgentRunLaunchRequest/session aggregate/zod/Sessiontype.
productModeand derives the exec mode (project→build); Project sessions pinthe pill display.
Phase 8 — gating + cleanup
manage_work_item/manage_projectmutation surfacedenied unless
product_mode == "project"(modes never grant tools).manage_project's duplicate WorkItem CRUD retired ontomanage_work_item(structured guidance for retired actions), terminology sweep, dead
workflow-drag signal removed.
Gating hardening (found by a full caller-surface audit + real-machine runs)
productMode; Create-Project-with-AI launchesas
projectexplicitly (its whole purpose ismanage_project, but it has noworkItemId for the resolver to key on).
agent_link_session_to_work_itemsetsproduct_mode='project'(same rule asthe launch resolver), so post-hoc-linked sessions stop getting the
linked-work-item prompt with the tool denied.
projectwhen launched from a work item.mode — closing a delegation escalation through both the inherited-policy and
fresh-registry dispatch paths.
debug_session_execute_toolnow enforces the samewith_modescompositionas the LLM path instead of bypassing policy, and the two E2E probe launches
pass
productMode:"project"(plus the launch helper now mapsagentExecModeto the wire namemode, which was silently dropped before).only advertise the PM tools when the effective (policy-filtered) tool
surface actually contains them.
usePatchSessionforwarded every patch field exceptproductMode(optimistic write, change detector and RPC object all skipped it), so the
composer's Project selection persisted only the derived exec mode. The
in-session ModePill was also mounted
hideWhenDefault, hiding the pickerfor Build sessions — the exact state every new session starts in. Both
fixed.
Verification
cargo test -p project_management— 559 tests green.cargo test -p orgtrack-pm-cli— 6 cross-process binary E2E green (fullagent loop, idempotency replay/conflict, routine lifecycle).
cargo check+clippy+tscgreen.Real-machine testing (dev-build exe against the real
~/.orgiihome,computer-use + CDP driven, real LLM runs)
product_mode+pm_*DDL migrated thereal DB cleanly; the unified routine scheduler starts (30s poll).
org2-pmwhile the GUI runs):work createhit the
PROJECT_MODE_REQUIREDgate (exit 5-family table respected),succeeded with
--mode project --actor human:vince; the new row appeared inthe GUI Work Items list within seconds (pm_change_seq watermark pickup).
UI status change → CLI
work showseesin_progress, revision bump andactor-attributed history; illegal
open→completedrejected withINVALID_TRANSITIONexit 4;in_progressonly enterable viawork claim --session-ref(namespace-validated).agent_list_effective_tools_for_session): Buildsession —
manage_work_item/manage_projectregistered but policy-filteredout of the prompt surface; after
session_patch productMode:"project"bothappear, exec mode stays
build(§5.2 axis separation).manage_work_item(MiniMax M3): Project-mode OS-Agent chatcreated CUF-0003 through the real tool loop; CLI cross-verified
(
created_by: agent, portableopen).pm-protocol-field-test+ work item PMP-0001 — this flow is policy-deadwithout the
productModelaunch fix.auto-transitions to In Progress, launched session gets
product_mode=project; on LLM failure the lock is released and Retry isoffered.
Sonnet 5): the run also caught a production deadlock — proof-of-work
collection shells out to git inside the work item's
BEGIN IMMEDIATEtransaction, and a GUI-spawned git hangs on this machine, so the open
transaction held the projects.db write lock forever (sync-worker gc
failing, CLI
STORE_UNAVAILABLE, execution lock never released, StartAgent permanently disabled). Fixed by collecting proof-of-work before the
transaction opens with a 10s bound and applying it in-memory inside the
mutator. Re-verified live: collection timed out (the git hang is real),
the transition then committed in ~20ms — session terminal → audited
work.transition→ statuscompleted, execution lock released.agent_link_session_to_work_itemon an old Build sessionreturned the row with
productMode:"project".deny_unknown_fieldsrejected a stray field with a precise pointer;routine apply(revision + fnv snapshot hash) →routine runmaterializedroot PMP-0002 + children PMP-0003/0004 with dependency edges → claim/complete
all → ordered
routine statusprojection convergedrunning → succeeded.picker and assignee picker (11 builtins).
Project-surface smoke (same rig, after the Phase 7 finish batch):
run shows the projection recomputed live (row stored
running, expand showssucceededwith PMP-0003/0004completed).(WI-0001) created and linked the moment an accepted submission landed;
a submission that failed session init (dead provider) correctly created
nothing — the boundary really is "accepted", not "sent".
active-WorkItem pill renders (static badge for standalone roots).
the first runs caught a real FK bug — session rows carry scopes with no
local
orgsrow (the implicit personal org andcloud:<uuid>sidebarscopes), which the standalone store's FK rejects. The bootstrap now falls
back to the NULL (personal) standalone scope for those; fixed and
re-verified on-device.
LLM account note: the overnight scenarios ran on MiniMax (the original
Anthropic OAuth token had expired); after the fresh Anthropic account was
added, the LLM-driven scenarios were re-exercised on Claude Sonnet 5,
including the full completion-policy loop above.
Phase 7 finish — the Project session surface (§7.2)
creates and links its root when the first non-empty user submission is
accepted — never on mode switch or empty open, no LLM classification. The
root body preserves the original request; a sessionRef-derived idempotency
key makes a create-then-link-failure retry replay the same root.
agent_track_session_as_projectswitches product mode, derives the exec mode like the composer picker,
invalidates Plan mode's snapshot/restore state (also on any
session_patch→ project, broadcastingagent:exit_plan_modewhen a planapproval was pending) and creates-or-replays the root from the recorded
first input. Session-header menu action next to "Link to Work Item".
pill in the session context bar (now actually mounted above the transcript);
click opens the real Work Item panel.
routine_service::list_runs+project_list_routine_runs/project_routine_run_statuscommands exposepm_routine_runs; a Runs entry in the sidebar BROWSE section opens anexpandable run list showing each run's generated WorkItems and the
recomputed ordered status projection.
Follow-ups (intentionally not in this PR)
path + legacy routine UI,
WorkItemSchedule.cronfield + migration(existing-store migration gate), SyncAdapter → PlanningAdapter rename, repo
split (dogfood gate).