From c78749b36691dbee07eec97cbe751ba718db41f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=93=80=E6=B4=9B=E8=8A=99?= <273111507+Ayleovelle@users.noreply.github.com> Date: Thu, 27 Aug 2026 03:31:58 +0800 Subject: [PATCH 1/2] fix: bootstrap unindexed new projects in v1.1.2 --- .codex-plugin/plugin.json | 2 +- .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- CHANGELOG.md | 13 +++++++ README.md | 12 +++--- README.zh-CN.md | 12 +++--- .../devkit_fastlane/FASTLANE_CONTRACT.md | 5 ++- .../references/efficiency-automation.md | 7 +++- .../scripts/team_efficiency.py | 7 +--- .../tests/test_team_efficiency.py | 21 +++++++--- mcp-tools/devkit_relay/compiler.py | 5 ++- mcp-tools/devkit_relay/service.py | 3 +- mcp-tools/devkit_runtime/relay_runtime.py | 11 ++++-- mcp-tools/pyproject.toml | 2 +- mcp-tools/tests/test_bugkiller_metadata.py | 2 +- mcp-tools/tests/test_primary_artifact.py | 4 +- mcp-tools/tests/test_project_bootstrap.py | 39 +++++++++++++++++-- mcp-tools/uv.lock | 2 +- .../code-atlas/references/status-contract.md | 7 ++++ skills/fast-lane-routing/SKILL.md | 4 ++ skills/workflow-design/SKILL.md | 11 ++++++ 20 files changed, 128 insertions(+), 43 deletions(-) diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 546d7f4..6963596 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "2718lab-devkit", - "version": "1.1.1", + "version": "1.1.2", "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 ad0ac7c..d0a632b 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.1" + placeholder: "v1.1.2" validations: required: true diff --git a/CHANGELOG.md b/CHANGELOG.md index fc26f2d..edd23e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,19 @@ only after the CI and artifact checks pass. ## [Unreleased] +## [1.1.2] - 2026-08-27 + +### Fixed + +- Treat a newly opened project without an index as normal cold start: initialize + it with one bounded `project_index_register -> project_index_sync` sequence + before considering degraded mode, including when README, configuration, or + source files already exist. +- Preserve the host-attested initial entry count through bootstrap and require + the synchronized manifest and entry count to match exactly before indexed + recompilation; registration, synchronization, identity, or attestation + failures remain fail-closed. + ## [1.1.1] - 2026-08-25 ### Fixed diff --git a/README.md b/README.md index eee2e1b..0db0b61 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ [简体中文](README.zh-CN.md) -# 2718lab DevKit — Codex + MCP v1.1.1 +# 2718lab DevKit — Codex + MCP v1.1.2 -[![version](https://img.shields.io/badge/version-v1.1.1-blue)](./.codex-plugin/plugin.json) +[![version](https://img.shields.io/badge/version-v1.1.2-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.1 package. +of reference manuals. This repository carries the versioned v1.1.2 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. @@ -168,7 +168,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.1.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.2.zip ## Install and run locally @@ -202,7 +202,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.1.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.2.zip The artifact contains the manifest, .mcp.json, LICENSE, the locked Python project, and the runtime files selected by @@ -360,7 +360,7 @@ freeze a transient regression count. ## Version -This repository represents the versioned v1.1.1 package. Release notes are +This repository represents the versioned v1.1.2 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 3b960f1..c992654 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,14 +1,14 @@ [English](README.md) -# 2718lab DevKit —— Codex + MCP v1.1.1 +# 2718lab DevKit —— Codex + MCP v1.1.2 -[![版本](https://img.shields.io/badge/version-v1.1.1-blue)](./.codex-plugin/plugin.json) +[![版本](https://img.shields.io/badge/version-v1.1.2-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.1 包;已提交的 manifest 和 allowlist 定义可执行运行时范围,说明书导航、 +v1.1.2 包;已提交的 manifest 和 allowlist 定义可执行运行时范围,说明书导航、 安装、构建和验证章节共同给出支持的工作流。 当前版本保留刻意 fail-closed 的 Fast Lane 预览。公共编译器和 CLI 固定返回 @@ -148,7 +148,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.1.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --allowlist .codex-plugin/marketplace-artifact-allowlist.json --output /2718lab-devkit-marketplace-v1.1.2.zip ## 本地安装与运行 @@ -179,7 +179,7 @@ RELAY_CAPABILITY_BROKER_UNAVAILABLE。服务器不会暴露原始 handle,也 allowlist builder 会在插件源码树之外生成确定性的 ZIP。请选择源码树之外的输出目录: - python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.1.zip + python .codex-plugin/build_main_artifact.py --plugin-root . --output /2718lab-devkit-v1.1.2.zip 产物包含 manifest、.mcp.json、LICENSE、锁定的 Python 项目,以及 .codex-plugin/main-artifact-allowlist.json 选中的运行时文件。它的可执行运行时 @@ -312,7 +312,7 @@ CI 和全新产物检查才是当前测试计数的唯一来源。它们验证 ## 版本 -本仓库代表版本化的 v1.1.1 包。发布说明见 +本仓库代表版本化的 v1.1.2 包。发布说明见 [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 844010d..59c74eb 100644 --- a/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md +++ b/mcp-tools/devkit_fastlane/FASTLANE_CONTRACT.md @@ -218,8 +218,9 @@ plan 可自报的根字段。 本地 Windows 的 `C:/` 和 `G:/` 以外的本机盘符都禁止作为 `TEMP`、`TMP`、`TMPDIR` 或临时根;可信 hosted Windows CI 仅可使用宿主提供的 `RUNNER_TEMP`,并在其下派生 任务临时与缓存路径。该宿主例外不放宽本地规则,也不是外部宿主 embedding 证据。 -这仍是当前 bootstrap/read-context 边界。空项目只能生成 bootstrap-only index:不得据此创建可执行 -assignment,直到可信宿主提供有界项目索引上下文。旧 schema 输入返回 +这仍是当前 bootstrap/read-context 边界。尚未建立索引的新项目(无论目录是否已包含 +源码、README 或配置)只能生成 bootstrap-only index:不得据此创建可执行 assignment, +直到可信宿主完成一次 `register -> sync` 并提供与初始 manifest 精确绑定的有界项目索引上下文。旧 schema 输入返回 `FASTLANE_SCHEMA_UPGRADE_REQUIRED`,不得降级猜测或启动工作。 ### 6. 验证与交付 diff --git a/mcp-tools/devkit_fastlane/references/efficiency-automation.md b/mcp-tools/devkit_fastlane/references/efficiency-automation.md index 43e5056..b26f017 100644 --- a/mcp-tools/devkit_fastlane/references/efficiency-automation.md +++ b/mcp-tools/devkit_fastlane/references/efficiency-automation.md @@ -68,8 +68,11 @@ apply behavior; neither is implemented or accepted as an input here. An older bootstrap or routing schema is rejected as `FASTLANE_SCHEMA_UPGRADE_REQUIRED`; it is never normalized into an executable -plan. An empty project may produce bootstrap-only index diagnostics, but cannot -produce an assignment until trusted host index context is available. +plan. An unindexed new project, including one that already contains source, +README, or configuration files, may produce bootstrap-only index diagnostics, +but cannot produce an assignment until the trusted host completes one +`register -> sync` and supplies index context bound to the attested initial +manifest. The `RuntimeRoot` host-private V2/V3 bootstrap path is covered with injected test doubles only. That coverage does not prove an external host embedding or diff --git a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py index 84487f7..d687bc6 100644 --- a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py +++ b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py @@ -1155,10 +1155,7 @@ def _project_execution_block_details( return "BOOTSTRAP_ATTESTATION_MISMATCH", None if binding["mode"] == "new_empty_bootstrap": attestation = binding["attestation"] - if ( - attestation["state"] != "new_empty" - or attestation["initial_entry_count"] != 0 - ): + if attestation["state"] != "new_empty": return "BOOTSTRAP_PROJECT_NOT_EMPTY", None issued = _bootstrap_attestation_timestamp( attestation["issued_at"], "project binding attestation.issued_at" @@ -8613,7 +8610,7 @@ def _fast_lane_bootstrap_index_ready_plan( *, source_identity: Mapping[str, Any], ) -> dict[str, Any]: - """Emit only the read-only descriptor for an attested empty project.""" + """Emit the read-only descriptor for an attested unindexed new project.""" binding = _mapping(source_identity["binding"], "new-empty bootstrap binding") attestation = _mapping(binding["attestation"], "new-empty bootstrap attestation") diff --git a/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py b/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py index 5dcc820..440863d 100644 --- a/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py +++ b/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py @@ -720,6 +720,7 @@ def new_empty_project_binding( issued_at: int, expires_at: int, initial_entry_count: int = 0, + state: str = "new_empty", snapshot_id: str | None = None, ) -> dict[str, object]: package = request["work_package"] @@ -737,7 +738,7 @@ def new_empty_project_binding( helper, authority=authority, mode="new_empty_bootstrap", - state="new_empty", + state=state, initial_entry_count=initial_entry_count, issued_at=issued_at, expires_at=expires_at, @@ -2280,13 +2281,22 @@ def bootstrap_request() -> dict[str, object]: issued_at=now - 130, expires_at=now - 10, ) - nonempty = bootstrap_request() - nonempty["project_binding"] = self.new_empty_project_binding( + invalid_state = bootstrap_request() + invalid_state["project_binding"] = self.new_empty_project_binding( helper, - nonempty, + invalid_state, issued_at=issued_at, expires_at=expires_at, initial_entry_count=1, + state="indexed", + ) + negative_count = bootstrap_request() + negative_count["project_binding"] = self.new_empty_project_binding( + helper, + negative_count, + issued_at=issued_at, + expires_at=expires_at, + initial_entry_count=-1, ) mismatch = bootstrap_request() mismatch["project_binding"] = self.new_empty_project_binding( @@ -2307,7 +2317,8 @@ def bootstrap_request() -> dict[str, object]: cases = { "missing": (missing, "BOOTSTRAP_ATTESTATION_REQUIRED"), "stale": (stale, "BOOTSTRAP_ATTESTATION_STALE"), - "nonempty": (nonempty, "BOOTSTRAP_PROJECT_NOT_EMPTY"), + "invalid_state": (invalid_state, "BOOTSTRAP_PROJECT_NOT_EMPTY"), + "negative_count": (negative_count, "BOOTSTRAP_ATTESTATION_UNKNOWN"), "mismatch": (mismatch, "BOOTSTRAP_ATTESTATION_MISMATCH"), "unknown": (unknown, "BOOTSTRAP_ATTESTATION_UNKNOWN"), } diff --git a/mcp-tools/devkit_relay/compiler.py b/mcp-tools/devkit_relay/compiler.py index 2f857d1..99c61dc 100644 --- a/mcp-tools/devkit_relay/compiler.py +++ b/mcp-tools/devkit_relay/compiler.py @@ -202,6 +202,7 @@ _MAX_RETRY_ATTEMPTS = 3 _MAX_WRITERS_PER_SCHEDULER = 3 _MAX_PARALLEL_WRITERS = 9 +_MAX_BOOTSTRAP_ENTRY_COUNT = 2**63 - 1 class RelayPlanError(ValueError): @@ -1135,7 +1136,7 @@ def _new_empty_project_binding( ) or attestation["state"] != "new_empty" or type(attestation["initial_entry_count"]) is not int - or attestation["initial_entry_count"] != 0 + or not 0 <= attestation["initial_entry_count"] <= _MAX_BOOTSTRAP_ENTRY_COUNT or type(attestation["capability_epoch"]) is not int or attestation["capability_epoch"] < 1 or not _finite_timestamp(attestation["issued_at"]) @@ -1166,7 +1167,7 @@ def _bootstrap_registry_binding( "project_id": attestation["project_id"], "bootstrap_root_identity": attestation["bootstrap_root_identity"], "initial_manifest_hash": attestation["initial_manifest_hash"], - "initial_entry_count": 0, + "initial_entry_count": attestation["initial_entry_count"], "capability_epoch": attestation["capability_epoch"], "capability_hash": attestation["capability_hash"], "attested_input_snapshot_id": attestation["attested_input_snapshot_id"], diff --git a/mcp-tools/devkit_relay/service.py b/mcp-tools/devkit_relay/service.py index 5faf08d..20c7b0e 100644 --- a/mcp-tools/devkit_relay/service.py +++ b/mcp-tools/devkit_relay/service.py @@ -26,6 +26,7 @@ _DIGEST = re.compile(r"sha256:[0-9a-f]{64}\Z") _WORKSPACE_ID = re.compile(r"sha256:[0-9a-f]{64}\Z") _SCOPE_PATH = re.compile(r"^[A-Za-z]:") +_MAX_BOOTSTRAP_ENTRY_COUNT = 2**63 - 1 class RelayError(RuntimeError): @@ -1066,7 +1067,7 @@ def _validated_bootstrap_recompile_binding( ) or attestation["state"] != "new_empty" or type(attestation["initial_entry_count"]) is not int - or attestation["initial_entry_count"] != 0 + or not 0 <= attestation["initial_entry_count"] <= _MAX_BOOTSTRAP_ENTRY_COUNT or type(attestation["capability_epoch"]) is not int or attestation["capability_epoch"] < 1 or not self._finite_timestamp(attestation["issued_at"]) diff --git a/mcp-tools/devkit_runtime/relay_runtime.py b/mcp-tools/devkit_runtime/relay_runtime.py index d1d36f7..419d809 100644 --- a/mcp-tools/devkit_runtime/relay_runtime.py +++ b/mcp-tools/devkit_runtime/relay_runtime.py @@ -41,6 +41,7 @@ "2718lab-devkit/project-registry-bootstrap-binding-v1" ) _BOOTSTRAP_RECEIPT_SCHEMA = "2718lab-devkit/project-index-bootstrap-receipt-v1" +_MAX_BOOTSTRAP_ENTRY_COUNT = 2**63 - 1 _PROJECT_BINDING_KEYS = frozenset( { "schema", @@ -280,7 +281,7 @@ def resolve_new_empty_bootstrap( if ( attestation["state"] != "new_empty" or type(attestation["initial_entry_count"]) is not int - or attestation["initial_entry_count"] != 0 + or not 0 <= attestation["initial_entry_count"] <= _MAX_BOOTSTRAP_ENTRY_COUNT ): raise RelayRuntimeError("BOOTSTRAP_PROJECT_NOT_EMPTY") now = _trusted_time(self._clock) @@ -315,7 +316,7 @@ def resolve_new_empty_bootstrap( "project_id": attestation["project_id"], "bootstrap_root_identity": attestation["bootstrap_root_identity"], "initial_manifest_hash": attestation["initial_manifest_hash"], - "initial_entry_count": 0, + "initial_entry_count": attestation["initial_entry_count"], "capability_epoch": attestation["capability_epoch"], "capability_hash": attestation["capability_hash"], "attested_input_snapshot_id": attestation["attested_input_snapshot_id"], @@ -406,7 +407,8 @@ def execute(self, registry_binding: Mapping[str, object]) -> dict[str, object]: or synchronized["attested_input_snapshot_id"] != binding["attested_input_snapshot_id"] or synchronized["initial_manifest_hash"] != binding["initial_manifest_hash"] - or synchronized["initial_entry_count"] != 0 + or type(synchronized["initial_entry_count"]) is not int + or synchronized["initial_entry_count"] != binding["initial_entry_count"] or not _is_hash(synchronized["index_snapshot_id"]) or synchronized["index_identity"] != expected_index_identity ): @@ -489,7 +491,8 @@ def _validated_bootstrap_registry_binding( binding["schema"] != _BOOTSTRAP_REGISTRY_BINDING_SCHEMA or binding["mode"] != "new_empty_bootstrap" or binding["bootstrap_only"] is not True - or binding["initial_entry_count"] != 0 + or type(binding["initial_entry_count"]) is not int + or not 0 <= binding["initial_entry_count"] <= _MAX_BOOTSTRAP_ENTRY_COUNT or not _is_hash(binding["binding_hash"]) or binding["binding_hash"] != _canonical_hash(_without_hash(binding, "binding_hash")) diff --git a/mcp-tools/pyproject.toml b/mcp-tools/pyproject.toml index 01f8b8b..32a3f02 100644 --- a/mcp-tools/pyproject.toml +++ b/mcp-tools/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "2718lab-devkit-mcp" -version = "1.1.1" +version = "1.1.2" 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 6769234..684d617 100644 --- a/mcp-tools/tests/test_bugkiller_metadata.py +++ b/mcp-tools/tests/test_bugkiller_metadata.py @@ -34,7 +34,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.1", codex["version"]) + self.assertEqual("1.1.2", 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_primary_artifact.py b/mcp-tools/tests/test_primary_artifact.py index 6490d7f..aef516b 100644 --- a/mcp-tools/tests/test_primary_artifact.py +++ b/mcp-tools/tests/test_primary_artifact.py @@ -228,14 +228,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.1" + assert project["project"]["version"] == "1.1.2" 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.1"' in lock_text + assert 'version = "1.1.2"' in lock_text def test_two_builds_are_byte_identical_with_normalized_zip_metadata( diff --git a/mcp-tools/tests/test_project_bootstrap.py b/mcp-tools/tests/test_project_bootstrap.py index 9a91cfe..6f2b1d6 100644 --- a/mcp-tools/tests/test_project_bootstrap.py +++ b/mcp-tools/tests/test_project_bootstrap.py @@ -154,7 +154,7 @@ def test_current_new_empty_attestation_yields_only_bootstrap_registry_binding() @pytest.mark.parametrize( ("changes", "code"), [ - ({"initial_entry_count": 1}, "BOOTSTRAP_PROJECT_NOT_EMPTY"), + ({"initial_entry_count": -1}, "BOOTSTRAP_PROJECT_NOT_EMPTY"), ({"state": "indexed"}, "BOOTSTRAP_PROJECT_NOT_EMPTY"), ({"issued_at": _NOW - 121, "expires_at": _NOW - 1}, "BOOTSTRAP_ATTESTATION_STALE"), ], @@ -195,8 +195,11 @@ def test_bootstrap_transport_rejects_raw_path_extra_field_and_forgery() -> None: class _HostOperations: - def __init__(self, *, fail: str | None = None) -> None: + def __init__( + self, *, fail: str | None = None, initial_entry_count: int = 0 + ) -> None: self.fail = fail + self.initial_entry_count = initial_entry_count self.calls: list[tuple[str, str, str]] = [] def project_index_register( @@ -219,7 +222,7 @@ def project_index_sync( "workspace_id": workspace_id, "attested_input_snapshot_id": "sha256:" + "7" * 64, "initial_manifest_hash": "sha256:" + "5" * 64, - "initial_entry_count": 0, + "initial_entry_count": self.initial_entry_count, "index_snapshot_id": "sha256:" + "8" * 64, } result["index_identity"] = _canonical_hash( @@ -319,6 +322,36 @@ def test_bootstrap_transport_runs_only_register_then_sync_and_binds_receipt() -> assert "path" not in repr(receipt).lower() +def test_new_empty_bootstrap_accepts_attested_initial_entries() -> None: + registry_binding = _resolver().resolve_new_empty_bootstrap( + _binding(initial_entry_count=3) + ) + host = _HostOperations(initial_entry_count=3) + + receipt = ProjectIndexBootstrapTransport(host, clock=lambda: _NOW).execute( + registry_binding + ) + + assert receipt["workspace_id"] == registry_binding["workspace_id"] + assert receipt["initial_manifest_hash"] == registry_binding[ + "initial_manifest_hash" + ] + + +def test_bootstrap_transport_rejects_count_that_does_not_match_binding() -> None: + registry_binding = _resolver().resolve_new_empty_bootstrap( + _binding(initial_entry_count=3) + ) + host = _HostOperations(initial_entry_count=2) + + with pytest.raises(RelayRuntimeError) as rejected: + ProjectIndexBootstrapTransport(host, clock=lambda: _NOW).execute( + registry_binding + ) + + assert rejected.value.code == "BOOTSTRAP_IDENTITY_MISMATCH" + + def test_resolver_validates_full_binding_and_receipt_for_indexed_recompile() -> None: project_binding = _binding() resolver = _resolver() diff --git a/mcp-tools/uv.lock b/mcp-tools/uv.lock index f164ac4..06e5079 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.1" +version = "1.1.2" source = { virtual = "." } dependencies = [ { name = "mcp", extra = ["cli"] }, diff --git a/skills/code-atlas/references/status-contract.md b/skills/code-atlas/references/status-contract.md index 9b4fd77..229f794 100644 --- a/skills/code-atlas/references/status-contract.md +++ b/skills/code-atlas/references/status-contract.md @@ -3,6 +3,13 @@ Treat every Code Atlas status as a bounded next action, never as authority to broaden a task or query an external service. +The absence of a workspace id or first snapshot is not a Code Atlas status. In +a newly opened project, establish the first local snapshot with the bounded +`project_index_register` -> `project_index_sync` cold-start sequence before +asking for status or choosing a fallback. Existing project files do not make +that initialization unsafe. Only tool unavailability or a failed register/sync +permits the degraded path. + | Status | Action | | --- | --- | | `READY` | Use the local verified recipe within the assigned scope and verify it. | diff --git a/skills/fast-lane-routing/SKILL.md b/skills/fast-lane-routing/SKILL.md index 5d6838d..d60cc0e 100644 --- a/skills/fast-lane-routing/SKILL.md +++ b/skills/fast-lane-routing/SKILL.md @@ -9,6 +9,10 @@ Read a plan as a bounded host contract: model, effort, route, worktree, lease, context, and predecessor bindings are exact values, not defaults to infer. - A missing, altered, or unattested route is \`NO_SAFE_WORK\`. +- A missing first index snapshot in a newly opened project is a cold-start + precondition, not a route verdict. Follow `workflow-design`'s one-time + `project_index_register` -> `project_index_sync` sequence before compiling or + declaring a degraded mode. - Cross-session projection is host-owned; the manual never opens a session. - \`index_context\` is bounded evidence, not an invitation to poll or rescan. - Refill follows a validated terminal event only; retained work stays retained. diff --git a/skills/workflow-design/SKILL.md b/skills/workflow-design/SKILL.md index 202d83a..76abc34 100644 --- a/skills/workflow-design/SKILL.md +++ b/skills/workflow-design/SKILL.md @@ -9,6 +9,17 @@ Use Fast Lane as the default planning policy for scoped DevKit work. Record the scope, ambiguity, risk, independent write count, verification cost, blocker, and available capacity before choosing a lane. +For a newly opened project that has no known workspace id or first index +snapshot, initialize the local index before deciding that the plugin is +degraded: call `project_index_register` once with the current canonical project +root, then call `project_index_sync` once with the returned opaque workspace id. +A successful first snapshot is normal cold start, even when the project already +contains source, README, or configuration files. Do not call +`project_index_status` before the first sync and do not report +`DEGRADED_SKILL_ONLY` merely because a new project did not already have an +index. Degrade only when the MCP tool is unavailable or register/sync fails; +preserve that failure as evidence and do not guess an index identity. + - Keep writers disjoint; prewarm and audit roles are read-only. - Treat compiler-selected model, effort, worktree, lease, and context as host-owned facts rather than prose suggestions. From db6d645c99b1eaeacba5e1a271a1e893ef05372d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=93=80=E6=B4=9B=E8=8A=99?= <273111507+Ayleovelle@users.noreply.github.com> Date: Thu, 27 Aug 2026 05:21:40 +0800 Subject: [PATCH 2/2] fix: align Fast Lane bootstrap entry bound --- .../scripts/team_efficiency.py | 5 +++- .../tests/test_team_efficiency.py | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py index d687bc6..ce4ee28 100644 --- a/mcp-tools/devkit_fastlane/scripts/team_efficiency.py +++ b/mcp-tools/devkit_fastlane/scripts/team_efficiency.py @@ -23,6 +23,7 @@ MAX_WRITE_SCOPES = 32 MAX_STATUS_TASKS = 64 MAX_MANIFEST_UNITS = 16 +MAX_BOOTSTRAP_ENTRY_COUNT = 2**63 - 1 MAX_LIST_ITEMS = 32 MAX_GRAPH_NODES = 64 MAX_GRAPH_EDGES = 128 @@ -997,7 +998,9 @@ def _validated_project_binding(value: object) -> dict[str, Any]: raise ValueError("project binding attestation schema is unknown") if ( type(attestation_source["initial_entry_count"]) is not int - or not 0 <= attestation_source["initial_entry_count"] <= MAX_MANIFEST_UNITS + or not 0 + <= attestation_source["initial_entry_count"] + <= MAX_BOOTSTRAP_ENTRY_COUNT ): raise ValueError("project binding initial entry count is invalid") if ( diff --git a/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py b/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py index 440863d..afcd1fe 100644 --- a/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py +++ b/mcp-tools/devkit_fastlane/tests/test_team_efficiency.py @@ -2256,6 +2256,30 @@ def test_task2_shaped_new_empty_project_emits_descriptor_only_plan( self.assertNotIn(forbidden, serialized) self.assertNotIn(str(self.repo).casefold(), serialized) + def test_unindexed_project_with_more_than_manifest_units_can_bootstrap( + self, + ) -> None: + helper = load_efficiency() + request = self.fast_lane_schedule_request(helper) + now = int(datetime.now(UTC).timestamp()) + request["project_binding"] = self.new_empty_project_binding( + helper, + request, + issued_at=now - 10, + expires_at=now + 110, + initial_entry_count=helper.MAX_MANIFEST_UNITS + 1, + ) + + result = helper.compile_fast_lane( + request, + reasoning_effort="ultra", + enable=True, + ) + + self.assertEqual("BOOTSTRAP_INDEX_READY", result["decision_code"]) + self.assertEqual("bootstrap", result["status"]) + self.assertEqual(1, len(result["bootstrap_queue"])) + def test_unattested_or_invalid_new_empty_project_stays_fenced(self) -> None: helper = load_efficiency() now = int(datetime.now(UTC).timestamp())