@tencent-rtc/trtc-agent-skills 0.1.0

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 (205) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/README.zh.md +173 -0
  4. package/bin/cli.js +434 -0
  5. package/knowledge-base/index.yaml +454 -0
  6. package/knowledge-base/platform-slice-template.md +233 -0
  7. package/knowledge-base/scenario-spec.md +350 -0
  8. package/knowledge-base/scenarios/conference/base/general-conference.md +365 -0
  9. package/knowledge-base/scenarios/conference/base/webinar-conference.md +130 -0
  10. package/knowledge-base/scenarios/conference/medical/1v1-video-consultation.md +145 -0
  11. package/knowledge-base/scenarios/conference/medical/medical-multidoctor-consultation.md +113 -0
  12. package/knowledge-base/scenarios/live/entertainment-live-room.md +118 -0
  13. package/knowledge-base/slice-spec.md +546 -0
  14. package/knowledge-base/slices/conference/web/ai-tools.md +225 -0
  15. package/knowledge-base/slices/conference/web/beauty-effects.md +188 -0
  16. package/knowledge-base/slices/conference/web/device-control.md +338 -0
  17. package/knowledge-base/slices/conference/web/login-auth.md +261 -0
  18. package/knowledge-base/slices/conference/web/network-quality.md +190 -0
  19. package/knowledge-base/slices/conference/web/official-roomkit-api.md +298 -0
  20. package/knowledge-base/slices/conference/web/official-roomkit-login-ui.md +246 -0
  21. package/knowledge-base/slices/conference/web/participant-list.md +238 -0
  22. package/knowledge-base/slices/conference/web/participant-management.md +718 -0
  23. package/knowledge-base/slices/conference/web/prejoin-check.md +293 -0
  24. package/knowledge-base/slices/conference/web/room-call.md +213 -0
  25. package/knowledge-base/slices/conference/web/room-chat.md +426 -0
  26. package/knowledge-base/slices/conference/web/room-lifecycle.md +534 -0
  27. package/knowledge-base/slices/conference/web/room-schedule.md +281 -0
  28. package/knowledge-base/slices/conference/web/screen-share.md +211 -0
  29. package/knowledge-base/slices/conference/web/video-layout.md +675 -0
  30. package/knowledge-base/slices/conference/web/virtual-background.md +197 -0
  31. package/knowledge-base/slices/conference/web/webinar-interaction.md +206 -0
  32. package/knowledge-base/slices/live/anchor-lifecycle.md +122 -0
  33. package/knowledge-base/slices/live/anchor-preview.md +90 -0
  34. package/knowledge-base/slices/live/anchor-room-config.md +104 -0
  35. package/knowledge-base/slices/live/audience-list.md +86 -0
  36. package/knowledge-base/slices/live/audience-manage.md +92 -0
  37. package/knowledge-base/slices/live/audience-watch.md +85 -0
  38. package/knowledge-base/slices/live/audio.md +116 -0
  39. package/knowledge-base/slices/live/barrage.md +88 -0
  40. package/knowledge-base/slices/live/beauty.md +99 -0
  41. package/knowledge-base/slices/live/coguest-apply.md +105 -0
  42. package/knowledge-base/slices/live/device-control.md +91 -0
  43. package/knowledge-base/slices/live/error-codes.md +167 -0
  44. package/knowledge-base/slices/live/gift.md +84 -0
  45. package/knowledge-base/slices/live/ios/.gitkeep +0 -0
  46. package/knowledge-base/slices/live/ios/anchor-lifecycle.md +313 -0
  47. package/knowledge-base/slices/live/ios/anchor-preview.md +228 -0
  48. package/knowledge-base/slices/live/ios/anchor-room-config.md +257 -0
  49. package/knowledge-base/slices/live/ios/audience-list.md +353 -0
  50. package/knowledge-base/slices/live/ios/audience-manage.md +381 -0
  51. package/knowledge-base/slices/live/ios/audience-watch.md +286 -0
  52. package/knowledge-base/slices/live/ios/audio.md +373 -0
  53. package/knowledge-base/slices/live/ios/barrage.md +285 -0
  54. package/knowledge-base/slices/live/ios/beauty.md +323 -0
  55. package/knowledge-base/slices/live/ios/coguest-apply.md +506 -0
  56. package/knowledge-base/slices/live/ios/device-control.md +286 -0
  57. package/knowledge-base/slices/live/ios/error-codes.md +270 -0
  58. package/knowledge-base/slices/live/ios/gift.md +315 -0
  59. package/knowledge-base/slices/live/ios/live-list.md +269 -0
  60. package/knowledge-base/slices/live/ios/login-auth.md +247 -0
  61. package/knowledge-base/slices/live/live-list.md +82 -0
  62. package/knowledge-base/slices/live/login-auth.md +78 -0
  63. package/package.json +34 -0
  64. package/skills/trtc/SKILL.md +326 -0
  65. package/skills/trtc/room-builder/SKILL.md +138 -0
  66. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/README.md +108 -0
  67. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/backend-contract.zh-CN.md +162 -0
  68. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/integration.zh-CN.md +154 -0
  69. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/docs/theme.zh-CN.md +78 -0
  70. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/index.html +12 -0
  71. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/package.json +28 -0
  72. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/postcss.config.js +5 -0
  73. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/App.vue +25 -0
  74. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/ConsultationManagePanel.vue +838 -0
  75. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LanguageSwitch.vue +102 -0
  76. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/LoadingSpinner.vue +6 -0
  77. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalAlert.vue +34 -0
  78. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalBusinessPanel.vue +148 -0
  79. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalButton.vue +49 -0
  80. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalConfirmDialog.vue +68 -0
  81. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalDataPanel.vue +196 -0
  82. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/MedicalRecordPanel.vue +270 -0
  83. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/components/PrescriptionPanel.vue +363 -0
  84. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/basic-info-config.ts +29 -0
  85. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.d.ts +4 -0
  86. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/lib-generate-test-usersig-es.min.js +2 -0
  87. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/config/runtime-config.ts +12 -0
  88. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/env.d.ts +32 -0
  89. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationChatPanel.vue +123 -0
  90. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationMembersPanel.vue +230 -0
  91. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationTranscriptionPanel.vue +135 -0
  92. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/ConsultationVideoStage.vue +113 -0
  93. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/InviteDoctorDialog.vue +132 -0
  94. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/components/KickMemberConfirmDialog.vue +50 -0
  95. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/types.ts +77 -0
  96. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationChat.ts +97 -0
  97. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationDevices.ts +48 -0
  98. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationParticipants.ts +121 -0
  99. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/useConsultationPermissions.ts +25 -0
  100. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/features/consultation/utils.ts +70 -0
  101. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/en-US/index.ts +553 -0
  102. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/index.ts +25 -0
  103. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/medicalTranslate.ts +85 -0
  104. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/state.ts +49 -0
  105. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/i18n/zh-CN/index.ts +463 -0
  106. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/main.ts +12 -0
  107. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/appointments.ts +96 -0
  108. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/mock/users.ts +79 -0
  109. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/router/index.ts +63 -0
  110. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/index.ts +25 -0
  111. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/appointmentService.ts +77 -0
  112. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/authService.ts +38 -0
  113. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/launchContext.ts +31 -0
  114. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/integration/userService.ts +35 -0
  115. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/appointmentService.ts +43 -0
  116. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/authService.ts +33 -0
  117. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/mock/userService.ts +43 -0
  118. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/services/adapters/types.ts +135 -0
  119. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/shared/icons.ts +53 -0
  120. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/index.css +106 -0
  121. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/tailwind.css +3 -0
  122. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/styles/theme.css +209 -0
  123. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/auth.ts +50 -0
  124. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/format.ts +24 -0
  125. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/navigation.ts +12 -0
  126. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/utils/session.ts +28 -0
  127. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorConsultationView.vue +777 -0
  128. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/DoctorDashboardView.vue +678 -0
  129. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/LoginView.vue +441 -0
  130. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationFinishedView.vue +185 -0
  131. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientConsultationView.vue +1003 -0
  132. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientSelectDoctorView.vue +317 -0
  133. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/src/views/PatientWaitingView.vue +454 -0
  134. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.json +21 -0
  135. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/tsconfig.node.json +8 -0
  136. package/skills/trtc/room-builder/templates/scenarios/medical-consultation/vite.config.ts +17 -0
  137. package/skills/trtc/room-builder/templates/scenarios/medical-consultation//346/216/245/345/205/245/350/257/264/346/230/216.md +6 -0
  138. package/skills/trtc/room-builder/tools/render_ai_instructions.py +226 -0
  139. package/skills/trtc-apply/SKILL.md +97 -0
  140. package/skills/trtc-apply/guardrails/apply_lib/__init__.py +0 -0
  141. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/__init__.cpython-313.pyc +0 -0
  142. package/skills/trtc-apply/guardrails/apply_lib/__pycache__/rule_parser.cpython-313.pyc +0 -0
  143. package/skills/trtc-apply/guardrails/apply_lib/rule_parser.py +268 -0
  144. package/skills/trtc-docs/SKILL.md +207 -0
  145. package/skills/trtc-onboarding/SKILL.md +839 -0
  146. package/skills/trtc-onboarding/reference/path-a1-demo.md +103 -0
  147. package/skills/trtc-onboarding/reference/path-a2-integrate.md +693 -0
  148. package/skills/trtc-onboarding/reference/path-b-troubleshoot.md +115 -0
  149. package/skills/trtc-onboarding/reference/path-c-expand.md +43 -0
  150. package/skills/trtc-onboarding/reference/reporting-protocol.md +174 -0
  151. package/skills/trtc-onboarding/reference/supported-matrix.md +100 -0
  152. package/skills/trtc-onboarding/reference/usersig-handling.md +140 -0
  153. package/skills/trtc-search/SKILL.md +221 -0
  154. package/skills/trtc-topic/SKILL.md +638 -0
  155. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_read.cpython-313.pyc +0 -0
  156. package/skills/trtc-topic/guardrails/__pycache__/gate_slice_write.cpython-313.pyc +0 -0
  157. package/skills/trtc-topic/guardrails/__pycache__/stop_require_apply_evidence.cpython-313.pyc +0 -0
  158. package/skills/trtc-topic/guardrails/gate_slice_read.py +133 -0
  159. package/skills/trtc-topic/guardrails/gate_slice_write.py +169 -0
  160. package/skills/trtc-topic/guardrails/stop_require_apply_evidence.py +97 -0
  161. package/skills/trtc-topic/references/execution-units.yaml +58 -0
  162. package/skills/trtc-topic/runtime/README.md +50 -0
  163. package/skills/trtc-topic/runtime/RUNTIME.md +128 -0
  164. package/skills/trtc-topic/runtime/lib/__init__.py +0 -0
  165. package/skills/trtc-topic/runtime/lib/platforms.py +194 -0
  166. package/skills/trtc-topic/runtime/package-lock.json +1211 -0
  167. package/skills/trtc-topic/runtime/package.json +13 -0
  168. package/skills/trtc-topic/runtime/telemetry-bridge.mjs +339 -0
  169. package/skills/trtc-topic/runtime/telemetry_collector.py +293 -0
  170. package/skills/trtc-topic/scripts/STATE-MACHINE-GUIDE.md +186 -0
  171. package/skills/trtc-topic/scripts/__pycache__/apply.cpython-313.pyc +0 -0
  172. package/skills/trtc-topic/scripts/apply.py +581 -0
  173. package/skills/trtc-topic/scripts/finalize_session.py +113 -0
  174. package/skills/trtc-topic/scripts/init_slice_queue.py +96 -0
  175. package/skills/trtc-topic/scripts/lib/__pycache__/state_machine.cpython-313.pyc +0 -0
  176. package/skills/trtc-topic/scripts/lib/state_machine.py +328 -0
  177. package/skills/trtc-topic/scripts/next_slice.py +137 -0
  178. package/skills/trtc-topic/tests/README.md +70 -0
  179. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.2.pyc +0 -0
  180. package/skills/trtc-topic/tests/__pycache__/conftest.cpython-313-pytest-9.0.3.pyc +0 -0
  181. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.2.pyc +0 -0
  182. package/skills/trtc-topic/tests/__pycache__/test_apply_cli.cpython-313-pytest-9.0.3.pyc +0 -0
  183. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.2.pyc +0 -0
  184. package/skills/trtc-topic/tests/__pycache__/test_end_to_end.cpython-313-pytest-9.0.3.pyc +0 -0
  185. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.2.pyc +0 -0
  186. package/skills/trtc-topic/tests/__pycache__/test_finalize_session.cpython-313-pytest-9.0.3.pyc +0 -0
  187. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.2.pyc +0 -0
  188. package/skills/trtc-topic/tests/__pycache__/test_gates.cpython-313-pytest-9.0.3.pyc +0 -0
  189. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.2.pyc +0 -0
  190. package/skills/trtc-topic/tests/__pycache__/test_session_resolver.cpython-313-pytest-9.0.3.pyc +0 -0
  191. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.2.pyc +0 -0
  192. package/skills/trtc-topic/tests/__pycache__/test_state_machine.cpython-313-pytest-9.0.3.pyc +0 -0
  193. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.2.pyc +0 -0
  194. package/skills/trtc-topic/tests/__pycache__/test_stop_require_apply.cpython-313-pytest-9.0.3.pyc +0 -0
  195. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.2.pyc +0 -0
  196. package/skills/trtc-topic/tests/__pycache__/test_topic_skill_invariants.cpython-313-pytest-9.0.3.pyc +0 -0
  197. package/skills/trtc-topic/tests/conftest.py +72 -0
  198. package/skills/trtc-topic/tests/test_apply_cli.py +480 -0
  199. package/skills/trtc-topic/tests/test_end_to_end.py +305 -0
  200. package/skills/trtc-topic/tests/test_finalize_session.py +51 -0
  201. package/skills/trtc-topic/tests/test_gates.py +316 -0
  202. package/skills/trtc-topic/tests/test_session_resolver.py +260 -0
  203. package/skills/trtc-topic/tests/test_state_machine.py +414 -0
  204. package/skills/trtc-topic/tests/test_stop_require_apply.py +99 -0
  205. package/skills/trtc-topic/tests/test_topic_skill_invariants.py +130 -0
@@ -0,0 +1,72 @@
1
+ """pytest fixtures for the topic state-machine and gate hooks.
2
+
3
+ These fixtures only know about session files and a simulated project root.
4
+ They never touch the real repo session.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+
10
+ import pytest
11
+ import yaml
12
+
13
+
14
+ # Default confirmed_plan used by most tests — mirrors the general-conference
15
+ # minimal scope (P0).
16
+ DEFAULT_CONFIRMED_PLAN = [
17
+ "conference/login-auth",
18
+ "conference/room-lifecycle",
19
+ "conference/participant-list",
20
+ "conference/video-layout",
21
+ "conference/device-control",
22
+ "conference/network-quality",
23
+ ]
24
+
25
+
26
+ @pytest.fixture
27
+ def session_factory(tmp_path: Path):
28
+ """Factory that writes a `.trtc-session.yaml` into tmp_path.
29
+
30
+ Returns a callable: ``make_session(**overrides) -> Path``.
31
+ Defaults are picked to match a typical mid-integration general-conference/web
32
+ session so individual tests only override what they care about.
33
+ """
34
+
35
+ def _make(**overrides) -> Path:
36
+ base = {
37
+ "schema_version": 1,
38
+ "status": "active",
39
+ "product": "conference",
40
+ "platform": "web",
41
+ "intent": "integrate-scenario",
42
+ "scenario": "general-conference",
43
+ "ui_mode": None,
44
+ "current_step": "topic-handoff",
45
+ "confirmed_plan": list(DEFAULT_CONFIRMED_PLAN),
46
+ "completed_steps": [],
47
+ "project_state": {
48
+ "project_root": str(tmp_path / "user-project"),
49
+ },
50
+ }
51
+ base.update(overrides)
52
+ path = tmp_path / ".trtc-session.yaml"
53
+ path.write_text(yaml.safe_dump(base, sort_keys=False, allow_unicode=True))
54
+ return path
55
+
56
+ return _make
57
+
58
+
59
+ @pytest.fixture
60
+ def project_factory(tmp_path: Path):
61
+ """Factory that creates an empty user project skeleton at tmp_path/user-project.
62
+
63
+ Returns the project root Path. Tests can write whatever Vue/TS files they
64
+ need into it.
65
+ """
66
+
67
+ def _make() -> Path:
68
+ root = tmp_path / "user-project"
69
+ (root / "src").mkdir(parents=True, exist_ok=True)
70
+ return root
71
+
72
+ return _make
@@ -0,0 +1,480 @@
1
+ """Tests for apply.py — the executable apply gate.
2
+
3
+ Contract:
4
+
5
+ apply.py --slice <slice_id> [--session PATH] [--project PATH]
6
+
7
+ Checks that the project has real source AND that each slice's entry symbol
8
+ (its composable/component) appears as real code in src/, writes
9
+ ``.trtc-apply-evidence/{slice_safename}.json`` next to the session file, and
10
+ advances the state machine:
11
+
12
+ pass → mark_apply_passed
13
+ fail → mark_apply_failed
14
+
15
+ Exit codes:
16
+ 0 — pass
17
+ 1 — fail
18
+ 2 — usage / config error (missing slice id, project root, etc.)
19
+ """
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import subprocess
24
+ import sys
25
+ from pathlib import Path
26
+
27
+ import pytest
28
+ import yaml
29
+
30
+ APPLY_SCRIPT = (
31
+ Path(__file__).resolve().parents[1] / "scripts" / "apply.py"
32
+ )
33
+ STATE_MACHINE_DIR = Path(__file__).resolve().parents[1] / "scripts" / "lib"
34
+ sys.path.insert(0, str(STATE_MACHINE_DIR))
35
+ import state_machine # noqa: E402
36
+ sys.path.pop(0)
37
+
38
+
39
+ def _run_apply(slice_id: str, session_path: Path, project_root: Path | None = None) -> subprocess.CompletedProcess:
40
+ args = [sys.executable, str(APPLY_SCRIPT), "--slice", slice_id, "--session", str(session_path)]
41
+ if project_root is not None:
42
+ args += ["--project", str(project_root)]
43
+ return subprocess.run(args, text=True, capture_output=True)
44
+
45
+
46
+ def _run_apply_unit(unit_id: str, session_path: Path, project_root: Path | None = None) -> subprocess.CompletedProcess:
47
+ args = [sys.executable, str(APPLY_SCRIPT), "--unit", unit_id, "--session", str(session_path)]
48
+ if project_root is not None:
49
+ args += ["--project", str(project_root)]
50
+ return subprocess.run(args, text=True, capture_output=True)
51
+
52
+
53
+ def _seed_to_code_written(session_path: Path) -> None:
54
+ state_machine.init_queue(session_path)
55
+ state_machine.advance(session_path, "mark_slice_read")
56
+ state_machine.advance(session_path, "mark_code_written")
57
+
58
+
59
+ _PASSING_VUE = '''<template><div /></template>
60
+ <script setup lang="ts">
61
+ import { useLoginState, LoginEvent } from "@trtc/tuikit-atomicx-vue3";
62
+ const { login, setSelfInfo, subscribeEvent } = useLoginState();
63
+ await login({ sdkAppId: 0, userId: "u", userSig: "x", scene: 5001 });
64
+ setSelfInfo({ nickName: "Alice" });
65
+ subscribeEvent(LoginEvent.onLoginExpired, () => { /* refresh */ });
66
+ subscribeEvent(LoginEvent.onKickedOffline, () => { /* re-login */ });
67
+ </script>
68
+ '''
69
+
70
+ # Real source code that never references the login-auth entry (useLoginState).
71
+ # Under the entry-symbol gate this FAILS for conference/login-auth: there is
72
+ # code (so not static-only) but the slice's entry was never wired up.
73
+ _NO_ENTRY_VUE = '''<template><div /></template>
74
+ <script setup lang="ts">
75
+ import { ref } from "vue";
76
+ const ready = ref(false);
77
+ ready.value = true;
78
+ </script>
79
+ '''
80
+
81
+ _UNIT_PASSING_VUE = '''<template><div /></template>
82
+ <script setup lang="ts">
83
+ import { LoginEvent } from "tuikit-atomicx-vue3";
84
+ import { useLoginState } from "tuikit-atomicx-vue3/login";
85
+ import { useRoomState } from "tuikit-atomicx-vue3/room";
86
+ const { login, setSelfInfo, subscribeEvent, loginUserInfo } = useLoginState();
87
+ const { createAndJoinRoom, currentRoom, leaveRoom } = useRoomState();
88
+ await login({ sdkAppId: 0, userId: "u", userSig: "x", scene: 5001 });
89
+ setSelfInfo({ nickName: "Alice" });
90
+ subscribeEvent(LoginEvent.onLoginExpired, () => {});
91
+ subscribeEvent(LoginEvent.onKickedOffline, () => {});
92
+ if (loginUserInfo.value?.userId) {
93
+ await createAndJoinRoom();
94
+ }
95
+ if (currentRoom.value) {
96
+ await leaveRoom();
97
+ }
98
+ </script>
99
+ '''
100
+
101
+
102
+ class TestApplyHappyPath:
103
+ def test_pass_advances_state_and_writes_evidence(
104
+ self, session_factory, project_factory
105
+ ):
106
+ path = session_factory()
107
+ proj = project_factory()
108
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
109
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
110
+
111
+ _seed_to_code_written(path)
112
+ result = _run_apply("conference/login-auth", path, proj)
113
+ assert result.returncode == 0, f"stdout={result.stdout}\nstderr={result.stderr}"
114
+
115
+ _, _, state = state_machine.current_slice(path)
116
+ assert state == "apply_passed"
117
+
118
+ ev_dir = path.parent / ".trtc-apply-evidence"
119
+ assert ev_dir.exists()
120
+ ev_files = list(ev_dir.glob("*.json"))
121
+ assert len(ev_files) == 1
122
+ ev = json.loads(ev_files[0].read_text())
123
+ assert ev["status"] == "pass"
124
+ assert ev["slice_id"] == "conference/login-auth"
125
+ assert ev["entries_checked"] >= 1
126
+
127
+
128
+ class TestApplyDeliveryUnit:
129
+ def test_unit_apply_checks_multiple_slices_and_advances_unit_state(
130
+ self, session_factory, project_factory
131
+ ):
132
+ path = session_factory(
133
+ execution_granularity="unit",
134
+ confirmed_plan=["conference/login-auth", "conference/room-lifecycle"],
135
+ )
136
+ proj = project_factory()
137
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
138
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_UNIT_PASSING_VUE)
139
+
140
+ state_machine.init_queue(path)
141
+ state_machine.advance(path, "mark_slice_read")
142
+ state_machine.advance(path, "mark_code_written")
143
+
144
+ result = _run_apply_unit("foundation", path, proj)
145
+ assert result.returncode == 0, f"stdout={result.stdout}\nstderr={result.stderr}"
146
+
147
+ scope = state_machine.current_scope(path)
148
+ assert scope["kind"] == "unit"
149
+ assert scope["state"] == "apply_passed"
150
+
151
+ ev = json.loads((path.parent / ".trtc-apply-evidence" / "foundation.json").read_text())
152
+ assert ev["kind"] == "unit"
153
+ assert ev["unit_id"] == "foundation"
154
+ assert ev["slice_ids"] == ["conference/login-auth", "conference/room-lifecycle"]
155
+ checked = {entry["slice_id"]: entry for entry in ev["slices_checked"]}
156
+ assert checked["conference/login-auth"]["entry_result"] == "pass"
157
+ assert checked["conference/room-lifecycle"]["entry_result"] == "pass"
158
+
159
+
160
+ class TestApplyFailure:
161
+ def test_fail_advances_to_apply_failed_with_issue_list(
162
+ self, session_factory, project_factory
163
+ ):
164
+ path = session_factory()
165
+ proj = project_factory()
166
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
167
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_NO_ENTRY_VUE)
168
+
169
+ _seed_to_code_written(path)
170
+ result = _run_apply("conference/login-auth", path, proj)
171
+ assert result.returncode == 1, f"stdout={result.stdout}\nstderr={result.stderr}"
172
+
173
+ _, _, state = state_machine.current_slice(path)
174
+ assert state == "apply_failed"
175
+
176
+ ev_files = list((path.parent / ".trtc-apply-evidence").glob("*.json"))
177
+ ev = json.loads(ev_files[0].read_text())
178
+ assert ev["status"] == "fail"
179
+ assert ev["issues"], "expected at least one issue listed"
180
+ joined = json.dumps(ev["issues"], ensure_ascii=False)
181
+ assert "useLoginState" in joined or "entry" in joined
182
+
183
+
184
+ class TestApplyAntiCheatStripping:
185
+ """The entry-symbol check still strips comments and string literals first.
186
+
187
+ The original demo-test-2 bug was AI stuffing a required symbol into a
188
+ `// comment` or `"string"` to satisfy a literal substring grep. The
189
+ per-API grep is gone (the gate now only checks the slice's entry
190
+ symbol), but the anti-cheat property must survive for the entry symbol:
191
+ an entry mentioned ONLY in a comment or string is not real wiring and
192
+ must FAIL.
193
+
194
+ Note: a correct implementation references the entry as a real code
195
+ identifier (e.g. `useLoginState()`), which is never inside a string —
196
+ so this stripping never false-negatives on correct code.
197
+ """
198
+
199
+ def test_entry_in_comment_does_not_pass(
200
+ self, session_factory, project_factory
201
+ ):
202
+ """A `.vue` whose only mention of the entry is in `// comments` must FAIL."""
203
+ path = session_factory()
204
+ proj = project_factory()
205
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
206
+
207
+ # useLoginState only appears in comments — no real wiring.
208
+ comment_stuffed = '''<template><div /></template>
209
+ <script setup lang="ts">
210
+ import { ref } from "vue";
211
+ // const { login } = useLoginState();
212
+ /*
213
+ const { login, setSelfInfo } = useLoginState();
214
+ */
215
+ const ready = ref(true);
216
+ </script>
217
+ '''
218
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(comment_stuffed)
219
+
220
+ _seed_to_code_written(path)
221
+ result = _run_apply("conference/login-auth", path, proj)
222
+ assert result.returncode == 1, (
223
+ "entry mentioned only in comments must fail apply — comments are "
224
+ "stripped before the entry check. "
225
+ f"stdout={result.stdout}\nstderr={result.stderr}"
226
+ )
227
+
228
+ _, _, state = state_machine.current_slice(path)
229
+ assert state == "apply_failed"
230
+
231
+ def test_entry_in_string_literal_does_not_pass(
232
+ self, session_factory, project_factory
233
+ ):
234
+ """A `.vue` whose only mention of the entry is in string literals must FAIL."""
235
+ path = session_factory()
236
+ proj = project_factory()
237
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
238
+
239
+ string_stuffed = '''<template><div /></template>
240
+ <script setup lang="ts">
241
+ import { ref } from "vue";
242
+ const dummy = "const { login } = useLoginState();";
243
+ const dummy2 = 'useLoginState';
244
+ const dummy3 = `useLoginState()`;
245
+ const ready = ref(true);
246
+ </script>
247
+ '''
248
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(string_stuffed)
249
+
250
+ _seed_to_code_written(path)
251
+ result = _run_apply("conference/login-auth", path, proj)
252
+ assert result.returncode == 1, (
253
+ "entry mentioned only in string literals must fail apply — strings "
254
+ "are stripped before the entry check. "
255
+ f"stdout={result.stdout}\nstderr={result.stderr}"
256
+ )
257
+
258
+ def test_fail_output_does_not_leak_literal_patterns(
259
+ self, session_factory, project_factory
260
+ ):
261
+ """Evidence JSON `issues[]` must carry rule_text + type, never a `patterns` field.
262
+
263
+ The entry-failure issue names the slice's documented entry composable
264
+ (safe — it's a public import, not a hidden API pattern), but it must
265
+ not regrow a separate clean `patterns` array.
266
+ """
267
+ path = session_factory()
268
+ proj = project_factory()
269
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
270
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_NO_ENTRY_VUE)
271
+
272
+ _seed_to_code_written(path)
273
+ result = _run_apply("conference/login-auth", path, proj)
274
+ assert result.returncode == 1
275
+
276
+ ev_files = list((path.parent / ".trtc-apply-evidence").glob("*.json"))
277
+ ev = json.loads(ev_files[0].read_text())
278
+ assert ev["issues"], "regression: failure should produce at least one issue"
279
+ for issue in ev["issues"]:
280
+ assert "patterns" not in issue, (
281
+ f"issue leaks 'patterns' field: {issue}."
282
+ )
283
+ assert "rule_text" in issue
284
+ assert "type" in issue
285
+
286
+
287
+ class TestApplyStaticOnly:
288
+ def test_no_project_src_falls_back_to_static_only(
289
+ self, session_factory, tmp_path
290
+ ):
291
+ path = session_factory()
292
+ empty_proj = tmp_path / "user-project"
293
+ empty_proj.mkdir(exist_ok=True)
294
+
295
+ _seed_to_code_written(path)
296
+ result = _run_apply("conference/login-auth", path, empty_proj)
297
+ assert result.returncode == 1
298
+ ev = json.loads(next((path.parent / ".trtc-apply-evidence").glob("*.json")).read_text())
299
+ assert ev["mode"] == "static-only"
300
+
301
+
302
+ class TestApplyUsageErrors:
303
+ def test_refuses_when_not_in_code_written(self, session_factory, project_factory):
304
+ path = session_factory()
305
+ proj = project_factory()
306
+ state_machine.init_queue(path)
307
+
308
+ result = _run_apply("conference/login-auth", path, proj)
309
+ assert result.returncode == 2
310
+ assert "code_written" in result.stderr or "state" in result.stderr.lower()
311
+
312
+ def test_refuses_when_slice_does_not_match_cursor(
313
+ self, session_factory, project_factory
314
+ ):
315
+ path = session_factory()
316
+ proj = project_factory()
317
+ _seed_to_code_written(path)
318
+ result = _run_apply("conference/room-lifecycle", path, proj)
319
+ assert result.returncode == 2
320
+ assert "current slice" in result.stderr.lower() or "login-auth" in result.stderr
321
+
322
+ def test_refuses_when_session_missing(self, tmp_path, project_factory):
323
+ proj = project_factory()
324
+ result = _run_apply("conference/login-auth", tmp_path / "missing.yaml", proj)
325
+ assert result.returncode == 2
326
+
327
+
328
+ # ---------- A+B: auto-advance policy ----------
329
+ #
330
+ # session.auto_advance_policy (root-level) controls what apply.py does
331
+ # AFTER recording pass:
332
+ #
333
+ # pause_each (or unset) → state stays at apply_passed; AI must wait
334
+ # for the user before calling mark_user_confirmed.
335
+ # This is the original behaviour.
336
+ # pause_on_failure → apply pass auto-advances mark_user_confirmed,
337
+ # landing on the next slice's not_started. apply
338
+ # fail / partial(warning) still pauses.
339
+ # pause_at_end → same as pause_on_failure (reserved for future).
340
+ #
341
+ # The motivation: forcing the user to type "继续" between every slice was the
342
+ # loud half of the protection. The quiet half — apply.py itself running real
343
+ # grep + advancing state — survives. AI still cannot skip apply, fake
344
+ # evidence, or batch-write multiple slices.
345
+
346
+
347
+ def _set_policy(session_path: Path, policy: str) -> None:
348
+ """Write auto_advance_policy at session root (matches the project's flat schema)."""
349
+ data = yaml.safe_load(session_path.read_text())
350
+ data["auto_advance_policy"] = policy
351
+ session_path.write_text(yaml.safe_dump(data, sort_keys=False, allow_unicode=True))
352
+
353
+
354
+ class TestAutoAdvanceOnPass:
355
+ def test_pause_on_failure_pass_advances_to_next_slice(
356
+ self, session_factory, project_factory
357
+ ):
358
+ path = session_factory()
359
+ proj = project_factory()
360
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
361
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
362
+
363
+ _seed_to_code_written(path)
364
+ _set_policy(path, "pause_on_failure")
365
+
366
+ result = _run_apply("conference/login-auth", path, proj)
367
+ assert result.returncode == 0, result.stderr
368
+
369
+ idx, sid, state = state_machine.current_slice(path)
370
+ assert idx == 1
371
+ assert sid == "conference/room-lifecycle"
372
+ assert state == "not_started"
373
+
374
+ def test_pause_each_pass_stays_at_apply_passed(
375
+ self, session_factory, project_factory
376
+ ):
377
+ """Original behaviour preserved when policy is pause_each."""
378
+ path = session_factory()
379
+ proj = project_factory()
380
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
381
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
382
+
383
+ _seed_to_code_written(path)
384
+ _set_policy(path, "pause_each")
385
+
386
+ result = _run_apply("conference/login-auth", path, proj)
387
+ assert result.returncode == 0
388
+ _, _, state = state_machine.current_slice(path)
389
+ assert state == "apply_passed"
390
+
391
+ def test_unset_policy_defaults_to_pause_each(
392
+ self, session_factory, project_factory
393
+ ):
394
+ """Backward-compat: sessions without the field keep the old behaviour."""
395
+ path = session_factory()
396
+ proj = project_factory()
397
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
398
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
399
+
400
+ _seed_to_code_written(path)
401
+ # No _set_policy call.
402
+
403
+ result = _run_apply("conference/login-auth", path, proj)
404
+ assert result.returncode == 0
405
+ _, _, state = state_machine.current_slice(path)
406
+ assert state == "apply_passed"
407
+
408
+ def test_pause_at_end_behaves_like_pause_on_failure(
409
+ self, session_factory, project_factory
410
+ ):
411
+ """Reserved value: same behaviour as pause_on_failure for now."""
412
+ path = session_factory()
413
+ proj = project_factory()
414
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
415
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
416
+
417
+ _seed_to_code_written(path)
418
+ _set_policy(path, "pause_at_end")
419
+
420
+ result = _run_apply("conference/login-auth", path, proj)
421
+ assert result.returncode == 0
422
+ _, _, state = state_machine.current_slice(path)
423
+ assert state == "not_started"
424
+
425
+ def test_unknown_policy_is_safe_pause_each(
426
+ self, session_factory, project_factory
427
+ ):
428
+ """An unrecognised policy value must not auto-advance — fail closed."""
429
+ path = session_factory()
430
+ proj = project_factory()
431
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
432
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
433
+
434
+ _seed_to_code_written(path)
435
+ _set_policy(path, "yolo_mode")
436
+
437
+ result = _run_apply("conference/login-auth", path, proj)
438
+ assert result.returncode == 0
439
+ _, _, state = state_machine.current_slice(path)
440
+ assert state == "apply_passed", "unknown policy must not auto-advance"
441
+
442
+
443
+ class TestAutoAdvanceOnFailure:
444
+ def test_pause_on_failure_apply_fail_still_pauses(
445
+ self, session_factory, project_factory
446
+ ):
447
+ path = session_factory()
448
+ proj = project_factory()
449
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
450
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_NO_ENTRY_VUE)
451
+
452
+ _seed_to_code_written(path)
453
+ _set_policy(path, "pause_on_failure")
454
+
455
+ result = _run_apply("conference/login-auth", path, proj)
456
+ assert result.returncode == 1
457
+
458
+ idx, sid, state = state_machine.current_slice(path)
459
+ # Cursor stays on this slice; state is apply_failed; AI must regenerate.
460
+ assert idx == 0
461
+ assert sid == "conference/login-auth"
462
+ assert state == "apply_failed"
463
+
464
+
465
+ class TestAutoAdvanceLastSlice:
466
+ def test_last_slice_auto_advance_lands_on_all_done(
467
+ self, session_factory, project_factory
468
+ ):
469
+ path = session_factory(confirmed_plan=["conference/login-auth"])
470
+ proj = project_factory()
471
+ (proj / "src" / "views").mkdir(parents=True, exist_ok=True)
472
+ (proj / "src" / "views" / "MeetingRoom.vue").write_text(_PASSING_VUE)
473
+
474
+ _seed_to_code_written(path)
475
+ _set_policy(path, "pause_on_failure")
476
+
477
+ result = _run_apply("conference/login-auth", path, proj)
478
+ assert result.returncode == 0
479
+ idx, sid, state = state_machine.current_slice(path)
480
+ assert state == "all_done"