@codyswann/lisa 4.60.3 → 4.60.5

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 (100) hide show
  1. package/{scripts/check-workflow-load-failures.ts → all/copy-overwrite/scripts/check-workflow-load-failures.mjs} +36 -76
  2. package/all/copy-overwrite/scripts/lib/reusable-workflow-load-adapter.d.mts +21 -0
  3. package/{dist/core/reusable-workflow-load-adapter.js → all/copy-overwrite/scripts/lib/reusable-workflow-load-adapter.mjs} +50 -41
  4. package/all/copy-overwrite/scripts/lib/reusable-workflow-load-failure.d.mts +29 -0
  5. package/all/copy-overwrite/scripts/lib/reusable-workflow-load-failure.mjs +124 -0
  6. package/all/copy-overwrite/scripts/lib/reusable-workflow-load-scan.d.mts +36 -0
  7. package/all/copy-overwrite/scripts/lib/reusable-workflow-load-scan.mjs +102 -0
  8. package/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +11 -1
  9. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net-heredoc.py +43 -0
  10. package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +16 -1
  11. package/all/create-only/.github/workflows/workflow-load-failure-sweep.yml +42 -0
  12. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  13. package/dist/core/lisa-owned-hash-ledger.js +72 -0
  14. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  15. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  16. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  17. package/dist/core/upstream-evidence-manifest.js +28 -13
  18. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  19. package/dist/opencode/plugin-templates/parity-safety-net-heredoc.py +43 -0
  20. package/dist/opencode/plugin-templates/parity-safety-net.sh +16 -1
  21. package/package.json +5 -5
  22. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  23. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  24. package/plugins/lisa/hooks/block-instruction-file-edits.sh +11 -1
  25. package/plugins/lisa/hooks/parity-safety-net-heredoc.py +43 -0
  26. package/plugins/lisa/hooks/parity-safety-net.sh +16 -1
  27. package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +11 -1
  28. package/plugins/lisa-agy/hooks/parity-safety-net-heredoc.py +43 -0
  29. package/plugins/lisa-agy/hooks/parity-safety-net.sh +16 -1
  30. package/plugins/lisa-agy/plugin.json +1 -1
  31. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  33. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  34. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  35. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  36. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +11 -1
  38. package/plugins/lisa-copilot/hooks/parity-safety-net-heredoc.py +43 -0
  39. package/plugins/lisa-copilot/hooks/parity-safety-net.sh +16 -1
  40. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  41. package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +11 -1
  42. package/plugins/lisa-cursor/hooks/parity-safety-net-heredoc.py +43 -0
  43. package/plugins/lisa-cursor/hooks/parity-safety-net.sh +16 -1
  44. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  45. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  46. package/plugins/lisa-expo-agy/plugin.json +1 -1
  47. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  57. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  62. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  64. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  67. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails-agy/plugin.json +1 -1
  72. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  73. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  74. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  77. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  82. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  84. package/plugins/src/base/hooks/block-instruction-file-edits.sh +11 -1
  85. package/plugins/src/base/hooks/parity-safety-net-heredoc.py +43 -0
  86. package/plugins/src/base/hooks/parity-safety-net.sh +16 -1
  87. package/scripts/check-workflow-load-failures.mjs +4 -0
  88. package/scripts/two-channel-couplings.json +1 -1
  89. package/typescript/copy-overwrite/.prettierignore +1 -0
  90. package/dist/core/reusable-workflow-load-adapter.d.ts +0 -78
  91. package/dist/core/reusable-workflow-load-adapter.d.ts.map +0 -1
  92. package/dist/core/reusable-workflow-load-adapter.js.map +0 -1
  93. package/dist/core/reusable-workflow-load-failure.d.ts +0 -144
  94. package/dist/core/reusable-workflow-load-failure.d.ts.map +0 -1
  95. package/dist/core/reusable-workflow-load-failure.js +0 -184
  96. package/dist/core/reusable-workflow-load-failure.js.map +0 -1
  97. package/dist/core/reusable-workflow-load-scan.d.ts +0 -92
  98. package/dist/core/reusable-workflow-load-scan.d.ts.map +0 -1
  99. package/dist/core/reusable-workflow-load-scan.js +0 -108
  100. package/dist/core/reusable-workflow-load-scan.js.map +0 -1
@@ -1,78 +0,0 @@
1
- /**
2
- * Turn GitHub's run endpoints into the pages `core/reusable-workflow-load-scan`
3
- * expects (CodySwannGT/lisa#3581).
4
- *
5
- * The HTTP request is injected. That keeps the endpoint shapes, the paging
6
- * parameters and the response handling under test against a stub, which is the
7
- * only way to prove this wiring without a live call whose rate limit decides
8
- * whether the suite passes — the lesson a sibling lane paid twenty minutes of
9
- * held-dirty tree to learn.
10
- *
11
- * ## Why this needs three endpoints and not one
12
- *
13
- * The run LIST gives id, caller path, timestamp and conclusion. It does not
14
- * give the two facts the verdict actually turns on: `referenced_workflows`
15
- * comes from the single-run endpoint, and the job count from the run's jobs
16
- * endpoint. So a full scan is inherently N+1, and reading every run's detail
17
- * across a wide window is the kind of request volume that gets a scheduled job
18
- * rate-limited into a false green.
19
- *
20
- * The narrowing that makes it affordable is sound rather than convenient: a
21
- * load failure is only reachable from a conclusion GitHub can produce BEFORE
22
- * creating a job. Detail is therefore fetched only for those, and a run with
23
- * any other conclusion keeps zeroes it never fetched.
24
- *
25
- * **Those zeroes are not measurements.** For a `success` run they are simply
26
- * absent evidence, and the classifier's label for such a run is not meaningful
27
- * — the only verdict this adapter's output supports is `load-failure`, which
28
- * is the one the scan filters for. Anything that starts reporting per-verdict
29
- * counts from these pages must fetch detail unconditionally first.
30
- * @module core/reusable-workflow-load-adapter
31
- */
32
- import type { RunPage } from "./reusable-workflow-load-scan.js";
33
- /** GitHub's default and maximum page size for the runs endpoint. */
34
- export declare const RUNS_PER_PAGE = 100;
35
- /** Performs one authenticated GET and returns the decoded JSON body. */
36
- export type GithubRequest = (path: string) => Promise<unknown>;
37
- /** What the fetcher needs to address a repository. */
38
- export interface AdapterOptions {
39
- /** Repository in `owner/name` form. */
40
- readonly repo: string;
41
- /** The injected request function. */
42
- readonly request: GithubRequest;
43
- /** Page size; defaults to `RUNS_PER_PAGE`. */
44
- readonly perPage?: number;
45
- }
46
- /**
47
- * Path for one page of run history, newest first.
48
- * @param repo - Repository in `owner/name` form
49
- * @param page - 1-based page number
50
- * @param perPage - Page size
51
- * @returns The API path to request
52
- */
53
- export declare function runsPath(repo: string, page: number, perPage: number): string;
54
- /**
55
- * Path for one run's detail, which is where `referenced_workflows` lives.
56
- * @param repo - Repository in `owner/name` form
57
- * @param id - The run id
58
- * @returns The API path to request
59
- */
60
- export declare function runPath(repo: string, id: number): string;
61
- /**
62
- * Path for one run's jobs, read only for its `total_count`.
63
- * @param repo - Repository in `owner/name` form
64
- * @param id - The run id
65
- * @returns The API path to request
66
- */
67
- export declare function jobsPath(repo: string, id: number): string;
68
- /**
69
- * Build the page fetcher `scanForLoadFailures` consumes.
70
- *
71
- * `hasMore` is derived from a full page rather than from `total_count`,
72
- * because a run created while the scan is paging shifts `total_count` under
73
- * it. A full page means "ask again"; a short page means the history ended.
74
- * @param options - Repository, request function and page size
75
- * @returns A fetcher for one 1-based page of run history
76
- */
77
- export declare function createRunPageFetcher(options: AdapterOptions): (page: number) => Promise<RunPage>;
78
- //# sourceMappingURL=reusable-workflow-load-adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-adapter.d.ts","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,KAAK,EAAE,OAAO,EAAc,MAAM,kCAAkC,CAAC;AAY5E,oEAAoE;AACpE,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,wEAAwE;AACxE,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAE/D,sDAAsD;AACtD,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,8CAA8C;IAC9C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzD;AA8ED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,cAAc,GACtB,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAqBpC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-adapter.js","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-adapter.ts"],"names":[],"mappings":"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"}
@@ -1,144 +0,0 @@
1
- /**
2
- * Distinguish a reusable workflow that failed to LOAD from one that failed to
3
- * RUN (CodySwannGT/lisa#3581).
4
- *
5
- * When an upstream commit makes a reusable workflow unparseable, GitHub
6
- * rejects the file before creating any job. The run has zero jobs, no
7
- * annotation naming the offending line, and a display `name` that degrades to
8
- * its `path` because GitHub never got as far as reading the `name:` key.
9
- * Nothing that watches for a failing JOB can see it — there is no red job to
10
- * open. Sibling module `core/reusable-workflow-pin` records the same failure
11
- * from the other end: "Actions creates zero jobs, so a required check is
12
- * ABSENT rather than red."
13
- *
14
- * ## Which moment this is open at
15
- *
16
- * On a pull request the exposure is CLOSED, and by something that already
17
- * exists: `check-skipped-required-checks --pr=<n>` raises
18
- * `absent_required_check` for a ruleset-required context that posted no
19
- * check-run at all, and a required context that never reports blocks the
20
- * merge regardless. Off a pull request — a `push` or `schedule` handler — no
21
- * arm looks. That is where the incident behind this ticket happened: a
22
- * consumer's handler failed five times in a row before anyone noticed, ~40
23
- * minutes after the upstream commit landed, with nothing changed on the
24
- * consumer side. So the gap is not that detection was never built; it is that
25
- * detection exists at exactly one moment and the failure occurs at every
26
- * moment.
27
- *
28
- * ## The one arm that discriminates, and the three that do not
29
- *
30
- * `referenced_workflows` on `GET /repos/{owner}/{repo}/actions/runs/{id}`
31
- * carries the resolved SHA of every reusable workflow a run actually used. A
32
- * caller that DECLARED one and RESOLVED ZERO did not parse; there is no other
33
- * way to reach that state.
34
- *
35
- * It is only meaningful INSIDE its population — runs whose caller declares a
36
- * reusable workflow at all. Measured on one consumer, 19 failing runs in a
37
- * day: the 5 in population all resolved at least one; the 14 outside it all
38
- * resolved zero. Read bare, the field reports 14 false positives out of 19.
39
- *
40
- * Three cheaper signals are each refuted by a measured run:
41
- *
42
- * - `conclusion == "failure"` — six failures in that window, four load
43
- * failures.
44
- * - `jobs == 0` — one run IN the population resolved its workflow and still
45
- * produced zero jobs, so an empty run is not the signature.
46
- * - `name == path` — a display coincidence. Any workflow that omits `name:`
47
- * satisfies it while perfectly healthy, so it corroborates and never
48
- * decides.
49
- *
50
- * ## The false-red this must not become
51
- *
52
- * A dead runner produces a nearly identical run: `conclusion: failure`, no
53
- * useful output, nothing to open. The difference is structural and total —
54
- * **a load failure has NO JOBS AT ALL; a dead runner has JOBS THAT EXIST AND
55
- * ARE EMPTY.** Collapsing them would report every runner outage as a Lisa
56
- * regression. A skipped or cancelled run is excluded for the same reason: it
57
- * never intended to resolve anything, so calling it a load failure would be a
58
- * control that can only manufacture a red.
59
- * @module core/reusable-workflow-load-failure
60
- */
61
- /** How a run's relationship to reusable-workflow loading was resolved. */
62
- export type RunLoadClass =
63
- /** In population, declared a reusable workflow, resolved none. */
64
- "load-failure"
65
- /** Jobs exist but are empty — a runner problem, not a parse problem. */
66
- | "dead-runner"
67
- /** Resolved at least one reusable workflow; it loaded. */
68
- | "resolved"
69
- /** The caller declares no reusable workflow, so the field says nothing. */
70
- | "out-of-population"
71
- /** Never intended to resolve anything. */
72
- | "skipped"
73
- /** Terminated without running and without failing. */
74
- | "inconclusive"
75
- /** Not finished; asking yet would read an unfinished field. */
76
- | "in-flight";
77
- /** The four run facts this classification needs. */
78
- export interface RunLoadFacts {
79
- /** Whether the caller workflow declares a reusable workflow at all. */
80
- readonly inPopulation: boolean;
81
- /** Length of the run's `referenced_workflows` array. */
82
- readonly referencedCount: number;
83
- /** Number of jobs the run created. */
84
- readonly jobCount: number;
85
- /** The run's `conclusion`, or null while it is still in flight. */
86
- readonly conclusion: string | null;
87
- }
88
- /**
89
- * Whether a caller workflow declares any reusable workflow.
90
- *
91
- * This is the population gate, and it is DERIVED from the caller's own source
92
- * rather than read from a roster. There is deliberately no list of known
93
- * callers or known upstream repositories anywhere in this module: a roster
94
- * would silently exclude every caller nobody remembered to add, which is the
95
- * failure mode this codebase already has a ticket for one level up.
96
- * @param source - Full text of a caller's workflow file
97
- * @returns True when at least one non-comment line calls a reusable workflow
98
- */
99
- export declare function callerDeclaresReusableWorkflow(source: string): boolean;
100
- /**
101
- * Classify what a finished run says about reusable-workflow loading.
102
- *
103
- * The order of these tests is the whole design, so it is worth stating: a run
104
- * that RESOLVED something is settled before the population gate is consulted,
105
- * because resolution is direct evidence that loading worked and the gate is
106
- * only a heuristic about whether the field can speak. That way an
107
- * under-detecting population gate can never manufacture a load-failure
108
- * verdict — it can only cause one to be missed, which is the direction a
109
- * detector should fail in.
110
- * @param facts - The four run facts
111
- * @returns The single class this run belongs to
112
- */
113
- export declare function classifyRunLoad(facts: RunLoadFacts): RunLoadClass;
114
- /** How far back a page set actually read. */
115
- export interface WindowScan {
116
- /** Timestamp of the oldest run read, or null when none was read. */
117
- readonly oldestSeen: string | null;
118
- /** Timestamp the caller intended to cover back to. */
119
- readonly windowStart: string;
120
- /** Whether paging stopped because the history ran out. */
121
- readonly exhausted: boolean;
122
- }
123
- /** Whether a scan covered its window, and why not when it did not. */
124
- export interface WindowVerdict {
125
- /** True only when every run in the window was actually read. */
126
- readonly covered: boolean;
127
- /** Operator-readable reason, empty when covered. */
128
- readonly reason: string;
129
- }
130
- /**
131
- * Whether a page set actually covered the window it claims to report on.
132
- *
133
- * A first implementation of this detector read one page of runs and filtered
134
- * by timestamp. On a busy repository the window's start fell off page one, so
135
- * it reported "OK, 100 runs inspected" across a period that contained four
136
- * known load failures. It was clean because it could not see. A scan that
137
- * cannot cover its window must report an ERROR, never a pass — a detector
138
- * that reports success for the range it failed to read is worse than no
139
- * detector, because it also retires the suspicion.
140
- * @param scan - How far back the page set reached
141
- * @returns Covered, or not covered with the reason
142
- */
143
- export declare function windowCoverage(scan: WindowScan): WindowVerdict;
144
- //# sourceMappingURL=reusable-workflow-load-failure.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-failure.d.ts","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAyCH,0EAA0E;AAC1E,MAAM,MAAM,YAAY;AACtB,kEAAkE;AAChE,cAAc;AAChB,wEAAwE;GACtE,aAAa;AACf,0DAA0D;GACxD,UAAU;AACZ,2EAA2E;GACzE,mBAAmB;AACrB,0CAA0C;GACxC,SAAS;AACX,sDAAsD;GACpD,cAAc;AAChB,+DAA+D;GAC7D,WAAW,CAAC;AAEhB,oDAAoD;AACpD,MAAM,WAAW,YAAY;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,wDAAwD;IACxD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,8BAA8B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQtE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY,CAYjE;AAED,6CAA6C;AAC7C,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;CAC7B;AAED,sEAAsE;AACtE,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,aAAa,CAgC9D"}
@@ -1,184 +0,0 @@
1
- /**
2
- * Distinguish a reusable workflow that failed to LOAD from one that failed to
3
- * RUN (CodySwannGT/lisa#3581).
4
- *
5
- * When an upstream commit makes a reusable workflow unparseable, GitHub
6
- * rejects the file before creating any job. The run has zero jobs, no
7
- * annotation naming the offending line, and a display `name` that degrades to
8
- * its `path` because GitHub never got as far as reading the `name:` key.
9
- * Nothing that watches for a failing JOB can see it — there is no red job to
10
- * open. Sibling module `core/reusable-workflow-pin` records the same failure
11
- * from the other end: "Actions creates zero jobs, so a required check is
12
- * ABSENT rather than red."
13
- *
14
- * ## Which moment this is open at
15
- *
16
- * On a pull request the exposure is CLOSED, and by something that already
17
- * exists: `check-skipped-required-checks --pr=<n>` raises
18
- * `absent_required_check` for a ruleset-required context that posted no
19
- * check-run at all, and a required context that never reports blocks the
20
- * merge regardless. Off a pull request — a `push` or `schedule` handler — no
21
- * arm looks. That is where the incident behind this ticket happened: a
22
- * consumer's handler failed five times in a row before anyone noticed, ~40
23
- * minutes after the upstream commit landed, with nothing changed on the
24
- * consumer side. So the gap is not that detection was never built; it is that
25
- * detection exists at exactly one moment and the failure occurs at every
26
- * moment.
27
- *
28
- * ## The one arm that discriminates, and the three that do not
29
- *
30
- * `referenced_workflows` on `GET /repos/{owner}/{repo}/actions/runs/{id}`
31
- * carries the resolved SHA of every reusable workflow a run actually used. A
32
- * caller that DECLARED one and RESOLVED ZERO did not parse; there is no other
33
- * way to reach that state.
34
- *
35
- * It is only meaningful INSIDE its population — runs whose caller declares a
36
- * reusable workflow at all. Measured on one consumer, 19 failing runs in a
37
- * day: the 5 in population all resolved at least one; the 14 outside it all
38
- * resolved zero. Read bare, the field reports 14 false positives out of 19.
39
- *
40
- * Three cheaper signals are each refuted by a measured run:
41
- *
42
- * - `conclusion == "failure"` — six failures in that window, four load
43
- * failures.
44
- * - `jobs == 0` — one run IN the population resolved its workflow and still
45
- * produced zero jobs, so an empty run is not the signature.
46
- * - `name == path` — a display coincidence. Any workflow that omits `name:`
47
- * satisfies it while perfectly healthy, so it corroborates and never
48
- * decides.
49
- *
50
- * ## The false-red this must not become
51
- *
52
- * A dead runner produces a nearly identical run: `conclusion: failure`, no
53
- * useful output, nothing to open. The difference is structural and total —
54
- * **a load failure has NO JOBS AT ALL; a dead runner has JOBS THAT EXIST AND
55
- * ARE EMPTY.** Collapsing them would report every runner outage as a Lisa
56
- * regression. A skipped or cancelled run is excluded for the same reason: it
57
- * never intended to resolve anything, so calling it a load failure would be a
58
- * control that can only manufacture a red.
59
- * @module core/reusable-workflow-load-failure
60
- */
61
- /**
62
- * A job-level `uses:` naming a reusable workflow in another repository.
63
- *
64
- * The path shape is what discriminates, not a roster of known callers: a
65
- * reusable workflow is always `<owner>/<repo>/.github/workflows/<file>@<ref>`.
66
- * A step action is `<owner>/<repo>@<ref>` with no path, so `actions/checkout@v6`
67
- * cannot match — which matters, because reading step actions as declarations
68
- * is exactly what produced 14 false positives out of 19.
69
- */
70
- const CROSS_REPO_USES = /^[ \t]*uses:[ \t]*["']?[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+\/\.github\/workflows\/[A-Za-z0-9._-]+\.ya?ml@[^\s"'#]+/u;
71
- /**
72
- * A job-level `uses:` naming a reusable workflow in the caller's own
73
- * repository.
74
- *
75
- * Unlike the cross-repo arm this one is NOT backed by a measured run — the
76
- * ticket's evidence is all cross-repo. It is included because the path shape
77
- * is unambiguous and excluding it would leave local reusable workflows
78
- * undetectable, but if this detector ever reports a false positive, this is
79
- * the first line to suspect.
80
- */
81
- const LOCAL_USES = /^[ \t]*uses:[ \t]*["']?\.\/\.github\/workflows\/[A-Za-z0-9._-]+\.ya?ml/u;
82
- /** Whether a line is entirely a YAML comment. */
83
- const COMMENT_LINE = /^\s*#/u;
84
- /**
85
- * Conclusions that can be reached before a single job is created.
86
- *
87
- * `startup_failure` is GitHub's own name for this class and is the conclusion
88
- * a load failure most often carries; `failure` is what the runs measured for
89
- * this ticket carried. Anything else — `success`, `cancelled`, `neutral` — is
90
- * never a load failure, and treating it as one is how a detector starts
91
- * inventing reds.
92
- */
93
- const PRE_JOB_FAILURES = ["failure", "startup_failure"];
94
- /**
95
- * Whether a caller workflow declares any reusable workflow.
96
- *
97
- * This is the population gate, and it is DERIVED from the caller's own source
98
- * rather than read from a roster. There is deliberately no list of known
99
- * callers or known upstream repositories anywhere in this module: a roster
100
- * would silently exclude every caller nobody remembered to add, which is the
101
- * failure mode this codebase already has a ticket for one level up.
102
- * @param source - Full text of a caller's workflow file
103
- * @returns True when at least one non-comment line calls a reusable workflow
104
- */
105
- export function callerDeclaresReusableWorkflow(source) {
106
- return source
107
- .split("\n")
108
- .some(line => !COMMENT_LINE.test(line) &&
109
- (CROSS_REPO_USES.test(line) || LOCAL_USES.test(line)));
110
- }
111
- /**
112
- * Classify what a finished run says about reusable-workflow loading.
113
- *
114
- * The order of these tests is the whole design, so it is worth stating: a run
115
- * that RESOLVED something is settled before the population gate is consulted,
116
- * because resolution is direct evidence that loading worked and the gate is
117
- * only a heuristic about whether the field can speak. That way an
118
- * under-detecting population gate can never manufacture a load-failure
119
- * verdict — it can only cause one to be missed, which is the direction a
120
- * detector should fail in.
121
- * @param facts - The four run facts
122
- * @returns The single class this run belongs to
123
- */
124
- export function classifyRunLoad(facts) {
125
- const { inPopulation, referencedCount, jobCount, conclusion } = facts;
126
- if (conclusion === null)
127
- return "in-flight";
128
- if (conclusion === "skipped")
129
- return "skipped";
130
- // Resolution outranks the population gate: see the note above.
131
- if (referencedCount > 0)
132
- return "resolved";
133
- if (!inPopulation)
134
- return "out-of-population";
135
- // Jobs that exist and are empty are a dead runner, not a parse failure.
136
- if (jobCount > 0)
137
- return "dead-runner";
138
- if (!PRE_JOB_FAILURES.includes(conclusion))
139
- return "inconclusive";
140
- return "load-failure";
141
- }
142
- /**
143
- * Whether a page set actually covered the window it claims to report on.
144
- *
145
- * A first implementation of this detector read one page of runs and filtered
146
- * by timestamp. On a busy repository the window's start fell off page one, so
147
- * it reported "OK, 100 runs inspected" across a period that contained four
148
- * known load failures. It was clean because it could not see. A scan that
149
- * cannot cover its window must report an ERROR, never a pass — a detector
150
- * that reports success for the range it failed to read is worse than no
151
- * detector, because it also retires the suspicion.
152
- * @param scan - How far back the page set reached
153
- * @returns Covered, or not covered with the reason
154
- */
155
- export function windowCoverage(scan) {
156
- const { oldestSeen, windowStart, exhausted } = scan;
157
- // An exhausted history is covered even when it stops short: a repository
158
- // younger than the window has no earlier runs to read, and refusing it
159
- // would be a red nobody can ever clear.
160
- if (exhausted)
161
- return { covered: true, reason: "" };
162
- if (oldestSeen === null) {
163
- return {
164
- covered: false,
165
- reason: `read no runs at all, so it did not reach ${windowStart}. An empty read is not an empty window.`,
166
- };
167
- }
168
- const oldest = Date.parse(oldestSeen);
169
- const start = Date.parse(windowStart);
170
- if (Number.isNaN(oldest) || Number.isNaN(start)) {
171
- return {
172
- covered: false,
173
- reason: `could not read \`${oldestSeen}\` or \`${windowStart}\` as a timestamp, so it did not reach the start of the window. An unreadable bound is not a satisfied one.`,
174
- };
175
- }
176
- if (oldest > start) {
177
- return {
178
- covered: false,
179
- reason: `paging stopped at ${oldestSeen} and did not reach ${windowStart}, so any load failure older than that was never read. Page further, or narrow the window.`,
180
- };
181
- }
182
- return { covered: true, reason: "" };
183
- }
184
- //# sourceMappingURL=reusable-workflow-load-failure.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-failure.js","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-failure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH;;;;;;;;GAQG;AACH,MAAM,eAAe,GACnB,iHAAiH,CAAC;AAEpH;;;;;;;;;GASG;AACH,MAAM,UAAU,GACd,yEAAyE,CAAC;AAE5E,iDAAiD;AACjD,MAAM,YAAY,GAAG,QAAQ,CAAC;AAE9B;;;;;;;;GAQG;AACH,MAAM,gBAAgB,GAAsB,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;AA+B3E;;;;;;;;;;GAUG;AACH,MAAM,UAAU,8BAA8B,CAAC,MAAc;IAC3D,OAAO,MAAM;SACV,KAAK,CAAC,IAAI,CAAC;SACX,IAAI,CACH,IAAI,CAAC,EAAE,CACL,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;QACxB,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CACxD,CAAC;AACN,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,eAAe,CAAC,KAAmB;IACjD,MAAM,EAAE,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IAEtE,IAAI,UAAU,KAAK,IAAI;QAAE,OAAO,WAAW,CAAC;IAC5C,IAAI,UAAU,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC/C,+DAA+D;IAC/D,IAAI,eAAe,GAAG,CAAC;QAAE,OAAO,UAAU,CAAC;IAC3C,IAAI,CAAC,YAAY;QAAE,OAAO,mBAAmB,CAAC;IAC9C,wEAAwE;IACxE,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,aAAa,CAAC;IACvC,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,cAAc,CAAC;IAClE,OAAO,cAAc,CAAC;AACxB,CAAC;AAoBD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,IAAgB;IAC7C,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAEpD,yEAAyE;IACzE,uEAAuE;IACvE,wCAAwC;IACxC,IAAI,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEpD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,4CAA4C,WAAW,yCAAyC;SACzG,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,oBAAoB,UAAU,WAAW,WAAW,6GAA6G;SAC1K,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,GAAG,KAAK,EAAE,CAAC;QACnB,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,qBAAqB,UAAU,sBAAsB,WAAW,2FAA2F;SACpK,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACvC,CAAC"}
@@ -1,92 +0,0 @@
1
- /**
2
- * Page a repository's run history and report reusable-workflow load failures
3
- * (CodySwannGT/lisa#3581).
4
- *
5
- * `core/reusable-workflow-load-failure` decides what a single run means. This
6
- * module is the part that has to READ enough runs for that verdict to be worth
7
- * anything, and it is a separate module because the two failure modes are
8
- * unrelated: one is a misclassification, the other is a detector reporting on
9
- * a window it never covered.
10
- *
11
- * The page fetcher is injected rather than imported. That is not a testing
12
- * convenience — it is the only way the paging and coverage logic here can be
13
- * exercised at all, because the alternative is a live API call whose rate
14
- * limit decides whether the suite passes. Every rule below is proven against
15
- * fixture pages; the `gh api` adapter that supplies real ones is a caller's
16
- * job and is deliberately not part of this module.
17
- * @module core/reusable-workflow-load-scan
18
- */
19
- import { type RunLoadClass } from "./reusable-workflow-load-failure.js";
20
- /** One run as this scan needs to see it. */
21
- export interface ScannedRun {
22
- /** The run's id, for the operator to open. */
23
- readonly id: number;
24
- /** The caller workflow's path, e.g. `.github/workflows/nightly.yml`. */
25
- readonly path: string;
26
- /** ISO timestamp the run was created. */
27
- readonly createdAt: string;
28
- /** Length of the run's `referenced_workflows` array. */
29
- readonly referencedCount: number;
30
- /** Number of jobs the run created. */
31
- readonly jobCount: number;
32
- /** The run's `conclusion`, or null while in flight. */
33
- readonly conclusion: string | null;
34
- }
35
- /** One page of run history. */
36
- export interface RunPage {
37
- /** Runs on this page, newest first, as the API returns them. */
38
- readonly runs: readonly ScannedRun[];
39
- /** Whether another page exists after this one. */
40
- readonly hasMore: boolean;
41
- }
42
- /** What the scan needs in order to run. */
43
- export interface ScanRequest {
44
- /** Fetches one 1-based page of run history. */
45
- readonly fetchPage: (page: number) => Promise<RunPage>;
46
- /** ISO timestamp to cover back to. */
47
- readonly windowStart: string;
48
- /** Whether a caller path declares a reusable workflow. */
49
- readonly inPopulation: (path: string) => boolean;
50
- /** Hard cap on pages read, so a busy repository cannot spin forever. */
51
- readonly maxPages: number;
52
- }
53
- /** One run the scan is reporting on. */
54
- export interface ScanFinding {
55
- /** The run's id. */
56
- readonly id: number;
57
- /** The caller workflow's path. */
58
- readonly path: string;
59
- /** What this run turned out to be. */
60
- readonly verdict: RunLoadClass;
61
- }
62
- /** The scan's answer. */
63
- export interface ScanResult {
64
- /** Runs classified as load failures. */
65
- readonly loadFailures: readonly ScanFinding[];
66
- /** How many runs were read. */
67
- readonly inspected: number;
68
- /**
69
- * True only when the window was covered AND the scan can speak for it.
70
- *
71
- * A false here is an ERROR, not a pass with a caveat: the whole reason this
72
- * field exists is that an earlier implementation read one page, missed four
73
- * known load failures that had fallen off it, and reported "OK, 100 runs
74
- * inspected". A detector that reports success for a range it failed to read
75
- * also retires the suspicion that would have found the failures by hand.
76
- */
77
- readonly covered: boolean;
78
- /** Why the window was not covered, empty when it was. */
79
- readonly reason: string;
80
- }
81
- /**
82
- * Read run history until the window is covered, then report load failures.
83
- *
84
- * Paging stops on the first of three conditions: a run older than the window
85
- * was seen, the history ran out, or `maxPages` was reached. Only the first
86
- * two are coverage; hitting the cap leaves `covered` false, because a cap is
87
- * a budget the operator set and not evidence about the runs beyond it.
88
- * @param request - Fetcher, window, population test, and page cap
89
- * @returns Findings plus whether the window was actually covered
90
- */
91
- export declare function scanForLoadFailures(request: ScanRequest): Promise<ScanResult>;
92
- //# sourceMappingURL=reusable-workflow-load-scan.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-scan.d.ts","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,qCAAqC,CAAC;AAE7C,4CAA4C;AAC5C,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,sCAAsC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,+BAA+B;AAC/B,MAAM,WAAW,OAAO;IACtB,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,SAAS,UAAU,EAAE,CAAC;IACrC,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,sCAAsC;IACtC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACjD,wEAAwE;IACxE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,oBAAoB;IACpB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;CAChC;AAED,yBAAyB;AACzB,MAAM,WAAW,UAAU;IACzB,wCAAwC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,WAAW,EAAE,CAAC;IAC9C,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAyED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CAgCrB"}
@@ -1,108 +0,0 @@
1
- /**
2
- * Page a repository's run history and report reusable-workflow load failures
3
- * (CodySwannGT/lisa#3581).
4
- *
5
- * `core/reusable-workflow-load-failure` decides what a single run means. This
6
- * module is the part that has to READ enough runs for that verdict to be worth
7
- * anything, and it is a separate module because the two failure modes are
8
- * unrelated: one is a misclassification, the other is a detector reporting on
9
- * a window it never covered.
10
- *
11
- * The page fetcher is injected rather than imported. That is not a testing
12
- * convenience — it is the only way the paging and coverage logic here can be
13
- * exercised at all, because the alternative is a live API call whose rate
14
- * limit decides whether the suite passes. Every rule below is proven against
15
- * fixture pages; the `gh api` adapter that supplies real ones is a caller's
16
- * job and is deliberately not part of this module.
17
- * @module core/reusable-workflow-load-scan
18
- */
19
- import { classifyRunLoad, windowCoverage, } from "./reusable-workflow-load-failure.js";
20
- /**
21
- * Whether a run predates the window, so everything after it can be ignored.
22
- * @param run - The run being considered
23
- * @param windowStart - ISO timestamp the scan intends to cover back to
24
- * @returns True only when this run is at or before the start of the window
25
- */
26
- function reachedWindow(run, windowStart) {
27
- const created = Date.parse(run.createdAt);
28
- const start = Date.parse(windowStart);
29
- // An unparseable timestamp must not read as "we are past the window" —
30
- // that would end the scan early and call the short read complete.
31
- if (Number.isNaN(created) || Number.isNaN(start))
32
- return false;
33
- return created <= start;
34
- }
35
- /**
36
- * Classify one run through the shared decision.
37
- * @param run - The run being classified
38
- * @param inPopulation - Whether this run's caller declares a reusable workflow
39
- * @returns The run's id, caller path, and verdict
40
- */
41
- function findingFor(run, inPopulation) {
42
- return {
43
- id: run.id,
44
- path: run.path,
45
- verdict: classifyRunLoad({
46
- inPopulation,
47
- referencedCount: run.referencedCount,
48
- jobCount: run.jobCount,
49
- conclusion: run.conclusion,
50
- }),
51
- };
52
- }
53
- /**
54
- * Fold one page into the running scan state.
55
- * @param state - Everything the scan has accumulated so far
56
- * @param page - The page just fetched
57
- * @param request - The scan request, for its window and population test
58
- * @returns The scan state after absorbing this page
59
- */
60
- function absorb(state, page, request) {
61
- const findings = page.runs.map(run => findingFor(run, request.inPopulation(run.path)));
62
- const oldest = page.runs.at(-1)?.createdAt ?? state.oldestSeen;
63
- const reached = page.runs.some(run => reachedWindow(run, request.windowStart));
64
- return {
65
- findings: [...state.findings, ...findings],
66
- inspected: state.inspected + page.runs.length,
67
- oldestSeen: oldest,
68
- exhausted: !page.hasMore,
69
- done: reached || !page.hasMore,
70
- };
71
- }
72
- /**
73
- * Read run history until the window is covered, then report load failures.
74
- *
75
- * Paging stops on the first of three conditions: a run older than the window
76
- * was seen, the history ran out, or `maxPages` was reached. Only the first
77
- * two are coverage; hitting the cap leaves `covered` false, because a cap is
78
- * a budget the operator set and not evidence about the runs beyond it.
79
- * @param request - Fetcher, window, population test, and page cap
80
- * @returns Findings plus whether the window was actually covered
81
- */
82
- export async function scanForLoadFailures(request) {
83
- const initial = {
84
- findings: [],
85
- inspected: 0,
86
- oldestSeen: null,
87
- exhausted: false,
88
- done: false,
89
- };
90
- const final = await Array.from({ length: request.maxPages }, (_unused, index) => index + 1).reduce(async (carried, pageNumber) => {
91
- const state = await carried;
92
- if (state.done)
93
- return state;
94
- return absorb(state, await request.fetchPage(pageNumber), request);
95
- }, Promise.resolve(initial));
96
- const coverage = windowCoverage({
97
- oldestSeen: final.oldestSeen,
98
- windowStart: request.windowStart,
99
- exhausted: final.exhausted,
100
- });
101
- return {
102
- loadFailures: final.findings.filter(finding => finding.verdict === "load-failure"),
103
- inspected: final.inspected,
104
- covered: coverage.covered,
105
- reason: coverage.reason,
106
- };
107
- }
108
- //# sourceMappingURL=reusable-workflow-load-scan.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"reusable-workflow-load-scan.js","sourceRoot":"","sources":["../../src/core/reusable-workflow-load-scan.ts"],"names":[],"mappings":"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"}