feat(orgtrack): audit batch0 + agent-CLI unification S0–S4 - #737
feat(orgtrack): audit batch0 + agent-CLI unification S0–S4#737Neonforge98 wants to merge 29 commits into
Conversation
has_active_run now reconciles stored 'running' runs against their generated items: once every child is terminal the outcome (succeeded/failed/cancelled) is written back, so a finished run stops suppressing the next scheduled fire. Converted recurring routines previously fired exactly once and were then suppressed forever. convert_all(disable_converted_legacy) now only disables legacy rows whose conversion carries a scope binding; scope-less conversions (DirectSession-shaped) keep the legacy driver, because the portable pass suppresses every scheduled fire as no_scope_binding and disabling the row would silently kill the routine. Pre-commit hook ran. Total eslint: 0, total circular: 0
The workitems PK is a single global column and the create path wrote through an INSERT..ON CONFLICT DO UPDATE, so a colliding short id silently replaced the existing row (and a cross-scope collision reassigned it to the caller's scope). Creation now runs a global existence guard and returns the typed PM_ERR:ALREADY_EXISTS sentinel; the CLI maps it onto the previously unreachable ALREADY_EXISTS wire code. Pre-commit hook ran. Total eslint: 2, total circular: 0
The shared test DDL and its column-repair list predate the product_mode migration, and the crud ops test helper drives the production upsert SQL with one parameter short — 34 agent_core tests failed on a clean checkout since the orgtrack merge. Pre-commit hook ran. Total eslint: 2, total circular: 0
create_session_impl resolves the product mode (work-item launch wins, then the validated explicit choice) but the launch result never carried it, so the optimistic session row defaulted to build: fresh Project sessions rendered a Build pill and hid the Active WorkItem indicator until a full session reload. The resolved value now rides create_session_impl's response through AgentRunLaunchResult and SessionLaunchResult into the frontend row builder; the CLI-agent branch reports none because CLI rows carry no product mode. Pre-commit hook ran. Total eslint: 2, total circular: 0
The in-session picker offered Project to every session type while the Rust side hard-rejects product-mode patches on CLI rows, so picking Project in a claude_code/codex session threw an uncaught RpcError that escalated to the full-screen error page. CLI sessions now get an exec-only picker per useSessionProductModeField's documented contract, and both mode patches swallow their rejections instead of feeding the ErrorBoundary. Pre-commit hook ran. Total eslint: 2, total circular: 0
|
真机升级 cell 实测通过(沙箱化真实 home 副本 + dev 构建,CLI↔GUI 跨进程):
|
Extract write_work_item_in_tx / allocate_short_id_in_tx / resolve_project_scope_in_tx from the self-committing store helpers so composed operations can share one transaction. work.create (project and standalone) now runs guard, row write, audit and pm_change_seq bump in a single BEGIN IMMEDIATE — the audit/watermark crash window on create is closed. routine.invoke materializes the whole graph in one transaction: id allocation, every item (each behind the existence guard), every relation, the run row, all audit rows sharing a single seq bump, and the idempotency record. A mid-graph failure rolls back wholesale instead of leaving a partial graph. invoke takes an idempotency key: the scheduler passes a plan-time key (routine:scheduled_millis), which also closes the crash-refire window between invoke and mark_evaluated; the CLI forwards --idempotency-key. Pre-commit hook ran. Total eslint: 2, total circular: 0
work.claim previously composed two serviced atomic calls — acquire bumped local_version, then the transition compared the caller's expected revision against the bumped value, so --expected-revision could never succeed and was marked retryable. claim_project_work_item now applies the execution lock and the strict open -> in_progress transition in one atomic mutation with OCC checked once at entry; the CLI drops its acquire/rollback composition and the cross-process e2e exercises the flag. Pre-commit hook ran. Total eslint: 2, total circular: 0
run_idempotent previously ran check-then-act on an untransacted connection: two concurrent callers with the same key both passed the empty check and both executed — the observed bootstrap double-root race. The key row is now reserved (response NULL) in an IMMEDIATE transaction before the operation runs; a concurrent duplicate waits briefly for the response and replays it, a crashed reservation older than the takeover window is reclaimed, and a failed execution deletes its reservation so deterministic failures stay retryable. Pre-commit hook ran. Total eslint: 2, total circular: 0
delete/restore now run through the serviced atomic mutation (work.delete / work.restore audit rows, version bump, watermark) with the collab-bridge deletion flag preserved; the atomic write-back gains the deleted_at column it needs to carry lifecycle changes. The two whole-row Tauri commands route through audited overwrite handlers (work.write on existing rows, guarded single-tx create on missing ones) instead of the silent upsert — OCC and the CLI watermark now see every writer. Pre-commit hook ran. Total eslint: 2, total circular: 0
- The second claim-contention branch (active linked session) now maps to ALREADY_CLAIMED instead of a retryable STORE_UNAVAILABLE. - execution-context.schema.json gains null branches for scopeId/actor: a bare 'org2 context' outside an initialized workspace legitimately reports both as unresolved, and the schema contradicted the implementation from day one. - The four drifted error-fixture messages are aligned with the implementation strings; the amendment is recorded in decisions.md. - New conformance test target: real org2-pm process envelopes are validated against envelope + execution-context schemas (jsonschema dev-dependency), and every error fixture's retryable flag is checked against ErrorCode::retryable. - parity-matrix picks up the landed-cell ticks it was missing since the orgtrack merge. Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
|
S0 地基批已并入本 PR(6 个新提交,per 设计稿 agent-cli-unification-design §11/§13):
验证:project_management 568 + agent_core 3150 + orgtrack-pm-cli 10(含 2 个新 conformance)= 3728 全绿;clippy 零警告。 |
- work assign <id> --assignee <human|agent:id>: audited work.assign, ownership only — dispatch semantics stay with the orchestration layer. - work release <id>: single-transaction work.release restricted to the claim holder; clears the lock, closes the holder's running linked-session entry, walks the release edge back to open. Contention maps to ALREADY_CLAIMED. - work list: --status now validates the portable vocabulary (legacy store words are rejected instead of silently missing rows), deterministic short-id ordering, and the frozen --cursor / meta.nextCursor pagination is implemented. - work update: routed through the OCC-capable work.update service handler (--expected-revision honored) and, with note/relate, through the idempotency guard — a passed --idempotency-key is no longer silently ignored. Stale module doc corrected. Pre-commit hook ran. Total eslint: 2, total circular: 0
New project-management::project_service owns the Project application boundary (slug allocation, org validation, ALREADY_EXISTS guard, project.create/update audit + watermark). The CLI grows the project family — list/show/find/members read-only, create/update mutations behind Project mode + actor + idempotency guard, no delete (human-plane only per the agent-surface design). Cross-process e2e covers create, duplicate-slug refusal, show/update/find and the mode gate. Pre-commit hook ran. Total eslint: 2, total circular: 0
work.* success payloads stay store-shaped this round: the portable work-item schema requires durable work_* ids while the store still has id = short_id, so switching now would emit payloads that fail their own schema. The schema + success fixtures are explicitly marked as the post-id-migration target shape. Pre-commit hook ran. Total eslint: 2, total circular: 0
Shell commands run by harness sessions now carry the orgtrack identity: ORGII_SESSION_REF/ACTOR/MODE/SCOPE resolved from the session record and the host binary directory prepended to PATH so the bundled org2-pm always matches the app version (design M6). Session launch writes .orgii/agent_session_context.json into the prepared workspace (sessionRef, actor, productMode, scope, capability list). The CLI resolver walks up to the marker and locks its identity to it: explicit --actor/--session-ref that contradict the marker return PERMISSION_DENIED — a model inside a managed workspace can never act as a human or as another session. Cross-process e2e covers marker-driven context, injected-identity mutation, actor spoofing and session override refusal. Pre-commit hook ran. Total eslint: 2, total circular: 0
Project-mode sessions get a Work Management brief section: the core org2-pm command signatures with --help progressive disclosure, the injected-identity rule (never pass --actor), and the explicit ban on treating harness-local planning tools as work-system writes. Pre-commit hook ran. Total eslint: 2, total circular: 0
A work item claimed by one session can no longer have its content (work.update) or lifecycle (work.transition) advanced by a different session: the service mutations gain a caller-session guard that returns the claim-contention error, and the CLI threads its --session-ref through. Human direct operation passes no session and stays exempt (actor/org policy governs the human plane). The Tauri transition command, previously actor=None, now attributes to a Desktop human actor. Cross-process e2e covers foreign-claimed update and transition refusal. Pre-commit hook ran. Total eslint: 2, total circular: 0
Shell tool calls that invoke org2-pm and emit an orgtrack/v1 envelope now render as a compact work card (operation label + short id/title/ status on success, wire error code + retryable flag on failure) instead of a raw terminal block. Detection is defensive — non-org2-pm commands, non-JSON output and foreign apiVersions fall through to the normal command/terminal rendering. Pre-commit hook ran. Total eslint: 2, total circular: 0
manage_work_item and manage_project emit an orgtrack_tool_deprecation info span on every invocation so the zero-call window can be confirmed before the tools are retired to the org2-pm CLI (design S4 gate). The e2e durable-object spec's retired 'Work Item Manager' scenario ids, titles and fixtures are renamed to 'OS Agent' — the flow already launched builtin:os; this clears the last of the naming debt. The typed tools are deliberately NOT deleted in this change: the AI work-item filler and Create-Project-with-AI flows still depend on them, and retirement is gated on the observation window plus live multi-agent verification that the CLI path fully replaces every flow. Pre-commit hook ran. Total eslint: 2, total circular: 0
全线进度收口(S0–S3 完成,S4 准备就绪)按
S4 工具删除为何未在本 PR
建议合并策略本 PR 已相当大(19 提交,跨 Rust/TS/docs/e2e)。S0 的事务原语是 PM Graph Commit 的地基,S2/S3 是 agent-CLI 统一的核心。可整体 review 合并,也可按 batch0/S0 与 S1–S3 拆两批——commit 边界清晰,每片独立绿、可回滚。 |
org2 pm from birth — the PM Case agent surface is the org2 pm command family carrying an org2/pm/v1 contract, landing with the PM Case implementation, no typed-tool exception. Amends the 'no PM CLI this round' item: unchanged scope this round, but the future form is now decided rather than open. Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
Pre-commit hook ran. Total eslint: 2, total circular: 0
|
双机实测 (S4) 完成 — 报告: docs/orgtrack-pm-protocol/dual-instance-s4-2026-08-07.md。Cell A filler-via-CLI 真 LLM GREEN;Cell B 双机 CLI→云→inst-2 闭环;Cell C 子代理 root-walk 过;boot-2 确定性 0 epoch 变。实测另修 4 个真 bug(exec-mode pin / org 注线 / 分配器全局碰撞 / patch actor 审计)。 |
The four upgrade-visible defects from the five-domain PR683 audit (batch 22.0 in the design docs), plus the test-schema debt the merge left behind. Rebased on latest develop (includes the work-item UI refresh).
Fixes
1. Converted recurring routines fired once, then never again (P0)
pm_routine_runs.statusonly ever received'running'— nothing wrote a terminal state — whilehas_active_runsuppresses scheduled fires on that stored status. After the startup auto-conversion disabled the legacy rows, every converted cron routine fired exactly once and was suppressed forever; DirectSession-shaped conversions (no scope binding) never fired at all.has_active_runnow reconciles stored running runs against their generated items and writes the outcome back (succeeded/failed/cancelled) once every child is terminal — the frozen §11 projection is untouched.convert_all(disable_converted_legacy)only disables legacy rows whose conversion carries a scope binding; scope-less conversions keep the legacy driver instead of being silently killed.2.
work.createsilently overwrote an existing idThe
workitemsPK is a single global column behind anINSERT..ON CONFLICT DO UPDATE, so a colliding short id wholesale replaced the existing row — and a cross-scope collision reassigned it to the caller's scope. Creation now runs a global existence guard, returns the typedPM_ERR:ALREADY_EXISTSsentinel, and the CLI maps it onto the previously unreachableALREADY_EXISTSwire code.3. Fresh Project sessions presented as Build
The launch result never carried the resolved product mode, so the optimistic session row defaulted to build: the ModePill showed Build and the Active WorkItem pill stayed hidden in the exact flows built to show it. The value resolved in
create_session_implnow rides throughAgentRunLaunchResult→SessionLaunchResult→ zod schema → the frontend row builder.4. Picking Project on a CLI session crashed to the error page
The in-session mode picker offered Project to every session type while
session_patchhard-rejects product mode on CLI rows; the uncaught RpcError escalated to the full-screen ErrorBoundary. CLI sessions now get an exec-only picker (peruseSessionProductModeField's documented contract) and both mode patches swallow their rejections.5. Test-schema debt: 34 agent_core tests red on a clean checkout
The shared test DDL, its column-repair list, and the crud ops test helper all predate the
product_modemigration.cargo test -p agent_corefailed 29+5 tests on develop since the orgtrack merge; all green now.Verification
cargo test -p project_management— 568 passed (4 new: run terminalization ×2, conversion gating, create guard)cargo test -p agent_core— 3150 passed (was 3121 passed / 29 failed on develop)cargo test -p orgtrack-pm-cli— 6 cross-process E2E greentsc --noEmitclean;vitestlaunchPayload (22) + InputArea (112) greenExisting-victim remediation (deliberate non-fix)
Databases that already ran the broken build (real `~/.orgii` homes migrated since the orgtrack merge) may hold legacy rows the old pass disabled without a scope binding. This PR does not auto-re-enable them: a disabled row is indistinguishable from one the user disabled on purpose, and silently flipping user intent is worse than the outage. Remediation is one manual toggle in the routines UI — safe for scope-less conversions (the portable pass cannot fire them), and this PR guarantees the next startup no longer re-disables the row. Do not re-enable rows whose portable twin has a scope binding (known dual-driver double-fire, tracked for the fast-follow batch).
Stuck runs from the broken build self-heal lazily: the first `has_active_run` check after upgrade terminalizes them and firing resumes.