@blxzer/cursor-trellis 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +45 -21
  3. package/dist/configurators/workflow.d.ts +1 -1
  4. package/dist/configurators/workflow.d.ts.map +1 -1
  5. package/dist/configurators/workflow.js +20 -9
  6. package/dist/configurators/workflow.js.map +1 -1
  7. package/dist/migrations/manifests/0.1.3.json +9 -0
  8. package/dist/migrations/manifests/0.1.4.json +9 -0
  9. package/dist/templates/cursor/rules/retrieval-routing.mdc +9 -7
  10. package/dist/templates/markdown/agents.md +1 -1
  11. package/dist/templates/markdown/index.d.ts +1 -0
  12. package/dist/templates/markdown/index.d.ts.map +1 -1
  13. package/dist/templates/markdown/index.js +1 -0
  14. package/dist/templates/markdown/index.js.map +1 -1
  15. package/dist/templates/markdown/prompts/run-semantic-slice-12.md.txt +13 -29
  16. package/dist/templates/markdown/spec/guides/code-reuse-thinking-guide.md.txt +0 -49
  17. package/dist/templates/markdown/spec/guides/cross-layer-thinking-guide.md.txt +0 -85
  18. package/dist/templates/markdown/spec/guides/cursor-semantic-compliance.md.txt +7 -21
  19. package/dist/templates/markdown/spec/guides/cursor-subagent-policy.md.txt +6 -10
  20. package/dist/templates/markdown/spec/guides/retrieval-daily-guide.md.txt +17 -18
  21. package/dist/templates/shared-hooks/inject-retrieval-plan.py +42 -23
  22. package/dist/templates/shared-hooks/inject-subagent-context.py +6 -2
  23. package/dist/templates/shared-hooks/session-start.py +17 -9
  24. package/dist/templates/trellis/index.d.ts +8 -4
  25. package/dist/templates/trellis/index.d.ts.map +1 -1
  26. package/dist/templates/trellis/index.js +13 -7
  27. package/dist/templates/trellis/index.js.map +1 -1
  28. package/dist/templates/trellis/scripts/common/codebase_retrieval_router.py +80 -31
  29. package/dist/templates/trellis/scripts/common/config.py +1 -1
  30. package/dist/templates/trellis/scripts/common/cursor_retrieval_env.py +92 -0
  31. package/dist/templates/trellis/scripts/common/parent_orchestration.py +27 -15
  32. package/dist/templates/trellis/scripts/common/retrieval_adapter_metadata.py +100 -9
  33. package/dist/templates/trellis/scripts/common/retrieval_agent_instructions.py +76 -31
  34. package/dist/templates/trellis/scripts/common/retrieval_tool_classification.py +18 -3
  35. package/dist/templates/trellis/scripts/common/semantic_plan_gate.py +19 -0
  36. package/dist/templates/trellis/scripts/common/smart_search_evidence.py +5 -2
  37. package/dist/templates/trellis/scripts/common/smart_search_resolve.py +28 -3
  38. package/dist/templates/trellis/scripts/common/task_gates.py +372 -73
  39. package/dist/templates/trellis/scripts/common/task_map.py +29 -1
  40. package/dist/templates/trellis/scripts/cursor_retrieval_probe.py +396 -0
  41. package/dist/templates/trellis/scripts/cursor_retrieval_probe_prompt.md +300 -0
  42. package/dist/templates/trellis/scripts/retrieval_probe_matrix_template.json +126 -0
  43. package/dist/templates/trellis/workflow.md +9 -53
  44. package/dist/utils/codebase-retrieval-router.d.ts +5 -0
  45. package/dist/utils/codebase-retrieval-router.d.ts.map +1 -1
  46. package/dist/utils/codebase-retrieval-router.js +48 -28
  47. package/dist/utils/codebase-retrieval-router.js.map +1 -1
  48. package/dist/utils/cursor-retrieval-env.d.ts +28 -0
  49. package/dist/utils/cursor-retrieval-env.d.ts.map +1 -0
  50. package/dist/utils/cursor-retrieval-env.js +89 -0
  51. package/dist/utils/cursor-retrieval-env.js.map +1 -0
  52. package/dist/utils/project-capabilities.d.ts.map +1 -1
  53. package/dist/utils/project-capabilities.js +22 -15
  54. package/dist/utils/project-capabilities.js.map +1 -1
  55. package/dist/utils/retrieval-agent-instructions.d.ts.map +1 -1
  56. package/dist/utils/retrieval-agent-instructions.js +37 -21
  57. package/dist/utils/retrieval-agent-instructions.js.map +1 -1
  58. package/dist/utils/retrieval-tool-classification.d.ts +2 -0
  59. package/dist/utils/retrieval-tool-classification.d.ts.map +1 -1
  60. package/dist/utils/retrieval-tool-classification.js +10 -2
  61. package/dist/utils/retrieval-tool-classification.js.map +1 -1
  62. package/dist/utils/semantic-plan-gate.d.ts +8 -0
  63. package/dist/utils/semantic-plan-gate.d.ts.map +1 -0
  64. package/dist/utils/semantic-plan-gate.js +42 -0
  65. package/dist/utils/semantic-plan-gate.js.map +1 -0
  66. package/package.json +5 -43
  67. package/bin/smart-search.js +0 -79
  68. package/dist/templates/trellis/scripts/aggregate_retrieval_telemetry.py +0 -342
  69. package/dist/templates/trellis/scripts/batch_plan_envelope.py +0 -105
  70. package/vendor/smart-search/LICENSE +0 -21
  71. package/vendor/smart-search/README.md +0 -412
  72. package/vendor/smart-search/README.zh-CN.md +0 -430
  73. package/vendor/smart-search/npm/bin/smart-search.js +0 -63
  74. package/vendor/smart-search/npm/scripts/postinstall.js +0 -87
  75. package/vendor/smart-search/npm/scripts/resolve-prerelease-version.js +0 -108
  76. package/vendor/smart-search/npm/scripts/set-package-version.js +0 -35
  77. package/vendor/smart-search/npm/scripts/sync-python-version.js +0 -22
  78. package/vendor/smart-search/npm/scripts/test-wrapper-repair.js +0 -99
  79. package/vendor/smart-search/npm/scripts/test.js +0 -85
  80. package/vendor/smart-search/package.json +0 -42
  81. package/vendor/smart-search/pyproject.toml +0 -37
  82. package/vendor/smart-search/skills/smart-search-cli/SKILL.md +0 -387
  83. package/vendor/smart-search/skills/smart-search-cli/agents/openai.yaml +0 -3
  84. package/vendor/smart-search/skills/smart-search-cli/examples/batch-search.md +0 -98
  85. package/vendor/smart-search/skills/smart-search-cli/examples/evidence-gathering.md +0 -89
  86. package/vendor/smart-search/skills/smart-search-cli/references/cli-contract.md +0 -337
  87. package/vendor/smart-search/src/smart_search/__init__.py +0 -1
  88. package/vendor/smart-search/src/smart_search/assets/skills/smart-search-cli/SKILL.md +0 -387
  89. package/vendor/smart-search/src/smart_search/assets/skills/smart-search-cli/agents/openai.yaml +0 -3
  90. package/vendor/smart-search/src/smart_search/assets/skills/smart-search-cli/examples/batch-search.md +0 -98
  91. package/vendor/smart-search/src/smart_search/assets/skills/smart-search-cli/examples/evidence-gathering.md +0 -89
  92. package/vendor/smart-search/src/smart_search/assets/skills/smart-search-cli/references/cli-contract.md +0 -337
  93. package/vendor/smart-search/src/smart_search/cli.py +0 -2003
  94. package/vendor/smart-search/src/smart_search/config.py +0 -580
  95. package/vendor/smart-search/src/smart_search/logger.py +0 -43
  96. package/vendor/smart-search/src/smart_search/providers/__init__.py +0 -16
  97. package/vendor/smart-search/src/smart_search/providers/base.py +0 -41
  98. package/vendor/smart-search/src/smart_search/providers/context7.py +0 -141
  99. package/vendor/smart-search/src/smart_search/providers/exa.py +0 -206
  100. package/vendor/smart-search/src/smart_search/providers/jina.py +0 -136
  101. package/vendor/smart-search/src/smart_search/providers/openai_compatible.py +0 -370
  102. package/vendor/smart-search/src/smart_search/providers/zhipu.py +0 -143
  103. package/vendor/smart-search/src/smart_search/service.py +0 -2865
  104. package/vendor/smart-search/src/smart_search/sources.py +0 -429
  105. package/vendor/smart-search/src/smart_search/utils.py +0 -220
@@ -2,8 +2,8 @@
2
2
  """
3
3
  Deterministic codebase retrieval intent router (Python template).
4
4
 
5
- Mirrors packages/cli/src/utils/codebase-retrieval-router.ts for dogfood and
6
- get_context retrieval-pack flows. Does not invoke rg, MCP, or network tools.
5
+ Shared contract with the Trellis CLI router; used by hooks, route_codebase_retrieval.py,
6
+ and get_context retrieval-pack flows. Does not invoke rg, MCP, or network tools.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
@@ -13,6 +13,12 @@ import re
13
13
  from pathlib import Path
14
14
  from typing import Any
15
15
 
16
+ from .cursor_retrieval_env import (
17
+ ENV_BYOK,
18
+ detect_cursor_retrieval_env,
19
+ semantic_route_spec,
20
+ )
21
+
16
22
  ROUTER_VERSION = 2
17
23
 
18
24
  PLATFORM_CURSOR = "cursor"
@@ -439,11 +445,32 @@ def _large_project(project_file_count: int | None) -> bool:
439
445
  return project_file_count > 2000
440
446
 
441
447
 
448
+ def _platform_semantic_route(
449
+ *,
450
+ base_rationale: str,
451
+ cursor_env: str,
452
+ ) -> dict[str, object]:
453
+ spec = semantic_route_spec(cursor_env)
454
+ suffix = str(spec.get("rationale_suffix", ""))
455
+ commands = spec.get("commands")
456
+ cmd_list = list(commands) if isinstance(commands, list) else []
457
+ return {
458
+ "id": "platform-semantic",
459
+ "role": "semantic",
460
+ "sourceFamily": "platform-semantic",
461
+ "commands": cmd_list,
462
+ "rationale": base_rationale + suffix,
463
+ "platformNative": bool(spec.get("platformNative", False)),
464
+ "semanticBackend": spec.get("semanticBackend"),
465
+ }
466
+
467
+
442
468
  def _ordered_routes(
443
469
  intents: list[dict[str, object]],
444
470
  include_optional_adapters: bool,
445
471
  *,
446
472
  project_file_count: int | None = None,
473
+ cursor_env: str | None = None,
447
474
  ) -> list[dict[str, object]]:
448
475
  ids = {str(item["id"]) for item in intents}
449
476
  active = list(ids)
@@ -459,6 +486,7 @@ def _ordered_routes(
459
486
  conceptual_primary = conceptual and not preserve and not exact_primary_first
460
487
  large = _large_project(project_file_count)
461
488
  semantic_promoted = False
489
+ cenv = cursor_env or detect_cursor_retrieval_env()
462
490
 
463
491
  routes: list[dict[str, object]] = []
464
492
 
@@ -542,14 +570,7 @@ def _ordered_routes(
542
570
  if policy
543
571
  else "Conceptual query without exact signals; semantic recall before exact rg narrowing."
544
572
  )
545
- append({
546
- "id": "platform-semantic",
547
- "role": "semantic",
548
- "sourceFamily": "platform-semantic",
549
- "commands": ["cursor @codebase or built-in semantic search"],
550
- "rationale": semantic_rationale + " (Cursor built-in semantic search)",
551
- "platformNative": True,
552
- })
573
+ append(_platform_semantic_route(base_rationale=semantic_rationale, cursor_env=cenv))
553
574
  semantic_promoted = True
554
575
  append({
555
576
  "id": "exact-rg-primary",
@@ -638,24 +659,25 @@ def _ordered_routes(
638
659
  })
639
660
  append({
640
661
  "id": "lsp-navigation",
641
- "role": "lsp",
642
- "sourceFamily": "language-server",
643
- "commands": ["definition", "references", "hover"],
662
+ "role": "ast",
663
+ "sourceFamily": "codegraph",
664
+ "commands": [
665
+ "codegraph_node <symbol> --includeCode",
666
+ "codegraph_search <symbol>",
667
+ ],
644
668
  "rationale": (
645
- "Optional editor/LSP navigation when exposed; "
646
- "not guaranteed in Agent tool telemetry prefer Read/Grep/codegraph after candidates exist."
669
+ "Definition/reference via codegraph (Cursor Agent does not expose "
670
+ "GO_TO_DEFINITION); corroborate with Read on returned line ranges."
647
671
  ),
648
- "platformNative": True,
672
+ "platformNative": False,
649
673
  })
650
674
  if not semantic_promoted:
651
- append({
652
- "id": "platform-semantic",
653
- "role": "semantic",
654
- "sourceFamily": "platform-semantic",
655
- "commands": ["cursor @codebase or built-in semantic search"],
656
- "rationale": "Semantic recall via Cursor built-in codebase search.",
657
- "platformNative": True,
658
- })
675
+ append(
676
+ _platform_semantic_route(
677
+ base_rationale="Semantic recall for conceptual narrowing.",
678
+ cursor_env=cenv,
679
+ )
680
+ )
659
681
 
660
682
  append({
661
683
  "id": "verification-source-git-tests",
@@ -678,6 +700,8 @@ def _fallback_hints(
678
700
  intents: list[dict[str, object]],
679
701
  include_optional_adapters: bool,
680
702
  routes: list[dict[str, object]],
703
+ *,
704
+ cursor_env: str | None = None,
681
705
  ) -> list[dict[str, object]]:
682
706
  hints: list[dict[str, object]] = [
683
707
  {
@@ -688,7 +712,22 @@ def _fallback_hints(
688
712
  if not include_optional_adapters:
689
713
  hints.append({
690
714
  "when": "codebase-retrieval not selected",
691
- "action": "Skip optional AST/LSP/semantic routes; use exact search and verification.",
715
+ "action": "Skip optional AST/semantic routes; use exact search and verification.",
716
+ "replacesRole": "semantic",
717
+ })
718
+ cenv = cursor_env or detect_cursor_retrieval_env()
719
+ if cenv == ENV_BYOK:
720
+ hints.append({
721
+ "when": "built-in @codebase / SemanticSearch not in agent tool list",
722
+ "action": (
723
+ "Use fast_context_search (fast-context MCP) per platform-semantic route; "
724
+ "do not use WebSearch for codebase questions."
725
+ ),
726
+ "replacesRole": "semantic",
727
+ })
728
+ hints.append({
729
+ "when": "DEEP_SEARCH not available for wide cross-cutting explore",
730
+ "action": "Use Task subagent (explore), then Grep/codegraph/Read to verify.",
692
731
  "replacesRole": "semantic",
693
732
  })
694
733
  if any(str(item["id"]) == INTENT_POLICY for item in intents):
@@ -716,8 +755,8 @@ def _fallback_hints(
716
755
  "(or only trap hits) before final Top-1"
717
756
  ),
718
757
  "action": (
719
- "Use Cursor @codebase or built-in semantic search per platform-semantic route, "
720
- "then narrow with rg on returned keywords and paths."
758
+ "Use fast_context_search (BYOK) or Cursor built-in semantic search (Native) "
759
+ "per platform-semantic route, then narrow with rg on returned keywords and paths."
721
760
  ),
722
761
  "replacesRole": "semantic",
723
762
  })
@@ -754,26 +793,33 @@ def route_codebase_retrieval(
754
793
  *,
755
794
  codebase_retrieval_selected: bool = True,
756
795
  project_file_count: int | None = None,
796
+ cursor_env: str | None = None,
757
797
  ) -> dict[str, object]:
758
798
  """Return the shared evidence envelope with router-owned fields populated."""
759
799
  normalized = _normalize_query(query)
760
800
  include_optional = codebase_retrieval_selected
801
+ cenv = cursor_env or detect_cursor_retrieval_env()
761
802
  if not normalized:
762
803
  intents = [
763
804
  _intent(INTENT_EXACT, "General codebase (exact baseline)", ["empty-query"], "low", True)
764
805
  ]
765
806
  empty_routes = _ordered_routes(
766
- intents, include_optional,
807
+ intents,
808
+ include_optional,
767
809
  project_file_count=project_file_count,
810
+ cursor_env=cenv,
768
811
  )
769
812
  return {
770
813
  "version": ROUTER_VERSION,
771
814
  "query": normalized,
815
+ "cursorEnv": cenv,
772
816
  "intents": intents,
773
817
  "routes": empty_routes,
774
818
  "adapterState": [],
775
819
  "freshness": [],
776
- "fallback": _fallback_hints(intents, include_optional, empty_routes),
820
+ "fallback": _fallback_hints(
821
+ intents, include_optional, empty_routes, cursor_env=cenv
822
+ ),
777
823
  "warnings": ["Empty query; only baseline exact route is emitted."],
778
824
  "verification": _base_verification(),
779
825
  "projectFileCount": project_file_count,
@@ -781,17 +827,20 @@ def route_codebase_retrieval(
781
827
 
782
828
  intents = _classify_intents(normalized)
783
829
  routes = _ordered_routes(
784
- intents, include_optional,
830
+ intents,
831
+ include_optional,
785
832
  project_file_count=project_file_count,
833
+ cursor_env=cenv,
786
834
  )
787
835
  return {
788
836
  "version": ROUTER_VERSION,
789
837
  "query": normalized,
838
+ "cursorEnv": cenv,
790
839
  "intents": intents,
791
840
  "routes": routes,
792
841
  "adapterState": [],
793
842
  "freshness": [],
794
- "fallback": _fallback_hints(intents, include_optional, routes),
843
+ "fallback": _fallback_hints(intents, include_optional, routes, cursor_env=cenv),
795
844
  "warnings": _warnings(intents),
796
845
  "verification": _verification_for_intents(intents),
797
846
  "projectFileCount": project_file_count,
@@ -289,7 +289,7 @@ def get_packages(repo_root: Path | None = None) -> dict[str, dict] | None:
289
289
  or None if not configured (single-repo mode).
290
290
 
291
291
  Example return:
292
- {"cli": {"path": "packages/cli"}, "docs-site": {"path": "docs-site", "type": "submodule"}}
292
+ {"api": {"path": "packages/api"}, "web": {"path": "packages/web"}}
293
293
  """
294
294
  config = _load_config(repo_root)
295
295
  packages = config.get("packages")
@@ -0,0 +1,92 @@
1
+ """Detect Cursor Native vs Cursor++ BYOK for retrieval routing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ ENV_NATIVE = "native"
11
+ ENV_BYOK = "byok"
12
+ ENV_UNKNOWN = "unknown"
13
+
14
+ CursorRetrievalEnv = str # native | byok | unknown
15
+
16
+
17
+ def ccursor_home() -> Path:
18
+ env = os.environ.get("TRELLIS_CCURSOR_HOME", "").strip()
19
+ if env:
20
+ return Path(env).expanduser()
21
+ return Path.home() / ".ccursor"
22
+
23
+
24
+ def detect_cursor_retrieval_env() -> CursorRetrievalEnv:
25
+ """Return native | byok | unknown.
26
+
27
+ Override: TRELLIS_CURSOR_BYOK=1|0
28
+ """
29
+ info = detect_cursor_retrieval_env_info()
30
+ env = info.get("env")
31
+ if env in (ENV_NATIVE, ENV_BYOK, ENV_UNKNOWN):
32
+ return str(env)
33
+ return ENV_UNKNOWN
34
+
35
+
36
+ def detect_cursor_retrieval_env_info() -> dict[str, Any]:
37
+ """Full detection record (for probes and envelopes)."""
38
+ env_override = os.environ.get("TRELLIS_CURSOR_BYOK", "").strip()
39
+ if env_override == "1":
40
+ return {"env": ENV_BYOK, "source": "env-override", "byokMode": 1}
41
+ if env_override == "0":
42
+ return {"env": ENV_NATIVE, "source": "env-override", "byokMode": 0}
43
+
44
+ routes_path = ccursor_home() / "routes.json"
45
+ if not routes_path.is_file():
46
+ return {
47
+ "env": ENV_UNKNOWN,
48
+ "source": "routes-not-found",
49
+ "byokMode": None,
50
+ "routes_path": str(routes_path),
51
+ }
52
+ try:
53
+ routes = json.loads(routes_path.read_text(encoding="utf-8"))
54
+ except (OSError, json.JSONDecodeError) as exc:
55
+ return {
56
+ "env": ENV_UNKNOWN,
57
+ "source": "routes-parse-error",
58
+ "byokMode": None,
59
+ "error": str(exc),
60
+ "routes_path": str(routes_path),
61
+ }
62
+ byok = routes.get("byokMode")
63
+ env = ENV_BYOK if byok == 1 else ENV_NATIVE if byok == 0 else ENV_UNKNOWN
64
+ redirect = routes.get("redirect", [])
65
+ return {
66
+ "env": env,
67
+ "source": "routes.json",
68
+ "byokMode": byok,
69
+ "routes_path": str(routes_path),
70
+ "redirect_endpoints": list(redirect) if isinstance(redirect, list) else [],
71
+ }
72
+
73
+
74
+ def is_byok(env: CursorRetrievalEnv | None = None) -> bool:
75
+ return (env or detect_cursor_retrieval_env()) == ENV_BYOK
76
+
77
+
78
+ def semantic_route_spec(cursor_env: CursorRetrievalEnv) -> dict[str, object]:
79
+ """platform-semantic route fields for conceptual / optional semantic slots."""
80
+ if cursor_env == ENV_BYOK:
81
+ return {
82
+ "commands": ["fast_context_search (fast-context MCP)"],
83
+ "rationale_suffix": " Cursor++ BYOK: built-in semantic unavailable; fast-context MCP primary.",
84
+ "platformNative": False,
85
+ "semanticBackend": "fast-context-mcp",
86
+ }
87
+ return {
88
+ "commands": ["cursor @codebase or built-in semantic search"],
89
+ "rationale_suffix": " Cursor built-in semantic search.",
90
+ "platformNative": True,
91
+ "semanticBackend": "cursor-builtin",
92
+ }
@@ -16,6 +16,7 @@ from .paths import get_repo_root
16
16
  from .task_gates import (
17
17
  build_spec_update_scaffold,
18
18
  durable_learning_decision_status,
19
+ task_closeout_profile,
19
20
  )
20
21
  from .task_map import (
21
22
  PARENT_TERMINAL_STATES,
@@ -31,7 +32,6 @@ _GLOBAL_CHILD_CONSTRAINTS = """General constraints:
31
32
  - Stay inside your child scope and declared touch areas.
32
33
  - Do not publish packages, push remotes, create tags, edit credentials, or change global/MCP configuration.
33
34
  - Preserve explicit user approval for destructive, remote, credential-bearing, publish, push, tag, and global configuration actions.
34
- - Keep template and dogfood copies synchronized when touching `.trellis/scripts` or Trellis CLI templates.
35
35
  - Write `verify.md` and `handoff.md` in your child task directory.
36
36
  - Handoff must include changed files, validation commands/results, residual risks, and parent integration notes.
37
37
  - If you find a needed contract change, stop and report it to the parent instead of silently redefining shared state/gate semantics.
@@ -530,18 +530,29 @@ def build_review_report(
530
530
  ),
531
531
  }
532
532
  )
533
- actions["gates"].append(
534
- {
535
- "task": "child",
536
- "transition": "child-review",
537
- "optional": True,
538
- "hint": (
539
- f"python ./.trellis/scripts/task.py record-gate {child_name} "
540
- f"--transition child-review --gate code-review --result PASS "
541
- f"--reviewer parent --evidence verify.md"
542
- ),
543
- }
544
- )
533
+ child_profile = task_closeout_profile(child_dir, child_data)
534
+ if child_profile == "full":
535
+ actions["gates"].append(
536
+ {
537
+ "task": "child",
538
+ "transition": "child-review",
539
+ "optional": False,
540
+ "hint": (
541
+ f"Required before accept: python ./.trellis/scripts/task.py record-gate {child_name} "
542
+ f"--transition child-review --gate code-review --result PASS "
543
+ f"--reviewer parent --evidence verify.md"
544
+ ),
545
+ }
546
+ )
547
+ elif child_profile == "lite":
548
+ actions["gates"].append(
549
+ {
550
+ "task": "child",
551
+ "transition": "child-review",
552
+ "optional": True,
553
+ "hint": "Lite child: no child-review gate chain required.",
554
+ }
555
+ )
545
556
  elif decision == "changes":
546
557
  target = "changes"
547
558
  errors.extend(
@@ -611,6 +622,7 @@ def build_review_report(
611
622
  evidence,
612
623
  ref,
613
624
  reason,
625
+ current_state_override=sim_state,
614
626
  )
615
627
  errors.extend(step_errors)
616
628
  if step_errors:
@@ -626,9 +638,9 @@ def build_review_report(
626
638
  {
627
639
  "task": "parent",
628
640
  "transition": "parent-integrated",
629
- "optional": True,
641
+ "optional": False,
630
642
  "hint": (
631
- f"python ./.trellis/scripts/task.py record-gate {parent_dir.name} "
643
+ f"Required before parent archive: python ./.trellis/scripts/task.py record-gate {parent_dir.name} "
632
644
  f"--transition parent-integrated --gate integration-review --result PASS "
633
645
  f"--reviewer parent --evidence task-map.md"
634
646
  ),
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
  from datetime import datetime, timezone
12
12
  from typing import Any
13
13
 
14
+ from common.cursor_retrieval_env import ENV_BYOK, detect_cursor_retrieval_env
14
15
  from common.retrieval_evidence import (
15
16
  SOURCE_ARTIFACT_SEARCH,
16
17
  SOURCE_CODEBASE_EVIDENCE,
@@ -71,7 +72,6 @@ REQUIRED_ADAPTERS = frozenset({ADAPTER_RG, ADAPTER_VERIFICATION})
71
72
  OPTIONAL_INTEGRATION_ADAPTERS = (
72
73
  ADAPTER_CODEGRAPH,
73
74
  ADAPTER_LSP,
74
- ADAPTER_FAST_CONTEXT,
75
75
  ADAPTER_MCP,
76
76
  ADAPTER_BROWSER,
77
77
  ADAPTER_NETWORK,
@@ -236,15 +236,11 @@ def build_adapter_states(
236
236
  optional_integration_adapter_state(adapter=adapter, hints=hints)
237
237
  )
238
238
 
239
- # Platform-semantic: Cursor built-in semantic search (replaces fast-context)
240
239
  states.append(
241
- adapter_state_entry(
242
- adapter=ADAPTER_PLATFORM_SEMANTIC,
243
- state=STATE_AVAILABLE,
244
- required=False,
245
- invoked=False,
246
- reason="Platform-native semantic search on Cursor (e.g. @codebase); supersedes fast-context",
247
- )
240
+ platform_semantic_adapter_state(router=router, hints=hints)
241
+ )
242
+ states.append(
243
+ fast_context_adapter_state(router=router, hints=hints)
248
244
  )
249
245
 
250
246
  states.append(
@@ -441,6 +437,101 @@ def codebase_adapter_state(
441
437
  )
442
438
 
443
439
 
440
+ def resolve_cursor_env_for_adapter_metadata(
441
+ router: dict[str, Any] | None,
442
+ ) -> str:
443
+ """native | byok | unknown for retrieval-pack adapter reasons."""
444
+ router = router or {}
445
+ env = string_value(router.get("cursorEnv"))
446
+ if env in ("native", "byok", "unknown"):
447
+ return env
448
+ return detect_cursor_retrieval_env()
449
+
450
+
451
+ def platform_semantic_adapter_state(
452
+ *,
453
+ router: dict[str, Any] | None,
454
+ hints: dict[str, dict[str, Any]],
455
+ ) -> dict[str, object]:
456
+ hint = hints.get(ADAPTER_PLATFORM_SEMANTIC, {})
457
+ if hint:
458
+ return adapter_state_entry(
459
+ adapter=ADAPTER_PLATFORM_SEMANTIC,
460
+ state=resolve_hint_state(hints, ADAPTER_PLATFORM_SEMANTIC, default=STATE_SKIPPED),
461
+ required=False,
462
+ invoked=bool(hint.get("invoked")),
463
+ reason=string_value(hint.get("reason")) or "platform-semantic adapter hint",
464
+ )
465
+ env = resolve_cursor_env_for_adapter_metadata(router)
466
+ if env == ENV_BYOK:
467
+ reason = (
468
+ "Cursor++ BYOK: built-in codebase semantic often absent (Experiment D); "
469
+ "concept recall Primary is fast-context MCP per router cursorEnv"
470
+ )
471
+ state = STATE_UNAVAILABLE
472
+ elif env == "native":
473
+ reason = (
474
+ "Cursor Native: built-in codebase semantic (e.g. SemanticSearch / @codebase) "
475
+ "is Primary for concept recall"
476
+ )
477
+ state = STATE_AVAILABLE
478
+ else:
479
+ reason = (
480
+ "Cursor platform-semantic: Native uses built-in search; BYOK uses fast-context "
481
+ "when cursorEnv is byok (see cursor_retrieval_env)"
482
+ )
483
+ state = STATE_UNVERIFIED
484
+ return adapter_state_entry(
485
+ adapter=ADAPTER_PLATFORM_SEMANTIC,
486
+ state=state,
487
+ required=False,
488
+ invoked=False,
489
+ reason=reason,
490
+ )
491
+
492
+
493
+ def fast_context_adapter_state(
494
+ *,
495
+ router: dict[str, Any] | None,
496
+ hints: dict[str, dict[str, Any]],
497
+ ) -> dict[str, object]:
498
+ hint = hints.get(ADAPTER_FAST_CONTEXT, {})
499
+ if hint:
500
+ return adapter_state_entry(
501
+ adapter=ADAPTER_FAST_CONTEXT,
502
+ state=resolve_hint_state(hints, ADAPTER_FAST_CONTEXT, default=STATE_SKIPPED),
503
+ required=False,
504
+ invoked=bool(hint.get("invoked")),
505
+ reason=string_value(hint.get("reason")) or "fast-context-mcp adapter hint",
506
+ )
507
+ env = resolve_cursor_env_for_adapter_metadata(router)
508
+ if env == ENV_BYOK:
509
+ reason = (
510
+ "Cursor++ BYOK: fast_context_search is compliant Primary for concept recall "
511
+ "(select codebase-retrieval at init for .cursor/mcp.json fast-context entry)"
512
+ )
513
+ state = STATE_AVAILABLE
514
+ elif env == "native":
515
+ reason = (
516
+ "Cursor Native: prefer built-in platform-semantic; fast-context is optional "
517
+ "and misuse when plan requires native semantic"
518
+ )
519
+ state = STATE_SKIPPED
520
+ else:
521
+ reason = (
522
+ "fast-context MCP: required for BYOK concept recall on Cursor when "
523
+ "codebase-retrieval capability is selected"
524
+ )
525
+ state = STATE_UNVERIFIED
526
+ return adapter_state_entry(
527
+ adapter=ADAPTER_FAST_CONTEXT,
528
+ state=state,
529
+ required=False,
530
+ invoked=False,
531
+ reason=reason,
532
+ )
533
+
534
+
444
535
  def optional_integration_adapter_state(
445
536
  *,
446
537
  adapter: str,