@erclx/aitk 3.14.0 → 3.14.2

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "3.14.0",
4
+ "version": "3.14.2",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -78,11 +78,14 @@ the `git-followup` skill, invoked with `reply-owned` so it stages, commits,
78
78
  pushes, and refreshes the open PR body without posting its own comment. This
79
79
  skill owns the reply. Do not reimplement that flow here.
80
80
 
81
- For in-place fixes to files the PR body already covers, `git-followup` leaves the body untouched and
82
- the reply comment carries the fix log. A rebase in step 5 rewrote the branch, so
83
- that push is a force-push and `git-followup` resolves it from the tracking
84
- branch. Worker branches are single-owner here, which is what makes overwriting
85
- the remote safe.
81
+ `git-followup` syncs the body and title against the fix commit on every
82
+ invocation, including `reply-owned`, so the merge record reflects what this pass
83
+ changed rather than only what opened the PR. The reply comment still carries the
84
+ fix log mapped to each finding, since the two serve different readers: the body
85
+ is the merge record and the reply is the review's own thread. A rebase in step 5
86
+ rewrote the branch, so that push is a force-push and `git-followup` resolves it
87
+ from the tracking branch. Worker branches are single-owner here, which is what
88
+ makes overwriting the remote safe.
86
89
 
87
90
  `git-followup` stops on an unchanged tree. A run that answered every finding as a
88
91
  conscious-accept and whose rebase left the generated files alone has nothing for
@@ -73,6 +73,20 @@ git merge-base --is-ancestor <prior-oid> <headRefOid>
73
73
 
74
74
  On exit zero, review `<prior-oid>..<headRefOid>` and nothing else. `git diff` and `git log --oneline` over that range are the whole read, because the first pass already covered everything behind it. A non-zero exit means the branch was rebased or force-pushed, so the delta is undefined rather than empty. Fall back to the full pass above and say so in the body.
75
75
 
76
+ A commit is its own ancestor, so an unchanged head passes that test too, with an empty range. When `<prior-oid>` equals `<headRefOid>`, decide whether this pass has anything to add before reading anything else, since the empty range itself cannot answer that:
77
+
78
+ ```bash
79
+ gh pr view <number> --json reviews,comments --jq '([.reviews[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review" or . == "## Review closed")] | last | .submittedAt) as $prior | [.comments[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review response") | select(.createdAt > $prior)] | last | .url // empty | split("-") | last'
80
+ ```
81
+
82
+ Scope the responses to those newer than the prior pass, never to every response the thread carries. A pass answering the newest response and a pass answering an older one derive the same third segment (Step 4), so an unscoped read hands a re-run after a close-out the name its own prior pass already wrote. That is the collision this case exists to prevent, reached without a rebase or an error.
83
+
84
+ Read the number off `.url`. The `id` field carries a GraphQL node id, which the thread never displays. Keep the `// empty` guard, since `split` aborts jq on the null an empty selection returns, and an aborted command reaches the session as an error rather than as the empty result the stop below reads.
85
+
86
+ An empty result means no response arrived since the prior pass, so the head is unchanged and this pass has nothing new to add. Stop here, before Step 3 or Step 4 run: `❌ The head is unchanged since the prior pass on <short-sha>. Nothing new to review.` This is the earliest point every path crosses, which is why the check sits here rather than inside Step 4's filename derivation. A path that decides there is nothing to add never reaches a step reached only when composing a body, so a stop written there is a stop a shortcut path can route around.
87
+
88
+ A non-empty result carries the comment id Step 4 needs for the third filename segment. Read that comment for what the worker changed or accepted, and treat an accepted finding as closed rather than restating it. This is the entire read on a repeated head, since the empty range above has nothing in it to say whether a prior finding landed. Skip the diff and file reads below.
89
+
76
90
  Read each changed file in scope. Skip deleted files. Run reads in parallel.
77
91
 
78
92
  ## Step 3: review
@@ -87,6 +101,8 @@ Then read the description's `## Testing` section, which is the one part of a pul
87
101
 
88
102
  Test every unchecked box against the testing discipline in `${CLAUDE_SKILL_DIR}/../../standards/pr.md`, which reserves an unchecked box for a capability the agent lacks. Raise the box when it names no human at all, when the human it names is a live agent session, or when it names a person for a step the repository ships a harness for, `scripts/sandbox/run.sh` and `scripts/eval/run.sh` being the two. Cost alone is not an answer, since authorizing a spend is the operator's and performing the run is not. A refusal the author actually met is an answer, and it names which one.
89
103
 
104
+ Test every ticked box too, bounded to one naming a file or a command. Confirm what it names still exists at `<headRefOid>`, `git show <headRefOid>:<path>` for a file or `git grep <command> <headRefOid>` for a command, rather than re-running what it claims. A box naming neither stays untested, since nothing here can confirm a claim carrying no artifact. A box ticked before a later commit removed or replaced what it names is a `should-fix` finding on the body itself, filed under a `**PR body**` block ahead of the file blocks, since what it corrupts is the merge record rather than a file in the diff.
105
+
90
106
  Ask rather than grade. Whether a human is genuinely required is a reading the branch author may hold a reason this session cannot see, so the question carries no severity and enters no count. What it does carry is the heading and the dispatch, on the rule Step 4 states, because a question the author never receives corrects nothing and the author is the only party who can answer it. Answering it closes it, and the answer may be that the requirement holds.
91
107
 
92
108
  Read `## For the reviewer` the same way, bounded to the bullets under that heading rather than the Summary or the Technical Context around it. Those two carry the author's argument for the change, and reading them while judging it is most of what an independent pass exists to avoid, so the read stops at the section itself.
@@ -107,19 +123,7 @@ Write the comment to `.claude/.tmp/pr-review/body-<number>-<short-sha>.md`. The
107
123
 
108
124
  Derive both segments from Step 1. Never pick a suffix by hand, and never reuse a name the folder already holds.
109
125
 
110
- When `<prior-oid>` from Step 2 equals `headRefOid`, the head repeats and the folder already holds `body-<number>-<short-sha>.md`. Add a third segment taking the id of the `## Review response` comment this pass answers, giving `body-<number>-<short-sha>-r<comment-id>.md`. That satisfies both prohibitions above rather than carving an exception into either.
111
-
112
- ```bash
113
- gh pr view <number> --json reviews,comments --jq '([.reviews[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review" or . == "## Review closed")] | last | .submittedAt) as $prior | [.comments[] | select(.body // "" | split("\n")[0] | rtrimstr("\r") | . == "## Review response") | select(.createdAt > $prior)] | last | .url // empty | split("-") | last'
114
- ```
115
-
116
- Scope the responses to those newer than the prior pass, never to every response the thread carries. A pass answering the newest response and a pass answering an older one derive the same third segment, so an unscoped read hands a re-run after a close-out the name its own prior pass already wrote. That is the collision this case exists to prevent, reached without a rebase or an error.
117
-
118
- Read the number off `.url`. The `id` field carries a GraphQL node id, which the thread never displays. Keep the `// empty` guard, since `split` aborts jq on the null an empty selection returns, and an aborted command reaches the session as an error rather than as the empty result the stop below reads.
119
-
120
- An empty result means no response arrived since the prior pass, so this pass would restate a body the folder already holds. Stop: `❌ No response since the prior pass on <short-sha>. Nothing new to review.`
121
-
122
- The response is also the whole read on a repeated head. Step 2 resolves an empty range, because a commit is its own ancestor and `<prior-oid>..<headRefOid>` spans nothing, so the delta cannot answer whether a prior finding landed. Read that comment for what the worker changed or accepted, and treat an accepted finding as closed rather than restating it.
126
+ When `<prior-oid>` from Step 2 equals `headRefOid`, the head repeats and the folder already holds `body-<number>-<short-sha>.md`. Add a third segment taking the id of the `## Review response` comment Step 2 resolved, giving `body-<number>-<short-sha>-r<comment-id>.md`. That satisfies both prohibitions above rather than carving an exception into either. Step 2 already stopped the pass when that resolution came back empty, so reaching this line means the comment id is in hand.
123
127
 
124
128
  The comment is a rendered-for-human GitHub surface, so load the `write-human` skill for voice and follow `${CLAUDE_SKILL_DIR}/../../standards/markdown.md` for the banned words: cut editorializing, and keep every sentence load-bearing. Match this shape on a first pass:
125
129
 
@@ -140,6 +144,8 @@ X critical, Y should-fix, Z minor. Reviewed against project docs and the board.
140
144
  🤖 Reviewed by Claude Code
141
145
  ```
142
146
 
147
+ A stale ticked box goes in a `**PR body**` block, in place of a `**`path/to/file.ext`**` block and ahead of every one of those, since it precedes the code the diff carries rather than sitting inside it.
148
+
143
149
  A later pass carrying findings keeps that shape and changes only the summary line:
144
150
 
145
151
  ```markdown
@@ -11,6 +11,8 @@ Without this skill, an edit made after a pull request is already open ships as a
11
11
 
12
12
  A caller that rebased the branch before handing over hits a rejected push, since the tracking branch no longer reaches the head. A branch entered through a worktree hits the opposite shape, carrying an open pull request with no tracking ref at all, which read as a branch that had never been pushed.
13
13
 
14
+ A fix commit answering a review is the sharpest case of the first paragraph, since it always changes what shipped. The routing that once picked whether to sync the body keyed off the same branch that decides whether to reply, so reaching `reply-owned` or a nonzero comment count meant the sync never ran at all. The description a merge record carries was true when the pull request opened and false by the time review finished, with nothing between the two that read it back.
15
+
14
16
  ## Must
15
17
 
16
18
  - Refuse unless the branch has an open pull request, since every later step addresses it
@@ -8,7 +8,7 @@ description: Ships a small self-review edit on the current PR branch by staging,
8
8
  Ship a small self-review edit on the current PR branch in one pass.
9
9
 
10
10
  When invoked with `reply-owned`, a caller such as `claude-address-review` posts
11
- its own reply, so skip the comment in step 6. The push and body sync still run.
11
+ its own reply, so skip the comment in step 7. The push and body sync still run.
12
12
 
13
13
  ## Guards
14
14
 
@@ -30,10 +30,14 @@ A missing tracking ref is no longer a guard. An open pull request proves the bra
30
30
  The lease is what stops the force from overwriting a commit this session never read. Run the ancestry test only where an upstream resolves, since it reads `@{u}`.
31
31
 
32
32
  5. Check for existing review comments: `gh api 'repos/{owner}/{repo}/pulls/<number>/comments' --jq 'length'`, resolving `<number>` from `gh pr view --json number`.
33
- 6. Route on the invocation and the comment count.
34
- - When invoked with `reply-owned`, skip this step's comment: the caller posts the reply.
33
+ 6. Sync the body and title on every invocation, before the routing below decides on the reply.
34
+ - Run `gh pr view --json url,title,body` and update the body with `gh pr edit --body` when the new commit changes scope, and the title with `gh pr edit --title` when the scope shifted enough to make it inaccurate.
35
+ - A fix commit answering a review changes what shipped exactly as much as an ordinary followup does, so the sync cannot wait on the invocation or the comment count below.
36
+ - A body a person edited by hand between rounds gets no special handling: judge it against the tree the same way regardless of who wrote it last, since a hand-edit the fix commit has made stale is the exact drift this sync exists to close.
37
+ 7. Route on the invocation and the comment count for the reply alone.
38
+ - When invoked with `reply-owned`, skip this step: the caller posts its own reply.
35
39
  - Otherwise, if the count is above zero, the followup addresses review feedback: post a one-line summary of the fix with `gh pr comment --body`, first running the scan in `${CLAUDE_SKILL_DIR}/../../standards/publish.md` against it, since the hook does not see an inline comment body.
36
- - If it is zero, run `gh pr view --json url,title,body` and update the body with `gh pr edit --body` when the new commit changes scope, and the title with `gh pr edit --title` when the scope shifted enough to make it inaccurate.
40
+ - If it is zero, nothing further runs. The sync in step 6 already did this branch's job.
37
41
 
38
42
  ## After completion
39
43
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "3.14.0",
4
+ "version": "3.14.2",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
package/standards/pr.md CHANGED
@@ -51,6 +51,7 @@ Does not govern:
51
51
  - Run the check before writing its line. A `- [ ]` reports a check that has not run rather than one that is planned.
52
52
  - Tick the box and state the observed result. `- [x] npm test passes, 42 tests` beats `- [ ] run npm test`.
53
53
  - Quote the count or output the run reported, never a figure carried from elsewhere.
54
+ - A ticked box describes the tree as it stands, not as it stood when it was written. A later commit that removes or replaces what it names makes the box false, and the branch that made that commit corrects it in the same pass, the same as any other stale claim in the body.
54
55
  - Leave a box unchecked only when a human is required, and name which human and why on the same line.
55
56
  - Human-only covers visual or aesthetic judgment, anything needing credentials or a live third-party service, anything needing a second machine or a fresh OS, and judgment about whether a boundary or an abstraction reads correctly. The agent runs everything else.
56
57
  - What makes a human required is a capability the agent lacks, never the cost of the run. Authorizing a spend is the operator's and performing the run is not, so an arm the repository ships a harness for gets driven once the operator has cleared the spend, and the box records what it returned.