@deftai/directive-core 0.79.1 → 0.79.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache/fetch.js +14 -4
- package/dist/capacity/backfill.js +4 -2
- package/dist/category-b-namespace/index.js +31 -6
- package/dist/check/orchestrator.d.ts +4 -0
- package/dist/check/orchestrator.js +9 -1
- package/dist/content-contracts/skills/skill-frontmatter.js +6 -2
- package/dist/eval/health.js +8 -1
- package/dist/eval/run.js +2 -0
- package/dist/fs/projection-containment.d.ts +11 -0
- package/dist/fs/projection-containment.js +62 -1
- package/dist/hooks/dispatcher.d.ts +1 -1
- package/dist/hooks/dispatcher.js +66 -7
- package/dist/init-deposit/hygiene.js +3 -0
- package/dist/intake/reconcile-issues.d.ts +1 -1
- package/dist/intake/reconcile-issues.js +25 -2
- package/dist/issue-sync/sync-from-xbrief-cli.d.ts +1 -0
- package/dist/issue-sync/sync-from-xbrief-cli.js +3 -0
- package/dist/issue-sync/sync-from-xbrief.d.ts +4 -0
- package/dist/issue-sync/sync-from-xbrief.js +12 -0
- package/dist/lifecycle/event.d.ts +15 -0
- package/dist/lifecycle/event.js +163 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/orchestration/verify-judgment-gates.js +18 -2
- package/dist/platform/resolve-version.d.ts +2 -6
- package/dist/platform/resolve-version.js +3 -129
- package/dist/pr-merge-readiness/compute.d.ts +2 -1
- package/dist/pr-merge-readiness/compute.js +23 -6
- package/dist/pr-merge-readiness/evaluate.d.ts +2 -1
- package/dist/pr-merge-readiness/evaluate.js +46 -31
- package/dist/pr-merge-readiness/gh.js +13 -5
- package/dist/pr-merge-readiness/greptile-inline.d.ts +27 -0
- package/dist/pr-merge-readiness/greptile-inline.js +239 -0
- package/dist/pr-merge-readiness/index.d.ts +1 -0
- package/dist/pr-merge-readiness/index.js +1 -0
- package/dist/pr-merge-readiness/mergeability.d.ts +2 -1
- package/dist/pr-merge-readiness/mergeability.js +7 -1
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.d.ts +18 -0
- package/dist/pr-merge-readiness/test-gh-fixtures.helpers.js +76 -0
- package/dist/pr-monitor/monitor.js +10 -1
- package/dist/pr-watch/constants.d.ts +5 -0
- package/dist/pr-watch/constants.js +27 -0
- package/dist/pr-watch/index.d.ts +1 -1
- package/dist/pr-watch/index.js +1 -1
- package/dist/pr-watch/main.d.ts +3 -0
- package/dist/pr-watch/main.js +29 -3
- package/dist/release/constants.d.ts +5 -0
- package/dist/release/constants.js +14 -2
- package/dist/release/flags.js +16 -0
- package/dist/release/main.js +7 -0
- package/dist/release/pipeline.js +7 -3
- package/dist/release/preflight.js +8 -1
- package/dist/release/skip-ci-incident.d.ts +22 -0
- package/dist/release/skip-ci-incident.js +68 -0
- package/dist/release/types.d.ts +2 -0
- package/dist/release/version.js +68 -32
- package/dist/release-e2e/entrypoint-worker.js +1 -0
- package/dist/release-e2e/entrypoint.js +13 -0
- package/dist/review-monitor/constants.d.ts +14 -0
- package/dist/review-monitor/constants.js +55 -0
- package/dist/review-monitor/index.d.ts +5 -0
- package/dist/review-monitor/index.js +5 -0
- package/dist/review-monitor/record.d.ts +50 -0
- package/dist/review-monitor/record.js +178 -0
- package/dist/review-monitor/tier-detection.d.ts +15 -0
- package/dist/review-monitor/tier-detection.js +61 -0
- package/dist/review-monitor/verify.d.ts +32 -0
- package/dist/review-monitor/verify.js +171 -0
- package/dist/scope/demote.js +20 -0
- package/dist/swarm/worktrees.d.ts +3 -0
- package/dist/swarm/worktrees.js +18 -2
- package/dist/task-surface/index.js +3 -2
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +16 -8
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +41 -14
- package/dist/xbrief-migrate/migration-containment.js +9 -25
- package/package.json +7 -3
package/dist/release/main.js
CHANGED
|
@@ -2,6 +2,7 @@ import { EXIT_CONFIG_ERROR } from "./constants.js";
|
|
|
2
2
|
import { formatReleaseHelp, parseReleaseFlags } from "./flags.js";
|
|
3
3
|
import { resolveProjectRoot, resolveRepo } from "./paths.js";
|
|
4
4
|
import { runPipeline } from "./pipeline.js";
|
|
5
|
+
import { validateSkipCiIncident } from "./skip-ci-incident.js";
|
|
5
6
|
import { validateVersion } from "./version.js";
|
|
6
7
|
export function cmdRelease(args, seams = {}) {
|
|
7
8
|
const flags = parseReleaseFlags(args);
|
|
@@ -27,6 +28,11 @@ export function cmdRelease(args, seams = {}) {
|
|
|
27
28
|
}
|
|
28
29
|
const projectRoot = resolveProjectRoot(flags.projectRoot);
|
|
29
30
|
const repo = resolveRepo(flags.repo, projectRoot, seams);
|
|
31
|
+
const skipCiGate = validateSkipCiIncident(flags.skipCi, flags.allowSkipCiIssue, process.env);
|
|
32
|
+
if (skipCiGate.kind === "invalid") {
|
|
33
|
+
process.stderr.write(`release: error: ${skipCiGate.reason}\n`);
|
|
34
|
+
return EXIT_CONFIG_ERROR;
|
|
35
|
+
}
|
|
30
36
|
const config = {
|
|
31
37
|
version: flags.version,
|
|
32
38
|
repo,
|
|
@@ -42,6 +48,7 @@ export function cmdRelease(args, seams = {}) {
|
|
|
42
48
|
summary: flags.summary,
|
|
43
49
|
allowVbriefDrift: flags.allowVbriefDrift,
|
|
44
50
|
allowCoverageDebtIssue: flags.allowCoverageDebtIssue,
|
|
51
|
+
allowSkipCiIssue: flags.allowSkipCiIssue,
|
|
45
52
|
};
|
|
46
53
|
return runPipeline(config, seams);
|
|
47
54
|
}
|
package/dist/release/pipeline.js
CHANGED
|
@@ -2,12 +2,13 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { assertProjectionContained } from "../fs/projection-containment.js";
|
|
4
4
|
import { prependUpgradeBanner, promoteChangelog, sectionForVersion } from "./changelog.js";
|
|
5
|
-
import { EXIT_CONFIG_ERROR, EXIT_OK, EXIT_VIOLATION, RELEASE_ARTIFACTS, TOTAL_STEPS, VERIFY_DRAFT_INTERVAL_SECONDS, VERIFY_DRAFT_MAX_ATTEMPTS, } from "./constants.js";
|
|
5
|
+
import { EXIT_CONFIG_ERROR, EXIT_OK, EXIT_VIOLATION, RELEASE_ARTIFACTS, RELEASE_CHECK_TIMEOUT_MINUTES, TOTAL_STEPS, VERIFY_DRAFT_INTERVAL_SECONDS, VERIFY_DRAFT_MAX_ATTEMPTS, } from "./constants.js";
|
|
6
6
|
import { checkTagAvailable, createGithubRelease, readTextFile, verifyReleaseDraft } from "./gh.js";
|
|
7
7
|
import { checkGitClean, commitReleaseArtifacts, createTag, currentBranch, pushRelease, releaseCommitSubject, } from "./git.js";
|
|
8
8
|
import { checkVbriefLifecycleSyncNative, refreshRoadmapNative, runBuildNative, } from "./native-steps.js";
|
|
9
9
|
import { todayIso } from "./paths.js";
|
|
10
10
|
import { runReleaseCheck } from "./preflight.js";
|
|
11
|
+
import { formatSkipCiIncidentWarning } from "./skip-ci-incident.js";
|
|
11
12
|
import { isPrereleaseTag } from "./version.js";
|
|
12
13
|
export function emit(step, label, status, target = process.stderr) {
|
|
13
14
|
target.write(`[${step}/${TOTAL_STEPS}] ${label}... ${status}\n`);
|
|
@@ -108,13 +109,16 @@ export function runPipeline(config, seams = {}) {
|
|
|
108
109
|
// golden-diff release-parity gate stays green until the oracle is retired.
|
|
109
110
|
label = "Pre-flight CI (task ci:local | fallback task check)";
|
|
110
111
|
if (config.skipCi) {
|
|
112
|
+
if (config.allowSkipCiIssue !== null && config.allowSkipCiIssue > 0) {
|
|
113
|
+
process.stderr.write(formatSkipCiIncidentWarning(config.allowSkipCiIssue));
|
|
114
|
+
}
|
|
111
115
|
emit(5, label, "SKIP (--skip-ci)");
|
|
112
116
|
}
|
|
113
117
|
else if (config.dryRun) {
|
|
114
118
|
const debtNote = config.allowCoverageDebtIssue !== null
|
|
115
119
|
? `; would pass --allow-coverage-debt=#${config.allowCoverageDebtIssue} to task check`
|
|
116
120
|
: "";
|
|
117
|
-
emit(5, label, `DRYRUN (would run task ci:local with task check fallback${debtNote})`);
|
|
121
|
+
emit(5, label, `DRYRUN (would run task ci:local with task check fallback${debtNote}; hard timeout ${RELEASE_CHECK_TIMEOUT_MINUTES}m)`);
|
|
118
122
|
}
|
|
119
123
|
else {
|
|
120
124
|
const [ok, reason] = runCiFn(projectRoot, config.allowCoverageDebtIssue);
|
|
@@ -128,7 +132,7 @@ export function runPipeline(config, seams = {}) {
|
|
|
128
132
|
const debtHint = config.allowCoverageDebtIssue === null
|
|
129
133
|
? "; pass --allow-coverage-debt=#N only after operator review"
|
|
130
134
|
: "";
|
|
131
|
-
emit(5, label, `FAIL (${reason}${debtHint})`);
|
|
135
|
+
emit(5, label, `FAIL (${reason}${debtHint}; Step 5 hard timeout is ${RELEASE_CHECK_TIMEOUT_MINUTES}m — cancel hung vitest and see docs/RELEASING.md § Vitest coverage hang recovery)`);
|
|
132
136
|
return EXIT_VIOLATION;
|
|
133
137
|
}
|
|
134
138
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* dispatches `check:framework-source`.
|
|
9
9
|
*/
|
|
10
10
|
import { dispatchTaskCheck } from "../check/orchestrator.js";
|
|
11
|
-
import { COVERAGE_DEBT_ENV, RELEASE_PREFLIGHT_ENV } from "./constants.js";
|
|
11
|
+
import { COVERAGE_DEBT_ENV, RELEASE_CHECK_TIMEOUT_MS, RELEASE_PREFLIGHT_ENV } from "./constants.js";
|
|
12
12
|
import { releaseSubprocessEnv } from "./git.js";
|
|
13
13
|
/** Step-5-only env: branch bypass plus release pre-flight cache staleness tolerance (#2386). */
|
|
14
14
|
export function releaseCheckEnv(options = {}) {
|
|
@@ -40,6 +40,7 @@ export function runReleaseCheck(projectRoot, seams = {}, allowCoverageDebtIssue
|
|
|
40
40
|
const dispatch = seams.dispatchCheck ?? dispatchTaskCheck;
|
|
41
41
|
const checkSeams = {
|
|
42
42
|
...seams.checkSeams,
|
|
43
|
+
timeoutMs: seams.checkSeams?.timeoutMs ?? RELEASE_CHECK_TIMEOUT_MS,
|
|
43
44
|
env: releaseCheckEnv({
|
|
44
45
|
base: seams.checkSeams?.env ?? process.env,
|
|
45
46
|
allowCoverageDebtIssue,
|
|
@@ -49,6 +50,12 @@ export function runReleaseCheck(projectRoot, seams = {}, allowCoverageDebtIssue
|
|
|
49
50
|
if (code === 0) {
|
|
50
51
|
return [true, "ran native TypeScript task check"];
|
|
51
52
|
}
|
|
53
|
+
if (code === 124) {
|
|
54
|
+
return [
|
|
55
|
+
false,
|
|
56
|
+
`task check timed out after ${RELEASE_CHECK_TIMEOUT_MS / 60_000}m (vitest coverage hang — see docs/RELEASING.md)`,
|
|
57
|
+
];
|
|
58
|
+
}
|
|
52
59
|
return [false, `task check failed (exit ${code})`];
|
|
53
60
|
}
|
|
54
61
|
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Parse `#2652`, `2652`, or bare numeric strings for skip-ci incident citation. */
|
|
2
|
+
export declare function parseSkipCiIncidentIssueNumber(raw: string): number | null;
|
|
3
|
+
export type SkipCiIncidentResolution = {
|
|
4
|
+
readonly kind: "none";
|
|
5
|
+
} | {
|
|
6
|
+
readonly kind: "valid";
|
|
7
|
+
readonly issue: number;
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: "invalid";
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
};
|
|
12
|
+
/** Set by `task release:e2e` worker subprocesses — permits `--skip-ci` without issue citation. */
|
|
13
|
+
export declare const RELEASE_E2E_ENV = "DEFT_RELEASE_E2E";
|
|
14
|
+
export declare function parseSkipCiIncidentArgv(argv: readonly string[]): SkipCiIncidentResolution;
|
|
15
|
+
/**
|
|
16
|
+
* Production `--skip-ci` is an incident (#2652): require `--allow-skip-ci=#N` or
|
|
17
|
+
* run inside `task release:e2e` (`DEFT_RELEASE_E2E=1`).
|
|
18
|
+
*/
|
|
19
|
+
export declare function validateSkipCiIncident(skipCi: boolean, allowSkipCiIssue: number | null, env?: NodeJS.ProcessEnv): SkipCiIncidentResolution;
|
|
20
|
+
/** Loud stderr banner when Step 5 is skipped with operator acknowledgment (#2652). */
|
|
21
|
+
export declare function formatSkipCiIncidentWarning(issue: number): string;
|
|
22
|
+
//# sourceMappingURL=skip-ci-incident.d.ts.map
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** Parse `#2652`, `2652`, or bare numeric strings for skip-ci incident citation. */
|
|
2
|
+
export function parseSkipCiIncidentIssueNumber(raw) {
|
|
3
|
+
const trimmed = raw.trim();
|
|
4
|
+
if (!trimmed)
|
|
5
|
+
return null;
|
|
6
|
+
const normalized = trimmed.startsWith("#") ? trimmed.slice(1) : trimmed;
|
|
7
|
+
if (!/^\d+$/.test(normalized))
|
|
8
|
+
return null;
|
|
9
|
+
const issue = Number.parseInt(normalized, 10);
|
|
10
|
+
return Number.isFinite(issue) && issue > 0 ? issue : null;
|
|
11
|
+
}
|
|
12
|
+
const SKIP_CI_FLAG = "--allow-skip-ci";
|
|
13
|
+
/** Set by `task release:e2e` worker subprocesses — permits `--skip-ci` without issue citation. */
|
|
14
|
+
export const RELEASE_E2E_ENV = "DEFT_RELEASE_E2E";
|
|
15
|
+
export function parseSkipCiIncidentArgv(argv) {
|
|
16
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
17
|
+
const token = argv[i] ?? "";
|
|
18
|
+
if (token === SKIP_CI_FLAG) {
|
|
19
|
+
const next = argv[i + 1];
|
|
20
|
+
if (next === undefined || next.startsWith("-")) {
|
|
21
|
+
return { kind: "invalid", reason: `${SKIP_CI_FLAG} requires an issue number (#N)` };
|
|
22
|
+
}
|
|
23
|
+
const issue = parseSkipCiIncidentIssueNumber(next);
|
|
24
|
+
return issue === null
|
|
25
|
+
? { kind: "invalid", reason: `${SKIP_CI_FLAG} value must be #N or N` }
|
|
26
|
+
: { kind: "valid", issue };
|
|
27
|
+
}
|
|
28
|
+
if (token.startsWith(`${SKIP_CI_FLAG}=`)) {
|
|
29
|
+
const value = token.slice(SKIP_CI_FLAG.length + 1);
|
|
30
|
+
const issue = parseSkipCiIncidentIssueNumber(value);
|
|
31
|
+
return issue === null
|
|
32
|
+
? { kind: "invalid", reason: `${SKIP_CI_FLAG}= value must be #N or N` }
|
|
33
|
+
: { kind: "valid", issue };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { kind: "none" };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Production `--skip-ci` is an incident (#2652): require `--allow-skip-ci=#N` or
|
|
40
|
+
* run inside `task release:e2e` (`DEFT_RELEASE_E2E=1`).
|
|
41
|
+
*/
|
|
42
|
+
export function validateSkipCiIncident(skipCi, allowSkipCiIssue, env = process.env) {
|
|
43
|
+
if (!skipCi) {
|
|
44
|
+
return { kind: "none" };
|
|
45
|
+
}
|
|
46
|
+
if (allowSkipCiIssue !== null) {
|
|
47
|
+
return { kind: "valid", issue: allowSkipCiIssue };
|
|
48
|
+
}
|
|
49
|
+
if (env[RELEASE_E2E_ENV] === "1") {
|
|
50
|
+
return { kind: "valid", issue: 0 };
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
kind: "invalid",
|
|
54
|
+
reason: "production --skip-ci skips Step 5 vitest coverage and ships untested npm builds (#2652). " +
|
|
55
|
+
"Pass --allow-skip-ci=#N citing the tracked incident after operator review, " +
|
|
56
|
+
"or use `task release:e2e` for rehearsal-only skips.",
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Loud stderr banner when Step 5 is skipped with operator acknowledgment (#2652). */
|
|
60
|
+
export function formatSkipCiIncidentWarning(issue) {
|
|
61
|
+
const cite = issue > 0 ? `#${issue}` : "release:e2e rehearsal";
|
|
62
|
+
return (`\n` +
|
|
63
|
+
`*** WARNING: release Step 5 CI/coverage SKIPPED (--skip-ci) ***\n` +
|
|
64
|
+
`*** This cut will NOT be validated by vitest coverage / task check. ***\n` +
|
|
65
|
+
`*** Incident citation: ${cite} — npm publish proceeds UNTESTED (#2652). ***\n` +
|
|
66
|
+
`*** Next production patch MUST cut without --skip-ci once the hang is fixed. ***\n\n`);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=skip-ci-incident.js.map
|
package/dist/release/types.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface ReleaseConfig {
|
|
|
13
13
|
readonly summary: string | null;
|
|
14
14
|
readonly allowVbriefDrift: boolean;
|
|
15
15
|
readonly allowCoverageDebtIssue: number | null;
|
|
16
|
+
readonly allowSkipCiIssue: number | null;
|
|
16
17
|
}
|
|
17
18
|
export interface ReleaseFlags {
|
|
18
19
|
readonly help: boolean;
|
|
@@ -30,6 +31,7 @@ export interface ReleaseFlags {
|
|
|
30
31
|
readonly draft: boolean;
|
|
31
32
|
readonly summary: string | null;
|
|
32
33
|
readonly allowCoverageDebtIssue: number | null;
|
|
34
|
+
readonly allowSkipCiIssue: number | null;
|
|
33
35
|
readonly unknown: readonly string[];
|
|
34
36
|
}
|
|
35
37
|
export interface SpawnResult {
|
package/dist/release/version.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const PEP440_TAG_RE = /^v?(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:-(?<kind>rc|alpha|beta|test)\.(?<num>\d+))?$/;
|
|
2
1
|
const PRE_KIND_MAP = {
|
|
3
2
|
alpha: "a",
|
|
4
3
|
beta: "b",
|
|
@@ -17,6 +16,55 @@ export class NonPublishableVersionError extends Error {
|
|
|
17
16
|
this.name = "NonPublishableVersionError";
|
|
18
17
|
}
|
|
19
18
|
}
|
|
19
|
+
/** Parse `[v]X.Y.Z[-(rc|alpha|beta|test).N]` via linear scan. */
|
|
20
|
+
function parseReleaseTag(version) {
|
|
21
|
+
let i = 0;
|
|
22
|
+
if (version[i] === "v" || version[i] === "V")
|
|
23
|
+
i += 1;
|
|
24
|
+
const readInt = () => {
|
|
25
|
+
const ch = version[i] ?? "";
|
|
26
|
+
if (i >= version.length || ch < "0" || ch > "9")
|
|
27
|
+
return null;
|
|
28
|
+
let n = 0;
|
|
29
|
+
while (i < version.length) {
|
|
30
|
+
const digit = version[i] ?? "";
|
|
31
|
+
if (digit < "0" || digit > "9")
|
|
32
|
+
break;
|
|
33
|
+
n = n * 10 + Number(digit);
|
|
34
|
+
i += 1;
|
|
35
|
+
}
|
|
36
|
+
return n;
|
|
37
|
+
};
|
|
38
|
+
const major = readInt();
|
|
39
|
+
if (major === null || version[i] !== ".")
|
|
40
|
+
return null;
|
|
41
|
+
i += 1;
|
|
42
|
+
const minor = readInt();
|
|
43
|
+
if (minor === null || version[i] !== ".")
|
|
44
|
+
return null;
|
|
45
|
+
i += 1;
|
|
46
|
+
const patch = readInt();
|
|
47
|
+
if (patch === null)
|
|
48
|
+
return null;
|
|
49
|
+
if (i >= version.length)
|
|
50
|
+
return { major, minor, patch, kind: null, num: null };
|
|
51
|
+
if (version[i] !== "-")
|
|
52
|
+
return null;
|
|
53
|
+
i += 1;
|
|
54
|
+
const kindStart = i;
|
|
55
|
+
while (i < version.length && version[i] !== ".")
|
|
56
|
+
i += 1;
|
|
57
|
+
if (i >= version.length || version[i] !== ".")
|
|
58
|
+
return null;
|
|
59
|
+
const kind = version.slice(kindStart, i);
|
|
60
|
+
if (!["rc", "alpha", "beta", "test"].includes(kind))
|
|
61
|
+
return null;
|
|
62
|
+
i += 1;
|
|
63
|
+
const num = readInt();
|
|
64
|
+
if (num === null || i !== version.length)
|
|
65
|
+
return null;
|
|
66
|
+
return { major, minor, patch, kind, num };
|
|
67
|
+
}
|
|
20
68
|
/** Raise Error when version does not match strict X.Y.Z semver. */
|
|
21
69
|
export function validateVersion(version) {
|
|
22
70
|
if (!/^\d+\.\d+\.\d+$/.test(version)) {
|
|
@@ -27,7 +75,7 @@ export function validateVersion(version) {
|
|
|
27
75
|
/** Return true when version carries a SemVer pre-release suffix (#425). */
|
|
28
76
|
export function isPrereleaseTag(version) {
|
|
29
77
|
let candidate = version.trim();
|
|
30
|
-
if (candidate.startsWith("v")) {
|
|
78
|
+
if (candidate.startsWith("v") || candidate.startsWith("V")) {
|
|
31
79
|
candidate = candidate.slice(1);
|
|
32
80
|
}
|
|
33
81
|
return candidate.includes("-");
|
|
@@ -41,32 +89,26 @@ export function toPep440(version) {
|
|
|
41
89
|
if (!candidate) {
|
|
42
90
|
throw new Error("version must be a non-empty string");
|
|
43
91
|
}
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
92
|
+
const parsed = parseReleaseTag(candidate);
|
|
93
|
+
if (parsed === null) {
|
|
46
94
|
throw new Error(`Cannot normalize '${candidate}' to PEP 440: expected ` +
|
|
47
95
|
"[v]X.Y.Z or [v]X.Y.Z-(rc|alpha|beta|test).N");
|
|
48
96
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
const patch = Number(match.groups.patch);
|
|
52
|
-
const base = `${major}.${minor}.${patch}`;
|
|
53
|
-
const kind = match.groups.kind;
|
|
54
|
-
if (kind === undefined) {
|
|
97
|
+
const base = `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
|
98
|
+
if (parsed.kind === null)
|
|
55
99
|
return base;
|
|
56
|
-
|
|
57
|
-
if (NON_PUBLISHABLE_KINDS.has(kind)) {
|
|
100
|
+
if (NON_PUBLISHABLE_KINDS.has(parsed.kind)) {
|
|
58
101
|
throw new NonPublishableVersionError(`Version '${candidate}' carries non-publishable pre-release ` +
|
|
59
|
-
`tag '${kind}'.${
|
|
102
|
+
`tag '${parsed.kind}'.${parsed.num} -- release pipeline MUST ` +
|
|
60
103
|
"skip pyproject.toml [project].version sync for this tag.");
|
|
61
104
|
}
|
|
62
|
-
const pepKind = PRE_KIND_MAP[kind];
|
|
105
|
+
const pepKind = PRE_KIND_MAP[parsed.kind];
|
|
63
106
|
if (pepKind === undefined) {
|
|
64
|
-
throw new Error(`Unmapped pre-release kind '${kind}' for version '${candidate}'; ` +
|
|
65
|
-
"add it to
|
|
66
|
-
"
|
|
107
|
+
throw new Error(`Unmapped pre-release kind '${parsed.kind}' for version '${candidate}'; ` +
|
|
108
|
+
"add it to PRE_KIND_MAP or NON_PUBLISHABLE_KINDS to keep the parser " +
|
|
109
|
+
"in lockstep with the publishability classifier.");
|
|
67
110
|
}
|
|
68
|
-
|
|
69
|
-
return `${base}${pepKind}${pepNum}`;
|
|
111
|
+
return `${base}${pepKind}${parsed.num}`;
|
|
70
112
|
}
|
|
71
113
|
export function isPublishable(version) {
|
|
72
114
|
try {
|
|
@@ -79,25 +121,19 @@ export function isPublishable(version) {
|
|
|
79
121
|
}
|
|
80
122
|
function publishableVersionSortKey(version) {
|
|
81
123
|
const candidate = version.trim();
|
|
82
|
-
const
|
|
83
|
-
if (
|
|
84
|
-
throw new Error(`Cannot compare '${candidate}': expected
|
|
124
|
+
const parsed = parseReleaseTag(candidate);
|
|
125
|
+
if (parsed === null) {
|
|
126
|
+
throw new Error(`Cannot compare '${candidate}': expected [v]X.Y.Z or [v]X.Y.Z-(rc|alpha|beta).N`);
|
|
85
127
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
throw new NonPublishableVersionError(`Version '${candidate}' carries non-publishable pre-release tag '${kind}'.${match.groups.num}.`);
|
|
128
|
+
if (parsed.kind !== null && NON_PUBLISHABLE_KINDS.has(parsed.kind)) {
|
|
129
|
+
throw new NonPublishableVersionError(`Version '${candidate}' carries non-publishable pre-release tag '${parsed.kind}'.${parsed.num}.`);
|
|
89
130
|
}
|
|
131
|
+
const kind = parsed.kind ?? "";
|
|
90
132
|
const rank = PRERELEASE_RANK[kind];
|
|
91
133
|
if (rank === undefined) {
|
|
92
134
|
throw new Error(`Cannot compare '${candidate}': unsupported pre-release kind '${kind}'.`);
|
|
93
135
|
}
|
|
94
|
-
return [
|
|
95
|
-
Number(match.groups.major),
|
|
96
|
-
Number(match.groups.minor),
|
|
97
|
-
Number(match.groups.patch),
|
|
98
|
-
rank,
|
|
99
|
-
Number(match.groups.num ?? 0),
|
|
100
|
-
];
|
|
136
|
+
return [parsed.major, parsed.minor, parsed.patch, rank, parsed.num ?? 0];
|
|
101
137
|
}
|
|
102
138
|
/** Compare two publishable Deft release versions using stable/prerelease ordering. */
|
|
103
139
|
export function comparePublishableVersions(left, right) {
|
|
@@ -2,6 +2,7 @@ import { cmdRelease } from "../release/main.js";
|
|
|
2
2
|
import { rollbackMain } from "./rollback-bridge.js";
|
|
3
3
|
export function runWorkerEntrypoint(data) {
|
|
4
4
|
process.env.DEFT_PROJECT_ROOT = data.cloneDir;
|
|
5
|
+
process.env.DEFT_RELEASE_E2E = "1";
|
|
5
6
|
const stdoutChunks = [];
|
|
6
7
|
const stderrChunks = [];
|
|
7
8
|
const prevStdout = process.stdout.write.bind(process.stdout);
|
|
@@ -3,9 +3,11 @@ import { sep } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { MessageChannel, receiveMessageOnPort, Worker } from "node:worker_threads";
|
|
5
5
|
import { cmdRelease } from "../release/main.js";
|
|
6
|
+
import { RELEASE_E2E_ENV } from "../release/skip-ci-incident.js";
|
|
6
7
|
import { ENTRYPOINT_TIMEOUT_EXIT_CODE, EXIT_VIOLATION, RELEASE_ENTRYPOINT_TIMEOUT_SECONDS, ROLLBACK_ENTRYPOINT_TIMEOUT_SECONDS, } from "./constants.js";
|
|
7
8
|
import { rollbackMain } from "./rollback-bridge.js";
|
|
8
9
|
let activeRestoreOwner = null;
|
|
10
|
+
let priorReleaseE2eEnv;
|
|
9
11
|
/** @internal Test hook for restore-owner branch coverage. */
|
|
10
12
|
export function restoreProcessStateForTest(restoreOwner, oldCwd, oldProjectRoot) {
|
|
11
13
|
restoreProcessState(restoreOwner, oldCwd, oldProjectRoot);
|
|
@@ -22,9 +24,18 @@ function restoreProcessState(restoreOwner, oldCwd, oldProjectRoot) {
|
|
|
22
24
|
else {
|
|
23
25
|
process.env.DEFT_PROJECT_ROOT = oldProjectRoot;
|
|
24
26
|
}
|
|
27
|
+
if (priorReleaseE2eEnv === undefined) {
|
|
28
|
+
delete process.env[RELEASE_E2E_ENV];
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
process.env[RELEASE_E2E_ENV] = priorReleaseE2eEnv;
|
|
32
|
+
}
|
|
33
|
+
priorReleaseE2eEnv = undefined;
|
|
25
34
|
}
|
|
26
35
|
function activateProcessState(restoreOwner, cloneDir) {
|
|
27
36
|
activeRestoreOwner = restoreOwner;
|
|
37
|
+
priorReleaseE2eEnv = process.env[RELEASE_E2E_ENV];
|
|
38
|
+
process.env[RELEASE_E2E_ENV] = "1";
|
|
28
39
|
process.env.DEFT_PROJECT_ROOT = cloneDir;
|
|
29
40
|
mkdirSync(cloneDir, { recursive: true });
|
|
30
41
|
process.chdir(cloneDir);
|
|
@@ -223,6 +234,8 @@ export function defaultRollbackEntrypoint(argv) {
|
|
|
223
234
|
return rollbackMain(argv);
|
|
224
235
|
}
|
|
225
236
|
export function dispatchTaskRelease(cloneDir, version, repo, seams = {}) {
|
|
237
|
+
// Rehearsal --skip-ci is permitted via DEFT_RELEASE_E2E (set in activateProcessState /
|
|
238
|
+
// worker), not a fake production --allow-skip-ci citation (#2652 Greptile).
|
|
226
239
|
const argv = [version, "--repo", repo, "--skip-ci", "--skip-build", "--allow-vbrief-drift"];
|
|
227
240
|
if (seams.releaseEntrypoint) {
|
|
228
241
|
const code = seams.releaseEntrypoint(argv);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Deterministic review-monitor gate (#2655). Three-state exit contract. */
|
|
2
|
+
export declare const EXIT_READY = 0;
|
|
3
|
+
export declare const EXIT_NOT_READY = 1;
|
|
4
|
+
export declare const EXIT_CONFIG_ERROR = 2;
|
|
5
|
+
export declare const SCHEMA_VERSION = 1;
|
|
6
|
+
export declare const REVIEW_MONITOR_FILENAME = "review-monitor.json";
|
|
7
|
+
export declare const REVIEW_MONITOR_RELPATH: readonly [".deft", "review-monitor.json"];
|
|
8
|
+
export declare const MONITORING_TIER_1 = 1;
|
|
9
|
+
export declare const MONITORING_TIER_2 = 2;
|
|
10
|
+
export declare const MONITORING_TIER_3 = 3;
|
|
11
|
+
export declare const DEFAULT_STALE_MINUTES = 30;
|
|
12
|
+
export declare const REVIEW_MONITOR_HELP: string;
|
|
13
|
+
export declare const REGISTER_HELP: string;
|
|
14
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Deterministic review-monitor gate (#2655). Three-state exit contract. */
|
|
2
|
+
export const EXIT_READY = 0;
|
|
3
|
+
export const EXIT_NOT_READY = 1;
|
|
4
|
+
export const EXIT_CONFIG_ERROR = 2;
|
|
5
|
+
export const SCHEMA_VERSION = 1;
|
|
6
|
+
export const REVIEW_MONITOR_FILENAME = "review-monitor.json";
|
|
7
|
+
export const REVIEW_MONITOR_RELPATH = [".deft", REVIEW_MONITOR_FILENAME];
|
|
8
|
+
export const MONITORING_TIER_1 = 1;
|
|
9
|
+
export const MONITORING_TIER_2 = 2;
|
|
10
|
+
export const MONITORING_TIER_3 = 3;
|
|
11
|
+
export const DEFAULT_STALE_MINUTES = 30;
|
|
12
|
+
export const REVIEW_MONITOR_HELP = "usage: task verify:review-monitor -- --pr <N> [options]\n" +
|
|
13
|
+
"\n" +
|
|
14
|
+
"Fail-closed gate: when Tier 1 (sub-agent primitive available), require a\n" +
|
|
15
|
+
"recorded active review-monitor before the parent may yield, Approach-3\n" +
|
|
16
|
+
"sleep-poll, or claim review ownership (#2655).\n" +
|
|
17
|
+
"\n" +
|
|
18
|
+
"options:\n" +
|
|
19
|
+
" -h, --help Show this help and exit 0\n" +
|
|
20
|
+
" --pr N Pull request number (required unless --help)\n" +
|
|
21
|
+
" --repo OWNER/REPO Repository (optional audit field)\n" +
|
|
22
|
+
" --head-sha SHA Expected HEAD SHA (optional freshness check)\n" +
|
|
23
|
+
" --project-root PATH Project root (default: cwd)\n" +
|
|
24
|
+
" --call-site SITE solo | swarm-phase5-6 | swarm-phase6-cascade\n" +
|
|
25
|
+
" --approach3 Assert Approach 3 (blocking poll) intent\n" +
|
|
26
|
+
" --approach3-warned User acknowledged Approach 3 warning (Tier 3 only)\n" +
|
|
27
|
+
" --json Emit structured JSON on stdout\n" +
|
|
28
|
+
"\n" +
|
|
29
|
+
"exit codes:\n" +
|
|
30
|
+
" 0 READY Tier 1 monitor recorded, or Tier 3 Approach 3 with warning ack\n" +
|
|
31
|
+
" 1 NOT READY Tier 1 without monitor, or Approach 3 blocked on Tier 1\n" +
|
|
32
|
+
" 2 CONFIG Usage / path / unreadable state error\n" +
|
|
33
|
+
"\n" +
|
|
34
|
+
"Register a monitor after spawning Approach 1:\n" +
|
|
35
|
+
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> \\\n" +
|
|
36
|
+
" --platform-primitive cursor-task|spawn_subagent|start_agent \\\n" +
|
|
37
|
+
" [--head-sha SHA] [--repo OWNER/REPO]\n";
|
|
38
|
+
export const REGISTER_HELP = "usage: task review-monitor:register -- --pr <N> --monitor-agent-id <id> \\\n" +
|
|
39
|
+
" --platform-primitive <primitive> [options]\n" +
|
|
40
|
+
"\n" +
|
|
41
|
+
"Record an active review-monitor after spawning an Approach 1 poller (#2655).\n" +
|
|
42
|
+
"\n" +
|
|
43
|
+
"required:\n" +
|
|
44
|
+
" --pr N Pull request number\n" +
|
|
45
|
+
" --monitor-agent-id ID Stable poller agent id / Task handle\n" +
|
|
46
|
+
" --platform-primitive P start_agent | spawn_subagent | cursor-task\n" +
|
|
47
|
+
"\n" +
|
|
48
|
+
"options:\n" +
|
|
49
|
+
" --repo OWNER/REPO Repository\n" +
|
|
50
|
+
" --head-sha SHA HEAD SHA at monitor start\n" +
|
|
51
|
+
" --project-root PATH Project root (default: cwd)\n" +
|
|
52
|
+
" --parent-session-id ID Optional parent ritual / session id\n" +
|
|
53
|
+
"\n" +
|
|
54
|
+
"exit codes: 0 registered / 2 config error\n";
|
|
55
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { PlatformPrimitive } from "./tier-detection.js";
|
|
2
|
+
export interface ReviewMonitorRecord {
|
|
3
|
+
readonly pr: number;
|
|
4
|
+
readonly repo: string | null;
|
|
5
|
+
readonly head_sha: string | null;
|
|
6
|
+
readonly platform_primitive: PlatformPrimitive;
|
|
7
|
+
readonly monitor_agent_id: string;
|
|
8
|
+
readonly started_at: string;
|
|
9
|
+
readonly worktree_path: string;
|
|
10
|
+
readonly parent_session_id: string | null;
|
|
11
|
+
readonly ended_at: string | null;
|
|
12
|
+
}
|
|
13
|
+
export interface ReviewMonitorFile {
|
|
14
|
+
readonly schema_version: number;
|
|
15
|
+
readonly records: ReviewMonitorRecord[];
|
|
16
|
+
}
|
|
17
|
+
export declare function reviewMonitorPath(projectRoot: string): string;
|
|
18
|
+
export declare function emptyReviewMonitorFile(): ReviewMonitorFile;
|
|
19
|
+
export declare function readReviewMonitorFile(path: string): {
|
|
20
|
+
data: ReviewMonitorFile | null;
|
|
21
|
+
error: string | null;
|
|
22
|
+
};
|
|
23
|
+
export declare function writeReviewMonitorFile(path: string, data: ReviewMonitorFile): void;
|
|
24
|
+
export interface RegisterReviewMonitorInput {
|
|
25
|
+
readonly pr: number;
|
|
26
|
+
readonly repo?: string | null;
|
|
27
|
+
readonly headSha?: string | null;
|
|
28
|
+
readonly platformPrimitive: PlatformPrimitive;
|
|
29
|
+
readonly monitorAgentId: string;
|
|
30
|
+
readonly projectRoot: string;
|
|
31
|
+
readonly parentSessionId?: string | null;
|
|
32
|
+
readonly startedAt?: Date;
|
|
33
|
+
}
|
|
34
|
+
export declare function registerReviewMonitor(input: RegisterReviewMonitorInput): {
|
|
35
|
+
path: string;
|
|
36
|
+
record: ReviewMonitorRecord;
|
|
37
|
+
};
|
|
38
|
+
export declare function parseIso8601Utc(value: string): Date | null;
|
|
39
|
+
export declare function isRecordActive(record: ReviewMonitorRecord, options: {
|
|
40
|
+
now?: Date;
|
|
41
|
+
staleMinutes?: number;
|
|
42
|
+
headSha?: string | null;
|
|
43
|
+
}): boolean;
|
|
44
|
+
export declare function findActiveMonitorForPr(file: ReviewMonitorFile, pr: number, options: {
|
|
45
|
+
now?: Date;
|
|
46
|
+
staleMinutes?: number;
|
|
47
|
+
headSha?: string | null;
|
|
48
|
+
}): ReviewMonitorRecord | null;
|
|
49
|
+
export declare function defaultSubagentStatusDir(projectRoot: string): string;
|
|
50
|
+
//# sourceMappingURL=record.d.ts.map
|