claude-dev-env 4.3.0 → 7.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 (480) hide show
  1. package/.agents/agents/pr-description-writer.md +2 -2
  2. package/.agents/agents/test_agent_frontmatter.py +2 -2
  3. package/.agents/agents/test_pr_description_writer_style.py +1 -3
  4. package/.agents/skills/e-code-review/SKILL.md +12 -12
  5. package/.agents/skills/e-code-review/preflight-proposal.contract.test.mjs +20 -0
  6. package/.agents/skills/e-code-review/reference/fix.md +15 -17
  7. package/.agents/skills/e-code-review/reference/loop.md +35 -39
  8. package/.agents/skills/e-code-review/reference/mode-contract.test.mjs +2 -1
  9. package/.agents/skills/e-code-review/reference/preflight-proposal.md +44 -9
  10. package/.agents/skills/e-code-review/reference/runner-selection.md +13 -14
  11. package/.agents/skills/issue-tracker/SKILL.md +5 -4
  12. package/.agents/skills/pull-request/SKILL.md +11 -13
  13. package/.agents/skills/pull-request/reference/publication-tasks.md +3 -3
  14. package/.agents/skills/pull-request/scripts/github_pr_command_constants/config/constants.py +9 -1
  15. package/.agents/skills/pull-request/scripts/pull_request.py +35 -44
  16. package/.agents/skills/pull-request/scripts/test_github_pr_command_constants.py +9 -0
  17. package/.agents/skills/pull-request/scripts/test_pull_request.py +35 -3
  18. package/.agents/skills/session-continuity/README.md +189 -0
  19. package/.agents/skills/session-continuity/SKILL.md +114 -0
  20. package/.agents/skills/session-continuity/continuity.mjs +375 -0
  21. package/.agents/skills/session-continuity/continuity.test.mjs +303 -0
  22. package/.agents/skills/skill-builder/SKILL.md +3 -196
  23. package/.agents/skills/syncing-submodules/scripts/test_sync_parent_pointer.py +0 -7
  24. package/.agents/skills/windows-scheduled-task/SKILL.md +153 -0
  25. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +6 -0
  26. package/_shared/pr-loop/scripts/terminology_sweep.py +64 -19
  27. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +59 -0
  28. package/bin/cde.mjs +36 -8
  29. package/bin/cde.test.mjs +48 -0
  30. package/bin/ever-shipped-skills.mjs +2 -20
  31. package/bin/ever-shipped-skills.test.mjs +8 -0
  32. package/bin/expand_home_directory_tokens.mjs +4 -5
  33. package/bin/expand_home_directory_tokens.test.mjs +128 -0
  34. package/bin/git_hooks_installer.mjs +22 -3
  35. package/bin/git_hooks_installer.test.mjs +159 -5
  36. package/bin/install-session-continuity.mjs +78 -0
  37. package/bin/install-session-continuity.test.mjs +42 -0
  38. package/bin/install.agents-home.test.mjs +15 -13
  39. package/bin/install.codex-rules.test.mjs +5 -2
  40. package/bin/install.cursor-rules.test.mjs +5 -2
  41. package/bin/install.mjs +506 -63
  42. package/bin/install.policy-lint.test.mjs +130 -0
  43. package/bin/install.profiles.test.mjs +10 -0
  44. package/bin/install.prune.test.mjs +286 -37
  45. package/bin/install.pstack-plugin.test.mjs +114 -0
  46. package/bin/install.test.mjs +537 -120
  47. package/bin/install.transaction.test.mjs +3 -3
  48. package/bin/refresh-pstack-plugin-skills.test.mjs +211 -0
  49. package/docs/CODE_RULES.md +1 -1
  50. package/hooks/advisory/pr_done_reminder.py +342 -0
  51. package/hooks/advisory/test_pr_done_reminder.py +249 -0
  52. package/hooks/blocking/bash_post_call_dispatcher.py +53 -6
  53. package/hooks/blocking/code_rules_banned_identifiers.py +3 -0
  54. package/hooks/blocking/code_rules_paired_test.py +22 -15
  55. package/hooks/blocking/precommit_code_rules_gate.py +5 -8
  56. package/hooks/blocking/test_bash_dispatcher_interpreter_starts.py +7 -52
  57. package/hooks/blocking/test_bash_post_call_dispatcher.py +65 -4
  58. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -69
  59. package/hooks/blocking/test_code_rules_banned_identifiers.py +64 -0
  60. package/hooks/blocking/test_code_rules_enforcer_paired_test.py +36 -29
  61. package/hooks/blocking/test_code_rules_js_resume_task_enumeration.py +4 -4
  62. package/hooks/blocking/test_code_rules_js_returns_object_schemaless.py +5 -5
  63. package/hooks/blocking/test_pre_tool_use_dispatcher.py +43 -342
  64. package/hooks/blocking/test_pre_tool_use_dispatcher_native.py +20 -51
  65. package/hooks/blocking/test_shared_stdin_adoption.py +1 -47
  66. package/hooks/git-hooks/git_hooks_constants/staged_policy_lint.py +12 -0
  67. package/hooks/git-hooks/git_hooks_constants/verification_notice_constants.py +116 -0
  68. package/hooks/git-hooks/post_commit.py +33 -0
  69. package/hooks/git-hooks/pre_commit.py +18 -49
  70. package/hooks/git-hooks/pre_push.py +45 -541
  71. package/hooks/git-hooks/test_installed_policy_lint.py +208 -0
  72. package/hooks/git-hooks/test_native_hook_support.py +129 -0
  73. package/hooks/git-hooks/test_native_policy_commit.py +120 -0
  74. package/hooks/git-hooks/test_post_commit.py +43 -0
  75. package/hooks/git-hooks/test_pre_commit.py +38 -148
  76. package/hooks/git-hooks/test_pre_push.py +48 -1631
  77. package/hooks/git-hooks/test_verification_notice.py +60 -0
  78. package/hooks/git-hooks/test_verification_start.py +173 -0
  79. package/hooks/git-hooks/verification_notice.py +192 -0
  80. package/hooks/git-hooks/verification_notice_context.py +253 -0
  81. package/hooks/git-hooks/verification_notice_state.py +237 -0
  82. package/hooks/git-hooks/verification_start.py +100 -0
  83. package/hooks/hooks.json +1 -120
  84. package/hooks/hooks_constants/banned_identifiers_constants.py +4 -0
  85. package/hooks/hooks_constants/bash_post_call_dispatcher_constants.py +14 -1
  86. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +1 -15
  87. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -4
  88. package/hooks/hooks_constants/pr_done_reminder_constants.py +98 -0
  89. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +10 -93
  90. package/hooks/hooks_constants/stop_dispatcher_constants.py +1 -4
  91. package/hooks/hooks_constants/test_bash_post_call_dispatcher_constants.py +19 -2
  92. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +3 -66
  93. package/hooks/hooks_constants/test_pr_done_reminder_constants.py +40 -0
  94. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +46 -201
  95. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +3 -25
  96. package/hooks/validation/test_post_tool_use_dispatcher.py +35 -298
  97. package/hooks/validators/run_all_validators.py +5 -1
  98. package/hooks/validators/security_checks.py +57 -2
  99. package/hooks/validators/test_hook_timing_harness.py +2 -5
  100. package/hooks/validators/test_run_all_validators_mutation_tool_parity.py +3 -11
  101. package/hooks/validators/test_run_all_validators_policy_lint.py +36 -0
  102. package/hooks/validators/test_security_checks.py +57 -0
  103. package/package.json +2 -2
  104. package/rules/confirm-implementation-forks.md +2 -0
  105. package/rules/durable-post-artifacts.md +2 -1
  106. package/rules/git-workflow.md +18 -6
  107. package/rules/pstack-host-mapping.md +48 -0
  108. package/rules/vault-context.md +3 -5
  109. package/rules/verify-runtime-state.md +7 -0
  110. package/scripts/audit-hooks-cli.mjs +5 -0
  111. package/scripts/audit-hooks.mjs +72 -16
  112. package/scripts/automatic_advisory/__init__.py +1 -0
  113. package/scripts/automatic_advisory/checkout.py +51 -0
  114. package/scripts/automatic_advisory/cli.py +334 -0
  115. package/scripts/automatic_advisory/config/__init__.py +1 -0
  116. package/scripts/automatic_advisory/config/constants.py +93 -0
  117. package/scripts/automatic_advisory/config/timing.py +4 -0
  118. package/scripts/automatic_advisory/configuration.py +197 -0
  119. package/scripts/automatic_advisory/execution.py +350 -0
  120. package/scripts/automatic_advisory/git.py +69 -0
  121. package/scripts/automatic_advisory/model.py +107 -0
  122. package/scripts/automatic_advisory/process_host.py +32 -0
  123. package/scripts/automatic_advisory/publisher.py +84 -0
  124. package/scripts/automatic_advisory/runner.py +399 -0
  125. package/scripts/automatic_advisory/state.py +116 -0
  126. package/scripts/automatic_advisory/window_flags.py +41 -0
  127. package/scripts/automatic_advisory/windows_job.py +122 -0
  128. package/scripts/check.ps1 +0 -1
  129. package/{.agents/skills/run-claude-dev-env/driver.mjs → scripts/ci/installer-lifecycle.mjs} +3 -28
  130. package/scripts/ci/installer-lifecycle.test.mjs +26 -0
  131. package/scripts/ci/windows-installer-lifecycle.ps1 +1 -11
  132. package/scripts/codex_compat_materializer.py +99 -13
  133. package/scripts/dev_env_scripts_constants/shared_tree_constants.py +4 -5
  134. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/durable_post_lint.py +35 -43
  135. package/{.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/durable_post_lint_constants.py → scripts/durable_post_lint_config/config/constants.py} +10 -10
  136. package/{.agents/skills/_shared/pr-loop/scripts/test_durable_post_lint_constants.py → scripts/durable_post_lint_config/config/test_constants.py} +14 -1
  137. package/scripts/local_report_cli.py +184 -0
  138. package/scripts/local_report_core.py +247 -0
  139. package/scripts/local_report_publisher.py +12 -0
  140. package/scripts/local_report_validation.py +357 -0
  141. package/scripts/local_verification/__init__.py +23 -0
  142. package/scripts/local_verification/check_runner.py +364 -0
  143. package/scripts/local_verification/check_support.py +99 -0
  144. package/scripts/local_verification/cli.py +158 -0
  145. package/scripts/local_verification/command_runner.py +84 -0
  146. package/scripts/local_verification/config/__init__.py +1 -0
  147. package/scripts/local_verification/config/constants.py +89 -0
  148. package/scripts/local_verification/config/timing.py +1 -0
  149. package/scripts/local_verification/git_state.py +146 -0
  150. package/scripts/local_verification/manifest.py +224 -0
  151. package/scripts/local_verification/model.py +174 -0
  152. package/scripts/local_verification/runner.py +234 -0
  153. package/scripts/policy_lint/adapter_detectors.py +13 -6
  154. package/scripts/policy_lint/adapter_pairing.py +58 -3
  155. package/scripts/policy_lint/adapters.py +7 -4
  156. package/scripts/policy_lint/config/approved_test_pairs.py +323 -0
  157. package/scripts/policy_lint/config/constants.py +5 -0
  158. package/scripts/policy_lint/engine.py +48 -4
  159. package/scripts/policy_lint/model.py +1 -0
  160. package/scripts/policy_lint/registry.py +1 -1
  161. package/scripts/policy_lint/selection.py +2 -1
  162. package/scripts/policy_lint/selection_git.py +2 -0
  163. package/scripts/pr_verification/__init__.py +1 -0
  164. package/scripts/pr_verification/branch_refs.py +114 -0
  165. package/scripts/pr_verification/config/__init__.py +1 -0
  166. package/scripts/pr_verification/config/constants.py +84 -0
  167. package/scripts/pr_verification/config/timing.py +5 -0
  168. package/scripts/pr_verification/github.py +137 -0
  169. package/scripts/pr_verification/github_api.py +376 -0
  170. package/scripts/pr_verification/github_labels.py +88 -0
  171. package/scripts/pr_verification/github_parsing.py +182 -0
  172. package/scripts/pr_verification/github_transport.py +91 -0
  173. package/scripts/pr_verification/lock.py +122 -0
  174. package/scripts/pr_verification/model.py +43 -0
  175. package/scripts/pr_verification/requirements.txt +1 -0
  176. package/scripts/refresh_pstack_plugin_skills.mjs +200 -0
  177. package/scripts/shared_tree_paths.py +32 -3
  178. package/{.agents/skills/_shared/pr-loop/scripts → scripts}/test_durable_post_lint.py +97 -0
  179. package/scripts/test_shared_tree_paths.py +33 -0
  180. package/scripts/tests/test-audit-hooks-cli.mjs +150 -0
  181. package/scripts/tests/test-audit-hooks.mjs +94 -2
  182. package/scripts/tests/test_automatic_advisory.py +376 -0
  183. package/scripts/tests/test_automatic_advisory_job_cleanup.py +71 -0
  184. package/scripts/tests/test_automatic_advisory_polling.py +216 -0
  185. package/scripts/tests/test_automatic_advisory_recovery.py +320 -0
  186. package/scripts/tests/test_automatic_advisory_repairs.py +400 -0
  187. package/scripts/tests/test_automatic_advisory_settings.py +48 -0
  188. package/scripts/tests/test_automatic_advisory_window_flags.py +163 -0
  189. package/scripts/tests/test_closed_pr_label.py +157 -0
  190. package/scripts/tests/test_codex_compat_materializer.py +189 -6
  191. package/scripts/tests/test_github_labels.py +101 -0
  192. package/scripts/tests/test_github_transport.py +125 -0
  193. package/scripts/tests/test_local_report_publisher.py +396 -0
  194. package/scripts/tests/test_local_report_publisher_cli.py +85 -0
  195. package/scripts/tests/test_local_verification.py +348 -0
  196. package/scripts/tests/test_local_verification_cli.py +98 -0
  197. package/scripts/tests/test_policy_lint_rules_archive.py +120 -0
  198. package/scripts/tests/test_policy_lint_rules_engine.py +25 -4
  199. package/scripts/tests/test_policy_lint_rules_installed_loader.py +29 -0
  200. package/scripts/tests/test_policy_lint_rules_pairing.py +163 -0
  201. package/scripts/tests/test_policy_lint_rules_registry.py +227 -13
  202. package/scripts/tests/test_policy_lint_selection.py +25 -2
  203. package/scripts/tests/test_policy_lint_selection_native_index.py +30 -0
  204. package/scripts/tests/test_pr_verification_github.py +392 -0
  205. package/scripts/tests/test_pr_verification_lock.py +168 -0
  206. package/scripts/tests/test_rules.py +3 -7
  207. package/scripts/tests/test_verification_notice.py +347 -0
  208. package/.agents/skills/_shared/pr-loop/.claude/CLAUDE.md +0 -1
  209. package/.agents/skills/_shared/pr-loop/audit-contract.md +0 -5
  210. package/.agents/skills/_shared/pr-loop/audit-reply-template.md +0 -5
  211. package/.agents/skills/_shared/pr-loop/code-rules-gate.md +0 -5
  212. package/.agents/skills/_shared/pr-loop/fix-protocol.md +0 -5
  213. package/.agents/skills/_shared/pr-loop/gh-payloads.md +0 -5
  214. package/.agents/skills/_shared/pr-loop/portable-driver.md +0 -149
  215. package/.agents/skills/_shared/pr-loop/post-audit-thread-contract.md +0 -5
  216. package/.agents/skills/_shared/pr-loop/precatch-rubric.md +0 -5
  217. package/.agents/skills/_shared/pr-loop/preflight-proposal.contract.test.mjs +0 -119
  218. package/.agents/skills/_shared/pr-loop/preflight-proposal.md +0 -5
  219. package/.agents/skills/_shared/pr-loop/prompts/.claude/CLAUDE.md +0 -1
  220. package/.agents/skills/_shared/pr-loop/prompts/AGENTS.md +0 -1
  221. package/.agents/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +0 -324
  222. package/.agents/skills/_shared/pr-loop/scripts/.claude/CLAUDE.md +0 -1
  223. package/.agents/skills/_shared/pr-loop/scripts/AGENTS.md +0 -1
  224. package/.agents/skills/_shared/pr-loop/scripts/RUNTIME_SCRIPTS.md +0 -29
  225. package/.agents/skills/_shared/pr-loop/scripts/_cli_utils.py +0 -22
  226. package/.agents/skills/_shared/pr-loop/scripts/_path_resolver.py +0 -186
  227. package/.agents/skills/_shared/pr-loop/scripts/_xml_utils.py +0 -20
  228. package/.agents/skills/_shared/pr-loop/scripts/audit_category_schema.py +0 -355
  229. package/.agents/skills/_shared/pr-loop/scripts/build_audit_prompt.py +0 -289
  230. package/.agents/skills/_shared/pr-loop/scripts/build_converge_task_list.py +0 -310
  231. package/.agents/skills/_shared/pr-loop/scripts/build_fix_prompt.py +0 -292
  232. package/.agents/skills/_shared/pr-loop/scripts/init_loop_state.py +0 -134
  233. package/.agents/skills/_shared/pr-loop/scripts/portable_converge_driver.py +0 -1646
  234. package/.agents/skills/_shared/pr-loop/scripts/preflight_worktree.py +0 -392
  235. package/.agents/skills/_shared/pr-loop/scripts/select_converge_pacer.py +0 -215
  236. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/.claude/CLAUDE.md +0 -1
  237. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/AGENTS.md +0 -1
  238. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/__init__.py +0 -0
  239. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/audit_category_schema_constants.py +0 -37
  240. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/converge_task_list_constants.py +0 -56
  241. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +0 -45
  242. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/pacer_constants.py +0 -47
  243. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +0 -157
  244. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/portable_driver_constants.py +0 -177
  245. package/.agents/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/preflight_constants.py +0 -70
  246. package/.agents/skills/_shared/pr-loop/scripts/teardown_worktrees.py +0 -172
  247. package/.agents/skills/_shared/pr-loop/scripts/test__path_resolver.py +0 -106
  248. package/.agents/skills/_shared/pr-loop/scripts/test_audit_category_schema.py +0 -94
  249. package/.agents/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +0 -499
  250. package/.agents/skills/_shared/pr-loop/scripts/test_build_converge_task_list.py +0 -140
  251. package/.agents/skills/_shared/pr-loop/scripts/test_build_fix_prompt.py +0 -262
  252. package/.agents/skills/_shared/pr-loop/scripts/test_init_loop_state.py +0 -71
  253. package/.agents/skills/_shared/pr-loop/scripts/test_portable_converge_driver.py +0 -1119
  254. package/.agents/skills/_shared/pr-loop/scripts/test_preflight_worktree.py +0 -287
  255. package/.agents/skills/_shared/pr-loop/scripts/test_select_converge_pacer.py +0 -207
  256. package/.agents/skills/_shared/pr-loop/scripts/test_teardown_worktrees.py +0 -59
  257. package/.agents/skills/_shared/pr-loop/scripts/test_write_handoff.py +0 -201
  258. package/.agents/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +0 -182
  259. package/.agents/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +0 -206
  260. package/.agents/skills/_shared/pr-loop/scripts/write_handoff.py +0 -309
  261. package/.agents/skills/_shared/pr-loop/state-schema.md +0 -5
  262. package/.agents/skills/_shared/pr-loop/worker-spawn.md +0 -5
  263. package/.agents/skills/autoconverge/.claude/CLAUDE.md +0 -1
  264. package/.agents/skills/autoconverge/AGENTS.md +0 -1
  265. package/.agents/skills/autoconverge/SKILL.md +0 -431
  266. package/.agents/skills/autoconverge/reference/.claude/CLAUDE.md +0 -1
  267. package/.agents/skills/autoconverge/reference/AGENTS.md +0 -1
  268. package/.agents/skills/autoconverge/reference/closing-report.md +0 -93
  269. package/.agents/skills/autoconverge/reference/convergence-gates.md +0 -31
  270. package/.agents/skills/autoconverge/reference/convergence.md +0 -219
  271. package/.agents/skills/autoconverge/reference/copilot-findings.md +0 -51
  272. package/.agents/skills/autoconverge/reference/gotchas.md +0 -66
  273. package/.agents/skills/autoconverge/reference/headless-safety.md +0 -44
  274. package/.agents/skills/autoconverge/reference/multi-pr.md +0 -128
  275. package/.agents/skills/autoconverge/reference/self-closing-loop.md +0 -84
  276. package/.agents/skills/autoconverge/reference/stop-conditions.md +0 -127
  277. package/.agents/skills/autoconverge/test_portable_pacer_gate.py +0 -54
  278. package/.agents/skills/autoconverge/workflow/.claude/CLAUDE.md +0 -1
  279. package/.agents/skills/autoconverge/workflow/AGENTS.md +0 -1
  280. package/.agents/skills/autoconverge/workflow/aggregate_runs.py +0 -371
  281. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/.claude/CLAUDE.md +0 -1
  282. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/AGENTS.md +0 -1
  283. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/__init__.py +0 -0
  284. package/.agents/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +0 -250
  285. package/.agents/skills/autoconverge/workflow/converge.clean-audit.test.mjs +0 -665
  286. package/.agents/skills/autoconverge/workflow/converge.codex-gate.test.mjs +0 -472
  287. package/.agents/skills/autoconverge/workflow/converge.contract.test.mjs +0 -64
  288. package/.agents/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +0 -1009
  289. package/.agents/skills/autoconverge/workflow/converge.fix-progress.test.mjs +0 -107
  290. package/.agents/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +0 -33
  291. package/.agents/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +0 -15
  292. package/.agents/skills/autoconverge/workflow/converge.mjs +0 -3132
  293. package/.agents/skills/autoconverge/workflow/converge.p2-advance.test.mjs +0 -202
  294. package/.agents/skills/autoconverge/workflow/converge.path-aware.test.mjs +0 -47
  295. package/.agents/skills/autoconverge/workflow/converge.precatch.test.mjs +0 -152
  296. package/.agents/skills/autoconverge/workflow/converge.run-input.test.mjs +0 -116
  297. package/.agents/skills/autoconverge/workflow/converge_multi.mjs +0 -169
  298. package/.agents/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +0 -173
  299. package/.agents/skills/autoconverge/workflow/convergence_summary.py +0 -110
  300. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +0 -2
  301. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +0 -2
  302. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +0 -2
  303. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +0 -2
  304. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +0 -2
  305. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +0 -2
  306. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ab1c2d3e4f5a6b7c8.jsonl +0 -2
  307. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +0 -2
  308. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +0 -2
  309. package/.agents/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +0 -266
  310. package/.agents/skills/autoconverge/workflow/render_report.py +0 -1024
  311. package/.agents/skills/autoconverge/workflow/test_aggregate_runs.py +0 -134
  312. package/.agents/skills/autoconverge/workflow/test_convergence_summary.py +0 -149
  313. package/.agents/skills/autoconverge/workflow/test_render_report.py +0 -812
  314. package/.agents/skills/hitl/SKILL.md +0 -45
  315. package/.agents/skills/pr-cleanup/SKILL.md +0 -140
  316. package/.agents/skills/pr-cleanup/reference/process-inventory.md +0 -17
  317. package/.agents/skills/pr-cleanup/reference/task-seeds.md +0 -22
  318. package/.agents/skills/pr-name-by-capability/SKILL.md +0 -73
  319. package/.agents/skills/pr-name-by-capability/reference/fetch-commands.md +0 -29
  320. package/.agents/skills/pr-name-by-capability/reference/offense-examples.md +0 -32
  321. package/.agents/skills/pr-name-by-capability/reference/report-template.md +0 -29
  322. package/.agents/skills/pr-name-by-capability/reference/rule-checklist.md +0 -38
  323. package/.agents/skills/pr-name-by-capability/reference/task-seeds.md +0 -10
  324. package/.agents/skills/pr-plain-language-cleanup/SKILL.md +0 -93
  325. package/.agents/skills/pr-refinement/SKILL.md +0 -31
  326. package/.agents/skills/pr-shared-extraction/SKILL.md +0 -177
  327. package/.agents/skills/pr-shared-extraction/reference/examples.md +0 -46
  328. package/.agents/skills/pr-shared-extraction/reference/offense-taxonomy.md +0 -100
  329. package/.agents/skills/pr-small-cl/SKILL.md +0 -52
  330. package/.agents/skills/pr-title-description/SKILL.md +0 -96
  331. package/.agents/skills/prototype/SKILL.md +0 -85
  332. package/.agents/skills/prototype/reference/honest-limitations.md +0 -23
  333. package/.agents/skills/prototype/reference/promotion-tasks.md +0 -23
  334. package/.agents/skills/prototype/scripts/build_sandbox_settings.py +0 -249
  335. package/.agents/skills/prototype/scripts/conftest.py +0 -15
  336. package/.agents/skills/prototype/scripts/launch_sandbox.py +0 -205
  337. package/.agents/skills/prototype/scripts/probe_sandbox_safety.py +0 -311
  338. package/.agents/skills/prototype/scripts/prototype_scripts_constants/__init__.py +0 -1
  339. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
  340. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +0 -41
  341. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +0 -23
  342. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +0 -45
  343. package/.agents/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +0 -10
  344. package/.agents/skills/prototype/scripts/test_build_sandbox_settings.py +0 -275
  345. package/.agents/skills/prototype/scripts/test_launch_sandbox.py +0 -303
  346. package/.agents/skills/prototype/scripts/test_probe_sandbox_safety.py +0 -312
  347. package/.agents/skills/prototype/workflows/promotion.md +0 -25
  348. package/.agents/skills/prototype/workflows/sandbox.md +0 -35
  349. package/.agents/skills/rebase/.claude/CLAUDE.md +0 -1
  350. package/.agents/skills/rebase/AGENTS.md +0 -1
  351. package/.agents/skills/rebase/SKILL.md +0 -178
  352. package/.agents/skills/review-router/SKILL.md +0 -51
  353. package/.agents/skills/review-router/reference/route-policy.json +0 -7
  354. package/.agents/skills/review-router/reference/task-seeds.md +0 -1
  355. package/.agents/skills/review-router/scripts/conftest.py +0 -4
  356. package/.agents/skills/review-router/scripts/review_router.py +0 -50
  357. package/.agents/skills/review-router/scripts/review_router_cli.py +0 -211
  358. package/.agents/skills/review-router/scripts/review_router_constants/__init__.py +0 -1
  359. package/.agents/skills/review-router/scripts/review_router_constants/config/__init__.py +0 -0
  360. package/.agents/skills/review-router/scripts/review_router_constants/config/constants.py +0 -13
  361. package/.agents/skills/review-router/scripts/test_review_router.py +0 -23
  362. package/.agents/skills/review-router/scripts/test_review_router_cli.py +0 -47
  363. package/.agents/skills/review-tier/SKILL.md +0 -56
  364. package/.agents/skills/review-tier/reference/task-seeds.md +0 -1
  365. package/.agents/skills/review-tier/reference/tier-policy.json +0 -9
  366. package/.agents/skills/review-tier/scripts/conftest.py +0 -4
  367. package/.agents/skills/review-tier/scripts/review_tier.py +0 -359
  368. package/.agents/skills/review-tier/scripts/review_tier_constants/__init__.py +0 -1
  369. package/.agents/skills/review-tier/scripts/review_tier_constants/config/__init__.py +0 -0
  370. package/.agents/skills/review-tier/scripts/review_tier_constants/config/constants.py +0 -29
  371. package/.agents/skills/review-tier/scripts/test_generation.py +0 -159
  372. package/.agents/skills/review-tier/scripts/test_review_tier.py +0 -33
  373. package/.agents/skills/run-claude-dev-env/SKILL.md +0 -84
  374. package/.agents/skills/session-log/.claude/CLAUDE.md +0 -1
  375. package/.agents/skills/session-log/AGENTS.md +0 -1
  376. package/.agents/skills/session-log/SKILL.md +0 -170
  377. package/.agents/skills/session-log/templates/frontmatter.md +0 -40
  378. package/.agents/skills/session-tidy/.claude/CLAUDE.md +0 -1
  379. package/.agents/skills/session-tidy/AGENTS.md +0 -1
  380. package/.agents/skills/session-tidy/SKILL.md +0 -191
  381. package/.agents/skills/skill-builder/references/.claude/CLAUDE.md +0 -1
  382. package/.agents/skills/skill-builder/references/AGENTS.md +0 -1
  383. package/.agents/skills/skill-builder/references/delegation-map.md +0 -130
  384. package/.agents/skills/skill-builder/references/description-field.md +0 -111
  385. package/.agents/skills/skill-builder/references/deterministic-elements.md +0 -218
  386. package/.agents/skills/skill-builder/references/progressive-disclosure.md +0 -122
  387. package/.agents/skills/skill-builder/references/self-audit-checklist.md +0 -126
  388. package/.agents/skills/skill-builder/references/skill-modularity.md +0 -111
  389. package/.agents/skills/skill-builder/references/skill-types.md +0 -232
  390. package/.agents/skills/skill-builder/references/thariq-x-post-skills.json +0 -33
  391. package/.agents/skills/skill-builder/templates/.claude/CLAUDE.md +0 -1
  392. package/.agents/skills/skill-builder/templates/AGENTS.md +0 -1
  393. package/.agents/skills/skill-builder/templates/gap-analysis.md +0 -87
  394. package/.agents/skills/skill-builder/workflows/.claude/CLAUDE.md +0 -1
  395. package/.agents/skills/skill-builder/workflows/AGENTS.md +0 -1
  396. package/.agents/skills/skill-builder/workflows/improve-skill.md +0 -136
  397. package/.agents/skills/skill-builder/workflows/new-skill.md +0 -179
  398. package/.agents/skills/skill-builder/workflows/polish-skill.md +0 -139
  399. package/.agents/skills/source-command-sr-loop/SKILL.md +0 -82
  400. package/.agents/skills/update/.claude/CLAUDE.md +0 -1
  401. package/.agents/skills/update/AGENTS.md +0 -1
  402. package/.agents/skills/update/SKILL.md +0 -190
  403. package/_shared/pr-loop/AGENTS.md +0 -1
  404. package/_shared/pr-loop/audit-contract.md +0 -183
  405. package/_shared/pr-loop/audit-reply-template.md +0 -151
  406. package/_shared/pr-loop/code-rules-gate.md +0 -65
  407. package/_shared/pr-loop/fix-protocol.md +0 -60
  408. package/_shared/pr-loop/gh-payloads.md +0 -75
  409. package/_shared/pr-loop/post-audit-thread-contract.md +0 -51
  410. package/_shared/pr-loop/precatch-rubric.md +0 -66
  411. package/_shared/pr-loop/preflight-proposal.md +0 -56
  412. package/_shared/pr-loop/scripts/post_audit_thread.py +0 -1242
  413. package/_shared/pr-loop/scripts/pr_loop_shared_constants/post_audit_thread_constants.py +0 -199
  414. package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +0 -1115
  415. package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +0 -129
  416. package/_shared/pr-loop/state-schema.md +0 -45
  417. package/_shared/pr-loop/worker-spawn.md +0 -189
  418. package/hooks/blocking/ask_user_question_shape_blocker.py +0 -83
  419. package/hooks/blocking/bot_mention_comment_blocker.py +0 -76
  420. package/hooks/blocking/claude_md_orphan_file_blocker.py +0 -129
  421. package/hooks/blocking/cursor_cli_python_misfire_blocker.py +0 -108
  422. package/hooks/blocking/destructive_command_blocker.py +0 -2073
  423. package/hooks/blocking/fable_spawn_gate.py +0 -187
  424. package/hooks/blocking/luna_fast_mode_gate.py +0 -160
  425. package/hooks/blocking/nas_ssh_binary_enforcer.py +0 -163
  426. package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +0 -260
  427. package/hooks/blocking/package_inventory_stale_blocker.py +0 -107
  428. package/hooks/blocking/pii_prevention_blocker.py +0 -365
  429. package/hooks/blocking/piped_pytest_blocker.py +0 -1223
  430. package/hooks/blocking/question_to_user_enforcer.py +0 -130
  431. package/hooks/blocking/send_user_file_open_locally_blocker.py +0 -71
  432. package/hooks/blocking/sensitive_file_protector.py +0 -167
  433. package/hooks/blocking/session_edit_stage_gate.py +0 -684
  434. package/hooks/blocking/session_handoff_blocker.py +0 -182
  435. package/hooks/blocking/shell_substitution_blocker.py +0 -350
  436. package/hooks/blocking/test_ask_user_question_shape.py +0 -69
  437. package/hooks/blocking/test_bot_mention_comment_blocker.py +0 -131
  438. package/hooks/blocking/test_claude_md_orphan_file_blocker.py +0 -1277
  439. package/hooks/blocking/test_cursor_cli_python_misfire_blocker.py +0 -208
  440. package/hooks/blocking/test_destructive_command_blocker.py +0 -2024
  441. package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +0 -105
  442. package/hooks/blocking/test_destructive_command_blocker_no_verify.py +0 -122
  443. package/hooks/blocking/test_destructive_command_blocker_patterns.py +0 -133
  444. package/hooks/blocking/test_fable_spawn_gate.py +0 -381
  445. package/hooks/blocking/test_inventory_deadlock_resolution.py +0 -154
  446. package/hooks/blocking/test_luna_fast_mode_gate.py +0 -211
  447. package/hooks/blocking/test_nas_ssh_binary_enforcer.py +0 -225
  448. package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +0 -231
  449. package/hooks/blocking/test_package_inventory_stale_blocker.py +0 -382
  450. package/hooks/blocking/test_pii_prevention_blocker.py +0 -1222
  451. package/hooks/blocking/test_pii_prevention_windows_git_path.py +0 -41
  452. package/hooks/blocking/test_pii_write_surface_ephemeral.py +0 -221
  453. package/hooks/blocking/test_piped_pytest_blocker.py +0 -587
  454. package/hooks/blocking/test_precommit_code_rules_gate_native_owner.py +0 -233
  455. package/hooks/blocking/test_question_to_user_enforcer.py +0 -169
  456. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +0 -118
  457. package/hooks/blocking/test_sensitive_file_protector.py +0 -365
  458. package/hooks/blocking/test_session_edit_stage_gate.py +0 -537
  459. package/hooks/blocking/test_session_edit_stage_gate_staging.py +0 -64
  460. package/hooks/blocking/test_session_edit_stage_gate_support.py +0 -204
  461. package/hooks/blocking/test_session_edit_stage_gate_tracker.py +0 -116
  462. package/hooks/blocking/test_session_handoff_blocker.py +0 -318
  463. package/hooks/blocking/test_shell_substitution_blocker.py +0 -298
  464. package/hooks/blocking/test_unscoped_search_blocker.py +0 -102
  465. package/hooks/blocking/test_write_existing_file_blocker.py +0 -122
  466. package/hooks/blocking/tests/test_pii_prevention_blocker.py +0 -259
  467. package/hooks/blocking/unscoped_search_blocker.py +0 -391
  468. package/hooks/blocking/write_existing_file_blocker.py +0 -124
  469. package/hooks/hooks_constants/bot_mention_comment_blocker_constants.py +0 -19
  470. package/hooks/hooks_constants/cursor_cli_python_misfire_blocker_constants.py +0 -79
  471. package/hooks/hooks_constants/fable_spawn_gate_constants.py +0 -61
  472. package/hooks/hooks_constants/luna_fast_mode_gate_constants.py +0 -45
  473. package/hooks/hooks_constants/nas_ssh_binary_enforcer_constants.py +0 -20
  474. package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +0 -57
  475. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +0 -17
  476. package/hooks/hooks_constants/sensitive_file_protector_constants.py +0 -40
  477. package/hooks/hooks_constants/session_handoff_blocker_constants.py +0 -10
  478. package/hooks/hooks_constants/shell_substitution_blocker_constants.py +0 -80
  479. /package/.agents/skills/{_shared/pr-loop → session-continuity}/AGENTS.md +0 -0
  480. /package/{_shared/pr-loop → .agents/skills/session-continuity}/CLAUDE.md +0 -0
@@ -1,812 +0,0 @@
1
- """Tests for render_report.py against the real wf_881252e6-700 fixture."""
2
-
3
- import json
4
- import re
5
- import shutil
6
- import subprocess
7
- import sys
8
- from pathlib import Path
9
-
10
- sys.path.insert(0, str(Path(__file__).resolve().parent))
11
-
12
- import render_report
13
-
14
- FIXTURE_DIR = Path(__file__).resolve().parent / "fixtures" / "wf_run"
15
- FIXTURE_JOURNAL = FIXTURE_DIR / "workflows" / "wf_881252e6-700.json"
16
-
17
- EXPECTED_TOTAL_FINDINGS = 15
18
- EXPECTED_FIX_COMMIT_COUNT = 2
19
- EXPECTED_GENERATED_DATE = "2026-06-13"
20
- EXPECTED_ROUND_COUNT = 4
21
-
22
-
23
- def _render_cli(journal_path: Path, out_path: Path) -> subprocess.CompletedProcess[str]:
24
- """Run the render_report CLI against a journal and return the completed process."""
25
- render_script = Path(__file__).resolve().parent / "render_report.py"
26
- return subprocess.run(
27
- [
28
- sys.executable,
29
- str(render_script),
30
- "--journal",
31
- str(journal_path),
32
- "--out",
33
- str(out_path),
34
- "--pr",
35
- "example-owner/example-repo#211",
36
- "--final-sha",
37
- "7c2f420c4d5b7c83aa47f93d99a0f1420e3373c4",
38
- "--rounds",
39
- "4",
40
- ],
41
- capture_output=True,
42
- text=True,
43
- )
44
-
45
-
46
- def test_rendered_report_defines_every_referenced_css_class(tmp_path: Path) -> None:
47
- """Every class the rendered report markup references resolves to a CSS selector.
48
-
49
- Renders the report from the findings fixture so the raw-findings appendix is
50
- present, then asserts no class attribute names a style the stylesheet omits,
51
- keeping the report markup and HTML_STYLE_BLOCK from drifting apart.
52
- """
53
- out_path = tmp_path / "report.html"
54
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
55
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
56
- html_content = out_path.read_text(encoding="utf-8")
57
-
58
- style_match = re.search(r"<style>(.*?)</style>", html_content, re.DOTALL)
59
- assert style_match is not None
60
- defined_classes = set(re.findall(r"\.([A-Za-z][\w-]*)", style_match.group(1)))
61
- referenced_classes = {
62
- each_name
63
- for attribute_value in re.findall(r'class="([^"]*)"', html_content)
64
- for each_name in attribute_value.split()
65
- }
66
-
67
- orphan_classes = referenced_classes - defined_classes
68
- assert not orphan_classes, f"classes referenced but undefined: {sorted(orphan_classes)}"
69
-
70
-
71
- def _copy_run_tree_without_summary_entry(destination_root: Path) -> Path:
72
- """Copy the fixture run tree, dropping the convergence-summary workflowProgress entry.
73
-
74
- Returns the path to the copied journal whose summarizer entry has been removed.
75
- """
76
- shutil.copytree(FIXTURE_DIR, destination_root)
77
- journal_destination = destination_root / "workflows" / FIXTURE_JOURNAL.name
78
- journal = json.loads(journal_destination.read_text(encoding="utf-8"))
79
- journal["workflowProgress"] = [
80
- each_entry
81
- for each_entry in journal["workflowProgress"]
82
- if each_entry.get("label") != render_report.LABEL_CONVERGENCE_SUMMARY
83
- ]
84
- journal_destination.write_text(json.dumps(journal, indent=2), encoding="utf-8")
85
- return journal_destination
86
-
87
-
88
- def test_load_run_data_aggregate_counts() -> None:
89
- """Should parse the fixture journal and transcripts into correct aggregate counts."""
90
- run_data = render_report.load_run_data(FIXTURE_JOURNAL)
91
-
92
- assert run_data.total_finding_count == EXPECTED_TOTAL_FINDINGS
93
- assert run_data.fix_commit_count == EXPECTED_FIX_COMMIT_COUNT
94
- assert run_data.generated_date == EXPECTED_GENERATED_DATE
95
- assert len(run_data.all_distinct_findings) == EXPECTED_TOTAL_FINDINGS
96
-
97
-
98
- def test_load_run_data_parses_convergence_summary() -> None:
99
- """Should locate the convergence-summary entry and parse its StructuredOutput."""
100
- run_data = render_report.load_run_data(FIXTURE_JOURNAL)
101
-
102
- assert run_data.convergence_summary is not None
103
- verdict_line = run_data.convergence_summary["verdictLine"]
104
- issue_classes = run_data.convergence_summary["issueClasses"]
105
- assert isinstance(verdict_line, str) and verdict_line
106
- assert isinstance(issue_classes, list) and len(issue_classes) == 3
107
-
108
-
109
- def test_load_run_data_carries_category_on_findings() -> None:
110
- """Should default each finding's category to 'bug' when the raw dict omits it."""
111
- run_data = render_report.load_run_data(FIXTURE_JOURNAL)
112
-
113
- assert all(
114
- each_finding.category == render_report.CATEGORY_BUG
115
- for each_finding in run_data.all_distinct_findings
116
- )
117
-
118
-
119
- def test_cli_renders_verdict_banner_with_python_computed_vsub(tmp_path: Path) -> None:
120
- """Should render the verdict banner with verdictLine and a Python-computed vsub."""
121
- out_path = tmp_path / "report.html"
122
-
123
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
124
-
125
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
126
- assert completed.stdout.strip() == str(out_path)
127
- assert out_path.exists(), "Output HTML file was not written"
128
-
129
- html_content = out_path.read_text(encoding="utf-8")
130
- assert "PR #211 Convergence Summary" in html_content
131
- assert 'class="verdict"' in html_content
132
- assert 'class="vtext"' in html_content
133
- assert "Converged in 4 rounds; 3 distinct issue classes were caught and fixed." in (
134
- html_content
135
- )
136
- assert 'class="vsub"' in html_content
137
- assert "2 fix commits" in html_content
138
- assert "final commit 7c2f420c" in html_content
139
-
140
-
141
- def test_cli_renders_scorecard_with_caught_rounds_and_zero_remaining(
142
- tmp_path: Path,
143
- ) -> None:
144
- """Should render an at-a-glance scorecard: findings caught, rounds, and zero left."""
145
- out_path = tmp_path / "report-scorecard.html"
146
-
147
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
148
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
149
-
150
- html_content = out_path.read_text(encoding="utf-8")
151
- assert 'class="scorecard"' in html_content
152
- assert 'class="stat good"' in html_content
153
- assert f'<div class="stat-num">{EXPECTED_TOTAL_FINDINGS}</div>' in html_content
154
- assert f'<div class="stat-num">{EXPECTED_ROUND_COUNT}</div>' in html_content
155
- assert '<div class="stat-num">0</div>' in html_content
156
- assert ">caught</div>" in html_content
157
- assert ">rounds</div>" in html_content
158
- assert ">left</div>" in html_content
159
-
160
-
161
- def test_render_issue_class_heading_carries_a_category_icon() -> None:
162
- """Should prefix the plain name with the icon matching the issue category."""
163
- bug_heading = render_report._render_issue_class_heading(
164
- {"plainName": "A plain symptom", "count": 2, "category": "bug"}
165
- )
166
- assert 'class="bug-title"' in bug_heading
167
- assert 'class="bug-icon"' in bug_heading
168
- assert render_report.ISSUE_ICON_BY_CATEGORY["bug"] in bug_heading
169
-
170
- standard_heading = render_report._render_issue_class_heading(
171
- {"plainName": "A standard symptom", "count": 1, "category": "code-standard"}
172
- )
173
- assert render_report.ISSUE_ICON_BY_CATEGORY["code-standard"] in standard_heading
174
-
175
-
176
- def test_render_issue_class_heading_uses_default_icon_for_unknown_category() -> None:
177
- """Should fall back to the default icon when the category has no mapped icon."""
178
- heading = render_report._render_issue_class_heading(
179
- {"plainName": "An unmapped symptom", "count": 1, "category": "mystery"}
180
- )
181
- assert render_report.DEFAULT_ISSUE_ICON in heading
182
-
183
-
184
- def test_cli_renders_problem_and_fix_scene_cards(tmp_path: Path) -> None:
185
- """Should draw problem and fix scene cards with trigger, result, and caption."""
186
- out_path = tmp_path / "report-scenes.html"
187
-
188
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
189
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
190
-
191
- html_content = out_path.read_text(encoding="utf-8")
192
- assert 'class="pf-grid"' in html_content
193
- assert 'class="pf problem"' in html_content
194
- assert 'class="pf fix"' in html_content
195
- assert "export stops at batch 90 of 100" in html_content
196
- assert "starts again at batch 1" in html_content
197
- assert "continues at batch 91" in html_content
198
- assert 'class="res-bad"' in html_content
199
- assert 'class="res-good"' in html_content
200
- assert "began again" in html_content
201
-
202
-
203
- def test_render_issue_class_panels_for_each_medium() -> None:
204
- """Should draw before/after panels: a code panel and a terminal panel per medium."""
205
- convergence_summary = {
206
- "verdictLine": "Converged.",
207
- "problemScenes": [],
208
- "fixScenes": [],
209
- "issueClasses": [
210
- {
211
- "plainName": "A missing return type",
212
- "count": 3,
213
- "severity": "P2",
214
- "category": "code-standard",
215
- "status": "fixed",
216
- "cause": "Tests did not declare their return type.",
217
- "medium": "code",
218
- "beforeLines": ["def test_x():"],
219
- "afterLines": ["def test_x() -> None:"],
220
- },
221
- {
222
- "plainName": "An install that did nothing",
223
- "count": 1,
224
- "severity": "P1",
225
- "category": "bug",
226
- "status": "fixed",
227
- "cause": "The command skipped the install.",
228
- "medium": "terminal",
229
- "beforeLines": ["~ $ install", "(no output)"],
230
- "afterLines": ["~ $ install", "Installed."],
231
- },
232
- ],
233
- }
234
-
235
- panels_html = render_report._render_issue_class_panels(convergence_summary)
236
-
237
- assert 'class="code-panel"' in panels_html
238
- assert "def test_x() -&gt; None:" in panels_html
239
- assert 'class="terminal"' in panels_html
240
- assert 'class="term-bar"' in panels_html
241
- assert "Installed." in panels_html
242
- assert 'class="term-grid"' in panels_html
243
- assert 'class="bug-head"' in panels_html
244
- assert "A missing return type" in panels_html
245
- assert "An install that did nothing" in panels_html
246
- assert "3 findings" in panels_html
247
- assert "1 finding" in panels_html
248
-
249
-
250
- def test_render_issue_class_panels_draws_text_panel_for_text_medium() -> None:
251
- """Should draw a text panel holding the supplied lines when the medium is text."""
252
- convergence_summary = {
253
- "verdictLine": "Converged.",
254
- "problemScenes": [],
255
- "fixScenes": [],
256
- "issueClasses": [
257
- {
258
- "plainName": "A plain-text symptom",
259
- "count": 1,
260
- "severity": "P2",
261
- "category": "bug",
262
- "status": "fixed",
263
- "cause": "A grounded cause sentence.",
264
- "medium": "text",
265
- "beforeLines": ["pages reloaded every visit"],
266
- "afterLines": ["pages reuse the saved copy"],
267
- }
268
- ],
269
- }
270
-
271
- panels_html = render_report._render_issue_class_panels(convergence_summary)
272
-
273
- assert 'class="text-panel"' in panels_html
274
- assert "pages reloaded every visit" in panels_html
275
- assert "pages reuse the saved copy" in panels_html
276
- assert 'class="terminal"' not in panels_html
277
- assert 'class="code-panel"' not in panels_html
278
-
279
-
280
- def test_cli_renders_cause_line_with_severity_parenthetical(tmp_path: Path) -> None:
281
- """Should render a cause line carrying the plain cause and a muted parenthetical."""
282
- out_path = tmp_path / "report-cause.html"
283
-
284
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
285
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
286
-
287
- html_content = out_path.read_text(encoding="utf-8")
288
- assert 'class="cause"' in html_content
289
- assert "which the project&#x27;s type checker wants" in html_content
290
- assert "P2" in html_content
291
- assert "code standard" in html_content
292
- assert "&times;7" in html_content
293
- assert "fixed" in html_content
294
-
295
-
296
- def test_render_issue_class_panels_omitted_when_lines_empty() -> None:
297
- """Should draw only the cause line when both before and after lines are empty."""
298
- convergence_summary = {
299
- "verdictLine": "Converged.",
300
- "problemScenes": [],
301
- "fixScenes": [],
302
- "issueClasses": [
303
- {
304
- "plainName": "A cause-only class",
305
- "count": 1,
306
- "severity": "P2",
307
- "category": "code-standard",
308
- "status": "fixed",
309
- "cause": "Nothing visual to show.",
310
- "medium": "text",
311
- "beforeLines": [],
312
- "afterLines": [],
313
- }
314
- ],
315
- }
316
-
317
- panels_html = render_report._render_issue_class_panels(convergence_summary)
318
-
319
- assert 'class="term-grid"' not in panels_html
320
- assert 'class="bug-head"' in panels_html
321
- assert "A cause-only class" in panels_html
322
- assert 'class="cause"' in panels_html
323
- assert "Nothing visual to show." in panels_html
324
-
325
-
326
- def test_render_issue_class_panels_clean_state_when_no_classes() -> None:
327
- """Should render a clean-state line, not an empty section, when no classes exist."""
328
- convergence_summary = {
329
- "verdictLine": "Converged with no issues caught.",
330
- "problemScenes": [],
331
- "fixScenes": [],
332
- "issueClasses": [],
333
- }
334
-
335
- panels_html = render_report._render_issue_class_panels(convergence_summary)
336
-
337
- assert 'class="term-grid"' not in panels_html
338
- assert "No issues were caught" in panels_html
339
-
340
-
341
- def test_cli_merges_run_stats_lead_into_caught_section(tmp_path: Path) -> None:
342
- """Should lead the caught section with run stats and omit any timeline section."""
343
- out_path = tmp_path / "report-caught-lead.html"
344
-
345
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
346
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
347
-
348
- html_content = out_path.read_text(encoding="utf-8")
349
- assert "What was caught" in html_content
350
- assert "3 bug classes" in html_content
351
- assert "15 findings in all" in html_content
352
- assert "caught and fixed across 4 rounds" in html_content
353
- assert "2 fix commits" in html_content
354
- assert "How it converged" not in html_content
355
- assert 'class="timeline"' not in html_content
356
- assert 'class="tstep' not in html_content
357
-
358
-
359
- def test_cli_includes_collapsed_appendix(tmp_path: Path) -> None:
360
- """Should include a collapsed details appendix listing every distinct finding."""
361
- out_path = tmp_path / "report-appendix.html"
362
-
363
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
364
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
365
-
366
- html_content = out_path.read_text(encoding="utf-8")
367
- assert '<details class="appendix"' in html_content
368
- assert f"Raw findings ({EXPECTED_TOTAL_FINDINGS})" in html_content
369
- assert "src/exports/tests/test_resume_skip_export.py:35" in html_content
370
-
371
-
372
- def test_cli_degraded_layout_when_summary_entry_absent(tmp_path: Path) -> None:
373
- """Should render the timeline and appendix but no scene, table, or rollup markup."""
374
- run_root = tmp_path / "wf_run_no_summary"
375
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
376
-
377
- out_path = tmp_path / "report-degraded.html"
378
- completed = _render_cli(journal_destination, out_path)
379
-
380
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
381
- html_content = out_path.read_text(encoding="utf-8")
382
-
383
- assert "PR #211 Convergence Summary" in html_content
384
- assert 'class="timeline"' not in html_content
385
- assert "distinct findings across 4 rounds" in html_content
386
- assert '<details class="appendix"' in html_content
387
- assert 'class="pf-grid"' not in html_content
388
- assert 'class="issue-table"' not in html_content
389
- assert 'class="rollup"' not in html_content
390
- assert 'class="pr-summary"' not in html_content
391
-
392
-
393
- def test_cli_injects_summary_from_file_bypassing_transcripts(tmp_path: Path) -> None:
394
- """Should render the full summary body from --summary-file when no summary transcript exists."""
395
- run_root = tmp_path / "wf_run_inject"
396
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
397
-
398
- summary = {
399
- "prProblem": "PhotoSync stopped backing up photos after an account switch.",
400
- "prFix": "It re-checks the account on each backup, so a switch never halts backups.",
401
- "problemScenes": [],
402
- "fixScenes": [],
403
- "verdictLine": "Converged in 4 rounds; every class is fixed.",
404
- "issueClasses": [
405
- {
406
- "plainName": "An injected class the transcript never carried",
407
- "count": 2,
408
- "severity": "P1",
409
- "category": "bug",
410
- "status": "fixed",
411
- "cause": "A concrete grounded cause sentence.",
412
- "medium": "text",
413
- "beforeLines": [],
414
- "afterLines": [],
415
- }
416
- ],
417
- }
418
- summary_path = tmp_path / "summary.json"
419
- summary_path.write_text(json.dumps(summary), encoding="utf-8")
420
-
421
- out_path = tmp_path / "report-injected.html"
422
- render_script = Path(__file__).resolve().parent / "render_report.py"
423
- completed = subprocess.run(
424
- [
425
- sys.executable,
426
- str(render_script),
427
- "--journal",
428
- str(journal_destination),
429
- "--out",
430
- str(out_path),
431
- "--pr",
432
- "example-owner/example-repo#211",
433
- "--final-sha",
434
- "7c2f420c4d5b7c83aa47f93d99a0f1420e3373c4",
435
- "--rounds",
436
- "4",
437
- "--summary-file",
438
- str(summary_path),
439
- ],
440
- capture_output=True,
441
- text=True,
442
- )
443
-
444
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
445
- html_content = out_path.read_text(encoding="utf-8")
446
- assert 'class="verdict"' in html_content
447
- assert "Converged in 4 rounds; every class is fixed." in html_content
448
- assert "An injected class the transcript never carried" in html_content
449
- assert 'class="pf-grid"' in html_content
450
- assert f"Raw findings ({EXPECTED_TOTAL_FINDINGS})" in html_content
451
- assert "https://github.com/example-owner/example-repo/pull/211" not in html_content
452
-
453
-
454
- def test_html_contains_no_hedging_words(tmp_path: Path) -> None:
455
- """Should produce HTML with no hedging language anywhere in the rendered narrative."""
456
- out_path = tmp_path / "report-hedge.html"
457
-
458
- completed = _render_cli(FIXTURE_JOURNAL, out_path)
459
- assert completed.returncode == 0, f"CLI failed:\n{completed.stderr}"
460
-
461
- html_content = out_path.read_text(encoding="utf-8")
462
- all_hedging_words = [
463
- "could",
464
- "might",
465
- "would",
466
- "should",
467
- "likely",
468
- "probably",
469
- "appears",
470
- "seems",
471
- ]
472
- for each_word in all_hedging_words:
473
- pattern = re.compile(r"\b" + re.escape(each_word) + r"\b", re.IGNORECASE)
474
- assert not pattern.search(html_content), (
475
- f"Hedging word {each_word!r} found in rendered HTML"
476
- )
477
-
478
-
479
- def test_extract_structured_output_returns_last_tool_input(tmp_path: Path) -> None:
480
- """Should return the input of the last StructuredOutput tool_use in the transcript."""
481
- transcript_path = tmp_path / "agent-stream.jsonl"
482
- earlier_line = json.dumps(
483
- {
484
- "message": {
485
- "content": [
486
- {
487
- "type": "tool_use",
488
- "name": render_report.STRUCTURED_OUTPUT_TOOL_NAME,
489
- "input": {"newSha": "aaaa1111", "pushed": False},
490
- }
491
- ]
492
- }
493
- }
494
- )
495
- later_line = json.dumps(
496
- {
497
- "message": {
498
- "content": [
499
- {
500
- "type": "tool_use",
501
- "name": render_report.STRUCTURED_OUTPUT_TOOL_NAME,
502
- "input": {"newSha": "bbbb2222", "pushed": True},
503
- }
504
- ]
505
- }
506
- }
507
- )
508
- transcript_path.write_text(
509
- earlier_line + "\n" + later_line + "\n", encoding="utf-8"
510
- )
511
-
512
- extracted = render_report._extract_structured_output(transcript_path)
513
-
514
- assert extracted == {"newSha": "bbbb2222", "pushed": True}
515
-
516
-
517
- def test_extract_structured_output_returns_none_on_missing_file(tmp_path: Path) -> None:
518
- """Should return None when the transcript file does not exist."""
519
- missing_path = tmp_path / "does-not-exist.jsonl"
520
-
521
- extracted = render_report._extract_structured_output(missing_path)
522
-
523
- assert extracted is None
524
-
525
-
526
- def test_fix_record_carries_summary_text() -> None:
527
- """Should read the fix agent's summary field into the FixRecord."""
528
- fix_record = render_report._parse_fix_record(
529
- {"newSha": "abcd1234", "pushed": True, "summary": "renamed and annotated"},
530
- base_sha="base",
531
- )
532
-
533
- assert fix_record.summary == "renamed and annotated"
534
-
535
-
536
- def _write_structured_output_transcript(
537
- transcript_path: Path, tool_input: dict[str, object]
538
- ) -> None:
539
- """Write a one-line agent transcript carrying a single StructuredOutput tool_use."""
540
- line = json.dumps(
541
- {
542
- "message": {
543
- "content": [
544
- {
545
- "type": "tool_use",
546
- "name": render_report.STRUCTURED_OUTPUT_TOOL_NAME,
547
- "input": tool_input,
548
- }
549
- ]
550
- }
551
- }
552
- )
553
- transcript_path.write_text(line + "\n", encoding="utf-8")
554
-
555
-
556
- def test_base_sha_resets_each_round_when_prior_fix_transcript_missing(
557
- tmp_path: Path,
558
- ) -> None:
559
- """Should bind each round's fix base sha to that round's own head, never a prior round's."""
560
- agents_dir = tmp_path / "agents"
561
- agents_dir.mkdir()
562
-
563
- round_one_head = "1111111111111111111111111111111111111111"
564
- round_two_head = "2222222222222222222222222222222222222222"
565
-
566
- round_one_gate_id = "round-one-gate"
567
- round_two_gate_id = "round-two-gate"
568
- round_two_fix_id = "round-two-fix"
569
-
570
- _write_structured_output_transcript(
571
- agents_dir / f"agent-{round_one_gate_id}.jsonl",
572
- {"sha": round_one_head, "clean": False, "findings": []},
573
- )
574
- _write_structured_output_transcript(
575
- agents_dir / f"agent-{round_two_gate_id}.jsonl",
576
- {"sha": round_two_head, "clean": False, "findings": []},
577
- )
578
- _write_structured_output_transcript(
579
- agents_dir / f"agent-{round_two_fix_id}.jsonl",
580
- {"newSha": round_two_head, "pushed": True, "resolvedWithoutCommit": False},
581
- )
582
-
583
- progress_entries: list[dict] = [
584
- {"label": render_report.LABEL_RESOLVE_HEAD, "agentId": "round-one-resolve"},
585
- {"label": render_report.LABEL_COPILOT_GATE, "agentId": round_one_gate_id},
586
- {"label": render_report.LABEL_PREFIX_FIX + "copilot", "agentId": "missing-fix"},
587
- {"label": render_report.LABEL_RESOLVE_HEAD, "agentId": "round-two-resolve"},
588
- {"label": render_report.LABEL_COPILOT_GATE, "agentId": round_two_gate_id},
589
- {
590
- "label": render_report.LABEL_PREFIX_FIX + "copilot",
591
- "agentId": round_two_fix_id,
592
- },
593
- ]
594
-
595
- _all_findings, fix_by_round = render_report._parse_progress_entries(
596
- progress_entries, agents_dir
597
- )
598
-
599
- assert fix_by_round[2].base_sha == round_two_head, (
600
- "Round 2 fix recorded a stale base sha leaked from round 1; "
601
- f"expected {round_two_head}, got {fix_by_round[2].base_sha}"
602
- )
603
-
604
-
605
- def _render_cli_with_summary_file(
606
- journal_path: Path, out_path: Path, summary_path: Path
607
- ) -> subprocess.CompletedProcess[str]:
608
- """Run the render CLI with an injected --summary-file and return the process."""
609
- render_script = Path(__file__).resolve().parent / "render_report.py"
610
- return subprocess.run(
611
- [
612
- sys.executable,
613
- str(render_script),
614
- "--journal",
615
- str(journal_path),
616
- "--out",
617
- str(out_path),
618
- "--pr",
619
- "example-owner/example-repo#211",
620
- "--final-sha",
621
- "7c2f420c4d5b7c83aa47f93d99a0f1420e3373c4",
622
- "--rounds",
623
- "4",
624
- "--summary-file",
625
- str(summary_path),
626
- ],
627
- capture_output=True,
628
- text=True,
629
- )
630
-
631
-
632
- def test_cli_renders_when_issue_class_count_is_null(tmp_path: Path) -> None:
633
- """Should exit 0 and show a zero count when an issue class carries count: null."""
634
- run_root = tmp_path / "wf_run_null_count"
635
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
636
-
637
- summary = {
638
- "prProblem": "A problem.",
639
- "prFix": "A fix.",
640
- "problemScenes": [],
641
- "fixScenes": [],
642
- "verdictLine": "Converged.",
643
- "issueClasses": [
644
- {
645
- "plainName": "A class with a null count",
646
- "count": None,
647
- "severity": "P2",
648
- "category": "bug",
649
- "status": "fixed",
650
- "cause": "A grounded cause.",
651
- "medium": "text",
652
- "beforeLines": [],
653
- "afterLines": [],
654
- }
655
- ],
656
- }
657
- summary_path = tmp_path / "summary-null-count.json"
658
- summary_path.write_text(json.dumps(summary), encoding="utf-8")
659
-
660
- out_path = tmp_path / "report-null-count.html"
661
- completed = _render_cli_with_summary_file(
662
- journal_destination, out_path, summary_path
663
- )
664
-
665
- assert completed.returncode == 0, f"CLI crashed on null count:\n{completed.stderr}"
666
- html_content = out_path.read_text(encoding="utf-8")
667
- assert "A class with a null count" in html_content
668
- assert "0 findings" in html_content
669
- assert "&times;0" in html_content
670
-
671
-
672
- def test_cli_renders_when_issue_class_count_is_non_numeric(tmp_path: Path) -> None:
673
- """Should exit 0 and show a zero count when an issue class count is a bad string."""
674
- run_root = tmp_path / "wf_run_bad_count"
675
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
676
-
677
- summary = {
678
- "prProblem": "A problem.",
679
- "prFix": "A fix.",
680
- "problemScenes": [],
681
- "fixScenes": [],
682
- "verdictLine": "Converged.",
683
- "issueClasses": [
684
- {
685
- "plainName": "A class with a non-numeric count",
686
- "count": "x",
687
- "severity": "P2",
688
- "category": "bug",
689
- "status": "fixed",
690
- "cause": "A grounded cause.",
691
- "medium": "text",
692
- "beforeLines": [],
693
- "afterLines": [],
694
- }
695
- ],
696
- }
697
- summary_path = tmp_path / "summary-bad-count.json"
698
- summary_path.write_text(json.dumps(summary), encoding="utf-8")
699
-
700
- out_path = tmp_path / "report-bad-count.html"
701
- completed = _render_cli_with_summary_file(
702
- journal_destination, out_path, summary_path
703
- )
704
-
705
- assert completed.returncode == 0, (
706
- f"CLI crashed on non-numeric count:\n{completed.stderr}"
707
- )
708
- html_content = out_path.read_text(encoding="utf-8")
709
- assert "A class with a non-numeric count" in html_content
710
- assert "0 findings" in html_content
711
-
712
-
713
- def test_cli_renders_degraded_body_when_summary_is_a_list(tmp_path: Path) -> None:
714
- """Should render the degraded layout and exit 0 when --summary-file holds a list."""
715
- run_root = tmp_path / "wf_run_list_summary"
716
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
717
-
718
- summary_path = tmp_path / "summary-list.json"
719
- summary_path.write_text(json.dumps([]), encoding="utf-8")
720
-
721
- out_path = tmp_path / "report-list-summary.html"
722
- completed = _render_cli_with_summary_file(
723
- journal_destination, out_path, summary_path
724
- )
725
-
726
- assert completed.returncode == 0, (
727
- f"CLI crashed on a list summary:\n{completed.stderr}"
728
- )
729
- html_content = out_path.read_text(encoding="utf-8")
730
- assert "distinct findings across 4 rounds" in html_content
731
- assert 'class="pf-grid"' not in html_content
732
-
733
-
734
- def test_cli_renders_degraded_body_when_summary_is_a_scalar(tmp_path: Path) -> None:
735
- """Should render the degraded layout and exit 0 when --summary-file holds a scalar."""
736
- run_root = tmp_path / "wf_run_scalar_summary"
737
- journal_destination = _copy_run_tree_without_summary_entry(run_root)
738
-
739
- summary_path = tmp_path / "summary-scalar.json"
740
- summary_path.write_text(json.dumps(5), encoding="utf-8")
741
-
742
- out_path = tmp_path / "report-scalar-summary.html"
743
- completed = _render_cli_with_summary_file(
744
- journal_destination, out_path, summary_path
745
- )
746
-
747
- assert completed.returncode == 0, (
748
- f"CLI crashed on a scalar summary:\n{completed.stderr}"
749
- )
750
- html_content = out_path.read_text(encoding="utf-8")
751
- assert "distinct findings across 4 rounds" in html_content
752
- assert 'class="pf-grid"' not in html_content
753
-
754
-
755
- def test_is_summary_structurally_valid_false_for_non_dict_summary() -> None:
756
- """Should return False for a list, string, or scalar summary, never raising."""
757
- assert render_report._is_summary_structurally_valid([]) is False
758
- assert render_report._is_summary_structurally_valid("str") is False
759
- assert render_report._is_summary_structurally_valid(5) is False
760
-
761
-
762
- def test_cli_rejects_orphaned_repo_argument(tmp_path: Path) -> None:
763
- """Should reject --repo with a usage error, proving the flag is no longer declared."""
764
- render_script = Path(__file__).resolve().parent / "render_report.py"
765
- out_path = tmp_path / "report-repo-rejected.html"
766
- completed = subprocess.run(
767
- [
768
- sys.executable,
769
- str(render_script),
770
- "--journal",
771
- str(FIXTURE_JOURNAL),
772
- "--out",
773
- str(out_path),
774
- "--pr",
775
- "example-owner/example-repo#211",
776
- "--final-sha",
777
- "7c2f420c4d5b7c83aa47f93d99a0f1420e3373c4",
778
- "--rounds",
779
- "4",
780
- "--repo",
781
- ".",
782
- ],
783
- capture_output=True,
784
- text=True,
785
- )
786
-
787
- assert completed.returncode != 0
788
- assert "unrecognized arguments: --repo" in completed.stderr
789
-
790
-
791
- def test_robustness_with_missing_transcripts(tmp_path: Path) -> None:
792
- """Should exit 0 and render the timeline and appendix when no transcripts exist."""
793
- run_root = tmp_path / "wf_run"
794
- journal_destination = run_root / "workflows" / FIXTURE_JOURNAL.name
795
- journal_destination.parent.mkdir(parents=True)
796
- shutil.copy(FIXTURE_JOURNAL, journal_destination)
797
-
798
- run_id = FIXTURE_JOURNAL.stem
799
- empty_agents_dir = run_root / "subagents" / "workflows" / run_id
800
- empty_agents_dir.mkdir(parents=True)
801
-
802
- out_path = tmp_path / "report-robust.html"
803
- completed = _render_cli(journal_destination, out_path)
804
-
805
- assert completed.returncode == 0, (
806
- f"Render failed despite missing transcripts:\n{completed.stderr}"
807
- )
808
-
809
- html_content = out_path.read_text(encoding="utf-8")
810
- assert "PR #211 Convergence Summary" in html_content
811
- assert 'class="timeline"' not in html_content
812
- assert 'class="pf-grid"' not in html_content