@effected/github-actions 0.9.1 → 0.10.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.
@@ -50,7 +50,7 @@ var PackageManagerInstallerError = class extends Schema.TaggedError()("PackageMa
50
50
  switch (this.reason) {
51
51
  case "downloadFailed": return `Could not download ${pin}${this.subject === void 0 ? "" : ` from ${this.subject}`}`;
52
52
  case "extractFailed": return `Could not extract ${pin}${this.subject === void 0 ? "" : ` (${this.subject})`}`;
53
- case "integrityMismatch": return `Integrity mismatch for ${pin}: expected ${this.expected}, got ${this.actual}`;
53
+ case "integrityMismatch": return this.expected === void 0 || this.actual === void 0 ? `Could not verify the integrity of ${pin}${this.subject === void 0 ? "" : ` (${this.subject})`}` : `Integrity mismatch for ${pin}: expected ${this.expected}, got ${this.actual}`;
54
54
  case "integrityMissing": return `The pin for ${pin} carries no integrity hash and requireIntegrity is set`;
55
55
  case "unsupportedPlatform": return `No ${pin} build is published for ${this.subject}`;
56
56
  case "layoutUnexpected": return `The ${pin} artifact does not have the expected layout${this.subject === void 0 ? "" : `: ${this.subject}`}`;
package/README.md CHANGED
@@ -139,6 +139,18 @@ const program = Effect.gen(function* () {
139
139
  await Action.run(program, { layer: ActionCache.layer });
140
140
  ```
141
141
 
142
+ `CacheKey.withNamespace(segment)` is the cache-bust case — a run that must match nothing an unbusted run wrote, and whose own entries must be invisible to unbusted runs:
143
+
144
+ ```ts
145
+ import { CacheKey } from "@effected/github-actions";
146
+
147
+ const key = CacheKey.of("Linux", "pnpm-store", "abc123").withNamespace("bust7");
148
+ console.log(key.key, key.restoreKeys);
149
+ // bust7-Linux-pnpm-store-abc123 []
150
+ ```
151
+
152
+ Two decisions make that safe for any segment value, with no prefix reasoning at the call site. The segment goes **first**, so the namespaced key shares no prefix with an unnamespaced one — a restore key is a prefix match, and folding a bust token in later leaves an ordinary run's rung prefix-matching busted entries. And the ladder is **dropped**, so no rung reaches outside the namespace even when the segment happens to equal an ordinary leading segment. It is a safe default rather than a prohibition: follow with `withRestoreDepths` for a deliberate in-namespace ladder.
153
+
142
154
  `CacheKey.hashFiles` is byte-compatible with `@actions/glob`'s `hashFiles`, and `OidcTokenIssuer.claims` decodes the runner's OIDC token for a SLSA provenance predicate without verifying its signature — the token arrived over TLS from the runner's own token service, so the transport is the trust boundary and nothing here branches on the claims for authorization.
143
155
 
144
156
  `ActionsProvenance.capture` turns those claims into an `@effected/sbom` `SlsaProvenance` predicate, and `ActionsIdentityToken.layer` serves that package's narrow `IdentityToken` contract from the same issuer — the inversion that keeps the Actions runtime out of every consumer that only wants to emit an SBOM:
@@ -228,6 +240,30 @@ Every region the run writes then carries its `at` and `runId`, and a pass whose
228
240
 
229
241
  `CheckState` is the vocabulary both of them speak, and it is deliberately wider than GitHub's conclusion set — `running` is a state rather than the absence of one, and `user_interaction_required` names a pipeline waiting on a human. `projectCheckState` maps each onto the wire form GitHub wants, as a status plus a conclusion exactly when the status is `completed`.
230
242
 
243
+ ## Typed failures
244
+
245
+ Four failure types in this package are **tagged unions of one error class per reason** rather than a single class carrying a `reason` field: `ActionOutputError`, `BlobEnvelopeError`, `CacheKeyError` and `DetachedProcessError`. Every one of those names still exists and still means the same set of outcomes, so no signature changed — each is now a type alias over its members:
246
+
247
+ | Union | Members |
248
+ | ----- | ------- |
249
+ | `ActionOutputError` | `RunnerFileUnavailableError`, `RunnerFileWriteError`, `InvalidOutputNameError`, `OutputEncodeError`, `DetachedOutputError` |
250
+ | `BlobEnvelopeError` | `NotABlobEnvelopeError`, `TruncatedBlobEnvelopeError`, `UnsupportedBlobEnvelopeVersionError`, `BlobMetadataDecodeError`, `BlobMetadataEncodeError` |
251
+ | `CacheKeyError` | `CacheKeyReadError`, `CacheKeyBadPatternError` |
252
+ | `DetachedProcessError` | `DetachedLogUnavailableError`, `DetachedSpawnFailedError`, `InvalidPidError`, `DetachedSignalFailedError`, `DetachedNotReadyError` |
253
+
254
+ **Breaking, and how to migrate.** Code that branched on `error.reason` matches on `_tag` instead, which is what buys the split: one failure can be handled on its own with `Effect.catchTag`, and the rest stay in the error channel rather than being caught by a handler that only meant to answer one of them.
255
+
256
+ ```ts
257
+ import { CacheKey } from "@effected/github-actions";
258
+ import { Effect, Option } from "effect";
259
+
260
+ const digest = CacheKey.hashMatching({ workspace: ".", patterns: ["**/pnpm-lock.yaml"] }).pipe(
261
+ Effect.catchTag("CacheKeyBadPatternError", () => Effect.succeed(Option.none<string>())),
262
+ );
263
+ // Effect<Option<string>, CacheKeyReadError, FileSystem | Path>
264
+ // a bad pattern becomes "no digest"; a read failure stays in the error channel
265
+ ```
266
+
231
267
  ## Testing
232
268
 
233
269
  Every service ships `makeTest(overrides?)` and `layerTest(overrides?)`, with unstubbed members dying loudly and naming themselves — three recorded exceptions state why they default instead: `ActionEnvironment.makeTest` seeds the twelve `GITHUB_*`/`RUNNER_*` variables, `ActionLogger.makeTest` defaults to silent, and `DryRun.makeTest` defaults to rehearsing, the safe direction. `ActionEnvironment.makeTest`/`layerTest` also take the webhook event payload as an optional second argument, serving it directly instead of routing it through a `GITHUB_EVENT_PATH` filesystem read the stubbed filesystem could never satisfy:
@@ -255,7 +291,7 @@ const TestOutputs = ActionOutputs.layerTest({
255
291
  ## Features
256
292
 
257
293
  - `Action.run` / `ActionRuntime.layer` — the entry point and default runtime: environment, logger, outputs, state, the Node platform and an `HttpClient`.
258
- - `ActionInput` — typed input accessors (`string`, `boolean`, `integer`, `redacted`, `lines`, `list`, `pairs`, `schema`) sharing one absence rule, plus the `INPUT_`-aware `ConfigProvider` `Action.run` installs by default.
294
+ - `ActionInput` — typed input accessors (`string`, `boolean`, `integer`, `redacted`, `lines`, `list`, `pairs`, `schema`) sharing one absence rule, plus the `INPUT_`-aware `ConfigProvider` `Action.run` installs by default. `pairs` always rejects an empty key (`=value`, whose damage lands far from the typo) and takes `requireValue` to reject an empty value too.
259
295
  - `ActionOutputs` — step outputs, JSON outputs, the job summary, exported variables, `PATH` additions, failure annotations and log masking.
260
296
  - `ActionState` / `ActionEnvironment` — persisted `pre`/`main`/`post` state, and the fiber-local `GITHUB_*`/`RUNNER_*` context read once from `process.env`.
261
297
  - `ActionLogger` — collapsible groups, buffered step transcripts (`withBuffer({ onSuccess: "discard" })` drops a green step's transcript entirely, while a failure, defect or interruption still flushes it), `withStep(name, effect, options?)` for the same buffering plus one summary line on success and a `❌ <name>` header above the transcript on failure, `::notice::` annotations, and the `Logger` that renders every `Effect.log*` as a workflow command.
@@ -263,15 +299,15 @@ const TestOutputs = ActionOutputs.layerTest({
263
299
  - `Secret` — the one declassification seam between a `Redacted` value and a plain string, masking before it returns plaintext.
264
300
  - `GitHubToken` — the App-token lifecycle shaped like the `pre`/`main`/`post` workflow: mint, verify scopes, persist, read, revoke.
265
301
  - `ActionCache` / `Artifact` — the runner's cache and artifact protocols over Twirp v2, excluded from the default runtime so a consumer that skips them never links Azure.
266
- - `CacheKey` — cache keys and their restore-key ladder, plus `hashFiles`/`matchingFiles`/`hashMatching` byte-compatible with `@actions/glob`.
302
+ - `CacheKey` — cache keys and their restore-key ladder, `withNamespace(segment)` for a bust token or a shared-runner prefix, plus `hashFiles`/`matchingFiles`/`hashMatching` byte-compatible with `@actions/glob`.
267
303
  - `OidcTokenIssuer` — the runner's OIDC token service, with unverified claim decoding for provenance predicates.
268
304
  - `ActionsIdentityToken` / `ActionsProvenance` — the `@effected/sbom` seam: `layer` serves that package's `IdentityToken` contract from the runner's issuer, and `capture` builds a `SlsaProvenance` predicate out of the runner's OIDC claims.
269
305
  - `GitHubMarkdown` — the escaping-safe writer for GitHub surfaces: `table`, `tableFor(schema)`, `heading`, `link`, `code`, `codeBlock`, `list`, `details` and `raw`, and the package's only importer of `@effected/markdown`.
270
306
  - `ManagedDocument` — marker-delimited named regions in text a human also edits, for a sticky comment or a managed pull request description; content outside the regions survives byte-for-byte, and each region can carry marker metadata that round-trips through `entry(key)` without ever addressing the region.
271
307
  - `CheckState` / `projectCheckState` — the kit's check vocabulary, wider than GitHub's conclusions, and its projection onto the check-run wire form.
272
308
  - `CheckDocument` — the debounced check-to-document reconciler: report states as they change, and one write lands carrying the final one. `stamp` plus a readable sink lets two runs share one document, dropping a strictly older run's pass instead of clobbering the newer one; `flush` reports `written`, `unchanged` or `stale`.
273
- - `ToolInstaller` — download, extract and cache a toolchain in the runner's tool cache, with no `@actions/tool-cache` dependency.
274
- - `BlobStore` — durable `get`/`put`/`has` over bytes plus a metadata channel, backed by `layerS3` (SigV4-signed, no `@aws-sdk/*` dependency) or `GitHubCacheBlobStore.layer` (the runner's own cache); `layerMemory` runs the real envelope framing for tests.
309
+ - `ToolInstaller` — download, extract and cache a toolchain in the runner's tool cache, with no `@actions/tool-cache` dependency; `ToolInstallerError` always names its `subject`.
310
+ - `BlobStore` — durable `get`/`put`/`has` over `StoredBlob<A>` values (bytes plus caller-schema metadata; the type was named `Blob<A>` before, which collided with the global), backed by `layerS3` (SigV4-signed, no `@aws-sdk/*` dependency) or `GitHubCacheBlobStore.layer` (the runner's own cache); `layerMemory` runs the real envelope framing for tests.
275
311
 
276
312
  ## License
277
313
 
package/ToolInstaller.js CHANGED
@@ -23,7 +23,7 @@ var ToolInstallerError = class extends Schema.TaggedError()("ToolInstallerError"
23
23
  /** The HTTP status, when there was one. Drives the retry decision. */
24
24
  status: Schema.optionalKey(Schema.Number),
25
25
  /** What was being worked on — a url, an archive path, or a tool name. */
26
- subject: Schema.optionalKey(Schema.String),
26
+ subject: Schema.String,
27
27
  /** The extraction tool's own complaint, which is the only useful part of a tar failure. */
28
28
  stderr: Schema.optionalKey(Schema.String),
29
29
  /** The underlying failure, preserved structurally. */
@@ -76,6 +76,7 @@ const make = Effect.gen(function* () {
76
76
  const requested = options?.destination;
77
77
  if (requested === void 0) return fs.makeTempDirectory({ prefix: "effected-extract-" }).pipe(Effect.mapError((cause) => new ToolInstallerError({
78
78
  reason: "extractFailed",
79
+ subject: "a temporary extraction directory",
79
80
  cause
80
81
  })));
81
82
  return fs.makeDirectory(requested, { recursive: true }).pipe(Effect.as(requested), Effect.mapError((cause) => new ToolInstallerError({
package/index.d.ts CHANGED
@@ -499,32 +499,93 @@ declare class ActionLogger extends ActionLogger_base {
499
499
  }
500
500
  //#endregion
501
501
  //#region src/ActionOutputs.d.ts
502
- declare const ActionOutputError_base: Schema.Class<ActionOutputError, Schema.TaggedStruct<"ActionOutputError", {
503
- /**
504
- * `unavailable` — the runner file variable is not set, which usually means
505
- * the code is running outside a GitHub runner. `writeFailed` — the file
506
- * exists but could not be appended to. `invalidName` — the name would
507
- * corrupt the file's block structure. `encodeFailed` — a value did not
508
- * satisfy its schema. `detached` — the member was called under
509
- * {@link ActionOutputs.layerDetached}, where the channel it writes cannot
510
- * reach the job that would read it.
511
- */
512
- readonly reason: Schema.Literals<readonly ["unavailable", "writeFailed", "invalidName", "encodeFailed", "detached"]>;
513
- /** Which runner file was involved, by environment variable name. */
502
+ declare const RunnerFileUnavailableError_base: Schema.Class<RunnerFileUnavailableError, Schema.TaggedStruct<"RunnerFileUnavailableError", {
503
+ /** The runner file involved, by environment variable name. */
504
+ readonly file: Schema.String;
505
+ /** The underlying failure, preserved structurally. */
506
+ readonly cause: Schema.optionalKey<Schema.Defect>;
507
+ }>, import("effect/Cause").YieldableError>;
508
+ /**
509
+ * Raised when an action cannot publish an output.
510
+ *
511
+ * @public
512
+ */
513
+ declare class RunnerFileUnavailableError extends RunnerFileUnavailableError_base {
514
+ get message(): string;
515
+ }
516
+ declare const RunnerFileWriteError_base: Schema.Class<RunnerFileWriteError, Schema.TaggedStruct<"RunnerFileWriteError", {
517
+ /** The runner file involved, by environment variable name. */
518
+ readonly file: Schema.String;
519
+ /** The underlying failure, preserved structurally. */
520
+ readonly cause: Schema.optionalKey<Schema.Defect>;
521
+ }>, import("effect/Cause").YieldableError>;
522
+ /**
523
+ * Raised when a runner file exists but could not be appended to.
524
+ *
525
+ * @public
526
+ */
527
+ declare class RunnerFileWriteError extends RunnerFileWriteError_base {
528
+ get message(): string;
529
+ }
530
+ declare const InvalidOutputNameError_base: Schema.Class<InvalidOutputNameError, Schema.TaggedStruct<"InvalidOutputNameError", {
531
+ /** The offending name. */
532
+ readonly name: Schema.String;
533
+ /** The runner file involved, by environment variable name. */
514
534
  readonly file: Schema.optionalKey<Schema.String>;
535
+ /** The underlying failure, preserved structurally. */
536
+ readonly cause: Schema.optionalKey<Schema.Defect>;
537
+ }>, import("effect/Cause").YieldableError>;
538
+ /**
539
+ * Raised when a name would corrupt the runner file's block structure.
540
+ *
541
+ * @public
542
+ */
543
+ declare class InvalidOutputNameError extends InvalidOutputNameError_base {
544
+ get message(): string;
545
+ }
546
+ declare const OutputEncodeError_base: Schema.Class<OutputEncodeError, Schema.TaggedStruct<"OutputEncodeError", {
547
+ /** The output or variable name whose value would not encode. */
548
+ readonly name: Schema.String;
549
+ /** The underlying failure, preserved structurally. */
550
+ readonly cause: Schema.optionalKey<Schema.Defect>;
551
+ }>, import("effect/Cause").YieldableError>;
552
+ /**
553
+ * Raised when a value did not satisfy its schema.
554
+ *
555
+ * @public
556
+ */
557
+ declare class OutputEncodeError extends OutputEncodeError_base {
558
+ get message(): string;
559
+ }
560
+ declare const DetachedOutputError_base: Schema.Class<DetachedOutputError, Schema.TaggedStruct<"DetachedOutputError", {
561
+ /** The runner file involved, by environment variable name. */
562
+ readonly file: Schema.String;
515
563
  /** The output or variable name, when one is involved. */
516
564
  readonly name: Schema.optionalKey<Schema.String>;
517
565
  /** The underlying failure, preserved structurally. */
518
566
  readonly cause: Schema.optionalKey<Schema.Defect>;
519
567
  }>, import("effect/Cause").YieldableError>;
520
568
  /**
521
- * Raised when an action cannot publish an output.
569
+ * Raised when an output member was called under
570
+ * {@link ActionOutputs.layerDetached}.
522
571
  *
523
572
  * @public
524
573
  */
525
- declare class ActionOutputError extends ActionOutputError_base {
574
+ declare class DetachedOutputError extends DetachedOutputError_base {
526
575
  get message(): string;
527
576
  }
577
+ /**
578
+ * Anything that can go wrong publishing an output, variable or summary.
579
+ *
580
+ * @remarks
581
+ * **One class per failure, rather than one class with a `reason` field.** The
582
+ * runner file is required on the members that name one and the output name on
583
+ * the members that name one, so a value short a field is a compile error rather
584
+ * than a message reading `"undefined"`.
585
+ *
586
+ * @public
587
+ */
588
+ type ActionOutputError = RunnerFileUnavailableError | RunnerFileWriteError | InvalidOutputNameError | OutputEncodeError | DetachedOutputError;
528
589
  /**
529
590
  * The {@link ActionOutputs} service shape.
530
591
  *
@@ -886,29 +947,50 @@ interface DataBlobTransfer {
886
947
  }
887
948
  //#endregion
888
949
  //#region src/CacheKey.d.ts
889
- declare const CacheKeyError_base: Schema.Class<CacheKeyError, Schema.TaggedStruct<"CacheKeyError", {
890
- /**
891
- * `readFailed` — a file or directory that was going to be hashed could not be
892
- * read. `badPattern` — a glob pattern would not compile. Key *derivation*
893
- * itself is pure and total once the bytes are in hand, so these are the only
894
- * two things that can go wrong.
895
- */
896
- readonly reason: Schema.Literals<readonly ["readFailed", "badPattern"]>;
950
+ declare const CacheKeyReadError_base: Schema.Class<CacheKeyReadError, Schema.TaggedStruct<"CacheKeyReadError", {
897
951
  /** The path being read when it went wrong. */
898
- readonly path: Schema.optionalKey<Schema.String>;
952
+ readonly path: Schema.String;
953
+ /** The underlying failure, preserved structurally. */
954
+ readonly cause: Schema.optionalKey<Schema.Defect>;
955
+ }>, import("effect/Cause").YieldableError>;
956
+ /**
957
+ * Raised when a file or directory that was going to be hashed could not be
958
+ * read.
959
+ *
960
+ * @public
961
+ */
962
+ declare class CacheKeyReadError extends CacheKeyReadError_base {
963
+ get message(): string;
964
+ }
965
+ declare const CacheKeyBadPatternError_base: Schema.Class<CacheKeyBadPatternError, Schema.TaggedStruct<"CacheKeyBadPatternError", {
899
966
  /** The pattern that would not compile. */
900
- readonly pattern: Schema.optionalKey<Schema.String>;
967
+ readonly pattern: Schema.String;
901
968
  /** The underlying failure, preserved structurally. */
902
969
  readonly cause: Schema.optionalKey<Schema.Defect>;
903
970
  }>, import("effect/Cause").YieldableError>;
904
971
  /**
905
- * Raised when a cache key cannot be derived from the filesystem.
972
+ * Raised when a glob pattern would not compile.
906
973
  *
907
974
  * @public
908
975
  */
909
- declare class CacheKeyError extends CacheKeyError_base {
976
+ declare class CacheKeyBadPatternError extends CacheKeyBadPatternError_base {
910
977
  get message(): string;
911
978
  }
979
+ /**
980
+ * Anything that can go wrong deriving a cache key from the filesystem.
981
+ *
982
+ * @remarks
983
+ * Key *derivation* is pure and total once the bytes are in hand, so the two
984
+ * members here are the only things that can fail.
985
+ *
986
+ * **One class per failure, rather than one class with a `reason` field.** Each
987
+ * member carries exactly the fields its own message needs, so a value short a
988
+ * field is a compile error instead of a message reading `"undefined"` — and
989
+ * `Effect.catchTag` can recover from one of them without catching the other.
990
+ *
991
+ * @public
992
+ */
993
+ type CacheKeyError = CacheKeyReadError | CacheKeyBadPatternError;
912
994
  declare const CacheKey_base: Schema.Class<CacheKey, Schema.Struct<{
913
995
  /** The components, most general first. */
914
996
  readonly segments: Schema.NonEmptyArray<Schema.String>;
@@ -1019,6 +1101,43 @@ declare class CacheKey extends CacheKey_base {
1019
1101
  * ```
1020
1102
  */
1021
1103
  withoutRestoreKeys(): CacheKey;
1104
+ /**
1105
+ * A copy of this key namespaced by `segment` — the whole key, ladder
1106
+ * included.
1107
+ *
1108
+ * @remarks
1109
+ * The cache-bust case: a run that must match nothing an unbusted run wrote,
1110
+ * and whose own entries must be invisible to unbusted runs. Both halves of
1111
+ * that are one intent, and spelling them separately is what makes the wrong
1112
+ * version undetectable — a restore key is a **prefix match**, so folding the
1113
+ * bust in *after* the retained prefix leaves an ordinary run's rung
1114
+ * prefix-matching busted entries. The cache still appears to work, and
1115
+ * quietly serves poisoned entries into unrelated runs.
1116
+ *
1117
+ * Two decisions make this safe for **any** segment value, with no prefix
1118
+ * reasoning at the call site. The segment goes **first**, so the namespaced
1119
+ * key shares no prefix with an unnamespaced one. And the ladder is
1120
+ * **dropped**, so no rung of this key can reach outside the namespace even
1121
+ * when the segment happens to equal an ordinary leading segment — which a
1122
+ * prepend alone would not survive.
1123
+ *
1124
+ * Dropping the ladder is the safe default, not a prohibition: the segments
1125
+ * are all still there, so a caller who wants one busted run to warm from
1126
+ * another can follow with {@link CacheKey.withRestoreDepths} and get an
1127
+ * in-namespace ladder deliberately.
1128
+ *
1129
+ * @example
1130
+ * ```ts
1131
+ * import { CacheKey } from "@effected/github-actions";
1132
+ *
1133
+ * const key = CacheKey.of("Linux", "pnpm-store", "abc123").withNamespace("bust7");
1134
+ * key.key; // "bust7-Linux-pnpm-store-abc123"
1135
+ * key.restoreKeys; // []
1136
+ * ```
1137
+ *
1138
+ * @param segment - The namespace, e.g. a cache-bust token.
1139
+ */
1140
+ withNamespace(segment: string): CacheKey;
1022
1141
  /** Build a key from its segments. */
1023
1142
  static of(...segments: readonly [string, ...ReadonlyArray<string>]): CacheKey;
1024
1143
  /**
@@ -1103,7 +1222,7 @@ declare class CacheKey extends CacheKey_base {
1103
1222
  * digest — it is the signal that the pattern is wrong, and a caller that
1104
1223
  * folds it into a key silently caches against a constant.
1105
1224
  */
1106
- static readonly hashFiles: (files: readonly string[]) => Effect.Effect<Option.Option<string>, CacheKeyError, FileSystem.FileSystem>;
1225
+ static readonly hashFiles: (files: readonly string[]) => Effect.Effect<Option.Option<string>, CacheKeyReadError, FileSystem.FileSystem>;
1107
1226
  /**
1108
1227
  * Every file under `workspace` that the pattern set matches.
1109
1228
  *
@@ -1132,7 +1251,7 @@ declare class CacheKey extends CacheKey_base {
1132
1251
  readonly workspace: string;
1133
1252
  /** Include patterns, with a leading `!` marking an exclusion. */
1134
1253
  readonly patterns: ReadonlyArray<string>;
1135
- }) => Effect.Effect<readonly string[], CacheKeyError, FileSystem.FileSystem | Path.Path>;
1254
+ }) => Effect.Effect<readonly string[], CacheKeyBadPatternError | CacheKeyReadError, FileSystem.FileSystem | Path.Path>;
1136
1255
  /**
1137
1256
  * Hash every file under `workspace` that the pattern set matches.
1138
1257
  *
@@ -1163,7 +1282,7 @@ declare class CacheKey extends CacheKey_base {
1163
1282
  static readonly hashMatching: (options: {
1164
1283
  readonly workspace: string;
1165
1284
  readonly patterns: ReadonlyArray<string>;
1166
- }) => Effect.Effect<Option.Option<string>, CacheKeyError, FileSystem.FileSystem | Path.Path>;
1285
+ }) => Effect.Effect<Option.Option<string>, CacheKeyBadPatternError | CacheKeyReadError, FileSystem.FileSystem | Path.Path>;
1167
1286
  }
1168
1287
  //#endregion
1169
1288
  //#region src/ActionCache.d.ts
@@ -1306,6 +1425,19 @@ declare class ActionCache extends ActionCache_base {
1306
1425
  }
1307
1426
  //#endregion
1308
1427
  //#region src/ActionInput.d.ts
1428
+ /**
1429
+ * Options for {@link ActionInput.pairs}.
1430
+ *
1431
+ * @public
1432
+ */
1433
+ interface PairsOptions {
1434
+ /**
1435
+ * Reject a pair whose value is empty (`key=`).
1436
+ *
1437
+ * @defaultValue `false` — an empty value is a legitimate empty string.
1438
+ */
1439
+ readonly requireValue?: boolean;
1440
+ }
1309
1441
  /**
1310
1442
  * Action inputs, read as `Config` values.
1311
1443
  *
@@ -1495,10 +1627,21 @@ declare class ActionInput {
1495
1627
  * @remarks
1496
1628
  * Only the **first** `=` splits, so a value may contain one.
1497
1629
  *
1630
+ * An **empty key** (`=value`, or a bare `=`) is always rejected: `{ "": v }`
1631
+ * cannot be what a workflow meant, and the damage lands far from the typo —
1632
+ * an empty key became a repository filter that matched nothing, and the run
1633
+ * reported zero results with no indication why. An empty **value** (`key=`)
1634
+ * is accepted by default, because setting a property to the empty string is
1635
+ * legitimate; pass `requireValue` to reject it. Every rejection names the
1636
+ * offending line.
1637
+ *
1498
1638
  * Absent or `""` fails as missing data (see {@link ActionInput.string}) —
1499
1639
  * `Config.withDefault({})` is the idiom for an optional pairs input.
1640
+ *
1641
+ * @param name - The input name, unmangled.
1642
+ * @param options - `requireValue` rejects a pair whose value is empty.
1500
1643
  */
1501
- static pairs(name: string): Config.Config<Record<string, string>>;
1644
+ static pairs(name: string, options?: PairsOptions): Config.Config<Record<string, string>>;
1502
1645
  /**
1503
1646
  * A JSON-valued input, decoded through a schema.
1504
1647
  *
@@ -2127,19 +2270,8 @@ declare class Artifact extends Artifact_base {
2127
2270
  }
2128
2271
  //#endregion
2129
2272
  //#region src/BlobEnvelope.d.ts
2130
- declare const BlobEnvelopeError_base: Schema.Class<BlobEnvelopeError, Schema.TaggedStruct<"BlobEnvelopeError", {
2131
- /**
2132
- * `notAnEnvelope` — the magic prefix is absent, so these are raw bytes
2133
- * written before this format existed (or by something else entirely).
2134
- * `unsupportedVersion` — an envelope from a newer revision.
2135
- * `truncated` — the frame ends mid-header or mid-metadata.
2136
- * `metadataDecodeFailed` — the metadata is well-framed but does not satisfy
2137
- * the caller's schema. `metadataEncodeFailed` — the value being stored does
2138
- * not satisfy it.
2139
- */
2140
- readonly reason: Schema.Literals<readonly ["notAnEnvelope", "unsupportedVersion", "truncated", "metadataDecodeFailed", "metadataEncodeFailed"]>;
2141
- /** The envelope version found, when one was readable. */
2142
- readonly version: Schema.optionalKey<Schema.Number>;
2273
+ declare const NotABlobEnvelopeError_base: Schema.Class<NotABlobEnvelopeError, Schema.TaggedStruct<"NotABlobEnvelopeError", {
2274
+ /** The underlying failure, preserved structurally. */
2143
2275
  readonly cause: Schema.optionalKey<Schema.Defect>;
2144
2276
  }>, import("effect/Cause").YieldableError>;
2145
2277
  /**
@@ -2147,9 +2279,71 @@ declare const BlobEnvelopeError_base: Schema.Class<BlobEnvelopeError, Schema.Tag
2147
2279
  *
2148
2280
  * @public
2149
2281
  */
2150
- declare class BlobEnvelopeError extends BlobEnvelopeError_base {
2282
+ declare class NotABlobEnvelopeError extends NotABlobEnvelopeError_base {
2283
+ get message(): string;
2284
+ }
2285
+ declare const TruncatedBlobEnvelopeError_base: Schema.Class<TruncatedBlobEnvelopeError, Schema.TaggedStruct<"TruncatedBlobEnvelopeError", {
2286
+ /** The underlying failure, preserved structurally. */
2287
+ readonly cause: Schema.optionalKey<Schema.Defect>;
2288
+ }>, import("effect/Cause").YieldableError>;
2289
+ /**
2290
+ * Raised when the frame ends mid-header or mid-metadata.
2291
+ *
2292
+ * @public
2293
+ */
2294
+ declare class TruncatedBlobEnvelopeError extends TruncatedBlobEnvelopeError_base {
2295
+ get message(): string;
2296
+ }
2297
+ declare const UnsupportedBlobEnvelopeVersionError_base: Schema.Class<UnsupportedBlobEnvelopeVersionError, Schema.TaggedStruct<"UnsupportedBlobEnvelopeVersionError", {
2298
+ /** The envelope version found. */
2299
+ readonly version: Schema.Number;
2300
+ /** The underlying failure, preserved structurally. */
2301
+ readonly cause: Schema.optionalKey<Schema.Defect>;
2302
+ }>, import("effect/Cause").YieldableError>;
2303
+ /**
2304
+ * Raised when the envelope came from a newer revision of the format.
2305
+ *
2306
+ * @public
2307
+ */
2308
+ declare class UnsupportedBlobEnvelopeVersionError extends UnsupportedBlobEnvelopeVersionError_base {
2309
+ get message(): string;
2310
+ }
2311
+ declare const BlobMetadataDecodeError_base: Schema.Class<BlobMetadataDecodeError, Schema.TaggedStruct<"BlobMetadataDecodeError", {
2312
+ /** The underlying failure, preserved structurally. */
2313
+ readonly cause: Schema.optionalKey<Schema.Defect>;
2314
+ }>, import("effect/Cause").YieldableError>;
2315
+ /**
2316
+ * Raised when well-framed metadata does not satisfy the caller's schema.
2317
+ *
2318
+ * @public
2319
+ */
2320
+ declare class BlobMetadataDecodeError extends BlobMetadataDecodeError_base {
2321
+ get message(): string;
2322
+ }
2323
+ declare const BlobMetadataEncodeError_base: Schema.Class<BlobMetadataEncodeError, Schema.TaggedStruct<"BlobMetadataEncodeError", {
2324
+ /** The underlying failure, preserved structurally. */
2325
+ readonly cause: Schema.optionalKey<Schema.Defect>;
2326
+ }>, import("effect/Cause").YieldableError>;
2327
+ /**
2328
+ * Raised when the value being stored does not satisfy its schema.
2329
+ *
2330
+ * @public
2331
+ */
2332
+ declare class BlobMetadataEncodeError extends BlobMetadataEncodeError_base {
2151
2333
  get message(): string;
2152
2334
  }
2335
+ /**
2336
+ * Anything that can go wrong reading or writing a blob envelope.
2337
+ *
2338
+ * @remarks
2339
+ * **One class per failure, rather than one class with a `reason` field.** Each
2340
+ * member carries exactly the fields its own message needs — the version is
2341
+ * required on the one member that reports it — so a value short a field is a
2342
+ * compile error rather than a message reading `"undefined"`.
2343
+ *
2344
+ * @public
2345
+ */
2346
+ type BlobEnvelopeError = NotABlobEnvelopeError | TruncatedBlobEnvelopeError | UnsupportedBlobEnvelopeVersionError | BlobMetadataDecodeError | BlobMetadataEncodeError;
2153
2347
  /**
2154
2348
  * The schema-versioned frame that gives a stored blob a **metadata channel**.
2155
2349
  *
@@ -2223,7 +2417,7 @@ declare class BlobStoreError extends BlobStoreError_base {
2223
2417
  *
2224
2418
  * @public
2225
2419
  */
2226
- interface Blob<A> {
2420
+ interface StoredBlob<A> {
2227
2421
  /** The caller's own metadata, decoded through the caller's own schema. */
2228
2422
  readonly metadata: A;
2229
2423
  /** The payload, verbatim. */
@@ -2236,9 +2430,9 @@ interface Blob<A> {
2236
2430
  */
2237
2431
  interface BlobStoreShape {
2238
2432
  /** Read a blob, or nothing if the key is absent. */
2239
- readonly get: <A, I>(key: string, schema: Schema.Codec<A, I>) => Effect.Effect<Option.Option<Blob<A>>, BlobStoreError | BlobEnvelopeError>;
2433
+ readonly get: <A, I>(key: string, schema: Schema.Codec<A, I>) => Effect.Effect<Option.Option<StoredBlob<A>>, BlobStoreError | BlobEnvelopeError>;
2240
2434
  /** Write a blob. */
2241
- readonly put: <A, I>(key: string, blob: Blob<A>, schema: Schema.Codec<A, I>) => Effect.Effect<void, BlobStoreError | BlobEnvelopeError>;
2435
+ readonly put: <A, I>(key: string, blob: StoredBlob<A>, schema: Schema.Codec<A, I>) => Effect.Effect<void, BlobStoreError | BlobEnvelopeError>;
2242
2436
  /** Whether a key is present, without transferring the body. */
2243
2437
  readonly has: (key: string) => Effect.Effect<boolean, BlobStoreError>;
2244
2438
  }
@@ -2905,20 +3099,9 @@ declare class ChildEnv {
2905
3099
  }
2906
3100
  //#endregion
2907
3101
  //#region src/DetachedProcess.d.ts
2908
- declare const DetachedProcessError_base: Schema.Class<DetachedProcessError, Schema.TaggedStruct<"DetachedProcessError", {
2909
- /**
2910
- * `logUnavailable` — the log file could not be opened, so there is nowhere
2911
- * for the child's output to go. `spawnFailed` — the child did not start.
2912
- * `invalidPid` — a non-positive pid was handed to {@link DetachedProcess.reap};
2913
- * see there for why this is the most important value in the module.
2914
- * `signalFailed` — the signal was refused, e.g. the process belongs to
2915
- * another user. `notReady` — the readiness probe never held.
2916
- */
2917
- readonly reason: Schema.Literals<readonly ["logUnavailable", "spawnFailed", "invalidPid", "signalFailed", "notReady"]>;
2918
- /** The pid involved, when one is. */
2919
- readonly pid: Schema.optionalKey<Schema.Number>;
2920
- /** The path involved, when one is. */
2921
- readonly path: Schema.optionalKey<Schema.String>;
3102
+ declare const DetachedLogUnavailableError_base: Schema.Class<DetachedLogUnavailableError, Schema.TaggedStruct<"DetachedLogUnavailableError", {
3103
+ /** The log file that could not be opened. */
3104
+ readonly path: Schema.String;
2922
3105
  /** The underlying failure, preserved structurally. */
2923
3106
  readonly cause: Schema.optionalKey<Schema.Defect>;
2924
3107
  }>, import("effect/Cause").YieldableError>;
@@ -2927,9 +3110,77 @@ declare const DetachedProcessError_base: Schema.Class<DetachedProcessError, Sche
2927
3110
  *
2928
3111
  * @public
2929
3112
  */
2930
- declare class DetachedProcessError extends DetachedProcessError_base {
3113
+ declare class DetachedLogUnavailableError extends DetachedLogUnavailableError_base {
2931
3114
  get message(): string;
2932
3115
  }
3116
+ declare const DetachedSpawnFailedError_base: Schema.Class<DetachedSpawnFailedError, Schema.TaggedStruct<"DetachedSpawnFailedError", {
3117
+ /** The underlying failure, preserved structurally. */
3118
+ readonly cause: Schema.optionalKey<Schema.Defect>;
3119
+ }>, import("effect/Cause").YieldableError>;
3120
+ /**
3121
+ * Raised when the detached child did not start.
3122
+ *
3123
+ * @public
3124
+ */
3125
+ declare class DetachedSpawnFailedError extends DetachedSpawnFailedError_base {
3126
+ get message(): string;
3127
+ }
3128
+ declare const InvalidPidError_base: Schema.Class<InvalidPidError, Schema.TaggedStruct<"InvalidPidError", {
3129
+ /** The pid that was refused. */
3130
+ readonly pid: Schema.Number;
3131
+ /** The underlying failure, preserved structurally. */
3132
+ readonly cause: Schema.optionalKey<Schema.Defect>;
3133
+ }>, import("effect/Cause").YieldableError>;
3134
+ /**
3135
+ * Raised when a non-positive pid was handed to {@link DetachedProcess.reap}.
3136
+ *
3137
+ * @remarks
3138
+ * See {@link DetachedProcess.reap} for why this is the most important value in
3139
+ * the module: a non-positive pid targets a process *group*, not a process.
3140
+ *
3141
+ * @public
3142
+ */
3143
+ declare class InvalidPidError extends InvalidPidError_base {
3144
+ get message(): string;
3145
+ }
3146
+ declare const DetachedSignalFailedError_base: Schema.Class<DetachedSignalFailedError, Schema.TaggedStruct<"DetachedSignalFailedError", {
3147
+ /** The pid that could not be signalled. */
3148
+ readonly pid: Schema.Number;
3149
+ /** The underlying failure, preserved structurally. */
3150
+ readonly cause: Schema.optionalKey<Schema.Defect>;
3151
+ }>, import("effect/Cause").YieldableError>;
3152
+ /**
3153
+ * Raised when a signal was refused, e.g. the process belongs to another user.
3154
+ *
3155
+ * @public
3156
+ */
3157
+ declare class DetachedSignalFailedError extends DetachedSignalFailedError_base {
3158
+ get message(): string;
3159
+ }
3160
+ declare const DetachedNotReadyError_base: Schema.Class<DetachedNotReadyError, Schema.TaggedStruct<"DetachedNotReadyError", {
3161
+ /** The underlying failure, preserved structurally. */
3162
+ readonly cause: Schema.optionalKey<Schema.Defect>;
3163
+ }>, import("effect/Cause").YieldableError>;
3164
+ /**
3165
+ * Raised when the readiness probe never held.
3166
+ *
3167
+ * @public
3168
+ */
3169
+ declare class DetachedNotReadyError extends DetachedNotReadyError_base {
3170
+ get message(): string;
3171
+ }
3172
+ /**
3173
+ * Anything that can go wrong spawning, awaiting or reaping a detached child.
3174
+ *
3175
+ * @remarks
3176
+ * **One class per failure, rather than one class with a `reason` field.** The
3177
+ * pid is required on the two members that report one and the path on the one
3178
+ * that does, so a value short a field is a compile error rather than a message
3179
+ * reading `"undefined"`.
3180
+ *
3181
+ * @public
3182
+ */
3183
+ type DetachedProcessError = DetachedLogUnavailableError | DetachedSpawnFailedError | InvalidPidError | DetachedSignalFailedError | DetachedNotReadyError;
2933
3184
  /**
2934
3185
  * The schema a pid crosses the phase boundary through.
2935
3186
  *
@@ -3083,7 +3334,7 @@ declare class DetachedProcess {
3083
3334
  * holds a duplicate, so the log keeps filling after the action's process
3084
3335
  * exits, which is the entire point.
3085
3336
  */
3086
- static readonly spawn: (options: DetachedSpawnOptions) => Effect.Effect<ChildProcessSpawner.ProcessId, DetachedProcessError, never>;
3337
+ static readonly spawn: (options: DetachedSpawnOptions) => Effect.Effect<ChildProcessSpawner.ProcessId, DetachedLogUnavailableError | DetachedSpawnFailedError, never>;
3087
3338
  /**
3088
3339
  * Poll a predicate until it holds.
3089
3340
  *
@@ -3147,7 +3398,7 @@ declare class DetachedProcess {
3147
3398
  * Returns `false` for a process that is already gone, because a `post` phase
3148
3399
  * finding its child already dead is the normal ending, not a failure.
3149
3400
  */
3150
- static readonly reap: (pid: number, signal?: NodeJS.Signals | undefined) => Effect.Effect<boolean, DetachedProcessError, never>;
3401
+ static readonly reap: (pid: number, signal?: NodeJS.Signals | undefined) => Effect.Effect<boolean, DetachedSignalFailedError | InvalidPidError, never>;
3151
3402
  /**
3152
3403
  * The real operations: the production default for a
3153
3404
  * {@link DetachedProcessOps} parameter.
@@ -3924,7 +4175,7 @@ declare const ToolInstallerError_base: Schema.Class<ToolInstallerError, Schema.T
3924
4175
  /** The HTTP status, when there was one. Drives the retry decision. */
3925
4176
  readonly status: Schema.optionalKey<Schema.Number>;
3926
4177
  /** What was being worked on — a url, an archive path, or a tool name. */
3927
- readonly subject: Schema.optionalKey<Schema.String>;
4178
+ readonly subject: Schema.String;
3928
4179
  /** The extraction tool's own complaint, which is the only useful part of a tar failure. */
3929
4180
  readonly stderr: Schema.optionalKey<Schema.String>;
3930
4181
  /** The underlying failure, preserved structurally. */
@@ -4540,5 +4791,5 @@ declare class Secret {
4540
4791
  static readonly adopt: (name: string) => Config.Config<Redacted.Redacted<string>>;
4541
4792
  }
4542
4793
  //#endregion
4543
- export { Action, ActionCache, ActionCacheError, type ActionCacheShape, ActionEnvironment, ActionEnvironmentError, type ActionEnvironmentShape, ActionInput, ActionLogger, type ActionLoggerShape, ActionOutputError, ActionOutputs, type ActionOutputsShape, type ActionRunOptions, ActionRuntime, type ActionServices, ActionState, ActionStateError, type ActionStateShape, ActionsIdentityToken, ActionsProvenance, AmbientPackageManager, type AnnotationProperties, Artifact, ArtifactError, type ArtifactItem, type ArtifactRef, type ArtifactShape, type Blob, BlobEnvelope, BlobEnvelopeError, BlobStore, BlobStoreError, type BlobStoreShape, BlobTransferError, CacheKey, CacheKeyError, CachedPackageManager, CheckDocument, CheckDocumentError, type CheckDocumentOptions, type CheckDocumentShape, type CheckDocumentSink, CheckDocumentStamp, type CheckFlushOutcome, CheckReport, type CheckRunConclusion, type CheckRunProjection, CheckState, ChildEnv, type ClientLayerOptions, type DataBlobTransfer, DetachedProcess, DetachedProcessError, type DetachedProcessOps, type DetachedSpawnOptions, type DownloadOptions, type DownloadResult, DryRun, type DryRunShape, type ExtractOptions, type FileBlobTransfer, GitHubCacheBlobStore, GitHubContext, type GitHubHeadingDepth, type GitHubListOptions, GitHubMarkdown, type GitHubRowSchema, type GitHubSchemaTable, type GitHubSchemaTableColumn, type GitHubSchemaTableColumns, type GitHubSchemaTableFormatRequiredKeys, type GitHubSchemaTableFormattedColumn, type GitHubSchemaTableOptions, GitHubToken, GitHubTokenError, InstalledPackageManager, ManagedDocument, ManagedDocumentError, type ManagedDocumentSource, OidcClaims, OidcTokenError, OidcTokenIssuer, type OidcTokenIssuerShape, type PackageManagerInstallOptions, PackageManagerInstaller, PackageManagerInstallerError, type PackageManagerInstallerShape, type PathPrependEnv, type PathPrependOptions, ProcessId, type ProvisionFileOptions, type ProvisionOptions, type ProvisionedFile, type ReadOptions, type ReadinessOptions, RunnerContext, type S3Config, Secret, type ToolDownloadOptions, ToolInstaller, ToolInstallerError, type ToolInstallerShape, type UploadOptions, type UploadResult, type WithBufferOptions, type WithStepOptions, WorkflowCommand, describeCause, projectCheckState };
4794
+ export { Action, ActionCache, ActionCacheError, type ActionCacheShape, ActionEnvironment, ActionEnvironmentError, type ActionEnvironmentShape, ActionInput, ActionLogger, type ActionLoggerShape, type ActionOutputError, ActionOutputs, type ActionOutputsShape, type ActionRunOptions, ActionRuntime, type ActionServices, ActionState, ActionStateError, type ActionStateShape, ActionsIdentityToken, ActionsProvenance, AmbientPackageManager, type AnnotationProperties, Artifact, ArtifactError, type ArtifactItem, type ArtifactRef, type ArtifactShape, BlobEnvelope, type BlobEnvelopeError, BlobMetadataDecodeError, BlobMetadataEncodeError, BlobStore, BlobStoreError, type BlobStoreShape, BlobTransferError, CacheKey, CacheKeyBadPatternError, type CacheKeyError, CacheKeyReadError, CachedPackageManager, CheckDocument, CheckDocumentError, type CheckDocumentOptions, type CheckDocumentShape, type CheckDocumentSink, CheckDocumentStamp, type CheckFlushOutcome, CheckReport, type CheckRunConclusion, type CheckRunProjection, CheckState, ChildEnv, type ClientLayerOptions, type DataBlobTransfer, DetachedLogUnavailableError, DetachedNotReadyError, DetachedOutputError, DetachedProcess, type DetachedProcessError, type DetachedProcessOps, DetachedSignalFailedError, DetachedSpawnFailedError, type DetachedSpawnOptions, type DownloadOptions, type DownloadResult, DryRun, type DryRunShape, type ExtractOptions, type FileBlobTransfer, GitHubCacheBlobStore, GitHubContext, type GitHubHeadingDepth, type GitHubListOptions, GitHubMarkdown, type GitHubRowSchema, type GitHubSchemaTable, type GitHubSchemaTableColumn, type GitHubSchemaTableColumns, type GitHubSchemaTableFormatRequiredKeys, type GitHubSchemaTableFormattedColumn, type GitHubSchemaTableOptions, GitHubToken, GitHubTokenError, InstalledPackageManager, InvalidOutputNameError, InvalidPidError, ManagedDocument, ManagedDocumentError, type ManagedDocumentSource, NotABlobEnvelopeError, OidcClaims, OidcTokenError, OidcTokenIssuer, type OidcTokenIssuerShape, OutputEncodeError, type PackageManagerInstallOptions, PackageManagerInstaller, PackageManagerInstallerError, type PackageManagerInstallerShape, type PairsOptions, type PathPrependEnv, type PathPrependOptions, ProcessId, type ProvisionFileOptions, type ProvisionOptions, type ProvisionedFile, type ReadOptions, type ReadinessOptions, RunnerContext, RunnerFileUnavailableError, RunnerFileWriteError, type S3Config, Secret, type StoredBlob, type ToolDownloadOptions, ToolInstaller, ToolInstallerError, type ToolInstallerShape, TruncatedBlobEnvelopeError, UnsupportedBlobEnvelopeVersionError, type UploadOptions, type UploadResult, type WithBufferOptions, type WithStepOptions, WorkflowCommand, describeCause, projectCheckState };
4544
4795
  //# sourceMappingURL=index.d.ts.map