@tech-leads-club/harness-toolkit 0.12.0 → 0.12.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/dist/ship-gate.mjs +2 -2
- package/dist/stop.mjs +2 -2
- package/dist/support.mjs +1 -1
- package/dist/tool-after.mjs +21 -21
- package/dist/tool-before.mjs +27 -27
- package/docs/log.md +1 -0
- package/package.json +1 -1
- package/src/entrypoints/ship-gate.ts +6 -6
- package/src/entrypoints/stop.ts +8 -8
- package/src/entrypoints/support.ts +8 -3
- package/src/entrypoints/tool-before.ts +7 -1
package/docs/log.md
CHANGED
|
@@ -16,6 +16,7 @@ newest first. For what landed in which npm release, see `CHANGELOG.md` at the re
|
|
|
16
16
|
## 2026-09-09
|
|
17
17
|
|
|
18
18
|
- **AD-128** — pr-open and push recognize their gh api equivalent, and pr-merge exists ([/decisions/ad-128.md](/decisions/ad-128.md))
|
|
19
|
+
- **AD-129** — Turn-scope git diffing runs at the event's own working directory, not the project root ([/decisions/ad-129.md](/decisions/ad-129.md))
|
|
19
20
|
|
|
20
21
|
## 2026-09-08
|
|
21
22
|
|
package/package.json
CHANGED
|
@@ -49,13 +49,13 @@ export async function shipGateVerdict(event: HarnessEvent, ctx: HandlerContext):
|
|
|
49
49
|
}
|
|
50
50
|
const { policy } = ctx;
|
|
51
51
|
const root = event.projectDir;
|
|
52
|
+
const shaRoot = shaScopeRoot(event);
|
|
52
53
|
const provider = event.provider;
|
|
53
54
|
const sessionKey = event.sessionKey;
|
|
54
55
|
const session = sessionIdFromKey(event);
|
|
55
|
-
const scope = await computeTurnScope(root, provider, sessionKey, policy);
|
|
56
|
-
const shaRoot = shaScopeRoot(event);
|
|
56
|
+
const scope = await computeTurnScope(root, shaRoot, provider, sessionKey, policy);
|
|
57
57
|
|
|
58
|
-
const commentHits = await pendingCommentViolations(root, provider, sessionKey, policy);
|
|
58
|
+
const commentHits = await pendingCommentViolations(root, shaRoot, provider, sessionKey, policy);
|
|
59
59
|
if (commentHits.length > 0) {
|
|
60
60
|
return {
|
|
61
61
|
kind: "deny",
|
|
@@ -143,13 +143,13 @@ export async function shipGateVerdict(event: HarnessEvent, ctx: HandlerContext):
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
if (policy.duplication.enabled && scope.codeTargets.length > 0) {
|
|
146
|
-
const added = await listAddedLines(
|
|
147
|
-
const tracked = await listTrackedFiles(
|
|
146
|
+
const added = await listAddedLines(shaRoot, scope.codeTargets, scope.turnBase);
|
|
147
|
+
const tracked = await listTrackedFiles(shaRoot);
|
|
148
148
|
const scan = coreFacade.duplication.scanProject(
|
|
149
149
|
tracked,
|
|
150
150
|
(relativePath) => {
|
|
151
151
|
try {
|
|
152
|
-
return readFileSync(`${
|
|
152
|
+
return readFileSync(`${shaRoot}/${relativePath}`, "utf8");
|
|
153
153
|
} catch {
|
|
154
154
|
return null;
|
|
155
155
|
}
|
package/src/entrypoints/stop.ts
CHANGED
|
@@ -490,7 +490,7 @@ export const stopHandler: Handler = async (event: HarnessEvent, ctx: HandlerCont
|
|
|
490
490
|
* says so once at the end instead of three times ([/decisions/ad-073.md](/decisions/ad-073.md)).
|
|
491
491
|
*/
|
|
492
492
|
const deferred: string[] = [];
|
|
493
|
-
const changedFiles = await listChangedRepoFiles(
|
|
493
|
+
const changedFiles = await listChangedRepoFiles(shaRoot, turnBase);
|
|
494
494
|
const codeTargets = filterCodeTargets(changedFiles, policy.codePaths);
|
|
495
495
|
const testTargets = filterTestTargets(changedFiles);
|
|
496
496
|
// why: the comment rail scopes by the syntax catalog (40+ languages), not `codeTargets`'s nine-extension
|
|
@@ -653,7 +653,7 @@ export const stopHandler: Handler = async (event: HarnessEvent, ctx: HandlerCont
|
|
|
653
653
|
coreFacade.observe.shouldObserve(policy.observe, "comments", policy.comments.enabled)
|
|
654
654
|
) {
|
|
655
655
|
const hits = await coreFacade.commentPolicy.scanAddedComments(
|
|
656
|
-
|
|
656
|
+
shaRoot,
|
|
657
657
|
commentTargets,
|
|
658
658
|
policy.comments.mode,
|
|
659
659
|
turnBase,
|
|
@@ -680,7 +680,7 @@ export const stopHandler: Handler = async (event: HarnessEvent, ctx: HandlerCont
|
|
|
680
680
|
|
|
681
681
|
if (policy.comments.enabled && policy.comments.onViolation === "followup" && commentTargets.length > 0) {
|
|
682
682
|
const hits = await coreFacade.commentPolicy.scanAddedComments(
|
|
683
|
-
|
|
683
|
+
shaRoot,
|
|
684
684
|
commentTargets,
|
|
685
685
|
policy.comments.mode,
|
|
686
686
|
turnBase,
|
|
@@ -708,13 +708,13 @@ export const stopHandler: Handler = async (event: HarnessEvent, ctx: HandlerCont
|
|
|
708
708
|
if (policy.supplyChain.enabled && changedFiles.length > 0) {
|
|
709
709
|
const manifests = changedFiles.filter((path) => coreFacade.supplyChain.isManifest(path));
|
|
710
710
|
if (manifests.length > 0) {
|
|
711
|
-
const added = await listAddedLines(
|
|
711
|
+
const added = await listAddedLines(shaRoot, manifests, turnBase);
|
|
712
712
|
const outcome = coreFacade.supplyChain.inspectSupplyChain({
|
|
713
713
|
changedFiles,
|
|
714
714
|
added,
|
|
715
715
|
readManifest: (relativePath) => {
|
|
716
716
|
try {
|
|
717
|
-
return readFileSync(join(
|
|
717
|
+
return readFileSync(join(shaRoot, relativePath), "utf8");
|
|
718
718
|
} catch {
|
|
719
719
|
return null;
|
|
720
720
|
}
|
|
@@ -742,13 +742,13 @@ export const stopHandler: Handler = async (event: HarnessEvent, ctx: HandlerCont
|
|
|
742
742
|
* ([/decisions/ad-071.md](/decisions/ad-071.md)).
|
|
743
743
|
*/
|
|
744
744
|
if (policy.duplication.enabled && codeTargets.length > 0) {
|
|
745
|
-
const added = await listAddedLines(
|
|
746
|
-
const tracked = await listTrackedFiles(
|
|
745
|
+
const added = await listAddedLines(shaRoot, codeTargets, turnBase);
|
|
746
|
+
const tracked = await listTrackedFiles(shaRoot);
|
|
747
747
|
const scan = coreFacade.duplication.scanProject(
|
|
748
748
|
tracked,
|
|
749
749
|
(relativePath) => {
|
|
750
750
|
try {
|
|
751
|
-
return readFileSync(join(
|
|
751
|
+
return readFileSync(join(shaRoot, relativePath), "utf8");
|
|
752
752
|
} catch {
|
|
753
753
|
return null;
|
|
754
754
|
}
|
|
@@ -94,9 +94,13 @@ export type TurnScope = {
|
|
|
94
94
|
*
|
|
95
95
|
* invariant: `turn_base_sha` falls back to `HEAD` when the handoff is absent or its seal diverged —
|
|
96
96
|
* identical to `stop.ts`'s own fallback.
|
|
97
|
+
* why: `root` is state (worktree-stable, AD-114); `gitRoot` is where the turn's files actually are
|
|
98
|
+
* (`shaScopeRoot(event)`) — diffing a worktree-valid sha from the wrong directory read a whole unrelated
|
|
99
|
+
* branch as added ([/decisions/ad-129.md](/decisions/ad-129.md)).
|
|
97
100
|
*/
|
|
98
101
|
export async function computeTurnScope(
|
|
99
102
|
root: string,
|
|
103
|
+
gitRoot: string,
|
|
100
104
|
provider: string,
|
|
101
105
|
sessionKey: string,
|
|
102
106
|
policy: Pick<Policy, "codePaths">,
|
|
@@ -104,7 +108,7 @@ export async function computeTurnScope(
|
|
|
104
108
|
const seal = coreFacade.handoff.handoffInjectable(root, sessionKey);
|
|
105
109
|
const handoff = seal.ok ? coreFacade.handoff.readHandoff(root, provider, sessionKey) : undefined;
|
|
106
110
|
const turnBase = handoff?.turn_base_sha ?? "HEAD";
|
|
107
|
-
const changedFiles = await listChangedRepoFiles(
|
|
111
|
+
const changedFiles = await listChangedRepoFiles(gitRoot, turnBase);
|
|
108
112
|
const codeTargets = filterCodeTargets(changedFiles, policy.codePaths);
|
|
109
113
|
const testTargets = filterTestTargets(changedFiles);
|
|
110
114
|
const commentScope = changedFiles.filter((file) =>
|
|
@@ -128,6 +132,7 @@ export async function computeTurnScope(
|
|
|
128
132
|
*/
|
|
129
133
|
export async function pendingCommentViolations(
|
|
130
134
|
root: string,
|
|
135
|
+
gitRoot: string,
|
|
131
136
|
provider: string,
|
|
132
137
|
sessionKey: string,
|
|
133
138
|
policy: Pick<Policy, "comments" | "codePaths">,
|
|
@@ -135,12 +140,12 @@ export async function pendingCommentViolations(
|
|
|
135
140
|
if (!policy.comments.enabled || policy.comments.onViolation !== "followup") {
|
|
136
141
|
return [];
|
|
137
142
|
}
|
|
138
|
-
const scope = await computeTurnScope(root, provider, sessionKey, policy);
|
|
143
|
+
const scope = await computeTurnScope(root, gitRoot, provider, sessionKey, policy);
|
|
139
144
|
if (scope.commentTargets.length === 0) {
|
|
140
145
|
return [];
|
|
141
146
|
}
|
|
142
147
|
return coreFacade.commentPolicy.scanAddedComments(
|
|
143
|
-
|
|
148
|
+
gitRoot,
|
|
144
149
|
scope.commentTargets,
|
|
145
150
|
policy.comments.mode,
|
|
146
151
|
scope.turnBase,
|
|
@@ -26,7 +26,13 @@ async function commentGateBeforeCommit(event: HarnessEvent, ctx: HandlerContext)
|
|
|
26
26
|
if (!coreFacade.rules.triggerMatches({ kind: "commit" }, context)) {
|
|
27
27
|
return { kind: "abstain" };
|
|
28
28
|
}
|
|
29
|
-
const hits = await pendingCommentViolations(
|
|
29
|
+
const hits = await pendingCommentViolations(
|
|
30
|
+
event.projectDir,
|
|
31
|
+
shaScopeRoot(event),
|
|
32
|
+
event.provider,
|
|
33
|
+
event.sessionKey,
|
|
34
|
+
ctx.policy,
|
|
35
|
+
);
|
|
30
36
|
if (hits.length === 0) {
|
|
31
37
|
return { kind: "abstain" };
|
|
32
38
|
}
|