archctx-contracts 0.7.2 → 0.8.1
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.
- package/fixtures/invalid/hook-debt-checkpoint-failed-read.json +54 -0
- package/fixtures/invalid/native-context-invalid-model-summary.json +635 -0
- package/fixtures/invalid/native-context-missing-contract-bytes.json +636 -0
- package/fixtures/invalid/native-context-missing-input-bytes.json +636 -0
- package/fixtures/invalid/native-context-mixed-snapshot.json +637 -0
- package/fixtures/invalid/projection-result-applied-incomplete-coverage.json +78 -0
- package/fixtures/invalid/refactor-decision-open-status.json +38 -0
- package/fixtures/valid/archctx-capabilities.json +3 -3
- package/fixtures/valid/capability-dependencies.json +1 -1
- package/fixtures/valid/docs-anchors.json +2 -1
- package/fixtures/valid/flow-selector-check.json +1 -1
- package/fixtures/valid/hook-debt-checkpoint.json +54 -0
- package/fixtures/valid/native-context-export.json +636 -0
- package/fixtures/valid/product-version-manifest.json +8 -8
- package/fixtures/valid/projection-manifest.json +47 -169
- package/fixtures/valid/projection-result.json +5 -5
- package/fixtures/valid/refactor-decision.json +38 -0
- package/fixtures/valid/runtime-recommendation-show.json +1 -0
- package/fixtures/valid/runtime-refactor-scan.json +6 -0
- package/package.json +1 -1
- package/schemas/repo/refactor-decision.schema.json +218 -0
- package/schemas/runtime/archctx-capabilities.schema.json +2 -2
- package/schemas/runtime/architecture-debt.schema.json +222 -0
- package/schemas/runtime/capability-dependencies.schema.json +10 -6
- package/schemas/runtime/changeset.schema.json +24 -1
- package/schemas/runtime/docs-anchors.schema.json +4 -3
- package/schemas/runtime/hook-debt-checkpoint.schema.json +69 -0
- package/schemas/runtime/native-context-export.schema.json +823 -0
- package/schemas/runtime/native-model-issue.schema.json +18 -0
- package/schemas/runtime/practice-checkpoint.schema.json +1 -0
- package/schemas/runtime/product-version-manifest.schema.json +2 -2
- package/schemas/runtime/projection-apply-readback.schema.json +470 -7
- package/schemas/runtime/projection-apply-recovery.schema.json +2 -2
- package/schemas/runtime/projection-manifest.schema.json +81 -186
- package/schemas/runtime/projection-result.schema.json +132 -5
- package/schemas/runtime/projection-target.schema.json +4 -1
- package/schemas/runtime/recommendation-v3.schema.json +106 -0
- package/schemas/runtime/review-result.schema.json +1 -0
- package/schemas/runtime/runtime-recommendation-show.schema.json +234 -13
- package/schemas/runtime/runtime-refactor-scan.schema.json +171 -0
- package/schemas/runtime/structural-observation-payload.schema.json +106 -0
- package/src/architecture-debt.ts +115 -0
- package/src/changeset-recovery.ts +49 -0
- package/src/docs-anchors.ts +2 -1
- package/src/failure.ts +810 -0
- package/src/index.ts +6 -0
- package/src/ledger.ts +14 -3
- package/src/native-context.ts +279 -0
- package/src/ownership-dependencies.ts +39 -1
- package/src/ports.ts +4 -0
- package/src/practices.ts +2 -0
- package/src/product-version.ts +37 -4
- package/src/projection-manifest.ts +372 -130
- package/src/projection-recovery.ts +111 -0
- package/src/projection.ts +443 -11
- package/src/refactor.ts +525 -2
- package/src/schema.ts +8 -0
- package/src/validator.ts +8 -3
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { projectionApplyReadbackRequestFromReceipt, projectionApplyReadbackRequestInvariantIssues, type ProjectionApplyReceiptV1, type ProjectionRequestV1 } from "./projection";
|
|
2
|
+
import type { Json } from "./schema";
|
|
3
|
+
|
|
4
|
+
/** Dedicated recovery payload budget; unrelated failure summaries retain their 8 KiB limit. */
|
|
5
|
+
export const PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES = 256 * 1024;
|
|
6
|
+
export const PROJECTION_COMMITTED_REASONS = ["projection-accepted-change-committed", "projection-apply-request-differs", "projection-apply-request-digest-unrecorded"] as const;
|
|
7
|
+
export type ProjectionCommittedReason = typeof PROJECTION_COMMITTED_REASONS[number];
|
|
8
|
+
export const PROJECTION_COMMITTED_MANUAL_ONLY = "projection-apply-committed-manual-only" as const;
|
|
9
|
+
const utf8 = (value: string) => new TextEncoder().encode(value).length;
|
|
10
|
+
const digest = (value: unknown) => typeof value === "string" && /^sha256:[a-f0-9]{64}$/.test(value);
|
|
11
|
+
|
|
12
|
+
/** Copy only own JSON data, never accessors/toJSON/iterators. Count exact compact UTF-8 bytes. */
|
|
13
|
+
function jsonDataCopy(input: unknown, maximum: number): Json {
|
|
14
|
+
let measured = 0;
|
|
15
|
+
const add = (count: number) => { measured += count; if (measured > maximum) throw new Error("Projection recovery details exceed byte budget"); };
|
|
16
|
+
const copy = (value: unknown, depth: number): Json => {
|
|
17
|
+
if (depth > 16) throw new Error("Projection recovery data exceeds depth budget");
|
|
18
|
+
if (value === null || typeof value === "boolean" || typeof value === "string" || (typeof value === "number" && Number.isFinite(value))) {
|
|
19
|
+
add(utf8(JSON.stringify(value))); return value;
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== "object" || value === null) throw new Error("Projection recovery data must be JSON");
|
|
22
|
+
const keys = Reflect.ownKeys(value);
|
|
23
|
+
const array = Array.isArray(value);
|
|
24
|
+
const prototype = Object.getPrototypeOf(value);
|
|
25
|
+
if (!array && prototype !== Object.prototype && prototype !== null) throw new Error("Projection recovery data must be plain");
|
|
26
|
+
const get = (key: string): unknown => {
|
|
27
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
28
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) throw new Error("Projection recovery data must use enumerable own data");
|
|
29
|
+
return descriptor.value;
|
|
30
|
+
};
|
|
31
|
+
add(2);
|
|
32
|
+
if (array) {
|
|
33
|
+
const length = Object.getOwnPropertyDescriptor(value, "length")?.value;
|
|
34
|
+
if (!Number.isSafeInteger(length) || length < 0 || keys.length !== length + 1) throw new Error("Projection recovery list must be dense data");
|
|
35
|
+
const result: Json[] = [];
|
|
36
|
+
for (let i = 0; i < length; i++) { if (i) add(1); result.push(copy(get(String(i)), depth + 1)); }
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
const entries: [string, Json][] = [];
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
if (typeof key !== "string") throw new Error("Projection recovery data cannot use symbols");
|
|
42
|
+
if (entries.length) add(1);
|
|
43
|
+
add(utf8(JSON.stringify(key)) + 1);
|
|
44
|
+
entries.push([key, copy(get(key), depth + 1)]);
|
|
45
|
+
}
|
|
46
|
+
return Object.fromEntries(entries);
|
|
47
|
+
};
|
|
48
|
+
return copy(input, 0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Freeze caller-owned receipt data before the writer awaits again. No mutation of caller state. */
|
|
52
|
+
export function projectionApplyReceiptDataSnapshot(receipt: ProjectionApplyReceiptV1): ProjectionApplyReceiptV1 {
|
|
53
|
+
return jsonDataCopy(receipt, Number.MAX_SAFE_INTEGER) as unknown as ProjectionApplyReceiptV1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** The complete recovery metadata, including the original unabridged readback request. */
|
|
57
|
+
export function projectionApplyCommittedDetails(receipt: ProjectionApplyReceiptV1, written?: boolean): { [key: string]: Json } {
|
|
58
|
+
const readback = projectionApplyReadbackRequestFromReceipt(receipt);
|
|
59
|
+
return {
|
|
60
|
+
requestId: receipt.result.requestId, lookupKey: receipt.identity.lookupKey, applyId: receipt.identity.applyId,
|
|
61
|
+
...(receipt.recovery?.requestDigest === undefined ? {} : { requestDigest: receipt.recovery.requestDigest }),
|
|
62
|
+
...(readback === undefined ? {} : { readbackRequest: readback as unknown as Json }),
|
|
63
|
+
...(written === undefined ? {} : { written })
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** false is the longest written branch; every later committed failure must fit in full. */
|
|
68
|
+
export function projectionApplyRecoveryDetailsBytes(receipt: ProjectionApplyReceiptV1): number {
|
|
69
|
+
return utf8(JSON.stringify(projectionApplyCommittedDetails(receipt, false)));
|
|
70
|
+
}
|
|
71
|
+
export function projectionApplyRecoveryDetailsFit(receipt: ProjectionApplyReceiptV1): boolean {
|
|
72
|
+
return projectionApplyRecoveryDetailsBytes(receipt) <= PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Historical receipts stay stored; this failure supplies complete IDs for explicit maintenance. */
|
|
76
|
+
export function projectionApplyManualOnlyDetails(receipt: ProjectionApplyReceiptV1, written?: boolean): { [key: string]: Json } {
|
|
77
|
+
const result: { [key: string]: Json } = {
|
|
78
|
+
lookupKey: receipt.identity.lookupKey, applyId: receipt.identity.applyId,
|
|
79
|
+
manualRecoveryRequired: true, readbackUnavailable: "details-budget-exceeded",
|
|
80
|
+
...(written === undefined ? {} : { written })
|
|
81
|
+
};
|
|
82
|
+
const withRequest = { ...result, requestId: receipt.result.requestId };
|
|
83
|
+
return utf8(JSON.stringify(withRequest)) <= PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES
|
|
84
|
+
? withRequest : { ...result, requestIdUnavailable: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** A closed exception to the summary policy, selected only by the committed code/reason pair. */
|
|
88
|
+
export function projectionRecoveryDetailsSnapshot(reason: string, input: unknown): { [key: string]: Json } {
|
|
89
|
+
const value = jsonDataCopy(input, PROJECTION_RECOVERY_DETAILS_LIMIT_BYTES);
|
|
90
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("Recovery details must be an object");
|
|
91
|
+
const manual = reason === PROJECTION_COMMITTED_MANUAL_ONLY;
|
|
92
|
+
if (!manual && !(PROJECTION_COMMITTED_REASONS as readonly string[]).includes(reason)) throw new Error("Unknown recovery reason");
|
|
93
|
+
const allowed = manual
|
|
94
|
+
? ["requestId", "requestIdUnavailable", "lookupKey", "applyId", "written", "manualRecoveryRequired", "readbackUnavailable"]
|
|
95
|
+
: ["requestId", "lookupKey", "applyId", "requestDigest", "readbackRequest", "written"];
|
|
96
|
+
if (Object.keys(value).some(key => !allowed.includes(key)) || !digest(value.lookupKey) || !digest(value.applyId)) throw new Error("Recovery details require complete identities");
|
|
97
|
+
const requestPresent = typeof value.requestId === "string" && /^[a-zA-Z0-9_.:-]+$/.test(value.requestId);
|
|
98
|
+
if (manual) {
|
|
99
|
+
if (value.manualRecoveryRequired !== true || value.readbackUnavailable !== "details-budget-exceeded"
|
|
100
|
+
|| (requestPresent ? Object.hasOwn(value, "requestIdUnavailable") : value.requestIdUnavailable !== true || Object.hasOwn(value, "requestId"))) throw new Error("Manual-only recovery must be explicit");
|
|
101
|
+
} else {
|
|
102
|
+
if (!requestPresent) throw new Error("Recovery details require the original requestId");
|
|
103
|
+
if (Object.hasOwn(value, "requestDigest") && !digest(value.requestDigest)) throw new Error("Recovery request digest is invalid");
|
|
104
|
+
if (Object.hasOwn(value, "readbackRequest")) {
|
|
105
|
+
const request = value.readbackRequest as unknown as ProjectionRequestV1;
|
|
106
|
+
if (projectionApplyReadbackRequestInvariantIssues(request).length || request.requestId !== value.requestId) throw new Error("Recovery readback request is invalid");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (Object.hasOwn(value, "written") && typeof value.written !== "boolean") throw new Error("Recovery written flag must be boolean");
|
|
110
|
+
return value;
|
|
111
|
+
}
|