carrick 0.3.53

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 (114) hide show
  1. package/LICENSE.md +99 -0
  2. package/README.md +79 -0
  3. package/bin/carrick.mjs +168 -0
  4. package/dist/channel.d.ts +16 -0
  5. package/dist/channel.js +37 -0
  6. package/dist/channel.js.map +1 -0
  7. package/dist/cli.d.ts +29 -0
  8. package/dist/cli.js +60 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/contract.d.ts +154 -0
  11. package/dist/contract.js +141 -0
  12. package/dist/contract.js.map +1 -0
  13. package/dist/diagnostics.d.ts +54 -0
  14. package/dist/diagnostics.js +199 -0
  15. package/dist/diagnostics.js.map +1 -0
  16. package/dist/hook/post-edit.d.ts +13 -0
  17. package/dist/hook/post-edit.js +76 -0
  18. package/dist/hook/post-edit.js.map +1 -0
  19. package/dist/hook/session-start.d.ts +2 -0
  20. package/dist/hook/session-start.js +44 -0
  21. package/dist/hook/session-start.js.map +1 -0
  22. package/dist/init/identity.d.ts +20 -0
  23. package/dist/init/identity.js +60 -0
  24. package/dist/init/identity.js.map +1 -0
  25. package/dist/init/repos.d.ts +19 -0
  26. package/dist/init/repos.js +46 -0
  27. package/dist/init/repos.js.map +1 -0
  28. package/dist/init/run.d.ts +11 -0
  29. package/dist/init/run.js +228 -0
  30. package/dist/init/run.js.map +1 -0
  31. package/dist/init/settings.d.ts +50 -0
  32. package/dist/init/settings.js +138 -0
  33. package/dist/init/settings.js.map +1 -0
  34. package/dist/log.d.ts +2 -0
  35. package/dist/log.js +35 -0
  36. package/dist/log.js.map +1 -0
  37. package/dist/native.d.ts +48 -0
  38. package/dist/native.js +127 -0
  39. package/dist/native.js.map +1 -0
  40. package/dist/render.d.ts +65 -0
  41. package/dist/render.js +334 -0
  42. package/dist/render.js.map +1 -0
  43. package/dist/root.d.ts +30 -0
  44. package/dist/root.js +82 -0
  45. package/dist/root.js.map +1 -0
  46. package/dist/server.d.ts +2 -0
  47. package/dist/server.js +255 -0
  48. package/dist/server.js.map +1 -0
  49. package/dist/templates.d.ts +18 -0
  50. package/dist/templates.js +66 -0
  51. package/dist/templates.js.map +1 -0
  52. package/package.json +72 -0
  53. package/plugin/.claude-plugin/plugin.json +6 -0
  54. package/plugin/.lsp.json +14 -0
  55. package/plugin/hooks/hooks.json +27 -0
  56. package/sidecar/dist/src/bundler.d.ts +141 -0
  57. package/sidecar/dist/src/bundler.js +680 -0
  58. package/sidecar/dist/src/capture/anchors.d.ts +61 -0
  59. package/sidecar/dist/src/capture/anchors.js +1132 -0
  60. package/sidecar/dist/src/capture/api.d.ts +378 -0
  61. package/sidecar/dist/src/capture/api.js +10 -0
  62. package/sidecar/dist/src/capture/augmentations.d.ts +20 -0
  63. package/sidecar/dist/src/capture/augmentations.js +60 -0
  64. package/sidecar/dist/src/capture/check-classify.d.ts +58 -0
  65. package/sidecar/dist/src/capture/check-classify.js +189 -0
  66. package/sidecar/dist/src/capture/check-deep.d.ts +32 -0
  67. package/sidecar/dist/src/capture/check-deep.js +91 -0
  68. package/sidecar/dist/src/capture/check-poison.d.ts +40 -0
  69. package/sidecar/dist/src/capture/check-poison.js +155 -0
  70. package/sidecar/dist/src/capture/check-probe.d.ts +70 -0
  71. package/sidecar/dist/src/capture/check-probe.js +147 -0
  72. package/sidecar/dist/src/capture/check-scrub.d.ts +31 -0
  73. package/sidecar/dist/src/capture/check-scrub.js +71 -0
  74. package/sidecar/dist/src/capture/check-workspace.d.ts +52 -0
  75. package/sidecar/dist/src/capture/check-workspace.js +218 -0
  76. package/sidecar/dist/src/capture/check.d.ts +39 -0
  77. package/sidecar/dist/src/capture/check.js +450 -0
  78. package/sidecar/dist/src/capture/deep-walk.d.ts +62 -0
  79. package/sidecar/dist/src/capture/deep-walk.js +243 -0
  80. package/sidecar/dist/src/capture/index.d.ts +36 -0
  81. package/sidecar/dist/src/capture/index.js +477 -0
  82. package/sidecar/dist/src/capture/lockfile.d.ts +48 -0
  83. package/sidecar/dist/src/capture/lockfile.js +490 -0
  84. package/sidecar/dist/src/capture/machinery.d.ts +59 -0
  85. package/sidecar/dist/src/capture/machinery.js +160 -0
  86. package/sidecar/dist/src/capture/node-builder.d.ts +37 -0
  87. package/sidecar/dist/src/capture/node-builder.js +123 -0
  88. package/sidecar/dist/src/capture/paths-rewrite.d.ts +34 -0
  89. package/sidecar/dist/src/capture/paths-rewrite.js +100 -0
  90. package/sidecar/dist/src/capture/self-check.d.ts +38 -0
  91. package/sidecar/dist/src/capture/self-check.js +317 -0
  92. package/sidecar/dist/src/capture/specifiers.d.ts +39 -0
  93. package/sidecar/dist/src/capture/specifiers.js +56 -0
  94. package/sidecar/dist/src/definition-resolver.d.ts +57 -0
  95. package/sidecar/dist/src/definition-resolver.js +153 -0
  96. package/sidecar/dist/src/index.d.ts +14 -0
  97. package/sidecar/dist/src/index.js +564 -0
  98. package/sidecar/dist/src/monorepo-builder.d.ts +129 -0
  99. package/sidecar/dist/src/monorepo-builder.js +584 -0
  100. package/sidecar/dist/src/project-loader.d.ts +130 -0
  101. package/sidecar/dist/src/project-loader.js +399 -0
  102. package/sidecar/dist/src/type-inferrer.d.ts +940 -0
  103. package/sidecar/dist/src/type-inferrer.js +3540 -0
  104. package/sidecar/dist/src/type-structural-expander.d.ts +61 -0
  105. package/sidecar/dist/src/type-structural-expander.js +283 -0
  106. package/sidecar/dist/src/type-text-canonicalizer.d.ts +40 -0
  107. package/sidecar/dist/src/type-text-canonicalizer.js +297 -0
  108. package/sidecar/dist/src/types.d.ts +669 -0
  109. package/sidecar/dist/src/types.js +5 -0
  110. package/sidecar/dist/src/validators.d.ts +2214 -0
  111. package/sidecar/dist/src/validators.js +336 -0
  112. package/sidecar/package.json +6 -0
  113. package/templates/carrick.json +7 -0
  114. package/templates/carrick.yml +45 -0
@@ -0,0 +1,336 @@
1
+ /**
2
+ * Zod validators for the sidecar message protocol
3
+ * These validators ensure incoming JSON messages are correctly typed
4
+ */
5
+ import { z } from 'zod';
6
+ // ============================================================================
7
+ // Inference Kind Schema
8
+ // ============================================================================
9
+ export const InferKindSchema = z.enum([
10
+ 'function_return',
11
+ 'expression',
12
+ 'call_result',
13
+ 'variable',
14
+ 'response_body',
15
+ 'request_body',
16
+ 'signature_return',
17
+ 'function_param',
18
+ 'receiver_type',
19
+ ]);
20
+ // ============================================================================
21
+ // Extraction Config Schemas (New)
22
+ // ============================================================================
23
+ const ExtractionRuleSchema = z.object({
24
+ wrapperSymbols: z.array(z.string()).optional(),
25
+ machineryIndicators: z.array(z.string()).optional(),
26
+ originModuleGlobs: z.array(z.string()).optional(),
27
+ payloadGenericIndex: z.number().int().nonnegative().optional(),
28
+ payloadPropertyPath: z.array(z.string()).optional(),
29
+ unwrapRecursively: z.boolean().optional(),
30
+ // Nonnegative, not positive: the Rust side accepts a model-emitted
31
+ // maxDepth of 0 (a harmless no-op rule), and one rule rejected here would
32
+ // fail the entire infer request.
33
+ maxDepth: z.number().int().nonnegative().optional(),
34
+ });
35
+ const ExtractionConfigSchema = z.object({
36
+ rules: z.array(ExtractionRuleSchema),
37
+ });
38
+ // ============================================================================
39
+ // Tsconfig Snapshot Schema
40
+ // ============================================================================
41
+ const TsconfigSnapshotSchema = z.object({
42
+ compilerOptions: z.object({
43
+ module: z.string().optional(),
44
+ moduleResolution: z.string().optional(),
45
+ target: z.string().optional(),
46
+ lib: z.array(z.string()).optional(),
47
+ types: z.array(z.string()).optional(),
48
+ typeRoots: z.array(z.string()).optional(),
49
+ jsx: z.string().optional(),
50
+ strict: z.boolean().optional(),
51
+ esModuleInterop: z.boolean().optional(),
52
+ skipLibCheck: z.boolean().optional(),
53
+ declaration: z.boolean().optional(),
54
+ declarationMap: z.boolean().optional(),
55
+ paths: z.record(z.array(z.string())).optional(),
56
+ baseUrl: z.string().optional(),
57
+ }).passthrough(),
58
+ });
59
+ // ============================================================================
60
+ // Pinned Dependency Snapshot Schema
61
+ // ============================================================================
62
+ const PinnedDependencySnapshotSchema = z.record(z.string());
63
+ // ============================================================================
64
+ // Repo Metadata Schema
65
+ // ============================================================================
66
+ const RepoMetadataSchema = z.object({
67
+ repoName: z.string().min(1, 'Repo name cannot be empty'),
68
+ dependencies: PinnedDependencySnapshotSchema,
69
+ tsconfig: TsconfigSnapshotSchema,
70
+ extractionConfig: ExtractionConfigSchema.optional(),
71
+ surfaceContent: z.string().optional(),
72
+ });
73
+ // ============================================================================
74
+ // Symbol Request Schema
75
+ // ============================================================================
76
+ export const SymbolRequestSchema = z.object({
77
+ symbol_name: z.string().min(1, 'Symbol name cannot be empty'),
78
+ source_file: z.string().min(1, 'Source file cannot be empty'),
79
+ alias: z.string().optional(),
80
+ // #248: SDL list depth to wrap the bundled element type in (`Order` → `Order[]`).
81
+ // No upper bound here deliberately: a batch invalid at this schema layer
82
+ // fails the *whole* bundle request (see the `maxDepth` note above), which
83
+ // is worse than one bad symbol. The bundler enforces MAX_ARRAY_DEPTH
84
+ // per-symbol instead, so an over-bound depth becomes a normal
85
+ // `symbol_failures` entry rather than sinking every other symbol in the
86
+ // batch.
87
+ array_depth: z.number().int().nonnegative().optional(),
88
+ });
89
+ // ============================================================================
90
+ // Infer Request Item Schema
91
+ // ============================================================================
92
+ export const InferRequestItemSchema = z.object({
93
+ file_path: z.string().min(1, 'File path cannot be empty'),
94
+ line_number: z.number().int().positive('Line number must be positive'),
95
+ span_start: z.number().int().nonnegative('Span start must be non-negative').optional(),
96
+ span_end: z.number().int().nonnegative('Span end must be non-negative').optional(),
97
+ expression_text: z.string().optional(),
98
+ expression_line: z.number().int().positive('Expression line must be positive').optional(),
99
+ infer_kind: InferKindSchema,
100
+ alias: z.string().optional(),
101
+ param_name: z.string().optional(),
102
+ });
103
+ /**
104
+ * Per-item locator validation for infer requests.
105
+ *
106
+ * This is deliberately NOT part of the envelope schema: real runs batch
107
+ * every alias of a repo into one infer request, and a single bad item
108
+ * must produce a per-item error (the alias then pads to `unknown`
109
+ * downstream), not reject the whole batch.
110
+ *
111
+ * Returns an error message, or null when the item is valid.
112
+ */
113
+ export function validateInferRequestItem(item) {
114
+ if (item.span_start !== undefined &&
115
+ item.span_end !== undefined &&
116
+ item.span_end < item.span_start) {
117
+ return 'span_end must be greater than or equal to span_start';
118
+ }
119
+ const hasSpan = item.span_start !== undefined && item.span_end !== undefined;
120
+ const hasText = item.expression_text !== undefined;
121
+ // Function-anchored inference (function_return for file-based routes,
122
+ // signature_return / function_param for the signature pass) locates the
123
+ // function by line_number alone, so it does not require a span or text.
124
+ // response_body / request_body also accept a line-only anchor: for a
125
+ // named-handler route registration the scanner falls back to the registration
126
+ // line, and the inferrer follows that line to the handler (`handlerAtLine`).
127
+ // Worst case the line resolves nothing and the alias pads to `unknown` — the
128
+ // same outcome rejection produced, but now the resolvable cases get through.
129
+ const lineOnlyOk = item.infer_kind === 'function_return' ||
130
+ item.infer_kind === 'signature_return' ||
131
+ item.infer_kind === 'function_param' ||
132
+ item.infer_kind === 'response_body' ||
133
+ item.infer_kind === 'request_body';
134
+ if (!hasSpan && !hasText && !lineOnlyOk) {
135
+ return 'at least one of (span_start + span_end) or expression_text is required';
136
+ }
137
+ return null;
138
+ }
139
+ // ============================================================================
140
+ // Payload Definition Schema (New)
141
+ // ============================================================================
142
+ const SourceLocationSchema = z.object({
143
+ file_path: z.string(),
144
+ start_line: z.number(),
145
+ end_line: z.number(),
146
+ start_column: z.number().optional(),
147
+ end_column: z.number().optional(),
148
+ });
149
+ const PayloadDefinitionSchema = z.object({
150
+ alias: z.string().min(1, 'Alias cannot be empty'),
151
+ type_string: z.string().min(1, 'Type string cannot be empty'),
152
+ source_file: z.string().optional(),
153
+ source_location: SourceLocationSchema.optional(),
154
+ });
155
+ // ============================================================================
156
+ // Compatibility Check Schema (New)
157
+ // ============================================================================
158
+ const CompatibilityCheckSchema = z.object({
159
+ source_repo: z.string().min(1, 'Source repo cannot be empty'),
160
+ source_alias: z.string().min(1, 'Source alias cannot be empty'),
161
+ target_repo: z.string().min(1, 'Target repo cannot be empty'),
162
+ target_alias: z.string().min(1, 'Target alias cannot be empty'),
163
+ direction: z.enum(['source_extends_target', 'target_extends_source', 'bidirectional']),
164
+ });
165
+ // ============================================================================
166
+ // Action-specific Request Schemas
167
+ // ============================================================================
168
+ const BaseRequestSchema = z.object({
169
+ request_id: z.string().min(1, 'Request ID cannot be empty'),
170
+ });
171
+ export const InitRequestSchema = BaseRequestSchema.extend({
172
+ action: z.literal('init'),
173
+ repo_root: z.string().min(1, 'Repo root cannot be empty'),
174
+ tsconfig_path: z.string().optional(),
175
+ tsconfig_snapshot: TsconfigSnapshotSchema.optional(),
176
+ pinned_dependencies: PinnedDependencySnapshotSchema.optional(),
177
+ });
178
+ export const BundleRequestSchema = BaseRequestSchema.extend({
179
+ action: z.literal('bundle'),
180
+ symbols: z.array(SymbolRequestSchema).min(1, 'At least one symbol is required'),
181
+ });
182
+ export const EmitSurfaceRequestSchema = BaseRequestSchema.extend({
183
+ action: z.literal('emit_surface'),
184
+ repo_name: z.string().min(1, 'Repo name cannot be empty'),
185
+ payloads: z.array(PayloadDefinitionSchema).min(1, 'At least one payload is required'),
186
+ output_path: z.string().min(1, 'Output path cannot be empty'),
187
+ });
188
+ /** v2 "tsc as serializer" capture (contract in ./capture/api.ts). */
189
+ const AnchorOriginSchema = z.enum(['llm-symbol', 'deterministic-infer', 'anchor-backfill']);
190
+ const CaptureAnchorRequestSchema = z.discriminatedUnion('kind', [
191
+ z.object({
192
+ kind: z.literal('symbol'),
193
+ alias: z.string().min(1),
194
+ symbol_name: z.string().min(1),
195
+ source_file: z.string().min(1),
196
+ anchor_origin: AnchorOriginSchema,
197
+ array_depth: z.number().int().nonnegative().optional(),
198
+ }),
199
+ z.object({
200
+ kind: z.literal('handler_return'),
201
+ alias: z.string().min(1),
202
+ symbol_name: z.string().min(1),
203
+ source_file: z.string().min(1),
204
+ anchor_origin: AnchorOriginSchema,
205
+ }),
206
+ z.object({
207
+ kind: z.literal('infer'),
208
+ alias: z.string().min(1),
209
+ source_file: z.string().min(1),
210
+ anchor_origin: AnchorOriginSchema,
211
+ span_start: z.number().int().nonnegative().optional(),
212
+ span_end: z.number().int().nonnegative().optional(),
213
+ line_number: z.number().int().positive().optional(),
214
+ expression_text: z.string().optional(),
215
+ unwrap: z.enum(['awaited', 'none']).optional(),
216
+ }),
217
+ z.object({
218
+ kind: z.literal('literal'),
219
+ alias: z.string().min(1),
220
+ type_text: z.string().min(1),
221
+ anchor_origin: AnchorOriginSchema,
222
+ }),
223
+ ]);
224
+ export const CaptureV2RequestSchema = BaseRequestSchema.extend({
225
+ action: z.literal('capture_v2'),
226
+ repo_root: z.string().min(1, 'Repo root cannot be empty'),
227
+ service_name: z.string().min(1, 'Service name cannot be empty'),
228
+ anchors: z.array(CaptureAnchorRequestSchema).min(1, 'At least one anchor is required'),
229
+ out_dir: z.string().min(1, 'Output dir cannot be empty'),
230
+ tsconfig_path: z.string().optional(),
231
+ });
232
+ const CheckStubInputSchema = z.object({
233
+ service_name: z.string().min(1),
234
+ stub_dir: z.string().min(1),
235
+ });
236
+ const CheckPairEndpointSchema = z.object({
237
+ service_name: z.string().min(1),
238
+ alias: z.string().min(1),
239
+ });
240
+ const CheckPairSpecSchema = z.object({
241
+ pair_key: z.string().min(1),
242
+ protocol: z.enum(['http', 'graphql', 'socket', 'pubsub']),
243
+ type_kind: z.enum(['request', 'response', 'both']),
244
+ producer: CheckPairEndpointSchema,
245
+ consumer: CheckPairEndpointSchema,
246
+ });
247
+ export const CheckV2RequestSchema = BaseRequestSchema.extend({
248
+ action: z.literal('check_v2'),
249
+ stubs: z.array(CheckStubInputSchema).min(1, 'At least one stub is required'),
250
+ pairs: z.array(CheckPairSpecSchema).min(1, 'At least one pair is required'),
251
+ workspace_root: z.string().optional(),
252
+ keep_workspace: z.boolean().optional(),
253
+ });
254
+ export const InferRequestSchema = BaseRequestSchema.extend({
255
+ action: z.literal('infer'),
256
+ requests: z.array(InferRequestItemSchema).min(1, 'At least one infer request is required'),
257
+ extraction_config: ExtractionConfigSchema.optional(),
258
+ });
259
+ export const BuildWorkspaceRequestSchema = BaseRequestSchema.extend({
260
+ action: z.literal('build_workspace'),
261
+ repos: z.array(RepoMetadataSchema).min(1, 'At least one repo is required'),
262
+ workspace_root: z.string().optional(),
263
+ });
264
+ export const CheckCompatibilityRequestSchema = BaseRequestSchema.extend({
265
+ action: z.literal('check_compatibility'),
266
+ workspace_root: z.string().min(1, 'Workspace root cannot be empty'),
267
+ checks: z.array(CompatibilityCheckSchema).min(1, 'At least one check is required'),
268
+ });
269
+ export const HealthRequestSchema = BaseRequestSchema.extend({
270
+ action: z.literal('health'),
271
+ });
272
+ export const ShutdownRequestSchema = BaseRequestSchema.extend({
273
+ action: z.literal('shutdown'),
274
+ });
275
+ export const ResolveDefinitionsRequestSchema = BaseRequestSchema.extend({
276
+ action: z.literal('resolve_definitions'),
277
+ stub_dir: z.string().min(1, 'Stub dir cannot be empty'),
278
+ aliases: z.array(z.string().min(1)).min(1, 'At least one alias is required'),
279
+ });
280
+ // ============================================================================
281
+ // Discriminated Union Schema
282
+ // ============================================================================
283
+ /**
284
+ * The main sidecar request schema - a discriminated union on the 'action' field
285
+ */
286
+ export const SidecarRequestSchema = z.discriminatedUnion('action', [
287
+ InitRequestSchema,
288
+ BundleRequestSchema,
289
+ EmitSurfaceRequestSchema,
290
+ CaptureV2RequestSchema,
291
+ CheckV2RequestSchema,
292
+ InferRequestSchema,
293
+ BuildWorkspaceRequestSchema,
294
+ CheckCompatibilityRequestSchema,
295
+ ResolveDefinitionsRequestSchema,
296
+ HealthRequestSchema,
297
+ ShutdownRequestSchema,
298
+ ]);
299
+ /**
300
+ * Validates and parses incoming JSON into a typed SidecarRequest
301
+ *
302
+ * @param json - The raw JSON value to parse
303
+ * @returns ParseResult with either the typed request or an error message
304
+ */
305
+ export function parseRequest(json) {
306
+ const result = SidecarRequestSchema.safeParse(json);
307
+ if (result.success) {
308
+ return {
309
+ success: true,
310
+ request: result.data,
311
+ };
312
+ }
313
+ // Format Zod errors nicely
314
+ const errorMessages = result.error.issues.map((issue) => {
315
+ const path = issue.path.join('.');
316
+ return path ? `${path}: ${issue.message}` : issue.message;
317
+ });
318
+ return {
319
+ success: false,
320
+ error: `Invalid request: ${errorMessages.join('; ')}`,
321
+ };
322
+ }
323
+ /**
324
+ * Validates and parses incoming JSON, throwing on error
325
+ *
326
+ * @param json - The raw JSON value to parse
327
+ * @returns The typed SidecarRequest
328
+ * @throws Error if validation fails
329
+ */
330
+ export function parseRequestOrThrow(json) {
331
+ const result = parseRequest(json);
332
+ if (!result.success) {
333
+ throw new Error(result.error);
334
+ }
335
+ return result.request;
336
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "@carrick/type-sidecar-bundled",
3
+ "private": true,
4
+ "type": "module",
5
+ "main": "dist/src/index.js"
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "serviceName": "",
3
+ "internalEnvVars": [],
4
+ "externalEnvVars": [],
5
+ "internalDomains": [],
6
+ "externalDomains": []
7
+ }
@@ -0,0 +1,45 @@
1
+ name: Carrick
2
+
3
+ on:
4
+ push:
5
+ branches: [{{DEFAULT_BRANCH}}]
6
+ pull_request:
7
+ branches: [{{DEFAULT_BRANCH}}]
8
+ # Carrick Cloud sends this when a sibling repo in the same project changes
9
+ # on its main branch, so this repo's cross-repo results catch up without
10
+ # anyone pushing. If you add deploy steps to this file, gate them on
11
+ # github.event_name so a sibling change never deploys this repo.
12
+ repository_dispatch:
13
+ types: [carrick-sibling-updated]
14
+ # Rescan on demand (Actions tab, or: gh workflow run carrick.yml). After a
15
+ # Carrick release the index only refreshes on the next scan.
16
+ workflow_dispatch:
17
+ inputs:
18
+ full-scan:
19
+ description: >-
20
+ Re-analyze every file instead of reusing the cached answer for a file
21
+ that has not changed. Slower and costs a full analysis; ask for it
22
+ when Carrick has started extracting something it did not extract
23
+ before, so the cache holds answers from before it could.
24
+ type: boolean
25
+ default: false
26
+
27
+ permissions:
28
+ # Mint a short-lived OIDC token Carrick exchanges for keyless upload auth
29
+ # (no upload secret to configure).
30
+ id-token: write
31
+ contents: read
32
+
33
+ jobs:
34
+ carrick:
35
+ runs-on: ubuntu-latest
36
+ steps:
37
+ - uses: actions/checkout@v4
38
+ # Full git history lets Carrick diff against the last scan and run incrementally.
39
+ with:
40
+ fetch-depth: 0
41
+ - uses: {{ACTION_REF}}
42
+ with:
43
+ # Empty on every trigger but workflow_dispatch, which is the point:
44
+ # a full re-analysis is asked for, never carried by a push.
45
+ full-scan: ${{ inputs.full-scan }}