claude-dev-env 8.73.1 → 8.74.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 (116) hide show
  1. package/.agents/agents/test_agent_frontmatter.py +23 -23
  2. package/.agents/agents-archived/clean-coder.md +5 -5
  3. package/.agents/skills/correction/playbooks/fix.md +1 -1
  4. package/.agents/skills/eli5/SKILL.md +3 -3
  5. package/.agents/skills/pr-lifecycle/SKILL.md +1 -1
  6. package/.agents/skills/recovering-codex-startup/SKILL.md +1 -1
  7. package/.agents/skills/recovering-codex-startup/reference/task-seeds.md +1 -1
  8. package/_shared/advisor/reference/third-party-bind.md +1 -1
  9. package/bin/cde.mjs +1 -0
  10. package/bin/cde.test.mjs +1 -0
  11. package/bin/install.mjs +1 -0
  12. package/bin/install.policy-lint.test.mjs +1 -0
  13. package/bin/install.profile-root.test.mjs +1 -1
  14. package/bin/install.settings-defaults.test.mjs +10 -2
  15. package/bin/install.test.mjs +1 -0
  16. package/bin/merge_settings_defaults.mjs +34 -1
  17. package/bin/merge_settings_defaults.test.mjs +53 -0
  18. package/commands/fix-followups.md +1 -1
  19. package/commands/sr-loop.md +1 -1
  20. package/docs/references/prose-style-enforcement.md +1 -1
  21. package/docs/references/working-style.md +1 -1
  22. package/docs/rule-guides/asd-ste100-language.md +9 -1
  23. package/docs/rule-guides/cleanup-temp-files.md +8 -2
  24. package/docs/rule-guides/destructive-commands.md +13 -3
  25. package/docs/rule-guides/explore-thoroughly.md +9 -1
  26. package/docs/rule-guides/features-start-with-an-eval.md +8 -2
  27. package/docs/rule-guides/filesystem-search.md +7 -1
  28. package/docs/rule-guides/flag-non-breaking-findings.md +3 -0
  29. package/docs/rule-guides/long-horizon-autonomy.md +1 -1
  30. package/docs/rule-guides/memory-stores-durable-facts.md +9 -3
  31. package/docs/rule-guides/no-contrast-framing.md +10 -4
  32. package/docs/rule-guides/prefer-existing-tools.md +2 -2
  33. package/docs/rule-guides/proof-before-pull-request.md +5 -1
  34. package/docs/rule-guides/request-scope.md +5 -1
  35. package/docs/rule-guides/research-mode.md +9 -1
  36. package/docs/rule-guides/session-title.md +7 -1
  37. package/docs/rule-guides/shell-invocation.md +9 -1
  38. package/docs/rule-guides/verify-before-asking.md +7 -1
  39. package/docs/rule-guides/verify-runtime-state.md +7 -1
  40. package/hooks/blocking/code_rules_enforcer.py +16 -6
  41. package/hooks/blocking/code_rules_shared.py +23 -4
  42. package/hooks/blocking/haiku_spawn_slim_gate.py +96 -0
  43. package/hooks/blocking/reply_length_gate.py +74 -14
  44. package/hooks/blocking/state_description_blocker.py +1 -1
  45. package/hooks/blocking/test_code_rules_enforcer_agent_home_tooling.py +54 -1
  46. package/hooks/blocking/test_code_rules_shared.py +33 -0
  47. package/hooks/blocking/test_haiku_spawn_slim_gate.py +65 -0
  48. package/hooks/blocking/test_reply_length_gate.py +184 -5
  49. package/hooks/blocking/test_state_description_blocker.py +1 -1
  50. package/hooks/features/conduct-gates.md +3 -6
  51. package/hooks/features/spawn-routing.md +5 -1
  52. package/hooks/followup_ledger.py +52 -2
  53. package/hooks/hooks.json +11 -6
  54. package/hooks/hooks_constants/code_rules_enforcer_constants.py +1 -1
  55. package/hooks/hooks_constants/haiku_spawn_slim_gate_constants.py +29 -0
  56. package/hooks/hooks_constants/reply_length_gate_constants.py +1 -0
  57. package/hooks/hooks_constants/session_env_cleanup_constants.py +0 -2
  58. package/hooks/hooks_constants/test_visual_reply_rules_constants.py +15 -0
  59. package/hooks/hooks_constants/test_windows_filesystem.py +57 -0
  60. package/hooks/hooks_constants/visual_reply_rules_constants.py +63 -0
  61. package/hooks/hooks_constants/windows_filesystem.py +51 -0
  62. package/hooks/session/session_env_cleanup.py +4 -30
  63. package/hooks/test_followup_ledger.py +58 -0
  64. package/hooks/test_visual_reply_rules.py +164 -0
  65. package/hooks/visual_reply_rules.py +268 -0
  66. package/hooks/workflow/test_auto_formatter.py +4 -25
  67. package/package.json +1 -1
  68. package/rules/index.md +30 -0
  69. package/rules/visual-reply-rules.json +41 -0
  70. package/scripts/Migrate-ShellPolicy.ps1 +1 -1
  71. package/scripts/conftest.py +13 -5
  72. package/scripts/dev_env_scripts_constants/contrast_framing_constants.py +2 -2
  73. package/scripts/dev_env_scripts_constants/followup_constants.py +4 -1
  74. package/scripts/dev_env_scripts_constants/test_contrast_framing_constants.py +12 -0
  75. package/scripts/followup_cli.py +25 -1
  76. package/scripts/policy_lint/adapter_configuration.py +0 -5
  77. package/scripts/policy_lint/config/constants.py +1 -1
  78. package/scripts/repository_checks/config/constants.py +6 -0
  79. package/scripts/repository_checks/followup_ledger_duplicates.py +55 -0
  80. package/scripts/repository_checks/runner.py +7 -0
  81. package/scripts/test_active_capability_references.py +0 -5
  82. package/scripts/test_broker_state_isolation.py +3 -3
  83. package/scripts/test_digest_check.py +0 -5
  84. package/scripts/test_durable_post_lint.py +1 -5
  85. package/scripts/test_followup_cli.py +18 -6
  86. package/scripts/test_resolve_worker_spawn.py +5 -10
  87. package/scripts/test_setup_project_paths.py +0 -12
  88. package/scripts/test_shared_tree_paths.py +2 -6
  89. package/scripts/test_validate_instruction_pairs.py +0 -7
  90. package/scripts/test_verify_installable_package.py +0 -5
  91. package/scripts/tests/test_contrast_framing.py +3 -3
  92. package/scripts/tests/test_followup_ledger_duplicates.py +58 -0
  93. package/scripts/tests/test_repository_policy.py +2 -0
  94. package/scripts/tests/test_rule_index_shape.py +59 -5
  95. package/scripts/tests/test_rule_load_scopes.py +1 -16
  96. package/settings.json +9 -0
  97. package/system-prompts/software-engineer.xml +4 -4
  98. package/hooks/blocking/artifact_look_gate.py +0 -223
  99. package/hooks/blocking/test_artifact_look_gate.py +0 -153
  100. package/hooks/hooks_constants/artifact_look_gate_constants.py +0 -32
  101. package/rules/asd-ste100-language.md +0 -9
  102. package/rules/cleanup-temp-files.md +0 -9
  103. package/rules/destructive-commands.md +0 -11
  104. package/rules/explore-thoroughly.md +0 -9
  105. package/rules/features-start-with-an-eval.md +0 -9
  106. package/rules/filesystem-search.md +0 -9
  107. package/rules/memory-stores-durable-facts.md +0 -9
  108. package/rules/no-contrast-framing.md +0 -9
  109. package/rules/proof-before-pull-request.md +0 -7
  110. package/rules/request-scope.md +0 -7
  111. package/rules/research-mode.md +0 -9
  112. package/rules/session-title.md +0 -9
  113. package/rules/shell-invocation.md +0 -9
  114. package/rules/skill-pointers.md +0 -3
  115. package/rules/verify-before-asking.md +0 -9
  116. package/rules/verify-runtime-state.md +0 -9
@@ -474,20 +474,20 @@ EXPECTED_SOURCE_LINK_PAIRS = {
474
474
  "packages/claude-dev-env/rules/bdd.md",
475
475
  ),
476
476
  (
477
- "<managed-root>/rules/verify-before-asking.md",
478
- "packages/claude-dev-env/rules/verify-before-asking.md",
477
+ "<managed-root>/docs/rule-guides/verify-before-asking.md",
478
+ "packages/claude-dev-env/docs/rule-guides/verify-before-asking.md",
479
479
  ),
480
480
  (
481
- "<managed-root>/rules/filesystem-search.md",
482
- "packages/claude-dev-env/rules/filesystem-search.md",
481
+ "<managed-root>/docs/rule-guides/filesystem-search.md",
482
+ "packages/claude-dev-env/docs/rule-guides/filesystem-search.md",
483
483
  ),
484
484
  (
485
- "<managed-root>/rules/shell-invocation.md",
486
- "packages/claude-dev-env/rules/shell-invocation.md",
485
+ "<managed-root>/docs/rule-guides/shell-invocation.md",
486
+ "packages/claude-dev-env/docs/rule-guides/shell-invocation.md",
487
487
  ),
488
488
  (
489
- "<managed-root>/rules/verify-runtime-state.md",
490
- "packages/claude-dev-env/rules/verify-runtime-state.md",
489
+ "<managed-root>/docs/rule-guides/verify-runtime-state.md",
490
+ "packages/claude-dev-env/docs/rule-guides/verify-runtime-state.md",
491
491
  ),
492
492
  (
493
493
  "<managed-root>/rules/doc-inventory-integrity.md",
@@ -714,7 +714,7 @@ def test_clean_coder_links_canonical_policy_areas() -> None:
714
714
  required_links = (
715
715
  "<managed-root>/docs/CODE_RULES.md#5-no-abbreviations",
716
716
  "<managed-root>/rules/testing.md",
717
- "<managed-root>/rules/verify-runtime-state.md",
717
+ "<managed-root>/docs/rule-guides/verify-runtime-state.md",
718
718
  "<managed-root>/rules/doc-inventory-integrity.md",
719
719
  "<managed-root>/rules/failure-blast-radius.md",
720
720
  "<agents-home>/skills/pr-lifecycle/SKILL.md",
@@ -950,24 +950,24 @@ def test_clean_coder_groups_session_policy_references() -> None:
950
950
  assert "AskUserQuestion" in body
951
951
  assert "do not ask in plain text or guess" in body
952
952
 
953
- expected_session_policy_files = (
954
- "testing.md",
955
- "anti-corollary-tests.md",
956
- "verify-before-asking.md",
957
- "filesystem-search.md",
958
- "shell-invocation.md",
959
- "verify-runtime-state.md",
960
- "doc-inventory-integrity.md",
961
- "docstring-prose-matches-implementation.md",
962
- "failure-blast-radius.md",
963
- "workers-done-before-complete.md",
953
+ expected_session_policy_paths = (
954
+ "rules/testing.md",
955
+ "rules/anti-corollary-tests.md",
956
+ "docs/rule-guides/verify-before-asking.md",
957
+ "docs/rule-guides/filesystem-search.md",
958
+ "docs/rule-guides/shell-invocation.md",
959
+ "docs/rule-guides/verify-runtime-state.md",
960
+ "rules/doc-inventory-integrity.md",
961
+ "rules/docstring-prose-matches-implementation.md",
962
+ "rules/failure-blast-radius.md",
963
+ "rules/workers-done-before-complete.md",
964
964
  )
965
965
  session_policy_map = body[body.index("## Session policy map") :]
966
966
  session_policy_links = SOURCE_LINK_PATTERN.findall(session_policy_map)
967
- for each_policy_file_name in expected_session_policy_files:
967
+ for each_policy_path in expected_session_policy_paths:
968
968
  assert (
969
- f"<managed-root>/rules/{each_policy_file_name}",
970
- f"packages/claude-dev-env/rules/{each_policy_file_name}",
969
+ f"<managed-root>/{each_policy_path}",
970
+ f"packages/claude-dev-env/{each_policy_path}",
971
971
  ) in session_policy_links
972
972
  assert (
973
973
  "<agents-home>/skills/pr-lifecycle/SKILL.md",
@@ -54,8 +54,8 @@ Installed paths use the active managed root and agents home resolved above; sour
54
54
  | `gh` body files | `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`) |
55
55
  | Plain illustrative docstrings | `<managed-root>/rules/plain-illustrative-docstrings.md` (source fallback: `packages/claude-dev-env/rules/plain-illustrative-docstrings.md`) |
56
56
  | Tests / TDD | `<managed-root>/rules/testing.md` (source fallback: `packages/claude-dev-env/rules/testing.md`), `<managed-root>/rules/paired-test-coverage.md` (source fallback: `packages/claude-dev-env/rules/paired-test-coverage.md`), `<managed-root>/rules/bdd.md` (source fallback: `packages/claude-dev-env/rules/bdd.md`) |
57
- | Questions / task tracking | `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
58
- | Runtime evidence | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/verify-runtime-state.md`) |
57
+ | Questions / task tracking | `<managed-root>/docs/rule-guides/verify-before-asking.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/verify-before-asking.md`) |
58
+ | Runtime evidence | `<managed-root>/docs/rule-guides/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/verify-runtime-state.md`) |
59
59
  | Documentation / durable artifacts | `<managed-root>/rules/doc-inventory-integrity.md` (source fallback: `packages/claude-dev-env/rules/doc-inventory-integrity.md`), `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`) |
60
60
  | Batch / failure blast radius | `<managed-root>/rules/failure-blast-radius.md` (source fallback: `packages/claude-dev-env/rules/failure-blast-radius.md`) |
61
61
  | Git / GitHub | `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`), `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`), `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`) |
@@ -69,9 +69,9 @@ Load only the group that matches the task. Keep session policy details in these
69
69
  | Group | Canonical refs |
70
70
  |---|---|
71
71
  | Tests | `<managed-root>/rules/testing.md` (source fallback: `packages/claude-dev-env/rules/testing.md`); `<managed-root>/rules/anti-corollary-tests.md` (source fallback: `packages/claude-dev-env/rules/anti-corollary-tests.md`) |
72
- | Questions | `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
73
- | Search and shell | `<managed-root>/rules/filesystem-search.md` (source fallback: `packages/claude-dev-env/rules/filesystem-search.md`); `<managed-root>/rules/shell-invocation.md` (source fallback: `packages/claude-dev-env/rules/shell-invocation.md`) |
74
- | Runtime checks | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/verify-runtime-state.md`) |
72
+ | Questions | `<managed-root>/docs/rule-guides/verify-before-asking.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/verify-before-asking.md`) |
73
+ | Search and shell | `<managed-root>/docs/rule-guides/filesystem-search.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/filesystem-search.md`); `<managed-root>/docs/rule-guides/shell-invocation.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/shell-invocation.md`) |
74
+ | Runtime checks | `<managed-root>/docs/rule-guides/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/docs/rule-guides/verify-runtime-state.md`) |
75
75
  | Documentation | `<managed-root>/rules/doc-inventory-integrity.md` (source fallback: `packages/claude-dev-env/rules/doc-inventory-integrity.md`); `<managed-root>/rules/docstring-prose-matches-implementation.md` (source fallback: `packages/claude-dev-env/rules/docstring-prose-matches-implementation.md`) |
76
76
  | Batch failures | `<managed-root>/rules/failure-blast-radius.md` (source fallback: `packages/claude-dev-env/rules/failure-blast-radius.md`) |
77
77
  | Git | `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`); `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`) |
@@ -8,7 +8,7 @@ Use this playbook when this session lands the fix for the correction itself. One
8
8
  4. Search for an existing control: a rule, hook, lint, test, or skill that already names this mistake. A control that exists and still let the mistake through makes this a repeat.
9
9
  5. Choose the layer with [`correction-lens.md`](../../../../docs/rule-guides/correction-lens.md). When `Repeat` is `yes`, or step 4 found a control, go one layer above the one that failed.
10
10
  6. Land the control in the repository whose code, CI, or agents it guards, per the guide's "Where the control lands" section. When that repository is not in this session, add it. When it cannot be added, print the brief, name the missing repository, and stop.
11
- 7. Prove the control. Show it fail on the mistake from the brief's `Evidence` line, then pass with the change, per [`falsify-before-green.md`](../../../../rules/falsify-before-green.md) and [`proof-before-pull-request.md`](../../../../rules/proof-before-pull-request.md). Without that proof, open no pull request.
11
+ 7. Prove the control. Show it fail on the mistake from the brief's `Evidence` line, then pass with the change, per [`falsify-before-green.md`](../../../../rules/falsify-before-green.md) and [`proof-before-pull-request.md`](../../../../docs/rule-guides/proof-before-pull-request.md). Without that proof, open no pull request.
12
12
  8. Open the pull request through `pr-lifecycle`. Put the brief, the layer, and both proof results in the body.
13
13
 
14
14
  **Reply:** the layer chosen, why each higher layer cannot hold the lesson, and the pull request link.
@@ -52,7 +52,7 @@ presentation capability local.
52
52
  | Owner | Owns | ELI5 action |
53
53
  |---|---|---|
54
54
  | ELI5 | Beginner framing, visual expectation, minimal content, HTML continuity, and sharing | Keep the response in one useful, stable artifact. |
55
- | `~/.claude/rules/asd-ste100-language.md` | Word choice, sentence structure, terminology, punctuation, procedural instructions, and safety text | Read and apply it before writing or updating any page or response prose. |
55
+ | `~/.claude/docs/rule-guides/asd-ste100-language.md` | Word choice, sentence structure, terminology, punctuation, procedural instructions, and safety text | Read and apply it before writing or updating any page or response prose. |
56
56
  | The named capability skill | Native data, evidence, workflow, and file-format requirements | Preserve its contract inside the HTML presentation envelope. |
57
57
  | User instructions and safety requirements | Task boundary, audience, confidentiality, and permitted result | Treat them as the controlling scope for the page. |
58
58
  | Human review | Technical accuracy, terminology, safety, confidentiality, intended meaning, and rendered usefulness | Review the completed artifact before delivery. |
@@ -67,7 +67,7 @@ keep ownership of their native artifact rules while ELI5 supplies the global
67
67
  ## Gotchas
68
68
 
69
69
  - Keep the HTML presentation envelope active when another capability skill creates the page.
70
- - Keep sentence-level prose under `~/.claude/rules/asd-ste100-language.md`.
70
+ - Keep sentence-level prose under `~/.claude/docs/rule-guides/asd-ste100-language.md`.
71
71
  - Preserve an explicit user-supplied output path and an existing artifact path.
72
72
  - Keep one artifact path across all updates in the active task or conversation.
73
73
  - Keep every page self-contained and browser-ready.
@@ -95,7 +95,7 @@ Process steps in order.
95
95
  such as 3a and 3b for a case that holds a draft and a ready variant. Give
96
96
  each case its own card and its own visual.
97
97
  3. **Borderline — add the current explanation to that artifact.** Read and apply
98
- `~/.claude/rules/asd-ste100-language.md` to every sentence. Frame the topic
98
+ `~/.claude/docs/rule-guides/asd-ste100-language.md` to every sentence. Frame the topic
99
99
  for a beginner, use a large useful visual, keep the text minimal, and update
100
100
  the same artifact in place.
101
101
  4. **Deterministic — run the digest check on a summary of many changes.** A
@@ -226,7 +226,7 @@ behind, so each new head runs the gate again.
226
226
  | Rule | Role |
227
227
  |---|---|
228
228
  | [`git-workflow.md`](#git-workflow) | Confirm each required context fired after the push |
229
- | [`verify-runtime-state.md`](~/.claude/rules/verify-runtime-state.md) | A status field is a report; read the thing the work was meant to make |
229
+ | [`verify-runtime-state.md`](~/.claude/docs/rule-guides/verify-runtime-state.md) | A status field is a report; read the thing the work was meant to make |
230
230
  | [`falsify-before-green.md`](~/.claude/rules/falsify-before-green.md) | A green counts as evidence once the check has run red on a deliberate break |
231
231
 
232
232
  ## Review Closure Is a Check
@@ -29,7 +29,7 @@ For a non-Windows run, return exactly:
29
29
  ## Process
30
30
 
31
31
  1. When the session exposes a task tool, register the tasks in `reference/task-seeds.md`.
32
- 2. Read `../../../rules/verify-runtime-state.md`.
32
+ 2. Read `../../../docs/rule-guides/verify-runtime-state.md`.
33
33
  3. Confirm Windows and collect a fresh timestamp.
34
34
  4. Use read-only Windows tools to inspect Codex processes only.
35
35
  5. Record process name, executable path, start time, and owner.
@@ -1,5 +1,5 @@
1
1
  1. Confirm Windows and use the exact refusal on another host.
2
- 2. Read `../../../rules/verify-runtime-state.md`.
2
+ 2. Read `../../../docs/rule-guides/verify-runtime-state.md`.
3
3
  3. Collect fresh, read-only Codex process evidence and a timestamp.
4
4
  4. Record the exact path, name, start time, and owner.
5
5
  5. Report observation separately from diagnosis.
@@ -26,7 +26,7 @@ When the Astra flag is off, follow the Claude-chain steps below.
26
26
  ```
27
27
 
28
28
  Use `--model opus --effort` with the value of `ADVISOR_EFFORT` (default `xhigh`) on Opus.
29
- User-facing wording follows [`rules/asd-ste100-language.md`](../../../rules/asd-ste100-language.md).
29
+ User-facing wording follows [`docs/rule-guides/asd-ste100-language.md`](../../../docs/rule-guides/asd-ste100-language.md).
30
30
  Account choice, failover, and the `advisor_blocked` status are in [`cli-chain.md`](cli-chain.md).
31
31
  4. Stop at the first successful bind.
32
32
  Record `{tier, result: "cli"}` for Opus or `{tier: "Astra", result: "codex"}` for the Astra helper, and set `selected_tier` to that tier.
package/bin/cde.mjs CHANGED
@@ -45,6 +45,7 @@ export function createHelpText() {
45
45
  " cde followup brief Write the task an agent fixes them from",
46
46
  " cde followup count Report the backlog against the threshold",
47
47
  " cde followup clear Empty the ledger",
48
+ " cde followup dedupe Remove exact repeated ledger lines",
48
49
  "",
49
50
  "Python may also be selected with CDE_PYTHON.",
50
51
  ].join("\n");
package/bin/cde.test.mjs CHANGED
@@ -292,4 +292,5 @@ test("dispatches followup to the follow-up ledger command", async () => {
292
292
  test("help names the followup command", () => {
293
293
  assert.match(createHelpText(), /cde <lint\|verify\|followup>/);
294
294
  assert.match(createHelpText(), /cde followup count/);
295
+ assert.match(createHelpText(), /cde followup dedupe/);
295
296
  });
package/bin/install.mjs CHANGED
@@ -1254,6 +1254,7 @@ export const RETIRED_HOOK_REGISTRATION_RELATIVE_PATHS = new Set([
1254
1254
  'blocking/send_user_file_open_locally_blocker.py',
1255
1255
  'blocking/question_to_user_enforcer.py',
1256
1256
  'blocking/session_handoff_blocker.py',
1257
+ 'blocking/artifact_look_gate.py',
1257
1258
  'session/issue_tracker_session_starter.py',
1258
1259
  ]);
1259
1260
 
@@ -45,6 +45,7 @@ const allRetiredPolicyRegistrations = [
45
45
  'blocking/send_user_file_open_locally_blocker.py',
46
46
  'blocking/question_to_user_enforcer.py',
47
47
  'blocking/session_handoff_blocker.py',
48
+ 'blocking/artifact_look_gate.py',
48
49
  ];
49
50
  const sourceHooks = JSON.parse(readFileSync(new URL('../hooks/hooks.json', import.meta.url), 'utf8'));
50
51
  const installedRoot = '/fixture-home/.claude';
@@ -82,7 +82,7 @@ test('explicit target has precedence over CLAUDE_CONFIG_DIR and the home default
82
82
 
83
83
  test('isPathWithinManagedRoot requires a separator boundary', () => {
84
84
  const managedRoot = resolve(join(tmpdir(), 'containment-root', '.claude'));
85
- const insideFile = join(managedRoot, 'rules', 'asd-ste100-language.md');
85
+ const insideFile = join(managedRoot, 'rules', 'index.md');
86
86
  const evilSibling = `${managedRoot}-evil`;
87
87
  const evilChild = join(`${managedRoot}-evil`, 'rules', 'x.md');
88
88
 
@@ -263,10 +263,18 @@ test('sandbox uninstall removes only package-owned permission entries and keeps
263
263
  }
264
264
  });
265
265
 
266
- test('package settings.json publishes the session agent, advisor model and auto mode defaults', () => {
266
+ test('package settings.json publishes the session agent, advisor model, auto-compact and auto mode defaults', () => {
267
267
  const packageSettings = JSON.parse(readFileSync(PACKAGE_SETTINGS_PATH, 'utf8'));
268
268
  const settingsDefaults = settingsDefaultsFromPackageSettings(packageSettings);
269
- assert.deepEqual(Object.keys(settingsDefaults).sort(), ['advisorModel', 'agent', 'autoMode']);
269
+ assert.deepEqual(
270
+ Object.keys(settingsDefaults).sort(),
271
+ ['advisorModel', 'agent', 'autoCompactWindow', 'autoMode', 'modelSettings'],
272
+ );
273
+ assert.equal(settingsDefaults.autoCompactWindow, 350000);
274
+ assert.deepEqual(settingsDefaults.modelSettings, {
275
+ 'claude-haiku-5-5': { autoCompactWindow: 100000 },
276
+ 'claude-haiku-4-5': { autoCompactWindow: 100000 },
277
+ });
270
278
  assert.equal(settingsDefaults.agent, 'dev-env-session');
271
279
  assert.ok(
272
280
  existsSync(join(PACKAGE_ROOT, '.agents', 'agents', `${settingsDefaults.agent}.md`)),
@@ -1230,6 +1230,7 @@ test('retired hook registrations stay managed so reinstall removes them', () =>
1230
1230
  'blocking/send_user_file_open_locally_blocker.py',
1231
1231
  'blocking/question_to_user_enforcer.py',
1232
1232
  'blocking/session_handoff_blocker.py',
1233
+ 'blocking/artifact_look_gate.py',
1233
1234
  'session/issue_tracker_session_starter.py',
1234
1235
  ]);
1235
1236
  const shippedHooks = JSON.parse(
@@ -4,9 +4,35 @@
4
4
  * Every top-level key in the package settings.json other than the managed
5
5
  * lists (`permissions`) is a default. Install adds a default only when the
6
6
  * user settings lack that key, so a value the user already set stays as is.
7
+ *
8
+ * A per-model key (`modelSettings`) merges one level deeper: install adds each
9
+ * model entry, and each field inside an entry, that the user settings lack.
7
10
  */
8
11
 
9
12
  export const MANAGED_LIST_SETTING_KEYS = Object.freeze(['permissions']);
13
+ export const PER_MODEL_SETTING_KEYS = Object.freeze(['modelSettings']);
14
+
15
+ function isPlainObject(candidate) {
16
+ return Boolean(candidate) && typeof candidate === 'object' && !Array.isArray(candidate);
17
+ }
18
+
19
+ function mergeMissingPerModelFields(targetPerModel, defaultPerModel, settingKey) {
20
+ const addedKeys = [];
21
+ for (const [modelName, defaultFields] of Object.entries(defaultPerModel)) {
22
+ if (!Object.hasOwn(targetPerModel, modelName)) {
23
+ targetPerModel[modelName] = { ...defaultFields };
24
+ addedKeys.push(`${settingKey}.${modelName}`);
25
+ continue;
26
+ }
27
+ if (!isPlainObject(targetPerModel[modelName])) continue;
28
+ for (const [fieldName, fieldDefault] of Object.entries(defaultFields)) {
29
+ if (Object.hasOwn(targetPerModel[modelName], fieldName)) continue;
30
+ targetPerModel[modelName][fieldName] = fieldDefault;
31
+ addedKeys.push(`${settingKey}.${modelName}.${fieldName}`);
32
+ }
33
+ }
34
+ return addedKeys;
35
+ }
10
36
 
11
37
  /**
12
38
  * Read the setting defaults from a package settings source object.
@@ -35,8 +61,15 @@ export function settingsDefaultsFromPackageSettings(packageSettings) {
35
61
  export function mergeMissingSettingsDefaults(targetSettings, settingsDefaults) {
36
62
  const addedKeys = [];
37
63
  for (const [settingKey, defaultValue] of Object.entries(settingsDefaults)) {
64
+ const isPerModelMerge = PER_MODEL_SETTING_KEYS.includes(settingKey)
65
+ && isPlainObject(targetSettings[settingKey])
66
+ && isPlainObject(defaultValue);
67
+ if (isPerModelMerge) {
68
+ addedKeys.push(...mergeMissingPerModelFields(targetSettings[settingKey], defaultValue, settingKey));
69
+ continue;
70
+ }
38
71
  if (Object.hasOwn(targetSettings, settingKey)) continue;
39
- targetSettings[settingKey] = defaultValue;
72
+ targetSettings[settingKey] = structuredClone(defaultValue);
40
73
  addedKeys.push(settingKey);
41
74
  }
42
75
  return { addedKeys };
@@ -41,3 +41,56 @@ test('mergeMissingSettingsDefaults adds absent keys and keeps present values', (
41
41
  theme: 'dark',
42
42
  });
43
43
  });
44
+
45
+ test('mergeMissingSettingsDefaults should add a missing model entry and a missing field inside a present one', () => {
46
+ const targetSettings = {
47
+ modelSettings: {
48
+ 'claude-opus-5-5': { effortLevel: 'medium' },
49
+ 'claude-haiku-4-5': { effortLevel: 'low' },
50
+ },
51
+ };
52
+
53
+ assert.deepEqual(
54
+ mergeMissingSettingsDefaults(targetSettings, {
55
+ modelSettings: {
56
+ 'claude-haiku-5-5': { autoCompactWindow: 100000 },
57
+ 'claude-haiku-4-5': { autoCompactWindow: 100000 },
58
+ },
59
+ }),
60
+ {
61
+ addedKeys: [
62
+ 'modelSettings.claude-haiku-5-5',
63
+ 'modelSettings.claude-haiku-4-5.autoCompactWindow',
64
+ ],
65
+ },
66
+ );
67
+ assert.deepEqual(targetSettings.modelSettings, {
68
+ 'claude-opus-5-5': { effortLevel: 'medium' },
69
+ 'claude-haiku-4-5': { effortLevel: 'low', autoCompactWindow: 100000 },
70
+ 'claude-haiku-5-5': { autoCompactWindow: 100000 },
71
+ });
72
+ });
73
+
74
+ test('mergeMissingSettingsDefaults should give each target its own copy of an added default', () => {
75
+ const settingsDefaults = { modelSettings: { 'claude-haiku-5-5': { autoCompactWindow: 100000 } } };
76
+ const firstTarget = {};
77
+ const secondTarget = {};
78
+
79
+ mergeMissingSettingsDefaults(firstTarget, settingsDefaults);
80
+ firstTarget.modelSettings['claude-haiku-5-5'].autoCompactWindow = 200000;
81
+ mergeMissingSettingsDefaults(secondTarget, settingsDefaults);
82
+
83
+ assert.deepEqual(secondTarget.modelSettings, { 'claude-haiku-5-5': { autoCompactWindow: 100000 } });
84
+ });
85
+
86
+ test('mergeMissingSettingsDefaults should keep a per-model value the user already set', () => {
87
+ const targetSettings = { modelSettings: { 'claude-haiku-5-5': { autoCompactWindow: 150000 } } };
88
+
89
+ assert.deepEqual(
90
+ mergeMissingSettingsDefaults(targetSettings, {
91
+ modelSettings: { 'claude-haiku-5-5': { autoCompactWindow: 100000 } },
92
+ }),
93
+ { addedKeys: [] },
94
+ );
95
+ assert.deepEqual(targetSettings.modelSettings, { 'claude-haiku-5-5': { autoCompactWindow: 150000 } });
96
+ });
@@ -9,7 +9,7 @@ and deliver them as one pull request.
9
9
  The repository root is `$ARGUMENTS`, or the current repository when that is
10
10
  blank.
11
11
 
12
- Use `rules/asd-ste100-language.md` for user-facing wording.
12
+ Use `docs/rule-guides/asd-ste100-language.md` for user-facing wording.
13
13
 
14
14
  ## 1. Read the ledger
15
15
 
@@ -7,7 +7,7 @@ Run the converging cleanup loop on the target: `$ARGUMENTS` (blank means the
7
7
  current branch's diff). Each phase invokes an existing skill with the Skill
8
8
  tool and repeats it until a pass returns zero new findings.
9
9
 
10
- Use `rules/asd-ste100-language.md` for user-facing wording. Keep the loop's
10
+ Use `docs/rule-guides/asd-ste100-language.md` for user-facing wording. Keep the loop's
11
11
  cleanup, verification, commit, push, and finish-report fields.
12
12
 
13
13
  ### Nit-only terminal
@@ -10,7 +10,7 @@ The flag is `PROSE_STYLE_ENFORCEMENT_ENABLED` in `hooks/blocking/config/prose_st
10
10
 
11
11
  ## Always on
12
12
 
13
- Historical and comparative language detection lives in `state_description_blocker.py`, which the staged policy lint applies under its state-description rule whatever this flag holds. Apply `rules/asd-ste100-language.md` for user-facing word choice and sentence style. Code-rules and security blockers are out of scope for this switch.
13
+ Historical and comparative language detection lives in `state_description_blocker.py`, which the staged policy lint applies under its state-description rule whatever this flag holds. Apply `docs/rule-guides/asd-ste100-language.md` for user-facing word choice and sentence style. Code-rules and security blockers are out of scope for this switch.
14
14
 
15
15
  ## Hedging claims
16
16
 
@@ -10,7 +10,7 @@ The SessionStart hook `hooks/session/working_style_prompt.py` points every sessi
10
10
  ## Presentation
11
11
 
12
12
  - Use ELI5 for beginner framing, large visuals, minimal text, one stable self-contained HTML artifact, update-in-place continuity, and sharing when a user-facing response needs that presentation.
13
- - Apply `~/.claude/rules/asd-ste100-language.md` for user-facing word choice, sentence style, tone, punctuation, and prose form.
13
+ - Apply `~/.claude/docs/rule-guides/asd-ste100-language.md` for user-facing word choice, sentence style, tone, punctuation, and prose form.
14
14
  - Keep responses focused, brief, and concise. Keep disclaimers and caveats short while giving the main answer most of the response.
15
15
  - Give a high-level explanation by default and provide depth when the request calls for it.
16
16
  - Match written-document length to the task. Cover the substance and keep every section, summary, and phrase useful.
@@ -1,7 +1,15 @@
1
- Back to the [rule entry](../../rules/asd-ste100-language.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # ASD-STE100 language policy
4
4
 
5
+ ## In brief
6
+
7
+ **When:** Writing chat, tool narration, or repository prose.
8
+
9
+ Use the sole general language rule. Write short, complete sentences on one topic. Use active voice; lead with conditions; one action per step. Use plain, precise words, stable terms. Expand abbreviations and contractions; name unclear pronouns. Be inclusive; punctuate clearly. Preserve exact labels, identifiers, formulas, titles, interface text. Say each fact once. Send a result, blocker, or question. Mark injury or death `WARNING`, equipment damage `CAUTION`; state condition then result. Aim for 20 words per step, 25 per description. Treat checks as aids; have a human verify accuracy, terms, safety, confidentiality, meaning.
10
+
11
+ **Enforcement:** none.
12
+
5
13
  Use this rule as the sole general language authority for user-facing text in this repository.
6
14
  It defines ordinary word choice, sentence style, tone, punctuation, and prose form.
7
15
 
@@ -1,7 +1,13 @@
1
- Back to the [rule entry](../../rules/cleanup-temp-files.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Clean up temporary files
4
4
 
5
+ ## In brief
6
+
7
+ Prefer memory to scratch files and track any temporary files you create. At completion, remove those files and leave user-requested files in place. Files under the OS temporary root or `$CLAUDE_JOB_DIR` need no explicit removal; a parent handles child-agent scratch.
8
+
9
+ **Enforcement:** none.
10
+
5
11
  **When this applies:** After tasks that created scratch files, debug dumps, or one-off scripts the user did not ask to keep.
6
12
 
7
13
  Source: [Anthropic: Reduce file creation in agentic coding](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#reduce-file-creation-in-agentic-coding)
@@ -25,7 +31,7 @@ Three kinds of file are already ephemeral and need no explicit removal:
25
31
  - A file under `$CLAUDE_JOB_DIR`, which the harness clears with the job.
26
32
  - A child agent's scratch file, which the parent removes at teardown.
27
33
 
28
- Use an allowed removal form for everything else: [`destructive-commands.md`](../../rules/destructive-commands.md) names them.
34
+ Use an allowed removal form for everything else: [`destructive-commands.md`](destructive-commands.md) names them.
29
35
 
30
36
  ## What counts as temporary
31
37
 
@@ -1,7 +1,17 @@
1
- Back to the [rule entry](../../rules/destructive-commands.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Destructive commands in Bash
4
4
 
5
+ ## In brief
6
+
7
+ **When:** Removing files or writing a destructive command string.
8
+
9
+ Keep destructive literals out of command strings, including data. Use literal absolute targets; never target a bare temporary root. Use `git rm` for tracked files. Pass bodies by file and test hooks through the test suite. Copy this line into every subagent prompt:
10
+
11
+ > Never use bash rm in any form. Delete scratch/probe files with the PowerShell tool (Remove-Item -Recurse -Force -Confirm:$false <absolute path>), or leave them in the OS temp dir; remove worktrees only via git worktree remove --force.
12
+
13
+ **Enforcement:** none; a harness permission prompt can stall unattended work.
14
+
5
15
  No hook watches Bash commands for destructive patterns. What you face is the harness permission prompt. The harness raises it from the session permission mode and the permission rules in effect on your host. In a background or auto-mode run no human can answer that prompt, so the call stalls.
6
16
 
7
17
  Two consequences follow. Use an allowed removal form, and keep a destructive literal out of the command string even when it rides only as data.
@@ -26,7 +36,7 @@ Never pass a bare ephemeral root, such as `/tmp`, the OS temp root itself, or a
26
36
 
27
37
  Write each target as a literal path. A variable, a `$(...)` or backtick expansion, or a brace glob hides what the command will delete from the reader and from the permission matcher.
28
38
 
29
- A file left in the OS temp directory or under `$CLAUDE_JOB_DIR` is cleaned by the harness and needs no explicit removal. See the exception clause in [`cleanup-temp-files.md`](../../rules/cleanup-temp-files.md).
39
+ A file left in the OS temp directory or under `$CLAUDE_JOB_DIR` is cleaned by the harness and needs no explicit removal. See the exception clause in [`cleanup-temp-files.md`](cleanup-temp-files.md).
30
40
 
31
41
  ## Keep destructive literals out of the command string
32
42
 
@@ -45,5 +55,5 @@ Prefer that a child leaves its scratch files for the parent to remove at teardow
45
55
 
46
56
  ## Sibling rules
47
57
 
48
- - [`cleanup-temp-files.md`](../../rules/cleanup-temp-files.md) names which scratch files a task removes, and which it leaves.
58
+ - [`cleanup-temp-files.md`](cleanup-temp-files.md) names which scratch files a task removes, and which it leaves.
49
59
  - [`windows-filesystem-safe.md`](../../rules/windows-filesystem-safe.md) holds the safe `rmtree` and `force_rmtree` patterns for read-only Windows files.
@@ -1,7 +1,15 @@
1
- Back to the [rule entry](../../rules/explore-thoroughly.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Explore thoroughly
4
4
 
5
+ ## In brief
6
+
7
+ **When:** Before you log, plan, delegate, or build an ask, or choose an approach.
8
+
9
+ Search for what already does the ask, in full or in part. When something exists, stop and report it to the user in four points; run the `search-before-acting` skill. Then read files, patterns, and constraints, scaled to risk. Once the files, constraints, and success condition are known, act without repeating settled research.
10
+
11
+ **Enforcement:** the pull request gate denies a body with no "Existing work" section.
12
+
5
13
  Source: [Anthropic - Overthinking and Excessive Thoroughness](https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/claude-prompting-best-practices#overthinking-and-excessive-thoroughness)
6
14
 
7
15
  Note: This deliberately chooses exploration depth over the "commit and execute quickly" pattern from the same source. Thorough upfront exploration is preferred for the intended workflow.
@@ -1,7 +1,13 @@
1
- Back to the [rule entry](../../rules/features-start-with-an-eval.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Features start with an eval
4
4
 
5
+ ## In brief
6
+
7
+ Your first action is the Skill tool with skill `claude-api` and args `build-eval`. Build the feature against that eval. When the work touches an existing feature with no eval, start a separate session to build that eval in parallel. An Agent-tool subagent does not count. You own its result.
8
+
9
+ **Enforcement:** `hooks/blocking/pr_lifecycle_skill_gate.py` denies a `feat` pull request with no "Eval" section or no build-eval call.
10
+
5
11
  **When this applies:** A user asks for a new feature or a change in behavior, in any project.
6
12
 
7
13
  ## What counts
@@ -13,7 +19,7 @@ An eval is three things for one feature: a set of input cases with their expecte
13
19
  ## Rule
14
20
 
15
21
  1. Make the Skill tool call with skill `claude-api` and args `build-eval` your first action on the ask. A user who types `/claude-api build-eval` has made the same call.
16
- 2. Run the existing-work search from [`explore-thoroughly.md`](../../rules/explore-thoroughly.md) next. The guide's own first step also looks for cases, graders, and runners that exist.
22
+ 2. Run the existing-work search from [`explore-thoroughly.md`](explore-thoroughly.md) next. The guide's own first step also looks for cases, graders, and runners that exist.
17
23
  3. Follow the guide the skill loads. It asks the user to approve the inputs and the grading method, and to approve the cost before the first paid run. Ask through the session's question tool. Where the host says to keep working while a question waits, continue on the option you recommended and change course if the answer differs.
18
24
  4. Run the eval before the change to get a baseline, build the feature, and run it again.
19
25
  5. Give the pull request an "Eval" section. Name the cases and the grader, give the command that runs the eval in backticks, and quote the baseline and the result.
@@ -1,7 +1,13 @@
1
- Back to the [rule entry](../../rules/filesystem-search.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Filesystem search
4
4
 
5
+ ## In brief
6
+
7
+ Scope every search to a project, worktree, package, or narrowing filter. Never start at a filesystem root, drive root, bare home, or share root. Read a known path directly; use scoped `es.exe`, Glob, or Grep for discovery. After `es.exe` Error 8, retry twice before falling back within the same scope. Run one large shell walk at a time.
8
+
9
+ **Enforcement:** none.
10
+
5
11
  **When this applies:** Any search for files by name, path, extension, size, or date. That includes `es.exe`, a shell `find`, a recursive `Get-ChildItem` / `gci` / `dir` / `ls -R`, or the harness Grep and Glob tools.
6
12
 
7
13
  ## The scope invariant
@@ -38,9 +38,12 @@ A message with no catalog entry resolves to `<rule>/unclassified`, which a parti
38
38
  | `cde followup brief` | Writes the task an agent fixes them from |
39
39
  | `cde followup clear` | Empties the ledger |
40
40
  | `cde followup count` | Reports the backlog against the threshold |
41
+ | `cde followup dedupe` | Removes exact repeated lines and keeps the first of each |
41
42
 
42
43
  `count` exits non-zero once the backlog passes `FOLLOWUP_BACKLOG_THRESHOLD` in `scripts/dev_env_scripts_constants/followup_constants.py`, so a scheduled job escalates. The number is the repository's setting.
43
44
 
45
+ `.gitattributes` marks the ledger `merge=union`, so a local `git merge` keeps both sides' appended lines without a conflict. GitHub's server-side merge ignores the attribute. Every ledger write drops exact repeated lines, and the `followup-ledger-duplicates` repository check fails a tree that still holds one.
46
+
44
47
  The `/fix-followups` command reads the brief, fixes each rule group, opens a pull request, and clears the ledger.
45
48
 
46
49
  ## Worked example
@@ -35,6 +35,6 @@ For a task that spawned workers, the completion condition and checklist live in
35
35
 
36
36
  ## Ground progress and the closing report
37
37
 
38
- Progress claims come from tool results in the current session. Failed tests carry their output. Skipped steps are named. The [ASD-STE100 language rule](../../rules/asd-ste100-language.md) governs the wording. The first progress update uses one sentence. Later updates cover important discoveries or a change in direction.
38
+ Progress claims come from tool results in the current session. Failed tests carry their output. Skipped steps are named. The [ASD-STE100 language rule](asd-ste100-language.md) governs the wording. The first progress update uses one sentence. Later updates cover important discoveries or a change in direction.
39
39
 
40
40
  Terse notes between tool calls can support the run. The final message briefs a reader who saw none of them. It opens with the outcome, then explains any input needed using plain names for each file, commit, or flag. A remaining context or token count does not end an unfinished run. Content the user must see word for word goes through the channel the harness provides for it.
@@ -1,7 +1,13 @@
1
- Back to the [rule entry](../../rules/memory-stores-durable-facts.md).
1
+ Back to the [rules index](../../rules/index.md).
2
2
 
3
3
  # Memory stores durable facts
4
4
 
5
+ ## In brief
6
+
7
+ Keep only a fact that helps a fresh session weeks later. Remove dates, task identifiers, commits, and paths from a draft; if the remainder is empty or false, put it in the task tracker or handoff instead. Store standing user preferences, stable setup traps, and useful resource pointers. Delete stale memory and its index line in the same run.
8
+
9
+ **Enforcement:** none.
10
+
5
11
  **When this applies:** Before you write or update a file in an auto-memory
6
12
  directory, and before you add its line to `MEMORY.md`.
7
13
 
@@ -55,5 +61,5 @@ into each session and reads as current.
55
61
  | Rule | Role |
56
62
  |---|---|
57
63
  | [`correction-lens.md`](../../rules/correction-lens.md) | A memory records a decision, and the control holds the behavior |
58
- | [`verify-before-asking.md`](../../rules/verify-before-asking.md) | A recalled fact is a claim to re-check |
59
- | [`cleanup-temp-files.md`](../../rules/cleanup-temp-files.md) | Scratch output leaves with the task |
64
+ | [`verify-before-asking.md`](verify-before-asking.md) | A recalled fact is a claim to re-check |
65
+ | [`cleanup-temp-files.md`](cleanup-temp-files.md) | Scratch output leaves with the task |