claude-dev-env 8.40.1 → 8.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/agents/test_agent_frontmatter.py +17 -14
- package/.agents/agents-archived/clean-coder.md +4 -4
- package/.agents/agents-archived/pr-description-writer.md +1 -1
- package/.agents/skills/pr-lifecycle/SKILL.md +470 -0
- package/.agents/skills/privacy-hygiene/reference/sweep-procedure.md +1 -1
- package/bin/ever-shipped-skills.mjs +1 -0
- package/bin/ever-shipped-skills.test.mjs +4 -0
- package/bin/install.cursor-rules.test.mjs +2 -1
- package/docs/rule-guides/code-standards.md +1 -1
- package/docs/rule-guides/destructive-commands.md +1 -1
- package/hooks/blocking/pr_lifecycle_skill_gate.py +190 -0
- package/hooks/blocking/test_pr_lifecycle_skill_gate.py +163 -0
- package/hooks/hooks.json +25 -0
- package/hooks/hooks_constants/pr_lifecycle_skill_gate_constants.py +33 -0
- package/hooks/hooks_constants/skill_loaded_reminder_constants.py +0 -10
- package/hooks/hooks_constants/spawn_readiness_hook_constants.py +30 -12
- package/hooks/hooks_constants/test_pr_lifecycle_skill_gate_constants.py +12 -0
- package/hooks/hooks_constants/test_transcript_skill_scan_constants.py +7 -0
- package/hooks/hooks_constants/transcript_skill_scan_constants.py +7 -0
- package/hooks/routing/spawn_readiness_hook.py +118 -46
- package/hooks/routing/test_spawn_readiness_hook.py +113 -21
- package/hooks/routing/test_spawn_readiness_steps.py +2 -4
- package/hooks/session/skill_loaded_reminder.py +4 -51
- package/hooks/session/test_skill_loaded_reminder.py +4 -0
- package/hooks/test_transcript_skill_scan.py +58 -0
- package/hooks/transcript_skill_scan.py +93 -0
- package/package.json +1 -1
- package/rules/flag-non-breaking-findings.md +2 -2
- package/rules/shell-invocation.md +2 -14
- package/rules/skill-pointers.md +3 -0
- package/scripts/policy_lint/adapter_configuration.py +5 -0
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/tests/test_adapter_configuration.py +8 -0
- package/scripts/tests/test_banned_prose_words.py +1 -1
- package/scripts/tests/test_rule_load_scopes.py +1 -7
- package/rules/agent-merges-its-own-green-pull-request.md +0 -55
- package/rules/ci-owns-the-gate.md +0 -101
- package/rules/durable-post-artifacts.md +0 -76
- package/rules/gh-cli-conventions.md +0 -36
- package/rules/git-workflow.md +0 -121
- package/rules/re-stage-before-commit.md +0 -14
- package/rules/review-closure-is-a-check.md +0 -54
|
@@ -450,8 +450,8 @@ EXPECTED_SOURCE_LINK_PAIRS = {
|
|
|
450
450
|
"packages/claude-dev-env/rules/windows-filesystem-safe.md",
|
|
451
451
|
),
|
|
452
452
|
(
|
|
453
|
-
"<
|
|
454
|
-
"packages/claude-dev-env/
|
|
453
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
454
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
455
455
|
),
|
|
456
456
|
(
|
|
457
457
|
"<managed-root>/rules/plain-illustrative-docstrings.md",
|
|
@@ -498,20 +498,20 @@ EXPECTED_SOURCE_LINK_PAIRS = {
|
|
|
498
498
|
"packages/claude-dev-env/rules/docstring-prose-matches-implementation.md",
|
|
499
499
|
),
|
|
500
500
|
(
|
|
501
|
-
"<
|
|
502
|
-
"packages/claude-dev-env/
|
|
501
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
502
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
503
503
|
),
|
|
504
504
|
(
|
|
505
505
|
"<managed-root>/rules/failure-blast-radius.md",
|
|
506
506
|
"packages/claude-dev-env/rules/failure-blast-radius.md",
|
|
507
507
|
),
|
|
508
508
|
(
|
|
509
|
-
"<
|
|
510
|
-
"packages/claude-dev-env/
|
|
509
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
510
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
511
511
|
),
|
|
512
512
|
(
|
|
513
|
-
"<
|
|
514
|
-
"packages/claude-dev-env/
|
|
513
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
514
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
515
515
|
),
|
|
516
516
|
(
|
|
517
517
|
"<managed-root>/rules/workers-done-before-complete.md",
|
|
@@ -524,8 +524,8 @@ EXPECTED_SOURCE_LINK_PAIRS = {
|
|
|
524
524
|
"packages/claude-dev-env/.agents/agents/reference/pr-description-illustrative-voice.md",
|
|
525
525
|
),
|
|
526
526
|
(
|
|
527
|
-
"<
|
|
528
|
-
"packages/claude-dev-env/
|
|
527
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
528
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
529
529
|
),
|
|
530
530
|
}),
|
|
531
531
|
}
|
|
@@ -628,7 +628,8 @@ def test_named_agents_resolve_active_managed_root_and_agents_home() -> None:
|
|
|
628
628
|
assert "active agents home" in agent_text.lower()
|
|
629
629
|
assert "CLAUDE_CONFIG_DIR" in agent_text
|
|
630
630
|
assert "--target" in agent_text
|
|
631
|
-
|
|
631
|
+
if each_agent_file_name == "clean-coder.md":
|
|
632
|
+
assert "<managed-root>/" in agent_text
|
|
632
633
|
assert "<agents-home>/" in agent_text
|
|
633
634
|
assert "do not assume" in agent_text.lower()
|
|
634
635
|
|
|
@@ -716,7 +717,7 @@ def test_clean_coder_links_canonical_policy_areas() -> None:
|
|
|
716
717
|
"<managed-root>/rules/verify-runtime-state.md",
|
|
717
718
|
"<managed-root>/rules/doc-inventory-integrity.md",
|
|
718
719
|
"<managed-root>/rules/failure-blast-radius.md",
|
|
719
|
-
"<
|
|
720
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
720
721
|
"<managed-root>/rules/workers-done-before-complete.md",
|
|
721
722
|
)
|
|
722
723
|
assert all(each_link in body for each_link in required_links)
|
|
@@ -959,8 +960,6 @@ def test_clean_coder_groups_session_policy_references() -> None:
|
|
|
959
960
|
"doc-inventory-integrity.md",
|
|
960
961
|
"docstring-prose-matches-implementation.md",
|
|
961
962
|
"failure-blast-radius.md",
|
|
962
|
-
"git-workflow.md",
|
|
963
|
-
"re-stage-before-commit.md",
|
|
964
963
|
"workers-done-before-complete.md",
|
|
965
964
|
)
|
|
966
965
|
session_policy_map = body[body.index("## Session policy map") :]
|
|
@@ -970,3 +969,7 @@ def test_clean_coder_groups_session_policy_references() -> None:
|
|
|
970
969
|
f"<managed-root>/rules/{each_policy_file_name}",
|
|
971
970
|
f"packages/claude-dev-env/rules/{each_policy_file_name}",
|
|
972
971
|
) in session_policy_links
|
|
972
|
+
assert (
|
|
973
|
+
"<agents-home>/skills/pr-lifecycle/SKILL.md",
|
|
974
|
+
"packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md",
|
|
975
|
+
) in session_policy_links
|
|
@@ -51,14 +51,14 @@ Installed paths use the active managed root and agents home resolved above; sour
|
|
|
51
51
|
| Naming and abbreviations | `<managed-root>/docs/CODE_RULES.md#5-no-abbreviations` (source fallback: `packages/claude-dev-env/docs/CODE_RULES.md#5-no-abbreviations`) |
|
|
52
52
|
| Policy surface map | `<managed-root>/rules/code-standards.md` (source fallback: `packages/claude-dev-env/rules/code-standards.md`) |
|
|
53
53
|
| Windows rmtree / mkdir | `<managed-root>/rules/windows-filesystem-safe.md` (source fallback: `packages/claude-dev-env/rules/windows-filesystem-safe.md`) |
|
|
54
|
-
| `gh` body files | `<
|
|
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
57
|
| Questions / task tracking | `<managed-root>/rules/verify-before-asking.md` (source fallback: `packages/claude-dev-env/rules/verify-before-asking.md`) |
|
|
58
58
|
| Runtime evidence | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/verify-runtime-state.md`) |
|
|
59
|
-
| Documentation / durable artifacts | `<managed-root>/rules/doc-inventory-integrity.md` (source fallback: `packages/claude-dev-env/rules/doc-inventory-integrity.md`), `<
|
|
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
|
-
| Git / GitHub | `<
|
|
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`) |
|
|
62
62
|
| Workers / completion | `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
|
|
63
63
|
| TDD / right-size | Review contract Tests + Design; `CODE_RULES.md` §7–§8 |
|
|
64
64
|
|
|
@@ -74,7 +74,7 @@ Load only the group that matches the task. Keep session policy details in these
|
|
|
74
74
|
| Runtime checks | `<managed-root>/rules/verify-runtime-state.md` (source fallback: `packages/claude-dev-env/rules/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
|
-
| Git | `<
|
|
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`) |
|
|
78
78
|
| Worker coordination | `<managed-root>/rules/workers-done-before-complete.md` (source fallback: `packages/claude-dev-env/rules/workers-done-before-complete.md`) |
|
|
79
79
|
|
|
80
80
|
Material implementation questions must return to the caller for `AskUserQuestion` handling; do not ask in plain text or guess.
|
|
@@ -37,7 +37,7 @@ For a voice sample only when needed, read `<agents-home>/agents/reference/pr-des
|
|
|
37
37
|
|
|
38
38
|
## Publish through GitHub CLI
|
|
39
39
|
|
|
40
|
-
Place markdown in a BOM-free temporary file and pass its path with `--body-file`. Follow `<
|
|
40
|
+
Place markdown in a BOM-free temporary file and pass its path with `--body-file`. Follow `<agents-home>/skills/pr-lifecycle/SKILL.md` (source fallback: `packages/claude-dev-env/.agents/skills/pr-lifecycle/SKILL.md`).
|
|
41
41
|
|
|
42
42
|
## Check the draft
|
|
43
43
|
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pr-lifecycle
|
|
3
|
+
description: Use for commits, pushes, pull requests, review threads, and merges.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Pull request lifecycle
|
|
7
|
+
|
|
8
|
+
## Git workflow
|
|
9
|
+
|
|
10
|
+
User-level rule: applies to **every** git repo that uses GitHub with `gh`. Small or non-primary repos follow the same rule unless the user says otherwise in the session.
|
|
11
|
+
|
|
12
|
+
### Workflow decision tree
|
|
13
|
+
|
|
14
|
+
**When to use stacked PRs:** Feature B depends on Feature A's implementation
|
|
15
|
+
|
|
16
|
+
**When to extract shared infrastructure first:** Multiple features need same utilities/helpers
|
|
17
|
+
|
|
18
|
+
**Extract Shared Infrastructure Pattern:**
|
|
19
|
+
1. Create infrastructure PR with only shared code
|
|
20
|
+
2. Get reviewed and MERGE infrastructure first
|
|
21
|
+
3. Launch parallel feature PRs that use merged infrastructure
|
|
22
|
+
|
|
23
|
+
### Pull request submission rules
|
|
24
|
+
|
|
25
|
+
**Open every pull request ready for review.** Pass `--draft` only when the owner asks
|
|
26
|
+
for a draft.
|
|
27
|
+
|
|
28
|
+
**A release bot's PR body is machine input. Leave it alone.** Release automation reads
|
|
29
|
+
back the body of its own merged pull request to decide it owns that merge. Rewriting the
|
|
30
|
+
body, or trimming its header or footer, makes the bot treat the merge as somebody else's
|
|
31
|
+
work: it cuts no tag, the publish job skips, and it opens one more release pull request on
|
|
32
|
+
the next run. The merge stays in the repository. No tag is cut and the package never publishes.
|
|
33
|
+
|
|
34
|
+
Spot one by its head branch, which starts `release-please--branches--`, or by a body that
|
|
35
|
+
opens with the bot's own marker line. The description rules in this file, the
|
|
36
|
+
`pstack:poteto-agent` writing brief, and the house wording style all step aside for it. The
|
|
37
|
+
failure signature in the release job log reads
|
|
38
|
+
`could not parse pull request body as a release PR`.
|
|
39
|
+
|
|
40
|
+
`pstack:poteto-agent` writes a title and body from the diff when you want one.
|
|
41
|
+
Publish the title and body file through
|
|
42
|
+
`~/.agents/skills/pull-request/scripts/pull_request.py`. That path is under the
|
|
43
|
+
agents home. A worktree holds no `.agents/` copy.
|
|
44
|
+
|
|
45
|
+
Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
|
|
46
|
+
otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before any
|
|
47
|
+
pull request, issue, or GitHub MCP post. The linter checks the action-specific
|
|
48
|
+
title, body, and volatile-path rules before credential lookup or network
|
|
49
|
+
access.
|
|
50
|
+
|
|
51
|
+
Use `.agents/skills/pull-request/scripts/recover_legacy_author.py
|
|
52
|
+
<exact-state-file> --confirm-inactive` only for one explicitly selected legacy
|
|
53
|
+
author record. Do not infer a record from age alone. Keep every other record
|
|
54
|
+
untouched.
|
|
55
|
+
|
|
56
|
+
### Confirm the required checks fired, and let CI run them
|
|
57
|
+
|
|
58
|
+
The gate runs once, and it runs on CI. Push the branch and read its verdict.
|
|
59
|
+
[`ci-owns-the-gate.md`](#ci-owns-the-gate) holds the reasoning and the shape
|
|
60
|
+
a local run takes when one is warranted.
|
|
61
|
+
|
|
62
|
+
Read the branch ruleset for the required check contexts before you push a
|
|
63
|
+
branch, or any level of a stack: `gh api repos/<owner>/<repo>/rules/branches/<trunk>`.
|
|
64
|
+
Read it to learn which checks must report. After the push, confirm each of those
|
|
65
|
+
contexts appears on that level's head. A required check that never fired is
|
|
66
|
+
invisible debt at every level, and it surfaces only after the whole stack is
|
|
67
|
+
pushed, when the repair costs a second pass over every branch.
|
|
68
|
+
|
|
69
|
+
A red required check blocks the branch, whoever owns the failing line. The
|
|
70
|
+
staged policy lint grades a change against the file's prior text, so a finding
|
|
71
|
+
that survives is one the change introduced or made worse. Fix that line in the
|
|
72
|
+
next push or report the branch blocked. A finding the change did not introduce
|
|
73
|
+
is a gate-scoping defect: report it against the lint and leave the file's shape
|
|
74
|
+
alone. Restructuring a file to satisfy a mis-scoped check trades one finding for
|
|
75
|
+
a set of new ones. Read the gate's own report. A
|
|
76
|
+
single-file mypy call cannot see sibling modules and reports false import
|
|
77
|
+
errors, so it neither clears nor convicts a change.
|
|
78
|
+
|
|
79
|
+
A checks listing that reports nothing on the branch is a finding.
|
|
80
|
+
Find out whether the workflow's event filters exclude the branch, or whether
|
|
81
|
+
the check simply never ran, before you treat that branch as clean.
|
|
82
|
+
|
|
83
|
+
### Each stack level stands on its own
|
|
84
|
+
|
|
85
|
+
A symbol belongs at the level that first **uses** it.
|
|
86
|
+
A bottom pull request that declares the imports its descendants will
|
|
87
|
+
need fails the linter on unused imports. A test helper that calls a function three
|
|
88
|
+
levels above it fails on an undefined name. Both defects stay invisible while you
|
|
89
|
+
read the finished tip, and both are obvious the moment you check one level alone.
|
|
90
|
+
|
|
91
|
+
Prove each level before you push it: import the modules that level changes, and run
|
|
92
|
+
the required linter against that level's own base. To repair a level, rebuild its
|
|
93
|
+
import header as the union of what that level references, let the linter's
|
|
94
|
+
autofix strip the rest, and move a premature helper up to the level that defines
|
|
95
|
+
what it calls.
|
|
96
|
+
|
|
97
|
+
### A force-push that moves content obliges a description refresh
|
|
98
|
+
|
|
99
|
+
Force-with-lease protects the ref. It protects nobody's understanding of what the
|
|
100
|
+
branch now holds. When a rewrite moves content between levels of a stack, or
|
|
101
|
+
otherwise changes what a branch contains, refresh that pull request's description
|
|
102
|
+
before you ask anyone to read or merge it.
|
|
103
|
+
|
|
104
|
+
### Never commit working documents or images
|
|
105
|
+
|
|
106
|
+
**Keep these files out of the repository:**
|
|
107
|
+
|
|
108
|
+
| Pattern | Reason |
|
|
109
|
+
|---------|--------|
|
|
110
|
+
| `docs/plans/*.md` | Working documents for planning |
|
|
111
|
+
| `*.plan.md` | Temporary planning files |
|
|
112
|
+
| `SESSION_STATE.md` | Local session state |
|
|
113
|
+
| `*.png *.jpg *.jpeg *.gif *.webp *.avif *.svg *.ico` | Images go to external storage |
|
|
114
|
+
|
|
115
|
+
An image a PR needs as visual evidence is not an exception to that row. Upload it to the repository's durable `artifacts` release with `python3 ~/.claude/scripts/gh_artifact_upload.py <file> <owner/repo>` and embed the permanent URL in the PR comment. The image lives on GitHub without entering the repository tree.
|
|
116
|
+
|
|
117
|
+
### Responding to review feedback
|
|
118
|
+
|
|
119
|
+
**When this applies:** GitHub PR review feedback on a branch you are fixing.
|
|
120
|
+
|
|
121
|
+
1. Fetch every reviewer comment before making any fix.
|
|
122
|
+
2. Create a checklist in the session's task tool with one item per comment.
|
|
123
|
+
3. Fix systematically, marking each todo complete.
|
|
124
|
+
4. Reply to each comment inline.
|
|
125
|
+
|
|
126
|
+
Repair only the reported findings.
|
|
127
|
+
|
|
128
|
+
Every `gh` post in this workflow uses `--body-file` per [gh-cli-conventions.md](#gh-cli-conventions) and keeps volatile scratch paths out per [durable-post-artifacts.md](#github-post-input-rules). Stage session edits per [re-stage-before-commit.md](#re-stage-session-edits-before-commit) before each commit.
|
|
129
|
+
|
|
130
|
+
## CI Owns the Gate
|
|
131
|
+
|
|
132
|
+
**When this applies:** Before pushing a branch, and any time a repository's full
|
|
133
|
+
check suite or policy gate is about to run on your own machine.
|
|
134
|
+
|
|
135
|
+
### Rule
|
|
136
|
+
|
|
137
|
+
The gate runs once, and it runs on CI. Push the branch, read the verdict, act on
|
|
138
|
+
what it says.
|
|
139
|
+
|
|
140
|
+
The inner development loop stays yours. Run the single test you are writing, as
|
|
141
|
+
often as it helps. That is how the change gets built. This rule governs the
|
|
142
|
+
second full pass, the one whose only product is a prediction of CI's answer.
|
|
143
|
+
Push instead, and spend the wait on the next piece of work.
|
|
144
|
+
|
|
145
|
+
### Why
|
|
146
|
+
|
|
147
|
+
**A pinned gate answers only from its pinned revision.** The workflow names an
|
|
148
|
+
exact revision of the policy package, and that revision decides which rules run.
|
|
149
|
+
The copy under your home directory is a separate artifact at its own revision. A
|
|
150
|
+
run against the home copy reports on those rules, which are a different question
|
|
151
|
+
from the one CI asks. Treat its exit code as information about the home copy
|
|
152
|
+
alone.
|
|
153
|
+
|
|
154
|
+
**Self-hosted runners often share your machine.** Where the runners execute on
|
|
155
|
+
the same host as your shell, a local suite run draws its processor time from the
|
|
156
|
+
runners, and it does so while they work on the branch you just pushed. Read
|
|
157
|
+
where the runners live, and count a local run against the same budget.
|
|
158
|
+
|
|
159
|
+
**One authoritative answer beats two.** Where both runs agree, the second one
|
|
160
|
+
restated the first. Where they differ, the environments differ, and CI is the
|
|
161
|
+
environment that decides.
|
|
162
|
+
|
|
163
|
+
### Running a gate locally
|
|
164
|
+
|
|
165
|
+
Clone the revision the workflow pins, then point the gate at that clone. That
|
|
166
|
+
run asks CI's question and its answer carries. Report a local result by naming
|
|
167
|
+
the revision it used, so a reader can tell which question it answered.
|
|
168
|
+
|
|
169
|
+
The selection flag decides which question the staged policy lint answers.
|
|
170
|
+
`--staged` and `--base <revision>` carry each file's prior text, so the lint
|
|
171
|
+
subtracts what the prior text already reported and only a breach the change
|
|
172
|
+
introduced survives. `--files` and `--repository` carry no prior text, so every
|
|
173
|
+
breach in the file reports and the command exits non-zero on debt the change
|
|
174
|
+
never touched. CI runs the merge-base form, so reproduce a CI verdict with it:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
git merge-base HEAD origin/main
|
|
178
|
+
python packages/claude-dev-env/scripts/cde_lint.py --base <the revision that printed>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
A `--files` run that comes back red on a file you touched has answered a
|
|
182
|
+
different question. Read the reported line before you treat it as yours.
|
|
183
|
+
|
|
184
|
+
### The verdict belongs to CI
|
|
185
|
+
|
|
186
|
+
CI decides whether a change passed, from evidence CI gathered. Keep that loop
|
|
187
|
+
closed. A flag, trailer, receipt, or environment variable through which the
|
|
188
|
+
change under test announces its own result hands the verdict to the subject.
|
|
189
|
+
Where the runner and the agent share one host, a signature names the same party
|
|
190
|
+
twice, so it carries the claim no further.
|
|
191
|
+
|
|
192
|
+
A cache stays available on one condition. CI derives the key itself from the
|
|
193
|
+
tree it is about to test, looks for a previous run under that key, and
|
|
194
|
+
republishes that result. CI computes, CI verifies, CI decides.
|
|
195
|
+
|
|
196
|
+
### A repository that runs no CI
|
|
197
|
+
|
|
198
|
+
An owner can rule that a repository spends no CI minutes. There the local gate
|
|
199
|
+
is the gate, and the agent that drives the pull request runs it on every head
|
|
200
|
+
it asks to merge. The repository carries the gate as a `cde verify` manifest at
|
|
201
|
+
`.claude/local-gate.json`: tests with collection floors, and lint.
|
|
202
|
+
|
|
203
|
+
1. Check out the pull request head with a clean tree and fetch its base.
|
|
204
|
+
2. Run the manifest against the base commit the pull request names:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
python <cde>/scripts/local_verification/cli.py --manifest .claude/local-gate.json --repo . --base <base sha> --output <outside the repository>/report.json
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
3. Publish the report:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
python <cde>/scripts/local_report_publisher.py --token-environment GH_TOKEN --repository <owner>/<name> --pull-number <number> --local-repo . --manifest .claude/local-gate.json --report <outside the repository>/report.json
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The publisher checks the report against the manifest digest, the clean tree,
|
|
217
|
+
and the live head and base, then posts the `local-checks` commit status. A
|
|
218
|
+
report that fails any of those posts a failure or an error. The branch ruleset
|
|
219
|
+
lists `local-checks` as a required status check, so the host refuses a merge
|
|
220
|
+
until a passing report covers the head, and `agent_merge_check.py` prints
|
|
221
|
+
`HOLD` on the same requirement. A push moves the head and leaves the status
|
|
222
|
+
behind, so each new head runs the gate again.
|
|
223
|
+
|
|
224
|
+
### Sibling rules
|
|
225
|
+
|
|
226
|
+
| Rule | Role |
|
|
227
|
+
|---|---|
|
|
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 |
|
|
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
|
+
|
|
232
|
+
## Review Closure Is a Check
|
|
233
|
+
|
|
234
|
+
**When this applies:** Any pull request an agent drives, from the first review comment on it to the merge.
|
|
235
|
+
|
|
236
|
+
### Rule
|
|
237
|
+
|
|
238
|
+
A review finding on the head is answered before the pull request merges. The agent driving it replies, pushes the fix, or both. The check named `Review closure` reads that state on every push, on every review event, and on every top-level comment, and reports red while a finding waits.
|
|
239
|
+
|
|
240
|
+
One command prints the same verdict:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
python packages/claude-dev-env/scripts/review_closure.py <owner>/<name> <number>
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
It prints `CLOSED` and exits 0 when every finding on the head is answered. It prints `OPEN`, one line per waiting finding, and exits 1. It exits 2 when the state could not be read.
|
|
247
|
+
|
|
248
|
+
### What closes a finding
|
|
249
|
+
|
|
250
|
+
| The thread | Closed by |
|
|
251
|
+
|---|---|
|
|
252
|
+
| A review comment on code | A push that replaced the code it points at |
|
|
253
|
+
| A review comment on code | A reply from the account driving the pull request |
|
|
254
|
+
| A review comment on code | Resolution, where the comment carries no red circle |
|
|
255
|
+
| A red-circle finding | A reply from the driving account, or a push that replaced the code |
|
|
256
|
+
| A thread the driving account opened | Itself |
|
|
257
|
+
| A blocking `Claude Approvals` row | A push, which moves the head the check reports on |
|
|
258
|
+
| A top-level comment on the pull request | A later top-level comment from the driving account |
|
|
259
|
+
| A bot notice: a review-skipped note, a pointer to an updated summary, a Graphite verdict mirror, a Qodo change summary, a Qodo in-progress placeholder, or a Qodo review that found no issues | Itself |
|
|
260
|
+
|
|
261
|
+
A review bot rewrites its summary comment on each pass. Its edit leaves an answered summary closed, because each new finding it has arrives as a review thread or a new comment. An edit from a person reopens the comment.
|
|
262
|
+
|
|
263
|
+
A red circle marks a finding a review states as blocking, so resolution in silence leaves it open. The reply says what changed or why the finding stands, and the reviewer reads it beside the diff.
|
|
264
|
+
|
|
265
|
+
The driving account is the one that opened the pull request. Where the agent comments under a second login, `--driver-login <login>` names it, repeatably.
|
|
266
|
+
|
|
267
|
+
A repository whose review bots post notices this package does not know passes each one's marker text with `--notice-marker <text>`, repeatably. A bot comment carrying that text closes itself, like the built-in notices above.
|
|
268
|
+
|
|
269
|
+
### Where the check runs
|
|
270
|
+
|
|
271
|
+
`.github/workflows/review-closure.yml` runs it here on a push to a pull request, on a submitted or dismissed review, on a review comment, and on a top-level comment posted or edited on a pull request. Each run reports on the pull request's head commit, so a finding posted after the last push still turns the check red.
|
|
272
|
+
|
|
273
|
+
A top-level comment arrives as an `issue_comment` event, and a run on that event belongs to the default branch commit. The comment job reads the pull request's head, runs the same command, and posts the verdict on that head through the Checks API as a `Review closure` check run. The token belongs to the GitHub Actions app, so that check run carries the same name and app as the pull request job's own, and the newest one on the head is the one branch rules read.
|
|
274
|
+
|
|
275
|
+
A private repository that installs this package runs the same command from its own workflow, against the revision of this package that its workflow pins.
|
|
276
|
+
|
|
277
|
+
A repository that merges through a merge queue also runs the check on `merge_group` and lists `Review closure` as a required check. The queue ref `gh-readonly-queue/<base>/pr-<number>-<sha>` names the pull request number the command takes. A finding posted while an entry waits in the queue then fails the queue build. Without that trigger, the entry merges on the verdict it carried when it joined the queue.
|
|
278
|
+
|
|
279
|
+
### Sibling rules
|
|
280
|
+
|
|
281
|
+
| Rule | Role |
|
|
282
|
+
|---|---|
|
|
283
|
+
| [`agent-merges-its-own-green-pull-request.md`](#the-agent-merges-its-own-green-pull-request) | The agent that drives a pull request merges it once its gate passes |
|
|
284
|
+
| [`git-workflow.md`](#git-workflow) | Open ready for review, and confirm each required context fired after the push |
|
|
285
|
+
| [`correction-lens.md`](~/.claude/rules/correction-lens.md) | A correction becomes a control at the highest layer that can hold it |
|
|
286
|
+
|
|
287
|
+
## The Agent Merges Its Own Green Pull Request
|
|
288
|
+
|
|
289
|
+
**When this applies:** Any pull request an agent opened or was asked to drive, once its checks report.
|
|
290
|
+
|
|
291
|
+
### Rule
|
|
292
|
+
|
|
293
|
+
The agent that drives a pull request merges it. A pull request that is green, carries no open review thread, and sits at a merge state of `clean` is merged in the same run that brought it there. A merge state of `unstable` counts as `clean` when every check's newest report on the head passes. GitHub also counts the older runs of a check that ran again, so a cancelled run followed by a passing re-run still reads `unstable`. Waiting for the owner to type "merge" parks finished work on the person the work was done for.
|
|
294
|
+
|
|
295
|
+
Three things stay with the owner, and nothing else does:
|
|
296
|
+
|
|
297
|
+
- A pull request the owner asked to hold.
|
|
298
|
+
- A change the owner said they want to read first.
|
|
299
|
+
- A repository whose branch rule requires an approving review the agent cannot give.
|
|
300
|
+
|
|
301
|
+
Where the branch rule requires zero approvals and one status check, that check is the gate, and the agent merges on its verdict.
|
|
302
|
+
|
|
303
|
+
Validation is the precondition, and green means every check reported on the exact head commit. A branch rule that requires one status check names the floor a merge needs; a pull request whose other checks are red or still running is held until they report.
|
|
304
|
+
|
|
305
|
+
### The precondition is mechanical
|
|
306
|
+
|
|
307
|
+
One command prints the verdict:
|
|
308
|
+
|
|
309
|
+
```
|
|
310
|
+
python packages/claude-dev-env/scripts/agent_merge_check.py <owner>/<name> <number>
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
It prints `MERGE` and exits 0 when the pull request is ready. It prints `HOLD` with the reason and exits 1 for a draft, for a head behind or conflicting with the base, for a required check that is not passing, for a check whose newest report on the head is still running, cancelled, or red, for a head the merge queue ejected for failed checks, and for an open review thread. It exits 2 when the state could not be read.
|
|
314
|
+
|
|
315
|
+
Each hold reason names its own repair, and each repair belongs to the agent:
|
|
316
|
+
|
|
317
|
+
| Hold | What the agent does |
|
|
318
|
+
|---|---|
|
|
319
|
+
| Head behind the base | Merge the base branch in and push |
|
|
320
|
+
| Head conflicts with the base | Merge the base branch in, resolve, push |
|
|
321
|
+
| A required check is red | Read the failing check, fix it, push |
|
|
322
|
+
| A check is red, cancelled, or still running | Fix a red one, re-run a cancelled one, or wait for a running one, then read the verdict again |
|
|
323
|
+
| A review thread is open | Answer it, push the fix, resolve the thread |
|
|
324
|
+
| Ejected from the merge queue for failed checks on this head | Read the merge_group run, fix the failure, push, then read the verdict again |
|
|
325
|
+
| The pull request is a draft | Mark it ready once the checks pass |
|
|
326
|
+
|
|
327
|
+
### When a gate elsewhere holds the merge command
|
|
328
|
+
|
|
329
|
+
A session working inside another repository can sit behind that repository's own pre-merge gate, which reads the checkout the session works in and refuses a merge command whatever repository the pull request belongs to. That session hands the merge to the session that owns this repository's pull requests, by message, naming the pull request. The receiving session reads the verdict above and merges. The hand-off carries the work; it never lands on the owner.
|
|
330
|
+
|
|
331
|
+
### After the merge
|
|
332
|
+
|
|
333
|
+
Delete nothing by hand. The repository deletes the head branch on merge.
|
|
334
|
+
|
|
335
|
+
### Sibling rules
|
|
336
|
+
|
|
337
|
+
| Rule | Role |
|
|
338
|
+
|---|---|
|
|
339
|
+
| [`git-workflow.md`](#git-workflow) | Open ready for review, and confirm each required context fired after the push |
|
|
340
|
+
| [`ci-owns-the-gate.md`](#ci-owns-the-gate) | The gate runs once, and it runs on CI |
|
|
341
|
+
| [`correction-lens.md`](~/.claude/rules/correction-lens.md) | A correction becomes a control at the highest layer that can hold it |
|
|
342
|
+
|
|
343
|
+
## GitHub post input rules
|
|
344
|
+
|
|
345
|
+
### When this applies
|
|
346
|
+
|
|
347
|
+
Use this rule for a GitHub issue, pull request, comment, or review created
|
|
348
|
+
through `gh` or a GitHub MCP post tool. The image section also covers a PNG a
|
|
349
|
+
commit adds and a file a release upload sends.
|
|
350
|
+
|
|
351
|
+
### Rule
|
|
352
|
+
|
|
353
|
+
A post remains after the job ends. Job scratch directories, worktrees, and
|
|
354
|
+
system temp folders do not. Do not put a path from one of those directories in
|
|
355
|
+
a post.
|
|
356
|
+
|
|
357
|
+
Handle text and binary content differently:
|
|
358
|
+
|
|
359
|
+
- **Text data** such as logs, tables, diffs, and stack traces belongs inline in
|
|
360
|
+
the post body. Do not link a scratch file that holds text data.
|
|
361
|
+
- **Binary artifacts** such as images, screenshots, and archives belong in the
|
|
362
|
+
repository's durable `artifacts` release. Use the helper:
|
|
363
|
+
|
|
364
|
+
```
|
|
365
|
+
python3 ~/.claude/scripts/gh_artifact_upload.py <file-path> <owner/repo>
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
The helper creates the `artifacts` prerelease when needed, uploads the file
|
|
369
|
+
under a `YYYYMMDD_HHMMSS_<name>` asset name, and prints a permanent download
|
|
370
|
+
URL. Put that URL in the post.
|
|
371
|
+
|
|
372
|
+
### Optimize every image before it reaches GitHub
|
|
373
|
+
|
|
374
|
+
The helper shrinks a PNG with [oxipng](https://github.com/oxipng/oxipng) before
|
|
375
|
+
it uploads, and prints the size before and after. Oxipng is lossless.
|
|
376
|
+
`--strip none` keeps every chunk, and `--nb --nc` keep the bit depth and the
|
|
377
|
+
color type, so a reader that checks the image mode sees the same file shape.
|
|
378
|
+
|
|
379
|
+
Any other route to GitHub runs the same pass first: a PNG a commit adds, and a
|
|
380
|
+
file sent with `gh release upload`.
|
|
381
|
+
|
|
382
|
+
```
|
|
383
|
+
oxipng --opt 4 --strip none --nb --nc <file> [<file> ...]
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
The `Binary optimization` check fails a pull request whose changed PNG files
|
|
387
|
+
still shrink under that pass. A fixture whose exact bytes a test pins takes
|
|
388
|
+
`binary-optimizer=keep` in `.gitattributes`, and the check skips it.
|
|
389
|
+
|
|
390
|
+
### Volatile paths that must not appear in a post body
|
|
391
|
+
|
|
392
|
+
- A job scratch directory: `.claude-profile-a/jobs/`
|
|
393
|
+
- A worktree: `.claude/worktrees/`
|
|
394
|
+
- A system temp location: `AppData\\Local\\Temp`, `%TEMP%`, `$env:TEMP`, or
|
|
395
|
+
`/tmp/`
|
|
396
|
+
- The job scratch environment variable: `$CLAUDE_JOB_DIR`
|
|
397
|
+
|
|
398
|
+
Both slash directions count. The path rule applies when a slash or backslash
|
|
399
|
+
precedes a marker, or when a path segment follows it. A standalone directory
|
|
400
|
+
name does not form a path.
|
|
401
|
+
|
|
402
|
+
### Validation
|
|
403
|
+
|
|
404
|
+
Resolve the active managed root (`CLAUDE_CONFIG_DIR` when set, `~/.claude`
|
|
405
|
+
otherwise), then run `<managed-root>/scripts/durable_post_lint.py` before the
|
|
406
|
+
server write. Pass the matching action and body file. Use `pr-create`,
|
|
407
|
+
`pr-edit`, `pr-comment`, `pr-review`, `issue-create`, `issue-edit`,
|
|
408
|
+
`issue-comment`, or `github-mcp-post`.
|
|
409
|
+
|
|
410
|
+
Pass `--repository <owner>/<name>` for the repository the post targets. A
|
|
411
|
+
post may name a private organization only inside a repository that
|
|
412
|
+
organization owns. The same digests cover the owner's private repository, the
|
|
413
|
+
owner's personal handle, and a private client. The linter holds the names as
|
|
414
|
+
digests, so it reports the line that names one without printing the name.
|
|
415
|
+
Describe the name in general terms and drop the link.
|
|
416
|
+
|
|
417
|
+
The linter reads the body file and reports a volatile local path without
|
|
418
|
+
printing the body. Fix the body and rerun the linter before posting.
|
|
419
|
+
|
|
420
|
+
## gh CLI conventions
|
|
421
|
+
|
|
422
|
+
Two `gh` call shapes need explicit handling.
|
|
423
|
+
|
|
424
|
+
### Put body content in a file
|
|
425
|
+
|
|
426
|
+
Every `gh` command that carries markdown body content uses
|
|
427
|
+
`--body-file <path>`. This applies to `gh pr create`, `gh pr edit`,
|
|
428
|
+
`gh pr comment`, `gh pr review`, `gh issue create`, `gh issue edit`, and
|
|
429
|
+
`gh issue comment`. Never pass a `--body` or `-b` string. Write the file as
|
|
430
|
+
BOM-free UTF-8:
|
|
431
|
+
|
|
432
|
+
```powershell
|
|
433
|
+
[IO.File]::WriteAllText($bodyPath, $body, [Text.UTF8Encoding]::new($false))
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
MCP GitHub tools take `body` as a structured parameter. Write the same body to
|
|
437
|
+
a UTF-8 file and run the shared linter before sending that parameter.
|
|
438
|
+
|
|
439
|
+
For pull requests, use
|
|
440
|
+
`.agents/skills/pull-request/scripts/pull_request.py`. It passes
|
|
441
|
+
`--body-file` to `gh` after the action-aware linter succeeds. For issues and
|
|
442
|
+
GitHub MCP posts, run the linter directly with `issue-create`, `issue-edit`,
|
|
443
|
+
`issue-comment`, or `github-mcp-post` as the action.
|
|
444
|
+
|
|
445
|
+
### Paginated reads slurp before they filter
|
|
446
|
+
|
|
447
|
+
Every `gh api` read of a paginated GitHub list endpoint uses
|
|
448
|
+
`--paginate --slurp` and pipes the result to external `jq`. This applies to PR
|
|
449
|
+
reviews, comments, and files, plus issue comments, pulls, and issues. The
|
|
450
|
+
built-in `--jq` runs once per page and can produce a wrong cross-page result.
|
|
451
|
+
|
|
452
|
+
Single-object endpoints such as `pulls/<n>` and `issues/<n>` do not need
|
|
453
|
+
pagination and may use `--jq` directly. For a newest-first walk, sort the
|
|
454
|
+
slurped array and take the last element. For one page, cap the request with a
|
|
455
|
+
`per_page` query parameter.
|
|
456
|
+
|
|
457
|
+
## Re-Stage Session Edits Before Commit
|
|
458
|
+
|
|
459
|
+
Stage the files you edited this session right before you commit them. A plain `git commit` records only the staged snapshot; a tracked file this session changed but left unstaged stays behind in the working tree.
|
|
460
|
+
|
|
461
|
+
No hook denies a commit that would drop tracked session edits. Run `git status` before you commit, then stage what you changed with `git add <paths>` or commit with `git commit -a`.
|
|
462
|
+
|
|
463
|
+
Staging covers tracked files you edited. Do not commit untracked files unless the user explicitly instructs it. An untracked file in the working tree is outside the change until they say otherwise.
|
|
464
|
+
|
|
465
|
+
### Staging shapes
|
|
466
|
+
|
|
467
|
+
- **A pathspec.** `git commit -- <paths>` or `git commit <paths>` commits only the named paths on purpose.
|
|
468
|
+
- **A preceding `git add` or `git stage`.** `git add <paths> && git commit …` stages the files in its own segment before the commit runs.
|
|
469
|
+
|
|
470
|
+
A `--amend` carries the same risk. An amend records the staged snapshot too, so an unstaged session edit is dropped the same way a plain commit drops it.
|
|
@@ -48,7 +48,7 @@ Review each hit. Ignore:
|
|
|
48
48
|
## 4. Re-check before commit / post
|
|
49
49
|
|
|
50
50
|
- Stage only clean files
|
|
51
|
-
- Prefer `--body-file` for `gh` posts (also required by the gh-cli-conventions
|
|
51
|
+
- Prefer `--body-file` for `gh` posts (also required by the [gh CLI conventions](../../pr-lifecycle/SKILL.md#gh-cli-conventions))
|
|
52
52
|
- No hook re-scans the next Write, commit, or post. Re-run this sweep before the next commit or post.
|
|
53
53
|
|
|
54
54
|
## Accepted residual (do not over-scrub)
|
|
@@ -37,6 +37,10 @@ test('EVER_SHIPPED_SKILL_NAMES includes the pull request skill', () => {
|
|
|
37
37
|
assert.equal(EVER_SHIPPED_SKILL_NAMES.has('pull-request'), true);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
+
test('EVER_SHIPPED_SKILL_NAMES includes the pull request lifecycle skill', () => {
|
|
41
|
+
assert.equal(EVER_SHIPPED_SKILL_NAMES.has('pr-lifecycle'), true);
|
|
42
|
+
});
|
|
43
|
+
|
|
40
44
|
test('EVER_SHIPPED_SKILL_NAMES includes the windows scheduled task skill', () => {
|
|
41
45
|
assert.equal(EVER_SHIPPED_SKILL_NAMES.has('windows-scheduled-task'), true);
|
|
42
46
|
});
|
|
@@ -122,8 +122,9 @@ test('seeds one editable policy and one native Codex routing hook', () => {
|
|
|
122
122
|
group => group.matcher === 'multi_agent_v1__spawn_agent',
|
|
123
123
|
);
|
|
124
124
|
assert.equal(firstRoutingGroups.length, 1);
|
|
125
|
-
assert.equal(firstRoutingGroups[0].hooks.length,
|
|
125
|
+
assert.equal(firstRoutingGroups[0].hooks.length, 2);
|
|
126
126
|
assert.match(firstRoutingGroups[0].hooks[0].command, /subagent_model_routing\.mjs/);
|
|
127
|
+
assert.match(firstRoutingGroups[0].hooks[1].command, /spawn_readiness_hook\.py/);
|
|
127
128
|
const firstSpawnPromptGroups = firstCodexHooks.hooks.PreToolUse.filter(
|
|
128
129
|
group => group.matcher === 'Agent|Task',
|
|
129
130
|
);
|
|
@@ -13,7 +13,7 @@ Full text behind [`rules/code-standards.md`](../../rules/code-standards.md), whi
|
|
|
13
13
|
| Contract | `docs/CODE_RULES.md` | Full review criteria for PR agents, loaded on demand |
|
|
14
14
|
| Pointer | `.cursor/BUGBOT.md` | Checked-in file Cursor BugBot reads; points at `CODE_RULES.md` |
|
|
15
15
|
| Enforcer | `hooks/blocking/code_rules_enforcer.py` | Hand-maintained checks the staged policy lint runs; not generated from the docs |
|
|
16
|
-
| Lint | `scripts/cde_lint.py` | Runs the enforcer and the other policy rules over staged or changed files, grading each against the file's prior text; see [
|
|
16
|
+
| Lint | `scripts/cde_lint.py` | Runs the enforcer and the other policy rules over staged or changed files, grading each against the file's prior text; see [CI Owns the Gate](../../.agents/skills/pr-lifecycle/SKILL.md#ci-owns-the-gate) for what each selection flag reports |
|
|
17
17
|
| Session rules | `rules/*.md` | Runtime session policy (questions, tasks, shell) |
|
|
18
18
|
|
|
19
19
|
Load `CODE_RULES.md` when reviewing a PR, resolving a policy conflict, or generating code. Prefer linking this ref over restating rules.
|