claude-dev-env 2.3.0 → 2.5.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.
- package/CLAUDE.md +53 -48
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +84 -0
- package/_shared/pr-loop/scripts/code_rules_gate.py +4 -2
- package/_shared/pr-loop/scripts/grant_project_claude_permissions.py +306 -306
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/claude_permissions_constants.py +44 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/copilot_quota_constants.py +24 -24
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/stale_worktree_rule_sweep_constants.py +107 -107
- package/_shared/pr-loop/scripts/revoke_project_claude_permissions.py +290 -48
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +42 -2
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +100 -1
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -497
- package/_shared/pr-loop/scripts/tests/test_revoke_project_claude_permissions.py +311 -2
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep.py +301 -301
- package/_shared/pr-loop/scripts/tests/test_stale_worktree_rule_sweep_constants.py +85 -85
- package/_shared/pr-loop/worker-spawn.md +1 -1
- package/agents/CLAUDE.md +3 -1
- package/agents/caveman.md +0 -1
- package/agents/clasp-deployment-orchestrator.md +0 -1
- package/agents/clean-coder.md +0 -1
- package/agents/code-advisor.md +0 -1
- package/agents/code-quality-agent.md +1 -2
- package/agents/code-verifier.md +3 -4
- package/agents/deep-research.md +0 -1
- package/agents/docs-agent.md +0 -1
- package/agents/git-commit-crafter.md +0 -1
- package/agents/issue-tracker.md +42 -0
- package/agents/plan-packet-validator.md +0 -1
- package/agents/pr-description-writer.md +0 -1
- package/agents/skill-writer-agent.md +84 -0
- package/agents/test_agent_frontmatter.py +67 -18
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +105 -3
- package/audit-rubrics/prompts/category-o-docstring-vs-impl-drift.md +29 -13
- package/bin/CLAUDE.md +68 -5
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/install-constants.mjs +88 -0
- package/bin/install.mjs +1138 -114
- package/bin/install.prune.test.mjs +869 -19
- package/bin/install.test.mjs +906 -2
- package/commands/implement.md +1 -1
- package/commands/right-size.md +1 -1
- package/docs/CLAUDE.md +2 -0
- package/docs/host-pool-health-monitor.md +102 -0
- package/docs/references/CLAUDE.md +5 -2
- package/docs/references/advisor-tool.md +13 -0
- package/docs/references/code-review-enforcement.md +107 -0
- package/docs/references/team-advisor-skill.md +14 -0
- package/docs/wsl-docker-cowork-starter-matrix.md +89 -0
- package/hooks/blocking/CLAUDE.md +9 -1
- package/hooks/blocking/code_review_enforcement_config_bootstrap.py +53 -0
- package/hooks/blocking/code_review_gate_deny.py +74 -0
- package/hooks/blocking/code_review_pr_create_gate.py +198 -0
- package/hooks/blocking/code_review_push_gate.py +145 -0
- package/hooks/blocking/code_review_stamp_directory_write_blocker.py +348 -0
- package/hooks/blocking/code_review_stamp_store.py +233 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +7 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +15 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +212 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +138 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +49 -0
- package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +38 -0
- package/hooks/blocking/code_verifier_spawn_preflight_gate.py +39 -27
- package/hooks/blocking/config/__init__.py +5 -5
- package/hooks/blocking/config/code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/test_code_review_enforcement_constants.py +113 -0
- package/hooks/blocking/config/verified_commit_constants.py +160 -155
- package/hooks/blocking/conftest.py +2 -0
- package/hooks/blocking/convergence_gate_blocker.py +112 -23
- package/hooks/blocking/destructive_command_blocker.py +19 -6
- package/hooks/blocking/orchestrator_refresh_reschedule_gate.py +256 -0
- package/hooks/blocking/pr_description_proof_of_work.py +52 -34
- package/hooks/blocking/pre_tool_use_dispatcher.py +24 -24
- package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +4 -1
- package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +62 -0
- package/hooks/blocking/test_code_review_gate_deny.py +54 -0
- package/hooks/blocking/test_code_review_pr_create_gate.py +199 -0
- package/hooks/blocking/test_code_review_push_gate.py +205 -0
- package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +199 -0
- package/hooks/blocking/test_code_review_stamp_store.py +205 -0
- package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +124 -2
- package/hooks/blocking/test_convergence_gate_blocker.py +153 -5
- package/hooks/blocking/test_destructive_command_blocker.py +1 -1
- package/hooks/blocking/test_destructive_command_blocker_deny_mode.py +45 -0
- package/hooks/blocking/test_orchestrator_refresh_reschedule_gate.py +231 -0
- package/hooks/blocking/test_pr_description_proof_of_work.py +151 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +17 -8
- package/hooks/blocking/test_verdict_directory_write_blocker.py +808 -808
- package/hooks/blocking/test_verification_verdict_store.py +974 -903
- package/hooks/blocking/test_verified_commit_gate.py +581 -581
- package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +131 -131
- package/hooks/blocking/test_volatile_path_in_post_blocker.py +114 -2
- package/hooks/blocking/verdict_directory_write_blocker.py +687 -687
- package/hooks/blocking/verification_verdict_store.py +1039 -1014
- package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +29 -17
- package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +35 -0
- package/hooks/blocking/verified_commit_message_accuracy_blocker.py +167 -167
- package/hooks/blocking/verifier_verdict_minter.py +280 -280
- package/hooks/blocking/volatile_path_in_post_blocker.py +69 -8
- package/hooks/git-hooks/git_hooks_constants/__init__.py +6 -0
- package/hooks/git-hooks/pre_push.py +89 -2
- package/hooks/git-hooks/test_pre_push.py +128 -0
- package/hooks/hooks.json +26 -1
- package/hooks/hooks_constants/CLAUDE.md +3 -1
- package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +8 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -0
- package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +26 -11
- package/hooks/hooks_constants/convergence_gate_blocker_constants.py +20 -3
- package/hooks/hooks_constants/destructive_command_segment_constants.py +3 -1
- package/hooks/hooks_constants/enter_worktree_prefetch_constants.py +18 -18
- package/hooks/hooks_constants/orchestrator_refresh_reschedule_gate_constants.py +48 -0
- package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -4
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/pyproject_config_discovery_constants.py +16 -0
- package/hooks/hooks_constants/ruff_integration_constants.py +16 -0
- package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +24 -0
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +6 -0
- package/hooks/hooks_constants/volatile_path_in_post_blocker_constants.py +8 -1
- package/hooks/lifecycle/enter_worktree_origin_prefetch.py +163 -146
- package/hooks/lifecycle/test_enter_worktree_origin_prefetch.py +185 -178
- package/hooks/pyproject.toml +1 -0
- package/hooks/validators/CLAUDE.md +2 -0
- package/hooks/validators/config/__init__.py +0 -0
- package/hooks/validators/config/directory_exemption_constants.py +183 -0
- package/hooks/validators/config/test_directory_exemption_constants.py +21 -0
- package/hooks/validators/conftest.py +4 -0
- package/hooks/validators/mypy_integration.py +63 -50
- package/hooks/validators/pyproject_config_discovery.py +101 -0
- package/hooks/validators/ruff_integration.py +257 -25
- package/hooks/validators/run_all_validators.py +223 -19
- package/hooks/validators/test_directory_exemption_constants.py +185 -0
- package/hooks/validators/test_mypy_integration.py +32 -0
- package/hooks/validators/test_pyproject_config_discovery.py +94 -0
- package/hooks/validators/test_python_antipattern_checks.py +110 -5
- package/hooks/validators/test_ruff_integration.py +160 -2
- package/hooks/validators/test_run_all_validators.py +140 -68
- package/hooks/validators/test_run_all_validators_config_discovery.py +123 -0
- package/hooks/validators/test_run_all_validators_pretooluse.py +159 -1
- package/package.json +10 -2
- package/rules/CLAUDE.md +1 -0
- package/rules/docstring-prose-matches-implementation.md +45 -67
- package/rules/durable-post-artifacts.md +7 -0
- package/rules/state-what-is.md +25 -0
- package/rules/verified-commit-gate-skip.md +1 -1
- package/scripts/CLAUDE.md +1 -0
- package/scripts/Capture-PoolHealth.ps1 +410 -0
- package/scripts/_code_review_test_support.py +404 -0
- package/scripts/claude_chain_runner.py +141 -1
- package/scripts/codec_forwarding_test_support.py +83 -0
- package/scripts/conftest.py +23 -0
- package/scripts/dev_env_scripts_constants/CLAUDE.md +2 -2
- package/scripts/dev_env_scripts_constants/claude_chain_constants.py +53 -1
- package/scripts/dev_env_scripts_constants/code_review_constants.py +129 -12
- package/scripts/dev_env_scripts_constants/test_code_review_constants.py +55 -0
- package/scripts/invoke_code_review.py +550 -38
- package/scripts/resolve_worker_spawn.py +626 -619
- package/scripts/spawn_grok_batch.py +672 -672
- package/scripts/test_claude_chain_runner.py +131 -0
- package/scripts/test_invoke_code_review_chain.py +70 -0
- package/scripts/test_invoke_code_review_cli.py +192 -0
- package/scripts/test_invoke_code_review_codec.py +77 -0
- package/scripts/test_invoke_code_review_contract.py +256 -0
- package/scripts/test_invoke_code_review_git.py +123 -0
- package/scripts/test_invoke_code_review_mode.py +99 -0
- package/scripts/test_resolve_worker_spawn.py +1014 -1014
- package/scripts/test_resolve_worker_spawn_codec.py +101 -0
- package/skills/CLAUDE.md +2 -0
- package/skills/auditing-claude-config/SKILL.md +114 -114
- package/skills/autoconverge/SKILL.md +427 -421
- package/skills/autoconverge/reference/convergence.md +26 -4
- package/skills/autoconverge/reference/multi-pr.md +6 -1
- package/skills/autoconverge/reference/stop-conditions.md +16 -10
- package/skills/autoconverge/workflow/CLAUDE.md +1 -0
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +4 -4
- package/skills/autoconverge/workflow/converge.codex-gate.test.mjs +175 -3
- package/skills/autoconverge/workflow/converge.contract.test.mjs +1263 -1244
- package/skills/autoconverge/workflow/converge.mjs +191 -8
- package/skills/autoconverge/workflow/converge.p2-advance.test.mjs +202 -0
- package/skills/autoconverge/workflow/converge_multi.mjs +7 -3
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +5 -0
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a11d903476b803493.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a26213978adeef6fb.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a3def0d15ed9d9110.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a41f41b1b708ee3b7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a758b880abecc3ff7.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-a8897b89656b1bd16.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-abd463d744a1437bc.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/subagents/workflows/wf_881252e6-700/agent-ad19d027ae8ee1816.jsonl +2 -2
- package/skills/autoconverge/workflow/fixtures/wf_run/workflows/wf_881252e6-700.json +265 -265
- package/skills/closeout/SKILL.md +33 -50
- package/skills/codex-review/scripts/codex_review_scripts_constants/run_constants.py +8 -0
- package/skills/codex-review/scripts/run_codex_review.py +233 -1
- package/skills/codex-review/scripts/test_run_codex_review.py +189 -0
- package/skills/condensing-instructions/SKILL.md +81 -0
- package/skills/copilot-review/SKILL.md +119 -119
- package/skills/e-code-review/SKILL.md +52 -0
- package/skills/e-code-review/reference/fix.md +54 -0
- package/skills/e-code-review/reference/loop.md +43 -0
- package/skills/e-code-review/reference/low.md +57 -0
- package/skills/e-code-review/reference/medium.md +153 -0
- package/skills/e-code-review/reference/xhigh.md +182 -0
- package/skills/e-simplify/SKILL.md +97 -0
- package/skills/fresh-branch/CLAUDE.md +2 -0
- package/skills/fresh-branch/SKILL.md +2 -0
- package/skills/fresh-branch/scripts/create_fresh_branch.py +78 -180
- package/skills/fresh-branch/scripts/fresh_branch_git_commands.py +285 -0
- package/skills/fresh-branch/scripts/fresh_branch_scripts_constants/fresh_branch_cli_constants.py +1 -0
- package/skills/fresh-branch/scripts/pytest.ini +4 -0
- package/skills/fresh-branch/scripts/test_create_fresh_branch.py +98 -0
- package/skills/fresh-branch/scripts/test_fresh_branch_git_commands.py +310 -0
- package/skills/issue-tracker/SKILL.md +92 -0
- package/skills/issue-tracker/reference/epic-and-sub-issue-model.md +55 -0
- package/skills/issue-tracker/reference/handoff-schema.md +64 -0
- package/skills/issue-tracker/reference/operation-matrix.md +41 -0
- package/skills/orchestrator/SKILL.md +162 -21
- package/skills/orchestrator/scripts/status_gate.py +625 -0
- package/skills/orchestrator/scripts/status_gate_constants/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/__init__.py +1 -0
- package/skills/orchestrator/scripts/status_gate_constants/config/constants.py +47 -0
- package/skills/orchestrator/scripts/test_status_gate.py +439 -0
- package/skills/orchestrator-refresh/SKILL.md +110 -35
- package/skills/plan-to-pr/SKILL.md +155 -0
- package/skills/plan-to-pr/reference/final-validation-tasks.md +15 -0
- package/skills/plan-to-pr/reference/model-routing.md +36 -0
- package/skills/plan-to-pr/reference/packet-contract.md +43 -0
- package/skills/plan-to-pr/reference/packet-schema.json +57 -0
- package/skills/plan-to-pr/reference/process-inventory.md +22 -0
- package/skills/plan-to-pr/reference/review-loop.md +33 -0
- package/skills/plan-to-pr/reference/run-record.schema.json +27 -0
- package/skills/plan-to-pr/reference/self-audit-tasks.md +15 -0
- package/skills/plan-to-pr/reference/task-seeds.md +14 -0
- package/skills/plan-to-pr/reference/task-ticket.md +38 -0
- package/skills/plan-to-pr/scripts/config/__init__.py +1 -0
- package/skills/plan-to-pr/scripts/config/constants.py +193 -0
- package/skills/plan-to-pr/scripts/create_packet.py +173 -0
- package/skills/plan-to-pr/scripts/test_create_packet.py +102 -0
- package/skills/plan-to-pr/scripts/test_validate_packet.py +256 -0
- package/skills/plan-to-pr/scripts/test_validate_protocol.py +135 -0
- package/skills/plan-to-pr/scripts/test_validate_run.py +158 -0
- package/skills/plan-to-pr/scripts/validate_packet.py +655 -0
- package/skills/plan-to-pr/scripts/validate_protocol.py +622 -0
- package/skills/plan-to-pr/scripts/validate_run.py +173 -0
- package/skills/plan-to-pr/test_skill_contract.py +207 -0
- package/skills/plan-to-pr/test_task_ticket_contract.py +151 -0
- package/skills/pr-converge/SKILL.md +472 -469
- package/skills/pr-converge/reference/examples.md +3 -3
- package/skills/pr-converge/reference/fix-protocol.md +1 -1
- package/skills/pr-converge/reference/ground-rules.md +7 -4
- package/skills/pr-converge/reference/multi-pr-orchestration.md +4 -1
- package/skills/pr-converge/reference/per-tick.md +5 -5
- package/skills/pr-converge/reference/progress-checklist.md +1 -1
- package/skills/pr-converge/scripts/check_convergence_gates.py +279 -279
- package/skills/pr-converge/scripts/test_check_convergence_codex.py +507 -507
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +84 -84
- package/skills/pr-converge/test_step5_host_branch.py +1 -1
- package/skills/pr-fix-protocol/SKILL.md +1 -1
- package/skills/privacy-hygiene/SKILL.md +68 -68
- package/skills/prototype/SKILL.md +86 -0
- package/skills/prototype/reference/honest-limitations.md +23 -0
- package/skills/prototype/reference/promotion-tasks.md +23 -0
- package/skills/prototype/scripts/build_sandbox_settings.py +249 -0
- package/skills/prototype/scripts/conftest.py +15 -0
- package/skills/prototype/scripts/launch_sandbox.py +205 -0
- package/skills/prototype/scripts/probe_sandbox_safety.py +311 -0
- package/skills/prototype/scripts/prototype_scripts_constants/__init__.py +1 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/__init__.py +0 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/build_sandbox_settings_constants.py +41 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/launch_sandbox_constants.py +23 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/probe_sandbox_safety_constants.py +45 -0
- package/skills/prototype/scripts/prototype_scripts_constants/config/prototype_common_constants.py +10 -0
- package/skills/prototype/scripts/test_build_sandbox_settings.py +275 -0
- package/skills/prototype/scripts/test_launch_sandbox.py +303 -0
- package/skills/prototype/scripts/test_probe_sandbox_safety.py +284 -0
- package/skills/prototype/workflows/promotion.md +27 -0
- package/skills/prototype/workflows/sandbox.md +35 -0
- package/skills/release-notes-html/SKILL.md +164 -0
- package/skills/skill-builder/CLAUDE.md +3 -3
- package/skills/skill-builder/SKILL.md +5 -5
- package/skills/skill-builder/references/CLAUDE.md +1 -1
- package/skills/skill-builder/references/delegation-map.md +3 -3
- package/skills/skill-builder/references/description-field.md +1 -1
- package/skills/skill-builder/references/skill-modularity.md +2 -3
- package/skills/skill-builder/workflows/CLAUDE.md +1 -1
- package/skills/skill-builder/workflows/improve-skill.md +1 -1
- package/skills/skill-builder/workflows/new-skill.md +2 -2
- package/skills/task-build/CLAUDE.md +8 -7
- package/skills/task-build/SKILL.md +16 -8
- package/skills/task-build/reference/tool-routing.md +19 -0
- package/skills/team-advisor/SKILL.md +2 -2
- package/scripts/test_invoke_code_review.py +0 -672
- package/skills/closeout/reference/issue-body-templates.md +0 -108
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
"""Behavioral tests for the review and Bugbot convergence gate leaves.
|
|
2
|
-
|
|
3
|
-
::
|
|
4
|
-
|
|
5
|
-
_flatten_paginated_reviews(two pages) -> newest-first flat list
|
|
6
|
-
_bugbot_run_conclusion_detail(success) -> (True, "check run ...")
|
|
7
|
-
_check_bugbot(named complete run) -> (True, "check run ...")
|
|
8
|
-
|
|
9
|
-
Each test drives the real leaf, stubbing only the ``gh`` transport helpers.
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
|
-
import json
|
|
15
|
-
|
|
16
|
-
import pytest
|
|
17
|
-
|
|
18
|
-
import check_convergence_gates as gates
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_flatten_paginated_reviews_flattens_and_sorts_newest_first() -> None:
|
|
22
|
-
pages = [
|
|
23
|
-
[{"id": 1, "submitted_at": "2026-01-01T00:00:00Z"}],
|
|
24
|
-
[{"id": 2, "submitted_at": "2026-03-01T00:00:00Z"}],
|
|
25
|
-
]
|
|
26
|
-
flattened = gates._flatten_paginated_reviews(json.dumps(pages))
|
|
27
|
-
assert flattened is not None
|
|
28
|
-
assert [each_review["id"] for each_review in flattened] == [2, 1]
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def test_flatten_paginated_reviews_returns_none_for_non_list_payload() -> None:
|
|
32
|
-
assert (
|
|
33
|
-
gates._flatten_paginated_reviews(json.dumps({"message": "Not Found"})) is None
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_bugbot_run_conclusion_detail_passes_on_a_complete_conclusion() -> None:
|
|
38
|
-
complete_conclusion = gates.ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS[0]
|
|
39
|
-
passed, detail = gates._bugbot_run_conclusion_detail(
|
|
40
|
-
{"id": 55, "conclusion": complete_conclusion, "html_url": ""}
|
|
41
|
-
)
|
|
42
|
-
assert passed is True
|
|
43
|
-
assert "check run 55" in detail
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def test_check_bugbot_reports_success_when_the_named_run_is_complete(
|
|
47
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
48
|
-
) -> None:
|
|
49
|
-
complete_conclusion = gates.ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS[0]
|
|
50
|
-
run_name = f"x {gates.BUGBOT_CHECK_RUN_NAME_SUBSTRING} y"
|
|
51
|
-
payload = {
|
|
52
|
-
"check_runs": [
|
|
53
|
-
{
|
|
54
|
-
"name": run_name,
|
|
55
|
-
"id": 9,
|
|
56
|
-
"conclusion": complete_conclusion,
|
|
57
|
-
"html_url": "",
|
|
58
|
-
}
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
def _stub_gh_api(endpoint_path: str) -> tuple[int, str]:
|
|
63
|
-
return 0, json.dumps(payload)
|
|
64
|
-
|
|
65
|
-
monkeypatch.setattr(gates, "_gh_api", _stub_gh_api)
|
|
66
|
-
passed, detail = gates._check_bugbot(owner="o", repo="r", sha="abc")
|
|
67
|
-
assert passed is True
|
|
68
|
-
assert "check run 9" in detail
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def should_evaluate_mergeable_from_pr_object_clean() -> None:
|
|
72
|
-
passed, detail = gates._evaluate_mergeable_from_pr_object(
|
|
73
|
-
{"mergeable": True, "mergeable_state": "clean"}
|
|
74
|
-
)
|
|
75
|
-
assert passed is True
|
|
76
|
-
assert detail == "clean"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
def should_evaluate_mergeable_from_pr_object_unknown() -> None:
|
|
80
|
-
passed, detail = gates._evaluate_mergeable_from_pr_object(
|
|
81
|
-
{"mergeable": None, "mergeable_state": "unknown"}
|
|
82
|
-
)
|
|
83
|
-
assert passed is False
|
|
84
|
-
assert detail == "unknown"
|
|
1
|
+
"""Behavioral tests for the review and Bugbot convergence gate leaves.
|
|
2
|
+
|
|
3
|
+
::
|
|
4
|
+
|
|
5
|
+
_flatten_paginated_reviews(two pages) -> newest-first flat list
|
|
6
|
+
_bugbot_run_conclusion_detail(success) -> (True, "check run ...")
|
|
7
|
+
_check_bugbot(named complete run) -> (True, "check run ...")
|
|
8
|
+
|
|
9
|
+
Each test drives the real leaf, stubbing only the ``gh`` transport helpers.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
|
|
16
|
+
import pytest
|
|
17
|
+
|
|
18
|
+
import check_convergence_gates as gates
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_flatten_paginated_reviews_flattens_and_sorts_newest_first() -> None:
|
|
22
|
+
pages = [
|
|
23
|
+
[{"id": 1, "submitted_at": "2026-01-01T00:00:00Z"}],
|
|
24
|
+
[{"id": 2, "submitted_at": "2026-03-01T00:00:00Z"}],
|
|
25
|
+
]
|
|
26
|
+
flattened = gates._flatten_paginated_reviews(json.dumps(pages))
|
|
27
|
+
assert flattened is not None
|
|
28
|
+
assert [each_review["id"] for each_review in flattened] == [2, 1]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_flatten_paginated_reviews_returns_none_for_non_list_payload() -> None:
|
|
32
|
+
assert (
|
|
33
|
+
gates._flatten_paginated_reviews(json.dumps({"message": "Not Found"})) is None
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_bugbot_run_conclusion_detail_passes_on_a_complete_conclusion() -> None:
|
|
38
|
+
complete_conclusion = gates.ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS[0]
|
|
39
|
+
passed, detail = gates._bugbot_run_conclusion_detail(
|
|
40
|
+
{"id": 55, "conclusion": complete_conclusion, "html_url": ""}
|
|
41
|
+
)
|
|
42
|
+
assert passed is True
|
|
43
|
+
assert "check run 55" in detail
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_check_bugbot_reports_success_when_the_named_run_is_complete(
|
|
47
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
48
|
+
) -> None:
|
|
49
|
+
complete_conclusion = gates.ALL_BUGBOT_CHECK_RUN_COMPLETE_CONCLUSIONS[0]
|
|
50
|
+
run_name = f"x {gates.BUGBOT_CHECK_RUN_NAME_SUBSTRING} y"
|
|
51
|
+
payload = {
|
|
52
|
+
"check_runs": [
|
|
53
|
+
{
|
|
54
|
+
"name": run_name,
|
|
55
|
+
"id": 9,
|
|
56
|
+
"conclusion": complete_conclusion,
|
|
57
|
+
"html_url": "",
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
def _stub_gh_api(endpoint_path: str) -> tuple[int, str]:
|
|
63
|
+
return 0, json.dumps(payload)
|
|
64
|
+
|
|
65
|
+
monkeypatch.setattr(gates, "_gh_api", _stub_gh_api)
|
|
66
|
+
passed, detail = gates._check_bugbot(owner="o", repo="r", sha="abc")
|
|
67
|
+
assert passed is True
|
|
68
|
+
assert "check run 9" in detail
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def should_evaluate_mergeable_from_pr_object_clean() -> None:
|
|
72
|
+
passed, detail = gates._evaluate_mergeable_from_pr_object(
|
|
73
|
+
{"mergeable": True, "mergeable_state": "clean"}
|
|
74
|
+
)
|
|
75
|
+
assert passed is True
|
|
76
|
+
assert detail == "clean"
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def should_evaluate_mergeable_from_pr_object_unknown() -> None:
|
|
80
|
+
passed, detail = gates._evaluate_mergeable_from_pr_object(
|
|
81
|
+
{"mergeable": None, "mergeable_state": "unknown"}
|
|
82
|
+
)
|
|
83
|
+
assert passed is False
|
|
84
|
+
assert detail == "unknown"
|
|
@@ -32,7 +32,7 @@ NEVER_PUSHES_PHRASE = "never pushes"
|
|
|
32
32
|
EMPTY_STDIN_PHRASE = "empty"
|
|
33
33
|
CWD_FLAG = "--cwd"
|
|
34
34
|
OPUS_MODEL = "opus"
|
|
35
|
-
HIGH_EFFORT_SLASH = "/code-review
|
|
35
|
+
HIGH_EFFORT_SLASH = "/code-review ultra --fix"
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def _read_markdown(markdown_path: Path) -> str:
|
|
@@ -20,7 +20,7 @@ The caller passes: its identity, the PR scope, the PR worktree path, this round'
|
|
|
20
20
|
|
|
21
21
|
## Executor choice
|
|
22
22
|
|
|
23
|
-
- **Single-PR loops** (no shared `state.json`): the lead spawns `Agent(subagent_type: "clean-coder")` to write the fix. Stop when `Agent` is unavailable. A spawned clean-coder starts in its own working directory, so its prompt names the PR worktree path and directs it to edit, stage, and commit there.
|
|
23
|
+
- **Single-PR loops** (no shared `state.json`): the lead spawns `Agent(subagent_type: "clean-coder", model: "sonnet")` — worker-model routing per [`skills/orchestrator/SKILL.md`](../orchestrator/SKILL.md#workflow-agent-routing); resolver-supplied sonnet-equivalent on third-party hosts — to write the fix. Stop when `Agent` is unavailable. A spawned clean-coder starts in its own working directory, so its prompt names the PR worktree path and directs it to edit, stage, and commit there.
|
|
24
24
|
- **Multi-PR orchestration** (shared `state.json`): a per-PR clean-coder teammate owns edits, replies, and state writes; the orchestrator holds back from inline edits. The teammate obligations — reply before writing state, which state fields to set, idle handoff — live in the calling skill's multi-PR reference.
|
|
25
25
|
|
|
26
26
|
Run every git command in the PR worktree. `git add`, `git commit`, and `git push` act on the repo of the current working directory, so a cross-repo PR's fix lands in the PR's repo only when the working directory is its worktree.
|
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: privacy-hygiene
|
|
3
|
-
description: Full-repo sweep for personal data and secrets before commit or durable GitHub post. Use when preparing a PR, cleaning a leak, or when `pii_prevention_blocker` denies a write, post, or commit. Triggers on "privacy hygiene", "personal data", "secret sweep", "sanitize repository", "/privacy-hygiene".
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# privacy-hygiene
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
Find and remove personal data and high-confidence secrets before they land in git history or a durable GitHub post. The `pii_prevention_blocker` hook blocks the common cases at write, post, and commit time. This skill is the full sweep when you need a broader pass or a remediation plan.
|
|
11
|
-
|
|
12
|
-
**Announce at start:** "Running privacy-hygiene sweep."
|
|
13
|
-
|
|
14
|
-
## When to run a full sweep
|
|
15
|
-
|
|
16
|
-
- Before the first push of a branch that touched logs, screenshots, config samples, or machine-local paths
|
|
17
|
-
- After a hook block on email, home path, LAN address, or secret material
|
|
18
|
-
- Before opening a PR to a repository that is public (or will be made public)
|
|
19
|
-
- After pasting support tickets, env dumps, or terminal transcripts into the tree
|
|
20
|
-
|
|
21
|
-
## What the automated gate blocks
|
|
22
|
-
|
|
23
|
-
| Category | Blocked examples | Allowed residual |
|
|
24
|
-
|---|---|---|
|
|
25
|
-
| Email | `user@example.com` | `user@example.com`, `user@example.org`, `user@example.net` |
|
|
26
|
-
| Home path | `C:/Users/example/...`, `/Users/example/...`, `/home/example/...` | `C:/Users/example/...`, `C:/Users/<you>/...`, `/Users/alice/...` |
|
|
27
|
-
| LAN address | Unlisted `10.x` / `172.16–31.x` / `192.168.x` | Public addresses; your NAS host from `CLAUDE_NAS_HOST` or `~/.claude/local-identity.json`; entries in `ALL_ALLOWLISTED_PRIVATE_IP_ADDRESSES` |
|
|
28
|
-
| Secret | `ghp_…`, `github_pat_…`, `AKIA…`, PEM private-key headers | Public keys, redacted `***`, env var names without values |
|
|
29
|
-
|
|
30
|
-
Surfaces:
|
|
31
|
-
|
|
32
|
-
1. **Write / Edit / MultiEdit** — payload text about to land on disk (via PreToolUse dispatcher)
|
|
33
|
-
2. **Durable posts** — `gh pr/issue create|comment|edit|review` bodies and GitHub MCP body/comment fields (Bash and PowerShell)
|
|
34
|
-
3. **git commit** — staged blob text (non-exempt paths) on Bash and PowerShell, including `git.exe` and flag forms (`--no-verify`, `-c`, `-C`). Commit message bodies (`-m` / `-F`) are out of scope for the automated gate
|
|
35
|
-
|
|
36
|
-
## Sweep procedure
|
|
37
|
-
|
|
38
|
-
Run the full-tree sweep in
|
|
39
|
-
[`reference/sweep-procedure.md`](reference/sweep-procedure.md): scope the tree,
|
|
40
|
-
run the ripgrep pass for the four high-confidence pattern families (email, home
|
|
41
|
-
path, LAN address, secret), review each hit against the ignore list, and
|
|
42
|
-
remediate. It also lists the accepted residual — what to leave in place rather
|
|
43
|
-
than over-scrub. The ripgrep command is the only full-tree pass; the write-time
|
|
44
|
-
`pii_prevention_blocker` scans one payload at a time.
|
|
45
|
-
|
|
46
|
-
## Enable on any machine / public repository
|
|
47
|
-
|
|
48
|
-
Install or reinstall the package so hooks and this skill land under `~/.claude/`:
|
|
49
|
-
|
|
50
|
-
```
|
|
51
|
-
cd packages/claude-dev-env
|
|
52
|
-
node bin/install.mjs
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
Hooks register via `hooks/hooks.json` into `~/.claude/settings.json`. Once installed, the same gates apply in every repository the agent touches — private or public.
|
|
56
|
-
|
|
57
|
-
## Open knobs
|
|
58
|
-
|
|
59
|
-
- **NAS / LAN allowlist:** Unlisted private IPs are blocked. The scanner resolves your NAS host from `CLAUDE_NAS_HOST`, then `~/.claude/local-identity.json` (`nas.host`), and allowlists it when it is a private address, so the committed tree holds no real host. `ALL_ALLOWLISTED_PRIVATE_IP_ADDRESSES` in `hooks_constants` holds the static allowlist for any host every machine must share.
|
|
60
|
-
- **Commit-scan exempt repositories:** named owner/repo slugs skip the staged-commit PII scan only. Set `CLAUDE_PII_EXEMPT_REPOS` (comma-separated `owner/repo` values) or list them under `pii_exempt_repositories` in `~/.claude/local-identity.json`. Matching uses the repository's `remote.origin.url` and accepts only the exact host `github.com` (https, ssh scheme, or scp-style). A repository with no readable origin is never exempt (fail-closed to scanning). Write / Edit / MultiEdit and durable post bodies still scan in every repository.
|
|
61
|
-
- **Public maintainer identity:** when a real email or name is intentional product surface, keep it and note that in the PR body so reviewers do not treat it as a leak.
|
|
62
|
-
|
|
63
|
-
## What this skill does not do
|
|
64
|
-
|
|
65
|
-
- Does not rewrite git history without explicit user approval
|
|
66
|
-
- Does not rotate credentials for you
|
|
67
|
-
- Does not replace the write-time hook — it complements it
|
|
68
|
-
- Does not scan commit-message text (`-m` / `-F`); keep messages free of secrets yourself
|
|
1
|
+
---
|
|
2
|
+
name: privacy-hygiene
|
|
3
|
+
description: Full-repo sweep for personal data and secrets before commit or durable GitHub post. Use when preparing a PR, cleaning a leak, or when `pii_prevention_blocker` denies a write, post, or commit. Triggers on "privacy hygiene", "personal data", "secret sweep", "sanitize repository", "/privacy-hygiene".
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# privacy-hygiene
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Find and remove personal data and high-confidence secrets before they land in git history or a durable GitHub post. The `pii_prevention_blocker` hook blocks the common cases at write, post, and commit time. This skill is the full sweep when you need a broader pass or a remediation plan.
|
|
11
|
+
|
|
12
|
+
**Announce at start:** "Running privacy-hygiene sweep."
|
|
13
|
+
|
|
14
|
+
## When to run a full sweep
|
|
15
|
+
|
|
16
|
+
- Before the first push of a branch that touched logs, screenshots, config samples, or machine-local paths
|
|
17
|
+
- After a hook block on email, home path, LAN address, or secret material
|
|
18
|
+
- Before opening a PR to a repository that is public (or will be made public)
|
|
19
|
+
- After pasting support tickets, env dumps, or terminal transcripts into the tree
|
|
20
|
+
|
|
21
|
+
## What the automated gate blocks
|
|
22
|
+
|
|
23
|
+
| Category | Blocked examples | Allowed residual |
|
|
24
|
+
|---|---|---|
|
|
25
|
+
| Email | `user@example.com` | `user@example.com`, `user@example.org`, `user@example.net` |
|
|
26
|
+
| Home path | `C:/Users/example/...`, `/Users/example/...`, `/home/example/...` | `C:/Users/example/...`, `C:/Users/<you>/...`, `/Users/alice/...` |
|
|
27
|
+
| LAN address | Unlisted `10.x` / `172.16–31.x` / `192.168.x` | Public addresses; your NAS host from `CLAUDE_NAS_HOST` or `~/.claude/local-identity.json`; entries in `ALL_ALLOWLISTED_PRIVATE_IP_ADDRESSES` |
|
|
28
|
+
| Secret | `ghp_…`, `github_pat_…`, `AKIA…`, PEM private-key headers | Public keys, redacted `***`, env var names without values |
|
|
29
|
+
|
|
30
|
+
Surfaces:
|
|
31
|
+
|
|
32
|
+
1. **Write / Edit / MultiEdit** — payload text about to land on disk (via PreToolUse dispatcher)
|
|
33
|
+
2. **Durable posts** — `gh pr/issue create|comment|edit|review` bodies and GitHub MCP body/comment fields (Bash and PowerShell)
|
|
34
|
+
3. **git commit** — staged blob text (non-exempt paths) on Bash and PowerShell, including `git.exe` and flag forms (`--no-verify`, `-c`, `-C`). Commit message bodies (`-m` / `-F`) are out of scope for the automated gate
|
|
35
|
+
|
|
36
|
+
## Sweep procedure
|
|
37
|
+
|
|
38
|
+
Run the full-tree sweep in
|
|
39
|
+
[`reference/sweep-procedure.md`](reference/sweep-procedure.md): scope the tree,
|
|
40
|
+
run the ripgrep pass for the four high-confidence pattern families (email, home
|
|
41
|
+
path, LAN address, secret), review each hit against the ignore list, and
|
|
42
|
+
remediate. It also lists the accepted residual — what to leave in place rather
|
|
43
|
+
than over-scrub. The ripgrep command is the only full-tree pass; the write-time
|
|
44
|
+
`pii_prevention_blocker` scans one payload at a time.
|
|
45
|
+
|
|
46
|
+
## Enable on any machine / public repository
|
|
47
|
+
|
|
48
|
+
Install or reinstall the package so hooks and this skill land under `~/.claude/`:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
cd packages/claude-dev-env
|
|
52
|
+
node bin/install.mjs
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Hooks register via `hooks/hooks.json` into `~/.claude/settings.json`. Once installed, the same gates apply in every repository the agent touches — private or public.
|
|
56
|
+
|
|
57
|
+
## Open knobs
|
|
58
|
+
|
|
59
|
+
- **NAS / LAN allowlist:** Unlisted private IPs are blocked. The scanner resolves your NAS host from `CLAUDE_NAS_HOST`, then `~/.claude/local-identity.json` (`nas.host`), and allowlists it when it is a private address, so the committed tree holds no real host. `ALL_ALLOWLISTED_PRIVATE_IP_ADDRESSES` in `hooks_constants` holds the static allowlist for any host every machine must share.
|
|
60
|
+
- **Commit-scan exempt repositories:** named owner/repo slugs skip the staged-commit PII scan only. Set `CLAUDE_PII_EXEMPT_REPOS` (comma-separated `owner/repo` values) or list them under `pii_exempt_repositories` in `~/.claude/local-identity.json`. Matching uses the repository's `remote.origin.url` and accepts only the exact host `github.com` (https, ssh scheme, or scp-style). A repository with no readable origin is never exempt (fail-closed to scanning). Write / Edit / MultiEdit and durable post bodies still scan in every repository.
|
|
61
|
+
- **Public maintainer identity:** when a real email or name is intentional product surface, keep it and note that in the PR body so reviewers do not treat it as a leak.
|
|
62
|
+
|
|
63
|
+
## What this skill does not do
|
|
64
|
+
|
|
65
|
+
- Does not rewrite git history without explicit user approval
|
|
66
|
+
- Does not rotate credentials for you
|
|
67
|
+
- Does not replace the write-time hook — it complements it
|
|
68
|
+
- Does not scan commit-message text (`-m` / `-F`); keep messages free of secrets yourself
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: prototype
|
|
3
|
+
description: >-
|
|
4
|
+
Isolated hookless worktree sandbox for zero-friction proof-of-concept builds, then a clean-room re-verification that promotes a successful POC into a real deploy. Triggers: prototype, /prototype, proof of concept, POC, spike, throwaway build, build without hooks, sandbox this idea, hookless worktree, prototype then ship, promote the prototype.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Prototype
|
|
8
|
+
|
|
9
|
+
## Principle
|
|
10
|
+
|
|
11
|
+
Give a build the freedom to move fast, then make it earn the right to ship. Two phases, one hard wall between them:
|
|
12
|
+
|
|
13
|
+
- **Sandbox** — an isolated worktree where an agent runs under `claude --bare`, so none of the standards gates (TDD, code rules, verified-commit, plain-language, stage) fire. The agent builds a proof-of-concept with zero friction.
|
|
14
|
+
- **Promotion** — back in the normal, fully-hooked session, the successful POC goes through a clean-room re-verification before it becomes a commit and a pull request. Nothing from the sandbox rides along un-checked.
|
|
15
|
+
|
|
16
|
+
Two safety gates stay live even in the sandbox: personal-data blocking and destructive-command blocking. A worktree shares the real repo's `.git` store and `rm` reaches the whole disk, so these are containment, not the "delays" the sandbox is meant to shed.
|
|
17
|
+
|
|
18
|
+
## Gotchas
|
|
19
|
+
|
|
20
|
+
Highest-signal content. Append a bullet each time a run fails in a new way.
|
|
21
|
+
|
|
22
|
+
- A `--settings` file's hooks still load under `--bare` (the file is passed explicitly). That is the mechanism for keeping the two safety gates, not a bug.
|
|
23
|
+
- The two safety hooks must point at the real installed scripts and import their `*_constants` packages at runtime. `scripts/build_sandbox_settings.py` resolves each hook's command from the live `~/.claude/settings.json` and registers it on the matchers the sandbox needs — personal-data on Write, Edit, MultiEdit, and Bash; destructive-command on Bash — so the paths stay correct on any machine and both gates cover the write surface. Do not hand-write the hook paths or matchers, and do not inherit whatever matcher the live config happens to use (a personal-data gate wired only to a narrow tool leaves disk writes ungated).
|
|
24
|
+
- Under `--dangerously-skip-permissions` an `ask` decision is auto-resolved, so only a hard `deny` blocks a destructive command. The settings file carries an `env` block that sets `CLAUDE_DESTRUCTIVE_DENY_MODE`, which turns the destructive gate's terminal `ask` into a `deny`. The probe runs the gate under that env block and passes only on the hard deny.
|
|
25
|
+
|
|
26
|
+
**Refusal cases — first match wins:**
|
|
27
|
+
|
|
28
|
+
- **Not in a git repository.** Respond: `Prototype needs a git repo to branch a worktree from. Run this from inside one.`
|
|
29
|
+
- **The `fresh-branch` skill is not installed.** Respond: `Prototype composes fresh-branch to make the sandbox worktree, and it is not installed. Install claude-dev-env first.`
|
|
30
|
+
- **The `claude` CLI is not on PATH.** Respond: `The sandbox launches a headless claude session, and the claude CLI is not on PATH.`
|
|
31
|
+
|
|
32
|
+
## Process
|
|
33
|
+
|
|
34
|
+
Two phases. Run the sandbox phase first; run the promotion phase only when the POC succeeds and the user wants it shipped.
|
|
35
|
+
|
|
36
|
+
### Phase 1 — Sandbox
|
|
37
|
+
|
|
38
|
+
Follow `workflows/sandbox.md`. In short:
|
|
39
|
+
|
|
40
|
+
1. Invoke the `fresh-branch` skill to create an isolated worktree off `origin/main`. Keep its returned `worktree_path` and `base_commit`.
|
|
41
|
+
2. Run `scripts/build_sandbox_settings.py` to emit the minimal safety settings (personal-data and destructive-command gates only).
|
|
42
|
+
3. Run `scripts/probe_sandbox_safety.py --settings <path>` and confirm both gates block. Do not continue on a non-zero exit.
|
|
43
|
+
4. Run `scripts/launch_sandbox.py` to start the hookless `claude -p --bare` session in the worktree with those settings and the POC task.
|
|
44
|
+
5. Read what the sandbox built. Decide whether the POC proves the idea.
|
|
45
|
+
|
|
46
|
+
### Phase 2 — Promotion
|
|
47
|
+
|
|
48
|
+
Run only in the normal, fully-hooked session — never inside the sandbox. Follow `workflows/promotion.md`, which drives the clean-room task seeds in `reference/promotion-tasks.md`: fresh branch off live `origin/main`, POC content as an uncommitted diff, cleanup and privacy sweep, `code-verifier` in a fresh context, then `/commit` and a draft PR handed to a PR-loop skill. State the two honest limitations from `reference/honest-limitations.md`.
|
|
49
|
+
|
|
50
|
+
## Task seeding
|
|
51
|
+
|
|
52
|
+
At the start of Phase 2, register every item in `reference/promotion-tasks.md` as a session task (`TaskCreate`, or `TodoWrite` if that is the host tool). Work only from the task list. Mark each complete with evidence. Do not track promotion as a markdown checklist.
|
|
53
|
+
|
|
54
|
+
## Sub-skills
|
|
55
|
+
|
|
56
|
+
| Skill / agent | When | Produces | If missing |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| `fresh-branch` | Sandbox step 1; Promotion step 2 | isolated worktree JSON (`worktree_path`, `base_commit`, `repo_root`) | Refuse — see refusal cases |
|
|
59
|
+
| `privacy-hygiene` | Promotion step 5 | personal-data and secret sweep of the diff | Warn; do a manual review before continuing |
|
|
60
|
+
| `code-verifier` (agent) | Promotion step 6 | fresh-context verdict against the real diff; mints the commit-gate verdict | Stop; the commit gate will block anyway |
|
|
61
|
+
| `/commit` (command) | Promotion step 7 | conventional commit + push | Commit and push by hand per `git-workflow` |
|
|
62
|
+
| `autoconverge` (default; `pr-converge` or `bugteam` as alternatives) | Promotion step 9 | the PR converged to ready | Stop after the draft PR; tell the user to converge manually |
|
|
63
|
+
|
|
64
|
+
## Degree of freedom
|
|
65
|
+
|
|
66
|
+
Low on the skill's own mechanics — the launch flags and the promotion order are fragile with cliffs, so they live in scripts and a fixed task list, not in prose the agent reconstructs. High inside the sandbox — the sandboxed agent's build freedom is the whole point.
|
|
67
|
+
|
|
68
|
+
## File index
|
|
69
|
+
|
|
70
|
+
| File | Purpose |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `SKILL.md` | This hub — principle, gotchas, when-applies, process, sub-skills, file index |
|
|
73
|
+
| `workflows/sandbox.md` | Phase 1 steps: worktree, safety settings, probe, hookless launch |
|
|
74
|
+
| `workflows/promotion.md` | Phase 2 steps: the clean-room re-verification that drives the promotion task seeds |
|
|
75
|
+
| `reference/promotion-tasks.md` | Task-seed catalog for the clean-room protocol (register via the task tool) |
|
|
76
|
+
| `reference/honest-limitations.md` | The two fixed statements to make on every promotion |
|
|
77
|
+
| `scripts/build_sandbox_settings.py` | Emit the minimal safety `--settings`: resolve each hook's command from live settings, register it on the required matchers |
|
|
78
|
+
| `scripts/launch_sandbox.py` | Launch the hookless `claude -p --bare` sandbox session in the worktree |
|
|
79
|
+
| `scripts/probe_sandbox_safety.py` | Prove both safety gates block before trusting the sandbox |
|
|
80
|
+
|
|
81
|
+
## Folder map
|
|
82
|
+
|
|
83
|
+
- `SKILL.md` — hub.
|
|
84
|
+
- `workflows/` — the two phase workflows.
|
|
85
|
+
- `reference/` — promotion task seeds and the honest-limitation statements.
|
|
86
|
+
- `scripts/` — the settings builder, the launcher, the safety probe, their `prototype_scripts_constants` package, and paired tests.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Honest limitations of a promoted prototype
|
|
2
|
+
|
|
3
|
+
State both of these to the user, in these terms, whenever a proof-of-concept is promoted. Do not soften or drop them. They are the price of building without standards gates.
|
|
4
|
+
|
|
5
|
+
## 1. Write-time code rules never ran on this code
|
|
6
|
+
|
|
7
|
+
`code_rules_enforcer` is a Write/Edit gate: it checks content as it is written. Prototype code is built under `--bare`, so that gate never fired, and content brought into promotion as a git diff (apply, checkout, cherry-pick) does not pass through it either.
|
|
8
|
+
|
|
9
|
+
Standards re-engage on promotion through three surfaces that stand in for the write-time hook:
|
|
10
|
+
|
|
11
|
+
- the `code-verifier` agent, in a fresh context, deriving and running the named gates against the real diff;
|
|
12
|
+
- the `privacy-hygiene` sweep for personal data and secrets;
|
|
13
|
+
- the pull-request review (AGENTS.md criteria and any PR-loop reviewers).
|
|
14
|
+
|
|
15
|
+
Say plainly: the write-time rule engine did not see this code; the verifier and review are what cover it.
|
|
16
|
+
|
|
17
|
+
## 2. TDD ordering is waived on promoted prototype lines
|
|
18
|
+
|
|
19
|
+
The sandbox agent wrote code first and tests, if any, after. Red-green-refactor ordering did not happen. So the honest claim on promoted prototype code is exactly this, and nothing more:
|
|
20
|
+
|
|
21
|
+
> code-verifier passed, privacy swept, review passed — TDD ordering waived.
|
|
22
|
+
|
|
23
|
+
Do not claim red-green compliance on these lines. A prototype is a reference build, not a test-first build. Fred Brooks: plan to throw one away. Promotion re-verifies the code and often rewrites it to standard; expect real work in the verifier repair loop, not a rubber stamp.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Promotion task seeds (clean-room protocol)
|
|
2
|
+
|
|
3
|
+
Register every numbered item below as a session task (`TaskCreate`, or `TodoWrite` if that is the host tool) at the start of the promotion phase. Work only from the task list. Mark each complete only with evidence — a command result, a path, a verdict, or a skill's return. This is not a checkbox board; it is a seed catalog for the task tool.
|
|
4
|
+
|
|
5
|
+
Promotion runs in the **normal, fully-hooked session** — never inside the `--bare` sandbox. Nothing from the sandbox history is carried; only its file content is re-applied and re-verified.
|
|
6
|
+
|
|
7
|
+
1. **Confirm the prototype is worth promoting.** The sandbox build works and the user (or the standing goal) wants it shipped. Evidence: the working behavior observed, one sentence on what the POC proves.
|
|
8
|
+
|
|
9
|
+
2. **Fresh branch off live upstream.** Re-fetch `origin/main` and branch from it via the `fresh-branch` skill. Evidence: the returned `base_commit` matches current `origin/main`. This starts clean history and keeps the work based on live upstream.
|
|
10
|
+
|
|
11
|
+
3. **Bring prototype content as an uncommitted working-tree diff, by allowlist.** Take the sandbox diff against its `base_commit` and copy only the product files you intend to ship into the fresh branch's working tree. Do NOT cherry-pick or merge the sandbox commits, and do NOT bulk-copy the worktree. Exclude the sandbox settings file (`.prototype-sandbox-settings.json`) and every scratch, debug, or artifact file the POC produced — name the files you are bringing, not the ones you are dropping. Evidence: the allowlist of copied paths; `git status` shows only those unstaged; `git log` shows no sandbox commits.
|
|
12
|
+
|
|
13
|
+
4. **Cleanup pass.** Remove every scratch file, debug dump, and temp helper the prototype created (see the `cleanup-temp-files` rule). Evidence: the removed paths, or a stated "none created".
|
|
14
|
+
|
|
15
|
+
5. **Privacy sweep.** Run the `privacy-hygiene` skill over the full applied working tree, not only the diff — a POC that pulled live data can leave a secret in a file the diff view hides. Evidence: its clean report, or the leak it found and how it was removed. If the skill is missing, do a manual PII and secret review and say so.
|
|
16
|
+
|
|
17
|
+
6. **Verify in a fresh context.** Spawn the `code-verifier` agent against the real diff. Expect findings and a repair loop — the code was un-TDD'd. Evidence: the verifier's clean verdict, and a note of what it made you fix. Do not skip this on the belief that the sandbox agent already tested it.
|
|
18
|
+
|
|
19
|
+
7. **Commit and open a draft PR.** Only on a clean verdict, run `/commit` (which mints the commit-gate verdict and pushes), then open a draft PR per the `git-workflow` rule. Evidence: the commit hash and the PR URL.
|
|
20
|
+
|
|
21
|
+
8. **State the honest limitations.** Post the two statements from `reference/honest-limitations.md` — write-time rules never ran; TDD ordering waived — in the PR body or to the user. Evidence: the text was included.
|
|
22
|
+
|
|
23
|
+
9. **Hand to a PR-loop skill.** Hand the PR to `autoconverge` by default — one autonomous run to ready. Reach for `pr-converge` when paced ticks fit better, or `bugteam` for an open-loop audit-fix. Evidence: the skill was invoked, or the user chose to converge manually.
|