claude-dev-env 8.98.11 → 8.99.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.
@@ -45,6 +45,6 @@ The person running the loop asked for a full look, so every pass gets a full loo
45
45
  - Prefer Before / After when the change is about how something is asked, found, checked, saved, retried, or stopped.
46
46
  - Use picture words: hand it, open it, find it, check it, save it, skip it, stop, call by name.
47
47
  - Prefer spoken results: “found it,” “missing,” “duplicated,” “couldn’t.”
48
- - Keep `Why` short and concrete.
49
- - Put proof only in `Verification`.
48
+ - Keep `What this does` short and concrete.
49
+ - Put proof only in `Proof in practice`.
50
50
  - Explain jargon immediately or replace it.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Use this only when the PR writer needs a concrete verification sample.
4
4
 
5
- Verification should lead with something a reviewer can open, see, click, compare, or try.
5
+ Proof in practice should lead with something a reviewer can open, see, click, compare, or try.
6
6
 
7
7
  Good:
8
8
 
@@ -30,8 +30,8 @@ def test_voice_reference_keeps_the_plain_illustrative_shape() -> None:
30
30
  "found it",
31
31
  "missing",
32
32
  "duplicated",
33
- "Why",
34
- "Verification",
33
+ "What this does",
34
+ "Proof in practice",
35
35
  ):
36
36
  assert required_text in reference_text
37
37
 
@@ -92,10 +92,9 @@ Check each behavior claim against the final diff and verification evidence.
92
92
  Preserve whether a rule is added, removed, or narrowed, and distinguish tests
93
93
  added from tests run. Refresh these claims after a rebase or correction.
94
94
 
95
- Open with the concrete problem and resulting behavior in one or two short
96
- paragraphs of plain prose. Write for a reviewer who has not read the worker's
97
- conversation. Include only claims supported by the final diff. State commands,
98
- results, and material limits under `## Verification`.
95
+ Write the body in the section order `reference/body-sections.md` gives: What
96
+ this does, What changed, Existing work, Proof in practice, Eval for a `feat`
97
+ title, Risk, How to revert. Include only claims supported by the final diff.
99
98
 
100
99
  Keep substantive review and test evidence. Put extensive logs, raw diffs, token
101
100
  counts, and agent transcripts in an existing linked artifact or a closed
@@ -189,6 +188,7 @@ Read `reference/examples.md` for a create, a rejected comment, and a recovery.
189
188
  | `reference/publication-tasks.md` | Ordered tasks for every publication gate |
190
189
  | `reference/legacy-author-recovery.md` | Recovery of one selected legacy author record |
191
190
  | `reference/examples.md` | Worked create, rejected comment, and recovery examples |
191
+ | `reference/body-sections.md` | Pull request body sections, their order, and what each holds |
192
192
  | `scripts/pull_request.py` | Command for author selection and create, edit, comment, and review actions |
193
193
  | `scripts/github_pr_command_constants/` | Constants for pull request actions, recovery, exit codes, and generic messages |
194
194
  | `scripts/test_pull_request.py` | Tests for `pull_request.py` |
@@ -0,0 +1,17 @@
1
+ # Pull request body sections
2
+
3
+ A reviewer reads the body in place of the code. The body says what the change does, how it was tested, what could break, and how to undo it. The order follows the Claude Code team's [code review article](https://claude.dev/blog/solving-code-review-at-anthropic/), whose template "asks how the change was tested and how to undo it". The opening sections follow the team's public pull requests, such as [claude-code #69470](https://github.com/anthropics/claude-code/pull/69470), which opens with What this does, Why now and What changed.
4
+
5
+ | Section | Holds |
6
+ |---|---|
7
+ | `## What this does` | Two or three plain sentences: the behavior before, the behavior after, and who notices. Written for a reviewer who has not read the worker's conversation. |
8
+ | `## What changed` | One bullet per area of the diff, in plain words. |
9
+ | `## Existing work` | What already did part of the job, with a link or file and line, and what this change adds. Or the places searched. |
10
+ | `## Proof in practice` | Each command in backticks, the output without the change and with it, and anything still unproven. |
11
+ | `## Eval` | Only for a `feat` title. The eval's cases and grader, the command, and its result. |
12
+ | `## Risk` | What could break, for whom, and how anyone would notice. `Low` and one reason for a doc or test change. |
13
+ | `## How to revert` | The exact undo step, and any data or state a plain revert leaves behind. |
14
+
15
+ A repository's own sections, such as a request record or a session link, follow these. Optional `## Why now` and `## After merge` sections go after What this does and after How to revert.
16
+
17
+ `pull_request.py template` writes the headings in this order into a new body, and appends each missing heading to the end of a body that already has some. `durable_post_lint.py` reports a create body missing What this does, Risk, or How to revert, and the proof gate holds one missing Existing work, Proof in practice, or Eval for a `feat` title.
@@ -9,11 +9,14 @@ ACTION_REVIEW = "review"
9
9
  ACTION_TEMPLATE = "template"
10
10
 
11
11
  ALL_BODY_TEMPLATE_HEADINGS: tuple[str, ...] = (
12
- "Why",
12
+ "What this does",
13
+ "What changed",
13
14
  "Existing work",
14
15
  "Proof in practice",
15
- "Verification",
16
+ "Risk",
17
+ "How to revert",
16
18
  )
19
+ FEATURE_BODY_TEMPLATE_HEADING_FOLLOWS = "Proof in practice"
17
20
  FEATURE_BODY_TEMPLATE_HEADING = "Eval"
18
21
  FEATURE_TITLE_PATTERN: re.Pattern[str] = re.compile(r"^\s*feat(\([^)]*\))?!?:")
19
22
  BODY_TEMPLATE_HEADING_FORMAT = "## {heading}"
@@ -26,6 +26,7 @@ from github_pr_command_constants.config.constants import (
26
26
  BODY_TEMPLATE_HEADING_FORMAT,
27
27
  BODY_TEMPLATE_LINE_END,
28
28
  FEATURE_BODY_TEMPLATE_HEADING,
29
+ FEATURE_BODY_TEMPLATE_HEADING_FOLLOWS,
29
30
  FEATURE_TITLE_PATTERN,
30
31
  ALL_GITHUB_AUTHORIZATION_ENVIRONMENT_KEYS,
31
32
  ALL_LINTER_ACTIONS_BY_COMMAND,
@@ -77,13 +78,11 @@ def _build_parser() -> argparse.ArgumentParser:
77
78
 
78
79
 
79
80
  def _template_headings(title: str) -> list[str]:
80
- all_headings = [
81
- BODY_TEMPLATE_HEADING_FORMAT.format(heading=each_heading)
82
- for each_heading in ALL_BODY_TEMPLATE_HEADINGS
83
- ]
81
+ all_heading_names = list(ALL_BODY_TEMPLATE_HEADINGS)
84
82
  if FEATURE_TITLE_PATTERN.match(title):
85
- all_headings.append(BODY_TEMPLATE_HEADING_FORMAT.format(heading=FEATURE_BODY_TEMPLATE_HEADING))
86
- return all_headings
83
+ eval_position = all_heading_names.index(FEATURE_BODY_TEMPLATE_HEADING_FOLLOWS) + 1
84
+ all_heading_names.insert(eval_position, FEATURE_BODY_TEMPLATE_HEADING)
85
+ return [BODY_TEMPLATE_HEADING_FORMAT.format(heading=each_heading) for each_heading in all_heading_names]
87
86
 
88
87
 
89
88
  def write_body_template(body_file: Path, title: str) -> list[str]:
@@ -91,8 +90,9 @@ def write_body_template(body_file: Path, title: str) -> list[str]:
91
90
 
92
91
  ::
93
92
 
94
- new file, title "fix: x" -> ["## Why", "## Existing work", "## Proof in practice", "## Verification"]
95
- new file, title "feat: x" -> the same plus "## Eval"
93
+ new file, title "fix: x" -> ["## What this does", "## What changed", "## Existing work",
94
+ "## Proof in practice", "## Risk", "## How to revert"]
95
+ new file, title "feat: x" -> the same with "## Eval" after "## Proof in practice"
96
96
  file already has them all -> [] and the file is unchanged
97
97
 
98
98
  Args:
@@ -544,3 +544,20 @@ def should_keep_written_text_and_add_only_missing_headings(tmp_path: Path) -> No
544
544
  assert first_text.startswith("## Why\nThe gate bounced twice.\n")
545
545
  assert first_text.count("## Why") == 1
546
546
  assert body_path.read_text(encoding="utf-8") == first_text
547
+
548
+
549
+ def should_write_the_reader_headings_in_order_with_eval_after_the_proof(tmp_path: Path) -> None:
550
+ body_path = tmp_path / BODY_FILENAME
551
+ _run_template(body_path, "feat(hooks): add the gate")
552
+ all_heading_lines = [
553
+ each_line for each_line in body_path.read_text(encoding="utf-8").splitlines() if each_line.startswith("## ")
554
+ ]
555
+ assert all_heading_lines == [
556
+ "## What this does",
557
+ "## What changed",
558
+ "## Existing work",
559
+ "## Proof in practice",
560
+ "## Eval",
561
+ "## Risk",
562
+ "## How to revert",
563
+ ]
@@ -35,6 +35,11 @@ const INSTALL_ENTRY = join(PACKAGE_ROOT, 'bin', 'install.mjs');
35
35
  const EXPECTED_DENY_ENTRIES = [];
36
36
  const EXPECTED_ALLOW_ENTRIES = [
37
37
  'WebFetch(domain:docs.github.com)',
38
+ 'WebFetch(domain:claude.com)',
39
+ 'WebFetch(domain:*.claude.com)',
40
+ 'WebFetch(domain:claude.dev)',
41
+ 'WebFetch(domain:*.claude.dev)',
42
+ 'WebFetch(domain:www.anthropic.com)',
38
43
  'Read(~/.claude/docs/**)',
39
44
  ];
40
45
  const PROFILE_ALLOW_ENTRIES = DECLARED_PROFILE_SETTINGS
@@ -76,7 +81,7 @@ function runInstallerInSandbox(sandboxHome, installerArguments = []) {
76
81
  });
77
82
  }
78
83
 
79
- test('package settings.json publishes the GitHub docs fetch allow and no managed deny entries', () => {
84
+ test('package settings.json publishes the docs fetch allows and no managed deny entries', () => {
80
85
  assert.deepEqual(packageManagedPermissions(), {
81
86
  allow: EXPECTED_ALLOW_ENTRIES,
82
87
  deny: EXPECTED_DENY_ENTRIES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-dev-env",
3
- "version": "8.98.11",
3
+ "version": "8.99.0",
4
4
  "description": "Claude Code development standards — rules, hooks, agents, commands, and skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,6 @@ from durable_post_lint_config.config.constants import (
25
25
  ALL_BODY_REQUIRED_ACTIONS,
26
26
  ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS,
27
27
  ALL_POST_ACTIONS,
28
- ALL_PR_DESCRIPTION_ACTIONS,
29
28
  ALL_RELEASE_BODY_MARKERS,
30
29
  ALL_REQUIRED_PR_DESCRIPTION_HEADINGS,
31
30
  ALL_TITLE_ACTIONS,
@@ -266,9 +265,8 @@ def _body_findings(
266
265
  if _is_release_automation_branch(head_branch):
267
266
  all_findings.extend(_release_body_findings(body_text))
268
267
  else:
269
- if action in ALL_PR_DESCRIPTION_ACTIONS:
270
- all_findings.extend(_description_heading_findings(body_text))
271
268
  if action == ACTION_PR_CREATE:
269
+ all_findings.extend(_description_heading_findings(body_text))
272
270
  all_findings.extend(_pull_request_proof_findings(title, body_text))
273
271
  if action == ACTION_GITHUB_MCP_COMMENT:
274
272
  all_findings.extend(_comment_length_findings(body_text))
@@ -37,9 +37,6 @@ ALL_BODY_REQUIRED_ACTIONS: frozenset[str] = frozenset(
37
37
  ACTION_PR_REVIEW,
38
38
  }
39
39
  )
40
- ALL_PR_DESCRIPTION_ACTIONS: frozenset[str] = frozenset(
41
- {ACTION_PR_CREATE, ACTION_PR_EDIT}
42
- )
43
40
  ALL_TITLE_ACTIONS: frozenset[str] = frozenset({ACTION_PR_CREATE, ACTION_PR_EDIT})
44
41
  ALL_CONVENTIONAL_TITLE_TYPES: tuple[str, ...] = (
45
42
  "build",
@@ -58,8 +55,9 @@ CONVENTIONAL_TITLE_PATTERN: re.Pattern[str] = re.compile(
58
55
  rf"^(?:{'|'.join(ALL_CONVENTIONAL_TITLE_TYPES)})(?:\([^)\r\n]+\))?!?: [^\s].*$"
59
56
  )
60
57
  ALL_REQUIRED_PR_DESCRIPTION_HEADINGS: tuple[str, ...] = (
61
- "Why",
62
- "Verification",
58
+ "What this does",
59
+ "Risk",
60
+ "How to revert",
63
61
  )
64
62
  ALL_PATH_ANCHORED_VOLATILE_PATH_MARKERS: tuple[str, ...] = (
65
63
  ".claude-profile-a/jobs/",
@@ -40,5 +40,5 @@ def should_name_the_branch_prefix_release_automation_pushes() -> None:
40
40
  assert RELEASE_BRANCH_PREFIX == "release-please--branches--"
41
41
 
42
42
 
43
- def should_require_only_the_headings_both_pull_request_rule_sets_name() -> None:
44
- assert ALL_REQUIRED_PR_DESCRIPTION_HEADINGS == ("Why", "Verification")
43
+ def should_require_the_reader_headings_a_reviewer_reads_in_place_of_the_code() -> None:
44
+ assert ALL_REQUIRED_PR_DESCRIPTION_HEADINGS == ("What this does", "Risk", "How to revert")
@@ -18,9 +18,9 @@ from hooks_constants.pull_request_proof_constants import (
18
18
  )
19
19
 
20
20
  SCRIPT_PATH = Path(__file__).resolve().parent / "durable_post_lint.py"
21
- VALID_PR_BODY = """## Why
21
+ VALID_PR_BODY = """## What this does
22
22
 
23
- Why text.
23
+ What this does text.
24
24
 
25
25
  ## Scope
26
26
 
@@ -34,9 +34,13 @@ Tradeoffs text.
34
34
 
35
35
  Blast radius text.
36
36
 
37
- ## Verification
37
+ ## Risk
38
38
 
39
- Verification text.
39
+ Risk text.
40
+
41
+ ## How to revert
42
+
43
+ Revert text.
40
44
 
41
45
  ## Existing work
42
46
 
@@ -201,7 +205,7 @@ def test_pr_body_reports_each_missing_heading(missing_heading: str) -> None:
201
205
  ]
202
206
 
203
207
 
204
- def should_accept_an_edit_body_carrying_only_the_two_required_headings() -> None:
208
+ def should_accept_an_edit_body_without_the_reader_headings() -> None:
205
209
  all_findings = durable_post_lint.lint_durable_post(
206
210
  action="pr-edit",
207
211
  title="fix(cli): tighten input handling",
package/settings.json CHANGED
@@ -3,6 +3,11 @@
3
3
  "permissions": {
4
4
  "allow": [
5
5
  "WebFetch(domain:docs.github.com)",
6
+ "WebFetch(domain:claude.com)",
7
+ "WebFetch(domain:*.claude.com)",
8
+ "WebFetch(domain:claude.dev)",
9
+ "WebFetch(domain:*.claude.dev)",
10
+ "WebFetch(domain:www.anthropic.com)",
6
11
  "Read(~/.claude/docs/**)"
7
12
  ]
8
13
  },