claude-dev-env 8.69.0 → 8.69.1
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/.agents/skills/pr-lifecycle/SKILL.md +3 -1
- package/.agents/skills/second-account-workers/SKILL.md +21 -3
- package/docs/rule-guides/features-start-with-an-eval.md +40 -0
- package/hooks/blocking/followup_pr_dedupe.py +55 -5
- package/hooks/blocking/pr_lifecycle_skill_gate.py +62 -11
- package/hooks/blocking/pull_request_proof.py +86 -20
- package/hooks/blocking/test_artifact_look_gate.py +1 -0
- package/hooks/blocking/test_followup_pr_dedupe.py +33 -0
- package/hooks/blocking/test_pr_lifecycle_skill_gate.py +117 -3
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +1 -5
- package/hooks/blocking/test_pull_request_proof.py +124 -1
- package/hooks/features/conduct-gates.md +1 -1
- package/hooks/features/write-edit-blocking.md +3 -5
- package/hooks/hooks.json +1 -1
- package/hooks/hooks_constants/artifact_look_gate_constants.py +1 -2
- package/hooks/hooks_constants/followup_pr_dedupe_constants.py +4 -0
- package/hooks/hooks_constants/pr_lifecycle_skill_gate_constants.py +3 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -5
- package/hooks/hooks_constants/pull_request_proof_constants.py +20 -1
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +4 -11
- package/hooks/hooks_constants/transcript_skill_scan_constants.py +1 -0
- package/hooks/test_transcript_skill_scan.py +75 -1
- package/hooks/transcript_skill_scan.py +87 -15
- package/package.json +1 -1
- package/rules/anti-corollary-tests.md +2 -2
- package/rules/archiving-agent-config.md +2 -2
- package/rules/asd-ste100-language.md +2 -2
- package/rules/bdd.md +1 -1
- package/rules/build-needs-a-user.md +1 -1
- package/rules/cleanup-temp-files.md +3 -3
- package/rules/code-standards.md +1 -1
- package/rules/correction-lens.md +3 -3
- package/rules/destructive-commands.md +3 -3
- package/rules/doc-inventory-integrity.md +1 -1
- package/rules/docstring-prose-matches-implementation.md +1 -1
- package/rules/explore-thoroughly.md +1 -1
- package/rules/failure-blast-radius.md +1 -1
- package/rules/falsify-before-green.md +2 -2
- package/rules/features-start-with-an-eval.md +9 -0
- package/rules/filesystem-search.md +3 -3
- package/rules/flag-non-breaking-findings.md +2 -2
- package/rules/long-horizon-autonomy.md +2 -2
- package/rules/memory-stores-durable-facts.md +2 -2
- package/rules/no-contrast-framing.md +1 -1
- package/rules/no-cross-skill-duplicate-helpers.md +1 -1
- package/rules/orphan-css-class.md +1 -1
- package/rules/paired-test-coverage.md +1 -1
- package/rules/plain-illustrative-docstrings.md +1 -1
- package/rules/prefer-existing-tools.md +2 -2
- package/rules/proof-before-pull-request.md +1 -1
- package/rules/request-scope.md +1 -1
- package/rules/research-mode.md +2 -2
- package/rules/retired-hook-prose.md +1 -1
- package/rules/session-title.md +1 -1
- package/rules/shell-invocation.md +2 -2
- package/rules/testing.md +1 -1
- package/rules/verify-before-asking.md +3 -3
- package/rules/verify-runtime-state.md +2 -2
- package/rules/windows-filesystem-safe.md +1 -1
- package/rules/workers-done-before-complete.md +2 -2
- package/rules/workflow-substitution-slots.md +1 -1
- package/scripts/account_broker.py +10 -3
- package/scripts/account_broker_support.py +7 -3
- package/scripts/claude_account_worker.py +24 -5
- package/scripts/claude_account_worker_process.py +5 -0
- package/scripts/claude_account_worker_report.py +16 -1
- package/scripts/dev_env_scripts_constants/claude_account_worker_constants.py +12 -0
- package/scripts/test_account_broker.py +38 -0
- package/scripts/test_account_broker_support.py +24 -0
- package/scripts/test_claude_account_worker.py +47 -0
- package/scripts/test_claude_account_worker_process.py +20 -0
- package/scripts/test_claude_account_worker_report.py +15 -0
- package/scripts/tests/test_policy_lint_rules_retired_hook_prose.py +4 -4
- package/scripts/tests/test_rule_load_scopes.py +1 -0
- package/hooks/advisory/conftest.py +0 -10
- package/hooks/advisory/refactor_guard.py +0 -385
- package/hooks/advisory/refactor_guard_test_support.py +0 -46
- package/hooks/advisory/test_refactor_guard.py +0 -148
- package/hooks/advisory/test_refactor_guard_advisory.py +0 -242
- package/hooks/advisory/test_refactor_guard_eligibility.py +0 -166
- package/hooks/hooks_constants/refactor_guard_constants.py +0 -77
- package/hooks/hooks_constants/test_refactor_guard_constants.py +0 -21
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
"""Find a skill invocation after the last transcript compaction."""
|
|
1
|
+
"""Find a skill invocation after the last transcript compaction, or one with a given first argument anywhere."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import json
|
|
6
|
-
|
|
6
|
+
import re
|
|
7
|
+
from collections.abc import Collection, Iterable, Iterator
|
|
7
8
|
|
|
8
9
|
from hooks_constants.transcript_skill_scan_constants import (
|
|
9
10
|
ASSISTANT_ENTRY_TYPE,
|
|
11
|
+
COMMAND_ARGUMENTS_PATTERN,
|
|
10
12
|
COMPACT_BOUNDARY_SUBTYPE,
|
|
11
13
|
SKILL_TOOL_NAME,
|
|
12
14
|
TOOL_USE_BLOCK_TYPE,
|
|
@@ -14,19 +16,23 @@ from hooks_constants.transcript_skill_scan_constants import (
|
|
|
14
16
|
)
|
|
15
17
|
|
|
16
18
|
|
|
17
|
-
def
|
|
18
|
-
all_entry_fields: dict[str, object],
|
|
19
|
-
skill_names: Collection[str],
|
|
20
|
-
slash_command_markers: Collection[str],
|
|
21
|
-
) -> bool:
|
|
19
|
+
def _user_command_text(all_entry_fields: dict[str, object]) -> str | None:
|
|
22
20
|
message = all_entry_fields.get("message")
|
|
23
|
-
if not isinstance(message, dict):
|
|
24
|
-
return
|
|
21
|
+
if all_entry_fields.get("type") != USER_ENTRY_TYPE or not isinstance(message, dict):
|
|
22
|
+
return None
|
|
25
23
|
content = message.get("content")
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
return content if isinstance(content, str) else None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _named_skill_inputs(
|
|
28
|
+
all_entry_fields: dict[str, object], skill_names: Collection[str]
|
|
29
|
+
) -> Iterator[dict[str, object]]:
|
|
30
|
+
message = all_entry_fields.get("message")
|
|
31
|
+
if all_entry_fields.get("type") != ASSISTANT_ENTRY_TYPE or not isinstance(message, dict):
|
|
32
|
+
return
|
|
33
|
+
content = message.get("content")
|
|
34
|
+
if not isinstance(content, list):
|
|
35
|
+
return
|
|
30
36
|
for each_block in content:
|
|
31
37
|
if not isinstance(each_block, dict) or each_block.get("type") != TOOL_USE_BLOCK_TYPE:
|
|
32
38
|
continue
|
|
@@ -36,8 +42,42 @@ def _invokes_skill(
|
|
|
36
42
|
if isinstance(invoked_name, str) and any(
|
|
37
43
|
invoked_name == name or invoked_name.endswith(":" + name) for name in skill_names
|
|
38
44
|
):
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
yield each_block["input"]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _invokes_skill(
|
|
49
|
+
all_entry_fields: dict[str, object],
|
|
50
|
+
skill_names: Collection[str],
|
|
51
|
+
slash_command_markers: Collection[str],
|
|
52
|
+
) -> bool:
|
|
53
|
+
command_text = _user_command_text(all_entry_fields)
|
|
54
|
+
if command_text is not None:
|
|
55
|
+
return any(marker in command_text for marker in slash_command_markers)
|
|
56
|
+
return next(_named_skill_inputs(all_entry_fields, skill_names), None) is not None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _first_word_is(text: object, argument_word: str) -> bool:
|
|
60
|
+
return isinstance(text, str) and text.split()[:1] == [argument_word]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _invokes_skill_with_argument(
|
|
64
|
+
all_entry_fields: dict[str, object],
|
|
65
|
+
skill_names: Collection[str],
|
|
66
|
+
argument_word: str,
|
|
67
|
+
command_marker: str,
|
|
68
|
+
) -> bool:
|
|
69
|
+
command_text = _user_command_text(all_entry_fields)
|
|
70
|
+
if command_text is not None:
|
|
71
|
+
command_arguments = re.search(COMMAND_ARGUMENTS_PATTERN, command_text, re.DOTALL)
|
|
72
|
+
return (
|
|
73
|
+
command_marker in command_text
|
|
74
|
+
and command_arguments is not None
|
|
75
|
+
and _first_word_is(command_arguments.group(1), argument_word)
|
|
76
|
+
)
|
|
77
|
+
return any(
|
|
78
|
+
_first_word_is(each_input.get("args"), argument_word)
|
|
79
|
+
for each_input in _named_skill_inputs(all_entry_fields, skill_names)
|
|
80
|
+
)
|
|
41
81
|
|
|
42
82
|
|
|
43
83
|
def _relevant_entry(each_line: str, skill_names: Collection[str]) -> dict[str, object] | None:
|
|
@@ -91,3 +131,35 @@ def is_skill_loaded_after_last_compaction(
|
|
|
91
131
|
slash_command_markers: Accepted user command tags.
|
|
92
132
|
"""
|
|
93
133
|
return skill_invocation_status(all_transcript_lines, skill_names, slash_command_markers)[1]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def invokes_skill_with_argument(
|
|
137
|
+
all_transcript_lines: Iterable[str],
|
|
138
|
+
skill_names: Collection[str],
|
|
139
|
+
argument_word: str,
|
|
140
|
+
command_marker: str,
|
|
141
|
+
) -> bool:
|
|
142
|
+
"""Return whether any entry invoked the skill with ``argument_word`` as its first argument.
|
|
143
|
+
|
|
144
|
+
::
|
|
145
|
+
|
|
146
|
+
Skill claude-api, args "build-eval extra" -> True
|
|
147
|
+
Skill plugin:claude-api, args "build-eval" -> True
|
|
148
|
+
Skill claude-api, args "migrate" -> False
|
|
149
|
+
user "/claude-api" command, command-args "build-eval" -> True
|
|
150
|
+
|
|
151
|
+
An invocation stays counted after a later compact boundary.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
all_transcript_lines: JSON transcript entries, one per line.
|
|
155
|
+
skill_names: Accepted skill names, with plugin prefixes accepted.
|
|
156
|
+
argument_word: The first whitespace-separated word the arguments must carry.
|
|
157
|
+
command_marker: The user command tag that names the skill.
|
|
158
|
+
"""
|
|
159
|
+
for each_line in all_transcript_lines:
|
|
160
|
+
if not any(name in each_line for name in skill_names):
|
|
161
|
+
continue
|
|
162
|
+
all_entry_fields = _relevant_entry(each_line, skill_names)
|
|
163
|
+
if all_entry_fields and _invokes_skill_with_argument(all_entry_fields, skill_names, argument_word, command_marker):
|
|
164
|
+
return True
|
|
165
|
+
return False
|
package/package.json
CHANGED
|
@@ -14,6 +14,6 @@ paths:
|
|
|
14
14
|
|
|
15
15
|
Before keeping a test, name one code change it would catch; replace a case that catches none. Skip spelling cross products after proving canonicalization once; compare a few discriminating cases. Exercise the production path with a non-default expected result so a dead implementation cannot pass. Test policy compliance on governed surfaces; in an audit, name a mutation and record how many tests it fails.
|
|
16
16
|
|
|
17
|
-
**Enforcement:** none
|
|
17
|
+
**Enforcement:** none.
|
|
18
18
|
|
|
19
|
-
**Full text:** [
|
|
19
|
+
**Full text:** [guide](../docs/rule-guides/anti-corollary-tests.md). Read it when choosing cases or auditing mutation evidence.
|
|
@@ -15,6 +15,6 @@ paths:
|
|
|
15
15
|
|
|
16
16
|
Trace checks and callers; `CHANGELOG.md`, `README.md`, shipped-skills registry, and host paths prove neither use nor disuse. Read `rules-archived/ARCHIVE-MANIFEST.md`; skip and report exemptions, then continue. Move unused files with `git mv` into sibling archives; record the reason, restore command, and companion edits. Retain skill names in `bin/ever-shipped-skills.mjs` and check tests lost when a skill directory moves.
|
|
17
17
|
|
|
18
|
-
**Enforcement:** none
|
|
18
|
+
**Enforcement:** none.
|
|
19
19
|
|
|
20
|
-
**Full text:** [
|
|
20
|
+
**Full text:** [guide](../docs/rule-guides/archiving-agent-config.md). Read it before moving or restoring.
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
Use the sole general language rule. Write short, complete sentences on one topic. Use active voice; lead with conditions; give one action per step. Use plain, precise words and stable terms. Expand abbreviations and contractions first; name unclear pronouns. Be inclusive; punctuate clearly. Preserve exact labels, identifiers, formulas, titles, and interface text. 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, and meaning.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/asd-ste100-language.md). Read it for sources.
|
package/rules/bdd.md
CHANGED
|
@@ -13,4 +13,4 @@ paths:
|
|
|
13
13
|
|
|
14
14
|
For every non-trivial feature, discover what you do not know, illustrate it with concrete examples in chat, formulate each behavior as a narrow "should ..." specification, then build each one through the TDD loop (CODE_RULES §8). Name developer-facing specs and tests as should sentences.
|
|
15
15
|
|
|
16
|
-
**Full text:** [
|
|
16
|
+
**Full text:** [guide](../docs/rule-guides/bdd.md), with the long-form BDD references.
|
|
@@ -17,4 +17,4 @@ Name its caller in this change, first-use date or event, and last manual use wit
|
|
|
17
17
|
|
|
18
18
|
**Enforcement:** `uncalled-new-file` checks new code under `scripts/`, `hooks/`, `bin/`, `ci/`, and `tools/` for a caller.
|
|
19
19
|
|
|
20
|
-
**Full text:** [
|
|
20
|
+
**Full text:** [guide](../docs/rule-guides/build-needs-a-user.md). Read it when sizing a new check.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**When:** Creating scratch files, debug dumps, or one-off helpers during a task.
|
|
4
4
|
|
|
5
|
-
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.
|
|
5
|
+
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.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/cleanup-temp-files.md). Read it to classify a file or choose cleanup.
|
package/rules/code-standards.md
CHANGED
|
@@ -16,4 +16,4 @@ paths:
|
|
|
16
16
|
|
|
17
17
|
**Enforcement:** the staged policy lint runs `hooks/blocking/code_rules_enforcer.py` over each changed file, and CI runs it against the merge base.
|
|
18
18
|
|
|
19
|
-
**Full text:** [
|
|
19
|
+
**Full text:** [guide](../docs/rule-guides/code-standards.md), with the policy surface map.
|
package/rules/correction-lens.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Open a control in the guarded repository, in the same run, at the highest capable layer. State the layer, why higher layers fail, and the change opened. Pair layers when both help. Move repeated corrections up a layer. Memory records the decision.
|
|
6
6
|
|
|
7
7
|
| Priority | Layer | Control |
|
|
8
|
-
|
|
8
|
+
|-|-|-|
|
|
9
9
|
| 1 | Codebase | Prevent the mistake. |
|
|
10
10
|
| 2 | Static analysis | Programmatic check. |
|
|
11
11
|
| 3 | Review tooling | Reviewer criterion. |
|
|
@@ -16,6 +16,6 @@ Open a control in the guarded repository, in the same run, at the highest capabl
|
|
|
16
16
|
|
|
17
17
|
Keep this file in `rules/`. A prune, archive, consolidation, or rewrite that would move it stops here. Edits that sharpen it are welcome.
|
|
18
18
|
|
|
19
|
-
**Enforcement:** none
|
|
19
|
+
**Enforcement:** none.
|
|
20
20
|
|
|
21
|
-
**Full text:** [
|
|
21
|
+
**Full text:** [guide](../docs/rule-guides/correction-lens.md). Read it when choosing a layer or locating a control.
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
**When:** Removing files or writing a destructive command string.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
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:
|
|
6
6
|
|
|
7
7
|
> 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.
|
|
8
8
|
|
|
9
|
-
**Enforcement:** none
|
|
9
|
+
**Enforcement:** none; a harness permission prompt can stall unattended work.
|
|
10
10
|
|
|
11
|
-
**Full text:** [
|
|
11
|
+
**Full text:** [guide](../docs/rule-guides/destructive-commands.md). Read it before any removal.
|
|
@@ -21,4 +21,4 @@ A doc that inventories code stays in step with the code, in the same change:
|
|
|
21
21
|
|
|
22
22
|
**Enforcement:** `repository_checks/claude_md.py`, `repository_checks/package_inventory.py`, and `repository_checks/env_var_documentation.py`. Run `python packages/claude-dev-env/scripts/repository_policy.py` before you commit. CI runs the same command.
|
|
23
23
|
|
|
24
|
-
**Full text:** [
|
|
24
|
+
**Full text:** [guide](../docs/rule-guides/doc-inventory-integrity.md).
|
|
@@ -12,4 +12,4 @@ The list covers every behavior the body applies, and the body accepts only what
|
|
|
12
12
|
|
|
13
13
|
**Enforcement:** `code_rules_docstrings.py` and the JS slices in `code_rules_imports_logging.py`, which the staged policy lint runs through `code_rules_enforcer.py`, plus its `hook-prose-consistency` rule. CI runs it against the merge base.
|
|
14
14
|
|
|
15
|
-
**Full text:** [
|
|
15
|
+
**Full text:** [guide](../docs/rule-guides/docstring-prose-matches-implementation.md), with the write-time checklist. The Category O audit rubric carries the full standard.
|
|
@@ -6,4 +6,4 @@ Search for what already does the ask, in full or in part. When something exists,
|
|
|
6
6
|
|
|
7
7
|
**Enforcement:** the pull request gate denies a body with no "Existing work" section.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/explore-thoroughly.md). Read it for search scope and depth.
|
|
@@ -17,4 +17,4 @@ Four failures end a run: the source bytes changed, a provenance or digest mismat
|
|
|
17
17
|
|
|
18
18
|
**Enforcement:** `code_rules_blast_radius.py`, which the staged policy lint runs through `code_rules_enforcer.py` under its `code-rules` rule. CI runs it against the merge base.
|
|
19
19
|
|
|
20
|
-
**Full text:** [
|
|
20
|
+
**Full text:** [guide](../docs/rule-guides/failure-blast-radius.md), with the boundary example and the Codex excerpt.
|
|
@@ -15,6 +15,6 @@ paths:
|
|
|
15
15
|
|
|
16
16
|
Apply a named break that the same check must catch, and run a passing control beside it on the same command. Record the break, its failing output, and the control before counting the restored green as evidence. If the check stays green under its break, fix its reach; a probe needs a trip input, a sweep needs a planted violation in its claimed file set, and a writer assertion needs a disabled production writer.
|
|
17
17
|
|
|
18
|
-
**Enforcement:** none
|
|
18
|
+
**Enforcement:** none.
|
|
19
19
|
|
|
20
|
-
**Full text:** [
|
|
20
|
+
**Full text:** [guide](../docs/rule-guides/falsify-before-green.md). Read it when designing a break or reviewing a red record.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Features start with an eval
|
|
2
|
+
|
|
3
|
+
**When:** A user asks for a new feature or a change in behavior.
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
**Enforcement:** `hooks/blocking/pr_lifecycle_skill_gate.py` denies a `feat` pull request with no "Eval" section or no build-eval call.
|
|
8
|
+
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/features-start-with-an-eval.md).
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**When:** Searching for files by name, path, extension, size, or date.
|
|
4
4
|
|
|
5
|
-
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.
|
|
5
|
+
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.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/filesystem-search.md). Read it for tool selection and search examples.
|
|
@@ -15,6 +15,6 @@ paths:
|
|
|
15
15
|
|
|
16
16
|
Give every check ID a breaking or smell severity row; style and structure findings are smells. Fail the gate and stop for a bug, secret, broken test, syntax error, or instruction file that cannot load. Record a smell in `.claude/followups/smells.jsonl`, proceed, and fix it later in one combined follow-up pull request per parent pull request, whose body names the parent as `Follow-up to #N`; ledger failure leaves the gate decision unchanged. Treat an unclassified check as breaking.
|
|
17
17
|
|
|
18
|
-
**Enforcement:** none
|
|
18
|
+
**Enforcement:** none.
|
|
19
19
|
|
|
20
|
-
**Full text:** [
|
|
20
|
+
**Full text:** [guide](../docs/rule-guides/flag-non-breaking-findings.md). Read it when adding a check.
|
|
@@ -10,6 +10,6 @@ paths:
|
|
|
10
10
|
|
|
11
11
|
Act on settled facts and authority, and recommend after researching unclear intent. When protected or irreversible actions, scope changes, or user-only input block the task, ask for one answer in the user-facing channel, name what resumes, and keep other work moving. Finish owed work without ending a turn on a promise, milestone, or context limit. Delegate independent tracks while working, ground claims in tool output, name skipped checks, make the first update one sentence, reserve later updates for discoveries, and close with the outcome and input.
|
|
12
12
|
|
|
13
|
-
**Enforcement:** none
|
|
13
|
+
**Enforcement:** none.
|
|
14
14
|
|
|
15
|
-
**Full text:** [
|
|
15
|
+
**Full text:** [guide](../docs/rule-guides/long-horizon-autonomy.md). Read it when preparing the run.
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
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.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/memory-stores-durable-facts.md). Read it when deciding whether a fact belongs in memory.
|
|
@@ -6,4 +6,4 @@ State the chosen point directly and leave out the discarded reading. Remove thes
|
|
|
6
6
|
|
|
7
7
|
**Enforcement:** staged policy lint and `scripts/durable_post_lint.py` check authored text; the agent checks chat.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/no-contrast-framing.md). Read it for examples and checker exceptions.
|
|
@@ -10,4 +10,4 @@ Within one skill, extract a shared module and import it at both call sites. Acro
|
|
|
10
10
|
|
|
11
11
|
**Enforcement:** `code_rules_duplicate_body.py` blocks sibling-module copies and emits cross-skill advisories through `code_rules_enforcer.py`; the agent judges copy size.
|
|
12
12
|
|
|
13
|
-
**Full text:** [
|
|
13
|
+
**Full text:** [guide](../docs/rule-guides/no-cross-skill-duplicate-helpers.md). Read it when judging copy size or dependency ownership.
|
|
@@ -11,4 +11,4 @@ Every class the markup references has a `.<class>` selector in a nearby `<style>
|
|
|
11
11
|
|
|
12
12
|
**Enforcement:** `check_orphan_css_classes` in `code_rules_orphan_css_class.py`, which the staged policy lint runs through `code_rules_enforcer.py`.
|
|
13
13
|
|
|
14
|
-
**Full text:** [
|
|
14
|
+
**Full text:** [guide](../docs/rule-guides/orphan-css-class.md).
|
|
@@ -11,4 +11,4 @@ Every public function such a module defines gets a behavioral test in its paired
|
|
|
11
11
|
|
|
12
12
|
**Enforcement:** `check_public_function_missing_paired_test` and `check_test_file_omits_module_public_function` in `code_rules_paired_test.py`, which the staged policy lint runs through `code_rules_enforcer.py`. Both record smells per [`flag-non-breaking-findings.md`](flag-non-breaking-findings.md).
|
|
13
13
|
|
|
14
|
-
**Full text:** [
|
|
14
|
+
**Full text:** [guide](../docs/rule-guides/paired-test-coverage.md).
|
|
@@ -11,4 +11,4 @@ Write the narrative so a general developer follows it on the first read. Paint a
|
|
|
11
11
|
|
|
12
12
|
**Enforcement:** `check_docstring_runon_sentence` and `check_docstring_prose_wall_without_illustration` in `code_rules_docstrings.py`, which the staged policy lint runs through `code_rules_enforcer.py`. Category O sub-bucket O9 of the audit rubric carries the judgment.
|
|
13
13
|
|
|
14
|
-
**Full text:** [
|
|
14
|
+
**Full text:** [guide](../docs/rule-guides/plain-illustrative-docstrings.md), with the canonical example.
|
|
@@ -17,6 +17,6 @@ paths:
|
|
|
17
17
|
|
|
18
18
|
Search local and shared code, then open-source options. Stop at first fit; build if neither fits. Choose an external option with an active security process, no open critical advisory, field reputation, broad use, and a release or commit within a year; add missing rules in its configuration without a wrapper. Report its name, link, license, and one use count; for custom code, name rejected candidates and reasons.
|
|
19
19
|
|
|
20
|
-
**Enforcement:** none
|
|
20
|
+
**Enforcement:** none.
|
|
21
21
|
|
|
22
|
-
**Full text:** [
|
|
22
|
+
**Full text:** [guide](../docs/rule-guides/prefer-existing-tools.md). Read it when comparing candidates.
|
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|
|
5
5
|
Run the change where it runs, with and without it, and quote both results under "Proof in practice" in the body. A hook denies a body without one. Unproven: leave it unopened and tell the user.
|
|
6
6
|
|
|
7
|
-
**Full text:** [
|
|
7
|
+
**Full text:** [guide](../docs/rule-guides/proof-before-pull-request.md).
|
package/rules/request-scope.md
CHANGED
|
@@ -4,4 +4,4 @@
|
|
|
4
4
|
|
|
5
5
|
A shipped gate makes each block line restating what it enforces dead text. Propose removing all of them, past the part asked about, and give each kept line's reason. Read the output back before reporting done.
|
|
6
6
|
|
|
7
|
-
**Full text:** [
|
|
7
|
+
**Full text:** [guide](../docs/rule-guides/request-scope.md)
|
package/rules/research-mode.md
CHANGED
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
State no claim without a credible source; do not guess or infer. Before an answer depends on an unsettled fact, run each tool that could settle it and that reads state or that permissions already allow, and stop at the first one that settles it. When none can, name the exact check and who can run or approve it. Cite a project file, web source, named expert or paper, or official documentation for each claim. Retract claims a source cannot support. Extract document text before analysis and ground the answer in word-for-word quotes. In chat, cite compactly with a link or `file:line`; put full quotes and citation lists in artifacts or replies that request them. Ground synthesis in sourced inputs. Creative ideas need no citation.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/research-mode.md). Read it when checking a source or citation.
|
|
@@ -11,4 +11,4 @@ Verify the module and its hook registration or dispatcher roster before claiming
|
|
|
11
11
|
|
|
12
12
|
**Enforcement:** `retired-hook-prose` in `scripts/policy_lint/registry.py` checks instruction Markdown, including `rules/` and `docs/`; the detour sweep is manual.
|
|
13
13
|
|
|
14
|
-
**Full text:** [
|
|
14
|
+
**Full text:** [guide](../docs/rule-guides/retired-hook-prose.md). Read it when tracing a hook's current path or removing its gate.
|
package/rules/session-title.md
CHANGED
|
@@ -6,4 +6,4 @@ Before each turn ends, set the title to `<emoji> <name>`. Use the red flag when
|
|
|
6
6
|
|
|
7
7
|
**Enforcement:** `hooks/blocking/session_title_format_gate.py` denies a malformed title; `hooks/blocking/session_title_stop_gate.py` blocks a turn end with no title.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/session-title.md). Read it for the emoji table and naming rules.
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
On Windows, run shell work through `pwsh`: `pwsh -NoProfile -File '<script>.ps1' <args>` for a script, and `pwsh -NoProfile -Command "..."` or the `PowerShell` tool for inline work. A direct interpreter call such as `python script.py` conforms as written. Keep `powershell`, `cmd /c`, and `bash -c` out of permission rules. Keep `$(...)`, unescaped backticks, and `<(...)` or `>(...)` out of Bash commands; split the call or use a flag form such as `git -C "<path>"`. Arithmetic `$((...))` passes.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none; a permission prompt on a wrapped command is the signal. `blocking/msys_rev_path_rewriter.py` only rewrites a `<rev>:<path>` argument.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/shell-invocation.md). Read it before writing a permission rule or a multi-step shell command.
|
package/rules/testing.md
CHANGED
|
@@ -16,4 +16,4 @@ Read [`TEST_QUALITY.md`](../docs/TEST_QUALITY.md) and give mocks every field the
|
|
|
16
16
|
|
|
17
17
|
**Enforcement:** `useless_test_checks.py` and `code_rules_test_assertions.py` catch weak assertions; `Fix test proof` checks fix tests.
|
|
18
18
|
|
|
19
|
-
**Full text:** [
|
|
19
|
+
**Full text:** [guide](../docs/rule-guides/testing.md). Read it for mock fields, test order, and fix proof details.
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
**When:** Before asking the user a clarifying question.
|
|
4
4
|
|
|
5
|
-
Inspect files, directories, configuration, environment, databases, and available tools for the answer. Recheck facts recalled from earlier sessions. Apply any criterion the user already supplied and state the decision. Ask only for a judgment, preference, or inaccessible fact
|
|
5
|
+
Inspect files, directories, configuration, environment, databases, and available tools for the answer. Recheck facts recalled from earlier sessions. Apply any criterion the user already supplied and state the decision. Ask only for a judgment, preference, or inaccessible fact.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/verify-before-asking.md). Read it when the source of an answer is uncertain.
|
|
@@ -4,6 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
Gather a live signal this session: process list, port probe, log, status code, or fresh reproduction. Read the user's terminal and named error log while a reported failure is still visible. Check the effect the work should produce, including each job result, loaded config, or deployed artifact; a success status alone does not establish the effect. When testing code, identify the loaded module path.
|
|
6
6
|
|
|
7
|
-
**Enforcement:** none
|
|
7
|
+
**Enforcement:** none.
|
|
8
8
|
|
|
9
|
-
**Full text:** [
|
|
9
|
+
**Full text:** [guide](../docs/rule-guides/verify-runtime-state.md). Read it when choosing the probe for a claim.
|
|
@@ -14,4 +14,4 @@ Call `shutil.rmtree` with an `onexc` (Python 3.12 and later) or `onerror` handle
|
|
|
14
14
|
|
|
15
15
|
**Enforcement:** the staged policy lint's `rmtree-safety` rule, which returns the full `force_rmtree` code. CI runs it against the merge base.
|
|
16
16
|
|
|
17
|
-
**Full text:** [
|
|
17
|
+
**Full text:** [guide](../docs/rule-guides/windows-filesystem-safe.md).
|
|
@@ -10,6 +10,6 @@ paths:
|
|
|
10
10
|
|
|
11
11
|
List every worker and confirm that each has finished and its result is merged into run state. Verify worker file lists, counts, descriptions, and findings against the repository and diff before repeating them. While a worker runs or output is missing, keep the task `in_progress`, report dead or hung workers, schedule a wakeup, and keep other work moving. Check the task goal against merged state before closing.
|
|
12
12
|
|
|
13
|
-
**Enforcement:** none
|
|
13
|
+
**Enforcement:** none.
|
|
14
14
|
|
|
15
|
-
**Full text:** [
|
|
15
|
+
**Full text:** [guide](../docs/rule-guides/workers-done-before-complete.md). Read it before closing a task that spawned workers.
|
|
@@ -11,4 +11,4 @@ Mark each changing value with angle brackets, including loop indices in paths or
|
|
|
11
11
|
|
|
12
12
|
**Enforcement:** `workflow-substitution` in the staged policy lint.
|
|
13
13
|
|
|
14
|
-
**Full text:** [
|
|
14
|
+
**Full text:** [guide](../docs/rule-guides/workflow-substitution-slots.md). Read it when a loop builds a path or output key.
|
|
@@ -216,6 +216,7 @@ class _RunContext:
|
|
|
216
216
|
all_spent_accounts: set[Account]
|
|
217
217
|
all_spent_resets: dict[Account, datetime]
|
|
218
218
|
start_failure_text: str = ""
|
|
219
|
+
live_log: Path | None = None
|
|
219
220
|
|
|
220
221
|
|
|
221
222
|
def _prepare_run(
|
|
@@ -269,6 +270,7 @@ def _invoke(context: _RunContext, account: Account) -> subprocess.CompletedProce
|
|
|
269
270
|
cwd=str(context.cwd) if context.cwd is not None else None,
|
|
270
271
|
encoding=context.encoding,
|
|
271
272
|
errors=context.errors,
|
|
273
|
+
live_log=context.live_log,
|
|
272
274
|
)
|
|
273
275
|
|
|
274
276
|
|
|
@@ -317,9 +319,11 @@ def _attempt_once(context: _RunContext, decision: Decision) -> JobOutcome | None
|
|
|
317
319
|
def _execute(
|
|
318
320
|
product: Product, all_argv: Sequence[str], *, now: datetime,
|
|
319
321
|
timeout_seconds: float | None = None, stdin_text: str | bytes | None = None,
|
|
320
|
-
cwd: str | Path | None = None, encoding: str = "utf-8", errors: str = "replace"
|
|
322
|
+
cwd: str | Path | None = None, encoding: str = "utf-8", errors: str = "replace",
|
|
323
|
+
live_log: Path | None = None,
|
|
321
324
|
) -> tuple[JobOutcome, Report]:
|
|
322
325
|
context = _prepare_run(product, all_argv, now, timeout_seconds, stdin_text, cwd, encoding, errors)
|
|
326
|
+
context.live_log = live_log
|
|
323
327
|
while True:
|
|
324
328
|
picked = choose_from_readings(product, context.all_readings, now=now, all_spent_accounts=frozenset(context.all_spent_accounts), preferred_command=context.preferred_command, all_spent_resets=context.all_spent_resets)
|
|
325
329
|
decision = _with_outside_spent_marks(picked, _outside_roster_resets(product, context.all_readings, context.all_state, now))
|
|
@@ -340,8 +344,9 @@ def run_job(
|
|
|
340
344
|
cwd: str | Path | None = None,
|
|
341
345
|
encoding: str = "utf-8",
|
|
342
346
|
errors: str = "replace",
|
|
347
|
+
live_log: Path | None = None,
|
|
343
348
|
) -> JobOutcome:
|
|
344
|
-
return _execute(product, all_argv, now=datetime.now(timezone.utc), timeout_seconds=timeout_seconds, stdin_text=stdin_text, cwd=cwd, encoding=encoding, errors=errors)[0]
|
|
349
|
+
return _execute(product, all_argv, now=datetime.now(timezone.utc), timeout_seconds=timeout_seconds, stdin_text=stdin_text, cwd=cwd, encoding=encoding, errors=errors, live_log=live_log)[0]
|
|
345
350
|
|
|
346
351
|
|
|
347
352
|
def _parser() -> argparse.ArgumentParser:
|
|
@@ -363,7 +368,9 @@ def _parser() -> argparse.ArgumentParser:
|
|
|
363
368
|
|
|
364
369
|
|
|
365
370
|
def _parse_spent_mark(all_accounts_by_name: Mapping[str, Reading], mark: str, product: Product) -> tuple[Reading, datetime | None]:
|
|
366
|
-
name, separator, reset_text = mark.
|
|
371
|
+
name, separator, reset_text = mark.rpartition(":")
|
|
372
|
+
if mark in all_accounts_by_name or not separator:
|
|
373
|
+
name, separator, reset_text = mark, "", ""
|
|
367
374
|
if not name:
|
|
368
375
|
raise BrokerConfigurationError(f"spent mark {mark!r} needs an account name")
|
|
369
376
|
try:
|
|
@@ -14,7 +14,7 @@ import tempfile
|
|
|
14
14
|
import threading
|
|
15
15
|
from contextlib import contextmanager
|
|
16
16
|
from datetime import datetime, timezone
|
|
17
|
-
from pathlib import Path
|
|
17
|
+
from pathlib import Path, PureWindowsPath
|
|
18
18
|
from typing import Callable, Iterator, Sequence
|
|
19
19
|
|
|
20
20
|
if sys.platform == "win32":
|
|
@@ -147,7 +147,9 @@ def _account_order(main_home: Path) -> tuple[str, ...]:
|
|
|
147
147
|
|
|
148
148
|
def _order_names(account: Account) -> frozenset[str]:
|
|
149
149
|
launcher_name = Path(CLAUDE_LAUNCHER_PROGRAM.file_name_template.format(profile_name=account.name)).stem
|
|
150
|
-
|
|
150
|
+
all_candidates = (account.name, account.command, launcher_name)
|
|
151
|
+
all_launcher_stems = tuple(PureWindowsPath(each_name).stem for each_name in all_candidates if each_name)
|
|
152
|
+
return frozenset(each_name.casefold() for each_name in (*all_candidates, *all_launcher_stems) if each_name)
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
def _with_priorities(all_accounts: Sequence[Account], all_ordered_names: Sequence[str]) -> tuple[Account, ...]:
|
|
@@ -509,7 +511,9 @@ def _run_captured_subprocess(all_argv: Sequence[str], **options: object) -> subp
|
|
|
509
511
|
encoding = str(options.get("encoding") or "utf-8")
|
|
510
512
|
errors = str(options.get("errors") or "replace")
|
|
511
513
|
stdin_bytes = options.get("input")
|
|
512
|
-
|
|
514
|
+
live_log = options.get("live_log")
|
|
515
|
+
stdout_target = open(live_log, "w+b") if live_log is not None else tempfile.TemporaryFile()
|
|
516
|
+
with stdout_target as stdout_file, tempfile.TemporaryFile() as stderr_file:
|
|
513
517
|
with subprocess.Popen(
|
|
514
518
|
_resolve_command(all_argv),
|
|
515
519
|
stdin=subprocess.PIPE if stdin_bytes is not None else None,
|