From 50f7163106ff0a65c3e0072fb25c1520bb5e09f7 Mon Sep 17 00:00:00 2001 From: Neonforge <48338160+Neonforge98@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:05:51 -0700 Subject: [PATCH 01/33] docs(orgtrack-pm): freeze v1 contract, fixtures and phase-0 decisions 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 --- docs/orgtrack-pm-protocol/README.md | 41 +++++ docs/orgtrack-pm-protocol/decisions.md | 144 +++++++++++++++++ .../fixtures/errors/actor-required.json | 15 ++ .../fixtures/errors/already-claimed.json | 18 +++ .../fixtures/errors/already-exists.json | 16 ++ .../fixtures/errors/context-required.json | 15 ++ .../fixtures/errors/dependency-cycle.json | 19 +++ .../fixtures/errors/idempotency-conflict.json | 16 ++ .../fixtures/errors/invalid-argument.json | 16 ++ .../fixtures/errors/invalid-transition.json | 16 ++ .../fixtures/errors/not-found.json | 16 ++ .../fixtures/errors/not-ready.json | 15 ++ .../fixtures/errors/permission-denied.json | 16 ++ .../errors/project-mode-required.json | 16 ++ .../fixtures/errors/provider-unavailable.json | 16 ++ .../errors/result-schema-mismatch.json | 16 ++ .../fixtures/errors/revision-conflict.json | 16 ++ .../fixtures/errors/scope-violation.json | 16 ++ .../fixtures/errors/store-unavailable.json | 15 ++ .../errors/unsupported-capability.json | 16 ++ .../fixtures/routine-spec.json | 83 ++++++++++ .../fixtures/success/context.json | 38 +++++ .../fixtures/success/routine-run.json | 27 ++++ .../fixtures/success/routine-status.json | 49 ++++++ .../fixtures/success/work-claim.json | 41 +++++ .../fixtures/success/work-list.json | 47 ++++++ .../fixtures/success/work-transition.json | 56 +++++++ docs/orgtrack-pm-protocol/parity-matrix.md | 20 +++ .../schemas/common.schema.json | 144 +++++++++++++++++ .../schemas/envelope.schema.json | 83 ++++++++++ .../schemas/execution-context.schema.json | 56 +++++++ .../schemas/routine-run.schema.json | 41 +++++ .../schemas/routine.schema.json | 153 ++++++++++++++++++ .../schemas/work-item.schema.json | 123 ++++++++++++++ 34 files changed, 1435 insertions(+) create mode 100644 docs/orgtrack-pm-protocol/README.md create mode 100644 docs/orgtrack-pm-protocol/decisions.md create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/actor-required.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/already-claimed.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/already-exists.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/context-required.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/dependency-cycle.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/idempotency-conflict.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/invalid-argument.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/invalid-transition.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/not-found.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/not-ready.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/permission-denied.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/project-mode-required.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/provider-unavailable.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/result-schema-mismatch.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/revision-conflict.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/scope-violation.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/store-unavailable.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/errors/unsupported-capability.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/routine-spec.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/context.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/routine-run.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/routine-status.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/work-claim.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/work-list.json create mode 100644 docs/orgtrack-pm-protocol/fixtures/success/work-transition.json create mode 100644 docs/orgtrack-pm-protocol/parity-matrix.md create mode 100644 docs/orgtrack-pm-protocol/schemas/common.schema.json create mode 100644 docs/orgtrack-pm-protocol/schemas/envelope.schema.json create mode 100644 docs/orgtrack-pm-protocol/schemas/execution-context.schema.json create mode 100644 docs/orgtrack-pm-protocol/schemas/routine-run.schema.json create mode 100644 docs/orgtrack-pm-protocol/schemas/routine.schema.json create mode 100644 docs/orgtrack-pm-protocol/schemas/work-item.schema.json diff --git a/docs/orgtrack-pm-protocol/README.md b/docs/orgtrack-pm-protocol/README.md new file mode 100644 index 000000000..97e3ed80f --- /dev/null +++ b/docs/orgtrack-pm-protocol/README.md @@ -0,0 +1,41 @@ +# Orgtrack PM Protocol — Phase 0 frozen artifacts + +`orgtrack/v1` WorkItem + Routine CLI 协议的 Phase 0 产出。本目录是协议 wire +contract 的 source of truth:schemas 与 golden fixtures 会被后续 Phase 3+ 的 +conformance 测试直接消费;`decisions.md` 记录 Phase 0 冻结的全部命名与边界 +决策,实现与之冲突时以本目录为准。 + +设计依据是《Orgtrack WorkItem + Routine CLI Protocol — 最终设计》rev 2 +(2026-08-04 审计修订版,暂未入库)。 + +## 目录 + +```text +decisions.md Phase 0 冻结决策(mode/capability/provider id/hook 名/ + manifest/CLI 载体/watermark/exit code) +parity-matrix.md 入口一致性矩阵(每个 phase 落地时填格) +schemas/ JSON Schema(draft-07) + common.schema.json 共享 $defs:ActorRef、SessionRef、状态枚举、 + capability 词汇表 + envelope.schema.json success/error CLI envelopes 与稳定错误码 + execution-context.schema.json `org2 context` 返回的 ExecutionContext + work-item.schema.json WorkItem canonical shape + routine.schema.json portable Routine spec + routine-run.schema.json RoutineRun occurrence +fixtures/ + success/ 每个 command family 的 success envelope golden fixture + errors/ 18 个稳定错误码各一份 golden fixture +``` + +## 约定 + +- fixtures 是 byte-level golden:conformance 测试比较真实 serialized bytes, + 不允许隐藏 `$schema`、remote `$ref`、secret 或本地路径混入 wire payload; +- 未列入 `envelope.schema.json` 错误码枚举的 code 不得出现在任何实现中; +- schemas 修改需要同步更新 fixtures 与 `decisions.md`,三者不一致视为 CI 失败 + (Phase 3 接入)。 + +## Phase 0 未尽项 + +- 现有 Routine/WorkItem 数据导出(migration fixture)需要开发机的 + `projects.db`,推迟到 Phase 4 开工时以脚本完成,脚本与导出样本届时入库。 diff --git a/docs/orgtrack-pm-protocol/decisions.md b/docs/orgtrack-pm-protocol/decisions.md new file mode 100644 index 000000000..8d461e6dd --- /dev/null +++ b/docs/orgtrack-pm-protocol/decisions.md @@ -0,0 +1,144 @@ +# Phase 0 冻结决策 + +本文件是 Phase 0 交付的决策冻结记录。实现阶段(Phase 1-8)与此冲突时,要么改 +实现,要么先修订本文件并说明理由——不允许静默偏离。 + +## 1. Product mode 与 resolver precedence + +Product mode enum:`build | plan | ask | project`(唯一 source of truth 是 +ExecutionContext 的 `mode` 字段;禁止任何 `tracking_enabled` 类影子布尔)。 + +Mode resolver 固定顺序: + +1. 从 WorkItem detail、Routine 或 RoutineRun 启动 → `project`; +2. 用户在当前 Session 明确选择的 mode; +3. 普通新 Session → `build`。 + +现状注意:mode 枚举目前有四处发散清单(Rust `AgentExecMode`、 +`agent_list_modes`(返回 4 项且无 UI caller)、TS `AGENT_EXEC_MODES` picker、 +`MODE_LABELS`)。Phase 3 收敛为单一 source 后才允许新增 `project`。 + +Plan mode 有"进入时快照、退出时恢复前一 mode"状态 +(`restore_mode_before_plan_entry`):`Convert to Project` 必须失效该快照。 + +## 2. Mode × capability allowlist + +Capability 词汇表(12 个,见 `common.schema.json#/definitions/capabilityId`): +`work.read|create|update|claim|transition|note|relate`、 +`routine.read|apply|run|cancel|set_enabled`。`context` 读取不需要 capability。 + +| Mode | allowlist | +| ----------------------------------- | ------------------ | +| build / plan / ask / 其他 exec mode | (空——仅 context) | +| project | 全部 12 个 | + +最终 capabilities = mode allowlist ∩ actor/org policy ∩ provider capabilities。 +实现必须用现有 deny-delta 机制表达("modes never grant tools"):Build/Plan 等 +显式 deny work/routine mutation surface,Project 不施加该 deny;授权本身只来自 +actor/org policy。 + +术语隔离:本协议的 `runtimeExecutionMode` 对应现有 `AgentExecMode` wire +values;**不得**复用现有 Rust `ExecutionMode { Direct, WorkStation }` 类型名。 +`review` 在三个域必须使用限定名:exec mode(`AgentExecMode::Review`)、agent +role(`AgentRole::Review`,orchestrator 状态机 key 的列)、orchestrator phase +(`OrchestratorPhase::Review`)。 + +## 3. 错误码 ↔ exit code + +错误码枚举见 `envelope.schema.json`(18 个,含 `RESULT_SCHEMA_MISMATCH`)。 + +| exit | 错误码 | +| ---- | ------------------------------------------------------------------------------------------------------------ | +| 0 | success | +| 2 | INVALID_ARGUMENT / RESULT_SCHEMA_MISMATCH / DEPENDENCY_CYCLE | +| 3 | NOT_FOUND / CONTEXT_REQUIRED / ACTOR_REQUIRED | +| 4 | REVISION_CONFLICT / IDEMPOTENCY_CONFLICT / ALREADY_CLAIMED / ALREADY_EXISTS / NOT_READY / INVALID_TRANSITION | +| 5 | PROJECT_MODE_REQUIRED | +| 6 | PROVIDER_UNAVAILABLE / STORE_UNAVAILABLE | +| 7 | UNSUPPORTED_CAPABILITY | +| 8 | PERMISSION_DENIED / SCOPE_VIOLATION | + +`PROJECT_MODE_REQUIRED`(5)与 `PERMISSION_DENIED`(8)分离:前者提示"需要用户 +切换 mode",后者是"该 actor 无权执行"。claim 竞争时 `ALREADY_CLAIMED` 优先于 +`REVISION_CONFLICT`。现有 `orgtrack check` 的 0/1/2 exit 语义属于该 binary 自身, +与本表无关。 + +## 4. CLI 载体与 crate 命名 + +- 用户侧 command 名为 `org2`;由**新的独立 console binary** 提供:crate + `orgtrack-pm-cli`,cargo bin name `org2-pm`(workspace 内唯一),distribution + 安装到 PATH 并命名/别名为 `org2`; +- 现有 GUI binary(cargo package `org2`,release 下 + `windows_subsystem = "windows"`,无 console)**不承载任何 CLI subcommand**; +- PM 协议 DTO crate 命名为 `orgtrack-pm-protocol`——注意 `orgtrack-protocol` + 已被 session provenance wire contracts 占用(设计文档 §19 的建议名与之冲突, + 以本条为准);同理后续 domain/application/store crates 使用 `orgtrack-pm-*` + 前缀; +- 现有 `orgtrack` binary(外部会话历史索引)不变;`packages/orgtrack` npm + stub 在 Phase 1 删除。 + +## 5. Provider ID 命名空间 + +SessionRef.provider / ProviderBinding.provider 使用以下冻结 registry: + +- `org2` —— 一切 ORG2 拥有/托管的 session(内部 canonical source + `orgii_rust_agents` / `orgii_cli_sessions` / `orgii_cloud_replay` 一律对外 + 呈现为 `org2`;底层 harness 记录在 SessionRef.metadata.nativeHarness); +- 外部 provider 使用 importer 端 canonical source id:`claude_code`、 + `codex_app`、`cursor_ide`、`cursor_cli`、`opencode`、`cline`、`copilot`、 + `kimi`、`qwen_code`、`droid`、`antigravity`、`zcode`、`warp`、`trae`、 + `qoder`、`windsurf` 等(以 `orgtrack-core` source registry 为准); +- hook 端短名映射到 canonical id,不对外出现:`claude→claude_code`、 + `codex→codex_app`、`cursor→cursor_ide`、`qwen→qwen_code`、其余同名直映; +- planning provider id:`linear`、`github`(现有 adapter registry id 不变)。 + +## 6. Session 生命周期 hook 命名 + +协议 canonical hook 名:`session.started`、`session.completed`、 +`work.claimed`、`work.transitioned`、`routine.invoked`、`routine.completed`、 +`artifact.produced`。 + +现状三套命名的映射(Phase 5 落地时接线,不新增第四套): + +| 现状 | canonical | +| -------------------------------------------------------------------- | --------------------------------------------------- | +| WS wire `session.completed` / `session.failed` / `session.cancelled` | `session.completed`(payload 携带 terminal status) | +| Tauri 内部 `session-status-changed` | 内部信号,驱动 canonical hooks,不直接暴露 | +| agent 流 `session_start` / `session_end` | `session.started` / `session.completed` | + +`session.started` 现状不存在,为新增。`session.completed` 默认只附加 +SessionRef;orchestrator 场景的自动完成改写为显式默认 completion policy(走 +canonical `work.transition`,带 attempt/session identity)。 + +## 7. Workspace manifest 与 env + +- Manifest 文件:`.orgii/orgtrack.json`,最小 shape + `{ "version": 1, "scopeId": "...", "orgId": "..." }`; +- `is_initialized(workspace)` = 该文件存在且 `version` 受支持。`.orgii/` 目录 + 存在本身**不**代表已初始化(现状该目录树由 git-folder sync 等副作用创建); +- trusted local resolver 顺序:explicit CLI flags → `ORGII_*` env → + manifest。冻结 env 名:`ORGII_MODE`、`ORGII_ACTOR`、`ORGII_SCOPE`、 + `ORGII_SESSION_REF`(沿用现有 `ORGII_` 前缀惯例;不新增 `ORG2_*`)。 + +## 8. 跨进程 wake(pm_change_seq) + +- `projects.db` 新增单行表 `pm_change_seq(id INTEGER PRIMARY KEY CHECK(id=1), +seq INTEGER NOT NULL)`; +- 每个 PM mutation 在同一 transaction 内 `seq = seq + 1`; +- 桌面 host 低频轮询(或 db 文件 watch 触发)读取 seq,变化时做增量 + reconciliation;进程内 mutation 直接内存通知; +- readiness / RoutineRun 投影 / output 绑定在 mutation 事务内同步完成,CLI + 写入不依赖 host 在线。 + +## 9. 列表分页与 list 返回形状 + +- list 类 data 形状固定为 `{ "items": [...] }`; +- `--cursor ` 请求下一页;`meta.nextCursor` 存在表示还有后续页; +- cursor 是 opaque token,实现可变,语义不进协议。 + +## 10. Claim 与既有锁的边界 + +- claim record 收编本地 `execution_lock`(CAS session 执行锁)职责; +- 云端 `orgii_acquire_work_item_lock` 保持为 human 协作**编辑锁**:编辑锁 ≠ + 工作 claim,两者并存但互不代理;Phase 2a 落地时在 service 层写清依赖关系 + (持有编辑锁不阻止 claim,claim 不授予编辑权)。 diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/actor-required.json b/docs/orgtrack-pm-protocol/fixtures/errors/actor-required.json new file mode 100644 index 000000000..4e8371121 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/actor-required.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "ACTOR_REQUIRED", + "message": "No actor resolved: pass --actor or set ORGII_ACTOR (actors are never inferred from OS username or git owner)", + "retryable": false, + "details": { + "missing": ["actor"] + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR03" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/already-claimed.json b/docs/orgtrack-pm-protocol/fixtures/errors/already-claimed.json new file mode 100644 index 000000000..5da4737b6 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/already-claimed.json @@ -0,0 +1,18 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "ALREADY_CLAIMED", + "message": "WorkItem work_01K0000000000000000000WORK0 is claimed by agent codex_app:reviewer-1", + "retryable": false, + "details": { + "claimedBy": { + "kind": "agent", + "id": "codex_app:reviewer-1" + } + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR10" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/already-exists.json b/docs/orgtrack-pm-protocol/fixtures/errors/already-exists.json new file mode 100644 index 000000000..4c3b5b65f --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/already-exists.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "ALREADY_EXISTS", + "message": "Routine name 'interaction-impact-analysis' already exists in this scope", + "retryable": false, + "details": { + "resource": "Routine", + "name": "interaction-impact-analysis" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR06" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/context-required.json b/docs/orgtrack-pm-protocol/fixtures/errors/context-required.json new file mode 100644 index 000000000..5ffd33e4b --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/context-required.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "CONTEXT_REQUIRED", + "message": "No scope resolved: pass --scope, set ORGII_SCOPE, or run inside an initialized workspace (.orgii/orgtrack.json)", + "retryable": false, + "details": { + "missing": ["scopeId"] + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR02" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/dependency-cycle.json b/docs/orgtrack-pm-protocol/fixtures/errors/dependency-cycle.json new file mode 100644 index 000000000..01c156253 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/dependency-cycle.json @@ -0,0 +1,19 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "DEPENDENCY_CYCLE", + "message": "Adding depends_on work_01K0000000000000000000ROOT0 would create a cycle", + "retryable": false, + "details": { + "cycle": [ + "work_01K0000000000000000000WORK0", + "work_01K000000000000000000COLL0", + "work_01K0000000000000000000WORK0" + ] + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR13" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/idempotency-conflict.json b/docs/orgtrack-pm-protocol/fixtures/errors/idempotency-conflict.json new file mode 100644 index 000000000..e98990126 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/idempotency-conflict.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "IDEMPOTENCY_CONFLICT", + "message": "Idempotency key 'session_abc:complete' was already used with a different canonical request body", + "retryable": false, + "details": { + "idempotencyKey": "session_abc:complete", + "operation": "work.transition" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR08" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/invalid-argument.json b/docs/orgtrack-pm-protocol/fixtures/errors/invalid-argument.json new file mode 100644 index 000000000..04ca530f4 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/invalid-argument.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "INVALID_ARGUMENT", + "message": "Unknown state 'done'; expected one of open|in_progress|blocked|completed|failed|cancelled", + "retryable": false, + "details": { + "field": "--to", + "value": "done" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR01" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/invalid-transition.json b/docs/orgtrack-pm-protocol/fixtures/errors/invalid-transition.json new file mode 100644 index 000000000..9e7a9541f --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/invalid-transition.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "INVALID_TRANSITION", + "message": "completed -> in_progress is not allowed; in_progress is only entered via work.claim or blocked -> in_progress", + "retryable": false, + "details": { + "from": "completed", + "to": "in_progress" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR11" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/not-found.json b/docs/orgtrack-pm-protocol/fixtures/errors/not-found.json new file mode 100644 index 000000000..8e84637d8 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/not-found.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "NOT_FOUND", + "message": "WorkItem work_01K000000000000000000GONE0 does not exist in scope project_01K0000000000000000000SCOPE", + "retryable": false, + "details": { + "resource": "WorkItem", + "id": "work_01K000000000000000000GONE0" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR05" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/not-ready.json b/docs/orgtrack-pm-protocol/fixtures/errors/not-ready.json new file mode 100644 index 000000000..d8fb08e6f --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/not-ready.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "NOT_READY", + "message": "WorkItem work_01K0000000000000000000WORK0 has 1 incomplete dependency", + "retryable": true, + "details": { + "incompleteDependencies": ["work_01K000000000000000000COLL0"] + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR09" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/permission-denied.json b/docs/orgtrack-pm-protocol/fixtures/errors/permission-denied.json new file mode 100644 index 000000000..94340c4f9 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/permission-denied.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "PERMISSION_DENIED", + "message": "Actor codex_app:reviewer-1 lacks capability routine.apply in this scope", + "retryable": false, + "details": { + "actor": "codex_app:reviewer-1", + "missingCapability": "routine.apply" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR15" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/project-mode-required.json b/docs/orgtrack-pm-protocol/fixtures/errors/project-mode-required.json new file mode 100644 index 000000000..2a8b7b546 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/project-mode-required.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "PROJECT_MODE_REQUIRED", + "message": "work.claim is a WorkItem mutation; current mode is 'build'. Switch the session to Project mode or pass --mode project", + "retryable": false, + "details": { + "operation": "work.claim", + "currentMode": "build" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR04" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/provider-unavailable.json b/docs/orgtrack-pm-protocol/fixtures/errors/provider-unavailable.json new file mode 100644 index 000000000..604c89160 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/provider-unavailable.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "PROVIDER_UNAVAILABLE", + "message": "Planning provider 'linear' is unreachable; the local mutation was preserved and queued in the outbox", + "retryable": true, + "details": { + "provider": "linear", + "outboxStatus": "pending" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR16" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/result-schema-mismatch.json b/docs/orgtrack-pm-protocol/fixtures/errors/result-schema-mismatch.json new file mode 100644 index 000000000..dc792344c --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/result-schema-mismatch.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "RESULT_SCHEMA_MISMATCH", + "message": "Step 'review-impact' declares output 'impact_report' of type artifact; result is missing it. Transition was not committed", + "retryable": false, + "details": { + "stepId": "review-impact", + "missingOutputs": ["impact_report"] + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR12" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/revision-conflict.json b/docs/orgtrack-pm-protocol/fixtures/errors/revision-conflict.json new file mode 100644 index 000000000..a2466ba6a --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/revision-conflict.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "REVISION_CONFLICT", + "message": "WorkItem changed after revision 7", + "retryable": true, + "details": { + "expectedRevision": 7, + "currentRevision": 8 + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR07" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/scope-violation.json b/docs/orgtrack-pm-protocol/fixtures/errors/scope-violation.json new file mode 100644 index 000000000..7ea1819fb --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/scope-violation.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "SCOPE_VIOLATION", + "message": "WorkItem work_01K0000000000000000000WORK0 belongs to a different scope than the resolved context", + "retryable": false, + "details": { + "resourceScopeId": "project_01K000000000000000000OTHER", + "contextScopeId": "project_01K0000000000000000000SCOPE" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR14" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/store-unavailable.json b/docs/orgtrack-pm-protocol/fixtures/errors/store-unavailable.json new file mode 100644 index 000000000..037cdfba5 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/store-unavailable.json @@ -0,0 +1,15 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "STORE_UNAVAILABLE", + "message": "projects.db is locked or unreachable; no fallback store is used for mutations", + "retryable": true, + "details": { + "store": "projects.db" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR18" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/errors/unsupported-capability.json b/docs/orgtrack-pm-protocol/fixtures/errors/unsupported-capability.json new file mode 100644 index 000000000..7e78fc0c3 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/errors/unsupported-capability.json @@ -0,0 +1,16 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": false, + "error": { + "code": "UNSUPPORTED_CAPABILITY", + "message": "Provider 'github' does not support webhook installation for this connection", + "retryable": false, + "details": { + "provider": "github", + "capability": "webhook" + } + }, + "meta": { + "requestId": "req_01K0000000000000000000ERR17" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/routine-spec.json b/docs/orgtrack-pm-protocol/fixtures/routine-spec.json new file mode 100644 index 000000000..ca27a2a24 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/routine-spec.json @@ -0,0 +1,83 @@ +{ + "apiVersion": "orgtrack/v1", + "kind": "Routine", + "metadata": { + "id": "routine_interaction_impact", + "name": "interaction-impact-analysis", + "revision": 3 + }, + "spec": { + "inputs": { + "requirement_id": { + "type": "string", + "required": true + }, + "prd_path": { + "type": "path", + "required": false + } + }, + "rootWork": { + "title": "交互影响分析:{{ inputs.requirement_id }}", + "body": "完成交付物收集、影响分析与归档通知。", + "priority": "high", + "labels": ["requirement", "impact-analysis"] + }, + "steps": [ + { + "id": "collect-deliverables", + "title": "收集交付物清单", + "actor": { + "role": "delivery-extractor", + "requires": ["artifact-read"] + }, + "instruction": "根据 requirement_id 收集 PRD、配置参数、原型标注、功能边界和测试用例。", + "outputs": { + "deliverables": { + "type": "artifact-list" + } + } + }, + { + "id": "review-impact", + "title": "执行交互影响分析", + "needs": ["collect-deliverables"], + "actor": { + "role": "reviewer", + "requires": ["artifact-read"] + }, + "inputs": { + "deliverables": "${steps.collect-deliverables.outputs.deliverables}" + }, + "outputs": { + "impact_report": { + "type": "artifact" + } + } + }, + { + "id": "archive-and-notify", + "title": "归档资产并通知", + "needs": ["review-impact"], + "actor": { + "role": "project-manager" + }, + "inputs": { + "report": "${steps.review-impact.outputs.impact_report}" + } + } + ], + "activations": [ + { + "type": "manual" + }, + { + "type": "schedule", + "cron": "0 9 * * 1-5", + "timezone": "America/Vancouver", + "concurrencyPolicy": "skip", + "catchUp": "none" + } + ] + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/context.json b/docs/orgtrack-pm-protocol/fixtures/success/context.json new file mode 100644 index 000000000..1d97298f0 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/context.json @@ -0,0 +1,38 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "apiVersion": "orgtrack/v1", + "mode": "project", + "runtimeExecutionMode": "build", + "scopeId": "project_01K0000000000000000000SCOPE", + "orgId": "org_01K00000000000000000000ORG0", + "workspace": "/workspace/org2", + "actor": { + "kind": "agent", + "id": "codex_app:reviewer-1", + "displayName": "Reviewer" + }, + "sessionRef": { + "provider": "org2", + "externalId": "session_abc", + "metadata": { + "nativeHarness": "codex_app" + } + }, + "runtimeProvider": { + "id": "org2", + "profiles": ["execution", "provenance"] + }, + "activeWorkItemId": "work_01K0000000000000000000WORK0", + "capabilities": [ + "work.read", + "work.claim", + "work.transition", + "routine.run" + ] + }, + "meta": { + "requestId": "req_01K0000000000000000000CTX00" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/routine-run.json b/docs/orgtrack-pm-protocol/fixtures/success/routine-run.json new file mode 100644 index 000000000..5572bbf88 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/routine-run.json @@ -0,0 +1,27 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "apiVersion": "orgtrack/v1", + "kind": "RoutineRun", + "id": "run_01K00000000000000000000RUN0", + "routineId": "routine_interaction_impact", + "routineRevision": 3, + "snapshotHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", + "scopeId": "project_01K0000000000000000000SCOPE", + "status": "running", + "inputs": { + "requirement_id": "REQ-20260803-001" + }, + "rootWorkItemId": "work_01K0000000000000000000ROOT0", + "createdBy": { + "kind": "human", + "id": "org2:user:vince" + }, + "createdAt": "2026-08-04T18:00:00Z", + "updatedAt": "2026-08-04T18:05:00Z" + }, + "meta": { + "requestId": "req_01K0000000000000000000RRUN0" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/routine-status.json b/docs/orgtrack-pm-protocol/fixtures/success/routine-status.json new file mode 100644 index 000000000..b8bcccdd1 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/routine-status.json @@ -0,0 +1,49 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "run": { + "apiVersion": "orgtrack/v1", + "kind": "RoutineRun", + "id": "run_01K00000000000000000000RUN0", + "routineId": "routine_interaction_impact", + "routineRevision": 3, + "snapshotHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", + "scopeId": "project_01K0000000000000000000SCOPE", + "status": "running", + "inputs": { + "requirement_id": "REQ-20260803-001" + }, + "rootWorkItemId": "work_01K0000000000000000000ROOT0", + "createdBy": { + "kind": "human", + "id": "org2:user:vince" + }, + "createdAt": "2026-08-04T18:00:00Z", + "updatedAt": "2026-08-04T18:05:00Z" + }, + "workItems": [ + { + "id": "work_01K000000000000000000COLL0", + "stepId": "collect-deliverables", + "state": "completed", + "claimedBy": { "kind": "agent", "id": "org2:agent:extractor" } + }, + { + "id": "work_01K0000000000000000000WORK0", + "stepId": "review-impact", + "state": "in_progress", + "claimedBy": { "kind": "agent", "id": "codex_app:reviewer-1" } + }, + { + "id": "work_01K000000000000000000ARCH0", + "stepId": "archive-and-notify", + "state": "open", + "claimedBy": null + } + ] + }, + "meta": { + "requestId": "req_01K0000000000000000000RSTAT" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/work-claim.json b/docs/orgtrack-pm-protocol/fixtures/success/work-claim.json new file mode 100644 index 000000000..be26cc8f8 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/work-claim.json @@ -0,0 +1,41 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "apiVersion": "orgtrack/v1", + "kind": "WorkItem", + "id": "work_01K0000000000000000000WORK0", + "scopeId": "project_01K0000000000000000000SCOPE", + "key": "ORG2-42", + "title": "执行交互影响分析", + "state": "in_progress", + "priority": "high", + "assignee": null, + "actorRequirement": { + "role": "reviewer", + "requires": ["artifact-read"] + }, + "claimedBy": { + "kind": "agent", + "id": "codex_app:reviewer-1" + }, + "parentId": "work_01K0000000000000000000ROOT0", + "dependsOn": ["work_01K000000000000000000COLL0"], + "labels": ["impact-analysis"], + "origin": { + "kind": "routine_step", + "routineId": "routine_interaction_impact", + "routineRunId": "run_01K00000000000000000000RUN0", + "stepId": "review-impact" + }, + "inputs": {}, + "result": null, + "revision": 8, + "createdAt": "2026-08-04T18:00:00Z", + "updatedAt": "2026-08-04T18:12:00Z" + }, + "meta": { + "requestId": "req_01K0000000000000000000CLAIM", + "revision": 8 + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/work-list.json b/docs/orgtrack-pm-protocol/fixtures/success/work-list.json new file mode 100644 index 000000000..8802636af --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/work-list.json @@ -0,0 +1,47 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "items": [ + { + "apiVersion": "orgtrack/v1", + "kind": "WorkItem", + "id": "work_01K0000000000000000000WORK0", + "scopeId": "project_01K0000000000000000000SCOPE", + "key": "ORG2-42", + "title": "执行交互影响分析", + "body": "检查需求对现有交互和交付物的影响。", + "state": "open", + "priority": "high", + "assignee": null, + "actorRequirement": { + "role": "reviewer", + "requires": ["artifact-read"] + }, + "claimedBy": null, + "parentId": "work_01K0000000000000000000ROOT0", + "dependsOn": ["work_01K000000000000000000COLL0"], + "labels": ["impact-analysis"], + "origin": { + "kind": "routine_step", + "routineId": "routine_interaction_impact", + "routineRunId": "run_01K00000000000000000000RUN0", + "stepId": "review-impact" + }, + "inputs": { + "deliverables": { + "ref": "artifact://deliverables/01K0000000000000000000ART00" + } + }, + "result": null, + "revision": 7, + "createdAt": "2026-08-04T18:00:00Z", + "updatedAt": "2026-08-04T18:10:00Z" + } + ] + }, + "meta": { + "requestId": "req_01K0000000000000000000LIST0", + "nextCursor": "cur_01K0000000000000000000NEXT0" + } +} diff --git a/docs/orgtrack-pm-protocol/fixtures/success/work-transition.json b/docs/orgtrack-pm-protocol/fixtures/success/work-transition.json new file mode 100644 index 000000000..e25f49ec8 --- /dev/null +++ b/docs/orgtrack-pm-protocol/fixtures/success/work-transition.json @@ -0,0 +1,56 @@ +{ + "apiVersion": "orgtrack/v1", + "ok": true, + "data": { + "apiVersion": "orgtrack/v1", + "kind": "WorkItem", + "id": "work_01K0000000000000000000WORK0", + "scopeId": "project_01K0000000000000000000SCOPE", + "key": "ORG2-42", + "title": "执行交互影响分析", + "state": "completed", + "priority": "high", + "assignee": null, + "actorRequirement": { + "role": "reviewer", + "requires": ["artifact-read"] + }, + "claimedBy": { + "kind": "agent", + "id": "codex_app:reviewer-1" + }, + "parentId": "work_01K0000000000000000000ROOT0", + "dependsOn": ["work_01K000000000000000000COLL0"], + "labels": ["impact-analysis"], + "origin": { + "kind": "routine_step", + "routineId": "routine_interaction_impact", + "routineRunId": "run_01K00000000000000000000RUN0", + "stepId": "review-impact" + }, + "inputs": {}, + "result": { + "summary": "完成影响分析,发现两个高风险交互。", + "artifacts": [ + { + "kind": "report", + "ref": "file://reports/impact.md", + "contentHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000" + } + ], + "references": [ + { + "kind": "pull_request", + "ref": "github://org2AI/ORGII/pull/87" + } + ] + }, + "revision": 9, + "createdAt": "2026-08-04T18:00:00Z", + "updatedAt": "2026-08-04T18:30:00Z" + }, + "meta": { + "requestId": "req_01K0000000000000000000TRANS", + "revision": 9 + } +} diff --git a/docs/orgtrack-pm-protocol/parity-matrix.md b/docs/orgtrack-pm-protocol/parity-matrix.md new file mode 100644 index 000000000..b642632b4 --- /dev/null +++ b/docs/orgtrack-pm-protocol/parity-matrix.md @@ -0,0 +1,20 @@ +# 入口一致性矩阵(Layer 9 / 设计文档 §22.3) + +每个入口必须经过同一 Context resolver 与同一 application command 层。空格 = +未落地;落地 phase 在括号内标注后打 ✓;任何长期空格需要书面理由。 + +| entry point | context resolver | actor 解析 | auth/capability 交集 | idempotency | OCC (expected-revision) | audit event | outbox | pm_change_seq bump | +| ----------------- | ---------------- | ---------- | -------------------- | ----------- | ----------------------- | ----------- | ------ | ------------------ | +| CLI (`org2-pm`) | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | +| Tauri commands | (P2a) | (P2a) | (P2a) | (P2a) | (P2a) | (P2a) | (P2a) | (P2a) | +| Routine scheduler | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | +| Provider adapter | (P6) | (P6) | (P6) | (P6) | (P6) | (P6) | (P6) | (P6) | +| hooks | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | (P5) | +| tests/E2E helpers | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | (P3) | + +规则: + +- Test/helper 不得走不同初始化或 debug-only mutation path(helpers 只能 seed + 或 inspect,不得成为被测行为的 side-effect 路径); +- scheduler 的 manual fire 与 automatic fire 调用同一个 `routine.invoke`; +- Provider adapter 不得绕过 application service 直接写 WorkItem/relation。 diff --git a/docs/orgtrack-pm-protocol/schemas/common.schema.json b/docs/orgtrack-pm-protocol/schemas/common.schema.json new file mode 100644 index 000000000..13af2dc3a --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/common.schema.json @@ -0,0 +1,144 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/common.schema.json", + "title": "Orgtrack v1 shared definitions", + "definitions": { + "apiVersion": { + "type": "string", + "const": "orgtrack/v1" + }, + "actorKind": { + "type": "string", + "enum": ["human", "agent", "service", "team"] + }, + "actorRef": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "id"], + "properties": { + "kind": { "$ref": "#/definitions/actorKind" }, + "id": { + "type": "string", + "minLength": 1, + "description": "Namespaced stable opaque identifier. MUST NOT carry a role: prefix — role requirements live in actorRequirement." + }, + "displayName": { "type": "string" } + } + }, + "actorRequirement": { + "type": "object", + "additionalProperties": false, + "required": ["role"], + "properties": { + "role": { "type": "string", "minLength": 1 }, + "requires": { + "type": "array", + "items": { "type": "string" } + } + } + }, + "sessionRef": { + "type": "object", + "additionalProperties": false, + "required": ["provider", "externalId"], + "properties": { + "provider": { + "type": "string", + "minLength": 1, + "description": "Stable provenance provider ID from the frozen registry (decisions.md §5). ORG2-owned sessions use org2; external sessions use canonical importer source ids (claude_code, codex_app, ...)." + }, + "externalId": { "type": "string", "minLength": 1 }, + "metadata": { + "type": "object", + "description": "Cloud/collab sessions MUST carry the org/owner identity tuple here because externalId alone is not globally unique.", + "properties": { + "nativeHarness": { "type": "string" }, + "orgId": { "type": "string" }, + "ownerId": { "type": "string" } + }, + "additionalProperties": true + } + } + }, + "workItemState": { + "type": "string", + "enum": [ + "open", + "in_progress", + "blocked", + "completed", + "failed", + "cancelled" + ] + }, + "workItemPriority": { + "type": "string", + "enum": ["none", "urgent", "high", "medium", "low"] + }, + "routineRunStatus": { + "type": "string", + "enum": [ + "pending", + "running", + "blocked", + "cancel_requested", + "succeeded", + "failed", + "cancelled" + ] + }, + "productMode": { + "type": "string", + "enum": ["build", "plan", "ask", "project"] + }, + "capabilityId": { + "type": "string", + "enum": [ + "work.read", + "work.create", + "work.update", + "work.claim", + "work.transition", + "work.note", + "work.relate", + "routine.read", + "routine.apply", + "routine.run", + "routine.cancel", + "routine.set_enabled" + ] + }, + "updateKind": { + "type": "string", + "enum": [ + "comment", + "progress", + "blocker", + "decision", + "handoff", + "review" + ] + }, + "relationKind": { + "type": "string", + "enum": [ + "depends_on", + "relates_to", + "duplicates", + "implements", + "supersedes", + "continued_by", + "generated_by", + "participated_in" + ] + }, + "isoTimestamp": { + "type": "string", + "format": "date-time" + }, + "revision": { + "type": "integer", + "minimum": 0 + } + } +} diff --git a/docs/orgtrack-pm-protocol/schemas/envelope.schema.json b/docs/orgtrack-pm-protocol/schemas/envelope.schema.json new file mode 100644 index 000000000..8235f7359 --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/envelope.schema.json @@ -0,0 +1,83 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/envelope.schema.json", + "title": "Orgtrack v1 CLI response envelopes", + "oneOf": [ + { "$ref": "#/definitions/successEnvelope" }, + { "$ref": "#/definitions/errorEnvelope" } + ], + "definitions": { + "requestId": { + "type": "string", + "pattern": "^req_[A-Za-z0-9]+$" + }, + "errorCode": { + "type": "string", + "enum": [ + "INVALID_ARGUMENT", + "CONTEXT_REQUIRED", + "ACTOR_REQUIRED", + "PROJECT_MODE_REQUIRED", + "NOT_FOUND", + "ALREADY_EXISTS", + "REVISION_CONFLICT", + "IDEMPOTENCY_CONFLICT", + "NOT_READY", + "ALREADY_CLAIMED", + "INVALID_TRANSITION", + "RESULT_SCHEMA_MISMATCH", + "DEPENDENCY_CYCLE", + "SCOPE_VIOLATION", + "PERMISSION_DENIED", + "PROVIDER_UNAVAILABLE", + "UNSUPPORTED_CAPABILITY", + "STORE_UNAVAILABLE" + ] + }, + "meta": { + "type": "object", + "additionalProperties": false, + "required": ["requestId"], + "properties": { + "requestId": { "$ref": "#/definitions/requestId" }, + "revision": { "type": "integer", "minimum": 0 }, + "nextCursor": { + "type": "string", + "description": "Present on list responses when more pages exist." + } + } + }, + "successEnvelope": { + "type": "object", + "additionalProperties": false, + "required": ["apiVersion", "ok", "data", "meta"], + "properties": { + "apiVersion": { "const": "orgtrack/v1" }, + "ok": { "const": true }, + "data": { "type": "object" }, + "meta": { "$ref": "#/definitions/meta" } + } + }, + "errorEnvelope": { + "type": "object", + "additionalProperties": false, + "required": ["apiVersion", "ok", "error", "meta"], + "properties": { + "apiVersion": { "const": "orgtrack/v1" }, + "ok": { "const": false }, + "error": { + "type": "object", + "additionalProperties": false, + "required": ["code", "message", "retryable"], + "properties": { + "code": { "$ref": "#/definitions/errorCode" }, + "message": { "type": "string" }, + "retryable": { "type": "boolean" }, + "details": { "type": "object" } + } + }, + "meta": { "$ref": "#/definitions/meta" } + } + } + } +} diff --git a/docs/orgtrack-pm-protocol/schemas/execution-context.schema.json b/docs/orgtrack-pm-protocol/schemas/execution-context.schema.json new file mode 100644 index 000000000..4df89f85f --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/execution-context.schema.json @@ -0,0 +1,56 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/execution-context.schema.json", + "title": "Orgtrack v1 ExecutionContext (org2 context response data)", + "type": "object", + "additionalProperties": false, + "required": [ + "apiVersion", + "mode", + "scopeId", + "orgId", + "actor", + "capabilities" + ], + "properties": { + "apiVersion": { "$ref": "common.schema.json#/definitions/apiVersion" }, + "mode": { "$ref": "common.schema.json#/definitions/productMode" }, + "runtimeExecutionMode": { + "type": "string", + "description": "Existing agent/provider wire values (build|ask|plan|debug|review|wingman|provider extension). Distinct from the Rust ExecutionMode{Direct,WorkStation} type — see decisions.md §2." + }, + "scopeId": { "type": "string", "minLength": 1 }, + "orgId": { "type": "string", "minLength": 1 }, + "workspace": { "type": "string" }, + "actor": { "$ref": "common.schema.json#/definitions/actorRef" }, + "sessionRef": { + "oneOf": [ + { "$ref": "common.schema.json#/definitions/sessionRef" }, + { "type": "null" } + ] + }, + "runtimeProvider": { + "type": "object", + "additionalProperties": false, + "required": ["id", "profiles"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "profiles": { + "type": "array", + "items": { + "type": "string", + "enum": ["planning", "execution", "provenance"] + } + } + } + }, + "activeWorkItemId": { + "oneOf": [{ "type": "string" }, { "type": "null" }] + }, + "capabilities": { + "type": "array", + "items": { "$ref": "common.schema.json#/definitions/capabilityId" }, + "uniqueItems": true + } + } +} diff --git a/docs/orgtrack-pm-protocol/schemas/routine-run.schema.json b/docs/orgtrack-pm-protocol/schemas/routine-run.schema.json new file mode 100644 index 000000000..af8abb8e0 --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/routine-run.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/routine-run.schema.json", + "title": "Orgtrack v1 RoutineRun", + "type": "object", + "additionalProperties": false, + "required": [ + "apiVersion", + "kind", + "id", + "routineId", + "routineRevision", + "snapshotHash", + "scopeId", + "status", + "createdBy", + "createdAt", + "updatedAt" + ], + "properties": { + "apiVersion": { "$ref": "common.schema.json#/definitions/apiVersion" }, + "kind": { "const": "RoutineRun" }, + "id": { "type": "string", "pattern": "^run_[A-Za-z0-9]+$" }, + "routineId": { "type": "string", "minLength": 1 }, + "routineRevision": { "type": "integer", "minimum": 1 }, + "snapshotHash": { "type": "string", "pattern": "^sha256:[0-9a-f]+$" }, + "scopeId": { "type": "string", "minLength": 1 }, + "status": { + "$ref": "common.schema.json#/definitions/routineRunStatus", + "description": "Durable projection of generated WorkItems, computed by the ordered decision procedure in design doc §11. pending only exists for queued (concurrencyPolicy: queue) runs that have not materialized yet." + }, + "inputs": { "type": "object" }, + "rootWorkItemId": { + "oneOf": [{ "type": "string" }, { "type": "null" }], + "description": "Null only while status is pending (queued, not yet materialized)." + }, + "createdBy": { "$ref": "common.schema.json#/definitions/actorRef" }, + "createdAt": { "$ref": "common.schema.json#/definitions/isoTimestamp" }, + "updatedAt": { "$ref": "common.schema.json#/definitions/isoTimestamp" } + } +} diff --git a/docs/orgtrack-pm-protocol/schemas/routine.schema.json b/docs/orgtrack-pm-protocol/schemas/routine.schema.json new file mode 100644 index 000000000..6656ed2d4 --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/routine.schema.json @@ -0,0 +1,153 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/routine.schema.json", + "title": "Orgtrack v1 portable Routine spec", + "type": "object", + "additionalProperties": false, + "required": ["apiVersion", "kind", "metadata", "spec"], + "properties": { + "apiVersion": { "$ref": "common.schema.json#/definitions/apiVersion" }, + "kind": { "const": "Routine" }, + "metadata": { + "type": "object", + "additionalProperties": false, + "required": ["id", "name"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "name": { "type": "string", "minLength": 1 }, + "revision": { "type": "integer", "minimum": 1 } + } + }, + "spec": { + "type": "object", + "additionalProperties": false, + "required": ["rootWork", "steps"], + "properties": { + "inputs": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/inputDecl" } + }, + "rootWork": { + "type": "object", + "additionalProperties": false, + "required": ["title"], + "properties": { + "title": { "type": "string", "minLength": 1 }, + "body": { "type": "string" }, + "priority": { + "$ref": "common.schema.json#/definitions/workItemPriority" + }, + "labels": { "type": "array", "items": { "type": "string" } } + } + }, + "steps": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/definitions/step" } + }, + "activations": { + "type": "array", + "items": { "$ref": "#/definitions/activation" } + } + } + } + }, + "definitions": { + "inputDecl": { + "type": "object", + "additionalProperties": false, + "required": ["type"], + "properties": { + "type": { + "type": "string", + "enum": ["string", "number", "boolean", "path"] + }, + "required": { "type": "boolean", "default": false } + } + }, + "step": { + "type": "object", + "additionalProperties": false, + "required": ["id", "title"], + "properties": { + "id": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9-]*$" + }, + "title": { "type": "string", "minLength": 1 }, + "needs": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "description": "Step ids within the same Routine only; the graph must be acyclic." + }, + "actor": { "$ref": "common.schema.json#/definitions/actorRequirement" }, + "instruction": { + "type": "string", + "description": "Becomes part of the generated WorkItem body. Never a session transcript." + }, + "inputs": { + "type": "object", + "additionalProperties": { "type": "string" }, + "description": "Mapping expressions like ${steps..outputs.} or ${inputs.}. No arbitrary code." + }, + "outputs": { + "type": "object", + "additionalProperties": { "$ref": "#/definitions/outputDecl" } + } + } + }, + "outputDecl": { + "type": "object", + "additionalProperties": false, + "required": ["type"], + "properties": { + "type": { + "type": "string", + "enum": ["artifact", "artifact-list", "reference"] + } + } + }, + "activation": { + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string", + "enum": ["manual", "schedule", "provider_event"] + }, + "cron": { "type": "string" }, + "timezone": { "type": "string" }, + "provider": { "type": "string" }, + "eventKind": { "type": "string" }, + "filter": { + "type": "object", + "description": "Declarative filter only; no executable code. Payload size limits enforced host-side." + }, + "concurrencyPolicy": { + "type": "string", + "enum": ["coalesce", "skip", "queue"], + "default": "skip", + "description": "Behavior when the previous Run is not terminal. coalesce/skip record an AuditEvent without creating a Run; queue creates a pending Run." + }, + "catchUp": { + "type": "string", + "enum": ["none", "fire_once"], + "default": "none", + "description": "Compensation for schedule fires missed while the ORG2 host process was not running." + } + }, + "allOf": [ + { + "if": { "properties": { "type": { "const": "schedule" } } }, + "then": { "required": ["cron", "timezone"] } + }, + { + "if": { "properties": { "type": { "const": "provider_event" } } }, + "then": { "required": ["provider", "eventKind"] } + } + ], + "additionalProperties": false + } + } +} diff --git a/docs/orgtrack-pm-protocol/schemas/work-item.schema.json b/docs/orgtrack-pm-protocol/schemas/work-item.schema.json new file mode 100644 index 000000000..0bccc7b74 --- /dev/null +++ b/docs/orgtrack-pm-protocol/schemas/work-item.schema.json @@ -0,0 +1,123 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "orgtrack/v1/work-item.schema.json", + "title": "Orgtrack v1 WorkItem", + "type": "object", + "additionalProperties": false, + "required": [ + "apiVersion", + "kind", + "id", + "scopeId", + "title", + "state", + "revision", + "createdAt", + "updatedAt" + ], + "properties": { + "apiVersion": { "$ref": "common.schema.json#/definitions/apiVersion" }, + "kind": { "const": "WorkItem" }, + "id": { "type": "string", "pattern": "^work_[A-Za-z0-9]+$" }, + "scopeId": { "type": "string", "minLength": 1 }, + "key": { + "type": "string", + "description": "Human-readable identifier, unique only within the scope." + }, + "title": { "type": "string", "minLength": 1 }, + "body": { "type": "string" }, + "state": { "$ref": "common.schema.json#/definitions/workItemState" }, + "priority": { "$ref": "common.schema.json#/definitions/workItemPriority" }, + "assignee": { + "oneOf": [ + { "$ref": "common.schema.json#/definitions/actorRef" }, + { "type": "null" } + ], + "description": "Concrete actor only. Role/capability requirements live in actorRequirement." + }, + "actorRequirement": { + "oneOf": [ + { "$ref": "common.schema.json#/definitions/actorRequirement" }, + { "type": "null" } + ] + }, + "claimedBy": { + "oneOf": [ + { "$ref": "common.schema.json#/definitions/actorRef" }, + { "type": "null" } + ], + "description": "Required (non-null) whenever state is in_progress. Presence does not prove the agent process is alive." + }, + "parentId": { + "oneOf": [{ "type": "string" }, { "type": "null" }] + }, + "dependsOn": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true + }, + "labels": { + "type": "array", + "items": { "type": "string" } + }, + "origin": { + "type": "object", + "description": "Immutable creation provenance. Later RoutineRun participation uses typed Run relations, never overwrites origin.", + "additionalProperties": false, + "required": ["kind"], + "properties": { + "kind": { + "type": "string", + "enum": [ + "manual", + "routine_step", + "track_this", + "convert_from_plan", + "project_first_submit" + ] + }, + "routineId": { "type": "string" }, + "routineRunId": { "type": "string" }, + "stepId": { "type": "string" } + } + }, + "inputs": { + "type": "object", + "description": "Typed references bound from upstream step outputs. Never inline payloads." + }, + "result": { + "oneOf": [{ "$ref": "#/definitions/result" }, { "type": "null" }] + }, + "revision": { "$ref": "common.schema.json#/definitions/revision" }, + "createdAt": { "$ref": "common.schema.json#/definitions/isoTimestamp" }, + "updatedAt": { "$ref": "common.schema.json#/definitions/isoTimestamp" } + }, + "definitions": { + "typedRef": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "ref"], + "properties": { + "kind": { "type": "string", "minLength": 1 }, + "ref": { "type": "string", "minLength": 1 }, + "contentHash": { "type": "string" }, + "label": { "type": "string" } + } + }, + "result": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { "type": "string" }, + "artifacts": { + "type": "array", + "items": { "$ref": "#/definitions/typedRef" } + }, + "references": { + "type": "array", + "items": { "$ref": "#/definitions/typedRef" } + } + } + } + } +} From f8b587d4bce0955cbb62e93f0e45699f089e5c0d Mon Sep 17 00:00:00 2001 From: Neonforge <48338160+Neonforge98@users.noreply.github.com> Date: Tue, 4 Aug 2026 20:52:19 -0700 Subject: [PATCH 02/33] refactor(agents): delete work-item-manager and dead automation paths 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 --- .../managed-cloud-collaboration.md | 5 +- packages/orgtrack/package.json | 21 - packages/orgtrack/src/cli.ts | 17 - packages/orgtrack/src/index.ts | 93 --- packages/orgtrack/tsconfig.json | 13 - .../src/core/definitions/builtin/mod.rs | 6 +- .../builtin/prompts/work_item_manager.md | 24 - .../definitions/builtin/work_item_manager.rs | 144 ----- .../agent-core/src/core/definitions/mod.rs | 4 +- .../src/core/definitions/tests_extended.rs | 7 +- .../src/core/session/turn/processor/prompt.rs | 25 +- .../src/specialization/policies/behavior.rs | 227 ------- .../src/specialization/policies/mod.rs | 2 - .../policies/tests/behavior_tests.rs | 133 ---- .../src/state/commands/automation.rs | 124 ---- .../agent-core/src/state/commands/mod.rs | 1 - src-tauri/crates/orgtrack-cli/README.md | 6 +- src-tauri/src/commands/handler_list.inc | 7 - src/api/tauri/agent/automation.ts | 42 +- src/app/root/E2EBootstrap.tsx | 6 - src/app/root/e2e/helpers/config.ts | 38 -- src/app/root/e2e/types.ts | 5 - .../ChatPanel/hooks/useAiWorkItemCreator.ts | 4 +- .../resolveSessionCreatorAgentHero.ts | 2 - .../MainApp/AgentOrgs/EditPanel/index.tsx | 587 ------------------ .../components/AutomationWorkflowStep.tsx | 218 ------- .../CommandCard/configs/controlConfigs.tsx | 90 --- .../components/CommandCard/configs/index.ts | 53 -- .../CommandCard/configs/repoConfigs.tsx | 49 -- .../CommandCard/configs/sessionConfigs.tsx | 203 ------ .../components/CommandCard/index.tsx | 224 ------- .../AgentOrgs/components/CommandCard/types.ts | 114 ---- .../components/EmptyWorkflowState/index.tsx | 21 - .../WorkflowEditorContent/index.tsx | 247 -------- .../components/WorkflowGap/index.tsx | 101 --- .../WorkflowNodeItem/ActionNode.tsx | 214 ------- .../WorkflowNodeItem/BranchLabelNode.tsx | 93 --- .../EmptyBranchPlaceholder.tsx | 86 --- .../WorkflowNodeItem/EndBlockNode.tsx | 85 --- .../components/WorkflowNodeItem/constants.ts | 2 - .../components/WorkflowNodeItem/index.tsx | 154 ----- .../MainApp/AgentOrgs/components/index.ts | 34 - .../AgentOrgs/data/actions/controlActions.ts | 132 ---- .../AgentOrgs/data/actions/generalActions.ts | 138 ---- .../MainApp/AgentOrgs/data/actions/index.ts | 12 - .../AgentOrgs/data/actions/stageActions.ts | 217 ------- .../data/actions/workflowConfigActions.ts | 138 ---- src/modules/MainApp/AgentOrgs/data/index.ts | 9 +- src/modules/MainApp/AgentOrgs/data/types.ts | 106 +--- src/modules/MainApp/AgentOrgs/hooks/index.ts | 6 - .../AgentOrgs/hooks/useAddActionState.ts | 44 -- .../AgentOrgs/hooks/useFlatWorkflowDnd.ts | 159 ----- .../AgentOrgs/hooks/useWorkflowActions.ts | 75 --- .../AgentOrgs/hooks/useWorkflowCollapse.ts | 161 ----- .../AgentOrgs/hooks/useWorkflowEditPanel.ts | 449 -------------- .../hooks/useWorkflowModelOptions.ts | 71 --- src/modules/MainApp/AgentOrgs/types/index.ts | 7 - .../MainApp/AgentOrgs/types/workflow.ts | 143 ----- .../AgentOrgs/utils/flattenWorkflow.ts | 329 ---------- .../AgentOrgs/utils/translateAction.ts | 77 --- .../Detail/AutomationOverviewSection.tsx | 123 ---- .../Detail/AutomationQuickActionsSection.tsx | 81 --- .../Detail/AutomationRuleDetailView.tsx | 84 --- .../Detail/PolicyGroupTable.tsx | 340 ---------- .../RulesMemoryEvolution/Detail/index.ts | 3 - .../RulesMemoryEvolution/config.ts | 87 --- .../RulesMemoryEvolution/types.ts | 214 +------ .../hooks/useRulesMemoryEvolutionState.ts | 6 - .../FileWatchPathsTable.tsx | 104 ---- .../AutomationTriggerConfig/TriggerFields.tsx | 269 -------- .../Policy/AutomationTriggerConfig/helpers.ts | 77 --- .../Policy/AutomationTriggerConfig/index.tsx | 417 ------------- .../Policy/AutomationTriggerConfig/types.ts | 33 - .../core/work-item-durable-object.spec.mjs | 6 +- 74 files changed, 49 insertions(+), 7599 deletions(-) delete mode 100644 packages/orgtrack/package.json delete mode 100644 packages/orgtrack/src/cli.ts delete mode 100644 packages/orgtrack/src/index.ts delete mode 100644 packages/orgtrack/tsconfig.json delete mode 100644 src-tauri/crates/agent-core/src/core/definitions/builtin/prompts/work_item_manager.md delete mode 100644 src-tauri/crates/agent-core/src/core/definitions/builtin/work_item_manager.rs delete mode 100644 src-tauri/crates/agent-core/src/specialization/policies/behavior.rs delete mode 100644 src-tauri/crates/agent-core/src/specialization/policies/tests/behavior_tests.rs delete mode 100644 src-tauri/crates/agent-core/src/state/commands/automation.rs delete mode 100644 src/modules/MainApp/AgentOrgs/EditPanel/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/AutomationWorkflowStep.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/configs/controlConfigs.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/configs/index.ts delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/configs/repoConfigs.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/configs/sessionConfigs.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/CommandCard/types.ts delete mode 100644 src/modules/MainApp/AgentOrgs/components/EmptyWorkflowState/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowEditorContent/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowGap/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/ActionNode.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/BranchLabelNode.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/EmptyBranchPlaceholder.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/EndBlockNode.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/constants.ts delete mode 100644 src/modules/MainApp/AgentOrgs/components/WorkflowNodeItem/index.tsx delete mode 100644 src/modules/MainApp/AgentOrgs/components/index.ts delete mode 100644 src/modules/MainApp/AgentOrgs/data/actions/controlActions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/data/actions/generalActions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/data/actions/index.ts delete mode 100644 src/modules/MainApp/AgentOrgs/data/actions/stageActions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/data/actions/workflowConfigActions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useAddActionState.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useFlatWorkflowDnd.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useWorkflowActions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useWorkflowCollapse.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useWorkflowEditPanel.ts delete mode 100644 src/modules/MainApp/AgentOrgs/hooks/useWorkflowModelOptions.ts delete mode 100644 src/modules/MainApp/AgentOrgs/types/index.ts delete mode 100644 src/modules/MainApp/AgentOrgs/types/workflow.ts delete mode 100644 src/modules/MainApp/AgentOrgs/utils/flattenWorkflow.ts delete mode 100644 src/modules/MainApp/AgentOrgs/utils/translateAction.ts delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/Detail/AutomationOverviewSection.tsx delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/Detail/AutomationQuickActionsSection.tsx delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/Detail/AutomationRuleDetailView.tsx delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/Detail/PolicyGroupTable.tsx delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/Detail/index.ts delete mode 100644 src/modules/MainApp/Integrations/RulesMemoryEvolution/config.ts delete mode 100644 src/scaffold/WizardSystem/variants/Policy/AutomationTriggerConfig/FileWatchPathsTable.tsx delete mode 100644 src/scaffold/WizardSystem/variants/Policy/AutomationTriggerConfig/TriggerFields.tsx delete mode 100644 src/scaffold/WizardSystem/variants/Policy/AutomationTriggerConfig/helpers.ts delete mode 100644 src/scaffold/WizardSystem/variants/Policy/AutomationTriggerConfig/index.tsx delete mode 100644 src/scaffold/WizardSystem/variants/Policy/AutomationTriggerConfig/types.ts diff --git a/docs/architecture/managed-cloud-collaboration.md b/docs/architecture/managed-cloud-collaboration.md index 2f5459eaa..debb88e26 100644 --- a/docs/architecture/managed-cloud-collaboration.md +++ b/docs/architecture/managed-cloud-collaboration.md @@ -123,8 +123,9 @@ detail views and ChatPanel. Lock release is serialized as explicit JSON ## Create with AI -Create with AI uses `builtin:work-item-manager` by default and follows one -durable-draft invariant: +Create with AI uses `builtin:os` by default (the Work Item Manager persona was +retired; `manage_work_item`/`manage_project` are ordinary built-in tools) and +follows one durable-draft invariant: 1. Before launch, the UI allocates a cloud-aware Work Item ID and writes one draft in the selected Project or organization-scoped standalone store. diff --git a/packages/orgtrack/package.json b/packages/orgtrack/package.json deleted file mode 100644 index b4939f450..000000000 --- a/packages/orgtrack/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "@orgii/orgtrack", - "version": "0.1.0", - "private": true, - "description": "TypeScript surface and CLI wrapper for orgtrack-core developer activity records, projections, and repo sync.", - "type": "module", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", - "bin": { - "orgtrack": "./dist/cli.js" - }, - "scripts": { - "build": "tsc -p tsconfig.json" - }, - "files": [ - "dist" - ], - "devDependencies": { - "typescript": "^5.9.3" - } -} diff --git a/packages/orgtrack/src/cli.ts b/packages/orgtrack/src/cli.ts deleted file mode 100644 index e931aaf89..000000000 --- a/packages/orgtrack/src/cli.ts +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env node - -const command = process.argv[2]; - -if (!command || command === "help" || command === "--help") { - process.stdout.write("orgtrack commands: scan, sync, repair, stats\n"); - process.exit(0); -} - -if (!["scan", "sync", "repair", "stats"].includes(command)) { - console.error(`Unknown orgtrack command: ${command}`); - process.exit(1); -} - -process.stdout.write( - `orgtrack ${command} is a thin package entrypoint. Native orgtrack-core bindings will be attached during publish prep.\n` -); diff --git a/packages/orgtrack/src/index.ts b/packages/orgtrack/src/index.ts deleted file mode 100644 index 07634774d..000000000 --- a/packages/orgtrack/src/index.ts +++ /dev/null @@ -1,93 +0,0 @@ -export const ORGTRACK_SCHEMA_VERSION = 1 as const; -export const ORGTRACK_DIR_NAME = ".orgtrack" as const; - -export type OrgtrackTier = "meta" | "details" | "trajectory"; -export type ActivityKind = - | "heartbeat" - | "tool_call" - | "file_edit" - | "file_create" - | "file_delete" - | "terminal_command" - | "agent_action" - | "message" - | "import_event" - | "focus_gained" - | "focus_lost"; - -export interface AgentMetadata { - dispatchCategory?: string; - rustAgentType?: string; - cliAgentType?: string; - agentExecMode?: string; - providerModelType?: string; - model?: string; - keySource?: string; - origin?: string; - displayName?: string; - parsedCategories: Record; -} - -export interface SessionRecord { - schemaVersion: number; - source: string; - sourceSessionId: string; - sessionId: string; - title: string; - status?: string; - createdAt?: string; - updatedAt?: string; - completedAt?: string; - workspacePath?: string; - branch?: string; - parentSessionId?: string; - orgMemberId?: string; - metadata: AgentMetadata; -} - -export interface ActivityRecord { - schemaVersion: number; - recordId: string; - source: string; - sourceEventId?: string; - sessionId?: string; - timestamp: string; - kind: ActivityKind; - workspacePath?: string; - filePath?: string; - language?: string; - linesAdded: number; - linesRemoved: number; - metadataJson?: string; - tier: OrgtrackTier; -} - -export interface FileChangeRecord { - schemaVersion: number; - recordId: string; - source: string; - sessionId: string; - filePath: string; - pathHash: string; - functionName?: string; - nodeType?: string; - startLine?: number; - endLine?: number; - linesAdded: number; - linesRemoved: number; - timestamp: number; - tier: OrgtrackTier; - metadata: AgentMetadata; -} - -export interface CoreSessionSummary { - sessionId: string; - title: string; - source: string; - workspacePath?: string; - filesChanged: number; - relatedCommits: number; - committedRatePercent: number; - model?: string; - keySource?: string; -} diff --git a/packages/orgtrack/tsconfig.json b/packages/orgtrack/tsconfig.json deleted file mode 100644 index d00b6c990..000000000 --- a/packages/orgtrack/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "declaration": true, - "outDir": "dist", - "rootDir": "src", - "strict": true, - "skipLibCheck": true - }, - "include": ["src/**/*.ts"] -} diff --git a/src-tauri/crates/agent-core/src/core/definitions/builtin/mod.rs b/src-tauri/crates/agent-core/src/core/definitions/builtin/mod.rs index a45acbaba..a7f0a8b34 100644 --- a/src-tauri/crates/agent-core/src/core/definitions/builtin/mod.rs +++ b/src-tauri/crates/agent-core/src/core/definitions/builtin/mod.rs @@ -34,7 +34,6 @@ mod os; mod sde; mod subagents; mod wingman; -mod work_item_manager; pub use ade_manager::*; pub use ai_research::*; @@ -46,7 +45,6 @@ pub use os::*; pub use sde::*; pub use subagents::*; pub use wingman::*; -pub use work_item_manager::*; use super::schema::AgentDefinition; @@ -117,7 +115,6 @@ pub fn get_builtin_agents() -> Vec { os_agent(), ai_research_agent(), wingman_agent(), - work_item_manager_agent(), // Subagents (used by the unified `agent` tool) explore_agent(), general_agent(), @@ -139,7 +136,7 @@ mod tests { #[test] fn test_builtin_agents_count() { let agents = get_builtin_agents(); - assert_eq!(agents.len(), 12); // ADE Manager, base, os, sde, ds, ai-research, wingman, work-item-manager + 2 subagents + 2 memory subagents + assert_eq!(agents.len(), 11); // ADE Manager, base, os, sde, ds, ai-research, wingman + 2 subagents + 2 memory subagents } #[test] @@ -151,7 +148,6 @@ mod tests { assert!(is_builtin_agent(DS_AGENT_ID)); assert!(is_builtin_agent(AI_RESEARCH_AGENT_ID)); assert!(is_builtin_agent(WINGMAN_AGENT_ID)); - assert!(is_builtin_agent(WORK_ITEM_MANAGER_AGENT_ID)); assert!(is_builtin_agent(EXPLORE_AGENT_ID)); assert!(is_builtin_agent(GENERAL_AGENT_ID)); assert!(is_builtin_agent(MEMORY_EXTRACTOR_ID)); diff --git a/src-tauri/crates/agent-core/src/core/definitions/builtin/prompts/work_item_manager.md b/src-tauri/crates/agent-core/src/core/definitions/builtin/prompts/work_item_manager.md deleted file mode 100644 index a4eacba92..000000000 --- a/src-tauri/crates/agent-core/src/core/definitions/builtin/prompts/work_item_manager.md +++ /dev/null @@ -1,24 +0,0 @@ -You are the Work Item Manager. - -Your job is to help users turn ambiguous intent into accurate Work Items, keep Work Item drafts up to date while chatting, and link the planning session to the Work Items you create or modify. - -Core behavior: - -- Use `manage_work_item` as the source of truth for creating, reading, updating, deleting, and linking Work Items. -- Omit `project_slug` for standalone Work Items. Only set `project_slug` when the user explicitly chooses a Project or context makes the Project unambiguous. -- If the user asks for multiple Work Items, create them in one `manage_work_item` `batch` call so partial failures are reported together. -- If items belong to different Projects, put `project_slug` on each batch item. If all items share one Project, you may put `project_slug` at the batch level. -- Use `link_session` after creating or updating a Work Item so the current chat appears in the Work Item's linked sessions. -- When the user changes assignee/model/account/org during planning, update the Work Item's `assignee` and orchestrator config fields with `manage_work_item`. -- Preserve standalone support. Never invent a fake Personal Workspace project slug. - -Research behavior: - -- Use read-only tools (`read_file`, `list_dir`, `code_search`, `web_search`, `web_fetch`, `manage_workspace`, `manage_project`) to understand context before creating detailed Work Items. -- Do not edit files, run shell commands, or use desktop control. If implementation is needed, create or update the Work Item and assign it to an implementation agent. - -Output style: - -- Be concise. -- After mutating Work Items, summarize the created/updated titles and IDs. -- If the user decides not to continue, delete or cancel the draft Work Item according to their wording. diff --git a/src-tauri/crates/agent-core/src/core/definitions/builtin/work_item_manager.rs b/src-tauri/crates/agent-core/src/core/definitions/builtin/work_item_manager.rs deleted file mode 100644 index 5c60d4e56..000000000 --- a/src-tauri/crates/agent-core/src/core/definitions/builtin/work_item_manager.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! Work Item Manager agent template. -//! -//! A focused planner/triage agent for creating and updating project-scoped -//! and standalone Work Items. It has read-only research tools plus the -//! management tools required to inspect projects and mutate Work Items. - -use crate::definitions::capabilities::{ - BrowserCapability, CapabilitySet, CodingCapability, ManagementCapability, -}; -use crate::definitions::schema::{ - AgentDefinition, AgentLearningsConfig, AgentPolicy, AgentTier, AgentToolSelection, - CompactionConfig, DelegationConfig, SessionMode, SessionModel, -}; -use crate::foundation::security::policy::AutonomyLevel; -use crate::tools::impls::orchestration::context_builders::ids as ctx_ids; -use crate::tools::names as tool_names; - -pub const WORK_ITEM_MANAGER_AGENT_ID: &str = "builtin:work-item-manager"; - -pub fn work_item_manager_agent() -> AgentDefinition { - let capabilities = CapabilitySet { - coding: Some(CodingCapability { mode_switch: false }), - desktop: None, - browser: Some(BrowserCapability { - external: true, - internal: false, - }), - gateway: None, - data: None, - management: Some(ManagementCapability {}), - }; - - AgentDefinition { - id: WORK_ITEM_MANAGER_AGENT_ID.to_string(), - name: "Work Item Manager".to_string(), - description: Some( - "Plans, researches, creates, links, and updates project or standalone Work Items." - .to_string(), - ), - built_in: true, - tier: AgentTier::Primary, - inherits_from: Some(super::BASE_AGENT_ID.to_string()), - capabilities: Some(capabilities), - session_model: Some(SessionModel { - mode: SessionMode::PerSession, - compaction: Some(CompactionConfig { - enabled: true, - keep_ratio: 0.5, - ..CompactionConfig::default() - }), - processing_lock: true, - max_iterations: 500, - }), - agent_policy: Some(AgentPolicy { - autonomy: AutonomyLevel::Full, - workspace_only: false, - ..Default::default() - }), - tools: AgentToolSelection { - system_restrict_to_tools: Some(vec![ - tool_names::READ_FILE.to_string(), - tool_names::LIST_DIR.to_string(), - tool_names::CODE_SEARCH.to_string(), - tool_names::MANAGE_WORKSPACE.to_string(), - tool_names::WEB_SEARCH.to_string(), - tool_names::WEB_FETCH.to_string(), - tool_names::MANAGE_PROJECT.to_string(), - tool_names::MANAGE_WORK_ITEM.to_string(), - tool_names::ASK_USER_QUESTIONS.to_string(), - ]), - ..Default::default() - }, - soul_content: Some(include_str!("prompts/work_item_manager.md").to_string()), - sovereign_prompt: false, - auto_continue: false, - delegation_config: Some(DelegationConfig { - delegatable: true, - context_builders: vec![ - ctx_ids::CODE_ACCOUNTS.to_string(), - ctx_ids::ENVIRONMENT.to_string(), - ], - }), - context_window: None, - max_tokens: None, - temperature: Some(0.0), - sub_agents: Some(vec![]), - load_workspace_resources: None, - load_workspace_rules: None, - skills_config: None, - selected_account_id: None, - selected_model_id: None, - icon_id: Some("layout-list".to_string()), - animate: None, - execution_mode: None, - exec_timeout: None, - max_tool_use_concurrency: None, - learnings: Some(AgentLearningsConfig { - enabled: true, - extract_memories_enabled: true, - auto_dream_enabled: false, - }), - reliability: None, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn work_item_manager_is_research_plus_work_item_only() { - let agent = work_item_manager_agent(); - let tools = agent - .tools - .system_restrict_to_tools - .expect("Work Item Manager pins a system allowlist"); - for expected in [ - tool_names::READ_FILE, - tool_names::LIST_DIR, - tool_names::CODE_SEARCH, - tool_names::WEB_SEARCH, - tool_names::WEB_FETCH, - tool_names::MANAGE_PROJECT, - tool_names::MANAGE_WORK_ITEM, - ] { - assert!( - tools.iter().any(|tool| tool == expected), - "missing {expected}" - ); - } - for forbidden in [ - tool_names::EDIT_FILE, - tool_names::APPLY_PATCH, - tool_names::RUN_SHELL, - tool_names::MANAGE_AGENT_DEF, - tool_names::CONTROL_DESKTOP_WITH_PEEKABOO, - ] { - assert!( - !tools.iter().any(|tool| tool == forbidden), - "Work Item Manager should not pin mutating/non-work-item tool {forbidden}" - ); - } - } -} diff --git a/src-tauri/crates/agent-core/src/core/definitions/mod.rs b/src-tauri/crates/agent-core/src/core/definitions/mod.rs index 28e1e024a..feacc338b 100644 --- a/src-tauri/crates/agent-core/src/core/definitions/mod.rs +++ b/src-tauri/crates/agent-core/src/core/definitions/mod.rs @@ -62,7 +62,7 @@ pub use store::{definitions_store, set_definitions_changed_hook, AgentDefinition // subagents, base) are consumed via the deeper `definitions::builtin::*` // path, so we deliberately don't flatten the whole `builtin::*` set. pub use builtin::{ - ai_research_agent, os_agent, sde_agent, wingman_agent, work_item_manager_agent, - AI_RESEARCH_AGENT_ID, OS_AGENT_ID, SDE_AGENT_ID, WORK_ITEM_MANAGER_AGENT_ID, + ai_research_agent, os_agent, sde_agent, wingman_agent, AI_RESEARCH_AGENT_ID, OS_AGENT_ID, + SDE_AGENT_ID, }; pub use capabilities::CapabilitySet; diff --git a/src-tauri/crates/agent-core/src/core/definitions/tests_extended.rs b/src-tauri/crates/agent-core/src/core/definitions/tests_extended.rs index 5dca0dc10..72aa021b9 100644 --- a/src-tauri/crates/agent-core/src/core/definitions/tests_extended.rs +++ b/src-tauri/crates/agent-core/src/core/definitions/tests_extended.rs @@ -257,10 +257,11 @@ mod tests_extended { #[test] fn get_builtin_agents_count_matches_registry() { // ADE Manager, base, os, sde, ds, ai-research, wingman, - // work-item-manager, explore, general, memory-extractor, - // memory-consolidator (gui-control merged into ADE Manager) + // explore, general, memory-extractor, memory-consolidator + // (gui-control merged into ADE Manager; work-item-manager retired — + // manage_work_item/manage_project are ordinary tools on OS Agent) let agents = get_builtin_agents(); - assert_eq!(agents.len(), 12); + assert_eq!(agents.len(), 11); } // ========================================================================= diff --git a/src-tauri/crates/agent-core/src/core/session/turn/processor/prompt.rs b/src-tauri/crates/agent-core/src/core/session/turn/processor/prompt.rs index 41e50269d..20e180738 100644 --- a/src-tauri/crates/agent-core/src/core/session/turn/processor/prompt.rs +++ b/src-tauri/crates/agent-core/src/core/session/turn/processor/prompt.rs @@ -187,23 +187,24 @@ impl UnifiedMessageProcessor { } // The ChatPanel "Create with AI" flow persists a draft before launch - // so the planning session has a durable Work Item target. The generic - // session runtime carries that linkage, but it was not previously - // visible to Work Item Manager; the model could therefore create a - // second item and strand the original "AI Work Item Draft". Keep this - // volatile (session-specific) and narrowly scoped to the manager. - if self.runtime.agent_definition_id.as_deref() - == Some(crate::core::definitions::WORK_ITEM_MANAGER_AGENT_ID) + // so the planning session has a durable Work Item target. Any agent + // launched through that flow (session agent_role "custom") needs the + // linkage in its prompt, or the model can create a second item and + // strand the original "AI Work Item Draft". Orchestrator-launched + // sessions carry work item context in their launch prompt and are + // excluded here. Keep this volatile (session-specific). { let linked_session = tokio::task::block_in_place(|| super::unified_persistence::get_session(session_id)); match linked_session { Ok(Some(session)) => { - if let Some(work_item_id) = session.work_item_id.as_deref() { - dynamic_sections.push(render_linked_work_item_context( - work_item_id, - session.project_slug.as_deref(), - )); + if session.agent_role.as_deref() == Some("custom") { + if let Some(work_item_id) = session.work_item_id.as_deref() { + dynamic_sections.push(render_linked_work_item_context( + work_item_id, + session.project_slug.as_deref(), + )); + } } } Ok(None) => {} diff --git a/src-tauri/crates/agent-core/src/specialization/policies/behavior.rs b/src-tauri/crates/agent-core/src/specialization/policies/behavior.rs deleted file mode 100644 index 9d52f735e..000000000 --- a/src-tauri/crates/agent-core/src/specialization/policies/behavior.rs +++ /dev/null @@ -1,227 +0,0 @@ -//! Behavior companion `.md` generation. -//! -//! When an ATC behavior is created/updated/removed, a companion markdown file -//! is generated in the global policies directory so other agents can see it. - -use crate::automation::types::{AutomationAction, AutomationRule, AutomationTrigger, GitEvent}; - -use crate::tool_infra::slugify; - -use super::config::{PoliciesConfig, PolicyConfig}; -use super::{global_policies_dir, BEHAVIOR_PREFIX}; - -fn truncate_preview(s: &str, max_bytes: usize) -> String { - if s.len() <= max_bytes { - return s.to_string(); - } - let mut end = max_bytes; - while end > 0 && !s.is_char_boundary(end) { - end -= 1; - } - format!("{}...", &s[..end]) -} - -fn trigger_summary(trigger: &AutomationTrigger) -> String { - match trigger { - AutomationTrigger::Timer { interval_secs } => { - format!("Timer: every {} seconds", interval_secs) - } - AutomationTrigger::ScheduledTime { - frequency, - time, - timezone, - days_of_week, - monthly_mode, - day_of_month, - week_of_month, - weekday_of_month, - } => format!( - "Scheduled time: {:?} at {} {} (days: {:?}, monthly_mode: {:?}, day_of_month: {:?}, week: {:?}, weekday: {:?})", - frequency, - time, - timezone, - days_of_week, - monthly_mode, - day_of_month, - week_of_month, - weekday_of_month - ), - AutomationTrigger::Cron { expression } => format!("Cron: {}", expression), - AutomationTrigger::GitActivity { - events, - repo_filter, - } => { - let event_names: Vec<&str> = events - .iter() - .map(|e| match e { - GitEvent::Commit => "commit", - GitEvent::Push => "push", - GitEvent::Pull => "pull", - GitEvent::BranchChange => "branch change", - GitEvent::FileChange => "file change", - }) - .collect(); - let base = format!("Git: {}", event_names.join(", ")); - match repo_filter { - Some(filter) => format!("{} (repo: {})", base, filter), - None => base, - } - } - AutomationTrigger::ChannelMessage { channel, pattern } => { - let base = format!("Channel message on '{}'", channel); - match pattern { - Some(pat) => format!("{} matching '{}'", base, pat), - None => base, - } - } - AutomationTrigger::FileWatch { paths, debounce_ms } => { - format!( - "File watch: {} (debounce {}ms)", - paths.join(", "), - debounce_ms - ) - } - AutomationTrigger::Webhook { route } => format!("Webhook: {}", route), - } -} - -fn action_summary(action: &AutomationAction) -> String { - match action { - AutomationAction::InjectPrompt { prompt, session_id } => { - let target = session_id.as_deref().unwrap_or("active session"); - let preview = truncate_preview(prompt, 120); - format!("Inject prompt into {}: \"{}\"", target, preview) - } - AutomationAction::StartSession { - agent_type, - prompt, - model, - .. - } => { - let model_str = model.as_deref().unwrap_or("default"); - let preview = truncate_preview(prompt, 80); - format!( - "Start {} session (model: {}): \"{}\"", - agent_type, model_str, preview - ) - } - AutomationAction::KillSession { session_id } => format!("Kill session: {}", session_id), - AutomationAction::SendMessage { channel, content } => { - let preview = truncate_preview(content, 80); - format!("Send to '{}': \"{}\"", channel, preview) - } - AutomationAction::InjectToSession { - session_id, - message, - } => { - let preview = truncate_preview(message, 80); - format!("Inject into session {}: \"{}\"", session_id, preview) - } - AutomationAction::Workflow { actions } => { - format!("Run workflow with {} action(s)", actions.len()) - } - } -} - -/// Generate a companion .md for an ATC behavior in global policies dir. -/// Returns the policy name (filename stem) used for the .md file. -pub fn generate_automation_md(rule: &AutomationRule) -> Result { - let slug = slugify(&rule.name); - let policy_name = format!("{}{}", BEHAVIOR_PREFIX, slug); - let dir = global_policies_dir(); - - if !dir.exists() { - std::fs::create_dir_all(&dir) - .map_err(|e| format!("Failed to create policies dir: {}", e))?; - } - - let trigger_desc = trigger_summary(&rule.trigger); - let action_desc = action_summary(&rule.action); - - let content = format!( - "---\n\ - generated_by: automation\n\ - automation_id: {id}\n\ - trigger: {trigger_type}\n\ - enabled: {enabled}\n\ - ---\n\ - \n\ - # {name}\n\ - \n\ - ## Trigger\n\ - \n\ - {trigger_desc}\n\ - \n\ - ## Action\n\ - \n\ - {action_desc}\n", - id = rule.id, - trigger_type = trigger_desc, - enabled = rule.enabled, - name = rule.name, - trigger_desc = trigger_desc, - action_desc = action_desc, - ); - - let file_path = dir.join(format!("{}.md", policy_name)); - std::fs::write(&file_path, content) - .map_err(|e| format!("Failed to write behavior .md: {}", e))?; - - let mut config = PoliciesConfig::load_global()?; - let entry = config - .policies - .entry(policy_name.clone()) - .or_insert_with(PolicyConfig::default); - entry.disabled = !rule.enabled; - config.save_global()?; - - Ok(policy_name) -} - -/// Remove a behavior .md by its ATC rule ID (scans for matching frontmatter). -pub fn remove_automation_md_by_id(rule_id: &str) -> Result<(), String> { - let dir = global_policies_dir(); - let entries = match std::fs::read_dir(&dir) { - Ok(entries) => entries, - Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(()), - Err(err) => { - return Err(format!( - "Failed to read policies directory {}: {}", - dir.display(), - err - )); - } - }; - - for entry in entries.flatten() { - let path = entry.path(); - if !path.is_file() { - continue; - } - let name = path - .file_stem() - .and_then(|s| s.to_str()) - .unwrap_or("") - .to_string(); - if !name.starts_with(BEHAVIOR_PREFIX) { - continue; - } - if let Ok(content) = std::fs::read_to_string(&path) { - let current_id_pattern = format!("automation_id: {}", rule_id); - let old_id_pattern = format!("behavior_id: {}", rule_id); - if content.contains(¤t_id_pattern) || content.contains(&old_id_pattern) { - std::fs::remove_file(&path) - .map_err(|e| format!("Failed to remove {}: {}", path.display(), e))?; - let mut config = PoliciesConfig::load_global()?; - config.policies.remove(&name); - config.save_global()?; - return Ok(()); - } - } - } - Ok(()) -} - -#[cfg(test)] -#[path = "tests/behavior_tests.rs"] -mod tests; diff --git a/src-tauri/crates/agent-core/src/specialization/policies/mod.rs b/src-tauri/crates/agent-core/src/specialization/policies/mod.rs index d14cf7925..052417608 100644 --- a/src-tauri/crates/agent-core/src/specialization/policies/mod.rs +++ b/src-tauri/crates/agent-core/src/specialization/policies/mod.rs @@ -15,12 +15,10 @@ //! Users can import rules from `.cursor/rules/*.mdc` (workspace-scoped only). pub(crate) mod activation; -mod behavior; mod commands; pub mod config; pub(crate) mod metadata; -pub use behavior::{generate_automation_md, remove_automation_md_by_id}; // Wildcard re-export needed: #[tauri::command] generates hidden __cmd__* items pub use commands::*; diff --git a/src-tauri/crates/agent-core/src/specialization/policies/tests/behavior_tests.rs b/src-tauri/crates/agent-core/src/specialization/policies/tests/behavior_tests.rs deleted file mode 100644 index 73aa6fc1f..000000000 --- a/src-tauri/crates/agent-core/src/specialization/policies/tests/behavior_tests.rs +++ /dev/null @@ -1,133 +0,0 @@ -use crate::automation::types::{AutomationAction, AutomationTrigger, GitEvent}; - -// -- trigger_summary (private) -- - -#[test] -fn trigger_summary_timer() { - let trigger = AutomationTrigger::Timer { interval_secs: 60 }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("Timer")); - assert!(out.contains("60")); -} - -#[test] -fn trigger_summary_cron() { - let trigger = AutomationTrigger::Cron { - expression: "0 * * * *".to_string(), - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("Cron")); - assert!(out.contains("0 * * * *")); -} - -#[test] -fn trigger_summary_git_activity() { - let trigger = AutomationTrigger::GitActivity { - events: vec![GitEvent::Commit, GitEvent::Push], - repo_filter: None, - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("Git")); - assert!(out.contains("commit")); - assert!(out.contains("push")); -} - -#[test] -fn trigger_summary_git_activity_with_repo_filter() { - let trigger = AutomationTrigger::GitActivity { - events: vec![GitEvent::Commit], - repo_filter: Some("/path/to/repo".to_string()), - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("repo:")); -} - -#[test] -fn trigger_summary_channel_message() { - let trigger = AutomationTrigger::ChannelMessage { - channel: "alerts".to_string(), - pattern: Some("error.*".to_string()), - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("alerts")); - assert!(out.contains("error.*")); -} - -#[test] -fn trigger_summary_channel_message_without_pattern() { - let trigger = AutomationTrigger::ChannelMessage { - channel: "alerts".to_string(), - pattern: None, - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("alerts")); - assert!(!out.contains("matching")); -} - -#[test] -fn trigger_summary_file_watch() { - let trigger = AutomationTrigger::FileWatch { - paths: vec!["src/".to_string()], - debounce_ms: 1000, - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("src/")); - assert!(out.contains("1000")); -} - -#[test] -fn trigger_summary_webhook() { - let trigger = AutomationTrigger::Webhook { - route: "/api/hook".to_string(), - }; - let out = super::trigger_summary(&trigger); - assert!(out.contains("/api/hook")); -} -// -- action_summary (private) -- - -#[test] -fn action_summary_inject_prompt_active_session() { - let action = AutomationAction::InjectPrompt { - prompt: "fix bug".to_string(), - session_id: None, - }; - let out = super::action_summary(&action); - assert!(out.contains("active session")); - assert!(out.contains("fix bug")); -} - -#[test] -fn action_summary_inject_prompt_with_session_id() { - let action = AutomationAction::InjectPrompt { - prompt: "fix bug".to_string(), - session_id: Some("sess-123".to_string()), - }; - let out = super::action_summary(&action); - assert!(out.contains("sess-123")); -} - -#[test] -fn action_summary_inject_prompt_long_truncated() { - let long_prompt = "a".repeat(150); - let action = AutomationAction::InjectPrompt { - prompt: long_prompt.clone(), - session_id: None, - }; - let out = super::action_summary(&action); - assert!(out.len() < 150 + 50); - assert!(out.contains("...")); -} - -#[test] -fn action_summary_start_session() { - let action = AutomationAction::StartSession { - agent_type: "sde".to_string(), - prompt: "do thing".to_string(), - model: None, - repo_path: None, - }; - let out = super::action_summary(&action); - assert!(out.contains("sde")); - assert!(out.contains("default")); - assert!(out.contains("do thing")); -} diff --git a/src-tauri/crates/agent-core/src/state/commands/automation.rs b/src-tauri/crates/agent-core/src/state/commands/automation.rs deleted file mode 100644 index e1005aa2f..000000000 --- a/src-tauri/crates/agent-core/src/state/commands/automation.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! Automation Tauri commands. - -use crate::automation; -use crate::state::AgentAppState; - -#[tauri::command] -pub async fn agent_automation_list_rules( - state: tauri::State<'_, AgentAppState>, -) -> Result, String> { - let engine_lock = state.gateway.automation_engine.lock().await; - match *engine_lock { - Some(ref engine) => Ok(engine.list_rules().await), - None => { - automation::persistence::load_rules(&automation::persistence::default_storage_path()) - } - } -} - -#[tauri::command] -pub async fn agent_automation_add_rule( - state: tauri::State<'_, AgentAppState>, - rule_json: String, -) -> Result { - let rule: automation::AutomationRule = - serde_json::from_str(&rule_json).map_err(|err| format!("Invalid rule JSON: {}", err))?; - - crate::policies::generate_automation_md(&rule)?; - - let mut engine_lock = state.gateway.automation_engine.lock().await; - match *engine_lock { - Some(ref mut engine) => Ok(engine.add_rule(rule).await), - None => { - let storage_path = automation::persistence::default_storage_path(); - let mut rules = automation::persistence::load_rules(&storage_path)?; - let rule_id = rule.id.clone(); - if let Some(existing) = rules - .iter_mut() - .find(|existing_rule| existing_rule.id == rule_id) - { - *existing = rule; - } else { - rules.push(rule); - } - automation::persistence::save_rules(&storage_path, &rules)?; - Ok(rule_id) - } - } -} - -#[tauri::command] -pub async fn agent_automation_update_rule( - state: tauri::State<'_, AgentAppState>, - rule_json: String, -) -> Result<(), String> { - let rule: automation::AutomationRule = - serde_json::from_str(&rule_json).map_err(|err| format!("Invalid rule JSON: {}", err))?; - - crate::policies::generate_automation_md(&rule)?; - - let mut engine_lock = state.gateway.automation_engine.lock().await; - match *engine_lock { - Some(ref mut engine) => engine.update_rule(rule).await, - None => { - let storage_path = automation::persistence::default_storage_path(); - let mut rules = automation::persistence::load_rules(&storage_path)?; - let rule_id = rule.id.clone(); - if let Some(existing) = rules.iter_mut().find(|r| r.id == rule_id) { - *existing = rule; - } else { - return Err(format!("Rule not found: {}", rule_id)); - } - automation::persistence::save_rules(&storage_path, &rules) - } - } -} - -#[tauri::command] -pub async fn agent_automation_remove_rule( - state: tauri::State<'_, AgentAppState>, - rule_id: String, -) -> Result { - crate::policies::remove_automation_md_by_id(&rule_id)?; - - let mut engine_lock = state.gateway.automation_engine.lock().await; - match *engine_lock { - Some(ref mut engine) => Ok(engine.remove_rule(&rule_id).await), - None => { - let storage_path = automation::persistence::default_storage_path(); - let mut rules = automation::persistence::load_rules(&storage_path)?; - let original_len = rules.len(); - rules.retain(|r| r.id != rule_id); - let removed = rules.len() < original_len; - if removed { - automation::persistence::save_rules(&storage_path, &rules)?; - } - Ok(removed) - } - } -} - -#[tauri::command] -pub async fn agent_automation_get_status( - state: tauri::State<'_, AgentAppState>, -) -> Result { - let engine_lock = state.gateway.automation_engine.lock().await; - match *engine_lock { - Some(ref engine) => Ok(engine.status().await), - None => Ok(automation::AutomationStatus { - running: false, - active_rules: 0, - total_rules: 0, - total_fires: 0, - uptime_secs: 0, - agent_alive: false, - messages_processed: 0, - last_health_check: String::new(), - }), - } -} - -#[tauri::command] -pub async fn agent_automation_fire_webhook(route: String) -> Result { - Ok(automation::triggers::webhook_registry::fire(&route)) -} diff --git a/src-tauri/crates/agent-core/src/state/commands/mod.rs b/src-tauri/crates/agent-core/src/state/commands/mod.rs index f44c09177..78d3b3283 100644 --- a/src-tauri/crates/agent-core/src/state/commands/mod.rs +++ b/src-tauri/crates/agent-core/src/state/commands/mod.rs @@ -1,6 +1,5 @@ //! Agent Tauri commands. -pub mod automation; pub mod channel_handler; pub mod desktop; pub mod routines; diff --git a/src-tauri/crates/orgtrack-cli/README.md b/src-tauri/crates/orgtrack-cli/README.md index e1008785b..bc3c9f000 100644 --- a/src-tauri/crates/orgtrack-cli/README.md +++ b/src-tauri/crates/orgtrack-cli/README.md @@ -232,6 +232,6 @@ is mechanical. The path to an independent publish: and ship prebuilt binaries (the existing `.goreleaser`-style release tooling in the repo can cross-compile a static, CGO-free binary thanks to bundled SQLite). -3. The `@orgii/orgtrack` npm package (`packages/orgtrack`) can then become the - Node distribution wrapper that downloads/execs this binary, replacing its - current stub entrypoint. +3. An npm distribution wrapper that downloads/execs this binary can be added + at that point (the old `packages/orgtrack` stub was removed — recreate it + only when there is a real binary to wrap). diff --git a/src-tauri/src/commands/handler_list.inc b/src-tauri/src/commands/handler_list.inc index e00bdc350..968fa7b31 100644 --- a/src-tauri/src/commands/handler_list.inc +++ b/src-tauri/src/commands/handler_list.inc @@ -939,13 +939,6 @@ agent_core::state::commands::session::org_tasks::agent_org_send_group_chat_messa agent_core::state::commands::session::org_tasks::agent_org_send_user_message_to_member, agent_core::state::commands::session::org_tasks::agent_org_pause_run, agent_core::state::commands::session::org_tasks::agent_org_resume_run, -// Automation (trigger->action rules) -agent_core::state::commands::automation::agent_automation_list_rules, -agent_core::state::commands::automation::agent_automation_add_rule, -agent_core::state::commands::automation::agent_automation_update_rule, -agent_core::state::commands::automation::agent_automation_remove_rule, -agent_core::state::commands::automation::agent_automation_get_status, -agent_core::state::commands::automation::agent_automation_fire_webhook, agent_core::specialization::policies::policies_list, agent_core::specialization::policies::policies_read, agent_core::specialization::policies::policies_create, diff --git a/src/api/tauri/agent/automation.ts b/src/api/tauri/agent/automation.ts index 2467ee993..5b502a787 100644 --- a/src/api/tauri/agent/automation.ts +++ b/src/api/tauri/agent/automation.ts @@ -1,12 +1,14 @@ /** - * Agent Automation API + * Agent desktop-permission and desktop-config API. * - * Desktop permissions, automation rules, and webhook triggers. + * The automation-rule wrappers that used to live here (list/add/update/ + * remove rule, engine status, webhook fire) were removed in Phase 1 of the + * Orgtrack PM protocol migration — they had no production caller and their + * TS payload shape was incompatible with the Rust `AutomationRule` schema. */ -import type { AutomationStatus } from "@src/modules/MainApp/Integrations/RulesMemoryEvolution/types"; import { invokeTauri } from "@src/util/platform/tauri/init"; -import type { AutomationRule, DesktopPermission } from "./types"; +import type { DesktopPermission } from "./types"; export async function checkDesktopPermissions(): Promise { return invokeTauri("agent_check_desktop_permissions"); @@ -21,38 +23,6 @@ export async function requestDesktopPermissions( ); } -export async function listAutomationRules(): Promise { - return invokeTauri("agent_automation_list_rules"); -} - -export async function getAutomationStatus(): Promise { - return invokeTauri("agent_automation_get_status"); -} - -export async function addAutomationRule( - rule: Omit -): Promise { - const ruleJson = JSON.stringify(rule); - return invokeTauri("agent_automation_add_rule", { ruleJson }); -} - -export async function updateAutomationRule( - rule: AutomationRule -): Promise { - const ruleJson = JSON.stringify(rule); - return invokeTauri("agent_automation_update_rule", { ruleJson }); -} - -export async function removeAutomationRule(ruleId: string): Promise { - return invokeTauri("agent_automation_remove_rule", { ruleId }); -} - -export async function fireAutomationWebhook(route: string): Promise { - return invokeTauri("agent_automation_fire_webhook", { - route, - }); -} - // ── Desktop sub-gates ─────────────────────────────────────────────── export interface DesktopConfig { diff --git a/src/app/root/E2EBootstrap.tsx b/src/app/root/E2EBootstrap.tsx index f820fd0e0..d6563a45b 100644 --- a/src/app/root/E2EBootstrap.tsx +++ b/src/app/root/E2EBootstrap.tsx @@ -118,9 +118,6 @@ export const E2EBootstrap: FC = () => { getSettingsRegistryKeys, getDesktopConfig, setDesktopConfig, - listAutomationRules, - addAutomationRule, - removeAutomationRule, } = createConfigHelpers(); const refreshAgentDefs = async (): Promise> => { @@ -411,9 +408,6 @@ export const E2EBootstrap: FC = () => { runWorkItemSchedulerOnce, getDesktopConfig, setDesktopConfig, - listAutomationRules, - addAutomationRule, - removeAutomationRule, listPolicies, createPolicy, readPolicy, diff --git a/src/app/root/e2e/helpers/config.ts b/src/app/root/e2e/helpers/config.ts index 3ea534846..21b951f49 100644 --- a/src/app/root/e2e/helpers/config.ts +++ b/src/app/root/e2e/helpers/config.ts @@ -227,41 +227,6 @@ export function createConfigHelpers() { } }; - const listAutomationRules = async (): Promise> => { - try { - const rules = (await invoke("agent_automation_list_rules")) as Json[]; - return { ok: true, rules }; - } catch (err) { - return asError(err); - } - }; - - const addAutomationRule = async ( - ruleJson: string - ): Promise> => { - try { - const ruleId = (await invoke("agent_automation_add_rule", { - ruleJson, - })) as string; - return { ok: true, ruleId }; - } catch (err) { - return asError(err); - } - }; - - const removeAutomationRule = async ( - ruleId: string - ): Promise> => { - try { - const removed = (await invoke("agent_automation_remove_rule", { - ruleId, - })) as boolean; - return { ok: true, removed }; - } catch (err) { - return asError(err); - } - }; - return { getAgentDef, updateAgentDefPatch, @@ -280,8 +245,5 @@ export function createConfigHelpers() { getSettingsRegistryKeys, getDesktopConfig, setDesktopConfig, - listAutomationRules, - addAutomationRule, - removeAutomationRule, }; } diff --git a/src/app/root/e2e/types.ts b/src/app/root/e2e/types.ts index f16f6b095..7a1d43dac 100644 --- a/src/app/root/e2e/types.ts +++ b/src/app/root/e2e/types.ts @@ -346,11 +346,6 @@ export interface E2EHelpers { >; getDesktopConfig: () => Promise>; setDesktopConfig: (config: Json) => Promise<{ ok: true } | Err>; - listAutomationRules: () => Promise>; - addAutomationRule: (ruleJson: string) => Promise>; - removeAutomationRule: ( - ruleId: string - ) => Promise>; listPolicies: ( workspacePath?: string ) => Promise>; diff --git a/src/engines/ChatPanel/hooks/useAiWorkItemCreator.ts b/src/engines/ChatPanel/hooks/useAiWorkItemCreator.ts index 0ae55ffa1..6d7c1cdd7 100644 --- a/src/engines/ChatPanel/hooks/useAiWorkItemCreator.ts +++ b/src/engines/ChatPanel/hooks/useAiWorkItemCreator.ts @@ -30,7 +30,9 @@ import { import type { WorkItemDraft } from "@src/store/workstation/projectManager"; import { getDispatchCategory } from "@src/util/session/sessionDispatch"; -const WORK_ITEM_DEFAULT_AGENT_DEF_ID = "builtin:work-item-manager"; +// Work Item Manager persona was retired; the generic OS Agent carries +// manage_work_item/manage_project as ordinary built-in tools. +const WORK_ITEM_DEFAULT_AGENT_DEF_ID = "builtin:os"; const AI_WORK_ITEM_DEFAULT_TITLE = "AI Work Item Draft"; interface AiWorkItemLaunchMetadata { diff --git a/src/features/SessionCreator/variants/ChatPanel/resolveSessionCreatorAgentHero.ts b/src/features/SessionCreator/variants/ChatPanel/resolveSessionCreatorAgentHero.ts index 25805bd08..a4fc30ed4 100644 --- a/src/features/SessionCreator/variants/ChatPanel/resolveSessionCreatorAgentHero.ts +++ b/src/features/SessionCreator/variants/ChatPanel/resolveSessionCreatorAgentHero.ts @@ -34,8 +34,6 @@ const BUILTIN_HERO_DESCRIPTIONS: Record = { "Your always-on assistant for workspace files, shell, and desktop tasks", "builtin:sde": "Plans, writes, and ships code in your repo with full tool access", - "builtin:work-item-manager": - "Researches, drafts, links, and updates Work Items across projects", "builtin:wingman": "Watches your screen and assists in real time while you work", "builtin:gateway": diff --git a/src/modules/MainApp/AgentOrgs/EditPanel/index.tsx b/src/modules/MainApp/AgentOrgs/EditPanel/index.tsx deleted file mode 100644 index 187335f7d..000000000 --- a/src/modules/MainApp/AgentOrgs/EditPanel/index.tsx +++ /dev/null @@ -1,587 +0,0 @@ -/** - * EditPanel Component - * - * Side panel for workflow editing with variants: - * 1. Nothing selected: Show placeholder - * 2. Adding: Select action to add to workflow - * 3. Editing: Edit properties of selected action - */ -import cn from "classnames"; -import { MousePointerClick, Plus, Search, X } from "lucide-react"; -import React, { useCallback, useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import Checkbox from "@src/components/Checkbox"; -import Input from "@src/components/Input"; -import Select from "@src/components/Select"; -import Textarea from "@src/components/Textarea"; -import { SECTION_GAP_CLASSES } from "@src/modules/shared/layouts/SectionLayout"; -import { - Placeholder, - ScrollFadeContainer, -} from "@src/modules/shared/layouts/blocks"; - -import type { SpotlightData } from "../components/CommandCard/types"; -import { - type ActionDefinition, - SESSION_STAGE_OPTIONS, - availableActions, -} from "../data"; -import { - useWorkflowAgentOptions, - useWorkflowModelOptions, -} from "../hooks/useWorkflowModelOptions"; -import { renderActionIcon } from "../iconHelper"; -import { - translateActionDescription, - translateActionTitle, - translateInputLabel, - translateInputPlaceholder, - translateInputUnit, - translateOptionLabel, -} from "../utils/translateAction"; - -export type EditPanelVariant = "nothing" | "adding" | "editing"; - -/** Map the canonical English category to its i18n key suffix. */ -function categoryKeySuffix(category: string): string { - switch (category) { - case "Controls": - return "controls"; - case "Session Workflow": - return "sessionWorkflow"; - case "Actions": - return "actions"; - default: - return category.toLowerCase().replace(/\s+/g, ""); - } -} - -interface EditPanelProps { - variant: EditPanelVariant; - onAddAction?: (action: ActionDefinition) => void; - onClose?: () => void; - insertIndex?: number | null; - // For editing variant - selectedInstanceId?: string | null; - selectedInstance?: unknown; - selectedDefinition?: ActionDefinition | null; - onUpdateAction?: ( - instanceId: string, - newData: Record - ) => void; - // For validation - parentBranchType?: "if-true" | "if-false" | "loop-body"; - // For dynamic select inputs - spotlightData?: SpotlightData; -} - -const EditPanel: React.FC = ({ - variant, - onAddAction, - onClose, - insertIndex, - selectedInstanceId, - selectedInstance, - selectedDefinition, - onUpdateAction, - parentBranchType, - spotlightData, -}) => { - const { t } = useTranslation("settings"); - const { t: tIntegrations } = useTranslation("integrations"); - const [search, setSearch] = useState(""); - const [activeCategory, setActiveCategory] = useState("All"); - const modelOptions = useWorkflowModelOptions(); - const agentOptions = useWorkflowAgentOptions(); - - // Categories are keyed by the canonical (English) `category` field so - // filter logic stays locale-independent; the visible label is - // translated via `categoryKey` at render time. - const categories = useMemo(() => { - const cats = new Set(availableActions.map((action) => action.category)); - return ["All", ...Array.from(cats)]; - }, []); - - const filteredActions = useMemo(() => { - const search_lc = search.toLowerCase(); - return availableActions.filter((action) => { - const title = translateActionTitle(tIntegrations, action).toLowerCase(); - const description = - translateActionDescription(tIntegrations, action)?.toLowerCase() ?? ""; - const matchesSearch = - title.includes(search_lc) || description.includes(search_lc); - const matchesCategory = - activeCategory === "All" || action.category === activeCategory; - - // Validation: Do NOT allow loops within loops - const isInsideLoop = parentBranchType === "loop-body"; - const isLoopAction = action.type === "loop"; - if (isInsideLoop && isLoopAction) { - return false; // Filter out loop action when inside a loop - } - - return matchesSearch && matchesCategory; - }); - }, [search, activeCategory, parentBranchType, tIntegrations]); - - // Group actions by category - const groupedActions = useMemo(() => { - const groups: Record = {}; - filteredActions.forEach((action) => { - if (!groups[action.category]) { - groups[action.category] = []; - } - groups[action.category].push(action); - }); - return groups; - }, [filteredActions]); - - const handleActionClick = useCallback( - (action: ActionDefinition) => { - if (variant === "adding") { - onAddAction?.(action); - } - }, - [variant, onAddAction] - ); - - // Render header based on variant - const renderHeader = () => { - if (variant === "adding") { - return ( -
-

- {t("editPanel.addAction")} - {insertIndex !== null && insertIndex !== undefined && ( - - {t("editPanel.atPosition", { position: insertIndex + 1 })} - - )} -

-
- ); - } - - if (variant === "editing") { - const editingTitle = selectedDefinition - ? translateActionTitle(tIntegrations, selectedDefinition) - : ""; - const editingDescription = selectedDefinition - ? translateActionDescription(tIntegrations, selectedDefinition) - : ""; - return ( -
-
-

- {editingTitle || t("editPanel.editAction")} -

-

- {editingDescription} -

-
- -
- ); - } - - // For "nothing" variant, still show a section title - return ( -
- - {t("editPanel.actionsTitle")} - -
- ); - }; - - // Handle property value change for editing variant - const handlePropertyChange = useCallback( - (inputIndex: number, value: unknown) => { - if (!selectedInstanceId || !selectedInstance || !onUpdateAction) return; - - const currentData = selectedInstance as Record; - const updatedData = { - ...currentData, - [inputIndex]: value, - }; - onUpdateAction(selectedInstanceId, updatedData); - }, - [selectedInstanceId, selectedInstance, onUpdateAction] - ); - - // Render content based on variant - const renderContent = () => { - // "nothing" variant - show placeholder - if (variant === "nothing") { - return ( -
-
- -
-

- {t("editPanel.clickToAdd")} -

-
- ); - } - - // "editing" variant - show action properties - if (variant === "editing") { - if (!selectedDefinition || !selectedInstance) { - return ( - - ); - } - - const instanceData = selectedInstance as Record; - - return ( - - {/* Action Icon and Category */} -
-
- {renderActionIcon(selectedDefinition.icon, { - size: 18, - className: "text-text-1", - })} -
-
-
- {selectedDefinition.category} -
-
-
- - {/* Properties Section */} - {selectedDefinition.inputs && selectedDefinition.inputs.length > 0 ? ( - <> -
- {t("editPanel.propertiesTitle")} -
-
- {selectedDefinition.inputs.map((input, index) => { - const currentValue = - instanceData[index] ?? input.defaultValue; - const inputLabel = translateInputLabel(tIntegrations, input); - const inputPlaceholder = translateInputPlaceholder( - tIntegrations, - input - ); - const inputUnit = translateInputUnit(tIntegrations, input); - - return ( -
- {/* Label */} - {inputLabel && ( - - )} - - {/* Input based on type */} - {input.type === "text" || input.type === "command" ? ( - - handlePropertyChange(index, newValue) - } - placeholder={inputPlaceholder} - size="default" - /> - ) : input.type === "prompt" ? ( -