@deftai/directive 0.119.13 → 0.120.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 +41 -7
- package/dist/changelog-check.js +14 -6
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +2 -0
- package/dist/pre-pr-run.d.ts +39 -0
- package/dist/pre-pr-run.js +514 -0
- package/dist/verify-completed-tracked.d.ts +4 -1
- package/dist/verify-completed-tracked.js +60 -9
- package/package.json +3 -3
package/dist/authz.js
CHANGED
|
@@ -27,6 +27,7 @@ import { existsSync } from "node:fs";
|
|
|
27
27
|
import { isAbsolute, resolve } from "node:path";
|
|
28
28
|
import { onePrUnit } from "@deftai/directive-core";
|
|
29
29
|
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
|
+
import { uatCampaignEndSeal } from "@deftai/directive-core/authz/campaign-end-seal";
|
|
30
31
|
import { refuseMintWhileUatActive, refuseNonInteractiveMint, resolveHumanPresenceMintSeams, } from "./human-presence-mint.js";
|
|
31
32
|
export { AUTHZ_AGENT_SHELL_ENV_MARKERS, AUTHZ_INTERACTIVE_CONFIRM_PHRASE, } from "./human-presence-mint.js";
|
|
32
33
|
function parseOps(raw) {
|
|
@@ -328,13 +329,18 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
328
329
|
const blocked = gateConfirm();
|
|
329
330
|
if (blocked !== null)
|
|
330
331
|
return blocked;
|
|
331
|
-
|
|
332
|
+
// Result narrowing; pinActive mint wiring stays parked (class 4).
|
|
333
|
+
const started = startUatLease({
|
|
332
334
|
projectRoot: args.projectRoot,
|
|
333
335
|
campaignId: args.campaign,
|
|
334
336
|
actor: args.actor,
|
|
335
337
|
note: args.note,
|
|
336
338
|
});
|
|
337
|
-
|
|
339
|
+
if (!started.ok) {
|
|
340
|
+
process.stderr.write(`authz:uat-start: ${started.reason}\n`);
|
|
341
|
+
return 2;
|
|
342
|
+
}
|
|
343
|
+
process.stdout.write(`✓ UAT lease ACTIVE campaign=${started.lease.campaignId} (human-origin operator-cli)\n`);
|
|
338
344
|
process.stdout.write(" Product edit/push/PR/merge denied until a named fix cohort grant is minted.\n");
|
|
339
345
|
process.stdout.write(" Tests, evidence capture, and issue filing remain allowed.\n");
|
|
340
346
|
return 0;
|
|
@@ -343,10 +349,18 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
343
349
|
const blocked = gateConfirm();
|
|
344
350
|
if (blocked !== null)
|
|
345
351
|
return blocked;
|
|
346
|
-
|
|
352
|
+
// Sealed campaign-end only after CLI human-presence gateConfirm (#4233).
|
|
353
|
+
// pinActive mint wiring stays parked (class 4 follow-up).
|
|
354
|
+
const suspended = suspendUatLease({
|
|
347
355
|
projectRoot: args.projectRoot,
|
|
348
356
|
actor: args.actor,
|
|
357
|
+
campaignEndSeal: uatCampaignEndSeal(),
|
|
349
358
|
});
|
|
359
|
+
if (!suspended.ok) {
|
|
360
|
+
process.stderr.write(`authz:uat-suspend: ${suspended.reason}\n`);
|
|
361
|
+
return 2;
|
|
362
|
+
}
|
|
363
|
+
const state = suspended.state;
|
|
350
364
|
if (state.uat === null) {
|
|
351
365
|
process.stdout.write("UAT lease was already inactive.\n");
|
|
352
366
|
}
|
|
@@ -400,7 +414,7 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
400
414
|
const blocked = gateConfirm();
|
|
401
415
|
if (blocked !== null)
|
|
402
416
|
return blocked;
|
|
403
|
-
const
|
|
417
|
+
const minted = mintDecomposeStructuralApplyGrant({
|
|
404
418
|
projectRoot: root,
|
|
405
419
|
parentPath: parentAbs,
|
|
406
420
|
draftPath: draftAbs,
|
|
@@ -409,6 +423,11 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
409
423
|
expiresAt: args.expiresAt,
|
|
410
424
|
singleUse: args.singleUse,
|
|
411
425
|
});
|
|
426
|
+
if (!minted.ok) {
|
|
427
|
+
process.stderr.write(`authz:grant: ${minted.reason}\n`);
|
|
428
|
+
return 2;
|
|
429
|
+
}
|
|
430
|
+
const grant = minted.grant;
|
|
412
431
|
// mintDecomposeStructuralApplyGrant always sets contentDigest + worktree.
|
|
413
432
|
const digest = grant.scope.contentDigest || "";
|
|
414
433
|
process.stdout.write(`✓ human-origin structural grant minted id=${grant.id} origin=${grant.origin.kind}\n`);
|
|
@@ -474,7 +493,7 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
474
493
|
const blocked = gateConfirm();
|
|
475
494
|
if (blocked !== null)
|
|
476
495
|
return blocked;
|
|
477
|
-
const
|
|
496
|
+
const minted = mintAfkTemplateGrant({
|
|
478
497
|
projectRoot: args.projectRoot,
|
|
479
498
|
template: args.template,
|
|
480
499
|
target: args.target,
|
|
@@ -489,6 +508,11 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
489
508
|
issueIds: args.issueIds,
|
|
490
509
|
cohortId: args.cohort,
|
|
491
510
|
});
|
|
511
|
+
if (!minted.ok) {
|
|
512
|
+
process.stderr.write(`authz:grant: ${minted.reason}\n`);
|
|
513
|
+
return 2;
|
|
514
|
+
}
|
|
515
|
+
const grant = minted.grant;
|
|
492
516
|
process.stdout.write(`✓ human-origin grant minted id=${grant.id} origin=${grant.origin.kind} ` +
|
|
493
517
|
`template=${args.template}\n`);
|
|
494
518
|
if (isFinishLoopTemplateName(args.template)) {
|
|
@@ -511,7 +535,7 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
511
535
|
if (blocked !== null)
|
|
512
536
|
return blocked;
|
|
513
537
|
}
|
|
514
|
-
const
|
|
538
|
+
const minted = mintHumanOriginGrant({
|
|
515
539
|
projectRoot: args.projectRoot,
|
|
516
540
|
actor: args.actor,
|
|
517
541
|
operations: args.operations,
|
|
@@ -525,6 +549,11 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
525
549
|
expiresAt: args.expiresAt,
|
|
526
550
|
singleUse: args.singleUse,
|
|
527
551
|
});
|
|
552
|
+
if (!minted.ok) {
|
|
553
|
+
process.stderr.write(`authz:grant: ${minted.reason}\n`);
|
|
554
|
+
return 2;
|
|
555
|
+
}
|
|
556
|
+
const grant = minted.grant;
|
|
528
557
|
process.stdout.write(`✓ human-origin grant minted id=${grant.id} origin=${grant.origin.kind}\n`);
|
|
529
558
|
process.stdout.write(` ops=[${grant.scope.operations.join(",")}] cohort=${grant.scope.cohortId ?? "-"}\n`);
|
|
530
559
|
if (grant.scope.surfaces.length > 0) {
|
|
@@ -542,10 +571,15 @@ export function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
542
571
|
if (blocked !== null)
|
|
543
572
|
return blocked;
|
|
544
573
|
}
|
|
545
|
-
const
|
|
574
|
+
const revokedResult = revokeGrant({
|
|
546
575
|
projectRoot: args.projectRoot,
|
|
547
576
|
grantId: args.grantId,
|
|
548
577
|
});
|
|
578
|
+
if (!revokedResult.ok) {
|
|
579
|
+
process.stderr.write(`authz:revoke: ${revokedResult.reason}\n`);
|
|
580
|
+
return 2;
|
|
581
|
+
}
|
|
582
|
+
const revoked = revokedResult.grant;
|
|
549
583
|
if (revoked === null) {
|
|
550
584
|
process.stderr.write(`authz: grant not found: ${args.grantId}\n`);
|
|
551
585
|
return 1;
|
package/dist/changelog-check.js
CHANGED
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { runChangelogCheck } from "@deftai/directive-core/task-surface";
|
|
5
|
-
function
|
|
5
|
+
function parseChangelogCheckArgs(argv) {
|
|
6
6
|
let projectRoot = ".";
|
|
7
|
+
let againstMergeBase = false;
|
|
7
8
|
for (let i = 0; i < argv.length; i += 1) {
|
|
8
9
|
const arg = argv[i] ?? "";
|
|
9
10
|
if (arg === "--project-root") {
|
|
10
11
|
const value = argv[i + 1];
|
|
11
12
|
if (value === undefined) {
|
|
12
|
-
return {
|
|
13
|
+
return {
|
|
14
|
+
projectRoot,
|
|
15
|
+
againstMergeBase,
|
|
16
|
+
error: "argument --project-root: expected one argument",
|
|
17
|
+
};
|
|
13
18
|
}
|
|
14
19
|
projectRoot = value;
|
|
15
20
|
i += 1;
|
|
@@ -17,14 +22,17 @@ function parseProjectRoot(argv) {
|
|
|
17
22
|
else if (arg.startsWith("--project-root=")) {
|
|
18
23
|
projectRoot = arg.slice("--project-root=".length);
|
|
19
24
|
}
|
|
25
|
+
else if (arg === "--against-merge-base") {
|
|
26
|
+
againstMergeBase = true;
|
|
27
|
+
}
|
|
20
28
|
else {
|
|
21
|
-
return { projectRoot, error: `unrecognized argument: ${arg}` };
|
|
29
|
+
return { projectRoot, againstMergeBase, error: `unrecognized argument: ${arg}` };
|
|
22
30
|
}
|
|
23
31
|
}
|
|
24
|
-
return { projectRoot };
|
|
32
|
+
return { projectRoot, againstMergeBase };
|
|
25
33
|
}
|
|
26
34
|
export function run(argv) {
|
|
27
|
-
const args =
|
|
35
|
+
const args = parseChangelogCheckArgs(argv);
|
|
28
36
|
if (args.error !== undefined) {
|
|
29
37
|
process.stderr.write(`changelog-check: ${args.error}\n`);
|
|
30
38
|
return 2;
|
|
@@ -36,7 +44,7 @@ export function run(argv) {
|
|
|
36
44
|
writeErr: (text) => {
|
|
37
45
|
process.stderr.write(text);
|
|
38
46
|
},
|
|
39
|
-
});
|
|
47
|
+
}, { againstMergeBase: args.againstMergeBase });
|
|
40
48
|
}
|
|
41
49
|
if (process.argv[1] !== undefined && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
42
50
|
process.exit(run(process.argv.slice(2)));
|
package/dist/dispatch.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ 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", "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"];
|
|
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", "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
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). */
|
package/dist/dispatch.js
CHANGED
|
@@ -64,6 +64,7 @@ export const CLI_MODULE_VERBS = [
|
|
|
64
64
|
"pr-watch",
|
|
65
65
|
"pr-finish-loop",
|
|
66
66
|
"directive-finish-loop",
|
|
67
|
+
"pre-pr-run",
|
|
67
68
|
"preflight-cache",
|
|
68
69
|
"preflight-gh",
|
|
69
70
|
"probe-session",
|
|
@@ -310,6 +311,7 @@ export const FRESHNESS_ALIAS_SUBCOMMANDS = {
|
|
|
310
311
|
const FRESHNESS_COLON_ALIASES = Object.fromEntries(Object.keys(FRESHNESS_ALIAS_SUBCOMMANDS).map((alias) => [alias, "freshness-report"]));
|
|
311
312
|
/** Task-style aliases (framework_commands / Taskfile names). */
|
|
312
313
|
export const VERB_ALIASES = {
|
|
314
|
+
"pre-pr:run": "pre-pr-run",
|
|
313
315
|
"hook:dispatch": "hook-dispatch",
|
|
314
316
|
"verify:encoding": "verify-encoding",
|
|
315
317
|
"verify:forward-coverage": "verify-forward-coverage",
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
interface ParsedArgs {
|
|
3
|
+
repo: string;
|
|
4
|
+
baseSha: string;
|
|
5
|
+
headSha: string;
|
|
6
|
+
treeHash: string;
|
|
7
|
+
prBodyHash: string;
|
|
8
|
+
prNodeId: string | null;
|
|
9
|
+
approvedRevision: string;
|
|
10
|
+
scope: string[];
|
|
11
|
+
acceptance: string;
|
|
12
|
+
generation: number;
|
|
13
|
+
skillVersion: string;
|
|
14
|
+
policyVersion: string;
|
|
15
|
+
runId: string | null;
|
|
16
|
+
complete: boolean;
|
|
17
|
+
observeCommand: boolean;
|
|
18
|
+
observeSemantic: boolean;
|
|
19
|
+
phase: string | null;
|
|
20
|
+
command: string;
|
|
21
|
+
exitCode: number | null;
|
|
22
|
+
inputHash: string | null;
|
|
23
|
+
skipReason: string | null;
|
|
24
|
+
suppliedContentsHash: string;
|
|
25
|
+
controllerObservedHash: string;
|
|
26
|
+
criteriaDigest: string | null;
|
|
27
|
+
reviewedFiles: string[];
|
|
28
|
+
reviewerReportRef: string | null;
|
|
29
|
+
markComplete: boolean;
|
|
30
|
+
skillOpen: boolean;
|
|
31
|
+
evaluate: boolean;
|
|
32
|
+
json: boolean;
|
|
33
|
+
help: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
37
|
+
export declare function run(argv: string[]): number;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=pre-pr-run.d.ts.map
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent-facing pre-PR controller entry (#4912 Limb 3).
|
|
4
|
+
* `deft pre-pr:run` starts the prescribed workflow. Opening the skill file
|
|
5
|
+
* is not completion. Generic mark-complete cannot mint a pass.
|
|
6
|
+
*/
|
|
7
|
+
import { completeRun, computeControllerObservedHash, controllerPublisher, digestApprovedCriteria, evaluateLivePrePrCheck, getDefaultPrePrStore, markComplete, noteSkillFileOpen, observeCommandPhase, PRE_PR_PHASE_IDS, phaseSpec, resolveRecordFromStore, startControllerRun, submitReviewerReport, } from "@deftai/directive-core/pre-pr-controller";
|
|
8
|
+
import { isDirectEntrypoint } from "./entrypoint.js";
|
|
9
|
+
const USAGE = `Usage: deft pre-pr:run [options]
|
|
10
|
+
|
|
11
|
+
Start the repository-controlled pre-PR workflow. Opening the skill file is
|
|
12
|
+
not completion. A generic mark-complete cannot mint a pass.
|
|
13
|
+
|
|
14
|
+
Options:
|
|
15
|
+
--repo <owner/name>
|
|
16
|
+
--base-sha <sha>
|
|
17
|
+
--head-sha <sha>
|
|
18
|
+
--tree-hash <sha>
|
|
19
|
+
--pr-body-hash <hex>
|
|
20
|
+
--pr-node-id <id>
|
|
21
|
+
--approved-revision <sha>
|
|
22
|
+
--scope <path> (repeatable)
|
|
23
|
+
--acceptance <text>
|
|
24
|
+
--generation <n>
|
|
25
|
+
--skill-version <ver>
|
|
26
|
+
--policy-version <ver>
|
|
27
|
+
--run-id <id> lookup hint only
|
|
28
|
+
--complete publisher-backed complete (fails without observables)
|
|
29
|
+
--observe-command record a command-observable phase before --complete
|
|
30
|
+
--observe-semantic record semantic / final-no-change evidence
|
|
31
|
+
--phase <id>
|
|
32
|
+
--command <text>
|
|
33
|
+
--exit-code <n> required with --observe-command; omit is a parse error
|
|
34
|
+
--input-hash <hex>
|
|
35
|
+
--skip-reason <text> skip also requires a non-zero --exit-code
|
|
36
|
+
--supplied-contents-hash <hex>
|
|
37
|
+
--controller-observed-hash <hex>
|
|
38
|
+
optional; must match independently hashed reviewed files
|
|
39
|
+
--criteria-digest <hex>
|
|
40
|
+
--reviewed-file <path> (repeatable)
|
|
41
|
+
--reviewer-report-ref <ref>
|
|
42
|
+
--mark-complete always refuses; cannot mint a pass
|
|
43
|
+
--skill-open always refuses; opening the skill is not completion
|
|
44
|
+
--evaluate evaluate live binding against the private store
|
|
45
|
+
--json
|
|
46
|
+
--help
|
|
47
|
+
`;
|
|
48
|
+
export function parseArgs(argv) {
|
|
49
|
+
const parsed = {
|
|
50
|
+
repo: "",
|
|
51
|
+
baseSha: "",
|
|
52
|
+
headSha: "",
|
|
53
|
+
treeHash: "",
|
|
54
|
+
prBodyHash: "",
|
|
55
|
+
prNodeId: null,
|
|
56
|
+
approvedRevision: "",
|
|
57
|
+
scope: [],
|
|
58
|
+
acceptance: "",
|
|
59
|
+
generation: 1,
|
|
60
|
+
skillVersion: "0.1",
|
|
61
|
+
policyVersion: "1",
|
|
62
|
+
runId: null,
|
|
63
|
+
complete: false,
|
|
64
|
+
observeCommand: false,
|
|
65
|
+
observeSemantic: false,
|
|
66
|
+
phase: null,
|
|
67
|
+
command: "",
|
|
68
|
+
exitCode: null,
|
|
69
|
+
inputHash: null,
|
|
70
|
+
skipReason: null,
|
|
71
|
+
suppliedContentsHash: "",
|
|
72
|
+
controllerObservedHash: "",
|
|
73
|
+
criteriaDigest: null,
|
|
74
|
+
reviewedFiles: [],
|
|
75
|
+
reviewerReportRef: null,
|
|
76
|
+
markComplete: false,
|
|
77
|
+
skillOpen: false,
|
|
78
|
+
evaluate: false,
|
|
79
|
+
json: false,
|
|
80
|
+
help: false,
|
|
81
|
+
};
|
|
82
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
83
|
+
const arg = argv[i];
|
|
84
|
+
if (arg === "--help" || arg === "-h") {
|
|
85
|
+
parsed.help = true;
|
|
86
|
+
}
|
|
87
|
+
else if (arg === "--json") {
|
|
88
|
+
parsed.json = true;
|
|
89
|
+
}
|
|
90
|
+
else if (arg === "--complete") {
|
|
91
|
+
parsed.complete = true;
|
|
92
|
+
}
|
|
93
|
+
else if (arg === "--observe-command") {
|
|
94
|
+
parsed.observeCommand = true;
|
|
95
|
+
}
|
|
96
|
+
else if (arg === "--observe-semantic") {
|
|
97
|
+
parsed.observeSemantic = true;
|
|
98
|
+
}
|
|
99
|
+
else if (arg === "--phase" || arg?.startsWith("--phase=")) {
|
|
100
|
+
const value = arg === "--phase" ? argv[i + 1] : arg.slice("--phase=".length);
|
|
101
|
+
if (value === undefined)
|
|
102
|
+
return { ...parsed, error: "argument --phase: expected one argument" };
|
|
103
|
+
parsed.phase = value;
|
|
104
|
+
if (arg === "--phase")
|
|
105
|
+
i += 1;
|
|
106
|
+
}
|
|
107
|
+
else if (arg === "--command" || arg?.startsWith("--command=")) {
|
|
108
|
+
const value = arg === "--command" ? argv[i + 1] : arg.slice("--command=".length);
|
|
109
|
+
if (value === undefined)
|
|
110
|
+
return { ...parsed, error: "argument --command: expected one argument" };
|
|
111
|
+
parsed.command = value;
|
|
112
|
+
if (arg === "--command")
|
|
113
|
+
i += 1;
|
|
114
|
+
}
|
|
115
|
+
else if (arg === "--exit-code" || arg?.startsWith("--exit-code=")) {
|
|
116
|
+
const value = arg === "--exit-code" ? argv[i + 1] : arg.slice("--exit-code=".length);
|
|
117
|
+
if (value === undefined) {
|
|
118
|
+
return { ...parsed, error: "argument --exit-code: expected one argument" };
|
|
119
|
+
}
|
|
120
|
+
const n = Number.parseInt(value, 10);
|
|
121
|
+
if (!Number.isFinite(n)) {
|
|
122
|
+
return { ...parsed, error: "argument --exit-code: expected an integer" };
|
|
123
|
+
}
|
|
124
|
+
parsed.exitCode = n;
|
|
125
|
+
if (arg === "--exit-code")
|
|
126
|
+
i += 1;
|
|
127
|
+
}
|
|
128
|
+
else if (arg === "--input-hash" || arg?.startsWith("--input-hash=")) {
|
|
129
|
+
const value = arg === "--input-hash" ? argv[i + 1] : arg.slice("--input-hash=".length);
|
|
130
|
+
if (value === undefined) {
|
|
131
|
+
return { ...parsed, error: "argument --input-hash: expected one argument" };
|
|
132
|
+
}
|
|
133
|
+
parsed.inputHash = value;
|
|
134
|
+
if (arg === "--input-hash")
|
|
135
|
+
i += 1;
|
|
136
|
+
}
|
|
137
|
+
else if (arg === "--skip-reason" || arg?.startsWith("--skip-reason=")) {
|
|
138
|
+
const value = arg === "--skip-reason" ? argv[i + 1] : arg.slice("--skip-reason=".length);
|
|
139
|
+
if (value === undefined) {
|
|
140
|
+
return { ...parsed, error: "argument --skip-reason: expected one argument" };
|
|
141
|
+
}
|
|
142
|
+
parsed.skipReason = value;
|
|
143
|
+
if (arg === "--skip-reason")
|
|
144
|
+
i += 1;
|
|
145
|
+
}
|
|
146
|
+
else if (arg === "--supplied-contents-hash" || arg?.startsWith("--supplied-contents-hash=")) {
|
|
147
|
+
const value = arg === "--supplied-contents-hash"
|
|
148
|
+
? argv[i + 1]
|
|
149
|
+
: arg.slice("--supplied-contents-hash=".length);
|
|
150
|
+
if (value === undefined) {
|
|
151
|
+
return { ...parsed, error: "argument --supplied-contents-hash: expected one argument" };
|
|
152
|
+
}
|
|
153
|
+
parsed.suppliedContentsHash = value;
|
|
154
|
+
if (arg === "--supplied-contents-hash")
|
|
155
|
+
i += 1;
|
|
156
|
+
}
|
|
157
|
+
else if (arg === "--controller-observed-hash" ||
|
|
158
|
+
arg?.startsWith("--controller-observed-hash=")) {
|
|
159
|
+
const value = arg === "--controller-observed-hash"
|
|
160
|
+
? argv[i + 1]
|
|
161
|
+
: arg.slice("--controller-observed-hash=".length);
|
|
162
|
+
if (value === undefined) {
|
|
163
|
+
return {
|
|
164
|
+
...parsed,
|
|
165
|
+
error: "argument --controller-observed-hash: expected one argument",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
parsed.controllerObservedHash = value;
|
|
169
|
+
if (arg === "--controller-observed-hash")
|
|
170
|
+
i += 1;
|
|
171
|
+
}
|
|
172
|
+
else if (arg === "--criteria-digest" || arg?.startsWith("--criteria-digest=")) {
|
|
173
|
+
const value = arg === "--criteria-digest" ? argv[i + 1] : arg.slice("--criteria-digest=".length);
|
|
174
|
+
if (value === undefined) {
|
|
175
|
+
return { ...parsed, error: "argument --criteria-digest: expected one argument" };
|
|
176
|
+
}
|
|
177
|
+
parsed.criteriaDigest = value;
|
|
178
|
+
if (arg === "--criteria-digest")
|
|
179
|
+
i += 1;
|
|
180
|
+
}
|
|
181
|
+
else if (arg === "--reviewed-file" || arg?.startsWith("--reviewed-file=")) {
|
|
182
|
+
const value = arg === "--reviewed-file" ? argv[i + 1] : arg.slice("--reviewed-file=".length);
|
|
183
|
+
if (value === undefined) {
|
|
184
|
+
return { ...parsed, error: "argument --reviewed-file: expected one argument" };
|
|
185
|
+
}
|
|
186
|
+
parsed.reviewedFiles.push(value);
|
|
187
|
+
if (arg === "--reviewed-file")
|
|
188
|
+
i += 1;
|
|
189
|
+
}
|
|
190
|
+
else if (arg === "--reviewer-report-ref" || arg?.startsWith("--reviewer-report-ref=")) {
|
|
191
|
+
const value = arg === "--reviewer-report-ref" ? argv[i + 1] : arg.slice("--reviewer-report-ref=".length);
|
|
192
|
+
if (value === undefined) {
|
|
193
|
+
return { ...parsed, error: "argument --reviewer-report-ref: expected one argument" };
|
|
194
|
+
}
|
|
195
|
+
parsed.reviewerReportRef = value;
|
|
196
|
+
if (arg === "--reviewer-report-ref")
|
|
197
|
+
i += 1;
|
|
198
|
+
}
|
|
199
|
+
else if (arg === "--mark-complete") {
|
|
200
|
+
parsed.markComplete = true;
|
|
201
|
+
}
|
|
202
|
+
else if (arg === "--skill-open") {
|
|
203
|
+
parsed.skillOpen = true;
|
|
204
|
+
}
|
|
205
|
+
else if (arg === "--evaluate") {
|
|
206
|
+
parsed.evaluate = true;
|
|
207
|
+
}
|
|
208
|
+
else if (arg === "--repo" || arg?.startsWith("--repo=")) {
|
|
209
|
+
const value = arg === "--repo" ? argv[i + 1] : arg.slice("--repo=".length);
|
|
210
|
+
if (value === undefined)
|
|
211
|
+
return { ...parsed, error: "argument --repo: expected one argument" };
|
|
212
|
+
parsed.repo = value;
|
|
213
|
+
if (arg === "--repo")
|
|
214
|
+
i += 1;
|
|
215
|
+
}
|
|
216
|
+
else if (arg === "--base-sha" || arg?.startsWith("--base-sha=")) {
|
|
217
|
+
const value = arg === "--base-sha" ? argv[i + 1] : arg.slice("--base-sha=".length);
|
|
218
|
+
if (value === undefined)
|
|
219
|
+
return { ...parsed, error: "argument --base-sha: expected one argument" };
|
|
220
|
+
parsed.baseSha = value;
|
|
221
|
+
if (arg === "--base-sha")
|
|
222
|
+
i += 1;
|
|
223
|
+
}
|
|
224
|
+
else if (arg === "--head-sha" || arg?.startsWith("--head-sha=")) {
|
|
225
|
+
const value = arg === "--head-sha" ? argv[i + 1] : arg.slice("--head-sha=".length);
|
|
226
|
+
if (value === undefined)
|
|
227
|
+
return { ...parsed, error: "argument --head-sha: expected one argument" };
|
|
228
|
+
parsed.headSha = value;
|
|
229
|
+
if (arg === "--head-sha")
|
|
230
|
+
i += 1;
|
|
231
|
+
}
|
|
232
|
+
else if (arg === "--tree-hash" || arg?.startsWith("--tree-hash=")) {
|
|
233
|
+
const value = arg === "--tree-hash" ? argv[i + 1] : arg.slice("--tree-hash=".length);
|
|
234
|
+
if (value === undefined)
|
|
235
|
+
return { ...parsed, error: "argument --tree-hash: expected one argument" };
|
|
236
|
+
parsed.treeHash = value;
|
|
237
|
+
if (arg === "--tree-hash")
|
|
238
|
+
i += 1;
|
|
239
|
+
}
|
|
240
|
+
else if (arg === "--pr-body-hash" || arg?.startsWith("--pr-body-hash=")) {
|
|
241
|
+
const value = arg === "--pr-body-hash" ? argv[i + 1] : arg.slice("--pr-body-hash=".length);
|
|
242
|
+
if (value === undefined) {
|
|
243
|
+
return { ...parsed, error: "argument --pr-body-hash: expected one argument" };
|
|
244
|
+
}
|
|
245
|
+
parsed.prBodyHash = value;
|
|
246
|
+
if (arg === "--pr-body-hash")
|
|
247
|
+
i += 1;
|
|
248
|
+
}
|
|
249
|
+
else if (arg === "--pr-node-id" || arg?.startsWith("--pr-node-id=")) {
|
|
250
|
+
const value = arg === "--pr-node-id" ? argv[i + 1] : arg.slice("--pr-node-id=".length);
|
|
251
|
+
if (value === undefined) {
|
|
252
|
+
return { ...parsed, error: "argument --pr-node-id: expected one argument" };
|
|
253
|
+
}
|
|
254
|
+
parsed.prNodeId = value;
|
|
255
|
+
if (arg === "--pr-node-id")
|
|
256
|
+
i += 1;
|
|
257
|
+
}
|
|
258
|
+
else if (arg === "--approved-revision" || arg?.startsWith("--approved-revision=")) {
|
|
259
|
+
const value = arg === "--approved-revision" ? argv[i + 1] : arg.slice("--approved-revision=".length);
|
|
260
|
+
if (value === undefined) {
|
|
261
|
+
return { ...parsed, error: "argument --approved-revision: expected one argument" };
|
|
262
|
+
}
|
|
263
|
+
parsed.approvedRevision = value;
|
|
264
|
+
if (arg === "--approved-revision")
|
|
265
|
+
i += 1;
|
|
266
|
+
}
|
|
267
|
+
else if (arg === "--scope" || arg?.startsWith("--scope=")) {
|
|
268
|
+
const value = arg === "--scope" ? argv[i + 1] : arg.slice("--scope=".length);
|
|
269
|
+
if (value === undefined)
|
|
270
|
+
return { ...parsed, error: "argument --scope: expected one argument" };
|
|
271
|
+
parsed.scope.push(value);
|
|
272
|
+
if (arg === "--scope")
|
|
273
|
+
i += 1;
|
|
274
|
+
}
|
|
275
|
+
else if (arg === "--acceptance" || arg?.startsWith("--acceptance=")) {
|
|
276
|
+
const value = arg === "--acceptance" ? argv[i + 1] : arg.slice("--acceptance=".length);
|
|
277
|
+
if (value === undefined) {
|
|
278
|
+
return { ...parsed, error: "argument --acceptance: expected one argument" };
|
|
279
|
+
}
|
|
280
|
+
parsed.acceptance = value;
|
|
281
|
+
if (arg === "--acceptance")
|
|
282
|
+
i += 1;
|
|
283
|
+
}
|
|
284
|
+
else if (arg === "--generation" || arg?.startsWith("--generation=")) {
|
|
285
|
+
const value = arg === "--generation" ? argv[i + 1] : arg.slice("--generation=".length);
|
|
286
|
+
if (value === undefined) {
|
|
287
|
+
return { ...parsed, error: "argument --generation: expected one argument" };
|
|
288
|
+
}
|
|
289
|
+
const n = Number.parseInt(value, 10);
|
|
290
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
291
|
+
return { ...parsed, error: "argument --generation: expected a positive integer" };
|
|
292
|
+
}
|
|
293
|
+
parsed.generation = n;
|
|
294
|
+
if (arg === "--generation")
|
|
295
|
+
i += 1;
|
|
296
|
+
}
|
|
297
|
+
else if (arg === "--skill-version" || arg?.startsWith("--skill-version=")) {
|
|
298
|
+
const value = arg === "--skill-version" ? argv[i + 1] : arg.slice("--skill-version=".length);
|
|
299
|
+
if (value === undefined) {
|
|
300
|
+
return { ...parsed, error: "argument --skill-version: expected one argument" };
|
|
301
|
+
}
|
|
302
|
+
parsed.skillVersion = value;
|
|
303
|
+
if (arg === "--skill-version")
|
|
304
|
+
i += 1;
|
|
305
|
+
}
|
|
306
|
+
else if (arg === "--policy-version" || arg?.startsWith("--policy-version=")) {
|
|
307
|
+
const value = arg === "--policy-version" ? argv[i + 1] : arg.slice("--policy-version=".length);
|
|
308
|
+
if (value === undefined) {
|
|
309
|
+
return { ...parsed, error: "argument --policy-version: expected one argument" };
|
|
310
|
+
}
|
|
311
|
+
parsed.policyVersion = value;
|
|
312
|
+
if (arg === "--policy-version")
|
|
313
|
+
i += 1;
|
|
314
|
+
}
|
|
315
|
+
else if (arg === "--run-id" || arg?.startsWith("--run-id=")) {
|
|
316
|
+
const value = arg === "--run-id" ? argv[i + 1] : arg.slice("--run-id=".length);
|
|
317
|
+
if (value === undefined)
|
|
318
|
+
return { ...parsed, error: "argument --run-id: expected one argument" };
|
|
319
|
+
parsed.runId = value;
|
|
320
|
+
if (arg === "--run-id")
|
|
321
|
+
i += 1;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
return { ...parsed, error: `unrecognized argument: ${arg}` };
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return parsed;
|
|
328
|
+
}
|
|
329
|
+
function emit(json, payload, text, err) {
|
|
330
|
+
const line = json ? `${JSON.stringify(payload)}\n` : `${text}\n`;
|
|
331
|
+
if (err)
|
|
332
|
+
process.stderr.write(line);
|
|
333
|
+
else
|
|
334
|
+
process.stdout.write(line);
|
|
335
|
+
}
|
|
336
|
+
function asPhaseId(value) {
|
|
337
|
+
return PRE_PR_PHASE_IDS.includes(value) ? value : null;
|
|
338
|
+
}
|
|
339
|
+
export function run(argv) {
|
|
340
|
+
const args = parseArgs(argv);
|
|
341
|
+
if (args.help) {
|
|
342
|
+
process.stdout.write(USAGE);
|
|
343
|
+
return 0;
|
|
344
|
+
}
|
|
345
|
+
if (args.error !== undefined) {
|
|
346
|
+
process.stderr.write(`pre_pr_run: ${args.error}\n`);
|
|
347
|
+
return 2;
|
|
348
|
+
}
|
|
349
|
+
if (args.skillOpen) {
|
|
350
|
+
const d = noteSkillFileOpen();
|
|
351
|
+
emit(args.json, d, d.message, true);
|
|
352
|
+
return 1;
|
|
353
|
+
}
|
|
354
|
+
const store = getDefaultPrePrStore();
|
|
355
|
+
if (args.markComplete) {
|
|
356
|
+
const d = markComplete(store, args.runId ?? "");
|
|
357
|
+
emit(args.json, d, d.message, true);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
if (args.observeCommand || args.observeSemantic) {
|
|
361
|
+
if (args.runId === null || args.runId.length === 0) {
|
|
362
|
+
process.stderr.write("pre_pr_run: observe requires --run-id\n");
|
|
363
|
+
return 2;
|
|
364
|
+
}
|
|
365
|
+
if (args.phase === null || args.phase.length === 0) {
|
|
366
|
+
process.stderr.write("pre_pr_run: observe requires --phase\n");
|
|
367
|
+
return 2;
|
|
368
|
+
}
|
|
369
|
+
const phase = asPhaseId(args.phase);
|
|
370
|
+
if (phase === null) {
|
|
371
|
+
process.stderr.write(`pre_pr_run: unknown phase ${args.phase}\n`);
|
|
372
|
+
return 2;
|
|
373
|
+
}
|
|
374
|
+
const rec = store.getById(args.runId);
|
|
375
|
+
if (args.observeCommand) {
|
|
376
|
+
if (args.exitCode === null) {
|
|
377
|
+
process.stderr.write("pre_pr_run: --observe-command requires --exit-code\n");
|
|
378
|
+
return 2;
|
|
379
|
+
}
|
|
380
|
+
if (args.skipReason !== null && args.skipReason.length > 0 && args.exitCode === 0) {
|
|
381
|
+
process.stderr.write("pre_pr_run: skip requires a non-zero --exit-code plus --skip-reason\n");
|
|
382
|
+
return 2;
|
|
383
|
+
}
|
|
384
|
+
const spec = phaseSpec(phase);
|
|
385
|
+
const d = observeCommandPhase(store, args.runId, {
|
|
386
|
+
phaseId: phase,
|
|
387
|
+
command: args.command.length > 0 ? args.command : (spec.command ?? ""),
|
|
388
|
+
exitCode: args.exitCode,
|
|
389
|
+
inputHash: args.inputHash ?? rec?.inputHash ?? "",
|
|
390
|
+
skipReason: args.skipReason,
|
|
391
|
+
});
|
|
392
|
+
emit(args.json, d, d.message, !d.ok);
|
|
393
|
+
return d.ok ? 0 : 1;
|
|
394
|
+
}
|
|
395
|
+
if (args.suppliedContentsHash.length === 0) {
|
|
396
|
+
process.stderr.write("pre_pr_run: --observe-semantic requires --supplied-contents-hash\n");
|
|
397
|
+
return 2;
|
|
398
|
+
}
|
|
399
|
+
if (args.reviewedFiles.length === 0) {
|
|
400
|
+
process.stderr.write("pre_pr_run: --observe-semantic requires --reviewed-file\n");
|
|
401
|
+
return 2;
|
|
402
|
+
}
|
|
403
|
+
const observed = computeControllerObservedHash({ reviewedFiles: args.reviewedFiles });
|
|
404
|
+
if (observed === null) {
|
|
405
|
+
process.stderr.write("pre_pr_run: --observe-semantic could not hash reviewed-file contents\n");
|
|
406
|
+
return 2;
|
|
407
|
+
}
|
|
408
|
+
if (args.controllerObservedHash.length > 0 && args.controllerObservedHash !== observed) {
|
|
409
|
+
process.stderr.write("pre_pr_run: --controller-observed-hash does not match independently hashed reviewed-file contents\n");
|
|
410
|
+
return 1;
|
|
411
|
+
}
|
|
412
|
+
if (args.suppliedContentsHash !== observed) {
|
|
413
|
+
process.stderr.write("pre_pr_run: --supplied-contents-hash does not match independently hashed reviewed-file contents\n");
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
const d = submitReviewerReport(store, args.runId, {
|
|
417
|
+
phaseId: phase,
|
|
418
|
+
reviewedFileManifest: args.reviewedFiles,
|
|
419
|
+
suppliedContentsHash: args.suppliedContentsHash,
|
|
420
|
+
criteriaDigest: args.criteriaDigest ?? rec?.criteria.digest ?? "",
|
|
421
|
+
reviewerReportRef: args.reviewerReportRef,
|
|
422
|
+
controllerObservedHash: observed,
|
|
423
|
+
});
|
|
424
|
+
emit(args.json, d, d.message, !d.ok);
|
|
425
|
+
return d.ok ? 0 : 1;
|
|
426
|
+
}
|
|
427
|
+
if (args.evaluate) {
|
|
428
|
+
if (args.repo.length === 0 || args.baseSha.length === 0 || args.headSha.length === 0) {
|
|
429
|
+
process.stderr.write("pre_pr_run: --evaluate requires --repo --base-sha --head-sha\n");
|
|
430
|
+
return 2;
|
|
431
|
+
}
|
|
432
|
+
if (args.prBodyHash.length === 0) {
|
|
433
|
+
process.stderr.write("pre_pr_run: --evaluate requires --pr-body-hash\n");
|
|
434
|
+
return 2;
|
|
435
|
+
}
|
|
436
|
+
const record = resolveRecordFromStore(store, {
|
|
437
|
+
id: args.runId,
|
|
438
|
+
prNodeId: args.prNodeId,
|
|
439
|
+
});
|
|
440
|
+
const headSource = args.approvedRevision.length > 0
|
|
441
|
+
? args.approvedRevision
|
|
442
|
+
: (record?.criteria.sourceRevisionSha ?? args.headSha);
|
|
443
|
+
const liveApproved = digestApprovedCriteria({
|
|
444
|
+
sourceRevisionSha: headSource,
|
|
445
|
+
scopePaths: args.scope,
|
|
446
|
+
acceptanceText: args.acceptance,
|
|
447
|
+
generation: args.generation,
|
|
448
|
+
});
|
|
449
|
+
const d = evaluateLivePrePrCheck({
|
|
450
|
+
store,
|
|
451
|
+
liveBinding: {
|
|
452
|
+
repo: args.repo,
|
|
453
|
+
baseSha: args.baseSha,
|
|
454
|
+
headSha: args.headSha,
|
|
455
|
+
prNodeId: args.prNodeId,
|
|
456
|
+
prBodyHash: args.prBodyHash,
|
|
457
|
+
},
|
|
458
|
+
presentedRunId: args.runId,
|
|
459
|
+
approvedCriteria: liveApproved,
|
|
460
|
+
currentGeneration: args.generation,
|
|
461
|
+
headCriteria: liveApproved,
|
|
462
|
+
});
|
|
463
|
+
emit(args.json, d, d.message, !d.ok);
|
|
464
|
+
return d.ok ? 0 : 1;
|
|
465
|
+
}
|
|
466
|
+
if (args.complete) {
|
|
467
|
+
if (args.runId === null || args.runId.length === 0) {
|
|
468
|
+
process.stderr.write("pre_pr_run: --complete requires --run-id\n");
|
|
469
|
+
return 2;
|
|
470
|
+
}
|
|
471
|
+
const d = completeRun(store, controllerPublisher(), args.runId);
|
|
472
|
+
emit(args.json, d, d.message, !d.ok);
|
|
473
|
+
return d.ok ? 0 : 1;
|
|
474
|
+
}
|
|
475
|
+
if (args.repo.length === 0 ||
|
|
476
|
+
args.baseSha.length === 0 ||
|
|
477
|
+
args.headSha.length === 0 ||
|
|
478
|
+
args.treeHash.length === 0 ||
|
|
479
|
+
args.prBodyHash.length === 0 ||
|
|
480
|
+
args.approvedRevision.length === 0) {
|
|
481
|
+
process.stderr.write("pre_pr_run: start requires --repo --base-sha --head-sha --tree-hash --pr-body-hash --approved-revision\n");
|
|
482
|
+
return 2;
|
|
483
|
+
}
|
|
484
|
+
const criteria = digestApprovedCriteria({
|
|
485
|
+
sourceRevisionSha: args.approvedRevision,
|
|
486
|
+
scopePaths: args.scope,
|
|
487
|
+
acceptanceText: args.acceptance,
|
|
488
|
+
generation: args.generation,
|
|
489
|
+
});
|
|
490
|
+
const started = startControllerRun(store, {
|
|
491
|
+
repo: args.repo,
|
|
492
|
+
baseSha: args.baseSha,
|
|
493
|
+
headSha: args.headSha,
|
|
494
|
+
treeHash: args.treeHash,
|
|
495
|
+
prBodyHash: args.prBodyHash,
|
|
496
|
+
prNodeId: args.prNodeId,
|
|
497
|
+
criteria,
|
|
498
|
+
skillVersion: args.skillVersion,
|
|
499
|
+
policyVersion: args.policyVersion,
|
|
500
|
+
approvedRevisionSha: args.approvedRevision,
|
|
501
|
+
runId: args.runId ?? undefined,
|
|
502
|
+
});
|
|
503
|
+
if (!started.ok) {
|
|
504
|
+
emit(args.json, started.decision, started.decision.message, true);
|
|
505
|
+
return 1;
|
|
506
|
+
}
|
|
507
|
+
const payload = { ok: started.ok, runId: started.runId, hint: "run id is a lookup hint only" };
|
|
508
|
+
emit(args.json, payload, `started ${started.runId} (lookup hint only; private store is authoritative)`, false);
|
|
509
|
+
return 0;
|
|
510
|
+
}
|
|
511
|
+
if (isDirectEntrypoint(import.meta.url)) {
|
|
512
|
+
process.exit(run(process.argv.slice(2)));
|
|
513
|
+
}
|
|
514
|
+
//# sourceMappingURL=pre-pr-run.js.map
|
|
@@ -6,9 +6,12 @@ interface ParsedArgs {
|
|
|
6
6
|
issue: number | null;
|
|
7
7
|
quiet: boolean;
|
|
8
8
|
skipGh: boolean;
|
|
9
|
+
/** Opt-in discovery when --issue is set; unscoped defaults to on (#3495). */
|
|
10
|
+
discover: boolean | null;
|
|
11
|
+
enforce: boolean;
|
|
9
12
|
error?: string;
|
|
10
13
|
}
|
|
11
|
-
/** Parse verify-completed-tracked CLI args (#3264 / #3476). */
|
|
14
|
+
/** Parse verify-completed-tracked CLI args (#3264 / #3476 / #3495). */
|
|
12
15
|
export declare function parseArgs(argv: string[]): ParsedArgs;
|
|
13
16
|
/** Run the gate and return the process exit code. */
|
|
14
17
|
export declare function run(argv: string[]): number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
4
|
+
import { COMPLETED_TRACKED_PROGRESS_MIN_BLOBS, COMPLETED_TRACKED_PROGRESS_THRESHOLD_MS, evaluateCompletedTracked, evaluateShippedClosedDiscovery, shouldAnnounceProgress, shouldAnnounceUpFrontCount, } from "@deftai/directive-core/lifecycle";
|
|
5
5
|
function parseIssueNumber(raw) {
|
|
6
6
|
const trimmed = raw.startsWith("#") ? raw.slice(1) : raw;
|
|
7
7
|
if (!/^\d+$/.test(trimmed)) {
|
|
@@ -10,7 +10,7 @@ function parseIssueNumber(raw) {
|
|
|
10
10
|
const value = Number(trimmed);
|
|
11
11
|
return Number.isInteger(value) && value > 0 ? value : null;
|
|
12
12
|
}
|
|
13
|
-
/** Parse verify-completed-tracked CLI args (#3264 / #3476). */
|
|
13
|
+
/** Parse verify-completed-tracked CLI args (#3264 / #3476 / #3495). */
|
|
14
14
|
export function parseArgs(argv) {
|
|
15
15
|
const parsed = {
|
|
16
16
|
projectRoot: ".",
|
|
@@ -19,6 +19,8 @@ export function parseArgs(argv) {
|
|
|
19
19
|
issue: null,
|
|
20
20
|
quiet: false,
|
|
21
21
|
skipGh: false,
|
|
22
|
+
discover: null,
|
|
23
|
+
enforce: false,
|
|
22
24
|
};
|
|
23
25
|
for (let i = 0; i < argv.length; i += 1) {
|
|
24
26
|
const arg = argv[i];
|
|
@@ -28,6 +30,15 @@ export function parseArgs(argv) {
|
|
|
28
30
|
else if (arg === "--skip-gh") {
|
|
29
31
|
parsed.skipGh = true;
|
|
30
32
|
}
|
|
33
|
+
else if (arg === "--discover") {
|
|
34
|
+
parsed.discover = true;
|
|
35
|
+
}
|
|
36
|
+
else if (arg === "--no-discover") {
|
|
37
|
+
parsed.discover = false;
|
|
38
|
+
}
|
|
39
|
+
else if (arg === "--enforce") {
|
|
40
|
+
parsed.enforce = true;
|
|
41
|
+
}
|
|
31
42
|
else if (arg === "--project-root") {
|
|
32
43
|
const value = argv[i + 1];
|
|
33
44
|
if (value === undefined) {
|
|
@@ -87,6 +98,16 @@ export function parseArgs(argv) {
|
|
|
87
98
|
}
|
|
88
99
|
return parsed;
|
|
89
100
|
}
|
|
101
|
+
function shouldRunDiscovery(args) {
|
|
102
|
+
if (args.discover === false) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (args.discover === true || args.enforce) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
// Warn-first default: unscoped corpus scans discover; --issue N stays land-only.
|
|
109
|
+
return args.issue === null;
|
|
110
|
+
}
|
|
90
111
|
/** Run the gate and return the process exit code. */
|
|
91
112
|
export function run(argv) {
|
|
92
113
|
const args = parseArgs(argv);
|
|
@@ -97,7 +118,7 @@ export function run(argv) {
|
|
|
97
118
|
const projectRoot = resolve(args.projectRoot);
|
|
98
119
|
const thresholdMs = resolveProgressThresholdMs();
|
|
99
120
|
let announced = false;
|
|
100
|
-
const
|
|
121
|
+
const land = evaluateCompletedTracked(projectRoot, {
|
|
101
122
|
quiet: args.quiet,
|
|
102
123
|
repo: args.repo,
|
|
103
124
|
tip: args.tip,
|
|
@@ -117,15 +138,45 @@ export function run(argv) {
|
|
|
117
138
|
writeProgress(event);
|
|
118
139
|
},
|
|
119
140
|
});
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
141
|
+
let discoveryCode = 0;
|
|
142
|
+
let discoveryMessage = "";
|
|
143
|
+
let discoveryStream = "none";
|
|
144
|
+
if (shouldRunDiscovery(args)) {
|
|
145
|
+
const discovery = evaluateShippedClosedDiscovery(projectRoot, {
|
|
146
|
+
quiet: args.quiet,
|
|
147
|
+
repo: args.repo,
|
|
148
|
+
tip: args.tip,
|
|
149
|
+
skipGh: args.skipGh,
|
|
150
|
+
enforce: args.enforce,
|
|
151
|
+
});
|
|
152
|
+
discoveryCode = discovery.code;
|
|
153
|
+
discoveryMessage = discovery.message;
|
|
154
|
+
discoveryStream = discovery.stream;
|
|
155
|
+
}
|
|
156
|
+
// Scoped land fail-closed must not be softened by discovery warn (#3495 Reject).
|
|
157
|
+
if (land.message.length > 0) {
|
|
158
|
+
if (land.stream === "stdout") {
|
|
159
|
+
process.stdout.write(`${land.message}\n`);
|
|
123
160
|
}
|
|
124
|
-
else if (
|
|
125
|
-
process.stderr.write(`${
|
|
161
|
+
else if (land.stream === "stderr") {
|
|
162
|
+
process.stderr.write(`${land.message}\n`);
|
|
126
163
|
}
|
|
127
164
|
}
|
|
128
|
-
|
|
165
|
+
if (discoveryMessage.length > 0) {
|
|
166
|
+
if (discoveryStream === "stdout") {
|
|
167
|
+
process.stdout.write(`${discoveryMessage}\n`);
|
|
168
|
+
}
|
|
169
|
+
else if (discoveryStream === "stderr") {
|
|
170
|
+
process.stderr.write(`${discoveryMessage}\n`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (land.code === 2 || discoveryCode === 2) {
|
|
174
|
+
return 2;
|
|
175
|
+
}
|
|
176
|
+
if (land.code === 1 || discoveryCode === 1) {
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
return 0;
|
|
129
180
|
}
|
|
130
181
|
function resolveProgressThresholdMs() {
|
|
131
182
|
const raw = process.env.DEFT_COMPLETED_TRACKED_PROGRESS_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.120.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.
|
|
39
|
-
"@deftai/directive-content": "^0.
|
|
38
|
+
"@deftai/directive-core": "^0.120.0",
|
|
39
|
+
"@deftai/directive-content": "^0.120.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsc -b",
|