@neurcode-ai/contracts 0.1.2 → 0.1.3

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 (82) hide show
  1. package/dist/admission/framing.d.ts +38 -0
  2. package/dist/admission/framing.d.ts.map +1 -0
  3. package/dist/admission/framing.js +78 -0
  4. package/dist/admission/framing.js.map +1 -0
  5. package/dist/admission/index.d.ts +4 -0
  6. package/dist/admission/index.d.ts.map +1 -0
  7. package/dist/admission/index.js +37 -0
  8. package/dist/admission/index.js.map +1 -0
  9. package/dist/admission/privacy.d.ts +23 -0
  10. package/dist/admission/privacy.d.ts.map +1 -0
  11. package/dist/admission/privacy.js +99 -0
  12. package/dist/admission/privacy.js.map +1 -0
  13. package/dist/admission/schema.d.ts +277 -0
  14. package/dist/admission/schema.d.ts.map +1 -0
  15. package/dist/admission/schema.js +156 -0
  16. package/dist/admission/schema.js.map +1 -0
  17. package/dist/index.d.ts +91 -11
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +182 -17
  20. package/dist/index.js.map +1 -1
  21. package/dist/intelligence.d.ts +522 -0
  22. package/dist/intelligence.d.ts.map +1 -0
  23. package/dist/intelligence.js +5 -0
  24. package/dist/intelligence.js.map +1 -0
  25. package/dist/remediation/capabilities.d.ts +36 -0
  26. package/dist/remediation/capabilities.d.ts.map +1 -0
  27. package/dist/remediation/capabilities.js +7 -0
  28. package/dist/remediation/capabilities.js.map +1 -0
  29. package/dist/remediation/index.d.ts +5 -0
  30. package/dist/remediation/index.d.ts.map +1 -0
  31. package/dist/remediation/index.js +3 -0
  32. package/dist/remediation/index.js.map +1 -0
  33. package/dist/remediation/request.d.ts +183 -0
  34. package/dist/remediation/request.d.ts.map +1 -0
  35. package/dist/remediation/request.js +15 -0
  36. package/dist/remediation/request.js.map +1 -0
  37. package/dist/remediation/response.d.ts +100 -0
  38. package/dist/remediation/response.d.ts.map +1 -0
  39. package/dist/remediation/response.js +11 -0
  40. package/dist/remediation/response.js.map +1 -0
  41. package/dist/remediation/validation.d.ts +87 -0
  42. package/dist/remediation/validation.d.ts.map +1 -0
  43. package/dist/remediation/validation.js +15 -0
  44. package/dist/remediation/validation.js.map +1 -0
  45. package/dist/status-vocabulary.d.ts +45 -0
  46. package/dist/status-vocabulary.d.ts.map +1 -0
  47. package/dist/status-vocabulary.js +101 -0
  48. package/dist/status-vocabulary.js.map +1 -0
  49. package/dist/verification/canonical-finding.d.ts +171 -0
  50. package/dist/verification/canonical-finding.d.ts.map +1 -0
  51. package/dist/verification/canonical-finding.js +3 -0
  52. package/dist/verification/canonical-finding.js.map +1 -0
  53. package/dist/verification/index.d.ts +6 -0
  54. package/dist/verification/index.d.ts.map +1 -0
  55. package/dist/verification/index.js +11 -0
  56. package/dist/verification/index.js.map +1 -0
  57. package/dist/verification/pipeline.d.ts +134 -0
  58. package/dist/verification/pipeline.d.ts.map +1 -0
  59. package/dist/verification/pipeline.js +57 -0
  60. package/dist/verification/pipeline.js.map +1 -0
  61. package/dist/verification/taxonomy.d.ts +10 -0
  62. package/dist/verification/taxonomy.d.ts.map +1 -0
  63. package/dist/verification/taxonomy.js +16 -0
  64. package/dist/verification/taxonomy.js.map +1 -0
  65. package/package.json +1 -1
  66. package/src/admission/admission-framing.test.ts +93 -0
  67. package/src/admission/framing.ts +78 -0
  68. package/src/admission/index.ts +58 -0
  69. package/src/admission/privacy.ts +93 -0
  70. package/src/admission/schema.ts +392 -0
  71. package/src/index.ts +266 -26
  72. package/src/intelligence.ts +698 -0
  73. package/src/remediation/capabilities.ts +53 -0
  74. package/src/remediation/index.ts +29 -0
  75. package/src/remediation/request.ts +236 -0
  76. package/src/remediation/response.ts +129 -0
  77. package/src/remediation/validation.ts +109 -0
  78. package/src/status-vocabulary.ts +125 -0
  79. package/src/verification/canonical-finding.ts +196 -0
  80. package/src/verification/index.ts +41 -0
  81. package/src/verification/pipeline.ts +199 -0
  82. package/src/verification/taxonomy.ts +46 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * Runtime Admission — Phase A schema (Provenance Core V1).
3
+ *
4
+ * Source-free, deterministic provenance types shared across CLI, the future
5
+ * OSS advisory Action, and future Enterprise enforcement. Phase A defines the
6
+ * normalized git tree-delta, the governed coverage manifest, the self-attested
7
+ * local artifact, and the consistency decision. No signing, no receipts, no
8
+ * backend, no Action here — those are later phases.
9
+ *
10
+ * Two distinct hashes by design (do not collapse them):
11
+ * - deltaHash: exact, base-specific tree-delta fingerprint (debugging /
12
+ * deterministic reproduction).
13
+ * - coverageSetHash: governed-effect set fingerprint used for squash/rebase-
14
+ * survivable, per-entry subset matching of a PR to sessions.
15
+ */
16
+
17
+ export const ADMISSION_COVERAGE_MANIFEST_SCHEMA_VERSION = 'neurcode.admission-coverage.v1' as const;
18
+ export const SELF_ATTESTED_ADMISSION_RECORD_SCHEMA_VERSION = 'neurcode.admission-record.v1' as const;
19
+ export const ADMISSION_CONSISTENCY_DECISION_SCHEMA_VERSION = 'neurcode.admission-consistency.v1' as const;
20
+
21
+ /**
22
+ * Mandatory honesty label. A locally committed artifact is authored by the same
23
+ * untrusted principal who authored the diff, so it can be fabricated with
24
+ * matching object ids. It is a claim, never proof.
25
+ */
26
+ export const SELF_ATTESTED_ADMISSION_DISCLAIMER: string =
27
+ 'Self-attested by the local Neurcode runtime: a source-free claim that a governed ' +
28
+ 'session produced these effects. This is NOT cryptographic proof that governance ran. ' +
29
+ 'Enterprise enforcement requires a backend-anchored signed receipt.';
30
+
31
+ /** Git object hash format. Determines object-id hex width (40 vs 64). */
32
+ export type GitObjectFormat = 'sha1' | 'sha256';
33
+
34
+ /** Post-image (or pre-image, for deletes) object kind, derived from git mode. */
35
+ export type AdmissionObjectType = 'blob' | 'symlink' | 'submodule' | 'absent';
36
+
37
+ /** Normalized change kind. Renames are normalized to delete + add (never a 'rename' type). */
38
+ export type AdmissionChangeType = 'added' | 'modified' | 'deleted' | 'typechanged';
39
+
40
+ /**
41
+ * Descriptive classification of a covered effect (source-free).
42
+ *
43
+ * NOTE: classification is descriptive, not an eligibility verdict. Strict
44
+ * runtime admission (see `isStrictlyAdmissible`) accepts only pre-write
45
+ * governance (`governed_prewrite`, `governed_delete`). `observed_postwrite`
46
+ * means the write was only observed after the fact — visible, but NOT strictly
47
+ * admissible. `generated` is admissible only under an explicit policy opt-in.
48
+ */
49
+ export type AdmissionCoverageClassification =
50
+ | 'governed_prewrite'
51
+ | 'governed_delete'
52
+ | 'observed_postwrite'
53
+ | 'generated'
54
+ | 'ungoverned';
55
+
56
+ /** Canonical git file modes used by this contract. */
57
+ export const GIT_MODE_BLOB = '100644' as const;
58
+ export const GIT_MODE_EXEC = '100755' as const;
59
+ export const GIT_MODE_SYMLINK = '120000' as const;
60
+ export const GIT_MODE_SUBMODULE = '160000' as const;
61
+ export const GIT_MODE_ABSENT = '000000' as const;
62
+
63
+ const KNOWN_MODES = new Set<string>([
64
+ GIT_MODE_BLOB,
65
+ GIT_MODE_EXEC,
66
+ GIT_MODE_SYMLINK,
67
+ GIT_MODE_SUBMODULE,
68
+ GIT_MODE_ABSENT,
69
+ ]);
70
+
71
+ /**
72
+ * Exact normalized tree-delta entry. Object ids are content-addressed git
73
+ * hashes (non-invertible); no file content is ever carried.
74
+ */
75
+ export interface AdmissionDeltaEntry {
76
+ /** Post-image path; for a delete, the deleted path. */
77
+ path: string;
78
+ changeType: AdmissionChangeType;
79
+ /** Object kind of the side that carries identity (new side, or old side for deletes). */
80
+ objectType: AdmissionObjectType;
81
+ /** Pre-image mode, or '000000' when absent (added). */
82
+ oldMode: string;
83
+ /** Post-image mode, or '000000' when absent (deleted). */
84
+ newMode: string;
85
+ /** Pre-image object id, or the all-zeros id when absent. */
86
+ oldObjectId: string;
87
+ /** Post-image object id, or the all-zeros id when absent. */
88
+ newObjectId: string;
89
+ }
90
+
91
+ /**
92
+ * A single governed effect. Identity fields per the matching contract:
93
+ * added/modified/typechanged → path + newMode + newObjectId
94
+ * deleted → path + oldMode + oldObjectId
95
+ * The identity mode/id are flattened into `mode`/`objectId` so matching is a
96
+ * simple per-entry set membership test. `classification` and `sessions` are
97
+ * annotations and are intentionally excluded from coverageSetHash.
98
+ */
99
+ export interface AdmissionCoverageEntry {
100
+ path: string;
101
+ changeType: AdmissionChangeType;
102
+ objectType: AdmissionObjectType;
103
+ /** Identity mode: newMode for non-deletes, oldMode for deletes. */
104
+ mode: string;
105
+ /** Identity object id: newObjectId for non-deletes, oldObjectId for deletes. */
106
+ objectId: string;
107
+ classification: AdmissionCoverageClassification;
108
+ /** Contributing governed session ids, sorted and de-duplicated. */
109
+ sessions: string[];
110
+ }
111
+
112
+ export interface AdmissionCoverageManifest {
113
+ schemaVersion: typeof ADMISSION_COVERAGE_MANIFEST_SCHEMA_VERSION;
114
+ objectFormat: GitObjectFormat;
115
+ framingVersion: string;
116
+ entryCount: number;
117
+ /** Exact, base-specific normalized tree-delta fingerprint (full SHA-256 hex). */
118
+ deltaHash: string;
119
+ /** Squash/rebase-survivable governed-effect set fingerprint (full SHA-256 hex). */
120
+ coverageSetHash: string;
121
+ /** Normalized delta entries, canonically sorted. */
122
+ delta: AdmissionDeltaEntry[];
123
+ /** Derived governed coverage entries, canonically sorted. */
124
+ coverage: AdmissionCoverageEntry[];
125
+ }
126
+
127
+ export interface AdmissionSessionRef {
128
+ sessionId: string;
129
+ replayHash?: string;
130
+ profileHash?: string;
131
+ }
132
+
133
+ export type AdmissionCaptureMode = 'worktree' | 'committed';
134
+
135
+ export interface AdmissionCaptureDescriptor {
136
+ mode: AdmissionCaptureMode;
137
+ capturedAt: string;
138
+ baseRef?: string;
139
+ headRef?: string;
140
+ }
141
+
142
+ /** Source-free repo identifiers. Hashes of local/remote paths, never the URL itself. */
143
+ export interface AdmissionRepoIdentifiers {
144
+ name?: string;
145
+ rootHash?: string;
146
+ remoteHash?: string;
147
+ }
148
+
149
+ export type RuntimeAdmissionTrustLevel = 'unsigned_local' | 'self_attested' | 'backend_signed';
150
+
151
+ export interface RuntimeAdmissionAgentHost {
152
+ adapter: string | null;
153
+ enforcementLevel: string | null;
154
+ controlLevel?: string | null;
155
+ automatic?: boolean;
156
+ }
157
+
158
+ export interface RuntimeAdmissionReceiptSummary {
159
+ present: boolean;
160
+ trustLevel: RuntimeAdmissionTrustLevel;
161
+ receiptId?: string;
162
+ keyId?: string | null;
163
+ replayHash?: string | null;
164
+ signatureStatus?: string | null;
165
+ verificationStatus?: string | null;
166
+ signedAt?: string | null;
167
+ verifier?: string | null;
168
+ }
169
+
170
+ export interface RuntimeAdmissionContext {
171
+ schemaVersion: 'neurcode.runtime-admission-context.v1';
172
+ trustLevel: RuntimeAdmissionTrustLevel;
173
+ createdAt: string;
174
+ sessionId: string;
175
+ sessionStatus: 'active' | 'finished' | string;
176
+ agentHost: RuntimeAdmissionAgentHost;
177
+ intentSummary: string | null;
178
+ scopeMode: string | null;
179
+ counts: {
180
+ changedPaths: number;
181
+ blockedPaths: number;
182
+ suggestedApprovalPaths: number;
183
+ approvedExactPaths: number;
184
+ deniedPaths: number;
185
+ approvalRequiredSurfaces: number;
186
+ owners: number;
187
+ preWriteChecks: number;
188
+ allowedChecks: number;
189
+ warningChecks: number;
190
+ };
191
+ paths: {
192
+ changed: string[];
193
+ blocked: string[];
194
+ suggestedApproval: string[];
195
+ approvedExact: string[];
196
+ denied: string[];
197
+ approvalRequiredSurfaces: string[];
198
+ };
199
+ owners: Array<{ owner: string; count: number }>;
200
+ guard: {
201
+ status: string;
202
+ verifiedPrewrite: number;
203
+ deniedButChanged: number;
204
+ unverifiedWrites: number;
205
+ observedAfterOnly: number;
206
+ };
207
+ integrity: {
208
+ sourceFree: true;
209
+ replayHash: string | null;
210
+ replayHashStatus: 'present' | 'missing';
211
+ deltaHash: string;
212
+ coverageSetHash: string;
213
+ evidenceIntegrityStatus: 'local_self_attested' | 'backend_signed' | 'unsigned_local';
214
+ receipt: RuntimeAdmissionReceiptSummary;
215
+ };
216
+ }
217
+
218
+ /**
219
+ * The local runtime artifact, written to ignored runtime state under
220
+ * .neurcode/admission/<sessionId>.json. A selected record may later be exported
221
+ * explicitly for the OSS advisory Action. Self-attested by construction — see
222
+ * SELF_ATTESTED_ADMISSION_DISCLAIMER.
223
+ */
224
+ export interface SelfAttestedAdmissionRecord {
225
+ schemaVersion: typeof SELF_ATTESTED_ADMISSION_RECORD_SCHEMA_VERSION;
226
+ attestationKind: 'self-attested';
227
+ admissionContractVersion: string;
228
+ disclaimer: string;
229
+ sessionId: string;
230
+ sessionRefs: AdmissionSessionRef[];
231
+ repo: AdmissionRepoIdentifiers;
232
+ capture: AdmissionCaptureDescriptor;
233
+ manifest: AdmissionCoverageManifest;
234
+ runtimeContext?: RuntimeAdmissionContext;
235
+ }
236
+
237
+ export type AdmissionConsistencyVerdict =
238
+ | 'self_attested_complete'
239
+ | 'self_attested_incomplete'
240
+ | 'self_attested_inconsistent'
241
+ | 'no_record';
242
+
243
+ /**
244
+ * Output of validating a self-attested record against a recomputed ground-truth
245
+ * delta. `trustLevel` is always 'self-attested' in Phase A; `notProof` is always
246
+ * true. Coverage verdict is decided by per-entry subset matching (squash/rebase
247
+ * survivable). `deltaHashMatches` is a diagnostic only and never gates coverage.
248
+ */
249
+ export interface AdmissionConsistencyDecision {
250
+ schemaVersion: typeof ADMISSION_CONSISTENCY_DECISION_SCHEMA_VERSION;
251
+ verdict: AdmissionConsistencyVerdict;
252
+ trustLevel: 'self-attested';
253
+ notProof: true;
254
+ /** True only when the PR delta is byte-identical to the captured delta (same base, no rebase). */
255
+ deltaHashMatches: boolean;
256
+ /** Ground-truth changed paths covered by an admission-eligible entry. */
257
+ coveredPaths: string[];
258
+ /** Ground-truth paths with no admission-eligible coverage (drift / post-session edits). */
259
+ uncoveredPaths: string[];
260
+ /** Coverage entries not present in the ground-truth delta (stale/multi-session/padding). */
261
+ unexpectedCoverage: string[];
262
+ reasons: string[];
263
+ }
264
+
265
+ // ── Pure helpers (no crypto, no IO) ─────────────────────────────────────────
266
+
267
+ export function objectIdHexLength(format: GitObjectFormat): number {
268
+ return format === 'sha256' ? 64 : 40;
269
+ }
270
+
271
+ export function zeroObjectId(format: GitObjectFormat): string {
272
+ return '0'.repeat(objectIdHexLength(format));
273
+ }
274
+
275
+ export function isZeroObjectId(objectId: string): boolean {
276
+ return /^0+$/.test(objectId);
277
+ }
278
+
279
+ export function isValidObjectId(objectId: string, format: GitObjectFormat): boolean {
280
+ const len = objectIdHexLength(format);
281
+ return new RegExp(`^[0-9a-f]{${len}}$`).test(objectId);
282
+ }
283
+
284
+ export function isKnownGitMode(mode: string): boolean {
285
+ return KNOWN_MODES.has(mode);
286
+ }
287
+
288
+ export function objectTypeForMode(mode: string): AdmissionObjectType {
289
+ switch (mode) {
290
+ case GIT_MODE_SUBMODULE:
291
+ return 'submodule';
292
+ case GIT_MODE_SYMLINK:
293
+ return 'symlink';
294
+ case GIT_MODE_BLOB:
295
+ case GIT_MODE_EXEC:
296
+ return 'blob';
297
+ case GIT_MODE_ABSENT:
298
+ return 'absent';
299
+ default:
300
+ throw new Error(`admission: unsupported git mode "${mode}"`);
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Canonical, ordered field list for a delta entry. The field ORDER is part of
306
+ * the hashing contract and must never be reordered.
307
+ */
308
+ export function deltaEntryCanonicalFields(entry: AdmissionDeltaEntry): string[] {
309
+ return [
310
+ entry.path,
311
+ entry.changeType,
312
+ entry.objectType,
313
+ entry.oldMode,
314
+ entry.newMode,
315
+ entry.oldObjectId,
316
+ entry.newObjectId,
317
+ ];
318
+ }
319
+
320
+ /**
321
+ * Canonical, ordered identity field list for a coverage entry.
322
+ *
323
+ * Identity per the matching contract:
324
+ * present (added/modified/typechanged) → path + newMode + newObjectId
325
+ * deleted → path + oldMode + oldObjectId
326
+ *
327
+ * `mode`/`objectId` already hold the correct side. `changeType` collapses to a
328
+ * single present/deleted flag so a squash/rebase that reclassifies modified↔added
329
+ * (same resulting content) still matches. `objectType` is derived from `mode` and
330
+ * is excluded. `classification` and `sessions` are annotations and excluded.
331
+ */
332
+ export function coverageEntryCanonicalFields(entry: AdmissionCoverageEntry): string[] {
333
+ const presence = entry.changeType === 'deleted' ? 'D' : 'P';
334
+ return [presence, entry.path, entry.mode, entry.objectId];
335
+ }
336
+
337
+ /** Stable in-memory identity key for set/union operations (not a security hash). */
338
+ export function coverageEntryIdentityKey(entry: AdmissionCoverageEntry): string {
339
+ return JSON.stringify(coverageEntryCanonicalFields(entry));
340
+ }
341
+
342
+ /**
343
+ * Descriptive test: did this effect have ANY governance evidence (pre- or
344
+ * post-write, or generated)? Use this for descriptive surfaces (telemetry,
345
+ * "what did the runtime observe"). It is NOT the admission eligibility test.
346
+ */
347
+ export function isGovernedClassification(classification: AdmissionCoverageClassification): boolean {
348
+ return (
349
+ classification === 'governed_prewrite' ||
350
+ classification === 'observed_postwrite' ||
351
+ classification === 'governed_delete' ||
352
+ classification === 'generated'
353
+ );
354
+ }
355
+
356
+ export type AdmissionEligibilityMode = 'strict' | 'descriptive';
357
+
358
+ export interface AdmissionEligibilityOptions {
359
+ /** 'strict' (default): pre-write governance only. 'descriptive': any evidence. */
360
+ mode?: AdmissionEligibilityMode;
361
+ /** When true, 'generated' counts as admissible under strict mode (explicit policy opt-in). */
362
+ allowGenerated?: boolean;
363
+ }
364
+
365
+ /**
366
+ * Strict runtime admission eligibility. Only pre-write governance is admissible:
367
+ * `governed_prewrite` and `governed_delete`. `observed_postwrite` is visible but
368
+ * NOT admissible (the write was only seen after it happened). `generated` is
369
+ * admissible only when `allowGenerated` is explicitly set by policy.
370
+ */
371
+ export function isStrictlyAdmissible(
372
+ classification: AdmissionCoverageClassification,
373
+ options: AdmissionEligibilityOptions = {},
374
+ ): boolean {
375
+ if (classification === 'governed_prewrite' || classification === 'governed_delete') return true;
376
+ if (classification === 'generated') return options.allowGenerated === true;
377
+ return false;
378
+ }
379
+
380
+ /**
381
+ * Eligibility predicate honoring the requested mode. Strict (default) uses
382
+ * `isStrictlyAdmissible`; descriptive uses `isGovernedClassification`.
383
+ */
384
+ export function isAdmissibleClassification(
385
+ classification: AdmissionCoverageClassification,
386
+ options: AdmissionEligibilityOptions = {},
387
+ ): boolean {
388
+ if ((options.mode ?? 'strict') === 'descriptive') {
389
+ return isGovernedClassification(classification);
390
+ }
391
+ return isStrictlyAdmissible(classification, options);
392
+ }