@deftai/directive-runtime 0.126.0-beta.2 → 0.126.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/UPGRADING.md +4 -3
  2. package/commands.md +9 -6
  3. package/contracts/design-critique.md +39 -1
  4. package/dist/doctor-diagnose.d.ts +1 -1
  5. package/dist/doctor-diagnose.js +29 -3
  6. package/dist/doctor.js +1 -1
  7. package/dist/session-start.js +28 -1
  8. package/dist/verify-review-monitor.d.ts +7 -0
  9. package/dist/verify-review-monitor.js +21 -0
  10. package/docs/getting-started.md +13 -1
  11. package/package.json +1 -1
  12. package/packs/skills/skills-pack-0.1.json +4 -4
  13. package/packs/strategies/strategies-pack-0.1.json +32 -19
  14. package/skills/deft-directive-article-review/SKILL.md +104 -24
  15. package/skills/deft-directive-design-critique/SKILL.md +10 -9
  16. package/skills/deft-directive-feedback/SKILL.md +20 -9
  17. package/skills/deft-directive-pre-pr/SKILL.md +14 -0
  18. package/skills/deft-directive-review-cycle/SKILL.md +18 -1
  19. package/skills/deft-directive-setup/SKILL.md +41 -11
  20. package/skills/deft-directive-swarm/references/host-grok-build.md +1 -1
  21. package/skills/deft-directive-tutorial/SKILL.md +1 -1
  22. package/strategies/README.md +1 -0
  23. package/strategies/artifact-guards.md +2 -2
  24. package/strategies/design-thinking.md +182 -0
  25. package/strategies/discuss.md +2 -0
  26. package/strategies/interview.md +76 -23
  27. package/strategies/rapid.md +22 -12
  28. package/templates/agents-entry.md +4 -3
  29. package/templates/design-critique-brief.md +5 -1
  30. package/tutorial/beats.json +2 -2
  31. package/vendor/directive-content/UPGRADING.md +4 -3
  32. package/vendor/directive-content/commands.md +9 -6
  33. package/vendor/directive-content/contracts/design-critique.md +39 -1
  34. package/vendor/directive-content/docs/getting-started.md +13 -1
  35. package/vendor/directive-content/packs/skills/skills-pack-0.1.json +4 -4
  36. package/vendor/directive-content/packs/strategies/strategies-pack-0.1.json +32 -19
  37. package/vendor/directive-content/skills/deft-directive-article-review/SKILL.md +104 -24
  38. package/vendor/directive-content/skills/deft-directive-design-critique/SKILL.md +10 -9
  39. package/vendor/directive-content/skills/deft-directive-feedback/SKILL.md +20 -9
  40. package/vendor/directive-content/skills/deft-directive-pre-pr/SKILL.md +14 -0
  41. package/vendor/directive-content/skills/deft-directive-review-cycle/SKILL.md +18 -1
  42. package/vendor/directive-content/skills/deft-directive-setup/SKILL.md +41 -11
  43. package/vendor/directive-content/skills/deft-directive-swarm/references/host-grok-build.md +1 -1
  44. package/vendor/directive-content/skills/deft-directive-tutorial/SKILL.md +1 -1
  45. package/vendor/directive-content/strategies/README.md +1 -0
  46. package/vendor/directive-content/strategies/artifact-guards.md +2 -2
  47. package/vendor/directive-content/strategies/design-thinking.md +182 -0
  48. package/vendor/directive-content/strategies/discuss.md +2 -0
  49. package/vendor/directive-content/strategies/interview.md +76 -23
  50. package/vendor/directive-content/strategies/rapid.md +22 -12
  51. package/vendor/directive-content/templates/agents-entry.md +4 -3
  52. package/vendor/directive-content/templates/design-critique-brief.md +5 -1
  53. package/vendor/directive-content/tutorial/beats.json +2 -2
  54. package/vendor/directive-core/dist/.tsbuildinfo +1 -1
  55. package/vendor/directive-core/dist/content-contracts/skills/helpers.d.ts +11 -1
  56. package/vendor/directive-core/dist/content-contracts/skills/helpers.d.ts.map +1 -1
  57. package/vendor/directive-core/dist/content-contracts/skills/helpers.js +18 -5
  58. package/vendor/directive-core/dist/content-contracts/skills/helpers.js.map +1 -1
  59. package/vendor/directive-core/dist/design-critique/auto-stamp-path1.d.ts +40 -0
  60. package/vendor/directive-core/dist/design-critique/auto-stamp-path1.d.ts.map +1 -1
  61. package/vendor/directive-core/dist/design-critique/auto-stamp-path1.js +116 -0
  62. package/vendor/directive-core/dist/design-critique/auto-stamp-path1.js.map +1 -1
  63. package/vendor/directive-core/dist/design-critique/citation-grammar.d.ts +36 -0
  64. package/vendor/directive-core/dist/design-critique/citation-grammar.d.ts.map +1 -1
  65. package/vendor/directive-core/dist/design-critique/citation-grammar.js +60 -3
  66. package/vendor/directive-core/dist/design-critique/citation-grammar.js.map +1 -1
  67. package/vendor/directive-core/dist/design-critique/completed-arc-record.d.ts +103 -3
  68. package/vendor/directive-core/dist/design-critique/completed-arc-record.d.ts.map +1 -1
  69. package/vendor/directive-core/dist/design-critique/completed-arc-record.js +127 -5
  70. package/vendor/directive-core/dist/design-critique/completed-arc-record.js.map +1 -1
  71. package/vendor/directive-core/dist/design-critique/ingest-posture.d.ts +27 -0
  72. package/vendor/directive-core/dist/design-critique/ingest-posture.d.ts.map +1 -0
  73. package/vendor/directive-core/dist/design-critique/ingest-posture.js +66 -0
  74. package/vendor/directive-core/dist/design-critique/ingest-posture.js.map +1 -0
  75. package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.d.ts +106 -7
  76. package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.d.ts.map +1 -1
  77. package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.js +284 -17
  78. package/vendor/directive-core/dist/design-critique/panel-handback-acceptance.js.map +1 -1
  79. package/vendor/directive-core/dist/docs/docs-impact.d.ts +13 -0
  80. package/vendor/directive-core/dist/docs/docs-impact.d.ts.map +1 -1
  81. package/vendor/directive-core/dist/docs/docs-impact.js +49 -4
  82. package/vendor/directive-core/dist/docs/docs-impact.js.map +1 -1
  83. package/vendor/directive-core/dist/doctor/main.d.ts +48 -0
  84. package/vendor/directive-core/dist/doctor/main.d.ts.map +1 -1
  85. package/vendor/directive-core/dist/doctor/main.js +221 -4
  86. package/vendor/directive-core/dist/doctor/main.js.map +1 -1
  87. package/vendor/directive-core/dist/hooks/dispatcher.d.ts.map +1 -1
  88. package/vendor/directive-core/dist/hooks/dispatcher.js +151 -13
  89. package/vendor/directive-core/dist/hooks/dispatcher.js.map +1 -1
  90. package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts +4 -0
  91. package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts.map +1 -1
  92. package/vendor/directive-core/dist/init-deposit/agent-hooks.js +25 -4
  93. package/vendor/directive-core/dist/init-deposit/agent-hooks.js.map +1 -1
  94. package/vendor/directive-core/dist/intake/issue-ingest.d.ts +2 -0
  95. package/vendor/directive-core/dist/intake/issue-ingest.d.ts.map +1 -1
  96. package/vendor/directive-core/dist/intake/issue-ingest.js +26 -1
  97. package/vendor/directive-core/dist/intake/issue-ingest.js.map +1 -1
  98. package/vendor/directive-core/dist/pr-closing-keywords/detect.d.ts.map +1 -1
  99. package/vendor/directive-core/dist/pr-closing-keywords/detect.js +52 -7
  100. package/vendor/directive-core/dist/pr-closing-keywords/detect.js.map +1 -1
  101. package/vendor/directive-core/dist/pr-watch/main.d.ts +5 -0
  102. package/vendor/directive-core/dist/pr-watch/main.d.ts.map +1 -1
  103. package/vendor/directive-core/dist/pr-watch/main.js.map +1 -1
  104. package/vendor/directive-core/dist/pr-watch/watch.d.ts.map +1 -1
  105. package/vendor/directive-core/dist/pr-watch/watch.js +5 -0
  106. package/vendor/directive-core/dist/pr-watch/watch.js.map +1 -1
  107. package/vendor/directive-core/dist/presentation-ceiling/evaluate.d.ts.map +1 -1
  108. package/vendor/directive-core/dist/presentation-ceiling/evaluate.js +3 -1
  109. package/vendor/directive-core/dist/presentation-ceiling/evaluate.js.map +1 -1
  110. package/vendor/directive-core/dist/release/auto-hatch.d.ts +6 -0
  111. package/vendor/directive-core/dist/release/auto-hatch.d.ts.map +1 -1
  112. package/vendor/directive-core/dist/release/auto-hatch.js +13 -4
  113. package/vendor/directive-core/dist/release/auto-hatch.js.map +1 -1
  114. package/vendor/directive-core/dist/review-monitor/record.d.ts +11 -0
  115. package/vendor/directive-core/dist/review-monitor/record.d.ts.map +1 -1
  116. package/vendor/directive-core/dist/review-monitor/record.js +23 -0
  117. package/vendor/directive-core/dist/review-monitor/record.js.map +1 -1
  118. package/vendor/directive-core/dist/run-summary/emit.d.ts +2 -1
  119. package/vendor/directive-core/dist/run-summary/emit.d.ts.map +1 -1
  120. package/vendor/directive-core/dist/run-summary/emit.js +3 -0
  121. package/vendor/directive-core/dist/run-summary/emit.js.map +1 -1
  122. package/vendor/directive-core/dist/run-summary/index.d.ts +1 -1
  123. package/vendor/directive-core/dist/run-summary/index.d.ts.map +1 -1
  124. package/vendor/directive-core/dist/run-summary/index.js.map +1 -1
  125. package/vendor/directive-core/dist/run-summary/types.d.ts +8 -2
  126. package/vendor/directive-core/dist/run-summary/types.d.ts.map +1 -1
  127. package/vendor/directive-core/dist/run-summary/types.js +1 -0
  128. package/vendor/directive-core/dist/run-summary/types.js.map +1 -1
  129. package/vendor/directive-core/dist/scope/delivery-evidence.d.ts +11 -0
  130. package/vendor/directive-core/dist/scope/delivery-evidence.d.ts.map +1 -1
  131. package/vendor/directive-core/dist/scope/delivery-evidence.js +34 -0
  132. package/vendor/directive-core/dist/scope/delivery-evidence.js.map +1 -1
  133. package/vendor/directive-core/dist/session/occupancy.d.ts +101 -0
  134. package/vendor/directive-core/dist/session/occupancy.d.ts.map +1 -1
  135. package/vendor/directive-core/dist/session/occupancy.js +276 -1
  136. package/vendor/directive-core/dist/session/occupancy.js.map +1 -1
  137. package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.d.ts.map +1 -1
  138. package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.js +5 -1
  139. package/vendor/directive-core/dist/swarm/cohort-review-monitors-cli.js.map +1 -1
  140. package/vendor/directive-core/dist/swarm/cohort-review-monitors.d.ts +71 -0
  141. package/vendor/directive-core/dist/swarm/cohort-review-monitors.d.ts.map +1 -1
  142. package/vendor/directive-core/dist/swarm/cohort-review-monitors.js +125 -2
  143. package/vendor/directive-core/dist/swarm/cohort-review-monitors.js.map +1 -1
  144. package/vendor/directive-core/dist/telemetry-coverage/fake-trial.d.ts.map +1 -1
  145. package/vendor/directive-core/dist/telemetry-coverage/fake-trial.js +9 -0
  146. package/vendor/directive-core/dist/telemetry-coverage/fake-trial.js.map +1 -1
  147. package/vendor/directive-core/dist/telemetry-coverage/kinds.d.ts.map +1 -1
  148. package/vendor/directive-core/dist/telemetry-coverage/kinds.js +2 -0
  149. package/vendor/directive-core/dist/telemetry-coverage/kinds.js.map +1 -1
  150. package/vendor/directive-core/dist/value/feedback-file.d.ts +49 -6
  151. package/vendor/directive-core/dist/value/feedback-file.d.ts.map +1 -1
  152. package/vendor/directive-core/dist/value/feedback-file.js +446 -41
  153. package/vendor/directive-core/dist/value/feedback-file.js.map +1 -1
@@ -18,7 +18,7 @@ import { SCOPE_NOT_READY_PROMOTE_THEN_ACTIVATE } from "../scope/transition-hint.
18
18
  import { appendSoftAgentsRebindToMessage, decisionCarriesSoftAgentsRebind, formatSoftAgentsRebindChecklist, } from "../session/compact-ritual.js";
19
19
  import { defaultGitRunner, detectBranch, existingAncestorDir, gitCommonDir, memoizeGitRunner, worktreePathOrNull, } from "../session/git.js";
20
20
  import { isLinkedWorktreePath } from "../session/main-worktree.js";
21
- import { evaluateOccupancyWriteGate, formatOccupancyLeaseViewNote, occupancyPath, resolveOccupancyLeaseRoot, } from "../session/occupancy.js";
21
+ import { evaluateApplyingHostLifecycleAdmission, evaluateOccupancyWriteGate, formatOccupancyLeaseViewNote, liveOccupant, occupancyPath, publishApplyingHost, publishApplyingHostFromCanonicalSessionId, resolveOccupancyLeaseRoot, } from "../session/occupancy.js";
22
22
  import { occupancyAwareDenialMessage } from "../session/occupancy-recovery.js";
23
23
  import { ENV_SESSION_POSTURE, isRequirementsPosture, overlayTrustedSessionPosture, parseSessionPostureToken, REQUIREMENTS_DEFAULT_ALLOW_PATHS, REQUIREMENTS_DEFAULT_DENY_PATHS, REQUIREMENTS_UPGRADE_PATH, } from "../session/posture.js";
24
24
  import { emitSessionRitualBlockedProcessCost } from "../session/process-cost.js";
@@ -2210,6 +2210,48 @@ function remapProxyShellUpdatedInput(outerPayload, outerToolName, shellProxyUnwr
2210
2210
  // (#5366 Greptile P1). nextToolInput keeps the inner tool name + rewritten command.
2211
2211
  return { ...decision, updatedInput: nextToolInput };
2212
2212
  }
2213
+ /**
2214
+ * Publish the worktree applying-host record from SessionStart (#5676).
2215
+ *
2216
+ * Best-effort and non-blocking: a publish miss must not deny SessionStart.
2217
+ * Leftover GROK_SESSION_ID alone never selects provider; only GROK_HOOK_EVENT
2218
+ * marks Claude→grok vendor-compat transport.
2219
+ */
2220
+ function tryPublishApplyingHostFromSessionStart(input, projectRoot) {
2221
+ try {
2222
+ const environ = input.environ ?? process.env;
2223
+ const identity = resolveHookHostIdentity(input.host, input.payload, environ);
2224
+ if (identity.status !== "ok" || identity.sessionId === null)
2225
+ return;
2226
+ // Do not replace applying-host while another session holds the live occupancy
2227
+ // lease — keeps the incumbent owner's heartbeat/release/session:end usable
2228
+ // (#5676 Greptile P1). Matching owner may republish as reinforcement.
2229
+ const live = liveOccupant(projectRoot);
2230
+ if (live !== null && live.sessionId !== identity.sessionId) {
2231
+ return;
2232
+ }
2233
+ const transport = identity.provider === "grok" && environ.GROK_HOOK_EVENT?.trim()
2234
+ ? "grok-hook-event"
2235
+ : "native";
2236
+ // Native Cursor SessionStart must stay cursor even when GROK_SESSION_ID lingers.
2237
+ if (input.host === "cursor" && transport === "native") {
2238
+ publishApplyingHost(projectRoot, {
2239
+ provider: "cursor",
2240
+ rawSessionId: identity.rawSessionId,
2241
+ transport: "native",
2242
+ });
2243
+ return;
2244
+ }
2245
+ publishApplyingHost(projectRoot, {
2246
+ provider: identity.provider,
2247
+ rawSessionId: identity.rawSessionId,
2248
+ transport,
2249
+ });
2250
+ }
2251
+ catch {
2252
+ /* SessionStart stays non-blocking */
2253
+ }
2254
+ }
2213
2255
  /**
2214
2256
  * Add the claim-time owner only after the existing shell decision allowed the
2215
2257
  * command. The host wire format must emit `allow` with updated input, so this
@@ -2243,6 +2285,21 @@ function attachLifecycleIdentityRewrite(input, toolName, decision, seams) {
2243
2285
  identity.status === "ok" &&
2244
2286
  identity.sessionId !== null &&
2245
2287
  hintedSession.sessionId !== identity.sessionId) {
2288
+ const admission = evaluateApplyingHostLifecycleAdmission({
2289
+ projectRoot: resolve(input.projectRoot),
2290
+ // Resolved provider (e.g. Claude+GROK_HOOK_EVENT → grok), not wire host.
2291
+ depositHost: identity.provider,
2292
+ depositSessionId: identity.sessionId,
2293
+ namedSessionId: hintedSession.sessionId,
2294
+ });
2295
+ if (admission.kind === "soft-pass")
2296
+ return decision;
2297
+ if (admission.kind === "repair") {
2298
+ return deny(input, "occupancy-identity-conflict", toolName, withHookHostArgv(input.host, admission.message));
2299
+ }
2300
+ if (admission.kind === "foreign-owner") {
2301
+ return deny(input, "occupancy-identity-conflict", toolName, withHookHostArgv(input.host, admission.message));
2302
+ }
2246
2303
  return deny(input, "occupancy-identity-conflict", toolName, `Directive denied ${toolName}: lifecycle command ${hinted} names ` +
2247
2304
  `${hintedSession.sessionId ?? "<missing>"}, but the host owner is ` +
2248
2305
  `${identity.sessionId}. Re-run as a simple command or pass ` +
@@ -2254,9 +2311,49 @@ function attachLifecycleIdentityRewrite(input, toolName, decision, seams) {
2254
2311
  }
2255
2312
  return deny(input, "occupancy-identity-unavailable", toolName, uninspectableLifecycleDenyMessage(hinted, named, input.payload));
2256
2313
  }
2257
- // #4660: non-claiming read-only session:start keeps the prior allow.
2258
- if (!lifecycle.requiresOwner)
2314
+ // #4660: non-claiming read-only session:start keeps the prior allow, and may
2315
+ // publish/repair the applying-host record without requiring a prior record (#5676).
2316
+ // Skip when --project-root (rewriteUnsafe) is present so this hook does not
2317
+ // overwrite a different worktree; the CLI publishes the command target.
2318
+ if (!lifecycle.requiresOwner) {
2319
+ if (lifecycle.rewriteSafe &&
2320
+ lifecycle.sessionIdStatus === "present" &&
2321
+ lifecycle.sessionId !== null) {
2322
+ try {
2323
+ const root = resolve(input.projectRoot);
2324
+ // Same live-lease guard as SessionStart (#5676 Greptile P1): read-only
2325
+ // session:start must not replace applying-host for a foreign live owner.
2326
+ const live = liveOccupant(root);
2327
+ if (live === null || live.sessionId === lifecycle.sessionId) {
2328
+ const environ = input.environ ?? process.env;
2329
+ const transport = environ.GROK_HOOK_EVENT?.trim()
2330
+ ? "grok-hook-event"
2331
+ : "native";
2332
+ // Prefer deposit identity when it matches the named canonical owner.
2333
+ const identity = resolveHookHostIdentity(input.host, input.payload, environ);
2334
+ if (identity.status === "ok" &&
2335
+ identity.sessionId === lifecycle.sessionId &&
2336
+ // Leftover GROK_SESSION_ID alone must not publish Cursor as Claude/grok.
2337
+ !(input.host === "cursor" && transport === "grok-hook-event")) {
2338
+ publishApplyingHost(root, {
2339
+ provider: identity.provider,
2340
+ rawSessionId: identity.rawSessionId,
2341
+ transport: input.host === "cursor" ? "native" : transport,
2342
+ });
2343
+ }
2344
+ else {
2345
+ publishApplyingHostFromCanonicalSessionId(root, lifecycle.sessionId, {
2346
+ transport: input.host === "cursor" ? "native" : transport,
2347
+ });
2348
+ }
2349
+ }
2350
+ }
2351
+ catch {
2352
+ /* read-only path stays non-blocking */
2353
+ }
2354
+ }
2259
2355
  return decision;
2356
+ }
2260
2357
  const denyIdentity = (code, detail) => deny(input, code, toolName, withHookHostArgv(input.host, detail));
2261
2358
  if (lifecycle.sessionIdStatus === "invalid") {
2262
2359
  return denyIdentity("occupancy-identity-conflict", `Directive denied exact lifecycle command ${lifecycle.verb}: ` +
@@ -2279,26 +2376,63 @@ function attachLifecycleIdentityRewrite(input, toolName, decision, seams) {
2279
2376
  "Directive cannot bind the occupancy claim without a stable host owner; " +
2280
2377
  "manual callers must pass --session-id explicitly outside the host rewrite path.");
2281
2378
  }
2379
+ const applyingAdmission = evaluateApplyingHostLifecycleAdmission({
2380
+ projectRoot: resolve(input.projectRoot),
2381
+ // Resolved provider (e.g. Claude+GROK_HOOK_EVENT → grok), not wire host.
2382
+ depositHost: identity.provider,
2383
+ depositSessionId: identity.sessionId,
2384
+ namedSessionId: lifecycle.sessionIdStatus === "present" ? (lifecycle.sessionId ?? null) : null,
2385
+ });
2386
+ // Confirmed occupancy:steal must reach stealOccupancy under the thief's
2387
+ // verified identity; applying-host still naming the incumbent must not block
2388
+ // that supported takeover (#5676 Greptile P1).
2389
+ const stealConfirmBypass = lifecycle.verb === "occupancy:steal" &&
2390
+ (hookShellCommand(input.payload) ?? "").split(/\s+/).includes("--confirm");
2391
+ if (applyingAdmission.kind === "repair" && !stealConfirmBypass) {
2392
+ return denyIdentity("occupancy-identity-conflict", applyingAdmission.message);
2393
+ }
2394
+ let liveOwnerBypass = false;
2395
+ if (applyingAdmission.kind === "foreign-owner") {
2396
+ const live = liveOccupant(resolve(input.projectRoot));
2397
+ if (live !== null && live.sessionId === identity.sessionId) {
2398
+ // Applying-host was replaced by another SessionStart; keep live lease owner
2399
+ // on the rewrite path for heartbeat/release/session:end (#5676).
2400
+ liveOwnerBypass = true;
2401
+ }
2402
+ else if (!stealConfirmBypass) {
2403
+ return denyIdentity("occupancy-identity-conflict", applyingAdmission.message);
2404
+ }
2405
+ }
2406
+ // Confirmed steal: thief is always the verified host identity. Never promote
2407
+ // applying-host (incumbent) or an unrelated --session-id into the lease
2408
+ // (#5676 Greptile P1 "Takeovers trust the wrong owner").
2409
+ const effectiveOwner = stealConfirmBypass || liveOwnerBypass
2410
+ ? identity.sessionId
2411
+ : applyingAdmission.kind === "soft-pass" || applyingAdmission.kind === "enforce"
2412
+ ? applyingAdmission.record.applyingHostSessionId
2413
+ : identity.sessionId;
2282
2414
  const environmentId = (input.environ ?? process.env).DEFT_SESSION_ID?.trim();
2283
- if (environmentId !== undefined &&
2284
- environmentId.length > 0 &&
2285
- environmentId !== identity.sessionId) {
2286
- return denyIdentity("occupancy-identity-conflict", `Directive denied ${toolName}: host owner ${identity.sessionId} conflicts with ` +
2415
+ if (environmentId !== undefined && environmentId.length > 0 && environmentId !== effectiveOwner) {
2416
+ return denyIdentity("occupancy-identity-conflict", `Directive denied ${toolName}: host owner ${effectiveOwner} conflicts with ` +
2287
2417
  `DEFT_SESSION_ID ${environmentId}; refusing an auto-approved lifecycle rewrite.`);
2288
2418
  }
2289
- if (lifecycle.sessionIdStatus === "present" && lifecycle.sessionId !== identity.sessionId) {
2419
+ if (lifecycle.sessionIdStatus === "present" && lifecycle.sessionId !== effectiveOwner) {
2290
2420
  return denyIdentity("occupancy-identity-conflict", `Directive denied ${toolName}: lifecycle command ${lifecycle.verb} names ` +
2291
2421
  `${lifecycle.sessionId ?? "<missing>"}, but the host owner is ` +
2292
- `${identity.sessionId}.`);
2422
+ `${effectiveOwner}.`);
2293
2423
  }
2294
2424
  const sourceTask = !lifecycle.task ||
2295
2425
  (seams.runningInsideDeftRepo ?? runningInsideDeftRepo)(resolve(input.projectRoot));
2296
- const executionRoot = lifecycleExecutionRootCheck(input.payload, input.projectRoot, seams.realpathLifecycleExecutionRoot ?? realpathSync, seams.lifecycleExecutionPlatform ?? process.platform, identity.sessionId);
2426
+ const executionRoot = lifecycleExecutionRootCheck(input.payload, input.projectRoot, seams.realpathLifecycleExecutionRoot ?? realpathSync, seams.lifecycleExecutionPlatform ?? process.platform, effectiveOwner);
2297
2427
  if (!executionRoot.aligned) {
2298
2428
  return denyIdentity("occupancy-identity-conflict", `Directive denied exact lifecycle command ${lifecycle.verb}: ${executionRoot.message}. ` +
2299
2429
  `Run the lifecycle command from ${normalizeHookProjectRoot(resolve(input.projectRoot))} ` +
2300
2430
  "so policy, lease, and ritual state target the same worktree.");
2301
2431
  }
2432
+ // Soft-pass still requires same-worktree execution before skipping rewrite (#5676).
2433
+ if (applyingAdmission.kind === "soft-pass") {
2434
+ return decision;
2435
+ }
2302
2436
  const rewriteAllowed = lifecycle.rewriteSafe && sourceTask;
2303
2437
  if (lifecycle.sessionIdStatus === "absent" && !rewriteAllowed) {
2304
2438
  let reason;
@@ -2309,15 +2443,15 @@ function attachLifecycleIdentityRewrite(input, toolName, decision, seams) {
2309
2443
  reason = "this argument shape is outside the narrowly auto-approved rewrite surface";
2310
2444
  }
2311
2445
  return denyIdentity("occupancy-identity-unavailable", `Directive denied exact lifecycle command ${lifecycle.verb}: ${reason}. ` +
2312
- `Re-run the command with --session-id=${identity.sessionId}; Directive will verify ` +
2446
+ `Re-run the command with --session-id=${effectiveOwner}; Directive will verify ` +
2313
2447
  "that explicit owner without auto-approving or replacing the command.");
2314
2448
  }
2315
- const rewrite = rewriteExactLifecycleCommand(input.payload, identity.sessionId);
2449
+ const rewrite = rewriteExactLifecycleCommand(input.payload, effectiveOwner);
2316
2450
  if (rewrite === null)
2317
2451
  return decision;
2318
2452
  if (rewrite.kind === "conflict") {
2319
2453
  return denyIdentity("occupancy-identity-conflict", `Directive denied ${toolName}: ${rewrite.message} Host payload owner is ` +
2320
- `${identity.sessionId}, but the command names ${rewrite.existingSessionId}.`);
2454
+ `${effectiveOwner}, but the command names ${rewrite.existingSessionId}.`);
2321
2455
  }
2322
2456
  return { ...decision, updatedInput: rewrite.updatedInput };
2323
2457
  }
@@ -2612,6 +2746,10 @@ function routeHookDecision(input, seams, observation) {
2612
2746
  scopePath: null,
2613
2747
  };
2614
2748
  }
2749
+ // #5676: SessionStart-first applying-host record before any Shell PreToolUse.
2750
+ // Leftover GROK_SESSION_ID alone must not remap native Cursor; GROK_HOOK_EVENT
2751
+ // still marks Claude→grok vendor-compat transport.
2752
+ tryPublishApplyingHostFromSessionStart(input, projectRoot);
2615
2753
  try {
2616
2754
  const result = (seams.sessionStart ?? runSessionStartHookWrite)(projectRoot);
2617
2755
  if (result.code !== 0) {