@deftai/directive 0.119.0 → 0.119.2

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
@@ -13,10 +13,11 @@
13
13
  * deft authz:grant -- --parent <parent.xbrief.json> --draft <draft.json> [--repo owner/name] [--confirm]
14
14
  * deft authz:revoke -- <grant-id> [--confirm]
15
15
  *
16
- * **UAT-active hard refuse (#3110):** while any UAT lease is active, ALL mutating
17
- * verbs (`uat-start`, `uat-suspend`, `grant`, `revoke`) refuse unconditionally —
18
- * no TTY, no `--confirm`, no typed phrase path. Self-approval under UAT is
19
- * impossible by construction (agent PTY cannot mint operator-cli authority).
16
+ * **UAT-active mint refuse (#3110 / #4632):** while any UAT lease is active,
17
+ * `uat-start`, `grant`, and `revoke` refuse unconditionally — no TTY, `--confirm`,
18
+ * or typed phrase path. `uat-suspend` skips that mint-refuse and still requires
19
+ * multi-factor human presence (real operator console). Suspend reuses the typed
20
+ * phrase `mint`; it is not a remint.
20
21
  *
21
22
  * **Outside UAT:** mutating verbs require multi-factor human presence: interactive
22
23
  * TTY + controlling terminal + `--confirm` + typed phrase `mint`; agent/CI env
@@ -222,16 +223,19 @@ function helpText() {
222
223
  "",
223
224
  "Human-origin grants are minted only via this CLI (origin.kind=operator-cli).",
224
225
  "Self-authored xBRIEF/lifecycle/dispatch tokens never satisfy implement gates (#2944).",
225
- "While any UAT lease is ACTIVE (#3110): ALL mutating verbs refuse unconditionally",
226
- " (grant / uat-start / uat-suspend / revoke) — no TTY, --confirm, or phrase path.",
227
- " Self-approval under UAT is impossible by construction. Mint grants BEFORE uat-start.",
226
+ "While any UAT lease is ACTIVE (#3110 / #4632): grant / uat-start / revoke refuse",
227
+ " unconditionally — no TTY, --confirm, or phrase path. Self-approval remint under",
228
+ " UAT is impossible by construction. Mint grants BEFORE uat-start.",
229
+ " uat-suspend is reachable from a real operator console: listed agent/CI markers",
230
+ " empty, stdin+stdout TTY, controlling terminal, --confirm, typed phrase 'mint'",
231
+ " (suspend reuses the mint phrase; it is not a remint). Agent/CI and missing-factor",
232
+ " seams still refuse (exit 2). After suspend, grant is available again through",
233
+ " the same human-presence gate.",
228
234
  "Outside UAT, mutating verbs require multi-factor human presence (#3110):",
229
235
  " - Interactive TTY (stdin+stdout) + controlling terminal (/dev/tty|\\\\.\\CONIN$)",
230
236
  " - Explicit --confirm (argv flag alone never enough)",
231
237
  " - Typed phrase 'mint' on the controlling TTY (PTY+--confirm alone never enough)",
232
238
  " - Known agent/CI env markers always refuse (fail-closed).",
233
- " End UAT only after the lease is cleared out-of-band (state edit / suspend path",
234
- " that does not run while the lease is active) — or suspend before re-minting.",
235
239
  "",
236
240
  "Structural scope:decompose apply (#3239 / #3291):",
237
241
  " Pass --parent + --draft (paths under project root). Digest is SHA-256 of exact",
@@ -263,17 +267,21 @@ export function main(argv = process.argv.slice(2), seams = {}) {
263
267
  }
264
268
  const resolved = resolveHumanPresenceMintSeams(seams);
265
269
  /**
266
- * Mutating-verb gate stack (#3110):
267
- * 1. Active UAT → hard refuse (no multi-factor escape; self-approval impossible)
268
- * 2. Else multi-factor: TTY + controlling tty + --confirm + typed phrase; agent/CI markers refuse
270
+ * Mutating-verb gate stack (#3110 / #4632):
271
+ * 1. Active UAT mint-refuse for grant / uat-start / revoke (no multi-factor escape)
272
+ * 2. uat-suspend skips that mint-refuse so campaign-end stays reachable
273
+ * 3. Multi-factor human presence: TTY + controlling tty + --confirm + typed phrase;
274
+ * agent/CI markers refuse. Suspend reuses phrase `mint`.
269
275
  * Required-arg validation runs before this so missing --campaign / --ops still report clearly.
270
276
  */
271
277
  const gateConfirm = () => {
272
278
  if (args.cmd === "show")
273
279
  return null;
274
- const uatBlocked = refuseMintWhileUatActive(`authz:${args.cmd}`, args.projectRoot);
275
- if (uatBlocked !== null)
276
- return uatBlocked;
280
+ if (args.cmd !== "uat-suspend") {
281
+ const uatBlocked = refuseMintWhileUatActive(`authz:${args.cmd}`, args.projectRoot);
282
+ if (uatBlocked !== null)
283
+ return uatBlocked;
284
+ }
277
285
  return refuseNonInteractiveMint({
278
286
  verb: `authz:${args.cmd}`,
279
287
  confirm: args.confirm,
@@ -434,12 +442,18 @@ export function main(argv = process.argv.slice(2), seams = {}) {
434
442
  "The in-process simulator is test-only and is discarded on process exit.\n");
435
443
  return 2;
436
444
  }
445
+ const resolved = onePrUnit.resolveProductionAppStore(process.env);
446
+ if (!resolved.ok) {
447
+ process.stderr.write(`${resolved.message}\n`);
448
+ return 2;
449
+ }
437
450
  const claim = onePrUnit.mintOnePrUnitGrant({
438
451
  actor: args.actor,
439
452
  approvalRef: args.note ?? "authz:grant --template one-pr-unit",
440
453
  rationale: args.note ?? "one-PR-unit",
441
454
  origins: args.issueIds.map((issueId) => ({ repo: args.repo, issueId })),
442
455
  repo: args.repo,
456
+ store: resolved.store,
443
457
  });
444
458
  process.stdout.write(`✓ one-PR-unit claim minted id=${claim.id} state=${claim.state} (reserved unbound)\n`);
445
459
  process.stdout.write(" Opaque id is not a bearer. Only the App store plus the bound PR node id authorizes.\n");
@@ -5,13 +5,19 @@ export interface DeftTsResult {
5
5
  }
6
6
  /** Repo root (deft framework checkout). */
7
7
  export declare function repoRoot(): string;
8
- /** Built `deft-ts` dispatcher binary. */
8
+ /** Built deft-ts dispatcher binary. Spawn leftovers use this path. */
9
9
  export declare function binPath(): string;
10
- /** Invoke `node packages/cli/dist/bin.js <verb> [...args]`. Pass an empty verb for `--help`. */
11
- export declare function runDeftTs(verb: string, args?: readonly string[], opts?: {
12
- cwd?: string;
13
- env?: NodeJS.ProcessEnv;
14
- }): DeftTsResult;
10
+ export interface RunDeftTsOptions {
11
+ readonly cwd?: string;
12
+ readonly env?: NodeJS.ProcessEnv;
13
+ }
14
+ /**
15
+ * Invoke the CLI router in-process (#4591). Coverage excludes packages/cli/src/bin.ts,
16
+ * so a node+bin.js spawn does not credit the child and is the Windows execute-scan
17
+ * chokepoint documented in vitest.config.ts.
18
+ */
19
+ export declare function runDeftTs(verb: string, args?: readonly string[], opts?: RunDeftTsOptions): Promise<DeftTsResult>;
20
+ export declare function runDeftTsArgv(argv: readonly string[], opts?: RunDeftTsOptions): Promise<DeftTsResult>;
15
21
  export declare function initGitRepo(root: string): string;
16
22
  export declare function writeProjectDef(root: string, policy?: Record<string, unknown>): void;
17
23
  export declare function seedProject(policy?: Record<string, unknown>): string;
@@ -1,37 +1,81 @@
1
- import { execFileSync, spawnSync } from "node:child_process";
1
+ import { execFileSync } from "node:child_process";
2
2
  import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { dirname, join, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
+ import { routeAndDispatch } from "../cli-router/index.js";
7
+ import { resetHandlerCacheForTests } from "../dispatch.js";
6
8
  const gatesDir = dirname(fileURLToPath(import.meta.url));
7
9
  /** Repo root (deft framework checkout). */
8
10
  export function repoRoot() {
9
11
  return resolve(gatesDir, "..", "..", "..", "..");
10
12
  }
11
- /** Built `deft-ts` dispatcher binary. */
13
+ /** Built deft-ts dispatcher binary. Spawn leftovers use this path. */
12
14
  export function binPath() {
13
15
  return join(repoRoot(), "packages/cli/dist/bin.js");
14
16
  }
15
- /** Invoke `node packages/cli/dist/bin.js <verb> [...args]`. Pass an empty verb for `--help`. */
16
- export function runDeftTs(verb, args = [], opts = {}) {
17
- const root = repoRoot();
18
- const argv = verb.length > 0 ? [binPath(), verb, ...args] : [binPath(), ...args];
19
- const res = spawnSync(process.execPath, argv, {
20
- cwd: opts.cwd ?? root,
21
- env: {
22
- ...process.env,
23
- DEFT_ROOT: root,
17
+ /**
18
+ * Invoke the CLI router in-process (#4591). Coverage excludes packages/cli/src/bin.ts,
19
+ * so a node+bin.js spawn does not credit the child and is the Windows execute-scan
20
+ * chokepoint documented in vitest.config.ts.
21
+ */
22
+ export async function runDeftTs(verb, args = [], opts = {}) {
23
+ const argv = verb.length > 0 ? [verb, ...args] : [...args];
24
+ return runDeftTsArgv(argv, opts);
25
+ }
26
+ export async function runDeftTsArgv(argv, opts = {}) {
27
+ const out = [];
28
+ const err = [];
29
+ const prevOut = process.stdout.write.bind(process.stdout);
30
+ const prevErr = process.stderr.write.bind(process.stderr);
31
+ const prevCwd = process.cwd();
32
+ resetHandlerCacheForTests();
33
+ const envSnapshot = { ...process.env };
34
+ process.stdout.write = ((chunk) => {
35
+ out.push(String(chunk));
36
+ return true;
37
+ });
38
+ process.stderr.write = ((chunk) => {
39
+ err.push(String(chunk));
40
+ return true;
41
+ });
42
+ try {
43
+ Object.assign(process.env, {
44
+ DEFT_ROOT: repoRoot(),
24
45
  DEFT_CACHE_DISABLE: "1",
25
46
  PYTHONUTF8: "1",
26
47
  ...opts.env,
27
- },
28
- encoding: "utf8",
29
- });
30
- return {
31
- exitCode: res.status ?? 1,
32
- stdout: res.stdout ?? "",
33
- stderr: res.stderr ?? "",
34
- };
48
+ });
49
+ if (opts.cwd !== undefined) {
50
+ process.chdir(opts.cwd);
51
+ }
52
+ const exitCode = await routeAndDispatch(argv, {
53
+ writeOut: (text) => {
54
+ out.push(text);
55
+ },
56
+ writeErr: (text) => {
57
+ err.push(text);
58
+ },
59
+ });
60
+ return {
61
+ exitCode,
62
+ stdout: out.join(""),
63
+ stderr: err.join(""),
64
+ };
65
+ }
66
+ finally {
67
+ process.stdout.write = prevOut;
68
+ process.stderr.write = prevErr;
69
+ if (opts.cwd !== undefined) {
70
+ process.chdir(prevCwd);
71
+ }
72
+ for (const key of Object.keys(process.env)) {
73
+ if (!(key in envSnapshot)) {
74
+ delete process.env[key];
75
+ }
76
+ }
77
+ Object.assign(process.env, envSnapshot);
78
+ }
35
79
  }
36
80
  export function initGitRepo(root) {
37
81
  writeFileSync(join(root, "README.md"), "fixture\n", "utf8");
@@ -1,20 +1,17 @@
1
+ import { type DeftTsResult, type RunDeftTsOptions } from "../gates-cli/_helpers.js";
2
+ export type { DeftTsResult, RunDeftTsOptions };
1
3
  /** Repo root (four levels up from packages/cli/src/render-cli). */
2
4
  export declare function resolveRepoRoot(): string;
3
- /** Built deft-ts dispatcher entrypoint. */
5
+ /** Built deft-ts dispatcher entrypoint. Spawn leftovers still resolve this path. */
4
6
  export declare function resolveBinPath(): string;
5
- export interface DeftTsResult {
6
- readonly exitCode: number;
7
- readonly stdout: string;
8
- readonly stderr: string;
9
- }
10
- /** Invoke `node packages/cli/dist/bin.js [...argv]` and capture output. */
7
+ /** Invoke `directive [...argv]` in-process (#4591). */
11
8
  export declare function runDeftTsArgv(argv: readonly string[], options?: {
12
9
  cwd?: string;
13
10
  env?: Record<string, string>;
14
- }): DeftTsResult;
15
- /** Invoke `node packages/cli/dist/bin.js <verb> [...args]` and capture output. */
11
+ }): Promise<DeftTsResult>;
12
+ /** Invoke `directive <verb> [...args]` in-process (#4591). */
16
13
  export declare function runDeftTs(verb: string, args?: readonly string[], options?: {
17
14
  cwd?: string;
18
15
  env?: Record<string, string>;
19
- }): DeftTsResult;
16
+ }): Promise<DeftTsResult>;
20
17
  //# sourceMappingURL=deft-ts-runner.d.ts.map
@@ -1,35 +1,19 @@
1
- import { spawnSync } from "node:child_process";
2
- import { dirname, resolve } from "node:path";
3
- import { fileURLToPath } from "node:url";
1
+ import { binPath, repoRoot, runDeftTsArgv as runDeftTsArgvInProcess, runDeftTs as runDeftTsInProcess, } from "../gates-cli/_helpers.js";
4
2
  /** Repo root (four levels up from packages/cli/src/render-cli). */
5
3
  export function resolveRepoRoot() {
6
- return resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "..");
4
+ return repoRoot();
7
5
  }
8
- /** Built deft-ts dispatcher entrypoint. */
6
+ /** Built deft-ts dispatcher entrypoint. Spawn leftovers still resolve this path. */
9
7
  export function resolveBinPath() {
10
- return resolve(resolveRepoRoot(), "packages/cli/dist/bin.js");
8
+ return binPath();
11
9
  }
12
- /** Invoke `node packages/cli/dist/bin.js [...argv]` and capture output. */
13
- export function runDeftTsArgv(argv, options = {}) {
14
- const bin = resolveBinPath();
15
- const result = spawnSync(process.execPath, [bin, ...argv], {
16
- cwd: options.cwd ?? resolveRepoRoot(),
17
- encoding: "utf8",
18
- env: {
19
- ...process.env,
20
- DEFT_CACHE_DISABLE: "1",
21
- PYTHONUTF8: "1",
22
- ...options.env,
23
- },
24
- });
25
- return {
26
- exitCode: result.status ?? 2,
27
- stdout: result.stdout ?? "",
28
- stderr: result.stderr ?? "",
29
- };
10
+ /** Invoke `directive [...argv]` in-process (#4591). */
11
+ export async function runDeftTsArgv(argv, options = {}) {
12
+ const opts = { cwd: options.cwd, env: options.env };
13
+ return runDeftTsArgvInProcess(argv, opts);
30
14
  }
31
- /** Invoke `node packages/cli/dist/bin.js <verb> [...args]` and capture output. */
32
- export function runDeftTs(verb, args = [], options = {}) {
33
- return runDeftTsArgv([verb, ...args], options);
15
+ /** Invoke `directive <verb> [...args]` in-process (#4591). */
16
+ export async function runDeftTs(verb, args = [], options = {}) {
17
+ return runDeftTsInProcess(verb, args, { cwd: options.cwd, env: options.env });
34
18
  }
35
19
  //# sourceMappingURL=deft-ts-runner.js.map
@@ -170,7 +170,7 @@ export function run(argv, seams = {}) {
170
170
  }
171
171
  const path = writeObservableScopeRecord(projectRoot, rec);
172
172
  if (!args.quiet) {
173
- process.stdout.write(`Wrote ${path}\nCommit this record on the merge base before the UI change PR.\n`);
173
+ process.stdout.write(`Wrote ${path}\nCommit this record on the merge base before the UI change PR. Demand this mint after the observableChange contract is authored, not at parking (#4588; predecessor #4383).\n`);
174
174
  }
175
175
  return 0;
176
176
  }
@@ -81,7 +81,14 @@ export function parseArgs(argv) {
81
81
  parsed.posture = posture;
82
82
  }
83
83
  else {
84
- return { ...parsed, error: `unrecognized argument: ${arg}` };
84
+ const error = `unrecognized argument: ${arg}`;
85
+ if (arg === "--session-id" || arg?.startsWith("--session-id=")) {
86
+ return {
87
+ ...parsed,
88
+ error: `${error} Drop the flag and retry deft verify:session-ritual -- --tier=gated.`,
89
+ };
90
+ }
91
+ return { ...parsed, error };
85
92
  }
86
93
  }
87
94
  return parsed;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive",
3
- "version": "0.119.0",
3
+ "version": "0.119.2",
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/",
@@ -34,8 +34,8 @@
34
34
  "provenance": true
35
35
  },
36
36
  "dependencies": {
37
- "@deftai/directive-core": "^0.119.0",
38
- "@deftai/directive-content": "^0.119.0"
37
+ "@deftai/directive-core": "^0.119.2",
38
+ "@deftai/directive-content": "^0.119.2"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "tsc -b"