diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 60c8949..37b27bd 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "2718lab-devkit", - "version": "1.1.4", + "version": "1.1.5", "description": "Local MCP server for developer workflow coordination, indexing, and evidence handling.", "author": { "name": "2718lab", diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index a94ae82..9e1de5c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -28,7 +28,7 @@ body: id: version attributes: label: DevKit version - placeholder: "v1.1.4" + placeholder: "v1.1.5" validations: required: true diff --git a/CHANGELOG.md b/CHANGELOG.md index ccf7473..1213f69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,34 @@ only after the CI and artifact checks pass. ## [Unreleased] +## [1.1.5] - 2026-09-05 + +### Added + +- Added the model-neutral `fast-lane-request-v2` / `work-package-v3` / + `fast-lane-plan-v3` path. New assignments describe complexity, capability, + effort, and cost requirements while the coordinator chooses from the model + IDs and efforts exposed by the current Codex dispatch tool. +- Added public request preparation plus model-selection record helpers. A + selection record binds the exact model ID, effort, reason, + `requirement_hash`, and `plan_item_id` without claiming dispatch or model + availability. + +### Fixed + +- Future bounded model IDs, including an explicitly requested `gpt-6-astra`, + no longer require a DevKit model-name whitelist. Explicit model/effort intent + is hash-bound and cannot be silently replaced by a fallback route. +- Reject mixed request/work-package versions in both directions; request-v1 is + paired with work-package-v2 and request-v2 with work-package-v3. + +### Security + +- Model-neutral plans remain `plan_only`, `not_dispatched`, and unauthorized; + the actual dispatch tool is the only availability gate and worker `ultra` + remains forbidden. Legacy v3/v4/v5 routing policy assets and legacy + request-v1/plan-v2 replay behavior are unchanged. + ## [1.1.4] - 2026-09-04 ### Added diff --git a/README.md b/README.md index 66888db..06e451d 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,27 @@ [简体中文](README.zh-CN.md) -# 2718lab DevKit — Codex + MCP v1.1.4 +# 2718lab DevKit — Codex + MCP v1.1.5 -[![version](https://img.shields.io/badge/version-v1.1.4-blue)](./.codex-plugin/plugin.json) +[![version](https://img.shields.io/badge/version-v1.1.5-blue)](./.codex-plugin/plugin.json) [![license](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE) 2718lab DevKit is a Codex-first engineering toolkit: a local, stdio-only MCP runtime for bounded project indexing, Atlas evidence, Relay lifecycle coordination, and deterministic Fast Lane planning, plus a compact Skill bundle -of reference manuals. This repository carries the versioned v1.1.4 package. +of reference manuals. This repository carries the versioned v1.1.5 package. The checked-in manifest and allowlist define the executable runtime surface; the manual map, install, build, and verification sections below describe the supported workflow. The public Python compiler and CLI retain the deliberately fail-closed -`team-efficiency/fast-lane-plan-v1` preview. The MCP `fastlane_compile` tool -instead resolves caller-supplied workspace/snapshot selectors through its local -RuntimeRoot and returns a verified `team-efficiency/fast-lane-plan-v2` planning -descriptor. That descriptor is `plan_only`: it never dispatches, claims a -lease, creates a worktree, or authorizes execution. +`team-efficiency/fast-lane-plan-v1` preview. For new work, prepare +`fast-lane-request-v2` with `prepare_model_neutral_fast_lane_request`; MCP +`fastlane_compile` then resolves the workspace/snapshot selectors through its +local RuntimeRoot and returns a verified `team-efficiency/fast-lane-plan-v3`. +Its assignments describe requirements and leave model selection to the +coordinator using current Codex tool metadata. Every descriptor remains +`plan_only`: it never dispatches, claims a lease, creates a worktree, or +authorizes execution. Request-v1/plan-v2 stays available for exact legacy replay. The prior v1.1.3 storage-governance integration references compatible Host source only on Ayleovelle's user-fork @@ -61,12 +64,14 @@ and continue to fail closed. - The optional Codex Skill bundle is part of DevKit's documentation surface. It provides short, module-specific manuals without becoming a second runtime or an executable prompt/agent surface. -- Fast Lane is a pure planning compiler. The MCP tool can emit bounded - `team-efficiency/fast-lane-plan-v2` assignments only after its private +- Fast Lane is a pure planning compiler. New model-neutral requests emit bounded + `team-efficiency/fast-lane-plan-v3` assignments only after its private RuntimeRoot verifies a registered `INDEX_READY` snapshot, Git HEAD, and full - write-scope coverage. Every result remains `plan_only`; no agent, Git command, - lease, or worktree action is executed. The public Python compiler/CLI remains - the authority-inert plan-v1 diagnostic surface. + write-scope coverage. Assignments contain no default model name; the + coordinator records an exact model/effort choice before the dispatch tool + validates availability. Every result remains `plan_only`; no agent, Git + command, lease, or worktree action is executed. The public Python compiler/CLI + remains the authority-inert plan-v1 diagnostic surface. ## Module overview @@ -84,8 +89,9 @@ and continue to fail closed. ## Overall workflow The repository workflow defaults to Fast Lane. `workflow-design` prepares a -bounded input. MCP `fastlane_compile` verifies the persisted local project -snapshot and returns a plan-v2 descriptor; `team_efficiency.py` retains the +bounded model-neutral request-v2 input. MCP `fastlane_compile` verifies the +persisted local project snapshot and returns a plan-v3 descriptor; +`team_efficiency.py` retains the blocked plan-v1 diagnostic path. Neither path starts agents or creates/executes cross-session worktrees. A coordinator must separately validate route availability, claim a lease, and dispatch any selected work. @@ -98,7 +104,7 @@ flowchart TD end subgraph FAST["Fast Lane"] F["fast-lane request"] --> G["MCP fastlane_compile
private RuntimeRoot verification"] - G --> X["plan-v2
plan_only, not dispatched"] + G --> X["plan-v3 requirements
unselected, plan_only"] F --> H["public Python / CLI"] --> I["plan-v1
NO_SAFE_WORK"] end B -->|MCP tools| C @@ -179,7 +185,7 @@ source of record remains `main` and immutable release tags. Maintainers build that snapshot with the dedicated marketplace allowlist: - python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.4.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.5.zip ## Install and run locally @@ -226,7 +232,7 @@ handles or falls back to an unrelated local start. The allowlisted builder creates a deterministic ZIP outside the plugin source tree. Choose an output directory outside the source tree: - python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.4.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.5.zip The artifact contains the manifest, .mcp.json, LICENSE, the locked Python project, and the runtime files selected by @@ -337,18 +343,25 @@ cleanup target. The Fast Lane compiler is in mcp-tools/devkit_fastlane/scripts/fastlane_routing.py and mcp-tools/devkit_fastlane/scripts/team_efficiency.py. The MCP `fastlane_compile` -entry returns `team-efficiency/fast-lane-plan-v2` only after a read-only +entry returns `team-efficiency/fast-lane-plan-v3` for a model-neutral request +only after a read-only RuntimeRoot unit of work resolves the registered workspace and snapshot. The snapshot must be current, `INDEX_READY`, bound to the repository Git HEAD, and its persisted `include_paths` must fully cover every writer `write_scope`. - `reasoning_effort` is required and accepts only `low`, `medium`, `high`, `xhigh`, or `max`; the MCP output never dispatches a worker. -- `team-efficiency/fast-lane-plan-v2` fixes `plan_only=true`, +- `team-efficiency/fast-lane-plan-v3` fixes `plan_only=true`, `dispatch_state="not_dispatched"`, and `execution_authorized=false`. - Assignments use `team-efficiency/local-writer-plan-v1`; path-free + Assignments use `team-efficiency/local-writer-plan-v2`, with bounded + `route_requirements` and coordinator-owned `selection.state="unselected"`; + path-free `index_evidence` includes snapshot/binding hashes, `include_paths_hash`, and the compiled `scope_hash`. +- `record_model_selection` binds `plan_item_id`, `requirement_hash`, the exact + model ID and effort, and the coordinator's reason. It is a `selection_only`, + `not_dispatched` record; the dispatch tool remains the availability gate. + An explicit request intent cannot be replaced by a fallback model. - `INDEX_STALE` rejects filesystem or Git drift. `INDEX_PARTIAL` rejects either a partial snapshot or any snapshot whose include roots omit a writer scope. - Caller IDs are selectors, not authority material. The public MCP request and @@ -395,7 +408,7 @@ freeze a transient regression count. ## Version -This repository represents the versioned v1.1.4 package. Release notes are +This repository represents the versioned v1.1.5 package. Release notes are in [CHANGELOG.md](CHANGELOG.md); build and install from the checked-in manifest, artifact allowlist, and locked dependency set. A maintainer dispatches Release from current `main`; it validates all declared gates, creates the annotated tag, diff --git a/README.zh-CN.md b/README.zh-CN.md index be8c255..71f475c 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,21 +1,23 @@ [English](README.md) -# 2718lab DevKit —— Codex + MCP v1.1.4 +# 2718lab DevKit —— Codex + MCP v1.1.5 -[![版本](https://img.shields.io/badge/version-v1.1.4-blue)](./.codex-plugin/plugin.json) +[![版本](https://img.shields.io/badge/version-v1.1.5-blue)](./.codex-plugin/plugin.json) [![许可证](https://img.shields.io/badge/license-AGPL--3.0-blue)](LICENSE) 2718lab DevKit 是一个 Codex-first 工程工具包:它包含一个本地、仅 stdio 传输的 MCP 运行时,用于有边界的项目索引、Atlas 证据、Relay 生命周期协调和 确定性的 Fast Lane 规划;同时还包含一组精简的 Skill 说明书。本仓库承载版本化的 -v1.1.4 包;已提交的 manifest 和 allowlist 定义可执行运行时范围,说明书导航、 +v1.1.5 包;已提交的 manifest 和 allowlist 定义可执行运行时范围,说明书导航、 安装、构建和验证章节共同给出支持的工作流。 公共 Python 编译器和 CLI 继续保留刻意 fail-closed 的 -`team-efficiency/fast-lane-plan-v1` 预览。MCP `fastlane_compile` 则通过本地 -RuntimeRoot 解析 caller 提供的 workspace/snapshot selector,并返回已验证的 -`team-efficiency/fast-lane-plan-v2` 规划描述符。该描述符固定为 `plan_only`:不会 -派发、领取 lease、创建 worktree 或授权执行。 +`team-efficiency/fast-lane-plan-v1` 预览。新任务先用 +`prepare_model_neutral_fast_lane_request` 准备 `fast-lane-request-v2`;MCP +`fastlane_compile` 再通过本地 RuntimeRoot 解析 workspace/snapshot selector,并返回 +已验证的 `team-efficiency/fast-lane-plan-v3`。assignment 只描述需求,由协调器根据 +本次 Codex 工具 metadata 选择模型。描述符仍固定为 `plan_only`:不会派发、领取 +lease、创建 worktree 或授权执行。request-v1/plan-v2 仅保留精确历史重放。 此前 v1.1.3 的存储治理集成只引用 Ayleovelle 用户 fork 上的兼容 Host 源码: [`codex/host-1.1.3-storage-governance-upstream`](https://github.com/Ayleovelle/codex/tree/codex/host-1.1.3-storage-governance-upstream) @@ -47,11 +49,12 @@ stock Codex Host 没有经过证明的 protected broker,继续 fail-closed。 静态 prompt agent 或模型运行器。 - 可选的 Codex Skill bundle 是 DevKit 的说明书表面,提供简短的模块化手册; 它不构成第二个运行时,也不是可执行的 prompt/agent 表面。 -- Fast Lane 是纯规划编译器。只有 server 私有 RuntimeRoot 验证已注册的 +- Fast Lane 是纯规划编译器。新 model-neutral request 只有在 server 私有 RuntimeRoot 验证已注册的 `INDEX_READY` snapshot、Git HEAD 和完整 writer scope 覆盖后,MCP 工具才会 - 产出有界的 `team-efficiency/fast-lane-plan-v2` assignments。所有结果仍为 - `plan_only`,不执行 agent、Git 命令、lease 或 worktree 动作。公共 Python - 编译器/CLI 继续作为无 authority 的 plan-v1 诊断面。 + 产出有界的 `team-efficiency/fast-lane-plan-v3` assignments。assignment 不带 + 默认模型名;协调器先记录 exact model/effort,再由 dispatch tool 校验可用性。 + 所有结果仍为 `plan_only`,不执行 agent、Git 命令、lease 或 worktree 动作。 + 公共 Python 编译器/CLI 继续作为无 authority 的 plan-v1 诊断面。 ## 核心模块速览 @@ -68,8 +71,9 @@ stock Codex Host 没有经过证明的 protected broker,继续 fail-closed。 ## 整体工作流 -仓库级默认工作流是 Fast Lane。`workflow-design` 准备有界输入;MCP -`fastlane_compile` 验证持久化本地项目 snapshot 后返回 plan-v2 描述符, +仓库级默认工作流是 Fast Lane。`workflow-design` 准备有界的 model-neutral +request-v2 输入;MCP `fastlane_compile` 验证持久化本地项目 snapshot 后返回 +plan-v3 描述符, `team_efficiency.py` 则保留被阻断的 plan-v1 诊断路径。两条路径都不会启动 agent, 也不会创建或执行跨会话工作树。协调器必须另行验证 route availability、领取 lease 并派发所选工作。 @@ -82,7 +86,7 @@ flowchart TD end subgraph FAST["Fast Lane"] F["fast-lane request"] --> G["MCP fastlane_compile
私有 RuntimeRoot 验证"] - G --> X["plan-v2
plan_only,未派发"] + G --> X["plan-v3 requirements
未选择,plan_only"] F --> H["公共 Python / CLI"] --> I["plan-v1
NO_SAFE_WORK"] end B -->|MCP 工具| C @@ -157,7 +161,7 @@ Fast Lane 不含额度协调器合同;公共编译器和 CLI 不读取、协 维护者使用专用的 marketplace allowlist 构建该快照: - python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.4.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.5.zip ## 本地安装与运行 @@ -197,7 +201,7 @@ RELAY_CAPABILITY_BROKER_UNAVAILABLE。服务器不会暴露原始 handle,也 allowlist builder 会在插件源码树之外生成确定性的 ZIP。请选择源码树之外的输出目录: - python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.4.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.5.zip 产物包含 manifest、.mcp.json、LICENSE、锁定的 Python 项目,以及 .codex-plugin/main-artifact-allowlist.json 选中的运行时文件。它的可执行运行时 @@ -293,16 +297,21 @@ Fast Lane 编译器位于 mcp-tools/devkit_fastlane/scripts/fastlane_routing.py 和 mcp-tools/devkit_fastlane/scripts/team_efficiency.py。MCP `fastlane_compile` 只有在 只读 RuntimeRoot UoW 解析已注册 workspace/snapshot 后才返回 -`team-efficiency/fast-lane-plan-v2`。snapshot 必须 current、处于 `INDEX_READY`、 +model-neutral request 返回 `team-efficiency/fast-lane-plan-v3`。snapshot 必须 +current、处于 `INDEX_READY`、 绑定仓库 Git HEAD,且持久化 `include_paths` 完整覆盖每个 writer `write_scope`。 - `reasoning_effort` 必填且只接受 `low`、`medium`、`high`、`xhigh` 或 `max`;MCP 输出永不派发 worker。 -- `team-efficiency/fast-lane-plan-v2` 固定 `plan_only=true`、 +- `team-efficiency/fast-lane-plan-v3` 固定 `plan_only=true`、 `dispatch_state="not_dispatched"`、`execution_authorized=false`。 - assignment 使用 `team-efficiency/local-writer-plan-v1`;无路径的 + assignment 使用 `team-efficiency/local-writer-plan-v2`,包含有界 + `route_requirements` 与协调器负责的 `selection.state="unselected"`;无路径的 `index_evidence` 包含 snapshot/binding hashes、`include_paths_hash` 与已编译 `scope_hash`。 +- `record_model_selection` 绑定 `plan_item_id`、`requirement_hash`、exact model ID、 + effort 与协调器选择理由。该记录仍为 `selection_only/not_dispatched`,可用性只由 + dispatch tool 校验;显式请求的模型不得被 fallback 替换。 - filesystem 或 Git 漂移返回 `INDEX_STALE`;snapshot 本身 partial,或其 include roots 遗漏任一 writer scope,均返回 `INDEX_PARTIAL`。 - caller ID 只是 selector,不是 authority material。公共 MCP request 与公共 @@ -342,7 +351,7 @@ CI 和全新产物检查才是当前测试计数的唯一来源。它们验证 ## 版本 -本仓库代表版本化的 v1.1.4 包。发布说明见 +本仓库代表版本化的 v1.1.5 包。发布说明见 [CHANGELOG.md](CHANGELOG.md);构建和安装请以已提交的 manifest、产物 allowlist 和锁定依赖为准。维护者从 current `main` 手动 dispatch Release;它通过全部 gates 后才创建注释 tag 并发布匹配的 GitHub Release。单独 push tag 不会触发发布。 diff --git a/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md b/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md index 59a374b..6855942 100644 --- a/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md +++ b/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md @@ -100,7 +100,8 @@ does not weaken host capability, lease, worktree, review, or safety gates. 状态的 Fast Lane 编译调用,遇到 v1 必须返回 `NO_SAFE_WORK` 和 `LEGACY_PROJECT_UNBOUND`,零 assignment、零队列、零外部派发。 -未来外部 Desktop bridge 如要消费的载荷必须是 `team-efficiency/work-package-v2` exact-key envelope,包含原始 canonical v1 +legacy request-v1 bridge 消费 `team-efficiency/work-package-v2` exact-key envelope; +新 request-v2 使用下述同边界的 work-package-v3。两者都包含原始 canonical v1 `package`、其 `package_payload_hash`、`project_fence`(仅 `project_id`、 `binding_digest`、`binding_version`)、`workspace_id` 与 `input_snapshot_id`。V2 的 source-plan hash 必须包含该整个 binding,因此相同 task/workflow 在不同项目、workspace 或输入 @@ -132,7 +133,7 @@ server-private 编译在创建任何 planned assignment 前必须同时证明: Git 漂移返回 `INDEX_STALE`;binding、root、snapshot 或 HEAD 错配必须 fail closed,不能产生 部分计划。 -成功响应 schema 为 `team-efficiency/fast-lane-plan-v2`。顶层固定 +legacy request-v1 的成功响应 schema 为 `team-efficiency/fast-lane-plan-v2`。顶层固定 `plan_only=true`、`dispatch_state="not_dispatched"`、`execution_authorized=false`;assignment 使用 `team-efficiency/local-writer-plan-v1`,固定 `execution_state="plan_only"`、 `lease_state="unclaimed"` 与 `worktree.state="planned"`。path-free `index_evidence` 使用 @@ -141,6 +142,46 @@ hash、`include_paths_hash`、所有 writer scopes 的 `scope_hash` 与最终 `e registry root、raw Git HEAD 或 include path 文本。`workflow_policy` 只说明协调器后续必须显式 验证 route、领取 lease 并调用 dispatch tool;它不是执行许可,编译器也不调用该工具。 +#### Model-neutral 新任务入口(V3) + +新任务默认使用 `team-efficiency/fast-lane-request-v2`,并与 +`team-efficiency/work-package-v3` exact-key 配对;request-v1 只能与 +work-package-v2 配对。两个方向的版本混配都返回 invalid。work-package-v3 保留原 +project authority、payload hash 与全部 local registry/index/Git 安全边界,但它生成 +`decomposition-plan-v2`:unit 不再公开带品牌的 `recommended_route`,而是携带 +`model-route-requirements-v1`。 + +现有 request-v1 可显式转换为新入口(命令只生成 JSON,不授予 authority): + +```text +cd mcp-tools +uv run --locked python -c "import json,sys; from devkit_fastlane import prepare_model_neutral_fast_lane_request as prepare; request=json.load(open(sys.argv[1], encoding='utf-8')); print(json.dumps(prepare(request), ensure_ascii=False, sort_keys=True))" request-v1.json +``` + +MCP `fastlane_compile` 对该请求返回 `team-efficiency/fast-lane-plan-v3`,assignment +schema 为 `team-efficiency/local-writer-plan-v2`。每个 assignment 包含 +`route_requirements`(complexity、capability tags、effort/cost preference、worker +ultra 禁令、可选 exact explicit intent)以及 +`selection={state:"unselected", owner:"coordinator", +catalog_source:"codex_tool_metadata", availability_gate:"dispatch_tool"}`。 +`unselected` 是正常 planned 状态,不因没有 Host 或持久 catalog 而 blocked;协调器只根据 +本次 Codex dispatch tool 暴露的 metadata 做判断。DevKit 不实现 model rank、品牌白名单、 +catalog 服务或 availability authority。 + +协调器选定路线后调用公开 `record_model_selection`。生成的 +`model-selection-record-v1` 绑定 assignment 的 `plan_item_id`、`requirement_hash`、exact +`model_id`、`reasoning_effort` 与选择理由,且固定 +`execution_state="selection_only"`、`dispatch_state="not_dispatched"`、 +`execution_authorized=false`。任何 bounded model ID 均可记录;effort 必须是 dispatch tool +支持的标准值且 worker 禁止 `ultra`。如果 request 已指定 exact model/effort,记录必须完全 +相同,否则拒绝,不能 silent fallback。记录本身不证明模型可用,真实 dispatch tool 仍是唯一 +availability gate。 + +request-v2 的完整 canonical request hash 进入 plan-v3,explicit intent 又进入 +`requirement_hash` 与 `plan_item_id`,从而选择记录可被精确重放。旧 +request-v1/work-package-v2、plan-v2、`fastlane_routing.py` v3/v4/v5 policy/hash 与旧 receipt +validator 继续按原字节和语义重放,不用新规则解释历史记录。 + 同一限制覆盖 `bootstrap --apply` 及 import-callable `apply_bootstrap_plan`:当前公开入口在构建 caller-supplied bootstrap plan 或调用 worktree mutation 前,无条件以 `NO_SAFE_WORK/PROJECT_AUTHORITY_UNAVAILABLE` 失败关闭,因而不能到达 @@ -165,10 +206,10 @@ probe 或 coordinator gate。 python scripts/team_efficiency.py fast-lane --input --host-status --reasoning-effort ultra ``` -`ultra` 自动激活(Ultra automatic activation);低于 Ultra 的 effort 必须显式传入 `--enable`,否则得到 inactive plan。这个 CLI/API 不消费 host-status、额度或 index 输入,因此仍输出 plan-v1 的 `NO_SAFE_WORK/PROJECT_AUTHORITY_UNAVAILABLE` 零 assignment/队列预览。MCP 工具只接受 `low`、`medium`、`high`、`xhigh`、`max`,并在 `enable=true` 时返回上述 plan-v2;`enable=false` 返回同 schema 的 inactive、零 assignment 计划,但仍先验证 registry/index/Git binding。两种编译路径都不调用模型、不启动 agent、不创建会话或工作树、不运行 gate、不改写 Git、不领取或完成 workflow。协调器 lane 保有设计、集成、风险决策和最终验收责任;同时负责 route availability 与 dispatch。 +`ultra` 自动激活(Ultra automatic activation);低于 Ultra 的 effort 必须显式传入 `--enable`,否则得到 inactive plan。这个 CLI/API 不消费 host-status、额度或 index 输入,因此仍输出 plan-v1 的 `NO_SAFE_WORK/PROJECT_AUTHORITY_UNAVAILABLE` 零 assignment/队列预览。MCP 工具只接受 `low`、`medium`、`high`、`xhigh`、`max`;新 request-v2 在 `enable=true` 时返回上述 plan-v3,legacy request-v1 仍返回 plan-v2;`enable=false` 返回各自同 schema 的 inactive、零 assignment 计划,但仍先验证 registry/index/Git binding。两种编译路径都不调用模型、不启动 agent、不创建会话或工作树、不运行 gate、不改写 Git、不领取或完成 workflow。协调器 lane 保有设计、集成、风险决策和最终验收责任;同时负责 route availability 与 dispatch。 以下 host-status、`host_dispatch`、cross-session projection 与 refill 规则只描述 plan-v1 的外部 -host contract;它们不由当前 MCP `fastlane_compile` 调用,也不能把 plan-v2 变成 dispatch receipt。 +host contract;它们不由当前 MCP `fastlane_compile` 调用,也不能把 plan-v2/plan-v3 变成 dispatch receipt。 host 通过不超过 3 MiB、有 exact-key 的 `--host-status` 传入 `workflow_id`、当前 lease/binding 与 `routing_context`。后者按 `(task_id, scheduler_role)` 唯一关联完整 diff --git a/mcp-tools/devkit_fastlane/__init__.py b/mcp-tools/devkit_fastlane/__init__.py index 5458cd0..aeee314 100644 --- a/mcp-tools/devkit_fastlane/__init__.py +++ b/mcp-tools/devkit_fastlane/__init__.py @@ -6,7 +6,7 @@ MCP server does not import subprocess or host execution helpers. """ -from collections.abc import Mapping +from collections.abc import Mapping, Sequence from typing import Any @@ -29,4 +29,52 @@ def compile_fast_lane( ) -__all__ = ["compile_fast_lane"] +def record_model_selection( + assignment: Mapping[str, Any], + *, + model_id: str, + reasoning_effort: str, + selection_reason: str, +) -> dict[str, Any]: + """Record an exact coordinator choice without claiming dispatch.""" + + from .scripts.team_efficiency import record_model_selection as _record + + return _record( + assignment, + model_id=model_id, + reasoning_effort=reasoning_effort, + selection_reason=selection_reason, + ) + + +def prepare_model_neutral_fast_lane_request( + request: Mapping[str, Any], + *, + route_intents: Sequence[Mapping[str, Any]] | None = None, +) -> dict[str, Any]: + """Prepare the default model-neutral Fast Lane request schema.""" + + from .scripts.team_efficiency import ( + prepare_model_neutral_fast_lane_request as _prepare, + ) + + return _prepare(request, route_intents=route_intents) + + +def validate_model_selection_record( + record: Mapping[str, Any], *, assignment: Mapping[str, Any] +) -> dict[str, Any]: + """Validate a selection record against its planned assignment.""" + + from .scripts.team_efficiency import validate_model_selection_record as _validate + + return _validate(record, assignment=assignment) + + +__all__ = [ + "compile_fast_lane", + "prepare_model_neutral_fast_lane_request", + "record_model_selection", + "validate_model_selection_record", +] diff --git a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py index 591f33c..e73d7a8 100644 --- a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py +++ b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py @@ -157,6 +157,7 @@ "scheduler_state", } ) +_FAST_LANE_REQUEST_V2_FIELDS = _FAST_LANE_REQUEST_FIELDS | frozenset({"route_intents"}) _FAST_LANE_REQUEST_HOST_PRIVATE_FIELDS = frozenset( { "host_status", @@ -189,6 +190,9 @@ ) _PROJECT_FENCE_SCHEMA = "team-efficiency/project-fence-v1" _WORK_PACKAGE_V2_SCHEMA = "team-efficiency/work-package-v2" +_WORK_PACKAGE_V3_SCHEMA = "team-efficiency/work-package-v3" +_FAST_LANE_REQUEST_V2_SCHEMA = "team-efficiency/fast-lane-request-v2" +_ROUTE_INTENT_FIELDS = frozenset({"task_id", "role", "model_id", "reasoning_effort"}) _PROJECT_BINDING_SCHEMA = "2718lab-devkit/project-binding-v1" _BOOTSTRAP_ATTESTATION_SCHEMA = "2718lab-devkit/new-project-bootstrap-attestation-v1" _PROJECT_BINDING_FIELDS = frozenset( @@ -755,6 +759,7 @@ "plan_hash", } ) +_LOCAL_PLAN_V3_FIELDS = _LOCAL_PLAN_V2_FIELDS | frozenset({"request_hash"}) _LOCAL_PLAN_ASSIGNMENT_FIELDS = frozenset( { "schema", @@ -780,6 +785,27 @@ "plan_item_id", } ) +_LOCAL_PLAN_ASSIGNMENT_V2_FIELDS = frozenset( + { + "schema", + "slot_id", + "task_id", + "role", + "goal", + "output_boundary", + "execution_state", + "dispatch_state", + "execution_authorized", + "lease_state", + "worktree", + "route_requirements", + "selection", + "depends_on", + "required_evidence", + "index_evidence", + "plan_item_id", + } +) _LOCAL_PLAN_WORKTREE_FIELDS = frozenset( { "state", @@ -803,6 +829,49 @@ "compiler_side_effects", } ) +_LOCAL_PLAN_POLICY_V2_FIELDS = _LOCAL_PLAN_POLICY_FIELDS | frozenset( + {"selection_owner", "catalog_source", "availability_gate", "selection_record"} +) +_ROUTE_REQUIREMENT_FIELDS = frozenset( + { + "schema", + "complexity", + "capability_tags", + "effort_preference", + "cost_preference", + "worker_ultra_forbidden", + "explicit_intent", + "requirement_hash", + } +) +_MODEL_SELECTION_FIELDS = frozenset( + { + "schema", + "state", + "owner", + "catalog_source", + "availability_gate", + "record_schema", + } +) +_MODEL_SELECTION_RECORD_FIELDS = frozenset( + { + "schema", + "plan_item_id", + "requirement_hash", + "model_id", + "reasoning_effort", + "selection_reason", + "availability_gate", + "execution_state", + "dispatch_state", + "execution_authorized", + "selection_record_hash", + } +) +_MODEL_NEUTRAL_WORKER_EFFORTS = frozenset( + {"none", "minimal", "low", "medium", "high", "xhigh", "max"} +) _LOCAL_PLAN_INDEX_EVIDENCE_FIELDS = frozenset( { "schema", @@ -1103,6 +1172,17 @@ def _validated_work_package_v2(value: object) -> dict[str, Any]: } +def _validated_work_package_v3(value: object) -> dict[str, Any]: + """Validate the model-neutral envelope without changing V2 replay bytes.""" + + source = _mapping(value, "work-package v3 envelope") + _exact_keys(source, _WORK_PACKAGE_V2_FIELDS, "work-package v3 envelope") + if source["schema"] != _WORK_PACKAGE_V3_SCHEMA: + raise ValueError("work-package v3 schema is invalid") + legacy_shape = {**dict(source), "schema": _WORK_PACKAGE_V2_SCHEMA} + return _validated_work_package_v2(legacy_shape) + + def _validated_project_binding(value: object) -> dict[str, Any]: """Validate Task 2's exact opaque project-binding-v1 wrapper locally.""" @@ -1244,8 +1324,17 @@ def _project_execution_block_details( return "BOOTSTRAP_ATTESTATION_REQUIRED", None try: - _exact_keys(raw_request, _FAST_LANE_REQUEST_FIELDS, "fast-lane request") - if raw_request["schema"] != "team-efficiency/fast-lane-request-v1": + request_schema = raw_request.get("schema") + request_fields = ( + _FAST_LANE_REQUEST_V2_FIELDS + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else _FAST_LANE_REQUEST_FIELDS + ) + _exact_keys(raw_request, request_fields, "fast-lane request") + if request_schema not in { + "team-efficiency/fast-lane-request-v1", + _FAST_LANE_REQUEST_V2_SCHEMA, + }: return "PROJECT_BINDING_INVALID", None if len(_json_bytes(raw_request)) > MAX_MANIFEST_INPUT_BYTES: return "PROJECT_BINDING_INVALID", None @@ -1257,14 +1346,26 @@ def _project_execution_block_details( return "PROJECT_BINDING_INVALID", None if package.get("schema") == "team-efficiency/work-package-v1": return "LEGACY_PROJECT_UNBOUND", None - if package.get("schema") == _WORK_PACKAGE_V2_SCHEMA: + package_schema = package.get("schema") + if package_schema in {_WORK_PACKAGE_V2_SCHEMA, _WORK_PACKAGE_V3_SCHEMA}: + expected_package_schema = ( + _WORK_PACKAGE_V3_SCHEMA + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else _WORK_PACKAGE_V2_SCHEMA + ) + if package_schema != expected_package_schema: + return "PROJECT_BINDING_INVALID", None try: - v2 = _validated_work_package_v2(package) + envelope = ( + _validated_work_package_v3(package) + if package_schema == _WORK_PACKAGE_V3_SCHEMA + else _validated_work_package_v2(package) + ) # Validate the canonical V1 payload before classifying the V2 # envelope as merely missing external authority. This is a pure # diagnostic parse: it cannot reach scheduler, host, or index, # worktree, or dispatch logic. - _decompose_v1(v2["package"]) + _decompose_v1(envelope["package"]) except (TypeError, ValueError): return "PROJECT_BINDING_INVALID", None try: @@ -1274,11 +1375,11 @@ def _project_execution_block_details( except (TypeError, ValueError): return "BOOTSTRAP_ATTESTATION_UNKNOWN", None if ( - binding["workspace_id"] != v2["project_authority"]["workspace_id"] + binding["workspace_id"] != envelope["project_authority"]["workspace_id"] or binding["project_id"] - != f"sha256:{v2['project_authority']['project_id']}" + != f"sha256:{envelope['project_authority']['project_id']}" or binding["attestation"]["attested_input_snapshot_id"] - != v2["project_authority"]["input_snapshot_id"] + != envelope["project_authority"]["input_snapshot_id"] ): return "BOOTSTRAP_ATTESTATION_MISMATCH", None if binding["mode"] == "new_empty_bootstrap": @@ -1310,8 +1411,8 @@ def _project_execution_block_details( "PROJECT_AUTHORITY_UNAVAILABLE", { "schema": "team-efficiency/project-fence-blocked-v2", - "package_payload_hash": v2["package_payload_hash"], - "project_authority": v2["project_authority"], + "package_payload_hash": envelope["package_payload_hash"], + "project_authority": envelope["project_authority"], }, ) return "PROJECT_BINDING_INVALID", None @@ -3895,6 +3996,18 @@ def _fast_lane_assignment( epoch = int(state.get("slot_epochs", {}).get(slot_id, 0)) + 1 task_id = str(unit["task_id"]) context = _fast_lane_build_dispatch_context(validated, unit, role) + if "route_requirements" in route: + return { + "slot_id": slot_id, + "action": "start", + "task_id": task_id, + "role": role, + "assignment_epoch": epoch, + "context_hash": context["context_hash"], + "route_requirements": dict(route["route_requirements"]), + "selection": dict(route["selection"]), + "_context": context, + } receipt = { "schema": "team-efficiency/fast-lane-dispatch-receipt-v1", "source_plan_hash": validated["source_plan_hash"], @@ -4471,7 +4584,9 @@ def _registration_plan( "workflow_id": _host_binding("workflow_id"), "task_id": unit["task_id"], "title": unit["goal"], - "owner_role": unit["recommended_route"], + "owner_role": unit.get( + "recommended_route", "coordinator_model_selection" + ), "card": card, "dependencies": list(unit["depends_on"]), "write_scope": list(unit["write_scope"]), @@ -4649,6 +4764,124 @@ def _scheduled_plan( } +def _route_requirements( + complexity: str, + *, + role: str = "execution", + explicit_intent: Mapping[str, Any] | None = None, +) -> dict[str, Any]: + """Describe task needs without naming a model or claiming availability.""" + + if complexity not in {"routine", "moderate", "complex", "exceptional"}: + raise ValueError("route requirement complexity is invalid") + capability_tags = { + "routine": {"bounded_task_execution"}, + "moderate": {"multi_step_task_execution"}, + "complex": {"complex_task_execution", "cross_module_reasoning"}, + "exceptional": {"highest_available_capability", "deep_reasoning"}, + }[complexity] + if role == "design_probe": + capability_tags.update({"architecture_design", "highest_available_capability"}) + complexity = "exceptional" + elif role == "review": + capability_tags.add("independent_review") + elif role == "verification": + capability_tags.add("verification") + elif role == "prewarm": + capability_tags.add("read_only_prewarm") + effort_preference = { + "routine": "medium", + "moderate": "high", + "complex": "max", + "exceptional": "max", + }[complexity] + cost_preference = { + "routine": "cost_efficient", + "moderate": "balanced", + "complex": "capability_first", + "exceptional": "capability_first", + }[complexity] + intent = ( + {"state": "unspecified"} + if explicit_intent is None + else { + "state": "required", + "model_id": explicit_intent["model_id"], + "reasoning_effort": explicit_intent["reasoning_effort"], + } + ) + requirement: dict[str, Any] = { + "schema": "team-efficiency/model-route-requirements-v1", + "complexity": complexity, + "capability_tags": sorted(capability_tags), + "effort_preference": effort_preference, + "cost_preference": cost_preference, + "worker_ultra_forbidden": True, + "explicit_intent": intent, + } + requirement["requirement_hash"] = _sha256_json(requirement) + return requirement + + +def _model_neutral_source_plan( + legacy_plan: Mapping[str, Any], package: Mapping[str, Any] +) -> dict[str, Any]: + """Project a new source-plan schema while leaving legacy replay untouched.""" + + if legacy_plan.get("status") != "planned": + result = dict(legacy_plan) + result["schema"] = "team-efficiency/decomposition-plan-v2" + if result.get("reason") == "Semantic decomposition requires Sol-owned design.": + result["reason"] = ( + "Semantic decomposition requires coordinator-owned design." + ) + return result + artifact_complexities = { + str(item.get("task_id")): str(item.get("complexity")) + for item in package.get("artifacts", []) + if isinstance(item, Mapping) + } + legacy_units = { + str(item["task_id"]): item + for item in legacy_plan.get("units", []) + if isinstance(item, Mapping) + } + units: list[dict[str, Any]] = [] + for task_id, old in sorted(legacy_units.items()): + complexity = artifact_complexities.get(task_id) + if complexity not in {"routine", "moderate", "complex", "exceptional"}: + complexity = ( + "moderate" if old.get("unit_kind") == "verification" else "routine" + ) + unit = { + key: json.loads(_canonical_json(value)) + for key, value in old.items() + if key != "recommended_route" + } + unit["route_requirements"] = _route_requirements(complexity) + units.append(unit) + unit_by_id = {unit["task_id"]: unit for unit in units} + waves = [ + [unit_by_id[str(item["task_id"])] for item in wave] + for wave in legacy_plan.get("waves", []) + ] + return { + **{ + key: json.loads(_canonical_json(value)) + for key, value in legacy_plan.items() + if key not in {"schema", "units", "waves", "registration_plan"} + }, + "schema": "team-efficiency/decomposition-plan-v2", + "units": units, + "waves": waves, + "registration_plan": _registration_plan( + units, + waves, + strict_index=legacy_plan.get("source_kind") in _ATLAS_SOURCE_KINDS, + ), + } + + def _decompose_v1(manifest: Mapping[str, Any]) -> dict[str, Any]: """Compile manual boundaries or verified Code Atlas evidence into safe waves.""" @@ -4785,6 +5018,14 @@ def decompose(manifest: Mapping[str, Any]) -> dict[str, Any]: """ source = _mapping(manifest, "work-package manifest") + if source.get("schema") == _WORK_PACKAGE_V3_SCHEMA: + v3 = _validated_work_package_v3(source) + plan = _model_neutral_source_plan(_decompose_v1(v3["package"]), v3["package"]) + return { + **plan, + "project_authority": v3["project_authority"], + "package_payload_hash": v3["package_payload_hash"], + } if source.get("schema") != _WORK_PACKAGE_V2_SCHEMA: return _decompose_v1(source) v2 = _validated_work_package_v2(source) @@ -4809,6 +5050,13 @@ def _fast_lane_effort(value: object) -> str: return effort +def _model_neutral_worker_effort(value: object) -> str: + effort = _text(value, "reasoning_effort", maximum=16) + if effort not in _MODEL_NEUTRAL_WORKER_EFFORTS: + raise ValueError("worker reasoning_effort is unavailable") + return effort + + def _one_fast_lane_effort(values: object) -> str: if ( not isinstance(values, Sequence) @@ -6759,6 +7007,9 @@ def _fast_lane_source_with_remediation( source_plan: Mapping[str, Any], remediation: Mapping[str, Any] ) -> dict[str, Any]: unit = _fast_lane_remediation_unit(remediation) + if source_plan.get("schema") == "team-efficiency/decomposition-plan-v2": + unit = {key: value for key, value in unit.items() if key != "recommended_route"} + unit["route_requirements"] = _route_requirements("complex") return { **source_plan, "units": [*source_plan["units"], unit], @@ -7503,6 +7754,32 @@ def _validated_fast_lane_scheduler_state( return normalized, remediation +def _validated_route_intents( + value: object, *, task_ids: frozenset[str] +) -> dict[tuple[str, str], dict[str, str]]: + if not isinstance(value, Sequence) or isinstance(value, (str, bytes, bytearray)): + raise ValueError("route_intents must be a list") + if len(value) > MAX_FAST_LANE_ROUTING_ENTRIES: + raise ValueError("route_intents exceeds its bound") + intents: dict[tuple[str, str], dict[str, str]] = {} + for index, raw in enumerate(value): + item = _mapping(raw, f"route_intents[{index}]") + _exact_keys(item, _ROUTE_INTENT_FIELDS, f"route_intents[{index}]") + task_id = _task_id(item["task_id"], f"route_intents[{index}].task_id") + role = _text(item["role"], f"route_intents[{index}].role", maximum=32) + if task_id not in task_ids or role not in _FAST_LANE_ROLES: + raise ValueError("route intent target is invalid") + model_id = _text( + item["model_id"], f"route_intents[{index}].model_id", maximum=128 + ) + effort = _model_neutral_worker_effort(item["reasoning_effort"]) + key = (task_id, role) + if key in intents: + raise ValueError("route intent target is duplicated") + intents[key] = {"model_id": model_id, "reasoning_effort": effort} + return intents + + def _validated_fast_lane_request( request: Mapping[str, Any], *, @@ -7516,12 +7793,36 @@ def _validated_fast_lane_request( """ candidate = _mapping(request, "fast-lane request") - _exact_keys(candidate, _FAST_LANE_REQUEST_FIELDS, "fast-lane request") - if candidate["schema"] != "team-efficiency/fast-lane-request-v1": + request_schema = candidate.get("schema") + request_fields = ( + _FAST_LANE_REQUEST_V2_FIELDS + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else _FAST_LANE_REQUEST_FIELDS + ) + _exact_keys(candidate, request_fields, "fast-lane request") + if request_schema not in { + "team-efficiency/fast-lane-request-v1", + _FAST_LANE_REQUEST_V2_SCHEMA, + }: raise ValueError("fast-lane request schema is invalid") if len(_json_bytes(candidate)) > MAX_MANIFEST_INPUT_BYTES: raise ValueError("fast-lane request exceeds its byte budget") + request_hash = _sha256_json(candidate) + package = _mapping(candidate["work_package"], "fast-lane work package") + expected_package_schema = ( + _WORK_PACKAGE_V3_SCHEMA + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else _WORK_PACKAGE_V2_SCHEMA + ) + if package.get("schema") != expected_package_schema: + raise ValueError("fast-lane request/work-package versions are incompatible") source_plan = decompose(candidate["work_package"]) + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA: + if source_plan.get("schema") != "team-efficiency/decomposition-plan-v2": + raise ValueError("fast-lane request v2 requires work-package v3") + route_intents_value = candidate["route_intents"] + else: + route_intents_value = () source_plan_hash = _sha256_json(source_plan) target_gates = _validated_fast_lane_target_gates( candidate["target_gates"], source_plan @@ -7534,6 +7835,14 @@ def _validated_fast_lane_request( integration_state=integration_state, ) if remediation is not None and remediation.get("_automation_stopped"): + route_intents = ( + _validated_route_intents( + route_intents_value, + task_ids=frozenset(_fast_lane_unit_index(source_plan)), + ) + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else {} + ) routing_context = _fast_lane_routing_context( host_routing_context, source_plan=source_plan, @@ -7586,6 +7895,8 @@ def _validated_fast_lane_request( "scheduler_state": scheduler_state, "routing_context": routing_context, "automation_stopped": True, + "route_intents": route_intents, + "request_hash": request_hash, } effective_source_plan = source_plan @@ -7601,6 +7912,14 @@ def _validated_fast_lane_request( "gates": remediation["target_gates"], }, ] + route_intents = ( + _validated_route_intents( + route_intents_value, + task_ids=frozenset(_fast_lane_unit_index(effective_source_plan)), + ) + if request_schema == _FAST_LANE_REQUEST_V2_SCHEMA + else {} + ) routing_context = _fast_lane_routing_context( host_routing_context, source_plan=effective_source_plan, @@ -7632,9 +7951,43 @@ def _validated_fast_lane_request( "scheduler_state": scheduler_state, "routing_context": routing_context, "automation_stopped": False, + "route_intents": route_intents, + "request_hash": request_hash, } +def prepare_model_neutral_fast_lane_request( + request: Mapping[str, Any], + *, + route_intents: Sequence[Mapping[str, Any]] | None = None, +) -> dict[str, Any]: + """Create the default request-v2 envelope from an explicit legacy request.""" + + source = _mapping(request, "fast-lane request") + request_schema = source.get("schema") + if request_schema == "team-efficiency/fast-lane-request-v1": + _exact_keys(source, _FAST_LANE_REQUEST_FIELDS, "fast-lane request") + package = _mapping(source["work_package"], "fast-lane work package") + if package.get("schema") != _WORK_PACKAGE_V2_SCHEMA: + raise ValueError("model-neutral request requires work-package v2") + prepared = json.loads(_canonical_json(source)) + prepared["schema"] = _FAST_LANE_REQUEST_V2_SCHEMA + prepared["work_package"]["schema"] = _WORK_PACKAGE_V3_SCHEMA + prepared["route_intents"] = ( + [] + if route_intents is None + else json.loads(_canonical_json(list(route_intents))) + ) + elif request_schema == _FAST_LANE_REQUEST_V2_SCHEMA: + prepared = json.loads(_canonical_json(source)) + if route_intents is not None: + prepared["route_intents"] = json.loads(_canonical_json(list(route_intents))) + else: + raise ValueError("model-neutral request source schema is invalid") + _validated_fast_lane_request(prepared) + return prepared + + def _fast_lane_phase(value: object) -> str: scheduler_state = _mapping(value, "scheduler_state") phase = _text(scheduler_state.get("phase"), "scheduler_state.phase", maximum=32) @@ -9650,8 +10003,111 @@ def _validated_fast_lane_local_worktree(value: object, *, role: str) -> dict[str return dict(worktree) +def _validated_route_requirements(value: object) -> dict[str, Any]: + requirement = _mapping(value, "route requirements") + _exact_keys(requirement, _ROUTE_REQUIREMENT_FIELDS, "route requirements") + if requirement["schema"] != "team-efficiency/model-route-requirements-v1": + raise ValueError("route requirements schema is invalid") + if requirement["complexity"] not in { + "routine", + "moderate", + "complex", + "exceptional", + }: + raise ValueError("route requirements complexity is invalid") + _normalised_list( + requirement["capability_tags"], + "route requirements.capability_tags", + _label, + maximum=8, + required=True, + ) + if requirement["effort_preference"] not in { + "low", + "medium", + "high", + "xhigh", + "max", + }: + raise ValueError("route requirements effort preference is invalid") + if requirement["cost_preference"] not in { + "cost_efficient", + "balanced", + "capability_first", + }: + raise ValueError("route requirements cost preference is invalid") + if requirement["worker_ultra_forbidden"] is not True: + raise ValueError("route requirements must forbid worker ultra") + intent = _mapping(requirement["explicit_intent"], "explicit route intent") + if intent.get("state") == "unspecified": + _exact_keys(intent, frozenset({"state"}), "explicit route intent") + elif intent.get("state") == "required": + _exact_keys( + intent, + frozenset({"state", "model_id", "reasoning_effort"}), + "explicit route intent", + ) + _text(intent["model_id"], "explicit route intent.model_id", maximum=128) + _model_neutral_worker_effort(intent["reasoning_effort"]) + else: + raise ValueError("explicit route intent state is invalid") + supplied_hash = _hash( + requirement["requirement_hash"], "route requirements.requirement_hash" + ) + normalized = { + key: item for key, item in requirement.items() if key != "requirement_hash" + } + if supplied_hash != _sha256_json(normalized): + raise ValueError("route requirements hash is invalid") + return dict(requirement) + + +def _validated_model_selection(value: object) -> dict[str, Any]: + selection = _mapping(value, "model selection") + _exact_keys(selection, _MODEL_SELECTION_FIELDS, "model selection") + if dict(selection) != { + "schema": "team-efficiency/model-selection-v1", + "state": "unselected", + "owner": "coordinator", + "catalog_source": "codex_tool_metadata", + "availability_gate": "dispatch_tool", + "record_schema": "team-efficiency/model-selection-record-v1", + }: + raise ValueError("model selection is invalid") + return dict(selection) + + def _validated_fast_lane_local_assignment(value: object) -> dict[str, Any]: assignment = _mapping(value, "local planned assignment") + if assignment.get("schema") == "team-efficiency/local-writer-plan-v2": + _exact_keys( + assignment, + _LOCAL_PLAN_ASSIGNMENT_V2_FIELDS, + "local planned assignment", + ) + role = _text(assignment["role"], "local planned assignment.role", maximum=32) + if role not in _FAST_LANE_ROLES: + raise ValueError("local planned assignment role is invalid") + if ( + assignment["execution_state"] != "plan_only" + or assignment["dispatch_state"] != "not_dispatched" + or assignment["execution_authorized"] is not False + or assignment["lease_state"] != "unclaimed" + ): + raise ValueError("local planned assignment execution state is invalid") + _validated_fast_lane_local_worktree(assignment["worktree"], role=role) + _validated_fast_lane_local_index_evidence(assignment["index_evidence"]) + _validated_route_requirements(assignment["route_requirements"]) + _validated_model_selection(assignment["selection"]) + supplied_id = _hash( + assignment["plan_item_id"], "local planned assignment.plan_item_id" + ) + normalized = { + key: item for key, item in assignment.items() if key != "plan_item_id" + } + if supplied_id != _sha256_json(normalized): + raise ValueError("local planned assignment identity is invalid") + return dict(assignment) _exact_keys( assignment, _LOCAL_PLAN_ASSIGNMENT_FIELDS, @@ -9682,8 +10138,110 @@ def _validated_fast_lane_local_assignment(value: object) -> dict[str, Any]: return dict(assignment) +def _validated_model_selection_record( + value: object, *, assignment: Mapping[str, Any] +) -> dict[str, Any]: + record = _mapping(value, "model selection record") + _exact_keys(record, _MODEL_SELECTION_RECORD_FIELDS, "model selection record") + if record["schema"] != "team-efficiency/model-selection-record-v1": + raise ValueError("model selection record schema is invalid") + planned = _validated_fast_lane_local_assignment(assignment) + if planned["schema"] != "team-efficiency/local-writer-plan-v2": + raise ValueError("model selection requires a model-neutral assignment") + plan_item_id = _hash(record["plan_item_id"], "model selection.plan_item_id") + requirement_hash = _hash( + record["requirement_hash"], "model selection.requirement_hash" + ) + if ( + plan_item_id != planned["plan_item_id"] + or requirement_hash != planned["route_requirements"]["requirement_hash"] + ): + raise ValueError("model selection record is not bound to its assignment") + model_id = _text(record["model_id"], "model selection.model_id", maximum=128) + reasoning_effort = _model_neutral_worker_effort(record["reasoning_effort"]) + _text(record["selection_reason"], "model selection.selection_reason", maximum=512) + if ( + record["availability_gate"] != "dispatch_tool" + or record["execution_state"] != "selection_only" + or record["dispatch_state"] != "not_dispatched" + or record["execution_authorized"] is not False + ): + raise ValueError("model selection record claims execution authority") + intent = _mapping( + planned["route_requirements"]["explicit_intent"], "explicit route intent" + ) + if intent["state"] == "required" and ( + model_id != intent["model_id"] or reasoning_effort != intent["reasoning_effort"] + ): + raise ValueError("explicit route intent cannot be replaced") + supplied_hash = _hash( + record["selection_record_hash"], "model selection.selection_record_hash" + ) + normalized = { + key: item for key, item in record.items() if key != "selection_record_hash" + } + if supplied_hash != _sha256_json(normalized): + raise ValueError("model selection record hash is invalid") + return dict(record) + + +def record_model_selection( + assignment: Mapping[str, Any], + *, + model_id: str, + reasoning_effort: str, + selection_reason: str, +) -> dict[str, Any]: + """Bind a coordinator choice without claiming dispatch or availability.""" + + planned = _validated_fast_lane_local_assignment(assignment) + if planned["schema"] != "team-efficiency/local-writer-plan-v2": + raise ValueError("model selection requires a model-neutral assignment") + selected_model = _text(model_id, "model selection.model_id", maximum=128) + selected_effort = _model_neutral_worker_effort(reasoning_effort) + reason = _text(selection_reason, "model selection.selection_reason", maximum=512) + record: dict[str, Any] = { + "schema": "team-efficiency/model-selection-record-v1", + "plan_item_id": planned["plan_item_id"], + "requirement_hash": planned["route_requirements"]["requirement_hash"], + "model_id": selected_model, + "reasoning_effort": selected_effort, + "selection_reason": reason, + "availability_gate": "dispatch_tool", + "execution_state": "selection_only", + "dispatch_state": "not_dispatched", + "execution_authorized": False, + } + record["selection_record_hash"] = _sha256_json(record) + return _validated_model_selection_record(record, assignment=planned) + + +def validate_model_selection_record( + record: Mapping[str, Any], *, assignment: Mapping[str, Any] +) -> dict[str, Any]: + """Validate the exact model choice against its model-neutral assignment.""" + + return _validated_model_selection_record(record, assignment=assignment) + + def _validated_fast_lane_local_policy(value: object) -> dict[str, Any]: policy = _mapping(value, "local plan workflow policy") + if policy.get("schema") == "team-efficiency/local-plan-consumer-policy-v2": + _exact_keys(policy, _LOCAL_PLAN_POLICY_V2_FIELDS, "local plan workflow policy") + if dict(policy) != { + "schema": "team-efficiency/local-plan-consumer-policy-v2", + "consumer": "main_coordinator", + "dispatch_tool": "collaboration.spawn_agent", + "explicit_route_required": True, + "lease_claim_required_before_execution": True, + "compiler_side_effects": False, + "selection_owner": "coordinator", + "catalog_source": "codex_tool_metadata", + "availability_gate": "dispatch_tool", + "selection_record": "team-efficiency/model-selection-record-v1", + }: + raise ValueError("local plan workflow policy is invalid") + return dict(policy) _exact_keys(policy, _LOCAL_PLAN_POLICY_FIELDS, "local plan workflow policy") if dict(policy) != { "schema": "team-efficiency/local-plan-consumer-policy-v1", @@ -9699,8 +10257,17 @@ def _validated_fast_lane_local_policy(value: object) -> dict[str, Any]: def _validated_fast_lane_local_plan(value: object) -> dict[str, Any]: plan = _mapping(value, "fast-lane local plan") - _exact_keys(plan, _LOCAL_PLAN_V2_FIELDS, "fast-lane local plan") - if plan["schema"] != "team-efficiency/fast-lane-plan-v2": + plan_schema = plan.get("schema") + expected_fields = ( + _LOCAL_PLAN_V3_FIELDS + if plan_schema == "team-efficiency/fast-lane-plan-v3" + else _LOCAL_PLAN_V2_FIELDS + ) + _exact_keys(plan, expected_fields, "fast-lane local plan") + if plan_schema not in { + "team-efficiency/fast-lane-plan-v2", + "team-efficiency/fast-lane-plan-v3", + }: raise ValueError("fast-lane local plan schema is invalid") if ( plan["plan_only"] is not True @@ -9723,6 +10290,13 @@ def _validated_fast_lane_local_plan(value: object) -> dict[str, Any]: raise ValueError("fast-lane local plan assignments are invalid") for assignment_value in assignments: assignment = _validated_fast_lane_local_assignment(assignment_value) + expected_assignment_schema = ( + "team-efficiency/local-writer-plan-v2" + if plan_schema == "team-efficiency/fast-lane-plan-v3" + else "team-efficiency/local-writer-plan-v1" + ) + if assignment["schema"] != expected_assignment_schema: + raise ValueError("local planned assignment version is invalid") if assignment["index_evidence"] != index_evidence: raise ValueError("local planned assignment index evidence is not bound") @@ -9814,6 +10388,41 @@ def _fast_lane_local_routing_context( } +def _fast_lane_model_neutral_routing_context( + source_plan: Mapping[str, Any], + route_intents: Mapping[tuple[str, str], Mapping[str, str]], +) -> dict[str, Any]: + """Return selectable requirements; catalog choice remains coordinator-owned.""" + + decisions: dict[tuple[str, str], dict[str, Any]] = {} + for task_id, unit in sorted(_fast_lane_unit_index(source_plan).items()): + base = _mapping(unit.get("route_requirements"), "source route requirements") + complexity = _text( + base.get("complexity"), "source route requirements.complexity", maximum=32 + ) + for role in sorted(_FAST_LANE_ROLES): + intent = route_intents.get((task_id, role)) + decisions[(task_id, role)] = { + "route_requirements": _route_requirements( + complexity, role=role, explicit_intent=intent + ), + "selection": { + "schema": "team-efficiency/model-selection-v1", + "state": "unselected", + "owner": "coordinator", + "catalog_source": "codex_tool_metadata", + "availability_gate": "dispatch_tool", + "record_schema": "team-efficiency/model-selection-record-v1", + }, + } + return { + "decisions": decisions, + "reasons": {}, + "default_reason": "coordinator_model_selection", + "global_failure_reason": None, + } + + def _validated_fast_lane_local_material( material_value: Mapping[str, Any], *, @@ -9985,6 +10594,30 @@ def _fast_lane_local_assignment_output( worktree = _fast_lane_public_value(worktree_value) if type(worktree) is not dict: raise ValueError("local planned worktree is invalid") + if "route_requirements" in assignment: + item_value = { + "schema": "team-efficiency/local-writer-plan-v2", + "slot_id": assignment["slot_id"], + "task_id": task_id, + "role": role, + "goal": unit["goal"], + "output_boundary": unit["output_boundary"], + "execution_state": "plan_only", + "dispatch_state": "not_dispatched", + "execution_authorized": False, + "lease_state": "unclaimed", + "worktree": worktree, + "route_requirements": dict(assignment["route_requirements"]), + "selection": dict(assignment["selection"]), + "depends_on": list(unit.get("depends_on", [])), + "required_evidence": list(unit.get("required_evidence", [])), + "index_evidence": dict(index_evidence), + } + item = _fast_lane_public_value(item_value) + if type(item) is not dict: + raise ValueError("local planned assignment is invalid") + item["plan_item_id"] = _sha256_json(item) + return _validated_fast_lane_local_assignment(item) item_value = { "schema": "team-efficiency/local-writer-plan-v1", "slot_id": assignment["slot_id"], @@ -10021,13 +10654,21 @@ def _compile_fast_lane_local_plan( enable: bool, local_plan_material: Mapping[str, Any], ) -> dict[str, Any]: + model_neutral = request.get("schema") == _FAST_LANE_REQUEST_V2_SCHEMA activation = _fast_lane_activation(reasoning_effort, enable) validated = _validated_fast_lane_request(request) + routing_context = ( + _fast_lane_model_neutral_routing_context( + validated["source_plan"], validated["route_intents"] + ) + if model_neutral + else _fast_lane_local_routing_context( + validated["source_plan"], validated["source_plan_hash"] + ) + ) validated = { **validated, - "routing_context": _fast_lane_local_routing_context( - validated["source_plan"], validated["source_plan_hash"] - ), + "routing_context": routing_context, } if validated["scheduler_state"]["running_assignments"]: raise _FastLaneLocalPlanError("FASTLANE_RUNTIME_STATE_UNSUPPORTED") @@ -10054,7 +10695,11 @@ def _compile_fast_lane_local_plan( "FAST_LANE_PLANNED" if planned_assignments else "NO_SAFE_LOCAL_PLAN" ) result_value: dict[str, Any] = { - "schema": "team-efficiency/fast-lane-plan-v2", + "schema": ( + "team-efficiency/fast-lane-plan-v3" + if model_neutral + else "team-efficiency/fast-lane-plan-v2" + ), "status": status, "decision_code": decision_code, "plan_only": True, @@ -10067,15 +10712,32 @@ def _compile_fast_lane_local_plan( "assignments": planned_assignments, "idle_slots": list(idle_slots), "index_evidence": index_evidence, - "workflow_policy": { - "schema": "team-efficiency/local-plan-consumer-policy-v1", - "consumer": "main_coordinator", - "dispatch_tool": "collaboration.spawn_agent", - "explicit_route_required": True, - "lease_claim_required_before_execution": True, - "compiler_side_effects": False, - }, + "workflow_policy": ( + { + "schema": "team-efficiency/local-plan-consumer-policy-v2", + "consumer": "main_coordinator", + "dispatch_tool": "collaboration.spawn_agent", + "explicit_route_required": True, + "lease_claim_required_before_execution": True, + "compiler_side_effects": False, + "selection_owner": "coordinator", + "catalog_source": "codex_tool_metadata", + "availability_gate": "dispatch_tool", + "selection_record": "team-efficiency/model-selection-record-v1", + } + if model_neutral + else { + "schema": "team-efficiency/local-plan-consumer-policy-v1", + "consumer": "main_coordinator", + "dispatch_tool": "collaboration.spawn_agent", + "explicit_route_required": True, + "lease_claim_required_before_execution": True, + "compiler_side_effects": False, + } + ), } + if model_neutral: + result_value["request_hash"] = validated["request_hash"] result = _fast_lane_public_value(result_value) if type(result) is not dict: raise ValueError("fast-lane local plan is invalid") diff --git a/mcp-tools/pyproject.toml b/mcp-tools/pyproject.toml index e22f84b..c1c0c8f 100644 --- a/mcp-tools/pyproject.toml +++ b/mcp-tools/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "2718lab-devkit-mcp" -version = "1.1.4" +version = "1.1.5" description = "MCP runtime for the 2718lab DevKit primary plugin." requires-python = ">=3.11" dependencies = [ diff --git a/mcp-tools/tests/test_bugkiller_metadata.py b/mcp-tools/tests/test_bugkiller_metadata.py index a7ff2bf..4967c10 100644 --- a/mcp-tools/tests/test_bugkiller_metadata.py +++ b/mcp-tools/tests/test_bugkiller_metadata.py @@ -39,7 +39,7 @@ def test_primary_plugin_manifest_is_stable_v1_and_has_no_prompt_runtime_surface( self, ) -> None: codex = load_json(".codex-plugin/plugin.json") - self.assertEqual("1.1.4", codex["version"]) + self.assertEqual("1.1.5", codex["version"]) self.assertEqual("./.mcp.json", codex["mcpServers"]) for legacy_surface in ("skills", "agents", "commands", "hooks"): self.assertNotIn(legacy_surface, codex) diff --git a/mcp-tools/tests/test_fastlane_runtime.py b/mcp-tools/tests/test_fastlane_runtime.py index ed642fe..77e11f1 100644 --- a/mcp-tools/tests/test_fastlane_runtime.py +++ b/mcp-tools/tests/test_fastlane_runtime.py @@ -253,7 +253,7 @@ def test_work_methodology_is_not_a_discoverable_skill() -> None: assert (runtime / "scripts" / "team_efficiency.py").is_file() -def test_fastlane_docs_describe_the_mcp_plan_v2_boundary() -> None: +def test_fastlane_docs_describe_the_mcp_plan_v3_and_legacy_v2_boundaries() -> None: contract = (ROOT / "mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md").read_text( encoding="utf-8" ) @@ -263,8 +263,9 @@ def test_fastlane_docs_describe_the_mcp_plan_v2_boundary() -> None: ] for document in (contract, *readmes): - assert "team-efficiency/fast-lane-plan-v2" in document + assert "team-efficiency/fast-lane-plan-v3" in document assert "plan_only" in document + assert "team-efficiency/fast-lane-plan-v2" in contract assert "include_paths" in contract assert "INDEX_PARTIAL" in contract assert "local_plan_material" not in contract @@ -305,6 +306,79 @@ def test_fastlane_tool_rejects_host_private_inputs() -> None: assert result["error"]["code"] == "FASTLANE_REQUEST_INVALID" +@pytest.mark.parametrize( + ("request_schema", "package_schema", "include_route_intents"), + [ + ( + "team-efficiency/fast-lane-request-v1", + "team-efficiency/work-package-v3", + False, + ), + ( + "team-efficiency/fast-lane-request-v2", + "team-efficiency/work-package-v2", + True, + ), + ], +) +def test_fastlane_rejects_mixed_request_and_work_package_versions( + request_schema: str, package_schema: str, include_route_intents: bool +) -> None: + helper, request, _ = _sample_request() + try: + request["schema"] = request_schema + request["work_package"]["schema"] = package_schema + if include_route_intents: + request["route_intents"] = [] + result = server.fastlane_compile( + request=request, reasoning_effort="max", enable=True + ) + with pytest.raises(ValueError, match="versions are incompatible"): + team_efficiency._validated_fast_lane_request(request) + finally: + helper.tearDown() + assert result["ok"] is False + assert result["error"]["code"] == "PROJECT_BINDING_INVALID" + + +def test_model_neutral_route_intent_can_target_appended_remediation( + monkeypatch, +) -> None: + helper, request, _ = _sample_request() + try: + prepared = devkit_fastlane.prepare_model_neutral_fast_lane_request(request) + remediation = helper.fast_lane_remediation_request(team_efficiency, prepared) + prepared["remediation_request"] = remediation + prepared["route_intents"] = [ + { + "task_id": remediation["task_id"], + "role": "execution", + "model_id": "gpt-99-future", + "reasoning_effort": "max", + } + ] + monkeypatch.setattr( + team_efficiency, + "_validated_fast_lane_contexts", + lambda *args, **kwargs: ([], []), + ) + monkeypatch.setattr( + team_efficiency, + "_validated_fast_lane_scheduler_state", + lambda *args, **kwargs: ({}, remediation), + ) + validated = team_efficiency._validated_fast_lane_request(prepared) + finally: + helper.tearDown() + assert remediation["task_id"] in { + item["task_id"] for item in validated["source_plan"]["units"] + } + assert validated["route_intents"][(remediation["task_id"], "execution")] == { + "model_id": "gpt-99-future", + "reasoning_effort": "max", + } + + def test_fastlane_tool_never_spawns_or_executes(monkeypatch) -> None: """The MCP compiler emits descriptors; the host owns execution and refill.""" @@ -489,6 +563,93 @@ def test_fastlane_tool_uses_persisted_runtime_registry_index_and_git_head( assert all(item["execution_state"] == "plan_only" for item in data["assignments"]) +def test_model_neutral_plan_preserves_future_intent_and_legacy_source_hash( + monkeypatch, +) -> None: + helper, _, _ = _sample_request() + legacy_hash = team_efficiency._sha256_json( + team_efficiency.decompose(helper.decomposition_manifest()) + ) + root = None + try: + root, request, snapshot_id = _install_persistent_local_plan_runtime( + monkeypatch, helper + ) + prepared = devkit_fastlane.prepare_model_neutral_fast_lane_request( + request, + route_intents=[ + { + "task_id": "ATLAS-12B-A", + "role": "execution", + "model_id": "gpt-99-future", + "reasoning_effort": "max", + } + ], + ) + result = server.fastlane_compile( + request=prepared, reasoning_effort="max", enable=True + ) + finally: + if root is not None: + root.shutdown() + helper.tearDown() + + assert legacy_hash == ( + "sha256:f736b99d55bc562252d1fd6a98fb0f2d12813b0b50ba518f88d4f12c298a7775" + ) + assert result["ok"] is True + data = result["data"] + assert data["schema"] == "team-efficiency/fast-lane-plan-v3" + assert data["status"] == "planned" + assert data["request_hash"] == team_efficiency._sha256_json(prepared) + assert data["index_evidence"]["snapshot_id"] == snapshot_id + assert all( + {"model", "reasoning_effort", "recommended_route"}.isdisjoint(assignment) + for assignment in data["assignments"] + ) + assignment = next( + item for item in data["assignments"] if item["task_id"] == "ATLAS-12B-A" + ) + assert assignment["selection"] == { + "schema": "team-efficiency/model-selection-v1", + "state": "unselected", + "owner": "coordinator", + "catalog_source": "codex_tool_metadata", + "availability_gate": "dispatch_tool", + "record_schema": "team-efficiency/model-selection-record-v1", + } + assert assignment["route_requirements"]["explicit_intent"] == { + "state": "required", + "model_id": "gpt-99-future", + "reasoning_effort": "max", + } + record = devkit_fastlane.record_model_selection( + assignment, + model_id="gpt-99-future", + reasoning_effort="max", + selection_reason="The current Codex tool metadata lists the requested route.", + ) + assert record["execution_state"] == "selection_only" + assert record["dispatch_state"] == "not_dispatched" + assert record["execution_authorized"] is False + assert record["plan_item_id"] == assignment["plan_item_id"] + assert ( + record["requirement_hash"] + == assignment["route_requirements"]["requirement_hash"] + ) + assert ( + devkit_fastlane.validate_model_selection_record(record, assignment=assignment) + == record + ) + with pytest.raises(ValueError, match="explicit route intent cannot be replaced"): + devkit_fastlane.record_model_selection( + assignment, + model_id="gpt-5.6-sol", + reasoning_effort="max", + selection_reason="Attempted fallback must be rejected.", + ) + + def test_fastlane_tool_rejects_stale_persisted_snapshot(monkeypatch) -> None: helper, _, _ = _sample_request() root = None diff --git a/mcp-tools/tests/test_primary_artifact.py b/mcp-tools/tests/test_primary_artifact.py index 8bc1a1d..2f618af 100644 --- a/mcp-tools/tests/test_primary_artifact.py +++ b/mcp-tools/tests/test_primary_artifact.py @@ -231,14 +231,14 @@ def test_python_project_and_lock_use_pep440_stable_v1_metadata() -> None: with project_path.open("rb") as project_file: project = tomllib.load(project_file) - assert project["project"]["version"] == "1.1.4" + assert project["project"]["version"] == "1.1.5" assert project["project"]["dependencies"] == ["mcp[cli]>=1,<2"] assert "devkit_atlas" in project["tool"]["pyright"]["include"] assert "devkit_runtime" in project["tool"]["pyright"]["include"] assert "code_atlas" not in project["tool"]["pyright"]["include"] lock_text = lock_path.read_text(encoding="utf-8") assert 'name = "2718lab-devkit-mcp"' in lock_text - assert 'version = "1.1.4"' in lock_text + assert 'version = "1.1.5"' in lock_text def test_two_builds_are_byte_identical_with_normalized_zip_metadata( diff --git a/mcp-tools/uv.lock b/mcp-tools/uv.lock index b33ed59..4a26f6f 100644 --- a/mcp-tools/uv.lock +++ b/mcp-tools/uv.lock @@ -10,7 +10,7 @@ resolution-markers = [ [[package]] name = "2718lab-devkit-mcp" -version = "1.1.4" +version = "1.1.5" source = { virtual = "." } dependencies = [ { name = "mcp", extra = ["cli"] }, diff --git a/skills/fast-lane-routing/SKILL.md b/skills/fast-lane-routing/SKILL.md index d60cc0e..9c5a86c 100644 --- a/skills/fast-lane-routing/SKILL.md +++ b/skills/fast-lane-routing/SKILL.md @@ -5,8 +5,29 @@ description: Reference manual for reading a compiled 2718lab Fast Lane plan, exp # Fast Lane Routing Manual -Read a plan as a bounded host contract: model, effort, route, worktree, lease, -context, and predecessor bindings are exact values, not defaults to infer. +For new work, prepare `fast-lane-request-v2` with +`prepare_model_neutral_fast_lane_request`; it pairs only with +`work-package-v3`. Read its plan-v3 assignments as requirements, not a model +registry: + +- Use `route_requirements` to judge complexity, capability, effort, and cost. +- Choose from the model IDs and supported efforts listed by the current + `collaboration.spawn_agent` tool metadata. Routine work should stay + cost-conscious; design, research, and exceptional cross-module work should + use the strongest suitable current model at its highest worker-safe effort. +- `selection.state="unselected"` is a normal planned state. Call + `record_model_selection` with the exact chosen model ID, effort, and reason, + then pass that exact route to the dispatch tool. The record does not claim + availability or dispatch. +- An `explicit_intent` with `state="required"` is non-substitutable. If the + current tool rejects it, report the route unavailable; never silently fall + back to another model. +- Model IDs are bounded strings, not a product whitelist. Do not add a model + name to DevKit routing tables merely because Codex introduces it. + +Legacy request-v1/plan-v2 records remain exact replay contracts: their model, +effort, route, worktree, lease, context, and predecessor bindings are not +defaults to reinterpret. - A missing, altered, or unattested route is \`NO_SAFE_WORK\`. - A missing first index snapshot in a newly opened project is a cold-start