foottrace 1.1.0 → 1.3.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 (110) hide show
  1. package/README.md +121 -2
  2. package/dist/cause.js +14 -0
  3. package/dist/esm/cause.d.ts +8 -0
  4. package/dist/esm/cause.js +5 -0
  5. package/dist/esm/index.d.ts +6 -0
  6. package/dist/esm/index.js +1 -0
  7. package/dist/esm/lib/cause/explain.d.ts +5 -0
  8. package/dist/esm/lib/cause/explain.js +114 -0
  9. package/dist/esm/lib/cause/queryMemo.d.ts +18 -0
  10. package/dist/esm/lib/cause/queryMemo.js +46 -0
  11. package/dist/esm/lib/cause/ranking.d.ts +6 -0
  12. package/dist/esm/lib/cause/ranking.js +10 -0
  13. package/dist/esm/lib/cause/recordAdapter.d.ts +9 -0
  14. package/dist/esm/lib/cause/recordAdapter.js +42 -0
  15. package/dist/esm/lib/cause/recordChildren.d.ts +4 -0
  16. package/dist/esm/lib/cause/recordChildren.js +78 -0
  17. package/dist/esm/lib/cause/recordControl.d.ts +13 -0
  18. package/dist/esm/lib/cause/recordControl.js +66 -0
  19. package/dist/esm/lib/cause/recordCoverage.d.ts +24 -0
  20. package/dist/esm/lib/cause/recordCoverage.js +87 -0
  21. package/dist/esm/lib/cause/recordIdentity.d.ts +9 -0
  22. package/dist/esm/lib/cause/recordIdentity.js +67 -0
  23. package/dist/esm/lib/cause/recordInspection.d.ts +4 -0
  24. package/dist/esm/lib/cause/recordInspection.js +86 -0
  25. package/dist/esm/lib/cause/recordLoading.d.ts +7 -0
  26. package/dist/esm/lib/cause/recordLoading.js +116 -0
  27. package/dist/esm/lib/cause/recordQuery.d.ts +9 -0
  28. package/dist/esm/lib/cause/recordQuery.js +22 -0
  29. package/dist/esm/lib/cause/recordScope.d.ts +8 -0
  30. package/dist/esm/lib/cause/recordScope.js +60 -0
  31. package/dist/esm/lib/cause/recordTypes.d.ts +67 -0
  32. package/dist/esm/lib/cause/recordTypes.js +1 -0
  33. package/dist/esm/lib/cause/recordValidation.d.ts +33 -0
  34. package/dist/esm/lib/cause/recordValidation.js +198 -0
  35. package/dist/esm/lib/cause/session.d.ts +30 -0
  36. package/dist/esm/lib/cause/session.js +164 -0
  37. package/dist/esm/lib/cause/storedRecordAdapter.d.ts +11 -0
  38. package/dist/esm/lib/cause/storedRecordAdapter.js +63 -0
  39. package/dist/esm/lib/cause/strategies.d.ts +3 -0
  40. package/dist/esm/lib/cause/strategies.js +21 -0
  41. package/dist/esm/lib/cause/types.d.ts +91 -0
  42. package/dist/esm/lib/cause/types.js +1 -0
  43. package/dist/esm/lib/cause/validation.d.ts +24 -0
  44. package/dist/esm/lib/cause/validation.js +117 -0
  45. package/dist/esm/lib/cause/walk.d.ts +12 -0
  46. package/dist/esm/lib/cause/walk.js +174 -0
  47. package/dist/esm/lib/evidence/admission.d.ts +31 -0
  48. package/dist/esm/lib/evidence/admission.js +72 -0
  49. package/dist/esm/lib/evidence/codes.d.ts +33 -0
  50. package/dist/esm/lib/evidence/codes.js +28 -0
  51. package/dist/esm/lib/evidence/requirementTypes.d.ts +46 -0
  52. package/dist/esm/lib/evidence/requirementTypes.js +1 -0
  53. package/dist/esm/lib/evidence/requirements.d.ts +24 -0
  54. package/dist/esm/lib/evidence/requirements.js +185 -0
  55. package/dist/esm/lib/memory/types.d.ts +4 -1
  56. package/dist/esm/lib/memory/types.js +1 -1
  57. package/dist/index.js +5 -1
  58. package/dist/lib/cause/explain.js +119 -0
  59. package/dist/lib/cause/queryMemo.js +52 -0
  60. package/dist/lib/cause/ranking.js +14 -0
  61. package/dist/lib/cause/recordAdapter.js +48 -0
  62. package/dist/lib/cause/recordChildren.js +82 -0
  63. package/dist/lib/cause/recordControl.js +74 -0
  64. package/dist/lib/cause/recordCoverage.js +99 -0
  65. package/dist/lib/cause/recordIdentity.js +73 -0
  66. package/dist/lib/cause/recordInspection.js +90 -0
  67. package/dist/lib/cause/recordLoading.js +121 -0
  68. package/dist/lib/cause/recordQuery.js +27 -0
  69. package/dist/lib/cause/recordScope.js +66 -0
  70. package/dist/lib/cause/recordTypes.js +2 -0
  71. package/dist/lib/cause/recordValidation.js +213 -0
  72. package/dist/lib/cause/session.js +168 -0
  73. package/dist/lib/cause/storedRecordAdapter.js +67 -0
  74. package/dist/lib/cause/strategies.js +26 -0
  75. package/dist/lib/cause/types.js +2 -0
  76. package/dist/lib/cause/validation.js +133 -0
  77. package/dist/lib/cause/walk.js +180 -0
  78. package/dist/lib/evidence/admission.js +80 -0
  79. package/dist/lib/evidence/codes.js +33 -0
  80. package/dist/lib/evidence/requirementTypes.js +2 -0
  81. package/dist/lib/evidence/requirements.js +190 -0
  82. package/dist/lib/memory/types.js +1 -1
  83. package/dist/types/cause.d.ts +8 -0
  84. package/dist/types/index.d.ts +6 -0
  85. package/dist/types/lib/cause/explain.d.ts +5 -0
  86. package/dist/types/lib/cause/queryMemo.d.ts +18 -0
  87. package/dist/types/lib/cause/ranking.d.ts +6 -0
  88. package/dist/types/lib/cause/recordAdapter.d.ts +9 -0
  89. package/dist/types/lib/cause/recordChildren.d.ts +4 -0
  90. package/dist/types/lib/cause/recordControl.d.ts +13 -0
  91. package/dist/types/lib/cause/recordCoverage.d.ts +24 -0
  92. package/dist/types/lib/cause/recordIdentity.d.ts +9 -0
  93. package/dist/types/lib/cause/recordInspection.d.ts +4 -0
  94. package/dist/types/lib/cause/recordLoading.d.ts +7 -0
  95. package/dist/types/lib/cause/recordQuery.d.ts +9 -0
  96. package/dist/types/lib/cause/recordScope.d.ts +8 -0
  97. package/dist/types/lib/cause/recordTypes.d.ts +67 -0
  98. package/dist/types/lib/cause/recordValidation.d.ts +33 -0
  99. package/dist/types/lib/cause/session.d.ts +30 -0
  100. package/dist/types/lib/cause/storedRecordAdapter.d.ts +11 -0
  101. package/dist/types/lib/cause/strategies.d.ts +3 -0
  102. package/dist/types/lib/cause/types.d.ts +91 -0
  103. package/dist/types/lib/cause/validation.d.ts +24 -0
  104. package/dist/types/lib/cause/walk.d.ts +12 -0
  105. package/dist/types/lib/evidence/admission.d.ts +31 -0
  106. package/dist/types/lib/evidence/codes.d.ts +33 -0
  107. package/dist/types/lib/evidence/requirementTypes.d.ts +46 -0
  108. package/dist/types/lib/evidence/requirements.d.ts +24 -0
  109. package/dist/types/lib/memory/types.d.ts +4 -1
  110. package/package.json +67 -13
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pathTo = exports.frontier = exports.walk = void 0;
4
+ const validation_js_1 = require("./validation.js");
5
+ /** BFS discovers the shallowest arrival; a repeated diamond node is not a cycle. */
6
+ async function walk(session, seeds, direction) {
7
+ const queue = [], seen = new Set(), marks = [], edges = [], missing = [];
8
+ const enqueue = (id, depth) => {
9
+ if (seen.has(id))
10
+ return;
11
+ const refusal = session.admit(id);
12
+ if (refusal) {
13
+ missing.push(refusal);
14
+ return;
15
+ }
16
+ seen.add(id);
17
+ queue.push([id, depth]);
18
+ };
19
+ for (const id of seeds)
20
+ enqueue(id, 0);
21
+ for (let index = 0; index < queue.length; index++) {
22
+ const [id, depth] = queue[index], stopped = session.stopGap(id);
23
+ if (stopped) {
24
+ missing.push(stopped);
25
+ break;
26
+ }
27
+ const marked = await session.read('attention', id, validation_js_1.attention);
28
+ if (marked.gap)
29
+ missing.push(marked.gap);
30
+ if (marked.value?.length) {
31
+ marks.push(Object.freeze({ nodeId: id, attention: marked.value }));
32
+ if (marked.value.some((item) => item.kind === 'not-collected'))
33
+ missing.push((0, validation_js_1.gap)('not-collected', id));
34
+ }
35
+ const coverage = await session.read('gaps', id, validation_js_1.gaps);
36
+ if (coverage.gap)
37
+ missing.push(coverage.gap);
38
+ if (coverage.value)
39
+ missing.push(...coverage.value);
40
+ const page = await session.read(direction, id, (raw) => (0, validation_js_1.adjacency)(raw, session.limits.maxEdges));
41
+ if (page.gap) {
42
+ missing.push(page.gap);
43
+ continue;
44
+ }
45
+ if (!page.value)
46
+ continue;
47
+ if (!page.value.complete)
48
+ missing.push((0, validation_js_1.gap)('not-collected', id));
49
+ if (depth >= session.limits.maxDepth) {
50
+ if (page.value.edges.length)
51
+ missing.push((0, validation_js_1.gap)('depth-limit', id));
52
+ continue;
53
+ }
54
+ for (const edge of page.value.edges) {
55
+ if (session.edges >= session.limits.maxEdges) {
56
+ missing.push((0, validation_js_1.gap)('edge-limit', id));
57
+ break;
58
+ }
59
+ session.edges++;
60
+ const refusal = session.admit(edge.nodeId);
61
+ if (refusal) {
62
+ missing.push(refusal);
63
+ continue;
64
+ }
65
+ edges.push(Object.freeze({
66
+ from: id,
67
+ to: edge.nodeId,
68
+ kind: edge.kind,
69
+ attribution: edge.attribution,
70
+ evidence: edge.evidence,
71
+ }));
72
+ if (edge.attribution === 'stage')
73
+ missing.push((0, validation_js_1.gap)('conservative', id));
74
+ enqueue(edge.nodeId, depth + 1);
75
+ }
76
+ }
77
+ if (hasCycle(edges))
78
+ missing.push((0, validation_js_1.gap)('cycle'));
79
+ return Object.freeze({
80
+ nodes: Object.freeze(queue.map((row) => row[0])),
81
+ marked: Object.freeze(marks),
82
+ edges: Object.freeze(edges),
83
+ gaps: (0, validation_js_1.uniqueGaps)(missing),
84
+ });
85
+ }
86
+ exports.walk = walk;
87
+ /** Iterative three-colour walk; shared nodes remain distinguishable from back edges. */
88
+ function hasCycle(edges) {
89
+ const next = new Map();
90
+ for (const edge of edges) {
91
+ if (!next.has(edge.from))
92
+ next.set(edge.from, []);
93
+ next.get(edge.from).push(edge.to);
94
+ }
95
+ const state = new Map();
96
+ for (const seed of next.keys()) {
97
+ if (state.has(seed))
98
+ continue;
99
+ const stack = [[seed, 0]];
100
+ state.set(seed, 1);
101
+ while (stack.length) {
102
+ const frame = stack[stack.length - 1], children = next.get(frame[0]) ?? [];
103
+ if (frame[1] >= children.length) {
104
+ state.set(frame[0], 2);
105
+ stack.pop();
106
+ continue;
107
+ }
108
+ const child = children[frame[1]++];
109
+ if (state.get(child) === 1)
110
+ return true;
111
+ if (!state.has(child)) {
112
+ state.set(child, 1);
113
+ stack.push([child, 0]);
114
+ }
115
+ }
116
+ }
117
+ return false;
118
+ }
119
+ function frontier(result) {
120
+ const marked = new Set(result.marked
121
+ .filter((item) => item.attention.some((mark) => mark.kind !== 'not-collected'))
122
+ .map((item) => item.nodeId));
123
+ const pending = new Map(result.nodes.map((id) => [id, 0]));
124
+ const dependents = new Map();
125
+ for (const edge of result.edges) {
126
+ pending.set(edge.from, (pending.get(edge.from) ?? 0) + 1);
127
+ if (!dependents.has(edge.to))
128
+ dependents.set(edge.to, []);
129
+ dependents.get(edge.to).push(edge.from);
130
+ }
131
+ // Process causal ancestors first, propagating one boolean through the DAG.
132
+ // This is O(nodes + edges), rather than a fresh ancestry scan per marked node.
133
+ const queue = [...pending].filter(([, count]) => count === 0).map(([id]) => id);
134
+ const earlierMark = new Set(), eligible = new Set();
135
+ for (let i = 0; i < queue.length; i++) {
136
+ const id = queue[i];
137
+ if (marked.has(id) && !earlierMark.has(id))
138
+ eligible.add(id);
139
+ for (const dependent of dependents.get(id) ?? []) {
140
+ if (marked.has(id) || earlierMark.has(id))
141
+ earlierMark.add(dependent);
142
+ const count = pending.get(dependent) - 1;
143
+ pending.set(dependent, count);
144
+ if (count === 0)
145
+ queue.push(dependent);
146
+ }
147
+ }
148
+ // Nodes left in a cycle cannot supply a validated earliest ancestor.
149
+ return result.marked.filter((item) => eligible.has(item.nodeId));
150
+ }
151
+ exports.frontier = frontier;
152
+ function pathTo(edges, start, target) {
153
+ const queue = [start], previous = new Map([[start, undefined]]);
154
+ const next = new Map();
155
+ for (const edge of edges) {
156
+ if (!next.has(edge.from))
157
+ next.set(edge.from, []);
158
+ next.get(edge.from).push(edge.to);
159
+ }
160
+ for (let i = 0; i < queue.length; i++) {
161
+ const id = queue[i];
162
+ if (id === target)
163
+ break;
164
+ for (const child of next.get(id) ?? [])
165
+ if (!previous.has(child)) {
166
+ previous.set(child, id);
167
+ queue.push(child);
168
+ }
169
+ }
170
+ if (!previous.has(target))
171
+ return Object.freeze([]);
172
+ const path = [];
173
+ let id = target;
174
+ while (id !== undefined) {
175
+ path.push(id);
176
+ id = previous.get(id);
177
+ }
178
+ return Object.freeze(path.reverse());
179
+ }
180
+ exports.pathTo = pathTo;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.spendFields = exports.boundedList = exports.boundedName = exports.ownData = exports.EvidenceRefusal = void 0;
4
+ /** One refusal representation; subclasses preserve the graph-port and record-loader boundaries. */
5
+ class EvidenceRefusal extends Error {
6
+ code;
7
+ constructor(code, message) {
8
+ super(message);
9
+ this.code = code;
10
+ }
11
+ }
12
+ exports.EvidenceRefusal = EvidenceRefusal;
13
+ /** Read data descriptors only; each boundary keeps its existing treatment of proxy traps. */
14
+ function ownData(value, key, refuse, policy) {
15
+ if (!value || typeof value !== 'object')
16
+ throw refuse();
17
+ let descriptor;
18
+ if (policy.translateTrapErrors) {
19
+ try {
20
+ descriptor = Object.getOwnPropertyDescriptor(value, key);
21
+ }
22
+ catch {
23
+ throw refuse();
24
+ }
25
+ }
26
+ else
27
+ descriptor = Object.getOwnPropertyDescriptor(value, key);
28
+ if (!descriptor)
29
+ return undefined;
30
+ if (policy.requireOwnValue ? !Object.hasOwn(descriptor, 'value') : !('value' in descriptor))
31
+ throw refuse();
32
+ return descriptor.value;
33
+ }
34
+ exports.ownData = ownData;
35
+ /** Names share one grammar, with explicit limits and the record path separator exception. */
36
+ function boundedName(value, maximum, refuse, path = false) {
37
+ if (typeof value !== 'string' ||
38
+ !value.length ||
39
+ value.length > maximum ||
40
+ (path ? /[\u0000-\u001e\u007f-\u009f]/ : /[\u0000-\u001f\u007f-\u009f]/).test(value))
41
+ throw refuse();
42
+ return value;
43
+ }
44
+ exports.boundedName = boundedName;
45
+ /** Bound iteration before reading entries, without invoking element accessors. */
46
+ function boundedList(value, maximum, read, options) {
47
+ const { refuse, overflow, freeze, descriptors } = options;
48
+ let array;
49
+ if (options.translateArrayErrors) {
50
+ try {
51
+ array = Array.isArray(value);
52
+ }
53
+ catch {
54
+ throw refuse();
55
+ }
56
+ }
57
+ else
58
+ array = Array.isArray(value);
59
+ if (!array)
60
+ throw refuse();
61
+ const length = ownData(value, 'length', refuse, descriptors);
62
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
63
+ throw refuse();
64
+ if (options.expectedLength !== undefined && length !== options.expectedLength)
65
+ throw refuse();
66
+ if (length > maximum)
67
+ throw refuse(overflow);
68
+ const result = [];
69
+ for (let index = 0; index < length; index++)
70
+ result.push(read(ownData(value, String(index), refuse, descriptors)));
71
+ return freeze ? Object.freeze(result) : result;
72
+ }
73
+ exports.boundedList = boundedList;
74
+ /** Charge retained fields against the same mutable allowance across admission helpers. */
75
+ function spendFields(budget, count, refuse) {
76
+ budget.left -= count;
77
+ if (budget.left < 0)
78
+ throw refuse('node-limit');
79
+ }
80
+ exports.spendFields = spendFields;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isRequirementReason = exports.isGapCode = exports.GAP_CODES = void 0;
4
+ /** One vocabulary; true marks a domain evidence reason, false a trace/admission gap only. */
5
+ exports.GAP_CODES = Object.freeze({
6
+ 'not-collected': true,
7
+ 'untracked-input': false,
8
+ redacted: true,
9
+ conservative: false,
10
+ 'scope-boundary': false,
11
+ 'invalid-data': true,
12
+ unavailable: true,
13
+ timeout: true,
14
+ cancelled: false,
15
+ cycle: false,
16
+ 'node-limit': false,
17
+ 'depth-limit': false,
18
+ 'edge-limit': false,
19
+ 'call-limit': false,
20
+ truncated: false,
21
+ 'strategy-failed': false,
22
+ 'not-checked': true,
23
+ 'no-match': true,
24
+ ambiguous: true,
25
+ stale: true,
26
+ incompatible: true,
27
+ unsupported: true,
28
+ 'not-permitted': true,
29
+ });
30
+ const isGapCode = (value) => typeof value === 'string' && Object.hasOwn(exports.GAP_CODES, value);
31
+ exports.isGapCode = isGapCode;
32
+ const isRequirementReason = (value) => (0, exports.isGapCode)(value) && exports.GAP_CODES[value];
33
+ exports.isRequirementReason = isRequirementReason;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,190 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readRequirementResults = exports.RequirementValidationError = exports.REQUIREMENT_RESULT_LIMITS = void 0;
4
+ const admission_js_1 = require("./admission.js");
5
+ const codes_js_1 = require("./codes.js");
6
+ /** Fixed per-call ceilings; characters are UTF-16 code units, not serialized transport bytes. */
7
+ exports.REQUIREMENT_RESULT_LIMITS = Object.freeze({
8
+ results: 512,
9
+ checks: 16,
10
+ affects: 32,
11
+ refs: 16,
12
+ name: 512,
13
+ fields: 32768,
14
+ characters: 1048576,
15
+ });
16
+ /** No input, field value or originating exception is retained on a refusal. */
17
+ class RequirementValidationError extends admission_js_1.EvidenceRefusal {
18
+ constructor(code = 'invalid-data') {
19
+ super(code, 'REQUIREMENT_RESULTS_INVALID');
20
+ }
21
+ }
22
+ exports.RequirementValidationError = RequirementValidationError;
23
+ const refuse = (code) => new RequirementValidationError(code);
24
+ const descriptors = { translateTrapErrors: true, requireOwnValue: true };
25
+ const own = (value, key) => (0, admission_js_1.ownData)(value, key, refuse, descriptors);
26
+ const resultKeys = new Set([
27
+ 'id',
28
+ 'requirementRef',
29
+ 'findingRef',
30
+ 'windowRef',
31
+ 'affects',
32
+ 'state',
33
+ 'reason',
34
+ 'detail',
35
+ 'evidenceRefs',
36
+ 'checks',
37
+ 'actionRefs',
38
+ 'validTimeRef',
39
+ 'learnedTimeRef',
40
+ ]);
41
+ const checkKeys = new Set(['checkRef', 'disposition', 'reason', 'detail', 'evidenceRefs', 'attemptRefs']);
42
+ const dispositions = new Set([
43
+ 'checked-pass',
44
+ 'checked-fail',
45
+ 'unreachable',
46
+ 'not-applicable',
47
+ ]);
48
+ /** Closed objects expose no unknown payloads. Never invoke data accessors, iterators or toJSON. */
49
+ function object(value, keys, budget) {
50
+ if (!value || typeof value !== 'object')
51
+ throw refuse();
52
+ let prototype;
53
+ let fields;
54
+ try {
55
+ prototype = Object.getPrototypeOf(value);
56
+ fields = Reflect.ownKeys(value);
57
+ }
58
+ catch {
59
+ throw refuse();
60
+ }
61
+ if (prototype !== Object.prototype && prototype !== null)
62
+ throw refuse();
63
+ if (fields.length > keys.size || fields.some((field) => typeof field !== 'string' || !keys.has(field)))
64
+ throw refuse();
65
+ (0, admission_js_1.spendFields)(budget, fields.length, refuse);
66
+ // Including optional own fields: an own accessor is never treated as an absent field.
67
+ for (const field of fields)
68
+ own(value, field);
69
+ }
70
+ function list(value, maximum, read, budget) {
71
+ try {
72
+ if (!Array.isArray(value))
73
+ throw refuse();
74
+ }
75
+ catch {
76
+ throw refuse();
77
+ }
78
+ const length = own(value, 'length');
79
+ if (typeof length !== 'number' || !Number.isSafeInteger(length) || length < 0)
80
+ throw refuse();
81
+ if (length > maximum)
82
+ throw refuse('node-limit');
83
+ let prototype;
84
+ let keys;
85
+ try {
86
+ prototype = Object.getPrototypeOf(value);
87
+ keys = Reflect.ownKeys(value);
88
+ }
89
+ catch {
90
+ throw refuse();
91
+ }
92
+ if (prototype !== Array.prototype)
93
+ throw refuse();
94
+ if (keys.length !== length + 1 ||
95
+ keys.some((key) => key !== 'length' && (typeof key !== 'string' || !/^(0|[1-9]\d*)$/.test(key) || Number(key) >= length)))
96
+ throw refuse();
97
+ (0, admission_js_1.spendFields)(budget, length + 1, refuse);
98
+ return (0, admission_js_1.boundedList)(value, maximum, read, {
99
+ refuse,
100
+ overflow: 'node-limit',
101
+ freeze: true,
102
+ descriptors,
103
+ expectedLength: length,
104
+ translateArrayErrors: true,
105
+ });
106
+ }
107
+ function name(value, budget) {
108
+ const result = (0, admission_js_1.boundedName)(value, exports.REQUIREMENT_RESULT_LIMITS.name, refuse);
109
+ if (/\s/u.test(result))
110
+ throw refuse();
111
+ budget.characters -= result.length;
112
+ if (budget.characters < 0)
113
+ throw refuse('node-limit');
114
+ return result;
115
+ }
116
+ function unique(items, key) {
117
+ if (new Set(items.map(key)).size !== items.length)
118
+ throw refuse();
119
+ return items;
120
+ }
121
+ function refs(value, budget, maximum = exports.REQUIREMENT_RESULT_LIMITS.refs) {
122
+ return unique(list(value, maximum, (item) => name(item, budget), budget), (item) => item);
123
+ }
124
+ function reason(value) {
125
+ if (!(0, codes_js_1.isRequirementReason)(value))
126
+ throw refuse();
127
+ return value;
128
+ }
129
+ function optionalName(value, key, budget) {
130
+ const raw = own(value, key);
131
+ return raw === undefined ? undefined : name(raw, budget);
132
+ }
133
+ function detail(value, budget) {
134
+ const id = optionalName(value, 'detail', budget);
135
+ if (id !== undefined && !/^[A-Za-z][A-Za-z0-9_.-]*:.+$/.test(id))
136
+ throw refuse();
137
+ return id;
138
+ }
139
+ function check(value, budget) {
140
+ object(value, checkKeys, budget);
141
+ const checkRef = name(own(value, 'checkRef'), budget), disposition = own(value, 'disposition');
142
+ if (typeof disposition !== 'string' || !dispositions.has(disposition))
143
+ throw refuse();
144
+ const rawReason = own(value, 'reason'), detailId = detail(value, budget);
145
+ return Object.freeze({
146
+ checkRef,
147
+ disposition: disposition,
148
+ ...(rawReason === undefined ? {} : { reason: reason(rawReason) }),
149
+ ...(detailId === undefined ? {} : { detail: detailId }),
150
+ evidenceRefs: refs(own(value, 'evidenceRefs'), budget),
151
+ attemptRefs: refs(own(value, 'attemptRefs'), budget),
152
+ });
153
+ }
154
+ function result(value, budget) {
155
+ object(value, resultKeys, budget);
156
+ const id = name(own(value, 'id'), budget), requirementRef = name(own(value, 'requirementRef'), budget), findingRef = name(own(value, 'findingRef'), budget), windowRef = name(own(value, 'windowRef'), budget), state = own(value, 'state'), rawReason = own(value, 'reason');
157
+ if (state !== 'satisfied' && state !== 'unresolved')
158
+ throw refuse();
159
+ if (state === 'satisfied' && rawReason !== undefined)
160
+ throw refuse();
161
+ const affects = refs(own(value, 'affects'), budget, exports.REQUIREMENT_RESULT_LIMITS.affects);
162
+ if (!affects.length)
163
+ throw refuse();
164
+ const evidenceRefs = refs(own(value, 'evidenceRefs'), budget), checks = unique(list(own(value, 'checks'), exports.REQUIREMENT_RESULT_LIMITS.checks, (item) => check(item, budget), budget), (item) => item.checkRef), actionRefs = refs(own(value, 'actionRefs'), budget), detailId = detail(value, budget), validTimeRef = optionalName(value, 'validTimeRef', budget), learnedTimeRef = optionalName(value, 'learnedTimeRef', budget);
165
+ const binding = {
166
+ id,
167
+ requirementRef,
168
+ findingRef,
169
+ windowRef,
170
+ affects,
171
+ evidenceRefs,
172
+ checks,
173
+ actionRefs,
174
+ ...(detailId === undefined ? {} : { detail: detailId }),
175
+ ...(validTimeRef === undefined ? {} : { validTimeRef }),
176
+ ...(learnedTimeRef === undefined ? {} : { learnedTimeRef }),
177
+ };
178
+ return Object.freeze(state === 'unresolved' ? { ...binding, state, reason: reason(rawReason) } : { ...binding, state });
179
+ }
180
+ /**
181
+ * Atomically project detached, frozen reference-only records. This is structural admission only.
182
+ * The host must authenticate producers, resolve authorized artifacts, verify current query/run/scope and
183
+ * domain validity, and enforce every unresolved affects reference before rendering any factual claim.
184
+ * Identifier grammar does not classify secrets; producers must supply non-secret identifiers only.
185
+ */
186
+ function readRequirementResults(value) {
187
+ const budget = { left: exports.REQUIREMENT_RESULT_LIMITS.fields, characters: exports.REQUIREMENT_RESULT_LIMITS.characters };
188
+ return unique(list(value, exports.REQUIREMENT_RESULT_LIMITS.results, (item) => result(item, budget), budget), (item) => item.id);
189
+ }
190
+ exports.readRequirementResults = readRequirementResults;
@@ -3,7 +3,7 @@
3
3
  * types.ts — the record's types: a bundle, its rows, its patches, its encoding, and the
4
4
  * execution tree as the record's readers read it.
5
5
  *
6
- * Imports nothing. The standalone record names nothing outside itself: the engine's frame
6
+ * Imports only the record's leaf attention type. The record names nothing outside itself: the engine's frame
7
7
  * types — the stage snapshot, flow messages and retention dials — remain in FootPrint's
8
8
  * `frameTypes.ts`. The closed record graph is enforced by scripts/check-layering.mjs.
9
9
  */
@@ -0,0 +1,8 @@
1
+ /** Optional read-only cause and impact queries over recorded dependency graphs. */
2
+ export { explain, attentionList } from './lib/cause/explain.js';
3
+ export { largestDrop, mostAffected } from './lib/cause/strategies.js';
4
+ export { createRecordGraph } from './lib/cause/recordAdapter.js';
5
+ export { createStoredRecordGraph } from './lib/cause/storedRecordAdapter.js';
6
+ export type { Adjacency, AttentionListOptions, AttentionListResult, AttentionReason, CauseGraph, CauseResult, Confidence, ExplainOptions, Gap, GapCode, GraphContext, GraphEdge, ImpactResult, ImpactStrategy, MarkedNode, MaybePromise, NodeId, RootStrategy, TraversedEdge, WalkLimits, } from './lib/cause/types.js';
7
+ export type { RecordScope, RecordGraphOptions, RecordCauseGraph, RecordNode } from './lib/cause/recordAdapter.js';
8
+ export type { StoredRecordScope, StoredRecordHost } from './lib/cause/storedRecordAdapter.js';
@@ -28,3 +28,9 @@ export type { EmitSourcePosition, LogAddress } from './lib/memory/eventPosition.
28
28
  export { EXECUTION_DELIMITER, PATH_DELIMITER, isExecutionKey, joinPath, pathSegments as idPathSegments, stageIdOf, subflowSegmentsOf, } from './lib/ids/runtimeStageId.js';
29
29
  export { deepEqual } from './lib/memory/equality.js';
30
30
  export { serveRecord } from './lib/capture/freeze.js';
31
+ /** Record attention types are shared with producers; the optional cause reader has its own door. */
32
+ export type { Attention, AttentionKind } from './lib/cause/types.js';
33
+ /** Reference-only requirement records: structural admission never grants authority or verifies domain truth. */
34
+ export type { RequirementReason } from './lib/evidence/codes.js';
35
+ export type { RequirementCheck, RequirementCheckDisposition, RequirementResult, } from './lib/evidence/requirementTypes.js';
36
+ export { readRequirementResults, RequirementValidationError, REQUIREMENT_RESULT_LIMITS, } from './lib/evidence/requirements.js';
@@ -0,0 +1,5 @@
1
+ import type { AttentionListOptions, AttentionListResult, CauseGraph, CauseResult, ExplainOptions, NodeId } from './types.js';
2
+ /** Read-only deterministic cause localisation and impact. No inference from timestamps or structural ancestry. */
3
+ export declare function explain(graph: CauseGraph, nodeId: NodeId, options?: ExplainOptions): Promise<CauseResult>;
4
+ /** Discover only bounded host seeds; this is not a global scan of arbitrary storage. */
5
+ export declare function attentionList(graph: CauseGraph, options?: AttentionListOptions): Promise<AttentionListResult>;
@@ -0,0 +1,18 @@
1
+ import type { GraphContext } from './types.js';
2
+ /** Share bounded derived work within one query without retaining it in the graph's storage cache. */
3
+ declare class QueryMemo {
4
+ private readonly maximumEntries;
5
+ private readonly owners;
6
+ private entries;
7
+ private closed;
8
+ constructor(maximumEntries: number);
9
+ remember<T>(owner: object, key: unknown, create: () => T): T;
10
+ close(): void;
11
+ }
12
+ /** Reserve one resolution and one inspection per admitted node, shared across all adapter owners. */
13
+ export declare function beginQueryMemo(context: GraphContext, maximumNodes: number): void;
14
+ /** Direct graph calls have no managed query lifetime and deliberately remain uncached. */
15
+ export declare function queryMemo(context: GraphContext): QueryMemo | undefined;
16
+ /** Clear synchronously without aborting an otherwise successful query's public signal. */
17
+ export declare function endQueryMemo(context: GraphContext): void;
18
+ export {};
@@ -0,0 +1,6 @@
1
+ import type { NodeId } from './types.js';
2
+ /** Strategies may only select admitted nodes; impact ranking must preserve the full set. */
3
+ export declare function admitRanking(raw: unknown, allowed: ReadonlySet<NodeId>, options: {
4
+ readonly exactPermutation: boolean;
5
+ readonly maximum: number;
6
+ }): readonly NodeId[];
@@ -0,0 +1,9 @@
1
+ import type { LogAddress } from '../memory/eventPosition.js';
2
+ import type { RecordCauseGraph, RecordGraphOptions, RecordLoaderResult, RecordScope } from './recordTypes.js';
3
+ import type { GraphContext } from './types.js';
4
+ export type { RecordCauseGraph, RecordGraphOptions, RecordLoaderResult, RecordNode, RecordScope, } from './recordTypes.js';
5
+ export { recordOptions } from './recordScope.js';
6
+ /** Common engine for memory and one-scope-at-a-time host storage. Loader functions are trusted executable code. */
7
+ export declare function createLazyRecordGraph(load: (address: LogAddress, context: GraphContext) => RecordLoaderResult | Promise<RecordLoaderResult>, options?: RecordGraphOptions): RecordCauseGraph;
8
+ /** A finished record already in memory; payload admission is lazy per requested scope. */
9
+ export declare function createRecordGraph(scopes: readonly RecordScope[], options?: RecordGraphOptions): RecordCauseGraph;
@@ -0,0 +1,4 @@
1
+ import type { RecordInspection, ResolvedRecordNode } from './recordTypes.js';
2
+ import type { Adjacency } from './types.js';
3
+ /** Share parent-side coverage checks before appending data then control children in their established order. */
4
+ export declare function childrenForRecordNode(node: ResolvedRecordNode, maximumEdges: number, inspection: RecordInspection): Adjacency;
@@ -0,0 +1,13 @@
1
+ import type { ControlDependency } from '../memory/backtrack.js';
2
+ import type { AddRecordGap, PreparedRecordScope, RecordIdentity } from './recordTypes.js';
3
+ import type { GraphEdge } from './types.js';
4
+ /** Only an execution or captured read may govern control flow; a written key alone cannot. */
5
+ export declare function controlChildren(scope: PreparedRecordScope, identity: RecordIdentity): Iterable<readonly [string, ControlDependency]>;
6
+ /** Canonical slicing receives only control parents that existed before the dependent execution. */
7
+ export declare function controlParentLookup(scope: PreparedRecordScope): ((runtimeStageId: string) => ControlDependency | undefined) | undefined;
8
+ /** Keep missing or future deciders visible rather than silently dropping their recorded dependency. */
9
+ export declare function collectControlParentCoverage(scope: PreparedRecordScope, identity: RecordIdentity, firstCommitIndex: number, addGap: AddRecordGap): void;
10
+ /** Include control fan-out in the same immediate-neighbor cap used for data adjacency. */
11
+ export declare function collectControlChildCoverage(scope: PreparedRecordScope, identity: RecordIdentity, firstCommitIndex: number, neighbors: Set<string>, addGap: AddRecordGap): void;
12
+ /** Preserve recorded control order and coverage when a dependent execution is absent or capped. */
13
+ export declare function appendControlChildren(scope: PreparedRecordScope, identity: RecordIdentity, commitIndex: number, maximumEdges: number, edges: GraphEdge[]): boolean;
@@ -0,0 +1,24 @@
1
+ import type { CommitBundle } from '../memory/types.js';
2
+ import type { AddRecordGap, PreparedRecordScope, RecordIdentity, ResolvedRecordNode } from './recordTypes.js';
3
+ import type { Attention, Gap } from './types.js';
4
+ /** Preserve first-observed coverage order while collapsing repeated codes from different evidence paths. */
5
+ export declare function recordGapCollector(): {
6
+ gaps: Gap[];
7
+ addGap: AddRecordGap;
8
+ };
9
+ /** Failed loading exposes a fixed category, never the host error or an apparently complete empty graph. */
10
+ export declare function gapForRecordFailure(code: Gap['code']): readonly Gap[];
11
+ /** Return captured mark references without treating an omitted attention map as collected evidence. */
12
+ export declare function attentionForRecordNode(node: ResolvedRecordNode): readonly Attention[];
13
+ /** Emit host and capture gaps before query-derived gaps so existing evidence order stays stable. */
14
+ export declare function collectScopeCoverage(scope: PreparedRecordScope, bundle: CommitBundle, extraGapCodes: readonly Gap['code'][] | undefined, addGap: AddRecordGap): void;
15
+ /** A read-only identity has no new value life; unqualified executions retain written-key encounter order. */
16
+ export declare function writtenKeysForNode(bundle: CommitBundle, identity: RecordIdentity): string[];
17
+ /** Immediate adjacency caps affect graph coverage even when the caller asks only for parents. */
18
+ export declare function collectForwardCoverage(scope: PreparedRecordScope, identity: RecordIdentity, commitIndex: number, firstCommitIndex: number, maximumEdges: number, addGap: AddRecordGap): void;
19
+ /** Exact attribution requires captured per-write provenance or a validated read-only key context. */
20
+ export declare function readAttributionIsPrecise(scope: PreparedRecordScope, identity: RecordIdentity, bundle: CommitBundle): boolean;
21
+ /** Distinguish conservative attribution from missing collection without marking a proven empty leaf. */
22
+ export declare function collectReadCoverage(scope: PreparedRecordScope, identity: RecordIdentity, precise: boolean, addGap: AddRecordGap): void;
23
+ /** Forward absence is trustworthy only when every potentially consuming execution collected its reads. */
24
+ export declare function allStageReadsCollected(scope: PreparedRecordScope): boolean;
@@ -0,0 +1,9 @@
1
+ import type { LogAddress } from '../memory/eventPosition.js';
2
+ import type { GraphContext, NodeId } from './types.js';
3
+ import type { RecordIdentity, RecordLoadFailure, RecordNode, RecordScopeLoader, ResolvedRecordNode } from './recordTypes.js';
4
+ /** Parse only the bounded identity encoding; payload fields never become graph addresses. */
5
+ export declare function readNodeIdentity(nodeId: string): RecordIdentity;
6
+ /** Stable field order preserves existing address/execution/key IDs across storage and memory readers. */
7
+ export declare function nodeIdFor(addressInput: LogAddress, node: RecordNode, read?: true): NodeId;
8
+ /** An admitted ID must also match captured evidence before it can introduce a dependency. */
9
+ export declare function resolveRecordNode(nodeId: NodeId, context: GraphContext, loader: RecordScopeLoader): Promise<ResolvedRecordNode | RecordLoadFailure>;
@@ -0,0 +1,4 @@
1
+ import type { RecordInspection, ResolvedRecordNode } from './recordTypes.js';
2
+ import type { Gap } from './types.js';
3
+ /** Derive parent adjacency and its ordered gaps together from the same bounded canonical query. */
4
+ export declare function inspectRecordNode(node: ResolvedRecordNode, maximumEdges: number, extraGapCodes: readonly Gap['code'][] | undefined): RecordInspection;
@@ -0,0 +1,7 @@
1
+ import type { LogAddress } from '../memory/eventPosition.js';
2
+ import type { GraphContext, NodeId } from './types.js';
3
+ import type { RecordGraphOptions, RecordScopeHost, RecordScopeLoader } from './recordTypes.js';
4
+ /** Bound caller waiting independently of host cooperation without sharing cancellation between requests. */
5
+ export declare function createRecordScopeLoader(hostLoad: RecordScopeHost, limits: Required<RecordGraphOptions>): RecordScopeLoader;
6
+ /** Discovery must distinguish an unavailable scope from a successfully loaded empty record. */
7
+ export declare function openRecordScope(addressInput: LogAddress, context: GraphContext, loader: RecordScopeLoader): Promise<readonly NodeId[]>;
@@ -0,0 +1,9 @@
1
+ import type { RecordLoadFailure, RecordScopeLoader, ResolvedRecordNode } from './recordTypes.js';
2
+ import type { GraphContext, NodeId } from './types.js';
3
+ /** Both dependency directions share the same admitted node and canonical inspection within a query. */
4
+ export declare function createRecordQuery(loader: RecordScopeLoader, maximumEdges: number): {
5
+ resolve(nodeId: NodeId, context: GraphContext): Promise<RecordLoadFailure | ResolvedRecordNode>;
6
+ inspect(node: ResolvedRecordNode, context: GraphContext): import("./recordTypes.js").RecordInspection;
7
+ };
8
+ /** Every port maps the same fixed loading failure while projecting only admitted nodes. */
9
+ export declare function whenResolved<T>(node: ResolvedRecordNode | RecordLoadFailure, onNode: (node: ResolvedRecordNode) => T, onFailure: (failure: RecordLoadFailure) => T): T;
@@ -0,0 +1,8 @@
1
+ import type { LogAddress } from '../memory/eventPosition.js';
2
+ import type { PreparedRecordScope, RecordGraphOptions, RecordScope } from './recordTypes.js';
3
+ /** Read configuration as data so option accessors cannot execute during adapter construction. */
4
+ export declare function recordOptions(options: RecordGraphOptions): Required<RecordGraphOptions>;
5
+ /** Spend one shared field budget before canonical readers see any untrusted scope data. */
6
+ export declare function prepareRecordScope(value: RecordScope, address: LogAddress, maximumCommits: number, maximumFields: number): PreparedRecordScope;
7
+ /** Preserve both stage reads and stronger per-write prefixes, including their original encounter order. */
8
+ export declare function readKeysForStage(scope: PreparedRecordScope, runtimeStageId: string): string[];