@deftai/directive-core 0.87.0 → 0.89.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 (193) hide show
  1. package/dist/authz/actions.d.ts +55 -0
  2. package/dist/authz/actions.js +125 -0
  3. package/dist/authz/classify.d.ts +23 -0
  4. package/dist/authz/classify.js +217 -0
  5. package/dist/authz/closed-verb.d.ts +42 -0
  6. package/dist/authz/closed-verb.js +279 -0
  7. package/dist/authz/evaluate.d.ts +41 -0
  8. package/dist/authz/evaluate.js +298 -0
  9. package/dist/authz/index.d.ts +15 -0
  10. package/dist/authz/index.js +15 -0
  11. package/dist/authz/origin.d.ts +28 -0
  12. package/dist/authz/origin.js +64 -0
  13. package/dist/authz/paths.d.ts +6 -0
  14. package/dist/authz/paths.js +19 -0
  15. package/dist/authz/store.d.ts +40 -0
  16. package/dist/authz/store.js +317 -0
  17. package/dist/authz/templates.d.ts +139 -0
  18. package/dist/authz/templates.js +241 -0
  19. package/dist/authz/types.d.ts +111 -0
  20. package/dist/authz/types.js +41 -0
  21. package/dist/authz/verb-classification.d.ts +44 -0
  22. package/dist/authz/verb-classification.js +237 -0
  23. package/dist/branch/evaluate.js +6 -1
  24. package/dist/cache/fetch.js +10 -5
  25. package/dist/cache/io.d.ts +9 -2
  26. package/dist/cache/io.js +30 -10
  27. package/dist/cache/scanner.d.ts +11 -1
  28. package/dist/cache/scanner.js +29 -4
  29. package/dist/cache/task-cache/store.js +11 -7
  30. package/dist/capacity/backfill.js +10 -4
  31. package/dist/category-b-namespace/index.js +10 -4
  32. package/dist/check/gate-lists.js +1 -0
  33. package/dist/codebase/default-extractor.js +3 -0
  34. package/dist/codebase/map.js +11 -4
  35. package/dist/content-contracts/skills/helpers.d.ts +10 -0
  36. package/dist/content-contracts/skills/helpers.js +35 -0
  37. package/dist/deposit/copy-tree.d.ts +19 -1
  38. package/dist/deposit/copy-tree.js +134 -5
  39. package/dist/doctor/doctor-state.js +28 -4
  40. package/dist/doctor/main.d.ts +10 -0
  41. package/dist/doctor/main.js +208 -0
  42. package/dist/doctor/types.d.ts +11 -0
  43. package/dist/escalation/actions.d.ts +63 -0
  44. package/dist/escalation/actions.js +137 -0
  45. package/dist/escalation/index.d.ts +8 -0
  46. package/dist/escalation/index.js +8 -0
  47. package/dist/escalation/paths.d.ts +3 -0
  48. package/dist/escalation/paths.js +10 -0
  49. package/dist/escalation/store.d.ts +17 -0
  50. package/dist/escalation/store.js +172 -0
  51. package/dist/escalation/types.d.ts +73 -0
  52. package/dist/escalation/types.js +43 -0
  53. package/dist/eval/crud-telemetry.js +30 -10
  54. package/dist/eval/health.js +22 -7
  55. package/dist/eval/readback.js +11 -10
  56. package/dist/eval/run.js +32 -16
  57. package/dist/events/attribution-enrichment.js +10 -4
  58. package/dist/finish-loop/directive-finish-loop.d.ts +37 -0
  59. package/dist/finish-loop/directive-finish-loop.js +239 -0
  60. package/dist/finish-loop/grant-gate.d.ts +31 -0
  61. package/dist/finish-loop/grant-gate.js +169 -0
  62. package/dist/finish-loop/index.d.ts +11 -0
  63. package/dist/finish-loop/index.js +11 -0
  64. package/dist/finish-loop/main.d.ts +35 -0
  65. package/dist/finish-loop/main.js +365 -0
  66. package/dist/finish-loop/pr-finish-loop.d.ts +34 -0
  67. package/dist/finish-loop/pr-finish-loop.js +217 -0
  68. package/dist/finish-loop/progress.d.ts +17 -0
  69. package/dist/finish-loop/progress.js +45 -0
  70. package/dist/finish-loop/queue.d.ts +16 -0
  71. package/dist/finish-loop/queue.js +65 -0
  72. package/dist/finish-loop/types.d.ts +52 -0
  73. package/dist/finish-loop/types.js +10 -0
  74. package/dist/fs/contained-write.d.ts +101 -0
  75. package/dist/fs/contained-write.js +281 -0
  76. package/dist/fs/projection-containment.d.ts +18 -0
  77. package/dist/fs/projection-containment.js +40 -0
  78. package/dist/hooks/classify/classify.d.ts +10 -0
  79. package/dist/hooks/classify/classify.js +37 -0
  80. package/dist/hooks/classify/index.d.ts +14 -0
  81. package/dist/hooks/classify/index.js +13 -0
  82. package/dist/hooks/classify/paths.d.ts +22 -0
  83. package/dist/hooks/classify/paths.js +72 -0
  84. package/dist/hooks/classify/payload.d.ts +16 -0
  85. package/dist/hooks/classify/payload.js +45 -0
  86. package/dist/hooks/classify/stdin.d.ts +12 -0
  87. package/dist/hooks/classify/stdin.js +63 -0
  88. package/dist/hooks/classify/tool-name.d.ts +18 -0
  89. package/dist/hooks/classify/tool-name.js +85 -0
  90. package/dist/hooks/classify/types.d.ts +41 -0
  91. package/dist/hooks/classify/types.js +7 -0
  92. package/dist/hooks/dispatcher.d.ts +30 -15
  93. package/dist/hooks/dispatcher.js +326 -134
  94. package/dist/hooks/fixtures/cases.d.ts +44 -0
  95. package/dist/hooks/fixtures/cases.js +595 -0
  96. package/dist/hooks/fixtures/index.d.ts +2 -0
  97. package/dist/hooks/fixtures/index.js +2 -0
  98. package/dist/hooks/index.d.ts +2 -0
  99. package/dist/hooks/index.js +2 -0
  100. package/dist/hooks/tools.d.ts +31 -0
  101. package/dist/hooks/tools.js +74 -0
  102. package/dist/index.d.ts +4 -0
  103. package/dist/index.js +5 -0
  104. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  105. package/dist/init-deposit/agent-hooks.js +65 -10
  106. package/dist/init-deposit/gitignore.js +11 -3
  107. package/dist/init-deposit/headless-manifest.js +12 -4
  108. package/dist/init-deposit/hygiene.d.ts +16 -0
  109. package/dist/init-deposit/hygiene.js +26 -0
  110. package/dist/init-deposit/init-dispatch.js +28 -0
  111. package/dist/init-deposit/migrate.d.ts +1 -1
  112. package/dist/init-deposit/migrate.js +13 -3
  113. package/dist/init-deposit/prettierignore.js +12 -5
  114. package/dist/init-deposit/refresh.js +38 -7
  115. package/dist/init-deposit/scaffold.js +42 -29
  116. package/dist/init-deposit/xbrief-projections.js +17 -10
  117. package/dist/intake/candidates-log.js +46 -35
  118. package/dist/intake/github-body-cli.d.ts +6 -0
  119. package/dist/intake/github-body-cli.js +17 -0
  120. package/dist/intake/github-body.d.ts +46 -3
  121. package/dist/intake/github-body.js +191 -20
  122. package/dist/intake/issue-emit.d.ts +1 -0
  123. package/dist/intake/issue-emit.js +41 -23
  124. package/dist/intake/issue-ingest.js +11 -2
  125. package/dist/lifecycle/event-detect.js +12 -4
  126. package/dist/lifecycle/events.js +29 -20
  127. package/dist/lifecycle/lifecycle-hygiene.js +9 -3
  128. package/dist/packs/pack-render.d.ts +33 -0
  129. package/dist/packs/pack-render.js +172 -13
  130. package/dist/packs/quarantine-ext.d.ts +10 -0
  131. package/dist/packs/quarantine-ext.js +26 -2
  132. package/dist/plan-sequence/store.js +11 -4
  133. package/dist/policy/hotfix-criteria.d.ts +79 -0
  134. package/dist/policy/hotfix-criteria.js +197 -0
  135. package/dist/policy/index.d.ts +5 -0
  136. package/dist/policy/index.js +32 -1
  137. package/dist/policy/intent-ceiling.d.ts +79 -0
  138. package/dist/policy/intent-ceiling.js +181 -0
  139. package/dist/policy/no-deft-directive.d.ts +59 -0
  140. package/dist/policy/no-deft-directive.js +110 -0
  141. package/dist/policy/org-force-on-migration.d.ts +52 -0
  142. package/dist/policy/org-force-on-migration.js +269 -27
  143. package/dist/policy/require-human-merge.d.ts +75 -0
  144. package/dist/policy/require-human-merge.js +324 -0
  145. package/dist/policy/resolve.js +17 -6
  146. package/dist/policy/runtime-authority.d.ts +56 -2
  147. package/dist/policy/runtime-authority.js +297 -3
  148. package/dist/policy/write-fence.d.ts +78 -0
  149. package/dist/policy/write-fence.js +164 -0
  150. package/dist/pr-wait-mergeable/cascade.d.ts +10 -0
  151. package/dist/pr-wait-mergeable/cascade.js +28 -0
  152. package/dist/preflight/evaluate.js +10 -0
  153. package/dist/product-signal/consent.js +21 -5
  154. package/dist/product-signal/submit.js +22 -12
  155. package/dist/release/build-dist-runner.js +5 -2
  156. package/dist/release/build-dist.d.ts +19 -1
  157. package/dist/release/build-dist.js +50 -2
  158. package/dist/release/native-steps.js +7 -2
  159. package/dist/release-publish/pipeline.d.ts +13 -0
  160. package/dist/release-publish/pipeline.js +46 -1
  161. package/dist/scope/audit-log.js +19 -24
  162. package/dist/scope/decompose.js +10 -5
  163. package/dist/scope/decomposed-refs.js +18 -3
  164. package/dist/scope/demote.js +9 -2
  165. package/dist/scope/undo.js +9 -2
  166. package/dist/session/release-availability.js +26 -6
  167. package/dist/session/ritual-sentinel.js +19 -12
  168. package/dist/session/session-start-hook.d.ts +3 -0
  169. package/dist/session/session-start-hook.js +15 -0
  170. package/dist/session/session-start.js +37 -0
  171. package/dist/staleness-tickler/state.js +26 -6
  172. package/dist/swarm/launch.js +13 -3
  173. package/dist/swarm/routing.js +9 -3
  174. package/dist/task-surface/index.js +24 -9
  175. package/dist/triage/bootstrap/gitignore.js +53 -10
  176. package/dist/triage/cache-path.js +10 -3
  177. package/dist/triage/welcome/summary.js +11 -4
  178. package/dist/triage/welcome/writers.js +45 -16
  179. package/dist/validate-content/validate-links.js +5 -0
  180. package/dist/value/readback.js +11 -6
  181. package/dist/vbrief-activate/activate.js +12 -4
  182. package/dist/vbrief-build/project-definition-io.js +14 -6
  183. package/dist/verify-source/contained-writes.d.ts +59 -0
  184. package/dist/verify-source/contained-writes.js +272 -0
  185. package/dist/verify-source/cursor-tier1.js +7 -2
  186. package/dist/verify-source/index.d.ts +1 -0
  187. package/dist/verify-source/index.js +1 -0
  188. package/dist/verify-source/openclaw-tier1.js +19 -1
  189. package/dist/verify-source/verify-stubs.js +3 -0
  190. package/dist/xbrief-migrate/migrate-project.js +26 -12
  191. package/dist/xbrief-migrate/transforms.d.ts +4 -2
  192. package/dist/xbrief-migrate/transforms.js +37 -14
  193. package/package.json +16 -3
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Disk store for typed escalations under `.deft/escalations/` (#518).
3
+ */
4
+ import { randomBytes } from "node:crypto";
5
+ import { existsSync, readdirSync, readFileSync, renameSync, rmSync } from "node:fs";
6
+ import { basename, dirname, join, resolve } from "node:path";
7
+ import { containedWrite } from "../fs/contained-write.js";
8
+ import { assertWriteTargetSafe } from "../fs/projection-containment.js";
9
+ import { escalationPath, escalationsDir } from "./paths.js";
10
+ import { DEFAULT_SLA_HOURS, ESCALATION_DECISIONS, ESCALATION_SCHEMA_VERSION, ESCALATION_STATUSES, ESCALATION_TYPES, isEscalationType, } from "./types.js";
11
+ export function utcIso(now) {
12
+ const dt = now ?? new Date();
13
+ return dt.toISOString().replace(/\.\d{3}Z$/, "Z");
14
+ }
15
+ function record(value) {
16
+ return value !== null && typeof value === "object" && !Array.isArray(value)
17
+ ? value
18
+ : null;
19
+ }
20
+ /**
21
+ * Contained atomic JSON write for escalations (#2980 / Greptile P1).
22
+ * Containment root is projectRoot (not dirname(target)) so parent-symlink escape fails closed.
23
+ * Unique random temp names avoid PID-reuse collisions; rename is the atomic publish step.
24
+ */
25
+ function writeJsonContained(projectRoot, targetPath, payload) {
26
+ const root = resolve(projectRoot);
27
+ const abs = resolve(targetPath);
28
+ // Refuse leaf/parent symlinks on the final path before temp+rename publish.
29
+ assertWriteTargetSafe(root, abs);
30
+ const dir = dirname(abs);
31
+ const tmpBase = `.${basename(abs)}.${process.pid}.${randomBytes(4).toString("hex")}.tmp`;
32
+ const tmp = join(dir, tmpBase);
33
+ try {
34
+ containedWrite({
35
+ root,
36
+ target: tmp,
37
+ data: `${JSON.stringify(payload, null, 2)}\n`,
38
+ mode: "create",
39
+ });
40
+ renameSync(tmp, abs);
41
+ }
42
+ catch (err) {
43
+ try {
44
+ rmSync(tmp, { force: true });
45
+ }
46
+ catch {
47
+ /* best-effort cleanup */
48
+ }
49
+ throw err;
50
+ }
51
+ }
52
+ function readString(rec, key) {
53
+ const v = rec[key];
54
+ return typeof v === "string" && v.trim().length > 0 ? v.trim() : null;
55
+ }
56
+ function readStringArray(raw) {
57
+ if (!Array.isArray(raw))
58
+ return [];
59
+ return raw.filter((x) => typeof x === "string" && x.trim().length > 0);
60
+ }
61
+ function readNumber(rec, key) {
62
+ const v = rec[key];
63
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
64
+ }
65
+ function parseResolution(raw) {
66
+ if (raw === null || raw === undefined)
67
+ return null;
68
+ const rec = record(raw);
69
+ if (rec === null)
70
+ return null;
71
+ const decision = readString(rec, "decision");
72
+ const resolvedAt = readString(rec, "resolvedAt") ?? readString(rec, "resolved_at") ?? utcIso();
73
+ const resolvedBy = readString(rec, "resolvedBy") ?? readString(rec, "resolved_by") ?? "operator";
74
+ if (decision === null || !ESCALATION_DECISIONS.includes(decision)) {
75
+ return null;
76
+ }
77
+ return {
78
+ decision: decision,
79
+ resolvedAt,
80
+ resolvedBy,
81
+ note: readString(rec, "note"),
82
+ answer: readString(rec, "answer"),
83
+ };
84
+ }
85
+ /**
86
+ * Parse an escalation JSON object.
87
+ * Returns null when type/status/required fields are invalid (tests reject invalid types).
88
+ */
89
+ export function parseEscalation(raw) {
90
+ const rec = record(raw);
91
+ if (rec === null)
92
+ return null;
93
+ const id = readString(rec, "id");
94
+ const agentId = readString(rec, "agentId") ?? readString(rec, "agent_id");
95
+ const typeRaw = readString(rec, "type");
96
+ const title = readString(rec, "title");
97
+ if (id === null || agentId === null || typeRaw === null || title === null)
98
+ return null;
99
+ if (!isEscalationType(typeRaw))
100
+ return null;
101
+ const statusRaw = (readString(rec, "status") ?? "open").toLowerCase();
102
+ if (!ESCALATION_STATUSES.includes(statusRaw))
103
+ return null;
104
+ const body = typeof rec.body === "string" ? rec.body : "";
105
+ const contextRefs = readStringArray(rec.contextRefs ?? rec.context_refs);
106
+ const createdAt = readString(rec, "createdAt") ?? readString(rec, "created_at") ?? utcIso();
107
+ const sla = readNumber(rec, "slaHours") ??
108
+ readNumber(rec, "sla_hours") ??
109
+ DEFAULT_SLA_HOURS[typeRaw];
110
+ const dangerous = rec.dangerous === true;
111
+ const resolution = parseResolution(rec.resolution);
112
+ return {
113
+ schemaVersion: ESCALATION_SCHEMA_VERSION,
114
+ id,
115
+ agentId,
116
+ type: typeRaw,
117
+ title,
118
+ body,
119
+ contextRefs,
120
+ createdAt,
121
+ slaHours: sla > 0 ? sla : DEFAULT_SLA_HOURS[typeRaw],
122
+ status: statusRaw,
123
+ dangerous,
124
+ resolution,
125
+ };
126
+ }
127
+ /** Validate type string alone (unit tests + CLI). */
128
+ export function validateEscalationType(type) {
129
+ const normalized = type.trim().toLowerCase();
130
+ if (!isEscalationType(normalized)) {
131
+ throw new Error(`invalid escalation type '${type}'; expected one of: ${ESCALATION_TYPES.join(", ")}`);
132
+ }
133
+ return normalized;
134
+ }
135
+ export function saveEscalation(projectRoot, event) {
136
+ writeJsonContained(projectRoot, escalationPath(projectRoot, event.id), event);
137
+ }
138
+ export function loadEscalation(projectRoot, escalationId) {
139
+ const path = escalationPath(projectRoot, escalationId);
140
+ if (!existsSync(path))
141
+ return null;
142
+ try {
143
+ return parseEscalation(JSON.parse(readFileSync(path, "utf8")));
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ }
149
+ export function listEscalations(projectRoot) {
150
+ const dir = escalationsDir(projectRoot);
151
+ if (!existsSync(dir))
152
+ return [];
153
+ const out = [];
154
+ for (const name of readdirSync(dir)) {
155
+ if (!name.endsWith(".json"))
156
+ continue;
157
+ try {
158
+ const event = parseEscalation(JSON.parse(readFileSync(join(dir, name), "utf8")));
159
+ if (event !== null)
160
+ out.push(event);
161
+ }
162
+ catch {
163
+ // skip corrupt files
164
+ }
165
+ }
166
+ out.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
167
+ return out;
168
+ }
169
+ export function listOpenEscalations(projectRoot) {
170
+ return listEscalations(projectRoot).filter((e) => e.status === "open");
171
+ }
172
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Typed escalation events + local queue store (#518 slim / #2948 Wave 5).
3
+ *
4
+ * Full priority-inbox web UI and metrics remain residual — this module is the
5
+ * versioned schema + `.deft/escalations/` file queue for CLI batching.
6
+ */
7
+ /** Fixed vocabulary of escalation reasons (#518). */
8
+ export declare const ESCALATION_TYPES: readonly ["cmd_approval", "design_decision", "approval", "resource", "external", "question"];
9
+ export type EscalationType = (typeof ESCALATION_TYPES)[number];
10
+ /**
11
+ * Types eligible for `escalation:batch-approve`.
12
+ * design_decision / approval / resource / external stay individual resolve.
13
+ */
14
+ export declare const BATCH_APPROVABLE_TYPES: readonly ["cmd_approval", "question"];
15
+ export type BatchApprovableType = (typeof BATCH_APPROVABLE_TYPES)[number];
16
+ export declare const ESCALATION_STATUSES: readonly ["open", "resolved"];
17
+ export type EscalationStatus = (typeof ESCALATION_STATUSES)[number];
18
+ export declare const ESCALATION_DECISIONS: readonly ["approved", "denied", "answered", "dismissed"];
19
+ export type EscalationDecision = (typeof ESCALATION_DECISIONS)[number];
20
+ /** Default SLA hours per type (issue #518 table). */
21
+ export declare const DEFAULT_SLA_HOURS: Readonly<Record<EscalationType, number>>;
22
+ export interface EscalationResolution {
23
+ readonly decision: EscalationDecision;
24
+ readonly resolvedAt: string;
25
+ readonly resolvedBy: string;
26
+ readonly note: string | null;
27
+ /** Free-text answer for `question` type (optional for other types). */
28
+ readonly answer: string | null;
29
+ }
30
+ /**
31
+ * Versioned escalation event (YAML shape from #518, camelCase on disk).
32
+ *
33
+ * ```json
34
+ * {
35
+ * "schemaVersion": 1,
36
+ * "id": "esc-…",
37
+ * "agentId": "agent-b",
38
+ * "type": "design_decision",
39
+ * "title": "…",
40
+ * "body": "…",
41
+ * "contextRefs": ["xbrief/…", "#496"],
42
+ * "createdAt": "2026-07-30T10:00:00Z",
43
+ * "slaHours": 4,
44
+ * "status": "open",
45
+ * "dangerous": false,
46
+ * "resolution": null
47
+ * }
48
+ * ```
49
+ */
50
+ export interface EscalationEvent {
51
+ readonly schemaVersion: 1;
52
+ readonly id: string;
53
+ readonly agentId: string;
54
+ readonly type: EscalationType;
55
+ readonly title: string;
56
+ readonly body: string;
57
+ readonly contextRefs: readonly string[];
58
+ readonly createdAt: string;
59
+ readonly slaHours: number;
60
+ readonly status: EscalationStatus;
61
+ /**
62
+ * When true, batch-approve skips this item unless `--include-dangerous`.
63
+ * Use for shell write-scope cmd_approval, PR merges, and similar.
64
+ */
65
+ readonly dangerous: boolean;
66
+ readonly resolution: EscalationResolution | null;
67
+ }
68
+ export declare const ESCALATION_SCHEMA_VERSION: 1;
69
+ export declare const ESCALATION_DIR = ".deft/escalations";
70
+ export declare function isEscalationType(value: string): value is EscalationType;
71
+ export declare function isBatchApprovableType(type: EscalationType): type is BatchApprovableType;
72
+ export declare function isEscalationDecision(value: string): value is EscalationDecision;
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Typed escalation events + local queue store (#518 slim / #2948 Wave 5).
3
+ *
4
+ * Full priority-inbox web UI and metrics remain residual — this module is the
5
+ * versioned schema + `.deft/escalations/` file queue for CLI batching.
6
+ */
7
+ /** Fixed vocabulary of escalation reasons (#518). */
8
+ export const ESCALATION_TYPES = [
9
+ "cmd_approval",
10
+ "design_decision",
11
+ "approval",
12
+ "resource",
13
+ "external",
14
+ "question",
15
+ ];
16
+ /**
17
+ * Types eligible for `escalation:batch-approve`.
18
+ * design_decision / approval / resource / external stay individual resolve.
19
+ */
20
+ export const BATCH_APPROVABLE_TYPES = ["cmd_approval", "question"];
21
+ export const ESCALATION_STATUSES = ["open", "resolved"];
22
+ export const ESCALATION_DECISIONS = ["approved", "denied", "answered", "dismissed"];
23
+ /** Default SLA hours per type (issue #518 table). */
24
+ export const DEFAULT_SLA_HOURS = {
25
+ cmd_approval: 1,
26
+ design_decision: 4,
27
+ approval: 4,
28
+ resource: 4,
29
+ external: 72,
30
+ question: 24,
31
+ };
32
+ export const ESCALATION_SCHEMA_VERSION = 1;
33
+ export const ESCALATION_DIR = ".deft/escalations";
34
+ export function isEscalationType(value) {
35
+ return ESCALATION_TYPES.includes(value);
36
+ }
37
+ export function isBatchApprovableType(type) {
38
+ return BATCH_APPROVABLE_TYPES.includes(type);
39
+ }
40
+ export function isEscalationDecision(value) {
41
+ return ESCALATION_DECISIONS.includes(value);
42
+ }
43
+ //# sourceMappingURL=types.js.map
@@ -1,6 +1,7 @@
1
- import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from "node:fs";
2
- import { dirname } from "node:path";
1
+ import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
2
+ import { basename, dirname, resolve } from "node:path";
3
3
  import { readCorePackageVersion } from "../engine-version.js";
4
+ import { containedWrite } from "../fs/contained-write.js";
4
5
  import { helpedMetricsHistoryPath } from "../metrics/resolve-metrics-home.js";
5
6
  import { scanVbrief } from "../vbrief-validate/conformance.js";
6
7
  import { validateVbriefSchema } from "../vbrief-validate/schema.js";
@@ -83,8 +84,19 @@ function assessDocument(path, parsed) {
83
84
  function sanitizeInlineMessage(message) {
84
85
  return message.replace(/\r?\n/g, " ");
85
86
  }
86
- function ensureParentDir(path) {
87
- mkdirSync(dirname(path), { recursive: true });
87
+ /**
88
+ * Contained replace under the target's parent directory (#2980 wave C).
89
+ * Metrics/CRUD sinks may live outside the project tree (platform metrics home).
90
+ */
91
+ function containedReplaceAt(path, content) {
92
+ const dir = dirname(path);
93
+ mkdirSync(dir, { recursive: true });
94
+ containedWrite({
95
+ root: resolve(dir),
96
+ target: basename(path),
97
+ data: content,
98
+ mode: "replace",
99
+ });
88
100
  }
89
101
  /** Absolute path to the versioned CRUD metrics ledger (#1703 Tier 1 / #2545). */
90
102
  export function crudMetricsHistoryPath(projectRoot) {
@@ -99,9 +111,17 @@ export function persistCrudMetrics(projectRoot, metrics) {
99
111
  if (path === null) {
100
112
  return;
101
113
  }
102
- mkdirSync(dirname(path), { recursive: true });
114
+ // #2980 wave C: product write sink routes through containedWrite.
115
+ // Metrics home is often outside the project tree (#2545) — contain under ledger parent.
116
+ const dir = dirname(path);
117
+ mkdirSync(dir, { recursive: true });
103
118
  for (const metric of metrics) {
104
- appendFileSync(path, `${JSON.stringify(metric)}\n`, "utf8");
119
+ containedWrite({
120
+ root: resolve(dir),
121
+ target: basename(path),
122
+ data: `${JSON.stringify(metric)}\n`,
123
+ mode: "append",
124
+ });
105
125
  }
106
126
  }
107
127
  /**
@@ -155,8 +175,8 @@ export class InstrumentedVbriefCrud {
155
175
  if (!assessment.schemaValid) {
156
176
  return { ok: false, error: assessment.schemaErrors.join("; ") };
157
177
  }
158
- ensureParentDir(path);
159
- writeFileSync(path, content, "utf8");
178
+ // #2980 wave C: product write sink routes through containedWrite.
179
+ containedReplaceAt(path, content);
160
180
  return { ok: true };
161
181
  }
162
182
  read(path) {
@@ -244,8 +264,8 @@ export class InstrumentedVbriefCrud {
244
264
  if (!assessment.schemaValid && !options.trustedWrite) {
245
265
  return { ok: false, error: assessment.schemaErrors.join("; ") };
246
266
  }
247
- ensureParentDir(path);
248
- writeFileSync(path, content, "utf8");
267
+ // #2980 wave C: product write sink routes through containedWrite.
268
+ containedReplaceAt(path, content);
249
269
  return { ok: true };
250
270
  }
251
271
  /** Record update metrics without persisting (caller owns validated atomic write). */
@@ -1,9 +1,9 @@
1
- import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
2
- import { dirname, isAbsolute, relative, resolve } from "node:path";
1
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { basename, dirname, isAbsolute, relative, resolve } from "node:path";
3
3
  import { agentsRefreshPlan } from "../doctor/agents-md.js";
4
4
  import { evaluate as evaluateEncoding } from "../encoding/evaluate.js";
5
5
  import { readCorePackageVersion } from "../engine-version.js";
6
- import { assertWriteTargetSafe } from "../fs/projection-containment.js";
6
+ import { containedWrite } from "../fs/contained-write.js";
7
7
  import { resolveProjectDefinitionPath } from "../layout/resolve.js";
8
8
  import { healthMetricsHistoryPath } from "../metrics/resolve-metrics-home.js";
9
9
  import { readPlanPolicy } from "../policy/plan-extensions.js";
@@ -163,14 +163,29 @@ export function persistHealthRun(projectRoot, report) {
163
163
  if (path === null) {
164
164
  return;
165
165
  }
166
+ // #2980 wave C: product write sink routes through containedWrite.
167
+ // Project-local metrics: contain under projectRoot. Platform/env home: under ledger parent.
166
168
  const projectAbs = resolve(projectRoot);
167
169
  const targetAbs = resolve(path);
168
170
  const rel = relative(projectAbs, targetAbs);
169
- if (rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel)) {
170
- assertWriteTargetSafe(projectRoot, path);
171
+ const nested = rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
172
+ if (nested) {
173
+ containedWrite({
174
+ root: projectAbs,
175
+ target: targetAbs,
176
+ data: `${JSON.stringify(report)}\n`,
177
+ mode: "append",
178
+ });
179
+ return;
171
180
  }
172
- mkdirSync(dirname(path), { recursive: true });
173
- appendFileSync(path, `${JSON.stringify(report)}\n`, "utf8");
181
+ const parent = dirname(targetAbs);
182
+ mkdirSync(parent, { recursive: true });
183
+ containedWrite({
184
+ root: resolve(parent),
185
+ target: basename(targetAbs),
186
+ data: `${JSON.stringify(report)}\n`,
187
+ mode: "append",
188
+ });
174
189
  }
175
190
  function collectStaticGates(projectRoot, frameworkSource) {
176
191
  const gates = [
@@ -1,6 +1,6 @@
1
- import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, readFileSync } from "node:fs";
2
2
  import { join, resolve } from "node:path";
3
- import { assertWriteTargetSafe, ProjectionContainmentError } from "../fs/projection-containment.js";
3
+ import { containedWrite } from "../fs/contained-write.js";
4
4
  import { MAX_LINE_CHARS } from "../triage/welcome/constants.js";
5
5
  import { evaluateHealth, healthHistoryPath } from "./health.js";
6
6
  /** Repeat-suppression window for the budgeted eval session nudge (#1703 / #1279 parity). */
@@ -125,15 +125,16 @@ function appendEvalReadbackHistory(projectRoot, nudgeKey, line, options = {}) {
125
125
  line,
126
126
  };
127
127
  try {
128
- assertWriteTargetSafe(projectRoot, path);
129
- mkdirSync(join(path, ".."), { recursive: true });
130
- appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
128
+ // #2980 wave C: product write sink routes through containedWrite.
129
+ containedWrite({
130
+ root: resolve(projectRoot),
131
+ target: path,
132
+ data: `${JSON.stringify(record)}\n`,
133
+ mode: "append",
134
+ });
131
135
  }
132
- catch (err) {
133
- if (err instanceof ProjectionContainmentError) {
134
- throw err;
135
- }
136
- // observability only
136
+ catch {
137
+ // observability only (containment refusals included — outer caller may swallow)
137
138
  }
138
139
  }
139
140
  function failedGateCount(report) {
package/dist/eval/run.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createHash } from "node:crypto";
2
- import { appendFileSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
- import { dirname, join } from "node:path";
4
+ import { join, resolve } from "node:path";
5
5
  import { readCorePackageVersion } from "../engine-version.js";
6
- import { assertWriteTargetSafe } from "../fs/projection-containment.js";
6
+ import { containedWrite } from "../fs/contained-write.js";
7
7
  import { resolveEvalPath } from "../layout/resolve.js";
8
8
  import { BYTE_DIFF_WHOLE_FILE_THRESHOLD, InstrumentedVbriefCrud } from "./crud-telemetry.js";
9
9
  import { evaluateHealth } from "./health.js";
@@ -94,16 +94,28 @@ function gradeCrudSurgicalUpdate(context) {
94
94
  };
95
95
  }
96
96
  function seedHealthFixture(tempDir) {
97
- writeFileSync(join(tempDir, "xbrief", "PROJECT-DEFINITION.xbrief.json"), JSON.stringify({
98
- xBRIEFInfo: { version: "0.6" },
99
- plan: {
100
- title: "Golden health fixture",
101
- status: "running",
102
- items: [],
103
- "x-directive/policy": { triageScope: [{ rule: "all-open" }] },
104
- },
105
- }), "utf8");
106
- writeFileSync(join(tempDir, "AGENTS.md"), "<!-- deft:managed-section v3 -->\n<!-- /deft:managed-section -->\n", "utf8");
97
+ const root = resolve(tempDir);
98
+ // #2980 wave C: product fixture seed routes through containedWrite.
99
+ containedWrite({
100
+ root,
101
+ target: join("xbrief", "PROJECT-DEFINITION.xbrief.json"),
102
+ data: JSON.stringify({
103
+ xBRIEFInfo: { version: "0.6" },
104
+ plan: {
105
+ title: "Golden health fixture",
106
+ status: "running",
107
+ items: [],
108
+ "x-directive/policy": { triageScope: [{ rule: "all-open" }] },
109
+ },
110
+ }),
111
+ mode: "replace",
112
+ });
113
+ containedWrite({
114
+ root,
115
+ target: "AGENTS.md",
116
+ data: "<!-- deft:managed-section v3 -->\n<!-- /deft:managed-section -->\n",
117
+ mode: "replace",
118
+ });
107
119
  }
108
120
  function gradeHealthFixture(context) {
109
121
  seedHealthFixture(context.tempDir);
@@ -195,9 +207,13 @@ export function goldenRunsHistoryPath(projectRoot) {
195
207
  /** Append one golden run to the versioned ledger (#1703 Tier 2). */
196
208
  export function persistGoldenRun(projectRoot, record) {
197
209
  const path = goldenRunsHistoryPath(projectRoot);
198
- assertWriteTargetSafe(projectRoot, path);
199
- mkdirSync(dirname(path), { recursive: true });
200
- appendFileSync(path, `${JSON.stringify(record)}\n`, "utf8");
210
+ // #2980 wave C: product write sink routes through containedWrite.
211
+ containedWrite({
212
+ root: resolve(projectRoot),
213
+ target: path,
214
+ data: `${JSON.stringify(record)}\n`,
215
+ mode: "append",
216
+ });
201
217
  }
202
218
  /** Stable hash for rotating holdout selection (#1703 Goodhart mitigation). */
203
219
  export function holdoutRotationIndex(directiveVersion, model, holdoutCount) {
@@ -1,7 +1,8 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
3
- import { dirname, join, resolve } from "node:path";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
4
4
  import { readCorePackageVersion } from "../engine-version.js";
5
+ import { containedWrite } from "../fs/contained-write.js";
5
6
  import { inferRepoFromGit } from "../triage/queue/repo.js";
6
7
  /** Record-shape version for attribution ledger entries (#2376). Bump on shape changes. */
7
8
  export const ATTRIBUTION_SCHEMA_VERSION = 1;
@@ -26,8 +27,13 @@ export function resolveInstallId(projectRoot) {
26
27
  }
27
28
  try {
28
29
  const id = randomUUID();
29
- mkdirSync(dirname(path), { recursive: true });
30
- writeFileSync(path, `${id}\n`, "utf8");
30
+ // #2951 Phase 2: product write sink routes through containedWrite.
31
+ containedWrite({
32
+ root: resolve(projectRoot),
33
+ target: path,
34
+ data: `${id}\n`,
35
+ mode: "replace",
36
+ });
31
37
  return id;
32
38
  }
33
39
  catch {
@@ -0,0 +1,37 @@
1
+ /**
2
+ * directive:finish-loop — outer walk-away cascade (#871 Wave 5).
3
+ *
4
+ * 1. Gate on finish-loop human-origin grant (or DEFT_ALLOW_FINISH_LOOP)
5
+ * 2. Scan xbrief queue (active/pending)
6
+ * 3. Append progress JSONL each iteration
7
+ * 4. When --pr supplied, run pr:finish-loop
8
+ * 5. Implementation steps are agent-owned: CLI emits AGENT_STEP and exits
9
+ * ACTION_REQUIRED so a live agent continues; full autonomous coding is
10
+ * not inlined in the Taskfile.
11
+ *
12
+ * Halt: empty queue | grant expiry/deny | max iterations | gate deny
13
+ */
14
+ import { type PrFinishLoopOptions, runPrFinishLoop } from "./pr-finish-loop.js";
15
+ import { scanFinishLoopQueue } from "./queue.js";
16
+ import { type DirectiveFinishLoopResult } from "./types.js";
17
+ export declare const DEFAULT_MAX_ITERATIONS = 20;
18
+ export interface DirectiveFinishLoopOptions {
19
+ readonly projectRoot: string;
20
+ /** Optional PR number to shepherd via pr:finish-loop this iteration. */
21
+ readonly prNumber?: number | null;
22
+ readonly repo?: string | null;
23
+ readonly maxIterations?: number;
24
+ readonly maxWaitMinutes?: number;
25
+ readonly pollSeconds?: number;
26
+ readonly oneShot?: boolean;
27
+ readonly merge?: boolean;
28
+ readonly env?: Readonly<Record<string, string | undefined>>;
29
+ readonly now?: Date;
30
+ readonly skipGrantGate?: boolean;
31
+ /** Inject for tests. */
32
+ readonly scanQueueFn?: typeof scanFinishLoopQueue;
33
+ readonly prFinishLoopFn?: (opts: PrFinishLoopOptions) => ReturnType<typeof runPrFinishLoop>;
34
+ readonly writeProgress?: boolean;
35
+ }
36
+ export declare function runDirectiveFinishLoop(options: DirectiveFinishLoopOptions): DirectiveFinishLoopResult;
37
+ //# sourceMappingURL=directive-finish-loop.d.ts.map