@aviaratech/ai-delivery 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CONTRIBUTING.md +47 -0
  2. package/LICENSE +21 -0
  3. package/README.md +84 -0
  4. package/dist/agent.d.ts +17 -0
  5. package/dist/agent.js +16 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/cli.d.ts +2 -0
  8. package/dist/cli.js +314 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/cliResult.d.ts +2 -0
  11. package/dist/cliResult.js +12 -0
  12. package/dist/cliResult.js.map +1 -0
  13. package/dist/cliResult.test.d.ts +1 -0
  14. package/dist/cliResult.test.js +30 -0
  15. package/dist/cliResult.test.js.map +1 -0
  16. package/dist/config/deliveryConfig.d.ts +113 -0
  17. package/dist/config/deliveryConfig.js +261 -0
  18. package/dist/config/deliveryConfig.js.map +1 -0
  19. package/dist/config/deliveryConfig.test.d.ts +1 -0
  20. package/dist/config/deliveryConfig.test.js +355 -0
  21. package/dist/config/deliveryConfig.test.js.map +1 -0
  22. package/dist/delivery/common.d.ts +28 -0
  23. package/dist/delivery/common.js +117 -0
  24. package/dist/delivery/common.js.map +1 -0
  25. package/dist/delivery/delivery.test.d.ts +1 -0
  26. package/dist/delivery/delivery.test.js +234 -0
  27. package/dist/delivery/delivery.test.js.map +1 -0
  28. package/dist/delivery/evidence.d.ts +92 -0
  29. package/dist/delivery/evidence.js +126 -0
  30. package/dist/delivery/evidence.js.map +1 -0
  31. package/dist/delivery/index.d.ts +8 -0
  32. package/dist/delivery/index.js +5 -0
  33. package/dist/delivery/index.js.map +1 -0
  34. package/dist/delivery/policy.d.ts +237 -0
  35. package/dist/delivery/policy.js +246 -0
  36. package/dist/delivery/policy.js.map +1 -0
  37. package/dist/delivery/stage.d.ts +125 -0
  38. package/dist/delivery/stage.js +218 -0
  39. package/dist/delivery/stage.js.map +1 -0
  40. package/dist/dispatch.d.ts +15 -0
  41. package/dist/dispatch.js +397 -0
  42. package/dist/dispatch.js.map +1 -0
  43. package/dist/errors.d.ts +4 -0
  44. package/dist/errors.js +9 -0
  45. package/dist/errors.js.map +1 -0
  46. package/dist/genericCompatibility.test.d.ts +1 -0
  47. package/dist/genericCompatibility.test.js +92 -0
  48. package/dist/genericCompatibility.test.js.map +1 -0
  49. package/dist/git.d.ts +13 -0
  50. package/dist/git.js +65 -0
  51. package/dist/git.js.map +1 -0
  52. package/dist/github/client.d.ts +34 -0
  53. package/dist/github/client.js +143 -0
  54. package/dist/github/client.js.map +1 -0
  55. package/dist/github/commandIdentityPolicy.d.ts +13 -0
  56. package/dist/github/commandIdentityPolicy.js +46 -0
  57. package/dist/github/commandIdentityPolicy.js.map +1 -0
  58. package/dist/github/nativeIssueMetadata.d.ts +78 -0
  59. package/dist/github/nativeIssueMetadata.js +249 -0
  60. package/dist/github/nativeIssueMetadata.js.map +1 -0
  61. package/dist/github/projectDelivery.d.ts +68 -0
  62. package/dist/github/projectDelivery.js +540 -0
  63. package/dist/github/projectDelivery.js.map +1 -0
  64. package/dist/github/relationships.d.ts +69 -0
  65. package/dist/github/relationships.js +395 -0
  66. package/dist/github/relationships.js.map +1 -0
  67. package/dist/github/repo.d.ts +8 -0
  68. package/dist/github/repo.js +34 -0
  69. package/dist/github/repo.js.map +1 -0
  70. package/dist/index.d.ts +2 -0
  71. package/dist/index.js +3 -0
  72. package/dist/index.js.map +1 -0
  73. package/dist/issue.d.ts +86 -0
  74. package/dist/issue.js +347 -0
  75. package/dist/issue.js.map +1 -0
  76. package/dist/lifecycle.test.d.ts +1 -0
  77. package/dist/lifecycle.test.js +1118 -0
  78. package/dist/lifecycle.test.js.map +1 -0
  79. package/dist/logger.d.ts +21 -0
  80. package/dist/logger.js +86 -0
  81. package/dist/logger.js.map +1 -0
  82. package/dist/mcp/index.d.ts +5 -0
  83. package/dist/mcp/index.js +46 -0
  84. package/dist/mcp/index.js.map +1 -0
  85. package/dist/mcp/tools.d.ts +162 -0
  86. package/dist/mcp/tools.js +154 -0
  87. package/dist/mcp/tools.js.map +1 -0
  88. package/dist/pr.d.ts +146 -0
  89. package/dist/pr.js +972 -0
  90. package/dist/pr.js.map +1 -0
  91. package/dist/pr.test.d.ts +1 -0
  92. package/dist/pr.test.js +252 -0
  93. package/dist/pr.test.js.map +1 -0
  94. package/dist/review.d.ts +104 -0
  95. package/dist/review.js +228 -0
  96. package/dist/review.js.map +1 -0
  97. package/dist/review.test.d.ts +1 -0
  98. package/dist/review.test.js +327 -0
  99. package/dist/review.test.js.map +1 -0
  100. package/dist/services/agentReadinessService.d.ts +17 -0
  101. package/dist/services/agentReadinessService.js +225 -0
  102. package/dist/services/agentReadinessService.js.map +1 -0
  103. package/dist/services/deliveryAdmission.d.ts +33 -0
  104. package/dist/services/deliveryAdmission.js +122 -0
  105. package/dist/services/deliveryAdmission.js.map +1 -0
  106. package/dist/services/deliveryRecordService.d.ts +47 -0
  107. package/dist/services/deliveryRecordService.js +199 -0
  108. package/dist/services/deliveryRecordService.js.map +1 -0
  109. package/dist/services/legacyIssueMigration.d.ts +46 -0
  110. package/dist/services/legacyIssueMigration.js +313 -0
  111. package/dist/services/legacyIssueMigration.js.map +1 -0
  112. package/dist/services/worktreeRegistry.d.ts +67 -0
  113. package/dist/services/worktreeRegistry.js +545 -0
  114. package/dist/services/worktreeRegistry.js.map +1 -0
  115. package/dist/terminalMessages.d.ts +13 -0
  116. package/dist/terminalMessages.js +43 -0
  117. package/dist/terminalMessages.js.map +1 -0
  118. package/dist/utils/atomicJson.d.ts +6 -0
  119. package/dist/utils/atomicJson.js +78 -0
  120. package/dist/utils/atomicJson.js.map +1 -0
  121. package/dist/utils/filesystem.d.ts +9 -0
  122. package/dist/utils/filesystem.js +19 -0
  123. package/dist/utils/filesystem.js.map +1 -0
  124. package/dist/utils/lockfile.d.ts +12 -0
  125. package/dist/utils/lockfile.js +78 -0
  126. package/dist/utils/lockfile.js.map +1 -0
  127. package/dist/verification.d.ts +24 -0
  128. package/dist/verification.js +227 -0
  129. package/dist/verification.js.map +1 -0
  130. package/dist/worktree.d.ts +44 -0
  131. package/dist/worktree.js +255 -0
  132. package/dist/worktree.js.map +1 -0
  133. package/package.json +82 -0
  134. package/plugins/ai-delivery/.claude-plugin/plugin.json +6 -0
  135. package/plugins/ai-delivery/.mcp.json +8 -0
  136. package/plugins/ai-delivery/README.md +5 -0
  137. package/plugins/ai-delivery/dist/mcp-launcher.js +22 -0
  138. package/plugins/ai-delivery/skills/intake-create/SKILL.md +26 -0
  139. package/plugins/ai-delivery/skills/pr-handoff/SKILL.md +28 -0
  140. package/plugins/ai-delivery/skills/worktree-lifecycle/SKILL.md +24 -0
@@ -0,0 +1,225 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join, normalize } from 'node:path';
3
+ export function evaluateAgentReadiness(input) {
4
+ const failures = [];
5
+ const body = input.body.trim();
6
+ const scope = extractScopePaths(body);
7
+ if (!hasSubstantiveOutcome(body)) {
8
+ failures.push({
9
+ code: 'outcome',
10
+ message: 'Add a substantive Outcome or Problem section.',
11
+ });
12
+ }
13
+ const acceptanceCriteria = extractCheckboxItems(extractSection(body, 'Acceptance Criteria'));
14
+ if (acceptanceCriteria.length < 2 || acceptanceCriteria.length > 5) {
15
+ failures.push({
16
+ code: 'acceptance-criteria',
17
+ message: 'Declare two to five checkbox acceptance criteria.',
18
+ });
19
+ }
20
+ if (!hasExecutableVerification(body)) {
21
+ failures.push({
22
+ code: 'verification',
23
+ message: 'Include an executable verification command in inline or fenced code.',
24
+ });
25
+ }
26
+ if (scope.length === 0 || scope.some((scopePath) => !isRepositoryPath(scopePath, input.repoRoot))) {
27
+ failures.push({
28
+ code: 'scope',
29
+ message: 'Declare scope paths that exist in the repository.',
30
+ });
31
+ }
32
+ if ((input.blockedBy?.length ?? 0) > 0) {
33
+ failures.push({
34
+ code: 'blocked',
35
+ message: `Resolve native blocked-by issue${input.blockedBy?.length === 1 ? '' : 's'} before autonomous development.`,
36
+ });
37
+ }
38
+ if (requiresRepositoryEvidence({ body, scope }) && !hasRepositoryEvidence(body, input.repoRoot)) {
39
+ failures.push({
40
+ code: 'evidence',
41
+ message: 'Add repository-backed current-state evidence for this scope.',
42
+ });
43
+ }
44
+ if (input.trackingParent === true) {
45
+ failures.push({
46
+ code: 'tracking-parent',
47
+ message: 'Dispatch an executable sub-issue instead of the tracking parent.',
48
+ });
49
+ }
50
+ else if (input.points === undefined) {
51
+ failures.push({
52
+ code: 'points',
53
+ message: 'Set configured native Points before autonomous development.',
54
+ });
55
+ }
56
+ if (hasUnresolvedMarker(body)) {
57
+ failures.push({
58
+ code: 'unresolved',
59
+ message: 'Resolve TODO, TBD, policy-choice, or architectural-fork markers.',
60
+ });
61
+ }
62
+ return { failures, ready: failures.length === 0 };
63
+ }
64
+ function extractCheckboxItems(section) {
65
+ return section
66
+ .split(/\r?\n/u)
67
+ .map((line) => {
68
+ const candidate = line.trimStart();
69
+ if (!['*', '+', '-'].includes(candidate.charAt(0))) {
70
+ return undefined;
71
+ }
72
+ const checkbox = candidate.slice(1).trimStart();
73
+ if (checkbox.length < 4 ||
74
+ !checkbox.startsWith('[') ||
75
+ ![' ', 'x', 'X'].includes(checkbox.charAt(1)) ||
76
+ checkbox.charAt(2) !== ']' ||
77
+ ![' ', '\t'].includes(checkbox.charAt(3))) {
78
+ return undefined;
79
+ }
80
+ return checkbox.slice(4).trim();
81
+ })
82
+ .filter((item) => item !== undefined && item.length > 0);
83
+ }
84
+ function extractFencedCode(body) {
85
+ const blocks = [];
86
+ let current = null;
87
+ for (const line of body.split(/\r?\n/u)) {
88
+ if (current !== null) {
89
+ if (line.trim() === '```') {
90
+ blocks.push(current.join('\n'));
91
+ current = null;
92
+ }
93
+ else {
94
+ current.push(line);
95
+ }
96
+ continue;
97
+ }
98
+ const candidate = line.trimStart();
99
+ const indentation = line.length - candidate.length;
100
+ if (indentation > 3 || !candidate.startsWith('```')) {
101
+ continue;
102
+ }
103
+ const info = candidate.slice(3).trim().toLowerCase();
104
+ if (['', 'bash', 'console', 'sh', 'shell', 'zsh'].includes(info)) {
105
+ current = [];
106
+ }
107
+ }
108
+ return blocks;
109
+ }
110
+ function extractInlineCode(body) {
111
+ return Array.from(body.matchAll(/`([^`\r\n]+)`/gu), (match) => match[1]?.trim() ?? '').filter(Boolean);
112
+ }
113
+ function extractScopePaths(body) {
114
+ const section = extractSection(body, 'Scope');
115
+ return Array.from(section.matchAll(/`([^`\r\n]+)`/gu), (match) => match[1]?.trim() ?? '').filter(Boolean);
116
+ }
117
+ function extractSection(body, heading) {
118
+ const lines = body.split(/\r?\n/u);
119
+ const start = lines.findIndex((line) => readMarkdownHeading(line)?.toLowerCase() === heading.toLowerCase());
120
+ if (start === -1)
121
+ return '';
122
+ const content = [];
123
+ for (const line of lines.slice(start + 1)) {
124
+ if (readMarkdownHeading(line) !== null)
125
+ break;
126
+ content.push(line);
127
+ }
128
+ return content.join('\n').trim();
129
+ }
130
+ function hasExecutableVerification(body) {
131
+ const executableCommands = new Set([
132
+ 'bash',
133
+ 'cargo',
134
+ 'git',
135
+ 'go',
136
+ 'make',
137
+ 'node',
138
+ 'npm',
139
+ 'npx',
140
+ 'pnpm',
141
+ 'python',
142
+ 'python3',
143
+ 'sh',
144
+ 'tsc',
145
+ 'tsx',
146
+ 'zsh',
147
+ ]);
148
+ const code = [...extractInlineCode(body), ...extractFencedCode(body)];
149
+ return code.some((value) => value.split(/\r?\n/u).some((line) => {
150
+ const command = line.trimStart().split(/\s/u, 1)[0];
151
+ return command !== undefined && executableCommands.has(command);
152
+ }));
153
+ }
154
+ function hasRepositoryEvidence(body, repoRoot) {
155
+ const evidenceSource = body.replace(extractSection(body, 'Scope'), '');
156
+ if (/\b[0-9a-f]{40}:[^\s`]+/iu.test(evidenceSource) || /\/blob\/[0-9a-f]{40}\//iu.test(evidenceSource)) {
157
+ return true;
158
+ }
159
+ return extractInlineCode(evidenceSource).some((candidate) => isRepositoryPath(candidate, repoRoot));
160
+ }
161
+ function hasSubstantiveOutcome(body) {
162
+ const outcome = extractSection(body, 'Outcome');
163
+ const problem = extractSection(body, 'Problem');
164
+ return [outcome, problem].some((section) => stripMarkdown(section).length >= 24);
165
+ }
166
+ function hasUnresolvedMarker(body) {
167
+ return /\b(?:TODO|TBD|policy choice|architectural fork)\b/iu.test(body);
168
+ }
169
+ function isAggregateScope(scopePath) {
170
+ const normalized = normalizeScopePath(scopePath);
171
+ if (normalized === null)
172
+ return false;
173
+ return normalized.length === 0 || ['.', 'apps', 'packages', 'plugins'].includes(normalized);
174
+ }
175
+ function isRepositoryPath(candidate, repoRoot) {
176
+ const path = normalizeScopePath(candidate);
177
+ if (path === null)
178
+ return false;
179
+ const resolved = normalize(join(repoRoot, path));
180
+ const normalizedRoot = normalize(repoRoot);
181
+ return (resolved === normalizedRoot || resolved.startsWith(`${normalizedRoot}/`)) && existsSync(resolved);
182
+ }
183
+ function normalizeScopePath(candidate) {
184
+ const raw = candidate.replace(/(?::\d+(?::\d+)?)$/u, '').replace(/\\/gu, '/');
185
+ if (raw.startsWith('/') || raw.split('/').includes('..'))
186
+ return null;
187
+ return raw
188
+ .split('/')
189
+ .filter((part) => part.length > 0 && part !== '.')
190
+ .join('/');
191
+ }
192
+ function readMarkdownHeading(line) {
193
+ let index = 0;
194
+ while (line.charAt(index) === ' ' && index < 4) {
195
+ index += 1;
196
+ }
197
+ if (index > 3 || line.charAt(index) !== '#') {
198
+ return null;
199
+ }
200
+ let markerCount = 0;
201
+ while (line.charAt(index + markerCount) === '#' && markerCount < 7) {
202
+ markerCount += 1;
203
+ }
204
+ if (markerCount === 0 || markerCount > 6 || ![' ', '\t'].includes(line.charAt(index + markerCount))) {
205
+ return null;
206
+ }
207
+ return line.slice(index + markerCount).trim();
208
+ }
209
+ function requiresRepositoryEvidence({ body, scope }) {
210
+ const ownershipRoots = new Set(scope
211
+ .map((scopePath) => normalizeScopePath(scopePath))
212
+ .filter((scopePath) => scopePath !== null)
213
+ .map((scopePath) => scopePath.split('/').filter(Boolean).slice(0, 2).join('/'))
214
+ .filter(Boolean));
215
+ return (scope.some(isAggregateScope) ||
216
+ ownershipRoots.size > 1 ||
217
+ /\b(?:migration|production|data|contract-sensitive)\b/iu.test(body));
218
+ }
219
+ function stripMarkdown(value) {
220
+ return value
221
+ .replace(/[`*_>#-]/gu, '')
222
+ .replace(/\s+/gu, ' ')
223
+ .trim();
224
+ }
225
+ //# sourceMappingURL=agentReadinessService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agentReadinessService.js","sourceRoot":"","sources":["../../src/services/agentReadinessService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ declare const RuntimeAdmissionSchema: z.ZodObject<{
3
+ admissionId: z.ZodString;
4
+ capability: z.ZodObject<{
5
+ cli: z.ZodNumber;
6
+ mcp: z.ZodNumber;
7
+ }, z.core.$strict>;
8
+ cliPath: z.ZodString;
9
+ cliSha256: z.ZodString;
10
+ configDigest: z.ZodString;
11
+ mcpLauncherPath: z.ZodString;
12
+ mcpLauncherSha256: z.ZodString;
13
+ packageDistSha256: z.ZodString;
14
+ packageManifestSha256: z.ZodString;
15
+ packageVersion: z.ZodString;
16
+ pluginManifestPath: z.ZodString;
17
+ pluginManifestSha256: z.ZodString;
18
+ repository: z.ZodString;
19
+ schemaVersion: z.ZodLiteral<"ai-delivery.runtime-admission@1">;
20
+ sourceArchiveSha256: z.ZodString;
21
+ sourceCommit: z.ZodString;
22
+ }, z.core.$strict>;
23
+ export type RuntimeAdmission = z.infer<typeof RuntimeAdmissionSchema>;
24
+ /**
25
+ * An installer-owned, private, immutable admission binds the actual CLI and MCP
26
+ * bytes to one source archive and the consuming repository configuration.
27
+ * Neither invocation surface can refresh its own admission during a mutation.
28
+ */
29
+ export declare function assertDeliveryRuntimeAdmitted(input: {
30
+ repoRoot: string;
31
+ runtimeEntryPath?: string;
32
+ }): RuntimeAdmission;
33
+ export {};
@@ -0,0 +1,122 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, readdirSync, realpathSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { z } from 'zod';
5
+ import { loadDeliveryConfig } from '../config/deliveryConfig.js';
6
+ import { assertPrivateFile } from '../delivery/common.js';
7
+ import { digestValue } from '../delivery/index.js';
8
+ import { DeliveryError } from '../errors.js';
9
+ import { gitCommonDir, gitRoot } from '../git.js';
10
+ const Sha256 = z.string().regex(/^sha256:[a-f0-9]{64}$/u);
11
+ const CAPABILITY = 1;
12
+ const RuntimeAdmissionSchema = z
13
+ .object({
14
+ admissionId: Sha256,
15
+ capability: z.object({ cli: z.number().int().positive(), mcp: z.number().int().positive() }).strict(),
16
+ cliPath: z.string().min(1),
17
+ cliSha256: Sha256,
18
+ configDigest: Sha256,
19
+ mcpLauncherPath: z.string().min(1),
20
+ mcpLauncherSha256: Sha256,
21
+ packageDistSha256: Sha256,
22
+ packageManifestSha256: Sha256,
23
+ packageVersion: z.string().min(1),
24
+ pluginManifestPath: z.string().min(1),
25
+ pluginManifestSha256: Sha256,
26
+ repository: z.string().min(1),
27
+ schemaVersion: z.literal('ai-delivery.runtime-admission@1'),
28
+ sourceArchiveSha256: Sha256,
29
+ sourceCommit: z.string().regex(/^[a-f0-9]{40}$/u),
30
+ })
31
+ .strict()
32
+ .superRefine((value, context) => {
33
+ const { admissionId, ...content } = value;
34
+ if (admissionId !== digestValue(content)) {
35
+ context.addIssue({ code: 'custom', message: 'Runtime admission identity is invalid.' });
36
+ }
37
+ });
38
+ function fileDigest(path) {
39
+ return `sha256:${createHash('sha256').update(readFileSync(path)).digest('hex')}`;
40
+ }
41
+ function directoryDigest(path) {
42
+ const entries = [];
43
+ const visit = (directory, relative) => {
44
+ for (const entry of readdirSync(directory, { withFileTypes: true }).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0)) {
45
+ const child = join(directory, entry.name);
46
+ const childRelative = relative ? `${relative}/${entry.name}` : entry.name;
47
+ if (entry.isDirectory())
48
+ visit(child, childRelative);
49
+ else if (entry.isFile())
50
+ entries.push({ path: childRelative, sha256: fileDigest(child) });
51
+ else
52
+ throw new DeliveryError('Runtime distribution contains an unsupported link or file type.');
53
+ }
54
+ };
55
+ visit(path, '');
56
+ if (entries.length === 0)
57
+ throw new DeliveryError('Runtime distribution is empty.');
58
+ return digestValue(entries);
59
+ }
60
+ function manifest(path) {
61
+ const value = JSON.parse(readFileSync(path, 'utf8'));
62
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
63
+ throw new DeliveryError('Runtime package or plugin manifest is invalid.');
64
+ }
65
+ return value;
66
+ }
67
+ /**
68
+ * An installer-owned, private, immutable admission binds the actual CLI and MCP
69
+ * bytes to one source archive and the consuming repository configuration.
70
+ * Neither invocation surface can refresh its own admission during a mutation.
71
+ */
72
+ export function assertDeliveryRuntimeAdmitted(input) {
73
+ const root = gitRoot(input.repoRoot);
74
+ const loaded = loadDeliveryConfig(root);
75
+ const path = join(gitCommonDir(root), 'ai-delivery', 'runtime-admission.json');
76
+ let admission;
77
+ try {
78
+ admission = RuntimeAdmissionSchema.parse(JSON.parse(assertPrivateFile(path).toString('utf8')));
79
+ }
80
+ catch {
81
+ throw new DeliveryError('Missing or invalid private ai-delivery runtime admission; refresh the installed binding before lifecycle mutation.');
82
+ }
83
+ const entry = input.runtimeEntryPath ?? process.argv[1];
84
+ if (!entry)
85
+ throw new DeliveryError('Running ai-delivery entry path is unavailable.');
86
+ try {
87
+ const cliPath = realpathSync(entry);
88
+ const launcherPath = realpathSync(admission.mcpLauncherPath);
89
+ const pluginManifestPath = realpathSync(admission.pluginManifestPath);
90
+ const distPath = dirname(cliPath);
91
+ const packageManifestPath = join(dirname(distPath), 'package.json');
92
+ const packageManifest = manifest(packageManifestPath);
93
+ const pluginManifest = manifest(pluginManifestPath);
94
+ if (cliPath !== admission.cliPath ||
95
+ launcherPath !== admission.mcpLauncherPath ||
96
+ pluginManifestPath !== admission.pluginManifestPath ||
97
+ realpathSync(join(dirname(dirname(launcherPath)), '.claude-plugin', 'plugin.json')) !== pluginManifestPath ||
98
+ fileDigest(cliPath) !== admission.cliSha256 ||
99
+ fileDigest(launcherPath) !== admission.mcpLauncherSha256 ||
100
+ fileDigest(pluginManifestPath) !== admission.pluginManifestSha256 ||
101
+ fileDigest(packageManifestPath) !== admission.packageManifestSha256 ||
102
+ directoryDigest(distPath) !== admission.packageDistSha256 ||
103
+ admission.configDigest !== loaded.configDigest ||
104
+ admission.repository !== loaded.config.repository ||
105
+ admission.capability.cli !== CAPABILITY ||
106
+ admission.capability.mcp !== CAPABILITY ||
107
+ packageManifest.name !== '@aviaratech/ai-delivery' ||
108
+ packageManifest.version !== admission.packageVersion ||
109
+ pluginManifest.name !== 'ai-delivery' ||
110
+ pluginManifest.packageVersion !== admission.packageVersion ||
111
+ pluginManifest.deliveryCapabilityVersion !== CAPABILITY) {
112
+ throw new DeliveryError('Installed CLI/MCP source, capability or repository admission disagrees with the running delivery contract.');
113
+ }
114
+ }
115
+ catch (error) {
116
+ if (error instanceof DeliveryError)
117
+ throw error;
118
+ throw new DeliveryError('Installed CLI/MCP source or capability readback is unavailable.');
119
+ }
120
+ return admission;
121
+ }
122
+ //# sourceMappingURL=deliveryAdmission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deliveryAdmission.js","sourceRoot":"","sources":["../../src/services/deliveryAdmission.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAC1D,MAAM,UAAU,GAAG,CAAC,CAAC;AACrB,MAAM,sBAAsB,GAAG,CAAC;KAC7B,MAAM,CAAC;IACN,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACrG,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,MAAM;IACpB,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAClC,iBAAiB,EAAE,MAAM;IACzB,iBAAiB,EAAE,MAAM;IACzB,qBAAqB,EAAE,MAAM;IAC7B,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,kBAAkB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,oBAAoB,EAAE,MAAM;IAC5B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7B,aAAa,EAAE,CAAC,CAAC,OAAO,CAAC,iCAAiC,CAAC;IAC3D,mBAAmB,EAAE,MAAM;IAC3B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC;CAClD,CAAC;KACD,MAAM,EAAE;KACR,WAAW,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IAC9B,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC;IAC1C,IAAI,WAAW,KAAK,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;QACzC,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC,CAAC,CAAC;AAIL,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;AACnF,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,MAAM,OAAO,GAAuC,EAAE,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,SAAiB,EAAE,QAAgB,EAAQ,EAAE;QAC1D,KAAK,MAAM,KAAK,IAAI,WAAW,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACvF,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAC7D,EAAE,CAAC;YACF,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,aAAa,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;YAC1E,IAAI,KAAK,CAAC,WAAW,EAAE;gBAAE,KAAK,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;iBAChD,IAAI,KAAK,CAAC,MAAM,EAAE;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;;gBACrF,MAAM,IAAI,aAAa,CAAC,iEAAiE,CAAC,CAAC;QAClG,CAAC;IACH,CAAC,CAAC;IACF,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,aAAa,CAAC,gCAAgC,CAAC,CAAC;IACpF,OAAO,WAAW,CAAC,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAY,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAY,CAAC;IACzE,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,aAAa,CAAC,gDAAgD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAgC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,6BAA6B,CAAC,KAG7C;IACC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,aAAa,EAAE,wBAAwB,CAAC,CAAC;IAC/E,IAAI,SAA2B,CAAC;IAChC,IAAI,CAAC;QACH,SAAS,GAAG,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAY,CAAC,CAAC;IAC5G,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,aAAa,CACrB,oHAAoH,CACrH,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxD,IAAI,CAAC,KAAK;QAAE,MAAM,IAAI,aAAa,CAAC,gDAAgD,CAAC,CAAC;IACtF,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;QACpC,MAAM,YAAY,GAAG,YAAY,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;QAC7D,MAAM,kBAAkB,GAAG,YAAY,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC;QACtE,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;QAClC,MAAM,mBAAmB,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;QACpE,MAAM,eAAe,GAAG,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QACtD,MAAM,cAAc,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAAC;QACpD,IACE,OAAO,KAAK,SAAS,CAAC,OAAO;YAC7B,YAAY,KAAK,SAAS,CAAC,eAAe;YAC1C,kBAAkB,KAAK,SAAS,CAAC,kBAAkB;YACnD,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,gBAAgB,EAAE,aAAa,CAAC,CAAC,KAAK,kBAAkB;YAC1G,UAAU,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,SAAS;YAC3C,UAAU,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,iBAAiB;YACxD,UAAU,CAAC,kBAAkB,CAAC,KAAK,SAAS,CAAC,oBAAoB;YACjE,UAAU,CAAC,mBAAmB,CAAC,KAAK,SAAS,CAAC,qBAAqB;YACnE,eAAe,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,iBAAiB;YACzD,SAAS,CAAC,YAAY,KAAK,MAAM,CAAC,YAAY;YAC9C,SAAS,CAAC,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU;YACjD,SAAS,CAAC,UAAU,CAAC,GAAG,KAAK,UAAU;YACvC,SAAS,CAAC,UAAU,CAAC,GAAG,KAAK,UAAU;YACvC,eAAe,CAAC,IAAI,KAAK,yBAAyB;YAClD,eAAe,CAAC,OAAO,KAAK,SAAS,CAAC,cAAc;YACpD,cAAc,CAAC,IAAI,KAAK,aAAa;YACrC,cAAc,CAAC,cAAc,KAAK,SAAS,CAAC,cAAc;YAC1D,cAAc,CAAC,yBAAyB,KAAK,UAAU,EACvD,CAAC;YACD,MAAM,IAAI,aAAa,CACrB,4GAA4G,CAC7G,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,aAAa;YAAE,MAAM,KAAK,CAAC;QAChD,MAAM,IAAI,aAAa,CAAC,iEAAiE,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,47 @@
1
+ import * as z from 'zod/v4';
2
+ declare const DeliveryRecordSchema: z.ZodObject<{
3
+ blockerTimeMs: z.ZodNullable<z.ZodNumber>;
4
+ cycleTimeMs: z.ZodNumber;
5
+ firstPassApproved: z.ZodNullable<z.ZodBoolean>;
6
+ issueNumber: z.ZodNumber;
7
+ mergedAt: z.ZodISODateTime;
8
+ mergeSha: z.ZodString;
9
+ points: z.ZodNumber;
10
+ recordedAt: z.ZodISODateTime;
11
+ repository: z.ZodString;
12
+ reviewRounds: z.ZodNullable<z.ZodNumber>;
13
+ schemaVersion: z.ZodLiteral<"ai-delivery.delivery-record@1">;
14
+ terminalCleanupAt: z.ZodISODateTime;
15
+ }, z.core.$strict>;
16
+ export type DeliveryRecord = z.infer<typeof DeliveryRecordSchema>;
17
+ export interface PointBucketStatistics {
18
+ blockerMeasurementCount: number;
19
+ deliveries: number;
20
+ firstPassRate: null | number;
21
+ meanReviewRounds: null | number;
22
+ medianBlockerTimeMs: null | number;
23
+ medianCycleTimeMs: null | number;
24
+ points: DeliveryRecord['points'];
25
+ }
26
+ export interface VelocityReport {
27
+ planningCapacityPoints: number;
28
+ pointBuckets: PointBucketStatistics[];
29
+ rollingFourWeekMedianPoints: number;
30
+ schemaVersion: 'ai-delivery.velocity-report@1';
31
+ weeks: {
32
+ end: string;
33
+ mergedPoints: number;
34
+ start: string;
35
+ }[];
36
+ }
37
+ export declare function buildVelocityReport(records: readonly DeliveryRecord[], now?: Date, configuredPoints?: readonly number[]): VelocityReport;
38
+ export declare function getDeliveryRecords(projectRoot?: string): DeliveryRecord[];
39
+ export declare function recordMergedDeliveries(candidates: readonly DeliveryRecord[], projectRoot?: string): Promise<{
40
+ records: DeliveryRecord[];
41
+ state: 'already-recorded' | 'recorded';
42
+ }>;
43
+ export declare function recordMergedDelivery(candidate: DeliveryRecord, projectRoot?: string): Promise<{
44
+ record: DeliveryRecord;
45
+ state: 'already-recorded' | 'recorded';
46
+ }>;
47
+ export {};
@@ -0,0 +1,199 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import * as z from 'zod/v4';
4
+ import { gitCommonDir } from '../git.js';
5
+ import { ensurePrivateDirectoryDurably, writePrivateJsonFileAtomically } from '../utils/atomicJson.js';
6
+ import { DeliveryError } from '../errors.js';
7
+ import { withLock } from '../utils/lockfile.js';
8
+ const deliveryRecordsPath = (root) => join(gitCommonDir(root), 'ai-delivery', 'deliveries.json');
9
+ const MS_PER_WEEK = 7 * 24 * 60 * 60 * 1000;
10
+ const DeliveryRecordSchema = z
11
+ .object({
12
+ blockerTimeMs: z.number().int().nonnegative().nullable(),
13
+ cycleTimeMs: z.number().int().nonnegative(),
14
+ firstPassApproved: z.boolean().nullable(),
15
+ issueNumber: z.number().int().positive(),
16
+ mergedAt: z.iso.datetime(),
17
+ mergeSha: z.string().regex(/^[0-9a-f]{40}$/u),
18
+ points: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
19
+ recordedAt: z.iso.datetime(),
20
+ repository: z.string().regex(/^[^/\s]+\/[^/\s]+$/u),
21
+ reviewRounds: z.number().int().positive().nullable(),
22
+ schemaVersion: z.literal('ai-delivery.delivery-record@1'),
23
+ terminalCleanupAt: z.iso.datetime(),
24
+ })
25
+ .strict()
26
+ .superRefine((record, context) => {
27
+ if (Date.parse(record.terminalCleanupAt) < Date.parse(record.mergedAt)) {
28
+ context.addIssue({
29
+ code: 'custom',
30
+ message: 'Terminal cleanup cannot precede the merge.',
31
+ path: ['terminalCleanupAt'],
32
+ });
33
+ }
34
+ if (Date.parse(record.recordedAt) < Date.parse(record.terminalCleanupAt)) {
35
+ context.addIssue({
36
+ code: 'custom',
37
+ message: 'Delivery recording cannot precede terminal cleanup.',
38
+ path: ['recordedAt'],
39
+ });
40
+ }
41
+ });
42
+ const DeliveryRecordFileSchema = z
43
+ .object({
44
+ records: z.array(DeliveryRecordSchema),
45
+ schemaVersion: z.literal('ai-delivery.delivery-records@1'),
46
+ })
47
+ .strict();
48
+ export function buildVelocityReport(records, now = new Date(), configuredPoints = []) {
49
+ if (!Number.isFinite(now.getTime())) {
50
+ throw new DeliveryError('Velocity report requires a valid observation time.');
51
+ }
52
+ const endMs = now.getTime();
53
+ const startMs = endMs - 4 * MS_PER_WEEK;
54
+ const selected = records.filter((record) => {
55
+ const mergedMs = Date.parse(record.mergedAt);
56
+ return mergedMs >= startMs && mergedMs < endMs;
57
+ });
58
+ const weeks = Array.from({ length: 4 }, (_, index) => {
59
+ const weekStart = startMs + index * MS_PER_WEEK;
60
+ const weekEnd = weekStart + MS_PER_WEEK;
61
+ return {
62
+ end: new Date(weekEnd).toISOString(),
63
+ mergedPoints: selected
64
+ .filter((record) => {
65
+ const mergedMs = Date.parse(record.mergedAt);
66
+ return mergedMs >= weekStart && mergedMs < weekEnd;
67
+ })
68
+ .reduce((sum, record) => sum + record.points, 0),
69
+ start: new Date(weekStart).toISOString(),
70
+ };
71
+ });
72
+ const rollingFourWeekMedianPoints = median(weeks.map((week) => week.mergedPoints)) ?? 0;
73
+ return {
74
+ planningCapacityPoints: Math.floor(rollingFourWeekMedianPoints * 0.8),
75
+ pointBuckets: [...new Set([...configuredPoints, ...selected.map((record) => record.points)])]
76
+ .sort((left, right) => left - right)
77
+ .map((points) => pointBucket(points, selected)),
78
+ rollingFourWeekMedianPoints,
79
+ schemaVersion: 'ai-delivery.velocity-report@1',
80
+ weeks,
81
+ };
82
+ }
83
+ export function getDeliveryRecords(projectRoot = process.cwd()) {
84
+ const path = deliveryRecordsPath(projectRoot);
85
+ if (!existsSync(path)) {
86
+ return [];
87
+ }
88
+ try {
89
+ return DeliveryRecordFileSchema.parse(JSON.parse(readFileSync(path, 'utf8'))).records;
90
+ }
91
+ catch (error) {
92
+ throw new DeliveryError(`Delivery record store is invalid: ${error instanceof Error ? error.message : String(error)}`);
93
+ }
94
+ }
95
+ export async function recordMergedDeliveries(candidates, projectRoot = process.cwd()) {
96
+ if (candidates.length === 0) {
97
+ throw new DeliveryError('Delivery recording requires at least one merged delivery.');
98
+ }
99
+ const candidateRecords = candidates.map((candidate) => DeliveryRecordSchema.parse(candidate));
100
+ ensurePrivateDirectoryDurably(join(gitCommonDir(projectRoot), 'ai-delivery'));
101
+ return await withLock(join(gitCommonDir(projectRoot), 'ai-delivery'), {
102
+ operation: () => {
103
+ const records = getDeliveryRecords(projectRoot);
104
+ const pending = [];
105
+ for (const candidate of candidateRecords) {
106
+ const existing = [...records, ...pending].find((value) => sameDeliveryIdentity(value, candidate));
107
+ if (existing !== undefined && !sameImmutableFacts(existing, candidate)) {
108
+ throw new DeliveryError('Delivery facts conflict with the existing immutable delivery record.');
109
+ }
110
+ if (existing === undefined) {
111
+ pending.push(candidate);
112
+ }
113
+ }
114
+ if (pending.length > 0) {
115
+ writePrivateJsonFileAtomically(deliveryRecordsPath(projectRoot), {
116
+ records: [...records, ...pending].sort(compareDeliveryRecords),
117
+ schemaVersion: 'ai-delivery.delivery-records@1',
118
+ });
119
+ }
120
+ const readback = getDeliveryRecords(projectRoot);
121
+ const persisted = candidateRecords.map((candidate) => {
122
+ const match = readback.find((value) => sameDeliveryIdentity(value, candidate));
123
+ if (match === undefined || !sameImmutableFacts(match, candidate)) {
124
+ throw new DeliveryError('Delivery record readback did not match the merged delivery.');
125
+ }
126
+ return match;
127
+ });
128
+ return {
129
+ records: persisted,
130
+ state: pending.length === 0 ? 'already-recorded' : 'recorded',
131
+ };
132
+ },
133
+ projectRoot,
134
+ timeout: 5000,
135
+ });
136
+ }
137
+ export async function recordMergedDelivery(candidate, projectRoot = process.cwd()) {
138
+ const result = await recordMergedDeliveries([candidate], projectRoot);
139
+ const [record] = result.records;
140
+ if (record === undefined) {
141
+ throw new DeliveryError('Delivery record batch did not return the merged delivery.');
142
+ }
143
+ return { record, state: result.state };
144
+ }
145
+ function compareDeliveryRecords(left, right) {
146
+ return (left.repository.localeCompare(right.repository) ||
147
+ left.issueNumber - right.issueNumber ||
148
+ left.mergeSha.localeCompare(right.mergeSha));
149
+ }
150
+ function median(values) {
151
+ if (values.length === 0) {
152
+ return null;
153
+ }
154
+ const sorted = [...values].sort((left, right) => left - right);
155
+ const middle = Math.floor(sorted.length / 2);
156
+ const upper = sorted[middle];
157
+ if (upper === undefined) {
158
+ return null;
159
+ }
160
+ if (sorted.length % 2 === 1) {
161
+ return upper;
162
+ }
163
+ const lower = sorted[middle - 1];
164
+ return lower === undefined ? upper : (lower + upper) / 2;
165
+ }
166
+ function pointBucket(points, records) {
167
+ const matches = records.filter((record) => record.points === points);
168
+ const blockerMeasurements = matches.flatMap((record) => record.blockerTimeMs === null ? [] : [record.blockerTimeMs]);
169
+ const firstPassMeasurements = matches.filter((record) => record.firstPassApproved !== null);
170
+ const reviewMeasurements = matches.flatMap((record) => (record.reviewRounds === null ? [] : [record.reviewRounds]));
171
+ return {
172
+ blockerMeasurementCount: blockerMeasurements.length,
173
+ deliveries: matches.length,
174
+ firstPassRate: firstPassMeasurements.length === 0
175
+ ? null
176
+ : firstPassMeasurements.filter((record) => record.firstPassApproved).length / firstPassMeasurements.length,
177
+ meanReviewRounds: reviewMeasurements.length === 0
178
+ ? null
179
+ : reviewMeasurements.reduce((sum, rounds) => sum + rounds, 0) / reviewMeasurements.length,
180
+ medianBlockerTimeMs: median(blockerMeasurements),
181
+ medianCycleTimeMs: median(matches.map((record) => record.cycleTimeMs)),
182
+ points,
183
+ };
184
+ }
185
+ function sameDeliveryIdentity(left, right) {
186
+ return (left.repository === right.repository && left.issueNumber === right.issueNumber && left.mergeSha === right.mergeSha);
187
+ }
188
+ function sameImmutableFacts(left, right) {
189
+ return (left.blockerTimeMs === right.blockerTimeMs &&
190
+ left.cycleTimeMs === right.cycleTimeMs &&
191
+ left.firstPassApproved === right.firstPassApproved &&
192
+ left.issueNumber === right.issueNumber &&
193
+ left.mergeSha === right.mergeSha &&
194
+ left.mergedAt === right.mergedAt &&
195
+ left.points === right.points &&
196
+ left.repository === right.repository &&
197
+ left.reviewRounds === right.reviewRounds);
198
+ }
199
+ //# sourceMappingURL=deliveryRecordService.js.map