Skip to content

feat(orgtrack): audit batch0 + agent-CLI unification S0–S4 - #737

Open
Neonforge98 wants to merge 29 commits into
developfrom
fix/orgtrack-audit-batch0
Open

feat(orgtrack): audit batch0 + agent-CLI unification S0–S4#737
Neonforge98 wants to merge 29 commits into
developfrom
fix/orgtrack-audit-batch0

Conversation

@Neonforge98

@Neonforge98 Neonforge98 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

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.status only ever received 'running' — nothing wrote a terminal state — while has_active_run suppresses 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_run now 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.create silently overwrote an existing id

The workitems PK is a single global column behind an INSERT..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 typed PM_ERR:ALREADY_EXISTS sentinel, and the CLI maps it onto the previously unreachable ALREADY_EXISTS wire 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_impl now rides through AgentRunLaunchResultSessionLaunchResult → 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_patch hard-rejects product mode on CLI rows; the uncaught RpcError escalated to the full-screen ErrorBoundary. CLI sessions now get an exec-only picker (per useSessionProductModeField'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_mode migration. cargo test -p agent_core failed 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 green
  • tsc --noEmit clean; vitest launchPayload (22) + InputArea (112) green

Existing-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.

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
@Neonforge98

Copy link
Copy Markdown
Collaborator Author

真机升级 cell 实测通过(沙箱化真实 home 副本 + dev 构建,CLI↔GUI 跨进程):

  • 转换行为:scope-bound legacy 行被禁用、portable 接管(default_scope 正确写入);scope-less legacy 行保持启用(Part B 生效),其 portable twin 每 tick 被 no_scope_binding 抑制(审计流可见 routine.fire_suppressed)、legacy watermark 持续推进
  • 解除压制闭环:portable 触发 run₁(root+child 物化,CUG-0001/0002)→ 子项标 done → 下一调度 pass:run₁ 终态写回 succeededrun₂ 立即触发(CUG-0004/0005)——坏版本在此处永久卡死
  • 跨进程 CLI:work create 无 mode → PROJECT_MODE_REQUIRED exit 5;带 --scope --mode project → exit 0 落库
  • UI 修复(launch productMode / ModePill CLI gating)由 134 个组件测试覆盖;dev 进程无法被 computer-use 索引,GUI 目测留待 review 时顺手一验

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
@Neonforge98 Neonforge98 changed the title fix(orgtrack): land the independent-fix batch from the PR683 audit fix(orgtrack): audit batch0 + S0 foundation (tx primitives, idempotency, conformance) Aug 7, 2026
@Neonforge98

Copy link
Copy Markdown
Collaborator Author

S0 地基批已并入本 PR(6 个新提交,per 设计稿 agent-cli-unification-design §11/§13):

  1. in-tx 原语 + 单事务 createwrite_work_item_in_tx / allocate_short_id_in_tx / resolve_project_scope_in_tx 抽取;work.create(project + standalone)的守卫、行写入、审计、watermark 同一个 BEGIN IMMEDIATE——create 的审计/幂等崩溃窗口关闭
  2. routine.invoke 整图单事务:分配、全部条目(每个过存在性守卫)、关系、run 行、共享单次 seq bump 的全部审计行、幂等记录一次提交;中途失败整图回滚(跨 org 地雷测试实证);调度器传 plan-time key(顺带关闭 invoke 与 mark_evaluated 之间的 crash-refire 窗口)
  3. 单事务 claim:锁获取 + strict open→in_progress 一次 atomic 调用,OCC 入口校验一次——--expected-revision 从必败变可用,跨进程 e2e 已覆盖
  4. 先占位再执行的幂等:并发同 key 恰好执行一次(并发测试实证),bootstrap 双开根项的竞态关闭;崩溃占位 30s 后可接管,失败执行删占位保持可重试
  5. whole-row 生命周期路径全部入审计:delete/restore 走 serviced atomic(work.delete/work.restore + 版本 bump + watermark,云删除传播保留);两个 whole-row Tauri 命令改走 audited overwrite;atomic 写回补 deleted_at 列
  6. 契约修偶 + conformance 最小闸:claim 第二竞争分支归位 ALREADY_CLAIMED;execution-context schema 补 null 分支;4 处 fixture 文案对齐(decisions.md 记修订);新 conformance 测试用真实 org2-pm 进程输出过 jsonschema 校验 + 全部 error fixture 的 retryable 一致性;parity-matrix 补上落地打勾

验证: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
@Neonforge98 Neonforge98 changed the title fix(orgtrack): audit batch0 + S0 foundation (tx primitives, idempotency, conformance) feat(orgtrack): audit batch0 + agent-CLI unification S0–S3 foundation Aug 7, 2026
@Neonforge98

Copy link
Copy Markdown
Collaborator Author

全线进度收口(S0–S3 完成,S4 准备就绪)

agent-cli-unification-design.md 的切片计划,本 PR 现含 19 个提交,覆盖到 S3 全部 + S4 可逆准备部分。3731 个 Rust 测试全绿。

状态 内容
batch0 Routine P0 自愈、create 覆盖守卫、launch productMode、ModePill 崩溃、683 测试债
S0 in-tx 原语 + 单事务 create/claim/routine.invoke、先占位幂等、whole-row 入审计、conformance 闸、契约修偶
S1 work assign/release、org2 project 命令族、分页、portable 状态过滤、幂等补全
S2 shell env 注入(ORGII_* + PATH)、fail-closed 会话标记(冒充拒绝)、Project brief 命令节
S3 L3 claim-holder 守卫、Tauri transition parity、转录 org2-pm 信封渲染器
S4 🟡 准备 deprecation 埋点 + WIM 命名清扫;工具删除未做(见下)

S4 工具删除为何未在本 PR

manage_work_item/manage_project 是 AI work-item filler、Create-Project-with-AI 等当前活流程的依赖。设计稿 §15.5 规定删除门控于:deprecation 零调用观察窗口 + 实测 CLI 全替换验证。本 PR 加了埋点(orgtrack_tool_deprecation span)让窗口可观察,但删除本身是时机决策,需要观察期数据 + 一轮双机实测确认 CLI 路径完整替换每个流程后再单开 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
@Neonforge98 Neonforge98 changed the title feat(orgtrack): audit batch0 + agent-CLI unification S0–S3 foundation feat(orgtrack): audit batch0 + agent-CLI unification S0–S4 Aug 7, 2026
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
@Neonforge98

Copy link
Copy Markdown
Collaborator Author

双机实测 (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 审计)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant