@oscharko-dev/keiko-contracts 0.2.7 → 0.2.9

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 (108) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/bff-wire.d.ts +86 -19
  3. package/dist/bff-wire.d.ts.map +1 -1
  4. package/dist/command-runner.d.ts +81 -0
  5. package/dist/command-runner.d.ts.map +1 -0
  6. package/dist/command-runner.js +209 -0
  7. package/dist/container-runtime.d.ts +125 -0
  8. package/dist/container-runtime.d.ts.map +1 -0
  9. package/dist/container-runtime.js +287 -0
  10. package/dist/context-engineering.js +2 -2
  11. package/dist/discussion-intelligence.d.ts +70 -0
  12. package/dist/discussion-intelligence.d.ts.map +1 -0
  13. package/dist/discussion-intelligence.js +440 -0
  14. package/dist/editor-agent-governance.d.ts +65 -0
  15. package/dist/editor-agent-governance.d.ts.map +1 -0
  16. package/dist/editor-agent-governance.js +180 -0
  17. package/dist/editor-agent.d.ts +29 -1
  18. package/dist/editor-agent.d.ts.map +1 -1
  19. package/dist/editor-agent.js +183 -6
  20. package/dist/editor-builtin-capabilities.d.ts +13 -0
  21. package/dist/editor-builtin-capabilities.d.ts.map +1 -0
  22. package/dist/editor-builtin-capabilities.js +135 -0
  23. package/dist/editor-language-mode-map.d.ts +11 -0
  24. package/dist/editor-language-mode-map.d.ts.map +1 -0
  25. package/dist/editor-language-mode-map.js +89 -0
  26. package/dist/editor-layout.d.ts +37 -0
  27. package/dist/editor-layout.d.ts.map +1 -1
  28. package/dist/editor-layout.js +125 -8
  29. package/dist/editor-workspace-path.d.ts +20 -0
  30. package/dist/editor-workspace-path.d.ts.map +1 -0
  31. package/dist/editor-workspace-path.js +133 -0
  32. package/dist/evidence.d.ts +3 -1
  33. package/dist/evidence.d.ts.map +1 -1
  34. package/dist/gateway.d.ts +72 -3
  35. package/dist/gateway.d.ts.map +1 -1
  36. package/dist/gateway.js +73 -3
  37. package/dist/git-commit-intent.d.ts +28 -0
  38. package/dist/git-commit-intent.d.ts.map +1 -0
  39. package/dist/git-commit-intent.js +155 -0
  40. package/dist/git-commit-policy.d.ts +29 -0
  41. package/dist/git-commit-policy.d.ts.map +1 -0
  42. package/dist/git-commit-policy.js +173 -0
  43. package/dist/git-delivery-action-sheet.d.ts +157 -0
  44. package/dist/git-delivery-action-sheet.d.ts.map +1 -0
  45. package/dist/git-delivery-action-sheet.js +430 -0
  46. package/dist/git-delivery-evidence.d.ts +92 -0
  47. package/dist/git-delivery-evidence.d.ts.map +1 -0
  48. package/dist/git-delivery-evidence.js +272 -0
  49. package/dist/git-delivery-policy.d.ts +40 -0
  50. package/dist/git-delivery-policy.d.ts.map +1 -0
  51. package/dist/git-delivery-policy.js +183 -0
  52. package/dist/git-delivery-provider.d.ts +53 -0
  53. package/dist/git-delivery-provider.d.ts.map +1 -0
  54. package/dist/git-delivery-provider.js +96 -0
  55. package/dist/git-delivery.d.ts +202 -0
  56. package/dist/git-delivery.d.ts.map +1 -0
  57. package/dist/git-delivery.js +410 -0
  58. package/dist/git-history.d.ts +27 -0
  59. package/dist/git-history.d.ts.map +1 -0
  60. package/dist/git-history.js +73 -0
  61. package/dist/git-merge.d.ts +67 -0
  62. package/dist/git-merge.d.ts.map +1 -0
  63. package/dist/git-merge.js +323 -0
  64. package/dist/git-pull-request.d.ts +112 -0
  65. package/dist/git-pull-request.d.ts.map +1 -0
  66. package/dist/git-pull-request.js +351 -0
  67. package/dist/git-repository-agent.d.ts +46 -0
  68. package/dist/git-repository-agent.d.ts.map +1 -0
  69. package/dist/git-repository-agent.js +198 -0
  70. package/dist/git-repository-summary.d.ts +54 -0
  71. package/dist/git-repository-summary.d.ts.map +1 -0
  72. package/dist/git-repository-summary.js +105 -0
  73. package/dist/git-repository.d.ts +60 -0
  74. package/dist/git-repository.d.ts.map +1 -0
  75. package/dist/git-repository.js +106 -0
  76. package/dist/git-sync.d.ts +49 -0
  77. package/dist/git-sync.d.ts.map +1 -0
  78. package/dist/git-sync.js +110 -0
  79. package/dist/index.d.ts +63 -9
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +33 -6
  82. package/dist/lsp-process.d.ts +38 -0
  83. package/dist/lsp-process.d.ts.map +1 -0
  84. package/dist/lsp-process.js +103 -0
  85. package/dist/prompt-enhancer-analyzer.d.ts.map +1 -1
  86. package/dist/prompt-enhancer-analyzer.js +198 -22
  87. package/dist/runtime-capabilities.d.ts +44 -0
  88. package/dist/runtime-capabilities.d.ts.map +1 -0
  89. package/dist/runtime-capabilities.js +141 -0
  90. package/dist/task-workspace.d.ts +302 -0
  91. package/dist/task-workspace.d.ts.map +1 -0
  92. package/dist/task-workspace.js +1236 -0
  93. package/dist/voice-action-intent.d.ts +86 -0
  94. package/dist/voice-action-intent.d.ts.map +1 -0
  95. package/dist/voice-action-intent.js +387 -0
  96. package/dist/voice-playback.d.ts +50 -0
  97. package/dist/voice-playback.d.ts.map +1 -0
  98. package/dist/voice-playback.js +240 -0
  99. package/dist/voice-protocol.d.ts +165 -0
  100. package/dist/voice-protocol.d.ts.map +1 -0
  101. package/dist/voice-protocol.js +312 -0
  102. package/dist/voice-transcript.d.ts +57 -0
  103. package/dist/voice-transcript.d.ts.map +1 -0
  104. package/dist/voice-transcript.js +221 -0
  105. package/package.json +5 -1
  106. package/dist/conversation-budget.d.ts +0 -37
  107. package/dist/conversation-budget.d.ts.map +0 -1
  108. package/dist/conversation-budget.js +0 -97
@@ -0,0 +1,272 @@
1
+ // Governed Git mutation EVIDENCE contracts (Issue #474, Epic #470). Ownership: the content-free,
2
+ // redacted-by-construction audit record produced for EVERY governed Git mutation attempt —
3
+ // succeeded, blocked, rejected, failed, recovery-required, or held for approval — plus the
4
+ // exportable audit packet that compliance and support workflows inspect without replaying raw logs
5
+ // or shell transcripts.
6
+ //
7
+ // This module is RETROSPECTIVE: it records what an attempt DID, after the #472 execution kernel ran
8
+ // its resolve -> preflight -> preview -> policy -> execute -> result lifecycle. It is disjoint from
9
+ // git-delivery.ts (the action model + risk + lifecycle envelope), git-delivery-policy.ts (the
10
+ // policy evaluator), git-delivery-provider.ts (provider-neutral state), and the PROSPECTIVE
11
+ // git-delivery-action-sheet.ts (the UI projection of what WOULD block and how to recover). It reuses
12
+ // the action sheet's recovery-action vocabulary and the core atom's recovery-strategy vocabulary
13
+ // rather than re-enumerating them, and adds only the missing retrospective axis: a three-way
14
+ // recovery DISPOSITION (retryable / user-fixable / policy-forbidden) that the prospective two-way
15
+ // GitDeliveryRemediationClass cannot express.
16
+ //
17
+ // Leaf-package rules (ADR-0019, ADR-0061): pure types, frozen const tables, and pure functions only.
18
+ // No IO, no clock, no crypto, no randomness. Relative imports end in ".js" and reference only the
19
+ // git-delivery sibling atoms — never a kernel (keiko-tools) type. The kernel's lifecycle-phase,
20
+ // status, and failure-category vocabularies are keiko-tools concerns; the keiko-tools evidence
21
+ // builder maps them onto the self-contained, mirrored vocabularies defined here so this contract
22
+ // stays a standalone, serialisable artifact.
23
+ //
24
+ // CONTENT-FREE invariant (AC2/AC5): records carry counts, flags, branch names, typed codes, and
25
+ // SHA-256 hashes ONLY. They never carry diff content, file paths, secrets, command strings, raw
26
+ // subprocess output, raw provider artifacts, or credential-bearing metadata. Remote identifiers,
27
+ // the provider-assigned external id, and the repository identity are stored as opaque SHA-256
28
+ // hashes; the approval token is recorded as a hash by the #471 contract and never appears here.
29
+ import { isGitDeliveryActionKind, isGitDeliveryBlockReason, isGitDeliveryEvidenceRef, isGitDeliveryExecutionErrorCode, isGitDeliveryExecutionOutcome, isGitDeliveryRecoveryStrategyHint, isGitDeliveryRiskClass, } from "./git-delivery.js";
30
+ import { isGitDeliveryRecoveryActionHint } from "./git-delivery-action-sheet.js";
31
+ // Pinned schema version. A breaking change adds a NEW literal member; this one is never mutated.
32
+ export const GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION = "1";
33
+ export const GIT_DELIVERY_EVIDENCE_OUTCOME_CLASSES = [
34
+ "succeeded",
35
+ "blocked",
36
+ "rejected",
37
+ "failed",
38
+ "recovery-required",
39
+ "approval-required",
40
+ ];
41
+ export const GIT_DELIVERY_RECOVERY_DISPOSITIONS = [
42
+ "retryable",
43
+ "user-fixable",
44
+ "policy-forbidden",
45
+ "none",
46
+ ];
47
+ export const GIT_DELIVERY_EVIDENCE_LIFECYCLE_PHASES = ["resolve", "preflight", "preview", "policy", "execute", "result"];
48
+ // ─── Deterministic recovery-disposition derivations (AC3) ────────────────────────────────────
49
+ // Total, exhaustive maps over the closed contract vocabularies. Adding a new execution error code or
50
+ // block reason forces a compile error here, so a disposition is never derived from a free-form
51
+ // message and a new code can never fall through to a silent default.
52
+ const RECOVERY_DISPOSITION_BY_EXECUTION_ERROR = {
53
+ // The remote refused the action: the operator must address the rejection (rebase, approval, etc.).
54
+ "provider-rejected": "user-fixable",
55
+ // The remote was unreachable: transient, safe to retry as-is.
56
+ "network-failure": "retryable",
57
+ // A merge/index conflict: the operator must resolve it before retrying.
58
+ conflict: "user-fixable",
59
+ // A stale precondition (e.g. non-fast-forward): the operator must re-resolve before retrying.
60
+ "precondition-failed": "user-fixable",
61
+ // A transient timeout: safe to retry.
62
+ timeout: "retryable",
63
+ // An internal fault: transient from the caller's view; safe to retry.
64
+ "internal-error": "retryable",
65
+ };
66
+ export function gitDeliveryRecoveryDispositionForExecutionError(code) {
67
+ return RECOVERY_DISPOSITION_BY_EXECUTION_ERROR[code];
68
+ }
69
+ const RECOVERY_DISPOSITION_BY_BLOCK_REASON = {
70
+ // A policy pack denied the action: forbidden until the policy changes.
71
+ "policy-pack-blocked": "policy-forbidden",
72
+ // The branch is protected: forbidden under current governance.
73
+ "protected-branch": "policy-forbidden",
74
+ // A required provider capability is absent: forbidden under the active environment.
75
+ "provider-capability-absent": "policy-forbidden",
76
+ // The approval grant expired: the operator can obtain a fresh approval.
77
+ "approval-expired": "user-fixable",
78
+ // The action exceeds the risk-class ceiling: forbidden until the ceiling changes.
79
+ "risk-class-ceiling": "policy-forbidden",
80
+ // Fail-closed deny with no applicable rule: forbidden until a rule permits it.
81
+ "no-applicable-rule": "policy-forbidden",
82
+ };
83
+ export function gitDeliveryRecoveryDispositionForBlockReason(reason) {
84
+ return RECOVERY_DISPOSITION_BY_BLOCK_REASON[reason];
85
+ }
86
+ // ─── Audit packet builder (AC4) ──────────────────────────────────────────────────────────────
87
+ // Pure aggregation of redacted records into the exportable packet. The honest, static limitations
88
+ // list states what the export does NOT overcome (the trust-boundary text mirrors EvidenceReport).
89
+ export const GIT_DELIVERY_AUDIT_PACKET_KNOWN_LIMITATIONS = [
90
+ "Records are content-free by construction: sensitive identifiers are hashed in the builder, and " +
91
+ "diff content, file paths, command output, and secrets are never recorded. The persist- and " +
92
+ "export-time redactors are a defence-in-depth backstop that scrubs secret-SHAPED strings and " +
93
+ "configured literals only — they do not redact arbitrary free-form text, so any new raw field " +
94
+ "must be hashed at the builder rather than relied upon to be redacted.",
95
+ "Remote identifiers, the provider external id, and the repository identity are stored as SHA-256 " +
96
+ "hashes that are irreversible to preimage; low-entropy inputs (e.g. a remote alias or a " +
97
+ "repository path) may remain confirmable by an operator who enumerates candidate values.",
98
+ "Local branch names and approver user ids are retained in clear text as deliberate governance " +
99
+ "provenance (which branch was acted on, who approved); they are not secrets and are not hashed.",
100
+ "Provider-side outcomes (merge-block reasons, remote rejections) are produced only by the " +
101
+ "provider execution slices; a local-only deployment records local-kernel outcomes.",
102
+ "Workflow correlation uses a hashed run id supplied by the caller; correlation across a server " +
103
+ "restart depends on the caller re-supplying a stable run id.",
104
+ ];
105
+ function emptyOutcomeClassCounts() {
106
+ return {
107
+ succeeded: 0,
108
+ blocked: 0,
109
+ rejected: 0,
110
+ failed: 0,
111
+ "recovery-required": 0,
112
+ "approval-required": 0,
113
+ };
114
+ }
115
+ function emptyDispositionCounts() {
116
+ return { retryable: 0, "user-fixable": 0, "policy-forbidden": 0, none: 0 };
117
+ }
118
+ export function buildGitDeliveryAuditPacket(records, generatedAtMs, extraLimitations = []) {
119
+ const outcomeClassCounts = emptyOutcomeClassCounts();
120
+ const recoveryDispositionCounts = emptyDispositionCounts();
121
+ for (const record of records) {
122
+ outcomeClassCounts[record.outcomeClass] += 1;
123
+ recoveryDispositionCounts[record.recovery.disposition] += 1;
124
+ }
125
+ return {
126
+ schemaVersion: GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION,
127
+ generatedAtMs,
128
+ recordCount: records.length,
129
+ records,
130
+ outcomeClassCounts,
131
+ recoveryDispositionCounts,
132
+ knownLimitations: [...GIT_DELIVERY_AUDIT_PACKET_KNOWN_LIMITATIONS, ...extraLimitations],
133
+ };
134
+ }
135
+ // ─── Local guard helpers ─────────────────────────────────────────────────────────────────────
136
+ function isString(value) {
137
+ return typeof value === "string";
138
+ }
139
+ function isFiniteNumber(value) {
140
+ return typeof value === "number" && Number.isFinite(value);
141
+ }
142
+ function isBoolean(value) {
143
+ return typeof value === "boolean";
144
+ }
145
+ function isStringArray(value) {
146
+ return Array.isArray(value) && value.every(isString);
147
+ }
148
+ function isRecord(value) {
149
+ return typeof value === "object" && value !== null && !Array.isArray(value);
150
+ }
151
+ function isUndefinedOr(check) {
152
+ return (v) => v === undefined || check(v);
153
+ }
154
+ function isInSet(set) {
155
+ return (v) => isString(v) && set.includes(v);
156
+ }
157
+ const POLICY_OUTCOMES = [
158
+ "allowed",
159
+ "blocked",
160
+ "approval-gated",
161
+ "constrained",
162
+ ];
163
+ // ─── Enum guards ───────────────────────────────────────────────────────────────────────────────
164
+ export const isGitDeliveryEvidenceOutcomeClass = isInSet(GIT_DELIVERY_EVIDENCE_OUTCOME_CLASSES);
165
+ export const isGitDeliveryRecoveryDisposition = isInSet(GIT_DELIVERY_RECOVERY_DISPOSITIONS);
166
+ export const isGitDeliveryEvidenceLifecyclePhase = isInSet(GIT_DELIVERY_EVIDENCE_LIFECYCLE_PHASES);
167
+ const isGitDeliveryPolicyOutcome = isInSet(POLICY_OUTCOMES);
168
+ // ─── Structural guards ───────────────────────────────────────────────────────────────────────
169
+ // Decomposed per sub-record so each guard stays well under the complexity ceiling and the on-read
170
+ // ledger can fail closed on any malformed record.
171
+ export function isGitDeliveryRecoveryMetadata(value) {
172
+ return (isRecord(value) &&
173
+ isGitDeliveryRecoveryDisposition(value.disposition) &&
174
+ isUndefinedOr(isGitDeliveryRecoveryActionHint)(value.actionHint) &&
175
+ isUndefinedOr(isGitDeliveryExecutionErrorCode)(value.executionErrorCode) &&
176
+ isUndefinedOr(isGitDeliveryBlockReason)(value.blockReason) &&
177
+ isUndefinedOr(isGitDeliveryRecoveryStrategyHint)(value.suggestedRecoveryStrategy));
178
+ }
179
+ function isEvidenceCorrelation(value) {
180
+ return (isRecord(value) &&
181
+ isString(value.workflowRunIdHash) &&
182
+ isString(value.actionId) &&
183
+ isUndefinedOr(isFiniteNumber)(value.attemptSequence));
184
+ }
185
+ function isEvidenceApproval(value) {
186
+ return (isRecord(value) &&
187
+ isBoolean(value.required) &&
188
+ isUndefinedOr(isString)(value.approvalTokenHash) &&
189
+ isUndefinedOr(isString)(value.approvedByUserId) &&
190
+ isUndefinedOr(isFiniteNumber)(value.approvedAtMs) &&
191
+ isUndefinedOr(isFiniteNumber)(value.expiresAtMs));
192
+ }
193
+ function isEvidencePreview(value) {
194
+ return (isRecord(value) &&
195
+ isUndefinedOr(isString)(value.affectedBranchName) &&
196
+ isUndefinedOr(isFiniteNumber)(value.estimatedFileCount) &&
197
+ isUndefinedOr(isFiniteNumber)(value.estimatedBytesDelta) &&
198
+ isBoolean(value.wouldCreateRemoteBranch) &&
199
+ isBoolean(value.wouldTriggerChecks));
200
+ }
201
+ function isEvidenceExecution(value) {
202
+ return (isRecord(value) &&
203
+ isGitDeliveryExecutionOutcome(value.outcome) &&
204
+ isFiniteNumber(value.durationMs) &&
205
+ isUndefinedOr(isGitDeliveryExecutionErrorCode)(value.errorCode) &&
206
+ isUndefinedOr(isFiniteNumber)(value.attemptedUnitCount) &&
207
+ isUndefinedOr(isFiniteNumber)(value.succeededUnitCount) &&
208
+ isUndefinedOr(isString)(value.externalIdHash));
209
+ }
210
+ function isEvidenceRepoContext(value) {
211
+ return (isRecord(value) &&
212
+ isUndefinedOr(isString)(value.repoIdHash) &&
213
+ isUndefinedOr(isString)(value.targetBranchName) &&
214
+ isUndefinedOr(isBoolean)(value.headDetached) &&
215
+ isUndefinedOr(isFiniteNumber)(value.stagedFileCount) &&
216
+ isUndefinedOr(isFiniteNumber)(value.unstagedFileCount) &&
217
+ isUndefinedOr(isFiniteNumber)(value.untrackedFileCount) &&
218
+ isUndefinedOr(isString)(value.remoteRefHash));
219
+ }
220
+ // Identity fields of a record (split from classification + nested checks to keep each guard within
221
+ // the complexity ceiling).
222
+ function hasValidEvidenceIdentity(value) {
223
+ return (value.schemaVersion === GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION &&
224
+ isString(value.evidenceId) &&
225
+ isGitDeliveryActionKind(value.actionKind) &&
226
+ isGitDeliveryRiskClass(value.riskClass) &&
227
+ isFiniteNumber(value.riskSeverity) &&
228
+ isFiniteNumber(value.recordedAtMs));
229
+ }
230
+ // Classification fields of a record.
231
+ function hasValidEvidenceClassification(value) {
232
+ return (isGitDeliveryEvidenceOutcomeClass(value.outcomeClass) &&
233
+ isGitDeliveryEvidenceLifecyclePhase(value.phaseReached) &&
234
+ isGitDeliveryPolicyOutcome(value.policyOutcome) &&
235
+ isUndefinedOr(isGitDeliveryBlockReason)(value.blockReason) &&
236
+ isUndefinedOr(isStringArray)(value.requiredApprovers));
237
+ }
238
+ // Nested-section fields of a record.
239
+ function hasValidEvidenceSections(value) {
240
+ return (isEvidenceCorrelation(value.correlation) &&
241
+ isEvidenceApproval(value.approval) &&
242
+ isUndefinedOr(isEvidencePreview)(value.preview) &&
243
+ isUndefinedOr(isEvidenceExecution)(value.execution) &&
244
+ isEvidenceRepoContext(value.repoContext) &&
245
+ isGitDeliveryRecoveryMetadata(value.recovery) &&
246
+ isUndefinedOr(isGitDeliveryEvidenceRef)(value.evidenceRef));
247
+ }
248
+ export function isGitDeliveryEvidenceRecord(value) {
249
+ return (isRecord(value) &&
250
+ hasValidEvidenceIdentity(value) &&
251
+ hasValidEvidenceClassification(value) &&
252
+ hasValidEvidenceSections(value));
253
+ }
254
+ function isOutcomeClassCounts(value) {
255
+ return (isRecord(value) &&
256
+ GIT_DELIVERY_EVIDENCE_OUTCOME_CLASSES.every((cls) => isFiniteNumber(value[cls])));
257
+ }
258
+ function isDispositionCounts(value) {
259
+ return (isRecord(value) && GIT_DELIVERY_RECOVERY_DISPOSITIONS.every((d) => isFiniteNumber(value[d])));
260
+ }
261
+ export function isGitDeliveryAuditPacket(value) {
262
+ return (isRecord(value) &&
263
+ value.schemaVersion === GIT_DELIVERY_EVIDENCE_SCHEMA_VERSION &&
264
+ isFiniteNumber(value.generatedAtMs) &&
265
+ isFiniteNumber(value.recordCount) &&
266
+ Array.isArray(value.records) &&
267
+ value.recordCount === value.records.length &&
268
+ value.records.every(isGitDeliveryEvidenceRecord) &&
269
+ isOutcomeClassCounts(value.outcomeClassCounts) &&
270
+ isDispositionCounts(value.recoveryDispositionCounts) &&
271
+ isStringArray(value.knownLimitations));
272
+ }
@@ -0,0 +1,40 @@
1
+ import type { GitDeliveryActionKind, GitDeliveryConstraint, GitDeliveryParseResult, GitDeliveryPolicyDecision, GitDeliveryProviderCapability } from "./git-delivery.js";
2
+ import { isGitDeliveryConstraint, isGitDeliveryProviderCapability } from "./git-delivery.js";
3
+ export declare const GIT_DELIVERY_POLICY_SCHEMA_VERSION: "1";
4
+ export type GitDeliveryRuleDecision = "allowed" | "blocked" | "approval-gated" | "constrained";
5
+ export declare const GIT_DELIVERY_RULE_DECISIONS: readonly GitDeliveryRuleDecision[];
6
+ export interface GitDeliveryPolicyRule {
7
+ readonly actionKind: GitDeliveryActionKind;
8
+ readonly decision: GitDeliveryRuleDecision;
9
+ readonly requiredApprovers?: readonly string[] | undefined;
10
+ readonly constraints?: readonly GitDeliveryConstraint[] | undefined;
11
+ }
12
+ export interface GitDeliveryDefaultRule {
13
+ readonly decision: GitDeliveryRuleDecision;
14
+ readonly requiredApprovers?: readonly string[] | undefined;
15
+ readonly constraints?: readonly GitDeliveryConstraint[] | undefined;
16
+ }
17
+ export interface GitDeliveryRepoPolicyPack {
18
+ readonly schemaVersion: typeof GIT_DELIVERY_POLICY_SCHEMA_VERSION;
19
+ readonly repoId: string;
20
+ readonly rules: readonly GitDeliveryPolicyRule[];
21
+ readonly defaultRule?: GitDeliveryDefaultRule | undefined;
22
+ }
23
+ export interface GitDeliveryOrgPolicyPack {
24
+ readonly schemaVersion: typeof GIT_DELIVERY_POLICY_SCHEMA_VERSION;
25
+ readonly orgId: string;
26
+ readonly rules: readonly GitDeliveryPolicyRule[];
27
+ readonly defaultRule?: GitDeliveryDefaultRule | undefined;
28
+ }
29
+ export interface GitDeliveryPolicyContext {
30
+ readonly actionKind: GitDeliveryActionKind;
31
+ readonly targetBranchName?: string | undefined;
32
+ readonly activeProviderCapabilities: readonly GitDeliveryProviderCapability[];
33
+ }
34
+ export declare function evaluateGitPolicy(orgPack: GitDeliveryOrgPolicyPack | undefined, repoPack: GitDeliveryRepoPolicyPack | undefined, context: GitDeliveryPolicyContext): GitDeliveryPolicyDecision;
35
+ export { isGitDeliveryConstraint, isGitDeliveryProviderCapability };
36
+ export declare function isGitDeliveryPolicyRule(value: unknown): value is GitDeliveryPolicyRule;
37
+ export declare function parseGitRepoPolicyPack(value: unknown): GitDeliveryParseResult<GitDeliveryRepoPolicyPack>;
38
+ export declare function parseGitOrgPolicyPack(value: unknown): GitDeliveryParseResult<GitDeliveryOrgPolicyPack>;
39
+ export declare function parseGitPolicyPack(value: unknown): GitDeliveryParseResult<GitDeliveryRepoPolicyPack | GitDeliveryOrgPolicyPack>;
40
+ //# sourceMappingURL=git-delivery-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-delivery-policy.d.ts","sourceRoot":"","sources":["../src/git-delivery-policy.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,qBAAqB,EACrB,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,uBAAuB,EACvB,+BAA+B,EAChC,MAAM,mBAAmB,CAAC;AAE3B,eAAO,MAAM,kCAAkC,EAAG,GAAY,CAAC;AAI/D,MAAM,MAAM,uBAAuB,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,GAAG,aAAa,CAAC;AAE/F,eAAO,MAAM,2BAA2B,EAAE,SAAS,uBAAuB,EAKhE,CAAC;AAEX,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAG3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAE3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,CAAC;CACrE;AAID,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC3D,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,CAAC;CACrE;AAID,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,aAAa,EAAE,OAAO,kCAAkC,CAAC;IAClE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,aAAa,EAAE,OAAO,kCAAkC,CAAC;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAC;CAC3D;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC;IAC3C,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,0BAA0B,EAAE,SAAS,6BAA6B,EAAE,CAAC;CAC/E;AA2ED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,wBAAwB,GAAG,SAAS,EAC7C,QAAQ,EAAE,yBAAyB,GAAG,SAAS,EAC/C,OAAO,EAAE,wBAAwB,GAChC,yBAAyB,CAI3B;AAgCD,OAAO,EAAE,uBAAuB,EAAE,+BAA+B,EAAE,CAAC;AAkBpE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAOtF;AAsCD,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,sBAAsB,CAAC,yBAAyB,CAAC,CAYnD;AAED,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,sBAAsB,CAAC,wBAAwB,CAAC,CAYlD;AAGD,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,GACb,sBAAsB,CAAC,yBAAyB,GAAG,wBAAwB,CAAC,CAW9E"}
@@ -0,0 +1,183 @@
1
+ // Policy-pack structures and deterministic pure evaluator for governed Git delivery
2
+ // (Issue #471, Epic #470). Ownership: git-delivery-policy domain.
3
+ // Leaf-package rules (ADR-0019): pure types and frozen const tables only.
4
+ // No IO, no clock, no randomness, no crypto. The evaluator is a pure function:
5
+ // same inputs always produce the same decision. Callers (keiko-server, keiko-workflows)
6
+ // assemble packs from storage BEFORE calling evaluateGitPolicy.
7
+ //
8
+ // Imports ONLY from ./git-delivery.js (action kinds, risk class, constraint union, policy decision,
9
+ // provider capability, parse result). No imports from git-delivery-provider.ts — one-directional DAG.
10
+ import { isGitDeliveryActionKind, isGitDeliveryConstraint, isGitDeliveryProviderCapability, } from "./git-delivery.js";
11
+ export const GIT_DELIVERY_POLICY_SCHEMA_VERSION = "1";
12
+ export const GIT_DELIVERY_RULE_DECISIONS = [
13
+ "allowed",
14
+ "blocked",
15
+ "approval-gated",
16
+ "constrained",
17
+ ];
18
+ function resolveRuleToLevel(rule) {
19
+ if (rule.decision === "allowed") {
20
+ return { kind: "allowed" };
21
+ }
22
+ if (rule.decision === "blocked") {
23
+ return { kind: "blocked" };
24
+ }
25
+ if (rule.decision === "approval-gated") {
26
+ return { kind: "approval-gated", approvers: rule.requiredApprovers ?? [] };
27
+ }
28
+ return { kind: "constrained", constraints: rule.constraints ?? [] };
29
+ }
30
+ function resolveLevel(pack, context) {
31
+ if (pack === undefined) {
32
+ return { kind: "none" };
33
+ }
34
+ const rule = pack.rules.find((candidate) => candidate.actionKind === context.actionKind);
35
+ if (rule !== undefined) {
36
+ return resolveRuleToLevel(rule);
37
+ }
38
+ if (pack.defaultRule !== undefined) {
39
+ return resolveRuleToLevel(pack.defaultRule);
40
+ }
41
+ return { kind: "none" };
42
+ }
43
+ function unionConstraints(org, repo) {
44
+ const orgConstraints = org.kind === "constrained" ? org.constraints : [];
45
+ const repoConstraints = repo.kind === "constrained" ? repo.constraints : [];
46
+ return [...orgConstraints, ...repoConstraints];
47
+ }
48
+ // Combine org (O) and repo (R) decisions by a total, deterministic precedence matrix (first match
49
+ // wins). Either level can tighten; org tightening dominates a repo loosen; both `none` fails closed.
50
+ function combineDecisions(org, repo) {
51
+ if (org.kind === "blocked" || repo.kind === "blocked") {
52
+ return { outcome: "blocked", reason: "policy-pack-blocked" };
53
+ }
54
+ if (org.kind === "approval-gated") {
55
+ return { outcome: "approval-gated", requiredApprovers: org.approvers };
56
+ }
57
+ if (repo.kind === "approval-gated") {
58
+ return { outcome: "approval-gated", requiredApprovers: repo.approvers };
59
+ }
60
+ if (org.kind === "constrained" || repo.kind === "constrained") {
61
+ return { outcome: "constrained", constraints: unionConstraints(org, repo) };
62
+ }
63
+ if (org.kind === "allowed" || repo.kind === "allowed") {
64
+ return { outcome: "allowed" };
65
+ }
66
+ return { outcome: "blocked", reason: "no-applicable-rule" };
67
+ }
68
+ export function evaluateGitPolicy(orgPack, repoPack, context) {
69
+ const org = resolveLevel(orgPack, context);
70
+ const repo = resolveLevel(repoPack, context);
71
+ return combineDecisions(org, repo);
72
+ }
73
+ // ─── Private predicate helpers ───────────────────────────────────────────────────
74
+ function isRecord(value) {
75
+ return typeof value === "object" && value !== null && !Array.isArray(value);
76
+ }
77
+ function isNonEmptyString(value) {
78
+ return typeof value === "string" && value.length > 0;
79
+ }
80
+ function isStringArray(value) {
81
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
82
+ }
83
+ function isUndefinedOr(check) {
84
+ return (v) => v === undefined || check(v);
85
+ }
86
+ function isConstraintArray(value) {
87
+ return Array.isArray(value) && value.every(isGitDeliveryConstraint);
88
+ }
89
+ function isRuleDecision(value) {
90
+ return (typeof value === "string" && GIT_DELIVERY_RULE_DECISIONS.includes(value));
91
+ }
92
+ // ─── Guards ──────────────────────────────────────────────────────────────────────
93
+ export { isGitDeliveryConstraint, isGitDeliveryProviderCapability };
94
+ function isActionKind(value) {
95
+ return isGitDeliveryActionKind(value);
96
+ }
97
+ // A decision's per-decision required fields must validate. approval-gated requires a string-array
98
+ // (possibly empty); constrained requires a valid constraint array. allowed/blocked carry neither.
99
+ function decisionShapeValid(value) {
100
+ if (value.decision === "approval-gated") {
101
+ return isUndefinedOr(isStringArray)(value.requiredApprovers);
102
+ }
103
+ if (value.decision === "constrained") {
104
+ return isUndefinedOr(isConstraintArray)(value.constraints);
105
+ }
106
+ return true;
107
+ }
108
+ export function isGitDeliveryPolicyRule(value) {
109
+ return (isRecord(value) &&
110
+ isActionKind(value.actionKind) &&
111
+ isRuleDecision(value.decision) &&
112
+ decisionShapeValid(value));
113
+ }
114
+ function isDefaultRule(value) {
115
+ return isRecord(value) && isRuleDecision(value.decision) && decisionShapeValid(value);
116
+ }
117
+ // ─── Parse functions (return the shared GitDeliveryParseResult) ──────────────────
118
+ function ruleErrors(rules, field) {
119
+ if (!Array.isArray(rules)) {
120
+ return [`pack.${field} must be an array`];
121
+ }
122
+ if (!rules.every(isGitDeliveryPolicyRule)) {
123
+ return [`pack.${field} contains an invalid GitDeliveryPolicyRule`];
124
+ }
125
+ return [];
126
+ }
127
+ function defaultRuleErrors(defaultRule) {
128
+ if (defaultRule === undefined) {
129
+ return [];
130
+ }
131
+ if (!isDefaultRule(defaultRule)) {
132
+ return ["pack.defaultRule is not a valid GitDeliveryDefaultRule"];
133
+ }
134
+ return [];
135
+ }
136
+ function commonPackErrors(value) {
137
+ const errors = [];
138
+ if (value.schemaVersion !== GIT_DELIVERY_POLICY_SCHEMA_VERSION) {
139
+ errors.push("pack.schemaVersion must equal the GIT_DELIVERY_POLICY_SCHEMA_VERSION literal");
140
+ }
141
+ errors.push(...ruleErrors(value.rules, "rules"));
142
+ errors.push(...defaultRuleErrors(value.defaultRule));
143
+ return errors;
144
+ }
145
+ export function parseGitRepoPolicyPack(value) {
146
+ if (!isRecord(value)) {
147
+ return { ok: false, errors: ["repo policy pack must be an object"] };
148
+ }
149
+ const errors = [...commonPackErrors(value)];
150
+ if (!isNonEmptyString(value.repoId)) {
151
+ errors.push("repo policy pack.repoId must be a non-empty string");
152
+ }
153
+ if (errors.length > 0) {
154
+ return { ok: false, errors };
155
+ }
156
+ return { ok: true, value: value };
157
+ }
158
+ export function parseGitOrgPolicyPack(value) {
159
+ if (!isRecord(value)) {
160
+ return { ok: false, errors: ["org policy pack must be an object"] };
161
+ }
162
+ const errors = [...commonPackErrors(value)];
163
+ if (!isNonEmptyString(value.orgId)) {
164
+ errors.push("org policy pack.orgId must be a non-empty string");
165
+ }
166
+ if (errors.length > 0) {
167
+ return { ok: false, errors };
168
+ }
169
+ return { ok: true, value: value };
170
+ }
171
+ // Discriminates a pack by the presence of repoId vs orgId.
172
+ export function parseGitPolicyPack(value) {
173
+ if (!isRecord(value)) {
174
+ return { ok: false, errors: ["policy pack must be an object"] };
175
+ }
176
+ if (isNonEmptyString(value.repoId)) {
177
+ return parseGitRepoPolicyPack(value);
178
+ }
179
+ if (isNonEmptyString(value.orgId)) {
180
+ return parseGitOrgPolicyPack(value);
181
+ }
182
+ return { ok: false, errors: ["policy pack must carry a non-empty repoId or orgId"] };
183
+ }
@@ -0,0 +1,53 @@
1
+ import type { GitDeliveryBranchPattern, GitDeliveryMergeBlockReason, GitDeliveryProviderCapability } from "./git-delivery.js";
2
+ export declare const GIT_DELIVERY_PROVIDER_SCHEMA_VERSION: "1";
3
+ export interface GitDeliveryBranchProtection {
4
+ readonly deletionAllowed: boolean;
5
+ readonly forcePushAllowed: boolean;
6
+ readonly linearHistoryRequired: boolean;
7
+ readonly requiredReviewCount: number;
8
+ readonly requiredStatusCheckCount: number;
9
+ }
10
+ export type GitDeliveryChecksOverallStatus = "passing" | "failing" | "pending" | "skipped";
11
+ export declare const GIT_DELIVERY_CHECKS_OVERALL_STATUSES: readonly GitDeliveryChecksOverallStatus[];
12
+ export interface GitDeliveryChecksState {
13
+ readonly total: number;
14
+ readonly passing: number;
15
+ readonly failing: number;
16
+ readonly pending: number;
17
+ readonly overallStatus: GitDeliveryChecksOverallStatus;
18
+ }
19
+ export type GitDeliveryPullRequestStatus = "open" | "closed" | "merged";
20
+ export declare const GIT_DELIVERY_PULL_REQUEST_STATUSES: readonly GitDeliveryPullRequestStatus[];
21
+ export interface GitDeliveryMergeReadiness {
22
+ readonly ready: boolean;
23
+ readonly blockingReason?: GitDeliveryMergeBlockReason | undefined;
24
+ readonly requiredApprovalCount: number;
25
+ readonly receivedApprovalCount: number;
26
+ }
27
+ export interface GitDeliveryPullRequestState {
28
+ readonly schemaVersion: typeof GIT_DELIVERY_PROVIDER_SCHEMA_VERSION;
29
+ readonly externalId: string;
30
+ readonly status: GitDeliveryPullRequestStatus;
31
+ readonly isDraft: boolean;
32
+ readonly headBranchName: string;
33
+ readonly baseBranchName: string;
34
+ readonly mergeReadiness: GitDeliveryMergeReadiness;
35
+ }
36
+ export interface GitDeliveryRemoteTargetPolicy {
37
+ readonly allowedPushTargetPatterns: readonly GitDeliveryBranchPattern[];
38
+ readonly forcePushGloballyDenied: boolean;
39
+ }
40
+ export interface GitDeliveryProviderDescriptor {
41
+ readonly schemaVersion: typeof GIT_DELIVERY_PROVIDER_SCHEMA_VERSION;
42
+ readonly providerInstanceId: string;
43
+ readonly capabilities: readonly GitDeliveryProviderCapability[];
44
+ }
45
+ export declare function isGitDeliveryChecksOverallStatus(value: unknown): value is GitDeliveryChecksOverallStatus;
46
+ export declare function isGitDeliveryPullRequestStatus(value: unknown): value is GitDeliveryPullRequestStatus;
47
+ export declare function isGitDeliveryBranchProtection(value: unknown): value is GitDeliveryBranchProtection;
48
+ export declare function isGitDeliveryChecksState(value: unknown): value is GitDeliveryChecksState;
49
+ export declare function isGitDeliveryMergeReadiness(value: unknown): value is GitDeliveryMergeReadiness;
50
+ export declare function isGitDeliveryPullRequestState(value: unknown): value is GitDeliveryPullRequestState;
51
+ export declare function isGitDeliveryProviderDescriptor(value: unknown): value is GitDeliveryProviderDescriptor;
52
+ export declare function isGitDeliveryRemoteTargetPolicy(value: unknown): value is GitDeliveryRemoteTargetPolicy;
53
+ //# sourceMappingURL=git-delivery-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-delivery-provider.d.ts","sourceRoot":"","sources":["../src/git-delivery-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ // Provider-neutral interfaces for governed Git delivery (Issue #471, Epic #470).
2
+ // Ownership: git-delivery-provider domain.
3
+ // THE RULE: no field name, value, or type from any specific provider's API documentation
4
+ // may appear in this file. Provider adapters (in keiko-workflows or keiko-server) map
5
+ // provider responses to these neutral interfaces. This is enforced by code review and by
6
+ // the leaf-package boundary (ADR-0019 rule 1): keiko-contracts cannot import a provider SDK.
7
+ // Leaf-package rules: pure types and frozen const tables only. No IO.
8
+ //
9
+ // Imports ONLY from ./git-delivery.js (GitDeliveryMergeBlockReason, GitDeliveryProviderCapability).
10
+ import { isGitDeliveryBranchPattern, isGitDeliveryMergeBlockReason, isGitDeliveryProviderCapability, } from "./git-delivery.js";
11
+ export const GIT_DELIVERY_PROVIDER_SCHEMA_VERSION = "1";
12
+ export const GIT_DELIVERY_CHECKS_OVERALL_STATUSES = [
13
+ "passing",
14
+ "failing",
15
+ "pending",
16
+ "skipped",
17
+ ];
18
+ export const GIT_DELIVERY_PULL_REQUEST_STATUSES = [
19
+ "open",
20
+ "closed",
21
+ "merged",
22
+ ];
23
+ // ─── Private predicate helpers ───────────────────────────────────────────────────
24
+ function isRecord(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+ function isNonEmptyString(value) {
28
+ return typeof value === "string" && value.length > 0;
29
+ }
30
+ function isBoolean(value) {
31
+ return typeof value === "boolean";
32
+ }
33
+ function isNonNegativeInteger(value) {
34
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
35
+ }
36
+ function isUndefinedOr(check) {
37
+ return (v) => v === undefined || check(v);
38
+ }
39
+ function isProviderCapability(value) {
40
+ return isGitDeliveryProviderCapability(value);
41
+ }
42
+ // ─── Guards ──────────────────────────────────────────────────────────────────────
43
+ export function isGitDeliveryChecksOverallStatus(value) {
44
+ return (isNonEmptyString(value) &&
45
+ GIT_DELIVERY_CHECKS_OVERALL_STATUSES.includes(value));
46
+ }
47
+ export function isGitDeliveryPullRequestStatus(value) {
48
+ return (isNonEmptyString(value) &&
49
+ GIT_DELIVERY_PULL_REQUEST_STATUSES.includes(value));
50
+ }
51
+ export function isGitDeliveryBranchProtection(value) {
52
+ return (isRecord(value) &&
53
+ isBoolean(value.deletionAllowed) &&
54
+ isBoolean(value.forcePushAllowed) &&
55
+ isBoolean(value.linearHistoryRequired) &&
56
+ isNonNegativeInteger(value.requiredReviewCount) &&
57
+ isNonNegativeInteger(value.requiredStatusCheckCount));
58
+ }
59
+ export function isGitDeliveryChecksState(value) {
60
+ return (isRecord(value) &&
61
+ isNonNegativeInteger(value.total) &&
62
+ isNonNegativeInteger(value.passing) &&
63
+ isNonNegativeInteger(value.failing) &&
64
+ isNonNegativeInteger(value.pending) &&
65
+ isGitDeliveryChecksOverallStatus(value.overallStatus));
66
+ }
67
+ export function isGitDeliveryMergeReadiness(value) {
68
+ return (isRecord(value) &&
69
+ isBoolean(value.ready) &&
70
+ isUndefinedOr(isGitDeliveryMergeBlockReason)(value.blockingReason) &&
71
+ isNonNegativeInteger(value.requiredApprovalCount) &&
72
+ isNonNegativeInteger(value.receivedApprovalCount));
73
+ }
74
+ export function isGitDeliveryPullRequestState(value) {
75
+ return (isRecord(value) &&
76
+ value.schemaVersion === GIT_DELIVERY_PROVIDER_SCHEMA_VERSION &&
77
+ isNonEmptyString(value.externalId) &&
78
+ isGitDeliveryPullRequestStatus(value.status) &&
79
+ isBoolean(value.isDraft) &&
80
+ isNonEmptyString(value.headBranchName) &&
81
+ isNonEmptyString(value.baseBranchName) &&
82
+ isGitDeliveryMergeReadiness(value.mergeReadiness));
83
+ }
84
+ export function isGitDeliveryProviderDescriptor(value) {
85
+ return (isRecord(value) &&
86
+ value.schemaVersion === GIT_DELIVERY_PROVIDER_SCHEMA_VERSION &&
87
+ isNonEmptyString(value.providerInstanceId) &&
88
+ Array.isArray(value.capabilities) &&
89
+ value.capabilities.every(isProviderCapability));
90
+ }
91
+ export function isGitDeliveryRemoteTargetPolicy(value) {
92
+ return (isRecord(value) &&
93
+ Array.isArray(value.allowedPushTargetPatterns) &&
94
+ value.allowedPushTargetPatterns.every(isGitDeliveryBranchPattern) &&
95
+ typeof value.forcePushGloballyDenied === "boolean");
96
+ }