@agent-delivery-harness/action 0.1.0 → 0.2.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.
Files changed (2) hide show
  1. package/package.json +8 -3
  2. package/src/main.ts +29 -8
package/package.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "name": "@agent-delivery-harness/action",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "GitHub Action verifying a tracked delivery record on pull requests",
6
6
  "license": "Apache-2.0",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/kwam1na/agent-delivery-harness.git",
10
+ "directory": "packages/action"
11
+ },
7
12
  "engines": {
8
- "node": ">=22"
13
+ "node": ">=22.6.0"
9
14
  },
10
15
  "dependencies": {
11
- "@agent-delivery-harness/kernel": "0.1.0"
16
+ "@agent-delivery-harness/kernel": "0.2.0"
12
17
  },
13
18
  "exports": {
14
19
  ".": "./src/index.ts"
package/src/main.ts CHANGED
@@ -64,6 +64,8 @@ import {
64
64
  createBlocker,
65
65
  createInternalErrorBlocker,
66
66
  deliveryRecordPathFor,
67
+ needsCommittedSymlinkTarget,
68
+ parseCandidateTreeListing,
67
69
  parseDeliveryRecord,
68
70
  renderBlockers,
69
71
  runGitCommand,
@@ -73,6 +75,7 @@ import {
73
75
  type Blocker,
74
76
  type BlockerSource,
75
77
  type CandidateCommandRunner,
78
+ type CandidateTreeEntry,
76
79
  type DeliveryRecordCheck,
77
80
  type DeliveryRecordClaim,
78
81
  type DeliveryRecordFile,
@@ -170,9 +173,6 @@ export interface ActionResult {
170
173
 
171
174
  // ── Blockers ─────────────────────────────────────────────────────────────────
172
175
 
173
- /** `git ls-tree -z` separates records with NUL, so a path containing a newline stays one record. */
174
- const NUL = "\u0000";
175
-
176
176
  const ACTION_SOURCE: BlockerSource = { kind: "command", id: "delivery-harness.action" };
177
177
 
178
178
  const RECORD_AND_COMMIT: Remediation = {
@@ -476,6 +476,13 @@ interface DiscoveredRecords {
476
476
  /** Parse failures. Findings, never skips — see below. */
477
477
  readonly blockers: readonly Blocker[];
478
478
  readonly trackedCount: number;
479
+ /**
480
+ * Every tracked entry in the head tree, for the protected-path check —
481
+ * mode and, for a delivery-owned symlink, the target read from its committed
482
+ * blob, because the one admitted exception under `.claude/skills/` turns on
483
+ * both and neither is visible in a name-only listing.
484
+ */
485
+ readonly allPaths: readonly CandidateTreeEntry[];
479
486
  }
480
487
 
481
488
  /**
@@ -503,11 +510,12 @@ async function readTrackedRecords(git: GitPort, headSha: string, config: Harness
503
510
  // from a root-level path of that name — a path/content confusion. The two
504
511
  // sides of this check must be anchored identically or they are not comparing
505
512
  // the same tree.
506
- const listing = await git(["ls-tree", "-r", "--name-only", "-z", "--full-tree", headSha]);
513
+ const listing = await git(["ls-tree", "-r", "-z", "--full-tree", headSha]);
507
514
  if (listing.exitCode !== 0) {
508
515
  return {
509
516
  files: [],
510
517
  trackedCount: 0,
518
+ allPaths: [],
511
519
  blockers: [
512
520
  actionBlocker({
513
521
  code: "tracked_tree_unreadable",
@@ -525,7 +533,20 @@ async function readTrackedRecords(git: GitPort, headSha: string, config: Harness
525
533
  };
526
534
  }
527
535
 
528
- const paths = listing.stdout.split(NUL).filter((entry) => entry.length > 0 && isRecordPath(entry, shape));
536
+ const listed = parseCandidateTreeListing(listing.stdout);
537
+ const allPaths: CandidateTreeEntry[] = [];
538
+ for (const entry of listed) {
539
+ if (!needsCommittedSymlinkTarget(entry)) {
540
+ allPaths.push(entry);
541
+ continue;
542
+ }
543
+ // Read out of the commit, like every other byte this verifier judges: the
544
+ // link under review is the one in the tree. A blob that will not read
545
+ // leaves the target absent, and an entry with no target cannot be admitted.
546
+ const link = await git(["cat-file", "blob", entry.objectSha]);
547
+ allPaths.push(link.exitCode === 0 ? { ...entry, symlinkTarget: link.stdout } : entry);
548
+ }
549
+ const paths = listed.map((entry) => entry.path).filter((entry) => isRecordPath(entry, shape));
529
550
  const files: DeliveryRecordFile[] = [];
530
551
  const blockers: Blocker[] = [];
531
552
  for (const recordPath of paths.sort()) {
@@ -548,7 +569,7 @@ async function readTrackedRecords(git: GitPort, headSha: string, config: Harness
548
569
  }
549
570
  files.push({ path: recordPath, record: parsed.record });
550
571
  }
551
- return { files, blockers, trackedCount: paths.length };
572
+ return { files, blockers, trackedCount: paths.length, allPaths };
552
573
  }
553
574
 
554
575
  // ── The summary ──────────────────────────────────────────────────────────────
@@ -949,7 +970,7 @@ export async function runAction(runtime: ActionRuntime): Promise<ActionResult> {
949
970
  }
950
971
 
951
972
  recordPath = selected.path;
952
- check = verifyDeliveryRecord(config, selected.record, identity, base);
973
+ check = verifyDeliveryRecord(config, selected.record, identity, base, { candidateTreePaths: discovered.allPaths });
953
974
  blockers.push(...check.blockers);
954
975
  return await settle();
955
976
  } catch (error) {
@@ -990,7 +1011,7 @@ async function workspaceHas(runtime: ActionRuntime, repoRoot: string, relativePa
990
1011
  *
991
1012
  * The Action carries its own loader rather than importing the CLI's: the Action
992
1013
  * is a wrapper over the *kernel*, and a dependency on the operator CLI would put
993
- * seven commands and an interactive prompt behind a read-only check. The
1014
+ * eleven commands and an interactive prompt behind a read-only check. The
994
1015
  * validation itself is the kernel's single implementation; only the import and
995
1016
  * the blocker's source id are local, so the two loaders cannot disagree about
996
1017
  * what a valid config is.