@deftai/directive 0.119.11 → 0.119.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/check.js CHANGED
@@ -13,6 +13,7 @@ import { join, resolve } from "node:path";
13
13
  import { fileURLToPath } from "node:url";
14
14
  import { dispatchTaskCheck } from "@deftai/directive-core/check";
15
15
  import { DEFT_REPO_POSITIVE_MARKERS } from "@deftai/directive-core/doctor";
16
+ import { refuseIfWslOwnershipBlocked } from "./wsl-ownership-guard.js";
16
17
  function isFrameworkSourceCheckoutAt(projectRoot) {
17
18
  try {
18
19
  if (!statSync(join(projectRoot, "main.md")).isFile()) {
@@ -135,6 +136,10 @@ export function run(argv) {
135
136
  process.stderr.write(FRAMEWORK_ROOT_MISS);
136
137
  return 2;
137
138
  }
139
+ // #1617: live WSL ownership gate before first protected check write/orchestration.
140
+ const blocked = refuseIfWslOwnershipBlocked(args.projectRoot);
141
+ if (blocked !== null)
142
+ return blocked;
138
143
  return dispatchTaskCheck(args.frameworkRoot, args.projectRoot, { noCache: args.noCache });
139
144
  }
140
145
  /* v8 ignore start -- entry guard */
@@ -10,9 +10,9 @@ export interface DispatchIo {
10
10
  writeErr: (text: string) => void;
11
11
  }
12
12
  /** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
13
- export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "release-wait-npm", "scope-lifecycle", "scope-record-approved-scope", "scope-record-observable-scope", "scope-record-intent-constraint", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "occupancy-release", "occupancy-heartbeat", "occupancy-grant", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-strip-withdrawn", "triage-evaluate", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "xbrief-adopt-stored-plan-id", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-class-checks", "verify-presentation-coverage", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-deposit-closure", "verify-consumer-hard-stops", "verify-closing-keywords", "verify-skill-external-fetch-gate", "verify-semantic-single-source", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "verify-spec-prd-fresh", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "verify-subagent-steer", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-pr-closeout-attestable", "verify-lifecycle-visible", "verify-completed-tracked", "verify-completed-write-guard", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-agents-md-advisory", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "verify-evaluator-surface", "verify-observable-scope", "verify-operator-scope-limit", "verify-presentation-ceiling", "verify-intent-constraint", "verify-durable-effect-acquisition", "verify-consumer-test-lane", "eval-health", "eval-run", "eval-report", "eval-triggers"];
13
+ export declare const CLI_MODULE_VERBS: readonly ["agents-refresh", "cache", "check", "capacity-backfill", "capacity-show", "codebase-default-extractor", "codebase-map", "codebase-map-fresh", "codebase-projection-registry", "codebase-provider", "doctor", "ownership-doctor", "ownership-fix", "verify-ownership", "install-upgrade", "install-uninstall", "migrate-preflight", "migrate-xbrief", "migrate-category-b", "framework-check-updates", "hook-dispatch", "umbrella-current-shape", "changelog-check", "change-init", "commit-lint", "coverage-hotspots", "policy", "authz", "escalation-cli", "pr-closing-keywords", "pr-merge-readiness", "pr-monitor", "pr-protected-issues", "pr-wait-mergeable", "pr-watch", "pr-finish-loop", "directive-finish-loop", "preflight-cache", "preflight-gh", "probe-session", "release", "release-e2e", "release-publish", "release-rollback", "release-wait-npm", "scope-lifecycle", "scope-record-approved-scope", "scope-record-observable-scope", "scope-record-intent-constraint", "scm-sync-default", "lifecycle-event", "lifecycle-stats", "session-start", "session-ready", "occupancy-steal", "occupancy-release", "occupancy-heartbeat", "occupancy-grant", "plan-sequence", "slice", "subagent-monitor", "toolchain-check", "triage-actions", "triage-bootstrap", "triage-bulk", "triage-classify", "triage-strip-withdrawn", "triage-evaluate", "triage-help", "triage-queue", "triage-reconcile", "triage-refresh", "triage-scope", "triage-scope-drift", "triage-smoketest", "triage-subscribe", "triage-summary", "triage-welcome", "ts-check-lane", "vbrief-activate", "vbrief-build", "vbrief-preflight", "vbrief-reconcile", "vbrief-validate", "vbrief-validation", "xbrief-create", "xbrief-verify", "xbrief-adopt-stored-plan-id", "verify-branch", "verify-encoding", "verify-forward-coverage", "verify-test-boundary", "verify-class-checks", "verify-presentation-coverage", "verify-scope-provenance", "verify-consumer-check-contract", "verify-hooks-installed", "verify-investigation", "verify-judgment-gates", "verify-no-task-runtime", "validate-links", "validate-strategy-output", "verify-biome-config", "verify-bridge-drift", "verify-capacity", "verify-contained-writes", "verify-telemetry-coverage", "verify-content-manifest", "verify-deposit-closure", "verify-consumer-hard-stops", "verify-closing-keywords", "verify-skill-external-fetch-gate", "verify-semantic-single-source", "verify-contract-drift", "verify-cursor-tier1", "verify-openclaw-tier1", "verify-go-freeze", "verify-scm-boundary", "verify-session-ritual", "verify-plan-sequence", "verify-stubs", "verify-xbrief-drift", "verify-spec-prd-fresh", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-l4-owner", "verify-subagent-alive", "verify-subagent-steer", "review-monitor-register", "review-monitor-release", "verify-tools", "verify-wip-cap", "verify-orphan-active", "verify-pr-closeout-attestable", "verify-lifecycle-visible", "verify-completed-tracked", "verify-completed-write-guard", "verify-literal-ac", "verify-ac", "verify-agents-md-budget", "verify-consumer-header-placeholder", "verify-agents-md-advisory", "verify-persisted-planning-narratives", "verify-eval-health-relocation", "verify-eval-triggers-relocation", "verify-evaluator-surface", "verify-observable-scope", "verify-operator-scope-limit", "verify-presentation-ceiling", "verify-intent-constraint", "verify-durable-effect-acquisition", "verify-consumer-test-lane", "eval-health", "eval-run", "eval-report", "eval-triggers"];
14
14
  /** Core-only CLI entrypoints without a packages/cli wrapper. */
15
- export declare const CORE_MODULE_VERBS: readonly ["scm", "scm-readiness", "github-auth-modes", "github-body", "issue-emit", "issue-ingest", "issue-sync-from-xbrief", "reconcile-issues", "swarm-launch", "swarm-complete-cohort", "swarm-finalize-cohort", "swarm-finalize-owed", "swarm-pre-dispatch", "swarm-readiness", "swarm-routing-verify", "swarm-routing-set", "swarm-verify-review-clean", "swarm-worktrees", "framework-commands", "pack-render", "packs-slice", "prd-render", "export-spec", "project-render", "project-write-narratives", "roadmap-render", "rule-map", "spec-render", "spec-validate", "code-structure-validate", "pack-migrate-skills", "pack-migrate-rules", "pack-migrate-strategies", "pack-migrate-patterns", "pack-migrate-swarm-spec", "policy-set", "setup-ghx", "scope-undo", "scope-demote", "scope-decompose", "changelog-resolve-unreleased", "architecture-preflight-sor", "feedback-file", "value-readback", "product-signal", "freshness-report", "decision-write", "decision-list", "docs-impact"];
15
+ export declare const CORE_MODULE_VERBS: readonly ["scm", "scm-readiness", "github-auth-modes", "github-body", "issue-emit", "issue-ingest", "issue-sync-from-xbrief", "reconcile-issues", "swarm-launch", "swarm-complete-cohort", "swarm-finalize-cohort", "swarm-finalize-owed", "swarm-pre-dispatch", "swarm-readiness", "swarm-routing-verify", "swarm-routing-set", "swarm-verify-review-clean", "swarm-worktrees", "framework-commands", "pack-render", "packs-slice", "prd-render", "export-spec", "project-render", "project-write-narratives", "roadmap-render", "rule-map", "spec-render", "spec-validate", "code-structure-validate", "pack-migrate-skills", "pack-migrate-rules", "pack-migrate-strategies", "pack-migrate-patterns", "pack-migrate-swarm-spec", "policy-set", "setup-ghx", "scope-undo", "scope-demote", "scope-decompose", "changelog-resolve-unreleased", "architecture-preflight-sor", "feedback-file", "value-readback", "product-signal", "freshness-report", "decision-write", "decision-list", "docs-impact", "migrate-clause-ids"];
16
16
  /** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
17
17
  export declare const TRIAGE_ACTION_ALIAS_SUBCOMMANDS: Readonly<Record<string, string>>;
18
18
  /** Colon aliases for policy subcommands (mirrors cli-router SUBCOMMAND_ROUTES). */
package/dist/dispatch.js CHANGED
@@ -38,6 +38,9 @@ export const CLI_MODULE_VERBS = [
38
38
  "codebase-projection-registry",
39
39
  "codebase-provider",
40
40
  "doctor",
41
+ "ownership-doctor",
42
+ "ownership-fix",
43
+ "verify-ownership",
41
44
  "install-upgrade",
42
45
  "install-uninstall",
43
46
  "migrate-preflight",
@@ -165,7 +168,9 @@ export const CLI_MODULE_VERBS = [
165
168
  "verify-literal-ac",
166
169
  "verify-ac",
167
170
  "verify-agents-md-budget",
171
+ "verify-consumer-header-placeholder",
168
172
  "verify-agents-md-advisory",
173
+ "verify-persisted-planning-narratives",
169
174
  "verify-eval-health-relocation",
170
175
  "verify-eval-triggers-relocation",
171
176
  "verify-evaluator-surface",
@@ -231,6 +236,7 @@ export const CORE_MODULE_VERBS = [
231
236
  "decision-write",
232
237
  "decision-list",
233
238
  "docs-impact",
239
+ "migrate-clause-ids",
234
240
  ];
235
241
  /** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
236
242
  export const TRIAGE_ACTION_ALIAS_SUBCOMMANDS = {
@@ -330,7 +336,9 @@ export const VERB_ALIASES = {
330
336
  "verify:ac": "verify-ac",
331
337
  "verify:acceptance": "verify-ac",
332
338
  "verify:agents-md-budget": "verify-agents-md-budget",
339
+ "verify:consumer-header-placeholder": "verify-consumer-header-placeholder",
333
340
  "verify:agents-md-advisory": "verify-agents-md-advisory",
341
+ "verify:persisted-planning-narratives": "verify-persisted-planning-narratives",
334
342
  "verify:eval-health-relocation": "verify-eval-health-relocation",
335
343
  "verify:eval-triggers-relocation": "verify-eval-triggers-relocation",
336
344
  "verify:evaluator-surface": "verify-evaluator-surface",
@@ -410,6 +418,7 @@ export const VERB_ALIASES = {
410
418
  "migrate:preflight": "migrate-preflight",
411
419
  "migrate:xbrief": "migrate-xbrief",
412
420
  "migrate:category-b": "migrate-category-b",
421
+ "migrate:clause-ids": "migrate-clause-ids",
413
422
  "framework:check-updates": "framework-check-updates",
414
423
  "umbrella:current-shape": "umbrella-current-shape",
415
424
  "issue:sync-from-xbrief": "issue-sync-from-xbrief",
@@ -437,6 +446,12 @@ export const VERB_ALIASES = {
437
446
  "pr:finish-loop": "pr-finish-loop",
438
447
  "directive:finish-loop": "directive-finish-loop",
439
448
  doctor: "doctor",
449
+ "ownership:doctor": "ownership-doctor",
450
+ "ownership:fix": "ownership-fix",
451
+ "verify:ownership": "verify-ownership",
452
+ "deft:ownership:doctor": "ownership-doctor",
453
+ "deft:ownership:fix": "ownership-fix",
454
+ "deft:verify:ownership": "verify-ownership",
440
455
  "eval:health": "eval-health",
441
456
  "feedback:file": "feedback-file",
442
457
  "value:show": "value-readback",
@@ -2619,6 +2634,10 @@ async function loadCoreModuleHandler(verb, io) {
2619
2634
  const { docsImpactMain } = await import("@deftai/directive-core/dist/docs/docs-impact.js");
2620
2635
  return (argv) => docsImpactMain(argv);
2621
2636
  }
2637
+ case "migrate-clause-ids": {
2638
+ const { mainEntry } = await import("@deftai/directive-core/dist/xbrief-migrate/clause-ids.js");
2639
+ return (argv) => mainEntry(argv);
2640
+ }
2622
2641
  default:
2623
2642
  throw new Error(`unknown core verb: ${verb}`);
2624
2643
  }
@@ -1,4 +1,5 @@
1
1
  import { parseUpdateArgv, runRefreshDepositCli, UnknownUpdateFlagError, } from "@deftai/directive-core/init-deposit";
2
+ import { refuseIfWslOwnershipBlocked } from "../wsl-ownership-guard.js";
2
3
  import { CANONICAL_UPDATE_ARGV, UPDATE_DRY_RUN_FLAGS } from "./constants.js";
3
4
  import { argvWantsHelp, printUpdateHelp } from "./help.js";
4
5
  /** True when the user argv asked for a classify-only dry-run (`--dry-run`/`--plan`). */
@@ -13,6 +14,12 @@ export function runUpdate(argv, io) {
13
14
  }
14
15
  try {
15
16
  const args = parseUpdateArgv(CANONICAL_UPDATE_ARGV, argv);
17
+ // #1617: live ownership gate before deposit mutation (dry-run exempt).
18
+ if (!isUpdateDryRun(argv)) {
19
+ const blocked = refuseIfWslOwnershipBlocked(args.projectDir, io.writeErr);
20
+ if (blocked !== null)
21
+ return Promise.resolve(blocked);
22
+ }
16
23
  return runRefreshDepositCli({
17
24
  ...args,
18
25
  dryRun: isUpdateDryRun(argv),
@@ -1,7 +1,17 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * First-class abandoned-lease recovery (#4667). Live actor is session_id in
4
+ * .deft/occupancy.json; bare session:end with empty identity refuses; TTL
5
+ * claim-over and deny-embedded copy-paste remain. host:none / address:none
6
+ * are unset metadata, not the lock cause. Does not reopen anonymous live
7
+ * auto-release (#3954).
8
+ */
9
+ export declare const ABANDONED_OCCUPANCY_LEASE_RECOVERY: string;
10
+ export declare function formatOccupancyReleaseHelp(): string;
2
11
  export declare function parseArgs(argv: readonly string[]): {
3
12
  projectRoot: string;
4
13
  sessionId?: string;
14
+ help?: boolean;
5
15
  error?: string;
6
16
  };
7
17
  export declare function run(argv: readonly string[]): number;
@@ -2,11 +2,38 @@
2
2
  import { resolve } from "node:path";
3
3
  import { releaseOccupancy } from "@deftai/directive-core/session";
4
4
  import { occupancyUnrecognizedArgument } from "./occupancy-unrecognized.js";
5
+ /**
6
+ * First-class abandoned-lease recovery (#4667). Live actor is session_id in
7
+ * .deft/occupancy.json; bare session:end with empty identity refuses; TTL
8
+ * claim-over and deny-embedded copy-paste remain. host:none / address:none
9
+ * are unset metadata, not the lock cause. Does not reopen anonymous live
10
+ * auto-release (#3954).
11
+ */
12
+ export const ABANDONED_OCCUPANCY_LEASE_RECOVERY = "Abandoned live occupancy lease: bare session:end / occupancy:release without a presented " +
13
+ "session identity refuses while the lease is live. Immediate recovery: " +
14
+ "occupancy:release --session-id=<id from .deft/occupancy.json> " +
15
+ "(read session_id from that file). Or wait for TTL claim-over. " +
16
+ "host:none / address:none are ordinary unset metadata, not the lock cause.";
17
+ export function formatOccupancyReleaseHelp() {
18
+ return [
19
+ "Usage: deft occupancy:release [--project-root <path>] [--session-id <id>]",
20
+ "",
21
+ "Release this worktree occupancy lease (owner live, or expired residue).",
22
+ "",
23
+ ABANDONED_OCCUPANCY_LEASE_RECOVERY,
24
+ "",
25
+ ].join("\n");
26
+ }
5
27
  export function parseArgs(argv) {
6
- const parsed = { projectRoot: "." };
28
+ const parsed = {
29
+ projectRoot: ".",
30
+ };
7
31
  for (let i = 0; i < argv.length; i += 1) {
8
32
  const arg = argv[i];
9
- if (arg === "--project-root") {
33
+ if (arg === "--help" || arg === "-h") {
34
+ parsed.help = true;
35
+ }
36
+ else if (arg === "--project-root") {
10
37
  const value = argv[i + 1];
11
38
  if (value === undefined || value.startsWith("--")) {
12
39
  return { ...parsed, error: "argument --project-root: expected one argument" };
@@ -52,6 +79,10 @@ export function run(argv) {
52
79
  process.stderr.write(`occupancy:release: ${args.error}\n`);
53
80
  return 2;
54
81
  }
82
+ if (args.help === true) {
83
+ process.stdout.write(formatOccupancyReleaseHelp());
84
+ return 0;
85
+ }
55
86
  const result = releaseOccupancy(resolve(args.projectRoot), {
56
87
  sessionId: args.sessionId,
57
88
  env: process.env,
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ export interface OwnershipDoctorArgs {
3
+ projectRoot: string;
4
+ json: boolean;
5
+ owner: string | null;
6
+ error?: string;
7
+ }
8
+ export declare function parseArgs(argv: readonly string[]): OwnershipDoctorArgs;
9
+ export declare function run(argv: readonly string[]): number;
10
+ //# sourceMappingURL=ownership-doctor.d.ts.map
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ownership:doctor / ownership-doctor (#1617)
4
+ * Diagnose WSL root-runtime vs filesystem project-owner mismatch.
5
+ * Reachable under mismatch (not blocked by the protected-mutation guard).
6
+ */
7
+ import { resolve } from "node:path";
8
+ import { evaluateWslOwnershipGuard, OWNERSHIP_FACTS_CLASSIFIER, ownershipGuardToDict, } from "@deftai/directive-core/platform";
9
+ import { isDirectEntrypoint } from "./entrypoint.js";
10
+ export function parseArgs(argv) {
11
+ const parsed = {
12
+ projectRoot: ".",
13
+ json: false,
14
+ owner: null,
15
+ };
16
+ for (let i = 0; i < argv.length; i += 1) {
17
+ const arg = argv[i];
18
+ if (arg === "--") {
19
+ }
20
+ else if (arg === "--json") {
21
+ parsed.json = true;
22
+ }
23
+ else if (arg === "--project-root") {
24
+ const value = argv[i + 1];
25
+ if (value === undefined) {
26
+ return { ...parsed, error: "argument --project-root: expected one argument" };
27
+ }
28
+ parsed.projectRoot = value;
29
+ i += 1;
30
+ }
31
+ else if (arg?.startsWith("--project-root=")) {
32
+ parsed.projectRoot = arg.slice("--project-root=".length);
33
+ }
34
+ else if (arg === "--owner") {
35
+ const value = argv[i + 1];
36
+ if (value === undefined) {
37
+ return { ...parsed, error: "argument --owner: expected uid:gid" };
38
+ }
39
+ parsed.owner = value;
40
+ i += 1;
41
+ }
42
+ else if (arg?.startsWith("--owner=")) {
43
+ parsed.owner = arg.slice("--owner=".length);
44
+ }
45
+ else if (arg === "--help" || arg === "-h") {
46
+ return { ...parsed, error: "__help__" };
47
+ }
48
+ else {
49
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
50
+ }
51
+ }
52
+ return parsed;
53
+ }
54
+ const HELP = "usage: deft ownership:doctor [--project-root PATH] [--owner uid:gid] [--json]\n" +
55
+ "Diagnose WSL agent-as-root vs filesystem project-owner mismatch (#1617).\n" +
56
+ `Classifier: ${OWNERSHIP_FACTS_CLASSIFIER}\n` +
57
+ "Vocabulary: filesystem project-owner (this command) ≠ occupancy/session owner ≠ issue-emit recovery owner.\n" +
58
+ "Recovery: deft ownership:fix -- --project-root . [--owner uid:gid]\n";
59
+ export function run(argv) {
60
+ const args = parseArgs(argv);
61
+ if (args.error === "__help__") {
62
+ process.stdout.write(HELP);
63
+ return 0;
64
+ }
65
+ if (args.error !== undefined) {
66
+ process.stderr.write(`ownership:doctor: ${args.error}\n`);
67
+ return 2;
68
+ }
69
+ const projectRoot = resolve(args.projectRoot);
70
+ const verdict = evaluateWslOwnershipGuard({
71
+ projectRoot,
72
+ explicitOwner: args.owner,
73
+ });
74
+ if (args.json) {
75
+ process.stdout.write(`${JSON.stringify(ownershipGuardToDict(verdict), null, 2)}\n`);
76
+ }
77
+ else {
78
+ for (const line of verdict.messages) {
79
+ process.stdout.write(`${line}\n`);
80
+ }
81
+ if (verdict.intendedOwner.uid !== null) {
82
+ process.stdout.write(`filesystem project-owner: ${verdict.intendedOwner.uid}:${verdict.intendedOwner.gid}` +
83
+ `${verdict.intendedOwner.account ? ` (${verdict.intendedOwner.account})` : ""}` +
84
+ ` via ${verdict.intendedOwner.source}\n`);
85
+ }
86
+ process.stdout.write(`status=${verdict.status} wsl=${verdict.wsl} mount=${verdict.mount.capability} ` +
87
+ `block_protected_mutation=${verdict.blockProtectedMutation}\n`);
88
+ }
89
+ if (verdict.status === "fail")
90
+ return 1;
91
+ return 0;
92
+ }
93
+ if (isDirectEntrypoint(import.meta.url)) {
94
+ process.exit(run(process.argv.slice(2)));
95
+ }
96
+ //# sourceMappingURL=ownership-doctor.js.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ export interface OwnershipFixArgs {
3
+ projectRoot: string;
4
+ owner: string | null;
5
+ json: boolean;
6
+ error?: string;
7
+ }
8
+ export declare function parseArgs(argv: readonly string[]): OwnershipFixArgs;
9
+ export declare function run(argv: readonly string[]): number;
10
+ //# sourceMappingURL=ownership-fix.d.ts.map
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ownership:fix / ownership-fix (#1617)
4
+ * Scoped repair of root-owned objects under approved roots only.
5
+ * Reachable under mismatch; env-sourced owner alone does not authorize chown.
6
+ */
7
+ import { resolve } from "node:path";
8
+ import { DEFT_PROJECT_OWNER, fixScopedOwnership, OWNERSHIP_FACTS_CLASSIFIER, } from "@deftai/directive-core/platform";
9
+ import { isDirectEntrypoint } from "./entrypoint.js";
10
+ export function parseArgs(argv) {
11
+ const parsed = {
12
+ projectRoot: ".",
13
+ owner: null,
14
+ json: false,
15
+ };
16
+ for (let i = 0; i < argv.length; i += 1) {
17
+ const arg = argv[i];
18
+ if (arg === "--") {
19
+ }
20
+ else if (arg === "--json") {
21
+ parsed.json = true;
22
+ }
23
+ else if (arg === "--project-root") {
24
+ const value = argv[i + 1];
25
+ if (value === undefined) {
26
+ return { ...parsed, error: "argument --project-root: expected one argument" };
27
+ }
28
+ parsed.projectRoot = value;
29
+ i += 1;
30
+ }
31
+ else if (arg?.startsWith("--project-root=")) {
32
+ parsed.projectRoot = arg.slice("--project-root=".length);
33
+ }
34
+ else if (arg === "--owner") {
35
+ const value = argv[i + 1];
36
+ if (value === undefined) {
37
+ return { ...parsed, error: "argument --owner: expected uid:gid" };
38
+ }
39
+ parsed.owner = value;
40
+ i += 1;
41
+ }
42
+ else if (arg?.startsWith("--owner=")) {
43
+ parsed.owner = arg.slice("--owner=".length);
44
+ }
45
+ else if (arg === "--help" || arg === "-h") {
46
+ return { ...parsed, error: "__help__" };
47
+ }
48
+ else {
49
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
50
+ }
51
+ }
52
+ return parsed;
53
+ }
54
+ const HELP = "usage: deft ownership:fix [--project-root PATH] [--owner uid:gid] [--json]\n" +
55
+ "Repair root-owned objects under approved project roots only (#1617).\n" +
56
+ "No blanket HOME chown. No symlink follow outside approved roots.\n" +
57
+ `Pass --owner uid:gid or ${DEFT_PROJECT_OWNER}=uid:gid when inference is ambiguous.\n` +
58
+ `Classifier: ${OWNERSHIP_FACTS_CLASSIFIER}\n`;
59
+ export function run(argv) {
60
+ const args = parseArgs(argv);
61
+ if (args.error === "__help__") {
62
+ process.stdout.write(HELP);
63
+ return 0;
64
+ }
65
+ if (args.error !== undefined) {
66
+ process.stderr.write(`ownership:fix: ${args.error}\n`);
67
+ return 2;
68
+ }
69
+ const projectRoot = resolve(args.projectRoot);
70
+ const result = fixScopedOwnership({
71
+ projectRoot,
72
+ explicitOwner: args.owner,
73
+ });
74
+ if (args.json) {
75
+ process.stdout.write(`${JSON.stringify({
76
+ repaired: result.repaired,
77
+ skipped_protected: result.skippedProtected,
78
+ failed: result.failed,
79
+ messages: result.messages,
80
+ ok: result.ok,
81
+ }, null, 2)}\n`);
82
+ }
83
+ else {
84
+ for (const line of result.messages) {
85
+ process.stdout.write(`${line}\n`);
86
+ }
87
+ }
88
+ return result.exitCode;
89
+ }
90
+ if (isDirectEntrypoint(import.meta.url)) {
91
+ process.exit(run(process.argv.slice(2)));
92
+ }
93
+ //# sourceMappingURL=ownership-fix.js.map
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import { dirname, resolve } from "node:path";
2
3
  import { fileURLToPath } from "node:url";
3
4
  import { emitJson, evaluate, PREFLIGHT_USAGE_HINT } from "@deftai/directive-core/preflight";
4
5
  import { scanWorkClaimForBriefPath } from "@deftai/directive-core/scm";
6
+ import { refuseIfWslOwnershipBlocked } from "./wsl-ownership-guard.js";
5
7
  const HELP_TEXT = `usage: xbrief:preflight [--vbrief-path PATH | PATH] [--json] [--help]
6
8
 
7
9
  Implementation-intent gate (#810): exits 0 only when the xBRIEF is in
@@ -100,6 +102,10 @@ export function run(argv, scan = scanWorkClaimForBriefPath) {
100
102
  return 2;
101
103
  }
102
104
  const vbriefPath = args.vbriefPath;
105
+ // #1617: gate the brief's tree, not process.cwd() (cross-directory callers).
106
+ const blocked = refuseIfWslOwnershipBlocked(dirname(resolve(vbriefPath)));
107
+ if (blocked !== null)
108
+ return blocked;
103
109
  const result = evaluate(vbriefPath);
104
110
  let scanLines = [];
105
111
  try {
package/dist/verify-ac.js CHANGED
@@ -12,7 +12,7 @@ import { join, resolve } from "node:path";
12
12
  import { fileURLToPath } from "node:url";
13
13
  import { resolveSessionCompletedVerifyAcTarget } from "@deftai/directive-core/check";
14
14
  import { formatRejectedLedger, resolveLiteralAcceptanceDetailed, } from "@deftai/directive-core/literal-acceptance";
15
- import { emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, readAdmittedSourceDigest, readAdmittedSourceSentences, readPlanAcceptance, resolveAcceptanceGateProfile, } from "@deftai/directive-core/product-first-done-gate";
15
+ import { emitVerifyAcTerminalOutcome, evaluateVerifyAcFromPath, readAdmittedSourceDigest, readAdmittedSourceSentences, readPlanAcceptance, resolveAcceptanceGateProfile, resolveSoftMissingAcTargets, } from "@deftai/directive-core/product-first-done-gate";
16
16
  /** Parse verify:ac CLI args. */
17
17
  export function parseArgs(argv) {
18
18
  const parsed = {
@@ -277,18 +277,44 @@ export function run(argv) {
277
277
  paths = [found.path];
278
278
  }
279
279
  else if (found.kind === "many") {
280
- // Greptile #3284: multi-active must verify EVERY active scope, not skip.
281
- if (args.softMissingXbrief || args.captureOnly) {
280
+ // #4285: soft-missing selects via DEFT_ACTIVE_SCOPE / explicit path
281
+ // (or fail closed). ALL-paths soft-missing is not the #3284 product bar.
282
+ // --capture-only still lists every active artifact (inventory, not run).
283
+ if (args.captureOnly) {
282
284
  paths = [...found.paths];
283
285
  if (!args.quiet) {
284
- process.stdout.write(`verify:ac multi-active (#3284): evaluating ${paths.length} scopes in ${found.dir}\n`);
286
+ process.stdout.write(`verify:ac capture-only multi-active: listing ${paths.length} scopes in ${found.dir}\n`);
287
+ }
288
+ }
289
+ else if (args.softMissingXbrief) {
290
+ const selected = resolveSoftMissingAcTargets(projectRoot, {
291
+ env: process.env,
292
+ scannedPaths: found.paths,
293
+ });
294
+ if (selected.kind === "one") {
295
+ paths = [selected.path];
296
+ if (!args.quiet) {
297
+ process.stdout.write(`verify:ac soft-missing multi-active (#4285): selected ${selected.path}\n`);
298
+ }
299
+ }
300
+ else {
301
+ process.stderr.write(`${selected.kind === "need-pin" ? selected.message : "verify_ac: multi-active soft-missing needs a pin"}\n` +
302
+ " Usage: task verify:ac -- <path-to-active.xbrief.json>\n" +
303
+ ` Or set DEFT_ACTIVE_SCOPE to the dispatched story under ${found.dir}\n` +
304
+ " Refs #4285 / #3284 product-first done-gate\n");
305
+ emitVerifyAcTerminalOutcome({
306
+ projectRoot,
307
+ env: process.env,
308
+ outcome: "config-error",
309
+ });
310
+ return 1;
285
311
  }
286
312
  }
287
313
  else {
288
314
  process.stderr.write(`verify_ac: ${found.paths.length} active scopes in ${found.dir}.\n` +
289
- " Pass an explicit path, or use check composition (--soft-missing-xbrief) to evaluate all.\n" +
315
+ " Pass an explicit path, or set DEFT_ACTIVE_SCOPE with check composition (--soft-missing-xbrief).\n" +
290
316
  " Usage: task verify:ac -- <path-to-active.xbrief.json>\n" +
291
- " Refs #3284 product-first done-gate\n");
317
+ " Refs #4285 / #3284 product-first done-gate\n");
292
318
  emitVerifyAcTerminalOutcome({
293
319
  projectRoot,
294
320
  env: process.env,
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ projectRoot: string;
4
+ quiet: boolean;
5
+ error?: string;
6
+ }
7
+ /** Parse verify-consumer-header-placeholder CLI args. */
8
+ export declare function parseArgs(argv: string[]): ParsedArgs;
9
+ /** Run the gate and return the process exit code. */
10
+ export declare function run(argv: string[]): number;
11
+ export {};
12
+ //# sourceMappingURL=verify-consumer-header-placeholder.d.ts.map
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI for verify:consumer-header-placeholder (#4544 Prefer-A).
4
+ *
5
+ * Fail closed when product-mutation completion still leaves the unmanaged
6
+ * AGENTS.md header on the exact scaffold edit-me placeholder. Process-only
7
+ * and custom headers pass. Returned failure only.
8
+ */
9
+ import { resolve } from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import { evaluateConsumerHeaderPlaceholderAtRoot } from "@deftai/directive-core/check";
12
+ function takeProjectRootValue(argv, index) {
13
+ const next = argv[index + 1];
14
+ if (next === undefined || next.length === 0 || next.startsWith("-")) {
15
+ return { error: "argument --project-root: expected one argument", next: index };
16
+ }
17
+ return { value: next, next: index + 1 };
18
+ }
19
+ /** Parse verify-consumer-header-placeholder CLI args. */
20
+ export function parseArgs(argv) {
21
+ const parsed = {
22
+ projectRoot: ".",
23
+ quiet: false,
24
+ };
25
+ for (let i = 0; i < argv.length; i += 1) {
26
+ const arg = argv[i];
27
+ if (arg === "--quiet") {
28
+ parsed.quiet = true;
29
+ }
30
+ else if (arg === "--project-root") {
31
+ const taken = takeProjectRootValue(argv, i);
32
+ if (taken.error !== undefined) {
33
+ return { ...parsed, error: taken.error };
34
+ }
35
+ parsed.projectRoot = taken.value ?? parsed.projectRoot;
36
+ i = taken.next;
37
+ }
38
+ else if (arg?.startsWith("--project-root=")) {
39
+ const value = arg.slice("--project-root=".length);
40
+ if (value.length === 0 || value.startsWith("-")) {
41
+ return { ...parsed, error: "argument --project-root: expected one argument" };
42
+ }
43
+ parsed.projectRoot = value;
44
+ }
45
+ else {
46
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
47
+ }
48
+ }
49
+ return parsed;
50
+ }
51
+ /** Run the gate and return the process exit code. */
52
+ export function run(argv) {
53
+ const args = parseArgs(argv);
54
+ if (args.error !== undefined) {
55
+ process.stderr.write(`verify_consumer_header_placeholder: ${args.error}\n`);
56
+ return 2;
57
+ }
58
+ const projectRoot = resolve(args.projectRoot);
59
+ const result = evaluateConsumerHeaderPlaceholderAtRoot(projectRoot);
60
+ if (result.message.length > 0 && !args.quiet) {
61
+ if (result.ok) {
62
+ process.stdout.write(`${result.message}\n`);
63
+ }
64
+ else {
65
+ process.stderr.write(`${result.message}\n`);
66
+ }
67
+ }
68
+ return result.ok ? 0 : 1;
69
+ }
70
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
71
+ process.exit(run(process.argv.slice(2)));
72
+ }
73
+ //# sourceMappingURL=verify-consumer-header-placeholder.js.map
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ export interface VerifyOwnershipArgs {
3
+ projectRoot: string;
4
+ json: boolean;
5
+ owner: string | null;
6
+ error?: string;
7
+ }
8
+ export declare function parseArgs(argv: readonly string[]): VerifyOwnershipArgs;
9
+ export declare function run(argv: readonly string[]): number;
10
+ //# sourceMappingURL=verify-ownership.d.ts.map
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * verify:ownership / verify-ownership (#1617)
4
+ * Fail-closed check for WSL root-runtime ownership mismatch.
5
+ */
6
+ import { resolve } from "node:path";
7
+ import { assertProtectedMutationOwnership, evaluateWslOwnershipGuard, ownershipGuardToDict, } from "@deftai/directive-core/platform";
8
+ import { isDirectEntrypoint } from "./entrypoint.js";
9
+ export function parseArgs(argv) {
10
+ const parsed = {
11
+ projectRoot: ".",
12
+ json: false,
13
+ owner: null,
14
+ };
15
+ for (let i = 0; i < argv.length; i += 1) {
16
+ const arg = argv[i];
17
+ if (arg === "--") {
18
+ }
19
+ else if (arg === "--json") {
20
+ parsed.json = true;
21
+ }
22
+ else if (arg === "--project-root") {
23
+ const value = argv[i + 1];
24
+ if (value === undefined) {
25
+ return { ...parsed, error: "argument --project-root: expected one argument" };
26
+ }
27
+ parsed.projectRoot = value;
28
+ i += 1;
29
+ }
30
+ else if (arg?.startsWith("--project-root=")) {
31
+ parsed.projectRoot = arg.slice("--project-root=".length);
32
+ }
33
+ else if (arg === "--owner") {
34
+ const value = argv[i + 1];
35
+ if (value === undefined) {
36
+ return { ...parsed, error: "argument --owner: expected uid:gid" };
37
+ }
38
+ parsed.owner = value;
39
+ i += 1;
40
+ }
41
+ else if (arg?.startsWith("--owner=")) {
42
+ parsed.owner = arg.slice("--owner=".length);
43
+ }
44
+ else if (arg === "--help" || arg === "-h") {
45
+ return { ...parsed, error: "__help__" };
46
+ }
47
+ else {
48
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
49
+ }
50
+ }
51
+ return parsed;
52
+ }
53
+ const HELP = "usage: deft verify:ownership [--project-root PATH] [--owner uid:gid] [--json]\n" +
54
+ "Fail closed when WSL root runtime can create root-owned project files (#1617).\n" +
55
+ "Native Windows/macOS always pass. Mount-pinned DrvFs/9p without metadata passes.\n";
56
+ export function run(argv) {
57
+ const args = parseArgs(argv);
58
+ if (args.error === "__help__") {
59
+ process.stdout.write(HELP);
60
+ return 0;
61
+ }
62
+ if (args.error !== undefined) {
63
+ process.stderr.write(`verify:ownership: ${args.error}\n`);
64
+ return 2;
65
+ }
66
+ const projectRoot = resolve(args.projectRoot);
67
+ const seams = { projectRoot, explicitOwner: args.owner };
68
+ const gate = assertProtectedMutationOwnership(seams);
69
+ if (args.json) {
70
+ process.stdout.write(`${JSON.stringify(ownershipGuardToDict(gate.verdict), null, 2)}\n`);
71
+ }
72
+ else {
73
+ process.stdout.write(`${gate.message}\n`);
74
+ }
75
+ if (!gate.ok)
76
+ return gate.exitCode;
77
+ // Also surface soft warn / override without failing when not blocked.
78
+ const soft = evaluateWslOwnershipGuard(seams);
79
+ if (soft.status === "warn" || soft.status === "exempt-override") {
80
+ if (!args.json) {
81
+ for (const line of soft.sessionWarnLines)
82
+ process.stderr.write(`${line}\n`);
83
+ }
84
+ }
85
+ return 0;
86
+ }
87
+ if (isDirectEntrypoint(import.meta.url)) {
88
+ process.exit(run(process.argv.slice(2)));
89
+ }
90
+ //# sourceMappingURL=verify-ownership.js.map
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ interface ParsedArgs {
3
+ projectRoot: string;
4
+ checkConjunct: boolean;
5
+ error?: string;
6
+ }
7
+ /** Parse verify:persisted-planning-narratives CLI args (#5176). */
8
+ export declare function parseArgs(argv: string[]): ParsedArgs;
9
+ /** Run the Prefer-A first-ship planning-narrative bar. */
10
+ export declare function run(argv: string[]): number;
11
+ export {};
12
+ //# sourceMappingURL=verify-persisted-planning-narratives.d.ts.map
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI for verify:persisted-planning-narratives (#5176 Prefer-A).
4
+ *
5
+ * Default mode is the unconditional setup Phase 2 bar. `--check-conjunct`
6
+ * mirrors the in-process deft check Prefer-A gate (marker present/unreadable +
7
+ * empty Overview/tech stack → refuse; missing PD / no marker → pass) so
8
+ * Taskfile aggregates like check:merge / check:consumer cannot bypass it.
9
+ */
10
+ import { resolve } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ import { checkRejectsEmptyPlanningNarratives, evaluateCheckPersistedPlanningNarratives, } from "@deftai/directive-core/check";
13
+ import { evaluatePersistedPlanningNarratives } from "@deftai/directive-core/project";
14
+ /** Parse verify:persisted-planning-narratives CLI args (#5176). */
15
+ export function parseArgs(argv) {
16
+ const parsed = { projectRoot: ".", checkConjunct: false };
17
+ for (let i = 0; i < argv.length; i += 1) {
18
+ const arg = argv[i];
19
+ if (arg === "--project-root") {
20
+ const value = argv[i + 1];
21
+ if (value === undefined) {
22
+ return { ...parsed, error: "argument --project-root: expected one argument" };
23
+ }
24
+ parsed.projectRoot = value;
25
+ i += 1;
26
+ }
27
+ else if (arg?.startsWith("--project-root=")) {
28
+ parsed.projectRoot = arg.slice("--project-root=".length);
29
+ }
30
+ else if (arg === "--check-conjunct") {
31
+ parsed.checkConjunct = true;
32
+ }
33
+ else if (arg === "--help" || arg === "-h") {
34
+ process.stdout.write("usage: verify:persisted-planning-narratives [--project-root PATH] [--check-conjunct]\n\n" +
35
+ "Fail closed when every tracked PROJECT-DEFINITION planning narrative\n" +
36
+ "(Overview + tech stack) is empty/whitespace (#5176 Prefer-A).\n" +
37
+ "Default: unconditional setup Phase 2 bar (missing PD → exit 2).\n" +
38
+ "--check-conjunct: Taskfile/check Prefer-A mirror (marker + empty →\n" +
39
+ "refuse; missing PD / no marker → pass). Reuse deft project:write-narratives.\n");
40
+ return parsed;
41
+ }
42
+ else {
43
+ return { ...parsed, error: `unrecognized argument: ${arg}` };
44
+ }
45
+ }
46
+ return parsed;
47
+ }
48
+ /** Run the Prefer-A first-ship planning-narrative bar. */
49
+ export function run(argv) {
50
+ const args = parseArgs(argv);
51
+ if (args.error !== undefined) {
52
+ process.stderr.write(`verify:persisted-planning-narratives: ${args.error}\n`);
53
+ return 2;
54
+ }
55
+ const projectRoot = resolve(args.projectRoot);
56
+ if (args.checkConjunct) {
57
+ const planning = evaluateCheckPersistedPlanningNarratives(projectRoot);
58
+ if (checkRejectsEmptyPlanningNarratives(planning.narratives, planning.productMutation)) {
59
+ process.stderr.write(`${planning.narratives.message}\n`);
60
+ return 1;
61
+ }
62
+ if (planning.narratives.ok) {
63
+ process.stdout.write(`${planning.narratives.message}\n`);
64
+ }
65
+ else {
66
+ process.stdout.write("verify:persisted-planning-narratives --check-conjunct: pass " +
67
+ `(${planning.narratives.cause}; Prefer-A check conjunct does not refuse)\n`);
68
+ }
69
+ return 0;
70
+ }
71
+ const result = evaluatePersistedPlanningNarratives(projectRoot);
72
+ if (result.ok) {
73
+ process.stdout.write(`${result.message}\n`);
74
+ return 0;
75
+ }
76
+ process.stderr.write(`${result.message}\n`);
77
+ return result.code;
78
+ }
79
+ if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
80
+ process.exit(run(process.argv.slice(2)));
81
+ }
82
+ //# sourceMappingURL=verify-persisted-planning-narratives.js.map
@@ -2,7 +2,7 @@
2
2
  import { resolve } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { bindLivePhaseCorrectWait, evaluateMergePathArm, } from "@deftai/directive-core/dist/pr-watch/main.js";
5
- import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, evaluateReviewMonitorGate, isTier1, REVIEW_MONITOR_HELP, verifyResultToJson, } from "@deftai/directive-core/review-monitor";
5
+ import { EXIT_CONFIG_ERROR, EXIT_NOT_READY, evaluateReviewMonitorGate, formatApproach1BabysitterOneLiner, isTier1, REVIEW_MONITOR_HELP, spawnRedirect, verifyResultToJson, } from "@deftai/directive-core/review-monitor";
6
6
  const CALL_SITES = new Set([
7
7
  "solo",
8
8
  "swarm-phase5-6",
@@ -134,15 +134,20 @@ export function run(argv) {
134
134
  const args = parseVerifyReviewMonitorArgs(argv);
135
135
  if (args.help) {
136
136
  process.stdout.write(REVIEW_MONITOR_HELP);
137
- process.stdout.write("\n#4882 / #5020 merge-path arm observer (optional):\n" +
137
+ process.stdout.write("\n#4882 / #5020 / #5219 merge-path arm observer (optional):\n" +
138
138
  " --merge-path-arm Fail closed when neither live wait nor explicit finish\n" +
139
139
  " --live-wait Require still-running wait evidence for this PR\n" +
140
140
  " (Tier 1: lease + active polling heartbeat with pid liveness;\n" +
141
- " lease+flag alone is not armed — #5020)\n" +
141
+ " lease+flag alone is not armed — #5020).\n" +
142
+ " On spawn_subagent hosts, lease platform_primitive must be\n" +
143
+ " spawn_subagent and heartbeat parent_id must match the lease\n" +
144
+ " monitor_agent_id, or pr-wait-mergeable only after local\n" +
145
+ " pr:watch CLEAN attestation — #5219;\n" +
146
+ " parent-shell pr:watch (parent_id=pr-watch) does not arm.\n" +
142
147
  " --explicit-finish Attest option-C BLOCKED/FAILED finish for this PR\n" +
143
148
  " --sticky-lease Attest a fresh sticky lease (not sufficient alone)\n" +
144
- " Prefer Approach 1 / native pr:watch or post-CLEAN pr:wait-mergeable-and-merge;\n" +
145
- " homemade line-parsed --json is not an arm.\n");
149
+ " Prefer Approach 1 / native pr:watch --monitor-agent-id <id> or post-CLEAN\n" +
150
+ " pr:wait-mergeable-and-merge; homemade line-parsed --json is not an arm.\n");
146
151
  return 0;
147
152
  }
148
153
  if (args.error !== undefined) {
@@ -170,6 +175,7 @@ export function run(argv) {
170
175
  let arm = null;
171
176
  if (args.mergePathArm) {
172
177
  // Bind --live-wait to lease (#5018) + process-liveness heartbeat (#5020) on Tier 1.
178
+ // spawn_subagent identity join is already applied in evaluateReviewMonitorGate (#5219).
173
179
  const leaseEvidence = result.monitorRecord !== null;
174
180
  const liveBind = bindLivePhaseCorrectWait({
175
181
  liveWaitFlag: args.liveWait,
@@ -183,15 +189,38 @@ export function run(argv) {
183
189
  explicitFinish: args.explicitFinish,
184
190
  stickyLeaseActive: args.stickyLease || leaseEvidence,
185
191
  });
186
- if (args.liveWait &&
187
- !liveBind.livePhaseCorrectWait &&
188
- !args.explicitFinish &&
189
- !arm.armed &&
190
- liveBind.message !== null) {
192
+ if (args.liveWait && !liveBind.livePhaseCorrectWait && !args.explicitFinish && !arm.armed) {
193
+ const spawnHost = result.tier.primitive === "spawn_subagent";
194
+ const lease = result.monitorRecord;
195
+ let message = liveBind.message;
196
+ if (spawnHost && lease !== null && lease.platform_primitive !== "spawn_subagent") {
197
+ message =
198
+ `unarmed stand-down: Tier-1 spawn_subagent requires lease ` +
199
+ `platform_primitive=spawn_subagent for PR #${args.pr} (#5219); ` +
200
+ `got ${lease.platform_primitive}.\n` +
201
+ ` ${spawnRedirect(result.tier)}`;
202
+ }
203
+ else if (spawnHost &&
204
+ lease !== null &&
205
+ lease.platform_primitive === "spawn_subagent" &&
206
+ liveBind.reason === "missing_process_liveness") {
207
+ message =
208
+ `unarmed stand-down: --live-wait for PR #${args.pr} has spawn_subagent lease ` +
209
+ `(monitor_agent_id=${lease.monitor_agent_id}) but no child-bound wait identity ` +
210
+ `(heartbeat parent_id must match monitor_agent_id, or pr-wait-mergeable after ` +
211
+ `pr:watch CLEAN attestation; start child pr:watch before verify; ` +
212
+ `parent-shell pr:watch does not arm) (#5219).\n` +
213
+ ` Cheaper path:\n` +
214
+ ` ${formatApproach1BabysitterOneLiner(args.pr, lease.monitor_agent_id)}\n` +
215
+ ` ${spawnRedirect(result.tier)}`;
216
+ }
217
+ else if (message === null) {
218
+ message = arm.message;
219
+ }
191
220
  arm = {
192
221
  armed: false,
193
222
  reason: "unarmed_stand_down",
194
- message: liveBind.message,
223
+ message,
195
224
  };
196
225
  }
197
226
  }
@@ -0,0 +1,2 @@
1
+ export declare function refuseIfWslOwnershipBlocked(projectRoot: string, writeErr?: (text: string) => void): number | null;
2
+ //# sourceMappingURL=wsl-ownership-guard.d.ts.map
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Shared live WSL ownership gate for protected mutating CLI entry points (#1617).
3
+ * Independent of ritual/doctor cache. ownership:doctor/fix are not gated here.
4
+ */
5
+ import { assertProtectedMutationOwnership } from "@deftai/directive-core/platform";
6
+ export function refuseIfWslOwnershipBlocked(projectRoot, writeErr = (text) => {
7
+ process.stderr.write(text);
8
+ }) {
9
+ const gate = assertProtectedMutationOwnership({ projectRoot });
10
+ if (gate.ok)
11
+ return null;
12
+ writeErr(`${gate.message}\n`);
13
+ return gate.exitCode;
14
+ }
15
+ //# sourceMappingURL=wsl-ownership-guard.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.119.11",
3
+ "version": "0.119.13",
4
4
  "description": "Directive CLI — npm install path for the Deft Directive framework.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://deftai.github.io/directive/",
@@ -35,8 +35,8 @@
35
35
  "provenance": true
36
36
  },
37
37
  "dependencies": {
38
- "@deftai/directive-core": "^0.119.11",
39
- "@deftai/directive-content": "^0.119.11"
38
+ "@deftai/directive-core": "^0.119.13",
39
+ "@deftai/directive-content": "^0.119.13"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsc -b",