@deftai/directive-core 0.105.0 → 0.106.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 (85) hide show
  1. package/dist/authz/classify.js +591 -34
  2. package/dist/check/named-cause.js +1 -1
  3. package/dist/forward-coverage/diff-coverage.d.ts +39 -0
  4. package/dist/forward-coverage/diff-coverage.js +112 -0
  5. package/dist/forward-coverage/diff-lines.d.ts +19 -0
  6. package/dist/forward-coverage/diff-lines.js +173 -0
  7. package/dist/forward-coverage/evaluate.d.ts +24 -9
  8. package/dist/forward-coverage/evaluate.js +125 -19
  9. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  10. package/dist/init-deposit/agent-hooks.js +1 -1
  11. package/dist/intake/clause-derivation.d.ts +9 -0
  12. package/dist/intake/clause-derivation.js +30 -3
  13. package/dist/lifecycle-visible/evaluate.d.ts +98 -0
  14. package/dist/lifecycle-visible/evaluate.js +710 -0
  15. package/dist/lifecycle-visible/index.d.ts +2 -0
  16. package/dist/lifecycle-visible/index.js +2 -0
  17. package/dist/literal-acceptance/capture.js +3 -3
  18. package/dist/orchestration/judgment-policy.d.ts +13 -0
  19. package/dist/orchestration/judgment-policy.js +15 -0
  20. package/dist/orchestration/probe-session.js +13 -8
  21. package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
  22. package/dist/orchestration/verify-judgment-gates.js +43 -11
  23. package/dist/policy/ceremony-dial.js +7 -7
  24. package/dist/policy/host-hooks.d.ts +20 -0
  25. package/dist/policy/host-hooks.js +121 -2
  26. package/dist/policy/org-force-on-migration.js +2 -1
  27. package/dist/policy/plan-extensions.d.ts +1 -1
  28. package/dist/policy/plan-extensions.js +2 -0
  29. package/dist/policy/product-signal.js +6 -7
  30. package/dist/policy/require-human-merge.js +8 -6
  31. package/dist/policy/resolve.d.ts +7 -2
  32. package/dist/policy/resolve.js +25 -8
  33. package/dist/policy/value-feedback.js +7 -8
  34. package/dist/preflight-cache/evaluate.d.ts +12 -0
  35. package/dist/preflight-cache/evaluate.js +15 -1
  36. package/dist/preflight-cache/index.d.ts +1 -1
  37. package/dist/preflight-cache/index.js +1 -1
  38. package/dist/product-first-done-gate/evaluate.d.ts +4 -0
  39. package/dist/product-first-done-gate/evaluate.js +46 -5
  40. package/dist/release/closed-verb-gate.d.ts +12 -0
  41. package/dist/release/closed-verb-gate.js +51 -0
  42. package/dist/release/index.d.ts +1 -0
  43. package/dist/release/index.js +1 -0
  44. package/dist/release/pipeline.js +7 -0
  45. package/dist/release/types.d.ts +12 -0
  46. package/dist/render/constants.d.ts +2 -1
  47. package/dist/render/constants.js +2 -1
  48. package/dist/render/framework-commands.js +4 -0
  49. package/dist/render/spec-validate.js +23 -12
  50. package/dist/run-summary/types.d.ts +4 -0
  51. package/dist/scope/main.js +15 -5
  52. package/dist/scope/promote-from-issue.js +5 -5
  53. package/dist/scope-provenance/evaluate.js +3 -3
  54. package/dist/session/ac-pass-banking.d.ts +5 -0
  55. package/dist/session/ac-pass-banking.js +3 -2
  56. package/dist/session/process-cost.d.ts +32 -10
  57. package/dist/session/process-cost.js +189 -1
  58. package/dist/session/product-state-hash.js +41 -24
  59. package/dist/session/ritual-entrypoint.js +16 -1
  60. package/dist/session/session-start.d.ts +6 -0
  61. package/dist/session/session-start.js +32 -2
  62. package/dist/session/verify-session-ritual.d.ts +24 -1
  63. package/dist/session/verify-session-ritual.js +89 -6
  64. package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
  65. package/dist/swarm/finalize-cohort-cli.js +165 -35
  66. package/dist/swarm/finalize-cohort.d.ts +1 -0
  67. package/dist/swarm/finalize-cohort.js +14 -2
  68. package/dist/swarm/routing-set-cli.js +2 -0
  69. package/dist/swarm/routing-verify.d.ts +1 -1
  70. package/dist/swarm/routing-verify.js +2 -2
  71. package/dist/triage/bootstrap/index.js +6 -6
  72. package/dist/triage/help/registry-data.d.ts +1 -1
  73. package/dist/triage/help/registry-data.js +1 -1
  74. package/dist/triage/welcome/writers.d.ts +1 -1
  75. package/dist/triage/welcome/writers.js +10 -5
  76. package/dist/value/readback.d.ts +4 -1
  77. package/dist/value/readback.js +51 -12
  78. package/dist/vbrief-validate/schema.d.ts +4 -0
  79. package/dist/vbrief-validate/schema.js +1 -1
  80. package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
  81. package/dist/verify-env/agent-hook-readiness.js +12 -8
  82. package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
  83. package/dist/verify-env/agent-hooks-live-probe.js +26 -5
  84. package/dist/verify-env/agent-hooks.js +3 -4
  85. package/package.json +7 -3
@@ -4,6 +4,7 @@ import { containedWrite } from "../../fs/contained-write.js";
4
4
  import { assertWriteTargetSafe } from "../../fs/projection-containment.js";
5
5
  import { hasArtifactSuffix, resolveLifecycleFolder, resolveProjectDefinitionPath, } from "../../layout/resolve.js";
6
6
  import { migrateLegacyPolicyKey, PLAN_ONBOARDING_KEY, PLAN_POLICY_KEY, } from "../../policy/plan-extensions.js";
7
+ import { stampChangedToken } from "../../policy/resolve.js";
7
8
  import { projectDefinitionMutationLock } from "../../vbrief-build/project-definition-io.js";
8
9
  import { AUDIT_LOG_REL_PATH, DEFAULT_RELIEF_AGE_DAYS, DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, WELCOME_AUDIT_TAG, } from "./constants.js";
9
10
  function projectDefinitionPath(projectRoot) {
@@ -12,9 +13,13 @@ function projectDefinitionPath(projectRoot) {
12
13
  function utcIso() {
13
14
  return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
14
15
  }
15
- export function appendAuditEntry(projectRoot, entry) {
16
+ export function appendAuditEntry(projectRoot, entry, changed = true) {
16
17
  const root = resolve(projectRoot);
17
18
  const logPath = join(root, AUDIT_LOG_REL_PATH);
19
+ const stamped = stampChangedToken(entry, changed);
20
+ if (!changed) {
21
+ return logPath;
22
+ }
18
23
  // #2980 wave D: product write sink routes through containedWrite.
19
24
  mkdirSync(root, { recursive: true });
20
25
  if (!existsSync(logPath)) {
@@ -28,7 +33,7 @@ export function appendAuditEntry(projectRoot, entry) {
28
33
  containedWrite({
29
34
  root,
30
35
  target: AUDIT_LOG_REL_PATH,
31
- data: `${utcIso()} ${entry}\n`,
36
+ data: `${utcIso()} ${stamped}\n`,
32
37
  mode: "append",
33
38
  });
34
39
  return logPath;
@@ -102,7 +107,7 @@ export function writeTriageScope(projectRoot, rules, options = { presetLabel: "c
102
107
  `rule_count=${rules.length}`,
103
108
  `changed=${changed ? "true" : "false"}`,
104
109
  ].join(" ");
105
- appendAuditEntry(projectRoot, auditEntry);
110
+ appendAuditEntry(projectRoot, auditEntry, changed);
106
111
  return [changed, auditEntry];
107
112
  });
108
113
  }
@@ -153,7 +158,7 @@ export function writeWipCapDecision(projectRoot, options = {}) {
153
158
  `acceptedDefault=${acceptedDefault ? "true" : "false"}`,
154
159
  `changed=${changed ? "true" : "false"}`,
155
160
  ].join(" ");
156
- appendAuditEntry(projectRoot, auditEntry);
161
+ appendAuditEntry(projectRoot, auditEntry, changed);
157
162
  return [changed, auditEntry];
158
163
  });
159
164
  }
@@ -220,7 +225,7 @@ export function writeWipCap(projectRoot, wipCap, options = {}) {
220
225
  const changed = previous !== wipCap;
221
226
  const auditEntry = `actor=${actor} field=plan.policy.wipCap value=${wipCap} previous=${JSON.stringify(previous)} ` +
222
227
  `changed=${changed ? "true" : "false"}`;
223
- appendAuditEntry(projectRoot, auditEntry);
228
+ appendAuditEntry(projectRoot, auditEntry, changed);
224
229
  return [changed, auditEntry];
225
230
  });
226
231
  }
@@ -1,5 +1,6 @@
1
1
  import { type BehavioralEventRecord } from "../lifecycle/events.js";
2
2
  import { type ValueFeedbackResolved } from "../policy/value-feedback.js";
3
+ import { type CeremonyCostRollup } from "../session/process-cost.js";
3
4
  /** Repeat-suppression window for the budgeted session readback (#1709 / #1279 parity). */
4
5
  export declare const VALUE_READBACK_SUPPRESSION_HOURS = 4;
5
6
  export declare const VALUE_READBACK_HISTORY_REL: string;
@@ -24,6 +25,8 @@ export interface ValueShowResult {
24
25
  readonly empty: boolean;
25
26
  readonly text: string;
26
27
  readonly trend: ValueShowTrend | null;
28
+ /** Composed #3508 ceremony-cost rollup (CLI process time, not turn clock). */
29
+ readonly ceremonyCost: CeremonyCostRollup | null;
27
30
  }
28
31
  export interface SessionReadbackResult {
29
32
  readonly line: string | null;
@@ -64,7 +67,7 @@ export declare function computeValueShowTrend(projectRoot: string, options?: {
64
67
  now?: Date;
65
68
  }): ValueShowTrend;
66
69
  export declare function formatValueShowReport(trend: ValueShowTrend): string;
67
- /** Pull-based value:show CLI core (#1709). */
70
+ /** Pull-based value:show CLI core (#1709) with composed ceremony-cost reader (#3508). */
68
71
  export declare function runValueShow(options: {
69
72
  projectRoot?: string | null;
70
73
  window?: string;
@@ -8,6 +8,7 @@ import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
8
8
  import { policyColonInvocation } from "../policy/policy-invocation.js";
9
9
  import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
10
10
  import { resolveProjectRoot } from "../scope/project-context.js";
11
+ import { computeCeremonyCostRollup, formatCeremonyCostReport, PROCESS_COST_EVENT_NAMES, } from "../session/process-cost.js";
11
12
  import { MAX_LINE_CHARS } from "../triage/welcome/constants.js";
12
13
  import { probeAdoptionAtWorkBoundary } from "./adoption-emit.js";
13
14
  import { probeFrictionAtWorkBoundary } from "./friction-emit.js";
@@ -391,7 +392,36 @@ export function formatValueShowReport(trend) {
391
392
  ];
392
393
  return `${lines.join("\n")}\n`;
393
394
  }
394
- /** Pull-based value:show CLI core (#1709). */
395
+ function attachCeremonyCost(result, ceremonyCost, format) {
396
+ if (format === "json") {
397
+ const payload = result.trend !== null
398
+ ? {
399
+ ...result.trend,
400
+ ceremonyCost,
401
+ process_cost_events: PROCESS_COST_EVENT_NAMES,
402
+ gated: result.gated,
403
+ attribution_gated: result.gated,
404
+ }
405
+ : {
406
+ ceremonyCost,
407
+ process_cost_events: PROCESS_COST_EVENT_NAMES,
408
+ gated: result.gated,
409
+ attribution_gated: result.gated,
410
+ empty: result.empty,
411
+ };
412
+ return {
413
+ ...result,
414
+ ceremonyCost,
415
+ text: `${JSON.stringify(payload, null, 2)}\n`,
416
+ };
417
+ }
418
+ return {
419
+ ...result,
420
+ ceremonyCost,
421
+ text: `${result.text}${formatCeremonyCostReport(ceremonyCost)}`,
422
+ };
423
+ }
424
+ /** Pull-based value:show CLI core (#1709) with composed ceremony-cost reader (#3508). */
395
425
  export function runValueShow(options) {
396
426
  const rootRaw = resolveProjectRoot(options.projectRoot ?? undefined);
397
427
  if (rootRaw === null) {
@@ -401,30 +431,39 @@ export function runValueShow(options) {
401
431
  empty: true,
402
432
  text: "Error: could not resolve project root.\n",
403
433
  trend: null,
434
+ ceremonyCost: null,
404
435
  };
405
436
  }
406
437
  const root = resolve(rootRaw);
438
+ const windowMs = parseWindowMs(options.window);
439
+ const ceremonyCost = computeCeremonyCostRollup({
440
+ projectRoot: root,
441
+ logPath: options.logPath,
442
+ windowMs,
443
+ now: options.now,
444
+ windowLabel: formatWindowLabel(windowMs),
445
+ });
407
446
  if (maintainerReadbackDisabled(root)) {
408
- return {
447
+ return attachCeremonyCost({
409
448
  exitCode: 0,
410
- gated: true,
449
+ gated: false,
411
450
  empty: true,
412
- text: "[value] Skipped: maintainer framework repo (set DEFT_VALUE_SELF_DOGFOOD=1 to dogfood).\n",
451
+ text: "[value] Attribution skipped: maintainer framework repo " +
452
+ "(set DEFT_VALUE_SELF_DOGFOOD=1 to dogfood).\n",
413
453
  trend: null,
414
- };
454
+ }, ceremonyCost, options.format);
415
455
  }
416
456
  const policy = options.policyOverride ?? resolveValueFeedback(root);
417
457
  if (!policy.enabled) {
418
- return {
458
+ return attachCeremonyCost({
419
459
  exitCode: 1,
420
460
  gated: true,
421
461
  empty: true,
422
462
  text: "[value] Blocked: plan.policy.valueFeedback is OFF. " +
423
463
  `Enable with \`${policyColonInvocation("enable-value-feedback", " -- --confirm")}\`.\n`,
424
464
  trend: null,
425
- };
465
+ }, ceremonyCost, options.format);
426
466
  }
427
- const windowMs = parseWindowMs(options.window);
428
467
  const trend = computeValueShowTrend(root, {
429
468
  windowMs,
430
469
  logPath: options.logPath,
@@ -432,21 +471,21 @@ export function runValueShow(options) {
432
471
  });
433
472
  const empty = trend.total === 0;
434
473
  if (options.format === "json") {
435
- return {
474
+ return attachCeremonyCost({
436
475
  exitCode: 0,
437
476
  gated: false,
438
477
  empty,
439
478
  text: `${JSON.stringify(trend, null, 2)}\n`,
440
479
  trend,
441
- };
480
+ }, ceremonyCost, "json");
442
481
  }
443
- return {
482
+ return attachCeremonyCost({
444
483
  exitCode: 0,
445
484
  gated: false,
446
485
  empty,
447
486
  text: formatValueShowReport(trend),
448
487
  trend,
449
- };
488
+ }, ceremonyCost, "text");
450
489
  }
451
490
  export function parseValueShowArgs(argv) {
452
491
  const out = {};
@@ -1,4 +1,8 @@
1
1
  export type JsonObject = Record<string, unknown>;
2
+ export declare function resolveInfoBlock(data: JsonObject): {
3
+ key: "vBRIEFInfo" | "xBRIEFInfo";
4
+ info: JsonObject;
5
+ } | null;
2
6
  /** Validate vBRIEF/xBRIEF structural requirements (v0.6 + v0.8 additive). */
3
7
  export declare function validateVbriefSchema(data: JsonObject, filepath: string): string[];
4
8
  /** Normalize a narrative key for D3 comparison. */
@@ -22,7 +22,7 @@ function validatePlanRefs(planRefs, path, errors) {
22
22
  }
23
23
  }
24
24
  }
25
- function resolveInfoBlock(data) {
25
+ export function resolveInfoBlock(data) {
26
26
  for (const key of VALID_INFO_ROOT_KEYS) {
27
27
  if (!(key in data)) {
28
28
  continue;
@@ -5,10 +5,10 @@ import { type AgentHookHealthResult } from "./agent-hooks.js";
5
5
  import { type AgentHookLiveProbeResult, type AgentHookLiveProbeSeams } from "./agent-hooks-live-probe.js";
6
6
  import type { OutputStream } from "./verify-hooks-installed.js";
7
7
  export type AgentHookReadinessRegistration = "registered" | "disabled" | "missing" | "drifted" | "not-applicable";
8
- export type AgentHookReadinessFunctionality = "functional" | "non-functional" | "unavailable" | "not-run" | "disabled" | "not-applicable";
8
+ export type AgentHookReadinessFunctionality = "functional" | "non-functional" | "unavailable" | "timed-out" | "not-run" | "disabled" | "not-applicable";
9
9
  export type AgentHookReadinessTrust = "manual-review-required" | "not-applicable" | "disabled";
10
10
  export type AgentHookReadinessInterception = "not-directly-verified" | "not-applicable" | "disabled";
11
- export type AgentHookReadinessLiveStatus = "functional" | "non-functional" | "unavailable" | "not-run" | "disabled" | "skipped";
11
+ export type AgentHookReadinessLiveStatus = "functional" | "non-functional" | "unavailable" | "timed-out" | "not-run" | "disabled" | "skipped";
12
12
  export interface AgentHookReadinessHostResult {
13
13
  readonly host: HookHost;
14
14
  readonly registration: AgentHookReadinessRegistration;
@@ -1,6 +1,6 @@
1
1
  import { resolve } from "node:path";
2
2
  import { resolveFrameworkRootForProject } from "../doctor/paths.js";
3
- import { loadHostHooksPolicyFromProject } from "../policy/host-hooks.js";
3
+ import { loadHostHooksPolicyFromProject, UNUSED_HOST_HOOKS_RECOVERY, } from "../policy/host-hooks.js";
4
4
  import { evaluateAgentHooks } from "./agent-hooks.js";
5
5
  import { probeAgentHooksLive, } from "./agent-hooks-live-probe.js";
6
6
  /** Stable CLI JSON projection; keep the four readiness dimensions orthogonal. */
@@ -114,31 +114,35 @@ export function evaluateAgentHookReadiness(projectRoot, options = {}) {
114
114
  ? "functional"
115
115
  : liveStatus === "unavailable"
116
116
  ? "unavailable"
117
- : liveStatus === "non-functional"
118
- ? "non-functional"
119
- : "not-run",
117
+ : liveStatus === "timed-out"
118
+ ? "timed-out"
119
+ : liveStatus === "non-functional"
120
+ ? "non-functional"
121
+ : "not-run",
120
122
  };
121
123
  });
122
124
  const allDisabled = enabledHosts.length === 0;
125
+ const timeoutOnly = liveProbe.cases.length > 0 && liveProbe.cases.every((entry) => entry.issue === "timed-out");
123
126
  const liveStatus = allDisabled
124
127
  ? "disabled"
125
128
  : liveProbe.code === 0
126
129
  ? "functional"
127
130
  : liveProbe.code === 2
128
131
  ? "unavailable"
129
- : "non-functional";
132
+ : timeoutOnly
133
+ ? "timed-out"
134
+ : "non-functional";
130
135
  const trustReview = policy.codex
131
136
  ? "\n Codex trust: manual-review-required. Open `/hooks` and approve the exact project hook commands. " +
132
137
  "The live probe validates the shim and codec, not host interception."
133
138
  : "";
139
+ const unusedHostRecovery = liveProbe.code === 0 || timeoutOnly ? "" : `\n ${UNUSED_HOST_HOOKS_RECOVERY}`;
134
140
  return {
135
141
  code: liveProbe.code,
136
142
  message: `${liveProbe.code === 0 ? "✓" : "❌"} deft agent hook readiness: ${liveProbe.message}\n` +
137
143
  renderHosts(evaluatedHosts) +
138
144
  trustReview +
139
- (liveProbe.code === 0
140
- ? ""
141
- : "\n If an affected host is unused, inspect `deft policy:show --field=hostHooks`, set `plan.policy.hostHooks.<host> = false`, and run `deft update`."),
145
+ unusedHostRecovery,
142
146
  stream: liveProbe.code === 0 ? "stdout" : "stderr",
143
147
  skipped: false,
144
148
  liveStatus,
@@ -1,5 +1,9 @@
1
1
  import { type HookEvent, type HookHost } from "../hooks/dispatcher.js";
2
2
  export declare const LIVE_PROBE_CASE_TIMEOUT_MS = 1500;
3
+ /** Extra attempts after a timeout. One retry keeps 4×2×1.5s×2 = 24s under Cursor 30s (#3570). */
4
+ export declare const LIVE_PROBE_CASE_RETRY_COUNT = 1;
5
+ export declare const LIVE_PROBE_TIMEOUT_RECOVERY = "Recovery: retry the gated ritual when load is lower. Do not reinstall @deftai/directive or disable hostHooks for a timeout.";
6
+ export declare const LIVE_PROBE_BROKEN_RECOVERY = "Recovery: reinstall @deftai/directive and run `deft update`.";
3
7
  export type AgentHookLiveProbeIssue = "hook-command-missing" | "spawn-failed" | "timed-out" | "empty-stdout" | "unparseable-json" | "missing-allow" | "missing-deny";
4
8
  export interface AgentHookLiveProbeCase {
5
9
  readonly host: HookHost;
@@ -8,7 +12,7 @@ export interface AgentHookLiveProbeCase {
8
12
  readonly issue: AgentHookLiveProbeIssue;
9
13
  readonly detail: string;
10
14
  }
11
- export type AgentHookLiveProbeHostStatus = "functional" | "non-functional" | "unavailable";
15
+ export type AgentHookLiveProbeHostStatus = "functional" | "non-functional" | "unavailable" | "timed-out";
12
16
  export interface AgentHookLiveProbeHostResult {
13
17
  readonly host: HookHost;
14
18
  readonly status: AgentHookLiveProbeHostStatus;
@@ -6,6 +6,10 @@ import { DEFT_HOOK_COMMAND_MARKER } from "../init-deposit/agent-hooks.js";
6
6
  import { SUBPROCESS_MAX_BUFFER } from "../subprocess/max-buffer.js";
7
7
  import { quoteWin32CommandForShell, resolveCommandOnPath, shouldUseShellForCommand, } from "./command-spawn.js";
8
8
  export const LIVE_PROBE_CASE_TIMEOUT_MS = 1_500;
9
+ /** Extra attempts after a timeout. One retry keeps 4×2×1.5s×2 = 24s under Cursor 30s (#3570). */
10
+ export const LIVE_PROBE_CASE_RETRY_COUNT = 1;
11
+ export const LIVE_PROBE_TIMEOUT_RECOVERY = "Recovery: retry the gated ritual when load is lower. Do not reinstall @deftai/directive or disable hostHooks for a timeout.";
12
+ export const LIVE_PROBE_BROKEN_RECOVERY = "Recovery: reinstall @deftai/directive and run `deft update`.";
9
13
  const LIVE_PROBE_EVENT = "tool.before";
10
14
  function elapsedMs(started) {
11
15
  return Math.max(0, Math.round(performance.now() - started));
@@ -122,21 +126,38 @@ function validateFixtureOutput(host, fixture, stdout) {
122
126
  ? { ok: true }
123
127
  : { ok: false, issue: "missing-deny", detail: `expected ${host} deny envelope` };
124
128
  }
129
+ function liveProbeRecovery(cases) {
130
+ if (cases.length > 0 && cases.every((entry) => entry.issue === "timed-out")) {
131
+ return LIVE_PROBE_TIMEOUT_RECOVERY;
132
+ }
133
+ return LIVE_PROBE_BROKEN_RECOVERY;
134
+ }
135
+ function hostStatusFromFailure(hostFailure) {
136
+ if (hostFailure === null)
137
+ return "functional";
138
+ if (hostFailure.issue === "timed-out")
139
+ return "timed-out";
140
+ return "non-functional";
141
+ }
125
142
  function runFixtureProbe(command, host, projectRoot, fixture, spawnHook) {
126
- const spawned = spawnHook({
143
+ const input = {
127
144
  command,
128
145
  args: ["--host", host, "--event", LIVE_PROBE_EVENT, "--project-root", projectRoot],
129
146
  stdin: fixture === "allow" ? allowFixture(projectRoot) : denyFixture(projectRoot),
130
147
  cwd: projectRoot,
131
148
  env: fixture === "allow" ? process.env : denyProbeEnv(),
132
- });
149
+ };
150
+ let spawned = spawnHook(input);
151
+ for (let retry = 0; retry < LIVE_PROBE_CASE_RETRY_COUNT && spawned.timedOut === true; retry += 1) {
152
+ spawned = spawnHook(input);
153
+ }
133
154
  if (spawned.timedOut === true) {
134
155
  return {
135
156
  host,
136
157
  event: LIVE_PROBE_EVENT,
137
158
  fixture,
138
159
  issue: "timed-out",
139
- detail: `hook command exceeded ${LIVE_PROBE_CASE_TIMEOUT_MS}ms`,
160
+ detail: `hook command exceeded ${LIVE_PROBE_CASE_TIMEOUT_MS}ms after ${LIVE_PROBE_CASE_RETRY_COUNT + 1} attempts`,
140
161
  };
141
162
  }
142
163
  if (spawned.status !== 0) {
@@ -204,7 +225,7 @@ export function probeAgentHooksLive(projectRoot, seams = {}) {
204
225
  break;
205
226
  }
206
227
  }
207
- hostResults.push({ host, status: hostFailure === null ? "functional" : "non-functional" });
228
+ hostResults.push({ host, status: hostStatusFromFailure(hostFailure) });
208
229
  }
209
230
  if (failures.length === 0) {
210
231
  return {
@@ -220,7 +241,7 @@ export function probeAgentHooksLive(projectRoot, seams = {}) {
220
241
  .join("; ");
221
242
  return {
222
243
  code: 1,
223
- message: `deft agent hooks live probe FAILED: ${summary}. Recovery: reinstall @deftai/directive and run \`deft update\`.`,
244
+ message: `deft agent hooks live probe FAILED: ${summary}. ${liveProbeRecovery(failures)}`,
224
245
  cases: failures,
225
246
  hosts: hostResults,
226
247
  durationMs: elapsedMs(started),
@@ -1,7 +1,7 @@
1
1
  import { statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
3
  import { inspectAgentHookDeposit } from "../init-deposit/agent-hooks.js";
4
- import { loadHostHooksPolicyFromProject } from "../policy/host-hooks.js";
4
+ import { loadHostHooksPolicyFromProject, UNUSED_HOST_HOOKS_RECOVERY, } from "../policy/host-hooks.js";
5
5
  function isDirectory(path) {
6
6
  try {
7
7
  return statSync(path).isDirectory();
@@ -30,9 +30,8 @@ export function evaluateAgentHooks(projectRoot, hostHooksPolicy = loadHostHooksP
30
30
  unhealthy
31
31
  .map((entry) => ` - ${entry.host}: ${entry.status} at ${entry.path} — ${entry.detail}`)
32
32
  .join("\n") +
33
- "\n Recovery: run `deft update` (or `directive init`) to refresh project hooks." +
34
- " If a failed host is intentionally unused, inspect `deft policy:show --field=hostHooks`, " +
35
- "set `plan.policy.hostHooks.<host> = false`, and run `deft update` again.",
33
+ "\n Recovery: run `deft update` (or `directive init`) to refresh project hooks. " +
34
+ UNUSED_HOST_HOOKS_RECOVERY,
36
35
  stream: "stderr",
37
36
  registrations,
38
37
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.105.0",
3
+ "version": "0.106.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -283,6 +283,10 @@
283
283
  "types": "./dist/lifecycle/index.d.ts",
284
284
  "default": "./dist/lifecycle/index.js"
285
285
  },
286
+ "./lifecycle-visible": {
287
+ "types": "./dist/lifecycle-visible/index.d.ts",
288
+ "default": "./dist/lifecycle-visible/index.js"
289
+ },
286
290
  "./literal-acceptance": {
287
291
  "types": "./dist/literal-acceptance/index.d.ts",
288
292
  "default": "./dist/literal-acceptance/index.js"
@@ -382,8 +386,8 @@
382
386
  "provenance": true
383
387
  },
384
388
  "dependencies": {
385
- "@deftai/directive-content": "^0.105.0",
386
- "@deftai/directive-types": "^0.105.0",
389
+ "@deftai/directive-content": "^0.106.0",
390
+ "@deftai/directive-types": "^0.106.0",
387
391
  "archiver": "^8.0.0"
388
392
  },
389
393
  "scripts": {