@deftai/directive 0.121.0 → 0.123.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.
package/dist/authz.js CHANGED
@@ -10,6 +10,7 @@
10
10
  * deft authz:grant -- --operations edit,push --surfaces 'src/**' --cohort <id> ... [--confirm]
11
11
  * deft authz:grant -- --template release-publish --target 0.30.0 [--confirm]
12
12
  * deft authz:grant -- --template finish-loop [--confirm]
13
+ * deft authz:grant -- --template spec-drift-override --target <baseline> --plan-ref <scopeId> --story-ids <ids> [--confirm]
13
14
  * deft authz:grant -- --parent <parent.xbrief.json> --draft <draft.json> [--repo owner/name] [--confirm]
14
15
  * deft authz:revoke -- <grant-id> [--confirm]
15
16
  *
@@ -28,6 +29,7 @@ import { isAbsolute, resolve } from "node:path";
28
29
  import { onePrUnit } from "@deftai/directive-core";
29
30
  import { AFK_TEMPLATE_NAMES, AUTHZ_OPERATIONS, CLOSED_VERB_TEMPLATE_NAMES, FINISH_LOOP_TEMPLATE_NAME, formatDecomposeStructuralMintCommand, isAfkTemplateName, isClosedVerbTemplateName, isFinishLoopTemplateName, mintAfkTemplateGrant, mintDecomposeStructuralApplyGrant, mintHumanOriginGrant, revokeGrant, showAuthzSnapshot, startUatLease, suspendUatLease, toProjectRelativePosix, } from "@deftai/directive-core/authz";
30
31
  import { uatCampaignEndSeal } from "@deftai/directive-core/authz/campaign-end-seal";
32
+ import { isSpecDriftOverrideTemplateName, mintSpecDriftOverrideTemplateGrant, SPEC_DRIFT_OVERRIDE_TEMPLATE_NAME, } from "@deftai/directive-core/policy";
31
33
  import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
32
34
  export { AUTHZ_AGENT_SHELL_ENV_MARKERS, AUTHZ_INTERACTIVE_CONFIRM_PHRASE, } from "./human-presence-mint.js";
33
35
  function parseOps(raw) {
@@ -479,10 +481,51 @@ export function main(argv = process.argv.slice(2), seams = {}) {
479
481
  process.stdout.write(` origins=${claim.origins.map((o) => `${o.repo}#${o.issueId}`).join(",")}\n`);
480
482
  return 0;
481
483
  }
484
+ // Spec-drift override hatch (#5350): policy module, not authz/** (class 4).
485
+ if (args.template !== null && isSpecDriftOverrideTemplateName(args.template)) {
486
+ if (args.target === null || args.target.trim().length === 0) {
487
+ process.stderr.write("authz:grant --template spec-drift-override requires --target <baselineRevision>\n");
488
+ return 2;
489
+ }
490
+ if (args.planRef === null || args.planRef.trim().length === 0) {
491
+ process.stderr.write("authz:grant --template spec-drift-override requires --plan-ref <scopeId>\n");
492
+ return 2;
493
+ }
494
+ if (args.storyIds.length === 0) {
495
+ process.stderr.write("authz:grant --template spec-drift-override requires --story-ids <covered-item-ids>\n");
496
+ return 2;
497
+ }
498
+ const blockedOverride = gateConfirm();
499
+ if (blockedOverride !== null)
500
+ return blockedOverride;
501
+ const mintedOverride = mintSpecDriftOverrideTemplateGrant({
502
+ projectRoot: args.projectRoot,
503
+ target: args.target,
504
+ planRef: args.planRef,
505
+ storyIds: args.storyIds,
506
+ actor: args.actor,
507
+ expiresAt: args.expiresAt,
508
+ singleUse: args.singleUse,
509
+ repo: args.repo,
510
+ branch: args.branch,
511
+ });
512
+ if (!mintedOverride.ok) {
513
+ process.stderr.write(`authz:grant: ${mintedOverride.reason}\n`);
514
+ return 2;
515
+ }
516
+ const overrideGrant = mintedOverride.grant;
517
+ process.stdout.write(`✓ human-origin grant minted id=${overrideGrant.id} origin=${overrideGrant.origin.kind} ` +
518
+ `template=${SPEC_DRIFT_OVERRIDE_TEMPLATE_NAME}\n`);
519
+ process.stdout.write(` ops=[${overrideGrant.scope.operations.join(",")}] scope=${overrideGrant.scope.planRef} ` +
520
+ `items=${overrideGrant.scope.storyIds.join(",")} surfaces=${overrideGrant.scope.surfaces.join(", ")}\n`);
521
+ process.stdout.write(" Hatch: discharges rewrite/coverage refuse for bound ids only under enforce; single-use preferred.\n");
522
+ process.stdout.write(" Authorization SoT: Wave 1 grant store (.deft/authz/grants) — not session-auth.\n");
523
+ return 0;
524
+ }
482
525
  // AFK template path (#1095 / #871): presets only — still mintHumanOriginGrant.
483
526
  if (args.template !== null && args.template.trim().length > 0) {
484
527
  if (!isAfkTemplateName(args.template)) {
485
- process.stderr.write(`authz:grant unknown --template '${args.template}'; expected one of: ${AFK_TEMPLATE_NAMES.join(", ")}\n`);
528
+ process.stderr.write(`authz:grant unknown --template '${args.template}'; expected one of: ${AFK_TEMPLATE_NAMES.join(", ")}, ${SPEC_DRIFT_OVERRIDE_TEMPLATE_NAME}\n`);
486
529
  return 2;
487
530
  }
488
531
  if (isClosedVerbTemplateName(args.template) &&
@@ -526,7 +569,7 @@ export function main(argv = process.argv.slice(2), seams = {}) {
526
569
  return 0;
527
570
  }
528
571
  if (args.operations.length === 0) {
529
- process.stderr.write("authz:grant requires --operations <edit,push,...>, --template <finish-loop|release-*>, " +
572
+ process.stderr.write("authz:grant requires --operations <edit,push,...>, --template <finish-loop|release-*|spec-drift-override>, " +
530
573
  "or --parent + --draft (structural scope:decompose apply)\n");
531
574
  return 2;
532
575
  }
@@ -89,6 +89,7 @@ export const SUBCOMMAND_ROUTES = {
89
89
  "policy:enable-value-feedback": ["policy", "enable-value-feedback"],
90
90
  "policy:disable-host-hooks": ["policy", "disable-host-hooks"],
91
91
  "policy:set-ceremony-dial": ["policy", "set-ceremony-dial"],
92
+ "policy:set-spec-guard-enforcement": ["policy", "set-spec-guard-enforcement"],
92
93
  "policy:clear-value-feedback": ["policy", "clear-value-feedback"],
93
94
  "authz:show": ["authz", "show"],
94
95
  "authz:uat-start": ["authz", "uat-start"],
@@ -127,6 +127,16 @@ export function run(argv) {
127
127
  pathFilter: args.pathFilter.length > 0 ? args.pathFilter : null,
128
128
  useDiffPaths: args.useDiffPaths,
129
129
  });
130
+ // Exit 3 = authorized coverage N/A (#5421); keep machine-readable artifact on stdout for --json.
131
+ if (result.exitCode === 3) {
132
+ if (args.json && result.applicability) {
133
+ process.stdout.write(`${JSON.stringify(result.applicability, null, 2)}\n`);
134
+ }
135
+ else if (!args.quiet) {
136
+ process.stderr.write(`${result.message}\n`);
137
+ }
138
+ return result.exitCode;
139
+ }
130
140
  if (result.exitCode === 2 || result.report === null) {
131
141
  process.stderr.write(`${result.message}\n`);
132
142
  return result.exitCode;
@@ -12,7 +12,7 @@ export interface DispatchIo {
12
12
  /** CLI modules in packages/cli/src (excluding parity harnesses and bin/index). */
13
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", "pre-pr-run", "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", "verify-spec-drift", "rule-ownership-lint", "verify-story-ready", "verify-review-monitor", "verify-cohort-review-monitors", "verify-l4-owner", "verify-subagent-alive", "verify-subagent-steer", "subagent-pre-cancel", "subagent-steer-write", "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-reconstruct", "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"];
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-reconstruct", "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", "migrate-confidence"];
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
@@ -243,6 +243,7 @@ export const CORE_MODULE_VERBS = [
243
243
  "decision-list",
244
244
  "docs-impact",
245
245
  "migrate-clause-ids",
246
+ "migrate-confidence",
246
247
  ];
247
248
  /** Colon aliases for triage-actions (mirrors cli-router SUBCOMMAND_ROUTES). */
248
249
  export const TRIAGE_ACTION_ALIAS_SUBCOMMANDS = {
@@ -265,6 +266,7 @@ export const POLICY_ACTION_ALIAS_SUBCOMMANDS = {
265
266
  "policy:enforce-destructive-gh-verbs": "enforce-destructive-gh-verbs",
266
267
  "policy:allow-bot-merge": "allow-bot-merge",
267
268
  "policy:enable-value-feedback": "enable-value-feedback",
269
+ "policy:set-spec-guard-enforcement": "set-spec-guard-enforcement",
268
270
  "policy:disable-host-hooks": "disable-host-hooks",
269
271
  "policy:set-ceremony-dial": "set-ceremony-dial",
270
272
  "policy:clear-value-feedback": "clear-value-feedback",
@@ -430,6 +432,7 @@ export const VERB_ALIASES = {
430
432
  "migrate:xbrief": "migrate-xbrief",
431
433
  "migrate:category-b": "migrate-category-b",
432
434
  "migrate:clause-ids": "migrate-clause-ids",
435
+ "migrate:confidence": "migrate-confidence",
433
436
  "framework:check-updates": "framework-check-updates",
434
437
  "umbrella:current-shape": "umbrella-current-shape",
435
438
  "issue:sync-from-xbrief": "issue-sync-from-xbrief",
@@ -2655,6 +2658,10 @@ async function loadCoreModuleHandler(verb, io) {
2655
2658
  const { mainEntry } = await import("@deftai/directive-core/dist/xbrief-migrate/clause-ids.js");
2656
2659
  return (argv) => mainEntry(argv);
2657
2660
  }
2661
+ case "migrate-confidence": {
2662
+ const { mainEntry } = await import("@deftai/directive-core/dist/xbrief-migrate/confidence.js");
2663
+ return (argv) => mainEntry(argv);
2664
+ }
2658
2665
  default:
2659
2666
  throw new Error(`unknown core verb: ${verb}`);
2660
2667
  }
package/dist/policy.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { type CeremonyDepth, type HookHost } from "@deftai/directive-core/policy";
2
+ import { type CeremonyDepth, type HookHost, type SpecGuardEnforcement } from "@deftai/directive-core/policy";
3
3
  interface ShowArgs {
4
4
  format: "text" | "json";
5
5
  changedOnly: boolean;
@@ -8,7 +8,7 @@ interface ShowArgs {
8
8
  error?: string;
9
9
  }
10
10
  interface SetArgs {
11
- cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-destructive-gh-verbs" | "enforce-destructive-gh-verbs" | "allow-bot-merge" | "enable-value-feedback" | "clear-value-feedback" | "set-ceremony-dial" | "disable-host-hooks" | "disable-directive" | "enable-directive" | "resolve";
11
+ cmd: "show" | "enforce-branches" | "allow-direct-commits" | "allow-destructive-gh-verbs" | "enforce-destructive-gh-verbs" | "allow-bot-merge" | "enable-value-feedback" | "clear-value-feedback" | "set-ceremony-dial" | "set-spec-guard-enforcement" | "disable-host-hooks" | "disable-directive" | "enable-directive" | "resolve";
12
12
  confirm: boolean;
13
13
  actor: string;
14
14
  note: string;
@@ -21,6 +21,8 @@ interface SetArgs {
21
21
  ceremonyOverride?: CeremonyDepth | null;
22
22
  /** #3214 ceremony dial enabled flag for set-ceremony-dial. */
23
23
  ceremonyEnabled?: boolean;
24
+ /** #5350 driftGuard.enforcement target for set-spec-guard-enforcement. */
25
+ specGuardEnforcement?: SpecGuardEnforcement;
24
26
  error?: string;
25
27
  }
26
28
  /** Parse policy:show flags (mirrors _policy_show_cli.py). */
package/dist/policy.js CHANGED
@@ -6,7 +6,7 @@
6
6
  import { existsSync } from "node:fs";
7
7
  import { resolve as pathResolve, relative } from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
- import { ALLOW_BOT_MERGE_CAPABILITY_COST, ALLOW_DESTRUCTIVE_GH_VERBS_CAPABILITY_COST, CEREMONY_DEPTHS, clearValueFeedback, createNoDeftDirectiveFlag, describeShadowedPlanExtension, detectNoDeftDirective, detectShadowedPlanExtensions, disableHostHooks, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, humanMergeDisclosureLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, POLICY_AUDIT_NOOP_STDOUT, parseHookHost, policyColonInvocation, projectDefinitionPath, pythonListRepr, pythonStringRepr, registeredPolicyNames, removeNoDeftDirectiveFlag, renderJson, renderText, resolveHumanMergePolicy, resolvePolicy, resolveValueFeedback, setAllowDestructiveGhVerbs, setCeremonyDial, setPolicy, setRequireHumanMerge, } from "@deftai/directive-core/policy";
9
+ import { ALLOW_BOT_MERGE_CAPABILITY_COST, ALLOW_DESTRUCTIVE_GH_VERBS_CAPABILITY_COST, CEREMONY_DEPTHS, clearValueFeedback, createNoDeftDirectiveFlag, describeShadowedPlanExtension, detectNoDeftDirective, detectShadowedPlanExtensions, disableHostHooks, disclosureLine, enableValueFeedback, FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, formatValueFeedbackStatusLine, humanMergeDisclosureLine, inspectAllPolicies, inspectOnePolicy, loadProjectDefinition, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, POLICY_AUDIT_NOOP_STDOUT, parseHookHost, policyColonInvocation, projectDefinitionPath, promoteSpecGuardDriftEnforcement, pythonListRepr, pythonStringRepr, registeredPolicyNames, removeNoDeftDirectiveFlag, renderJson, renderText, resolveHumanMergePolicy, resolvePolicy, resolveValueFeedback, SPEC_GUARD_ENFORCEMENTS, setAllowDestructiveGhVerbs, setCeremonyDial, setPolicy, setRequireHumanMerge, } from "@deftai/directive-core/policy";
10
10
  const CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost disclosure -- enabling direct commits to the default " +
11
11
  "branch turns OFF the deft branch-protection policy.\n" +
12
12
  " \u2022 Pre-commit + pre-push hooks will no longer block default-branch " +
@@ -114,10 +114,87 @@ export function parseShowArgs(argv) {
114
114
  /** Parse argv for the policy CLI (show + set subcommands). */
115
115
  export function parseArgs(argv) {
116
116
  if (argv.length === 0) {
117
- const usage = "usage: policy [show|enforce-branches|allow-direct-commits|allow-destructive-gh-verbs|enforce-destructive-gh-verbs|allow-bot-merge|enable-value-feedback|clear-value-feedback|set-ceremony-dial|disable-host-hooks|disable-directive|enable-directive|resolve] ...";
117
+ const usage = "usage: policy [show|enforce-branches|allow-direct-commits|allow-destructive-gh-verbs|enforce-destructive-gh-verbs|allow-bot-merge|enable-value-feedback|clear-value-feedback|set-ceremony-dial|set-spec-guard-enforcement|disable-host-hooks|disable-directive|enable-directive|resolve] ...";
118
118
  return makeSetError(usage);
119
119
  }
120
120
  const cmd = argv[0];
121
+ if (cmd === "set-spec-guard-enforcement") {
122
+ let confirm = false;
123
+ let actor = policyColonInvocation("set-spec-guard-enforcement");
124
+ let note = "";
125
+ let projectRoot = ".";
126
+ let specGuardEnforcement;
127
+ for (let i = 1; i < argv.length; i += 1) {
128
+ const arg = argv[i];
129
+ if (arg === "--confirm") {
130
+ confirm = true;
131
+ }
132
+ else if (arg === "--actor") {
133
+ const v = argv[i + 1];
134
+ if (v === undefined)
135
+ return makeSetError("argument --actor: expected one argument");
136
+ actor = v;
137
+ i += 1;
138
+ }
139
+ else if (arg?.startsWith("--actor=")) {
140
+ actor = arg.slice("--actor=".length);
141
+ }
142
+ else if (arg === "--note") {
143
+ const v = argv[i + 1];
144
+ if (v === undefined)
145
+ return makeSetError("argument --note: expected one argument");
146
+ note = v;
147
+ i += 1;
148
+ }
149
+ else if (arg?.startsWith("--note=")) {
150
+ note = arg.slice("--note=".length);
151
+ }
152
+ else if (arg === "--set") {
153
+ const v = argv[i + 1];
154
+ if (v === undefined) {
155
+ return makeSetError("argument --set: expected advise|shadow|enforce");
156
+ }
157
+ if (!SPEC_GUARD_ENFORCEMENTS.has(v)) {
158
+ return makeSetError(`argument --set: expected advise|shadow|enforce, got ${JSON.stringify(v)}`);
159
+ }
160
+ specGuardEnforcement = v;
161
+ i += 1;
162
+ }
163
+ else if (arg?.startsWith("--set=")) {
164
+ const v = arg.slice("--set=".length);
165
+ if (!SPEC_GUARD_ENFORCEMENTS.has(v)) {
166
+ return makeSetError(`argument --set: expected advise|shadow|enforce, got ${JSON.stringify(v)}`);
167
+ }
168
+ specGuardEnforcement = v;
169
+ }
170
+ else if (arg === "--project-root") {
171
+ const v = argv[i + 1];
172
+ if (v === undefined)
173
+ return makeSetError("argument --project-root: expected one argument");
174
+ projectRoot = v;
175
+ i += 1;
176
+ }
177
+ else if (arg?.startsWith("--project-root=")) {
178
+ projectRoot = arg.slice("--project-root=".length);
179
+ }
180
+ else if (arg === "--") {
181
+ }
182
+ else {
183
+ return makeSetError(`unrecognized argument: ${arg}`);
184
+ }
185
+ }
186
+ return {
187
+ cmd: "set-spec-guard-enforcement",
188
+ confirm,
189
+ actor,
190
+ note,
191
+ projectRoot,
192
+ format: "text",
193
+ changedOnly: false,
194
+ field: null,
195
+ specGuardEnforcement,
196
+ };
197
+ }
121
198
  if (cmd === "show") {
122
199
  const show = parseShowArgs(argv.slice(1));
123
200
  return {
@@ -608,6 +685,9 @@ export function run(argv) {
608
685
  if (args.cmd === "set-ceremony-dial") {
609
686
  return runSetCeremonyDial(args);
610
687
  }
688
+ if (args.cmd === "set-spec-guard-enforcement") {
689
+ return runSetSpecGuardEnforcement(args);
690
+ }
611
691
  if (args.cmd === "disable-directive") {
612
692
  return runDisableDirective(args);
613
693
  }
@@ -616,6 +696,24 @@ export function run(argv) {
616
696
  }
617
697
  return 2;
618
698
  }
699
+ /** Persist plan.policy.specGuard.driftGuard.enforcement (#5350). */
700
+ function runSetSpecGuardEnforcement(args) {
701
+ const root = pathResolve(args.projectRoot);
702
+ if (args.specGuardEnforcement === undefined) {
703
+ process.stdout.write("usage: policy set-spec-guard-enforcement -- --set advise|shadow|enforce [--confirm] [--project-root PATH]\n" +
704
+ " Promote ladder: advise → shadow → enforce (advise→enforce skip refused without shadow attestation).\n" +
705
+ ` Inspect: ${policyColonInvocation("show", " --field=specGuard")}\n`);
706
+ return 1;
707
+ }
708
+ const result = promoteSpecGuardDriftEnforcement(root, {
709
+ to: args.specGuardEnforcement,
710
+ confirm: args.confirm,
711
+ actor: args.actor,
712
+ note: args.note,
713
+ });
714
+ process.stdout.write(result.stdout);
715
+ return result.exitCode;
716
+ }
619
717
  /** Persist plan.policy.ceremonyDial override/enabled (#3214). */
620
718
  function runSetCeremonyDial(args) {
621
719
  const root = pathResolve(args.projectRoot);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.121.0",
3
+ "version": "0.123.0",
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.121.0",
39
- "@deftai/directive-content": "^0.121.0"
38
+ "@deftai/directive-core": "^0.123.0",
39
+ "@deftai/directive-content": "^0.123.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsc -b",