@deftai/directive-core 0.77.0 → 0.79.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/cache/empty-populate.d.ts +44 -0
- package/dist/cache/empty-populate.js +117 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +1 -0
- package/dist/check-updates/index.d.ts +10 -0
- package/dist/check-updates/index.js +81 -1
- package/dist/doctor/main.d.ts +1 -0
- package/dist/doctor/main.js +48 -0
- package/dist/doctor/payload-staleness.js +7 -1
- package/dist/doctor/types.d.ts +3 -0
- package/dist/eval/triggers.d.ts +76 -0
- package/dist/eval/triggers.js +259 -0
- package/dist/eval-triggers-relocation/evaluate.d.ts +36 -0
- package/dist/eval-triggers-relocation/evaluate.js +115 -0
- package/dist/eval-triggers-relocation/index.d.ts +2 -0
- package/dist/eval-triggers-relocation/index.js +2 -0
- package/dist/hooks/dispatcher.d.ts +43 -0
- package/dist/hooks/dispatcher.js +173 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/scope.d.ts +12 -0
- package/dist/hooks/scope.js +46 -0
- package/dist/hooks/tools.d.ts +4 -0
- package/dist/hooks/tools.js +23 -0
- package/dist/init-deposit/agent-hooks.d.ts +22 -0
- package/dist/init-deposit/agent-hooks.js +238 -0
- package/dist/init-deposit/hygiene.js +4 -0
- package/dist/init-deposit/index.d.ts +1 -0
- package/dist/init-deposit/index.js +1 -0
- package/dist/init-deposit/init-deposit.js +4 -0
- package/dist/init-deposit/prettierignore.d.ts +21 -0
- package/dist/init-deposit/prettierignore.js +83 -0
- package/dist/init-deposit/refresh.d.ts +3 -1
- package/dist/init-deposit/refresh.js +31 -40
- package/dist/init-deposit/scaffold.js +6 -12
- package/dist/init-deposit/xbrief-projections.d.ts +18 -0
- package/dist/init-deposit/xbrief-projections.js +117 -0
- package/dist/intake/reconcile-issues.d.ts +11 -0
- package/dist/intake/reconcile-issues.js +166 -51
- package/dist/issue-sync/index.d.ts +2 -0
- package/dist/issue-sync/index.js +2 -0
- package/dist/issue-sync/sync-from-xbrief-cli.d.ts +11 -0
- package/dist/issue-sync/sync-from-xbrief-cli.js +44 -0
- package/dist/issue-sync/sync-from-xbrief.d.ts +46 -0
- package/dist/issue-sync/sync-from-xbrief.js +208 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +5 -0
- package/dist/platform/platform-capabilities.js +9 -0
- package/dist/platform/shell-context.d.ts +30 -0
- package/dist/platform/shell-context.js +111 -0
- package/dist/pr-monitor/cadence.d.ts +6 -0
- package/dist/pr-monitor/cadence.js +13 -0
- package/dist/pr-monitor/index.d.ts +1 -1
- package/dist/pr-monitor/index.js +1 -1
- package/dist/pr-monitor/monitor.d.ts +7 -0
- package/dist/pr-monitor/monitor.js +59 -13
- package/dist/preflight-cache/evaluate.d.ts +3 -8
- package/dist/preflight-cache/evaluate.js +19 -19
- package/dist/release/build-dist.js +1 -0
- package/dist/release/constants.d.ts +2 -0
- package/dist/release/constants.js +8 -1
- package/dist/release/flags.js +26 -0
- package/dist/release/issue-state-fetch.d.ts +27 -0
- package/dist/release/issue-state-fetch.js +146 -0
- package/dist/release/main.js +1 -0
- package/dist/release/native-steps.js +6 -4
- package/dist/release/pipeline.js +15 -5
- package/dist/release/preflight.d.ts +6 -2
- package/dist/release/preflight.js +19 -5
- package/dist/release/types.d.ts +3 -1
- package/dist/release-e2e/constants.js +6 -6
- package/dist/release-e2e/flags.d.ts +2 -0
- package/dist/release-e2e/flags.js +8 -4
- package/dist/release-e2e/greenfield-python-free-smoke-cli.js +40 -1
- package/dist/release-e2e/greenfield-python-free-smoke.d.ts +2 -0
- package/dist/release-e2e/greenfield-python-free-smoke.js +28 -8
- package/dist/release-e2e/main.js +15 -9
- package/dist/release-e2e/types.d.ts +4 -2
- package/dist/resolution/classify.d.ts +1 -1
- package/dist/resolution/classify.js +36 -11
- package/dist/scope/project-definition-sync.js +2 -150
- package/dist/scope/registry-artifact-sync.d.ts +3 -0
- package/dist/scope/registry-artifact-sync.js +159 -0
- package/dist/scope/specification-sync.d.ts +3 -0
- package/dist/scope/specification-sync.js +16 -0
- package/dist/scope/transition.js +40 -12
- package/dist/session/cache-recovery.d.ts +19 -0
- package/dist/session/cache-recovery.js +26 -0
- package/dist/session/session-start.d.ts +2 -0
- package/dist/session/session-start.js +14 -3
- package/dist/session/verify-session-ritual.d.ts +17 -0
- package/dist/session/verify-session-ritual.js +64 -0
- package/dist/triage/help/registry-data.d.ts +12 -1
- package/dist/triage/help/registry-data.js +22 -1
- package/dist/triage/welcome/default-mode.js +2 -0
- package/dist/triage/welcome/onboard.js +2 -0
- package/dist/ts-check-lane/run-lane.d.ts +5 -0
- package/dist/ts-check-lane/run-lane.js +9 -0
- package/dist/vbrief-reconcile/labels.d.ts +3 -0
- package/dist/vbrief-reconcile/labels.js +14 -0
- package/dist/vbrief-reconcile/main.d.ts +1 -0
- package/dist/vbrief-reconcile/main.js +13 -3
- package/dist/vbrief-reconcile/parity-scenarios.js +18 -4
- package/dist/vbrief-reconcile/repo-guard.d.ts +17 -0
- package/dist/vbrief-reconcile/repo-guard.js +33 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +3 -0
- package/dist/vbrief-reconcile/umbrellas.js +17 -1
- package/dist/verify-env/agent-hooks.d.ts +11 -0
- package/dist/verify-env/agent-hooks.js +46 -0
- package/dist/verify-env/command-spawn.d.ts +5 -0
- package/dist/verify-env/command-spawn.js +28 -9
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/dist/verify-env/verify-hooks-installed.js +5 -5
- package/dist/vitest-runner/coverage-debt-teardown.d.ts +3 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +33 -0
- package/dist/vitest-runner/coverage-debt.d.ts +50 -0
- package/dist/vitest-runner/coverage-debt.js +155 -0
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +12 -0
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +24 -0
- package/dist/xbrief-migrate/detect.js +46 -1
- package/dist/xbrief-migrate/migrate-project.js +19 -4
- package/dist/xbrief-migrate/migration-containment.d.ts +6 -0
- package/dist/xbrief-migrate/migration-containment.js +37 -0
- package/package.json +19 -3
package/dist/scope/transition.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync,
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
3
|
import { InstrumentedVbriefCrud, persistCrudMetrics } from "../eval/crud-telemetry.js";
|
|
4
4
|
import { assertProjectionContained, ProjectionContainmentError, } from "../fs/projection-containment.js";
|
|
@@ -8,6 +8,7 @@ import { stampCompletionMetadata } from "./capacity-stamp.js";
|
|
|
8
8
|
import { LIFECYCLE_FOLDERS, MOVE_LABELS, STATUS_PRECONDITIONS, STAY_LABELS, TRANSITIONS, } from "./constants.js";
|
|
9
9
|
import { detectLifecycleFolder, updateDecomposedChildBackReferences, updateDecomposedParentBackReferences, } from "./decomposed-refs.js";
|
|
10
10
|
import { syncProjectDefinitionAfterScopeMove } from "./project-definition-sync.js";
|
|
11
|
+
import { syncSpecificationAfterScopeMove } from "./specification-sync.js";
|
|
11
12
|
import { formatVbriefJson, utcNowIso } from "./vbrief-json.js";
|
|
12
13
|
export function runTransition(action, filePath, now = new Date()) {
|
|
13
14
|
if (!(action in TRANSITIONS)) {
|
|
@@ -101,30 +102,57 @@ export function runTransition(action, filePath, now = new Date()) {
|
|
|
101
102
|
}
|
|
102
103
|
const formatted = formatVbriefJson(data);
|
|
103
104
|
const crud = new InstrumentedVbriefCrud({ now: () => now });
|
|
104
|
-
const writeResult = crud.update(resolvedPath, formatted, { trustedWrite: true });
|
|
105
|
-
if (!writeResult.ok) {
|
|
106
|
-
return { ok: false, message: writeResult.error ?? `CRUD update failed for ${resolvedPath}` };
|
|
107
|
-
}
|
|
108
|
-
try {
|
|
109
|
-
persistCrudMetrics(projectRoot, crud.getMetrics());
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
/* best-effort telemetry persistence */
|
|
113
|
-
}
|
|
114
105
|
if (targetFolder !== null) {
|
|
115
106
|
const destDir = join(vbriefRoot, targetFolder);
|
|
116
107
|
mkdirSync(destDir, { recursive: true });
|
|
117
108
|
const destPath = join(destDir, basename);
|
|
118
|
-
|
|
109
|
+
if (existsSync(destPath)) {
|
|
110
|
+
return { ok: false, message: `Target already exists: ${destPath}` };
|
|
111
|
+
}
|
|
112
|
+
// #2578: stamp terminal status at the destination path in the same write as
|
|
113
|
+
// folder placement — never leave a non-terminal status under completed/.
|
|
114
|
+
const writeResult = crud.update(destPath, formatted, { trustedWrite: true });
|
|
115
|
+
if (!writeResult.ok) {
|
|
116
|
+
return { ok: false, message: writeResult.error ?? `CRUD update failed for ${destPath}` };
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
unlinkSync(resolvedPath);
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
try {
|
|
123
|
+
unlinkSync(destPath);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
/* best-effort rollback */
|
|
127
|
+
}
|
|
128
|
+
return { ok: false, message: `Failed to remove source after move: ${String(err)}` };
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
persistCrudMetrics(projectRoot, crud.getMetrics());
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
/* best-effort telemetry persistence */
|
|
135
|
+
}
|
|
119
136
|
updateDecomposedParentBackReferences(data, resolvedPath, destPath, vbriefRoot);
|
|
120
137
|
updateDecomposedChildBackReferences(data, resolvedPath, destPath, vbriefRoot);
|
|
121
138
|
syncProjectDefinitionAfterScopeMove(data, resolvedPath, destPath, vbriefRoot, targetStatus);
|
|
139
|
+
syncSpecificationAfterScopeMove(data, resolvedPath, destPath, vbriefRoot, targetStatus);
|
|
122
140
|
const actionLabel = MOVE_LABELS[act] ?? act.charAt(0).toUpperCase() + act.slice(1);
|
|
123
141
|
return {
|
|
124
142
|
ok: true,
|
|
125
143
|
message: `${actionLabel} ${basename}: ${currentFolder}/ -> ${targetFolder}/ (status: ${targetStatus})`,
|
|
126
144
|
};
|
|
127
145
|
}
|
|
146
|
+
const writeResult = crud.update(resolvedPath, formatted, { trustedWrite: true });
|
|
147
|
+
if (!writeResult.ok) {
|
|
148
|
+
return { ok: false, message: writeResult.error ?? `CRUD update failed for ${resolvedPath}` };
|
|
149
|
+
}
|
|
150
|
+
try {
|
|
151
|
+
persistCrudMetrics(projectRoot, crud.getMetrics());
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
/* best-effort telemetry persistence */
|
|
155
|
+
}
|
|
128
156
|
const actionLabel = STAY_LABELS[act] ?? act.charAt(0).toUpperCase() + act.slice(1);
|
|
129
157
|
return {
|
|
130
158
|
ok: true,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format a consumer-runnable `deft cache fetch-all` recovery command (#2574).
|
|
3
|
+
*
|
|
4
|
+
* Uses the space-separated CLI surface (not the colon task alias) and always
|
|
5
|
+
* includes required `--source` / `--repo` flags so following the hint literally
|
|
6
|
+
* reaches the handler instead of `unknown verb 'cache:fetch-all'`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatCacheFetchAllRecoveryCommand(repo: string | null, options?: {
|
|
9
|
+
force?: boolean;
|
|
10
|
+
}): string;
|
|
11
|
+
/**
|
|
12
|
+
* Branch-aware recovery hint (#1953 Option 3, #2574 argv fix).
|
|
13
|
+
* Age-stale (or age+drift mixed) → --force bypasses TTL; drift-only → plain refetch.
|
|
14
|
+
*/
|
|
15
|
+
export declare function recoveryHintForStaleFailure(causes: Readonly<{
|
|
16
|
+
ageStale: boolean;
|
|
17
|
+
driftDetected: boolean;
|
|
18
|
+
}>, repo?: string | null): string;
|
|
19
|
+
//# sourceMappingURL=cache-recovery.d.ts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
2
|
+
const CACHE_FETCH_SOURCE = "github-issue";
|
|
3
|
+
/**
|
|
4
|
+
* Format a consumer-runnable `deft cache fetch-all` recovery command (#2574).
|
|
5
|
+
*
|
|
6
|
+
* Uses the space-separated CLI surface (not the colon task alias) and always
|
|
7
|
+
* includes required `--source` / `--repo` flags so following the hint literally
|
|
8
|
+
* reaches the handler instead of `unknown verb 'cache:fetch-all'`.
|
|
9
|
+
*/
|
|
10
|
+
export function formatCacheFetchAllRecoveryCommand(repo, options = {}) {
|
|
11
|
+
const repoSlug = repo ?? "OWNER/NAME";
|
|
12
|
+
const args = ["cache", "fetch-all", "--source", CACHE_FETCH_SOURCE, "--repo", repoSlug];
|
|
13
|
+
if (options.force) {
|
|
14
|
+
args.push("--force");
|
|
15
|
+
}
|
|
16
|
+
return formatFrameworkCommand(args);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Branch-aware recovery hint (#1953 Option 3, #2574 argv fix).
|
|
20
|
+
* Age-stale (or age+drift mixed) → --force bypasses TTL; drift-only → plain refetch.
|
|
21
|
+
*/
|
|
22
|
+
export function recoveryHintForStaleFailure(causes, repo = null) {
|
|
23
|
+
const command = formatCacheFetchAllRecoveryCommand(repo, { force: causes.ageStale });
|
|
24
|
+
return ` Recovery: run \`${command}\` to refresh and reconcile upstream state.`;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=cache-recovery.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EnvironmentContext } from "../platform/shell-context.js";
|
|
1
2
|
import { type ResolveUserMdResult } from "../user-config/resolve-user-md.js";
|
|
2
3
|
import type { GitRunner } from "./git.js";
|
|
3
4
|
import { ritualStatePath } from "./ritual-sentinel.js";
|
|
@@ -40,6 +41,7 @@ export interface SessionStartOptions {
|
|
|
40
41
|
exitCode: number;
|
|
41
42
|
};
|
|
42
43
|
readonly resolveUserMd?: (projectRoot: string) => ResolveUserMdResult;
|
|
44
|
+
readonly probeEnvironment?: () => EnvironmentContext;
|
|
43
45
|
}
|
|
44
46
|
export declare function parseDeferrals(rawValues: readonly string[]): {
|
|
45
47
|
deferrals: Record<string, string>;
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { runningInsideDeftRepo } from "../doctor/paths.js";
|
|
3
3
|
import { emitSessionEvalReadback } from "../eval/readback.js";
|
|
4
4
|
import { MIGRATE_COMPLETION_NUDGE, shouldEmitMigrateNudge } from "../init-deposit/migrate.js";
|
|
5
|
+
import { detectEnvironmentContext, environmentContextToDict, formatEnvironmentContext, } from "../platform/shell-context.js";
|
|
5
6
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
6
7
|
import { resolvePolicy } from "../policy/resolve.js";
|
|
7
8
|
import { runDefaultMode } from "../triage/welcome/default-mode.js";
|
|
@@ -187,7 +188,7 @@ export function defaultBranchSync(projectRoot, runGit = defaultGitRunner) {
|
|
|
187
188
|
}
|
|
188
189
|
return { branch, upstream, ahead, behind, warning };
|
|
189
190
|
}
|
|
190
|
-
function runReadOnlySessionStart(projectRoot, options, instant) {
|
|
191
|
+
function runReadOnlySessionStart(projectRoot, options, instant, environment) {
|
|
191
192
|
const lines = [];
|
|
192
193
|
const resolveUserMd = options.resolveUserMd ?? ((root) => resolveUserMdPath({ projectRoot: root }));
|
|
193
194
|
const userMd = resolveUserMd(projectRoot);
|
|
@@ -198,6 +199,7 @@ function runReadOnlySessionStart(projectRoot, options, instant) {
|
|
|
198
199
|
: safeDiagnostic;
|
|
199
200
|
lines.push(READ_ONLY_ALIGNMENT_MESSAGE);
|
|
200
201
|
lines.push(userMdLine);
|
|
202
|
+
lines.push(formatEnvironmentContext(environment));
|
|
201
203
|
const resultPayload = {
|
|
202
204
|
ready: true,
|
|
203
205
|
exit_code: 0,
|
|
@@ -217,6 +219,7 @@ function runReadOnlySessionStart(projectRoot, options, instant) {
|
|
|
217
219
|
found: userMd.found,
|
|
218
220
|
diagnostic: userMd.diagnostic,
|
|
219
221
|
},
|
|
222
|
+
environment: environmentContextToDict(environment),
|
|
220
223
|
message: READ_ONLY_RESULT_MESSAGE,
|
|
221
224
|
};
|
|
222
225
|
return { code: 0, payload: resultPayload, lines };
|
|
@@ -226,16 +229,22 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
226
229
|
const instant = options.now ?? new Date();
|
|
227
230
|
const deferrals = options.deferrals ?? {};
|
|
228
231
|
const runGit = options.runGit ?? defaultGitRunner;
|
|
232
|
+
const environment = (options.probeEnvironment ?? detectEnvironmentContext)();
|
|
229
233
|
if (posture === READ_ONLY_POSTURE) {
|
|
230
|
-
return runReadOnlySessionStart(projectRoot, options, instant);
|
|
234
|
+
return runReadOnlySessionStart(projectRoot, options, instant, environment);
|
|
231
235
|
}
|
|
232
236
|
const { head: gitHeadValue, error: gitError } = gitHead(projectRoot, runGit);
|
|
233
237
|
if (gitHeadValue === null) {
|
|
234
238
|
const payload = {
|
|
235
239
|
ready: false,
|
|
240
|
+
environment: environmentContextToDict(environment),
|
|
236
241
|
message: gitError ?? "could not resolve git HEAD",
|
|
237
242
|
};
|
|
238
|
-
return {
|
|
243
|
+
return {
|
|
244
|
+
code: 2,
|
|
245
|
+
payload,
|
|
246
|
+
lines: [formatEnvironmentContext(environment), payload.message],
|
|
247
|
+
};
|
|
239
248
|
}
|
|
240
249
|
const quickSteps = recordDeferredSteps(QUICK_STEPS, deferrals, instant);
|
|
241
250
|
const gatedSteps = recordDeferredSteps(GATED_STEPS, deferrals, instant);
|
|
@@ -265,6 +274,7 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
265
274
|
lines.push(message);
|
|
266
275
|
lines.push(userMdLine);
|
|
267
276
|
}
|
|
277
|
+
lines.push(formatEnvironmentContext(environment));
|
|
268
278
|
if (!quickSteps.branch_policy) {
|
|
269
279
|
const result = resolvePolicy(projectRoot);
|
|
270
280
|
const message = disclosureLine(result);
|
|
@@ -377,6 +387,7 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
377
387
|
found: userMd.found,
|
|
378
388
|
diagnostic: userMd.diagnostic,
|
|
379
389
|
},
|
|
390
|
+
environment: environmentContextToDict(environment),
|
|
380
391
|
message: code === 0 ? "session ritual recorded" : "session ritual failed",
|
|
381
392
|
};
|
|
382
393
|
return { code, payload: resultPayload, lines };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type GitRunner } from "./git.js";
|
|
2
2
|
import { type DirectivePosture } from "./posture.js";
|
|
3
|
+
export { formatCacheFetchAllRecoveryCommand, recoveryHintForStaleFailure, } from "./cache-recovery.js";
|
|
3
4
|
export declare const ENV_SKIP = "DEFT_SESSION_RITUAL_SKIP";
|
|
4
5
|
export { ENTRYPOINT_TIMEOUT_EXIT_CODE, ENTRYPOINT_TIMEOUT_SECONDS, } from "./ritual-entrypoint.js";
|
|
5
6
|
export declare const GATED_ENTRYPOINT_COMMANDS: Readonly<Record<string, readonly string[]>>;
|
|
@@ -29,6 +30,22 @@ export interface VerifySessionRitualOptions {
|
|
|
29
30
|
readonly envPosture?: string | undefined;
|
|
30
31
|
readonly handoffText?: string | null;
|
|
31
32
|
}
|
|
33
|
+
export interface InspectSessionRitualOptions {
|
|
34
|
+
readonly tier?: "quick" | "gated";
|
|
35
|
+
readonly now?: Date;
|
|
36
|
+
readonly runGit?: GitRunner;
|
|
37
|
+
readonly posture?: DirectivePosture;
|
|
38
|
+
readonly envPosture?: string | undefined;
|
|
39
|
+
readonly handoffText?: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read-only ritual-state inspection for host hooks.
|
|
43
|
+
*
|
|
44
|
+
* Unlike {@link verifySessionRitual}, this never runs missing gated entrypoints
|
|
45
|
+
* and never rewrites `.deft/ritual-state.json`. A PreToolUse decision must be a
|
|
46
|
+
* probe, not a hidden `doctor` / cache-refresh mutation boundary.
|
|
47
|
+
*/
|
|
48
|
+
export declare function inspectSessionRitual(projectRoot: string, options?: InspectSessionRitualOptions): VerifyResult;
|
|
32
49
|
export declare function verifySessionRitual(projectRoot: string, options?: VerifySessionRitualOptions): VerifyResult;
|
|
33
50
|
export declare function emitVerifyJson(result: VerifyResult): string;
|
|
34
51
|
export declare function emitBypassWarning(result: VerifyResult): string;
|
|
@@ -7,6 +7,7 @@ import { defaultRitualRunner } from "./ritual-entrypoint.js";
|
|
|
7
7
|
import { readRitualState, ritualStatePath, ritualStep, writeRitualState, } from "./ritual-sentinel.js";
|
|
8
8
|
import { GATED_STEPS, QUICK_STEPS } from "./session-start.js";
|
|
9
9
|
import { resolveSessionRitualStalenessHours } from "./staleness.js";
|
|
10
|
+
export { formatCacheFetchAllRecoveryCommand, recoveryHintForStaleFailure, } from "./cache-recovery.js";
|
|
10
11
|
export const ENV_SKIP = "DEFT_SESSION_RITUAL_SKIP";
|
|
11
12
|
export { ENTRYPOINT_TIMEOUT_EXIT_CODE, ENTRYPOINT_TIMEOUT_SECONDS, } from "./ritual-entrypoint.js";
|
|
12
13
|
export const GATED_ENTRYPOINT_COMMANDS = {
|
|
@@ -105,6 +106,69 @@ function evaluateLoadedState(projectRoot, state, input) {
|
|
|
105
106
|
}
|
|
106
107
|
return [0, `OK session ritual ${input.tier} tier is fresh.`];
|
|
107
108
|
}
|
|
109
|
+
/**
|
|
110
|
+
* Read-only ritual-state inspection for host hooks.
|
|
111
|
+
*
|
|
112
|
+
* Unlike {@link verifySessionRitual}, this never runs missing gated entrypoints
|
|
113
|
+
* and never rewrites `.deft/ritual-state.json`. A PreToolUse decision must be a
|
|
114
|
+
* probe, not a hidden `doctor` / cache-refresh mutation boundary.
|
|
115
|
+
*/
|
|
116
|
+
export function inspectSessionRitual(projectRoot, options = {}) {
|
|
117
|
+
const tier = options.tier ?? "quick";
|
|
118
|
+
const posture = resolveSessionPosture({
|
|
119
|
+
explicitPosture: options.posture ?? null,
|
|
120
|
+
envPosture: options.envPosture ?? process.env.DEFT_SESSION_POSTURE,
|
|
121
|
+
handoffText: options.handoffText,
|
|
122
|
+
tier,
|
|
123
|
+
});
|
|
124
|
+
const ritualStateRequired = posture === "mutation" && !ritualStateIsPostureAuthority();
|
|
125
|
+
const statePath = ritualStatePath(projectRoot);
|
|
126
|
+
if (posture === "read-only") {
|
|
127
|
+
return {
|
|
128
|
+
code: 0,
|
|
129
|
+
message: readOnlyPostureMessage(tier),
|
|
130
|
+
tier,
|
|
131
|
+
statePath,
|
|
132
|
+
bypassed: false,
|
|
133
|
+
wouldFailCode: null,
|
|
134
|
+
posture,
|
|
135
|
+
ritualStateRequired: false,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
const missingStateFile = !existsSync(statePath);
|
|
139
|
+
const [state, err] = readRitualState(projectRoot);
|
|
140
|
+
if (state === null) {
|
|
141
|
+
const code = missingStateFile ? 1 : 2;
|
|
142
|
+
const startCommand = formatFrameworkCommand(["session:start"]);
|
|
143
|
+
return {
|
|
144
|
+
code,
|
|
145
|
+
message: code === 1
|
|
146
|
+
? `${err}. Run \`${startCommand}\` before implementation dispatch.`
|
|
147
|
+
: (err ?? "ritual state invalid"),
|
|
148
|
+
tier,
|
|
149
|
+
statePath,
|
|
150
|
+
bypassed: false,
|
|
151
|
+
wouldFailCode: null,
|
|
152
|
+
posture,
|
|
153
|
+
ritualStateRequired,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
const [code, message] = evaluateLoadedState(projectRoot, state, {
|
|
157
|
+
tier,
|
|
158
|
+
now: options.now ?? new Date(),
|
|
159
|
+
runGit: options.runGit,
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
code,
|
|
163
|
+
message,
|
|
164
|
+
tier,
|
|
165
|
+
statePath,
|
|
166
|
+
bypassed: false,
|
|
167
|
+
wouldFailCode: null,
|
|
168
|
+
posture,
|
|
169
|
+
ritualStateRequired,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
108
172
|
export function verifySessionRitual(projectRoot, options = {}) {
|
|
109
173
|
const tier = options.tier ?? "quick";
|
|
110
174
|
const posture = resolveSessionPosture({
|
|
@@ -364,6 +364,17 @@ export declare const registryData: {
|
|
|
364
364
|
readonly see_also: readonly ["task eval:run", "task eval:report", "#1703"];
|
|
365
365
|
readonly placeholder: false;
|
|
366
366
|
};
|
|
367
|
+
readonly "task eval:triggers": {
|
|
368
|
+
readonly name: "task eval:triggers";
|
|
369
|
+
readonly summary: "Trigger routing coverage for Skills Index rules";
|
|
370
|
+
readonly refs: "(#1586)";
|
|
371
|
+
readonly description: "Offline skill-pi-trigger-eval compatible routing check: grades evals/trigger-cases.jsonl against REFERENCES.md Skills Index triggers (should-fire / should-not-fire per skill).";
|
|
372
|
+
readonly usage: "task eval:triggers [-- --json] [--project-root PATH]";
|
|
373
|
+
readonly flags: readonly [readonly ["--json", "(off)", "Emit the TriggerEvalReport JSON."], readonly ["--project-root PATH", "(cwd)", "Project root override (Taskfile threads USER_WORKING_DIR)."]];
|
|
374
|
+
readonly examples: readonly ["task eval:triggers", "task eval:triggers -- --json"];
|
|
375
|
+
readonly see_also: readonly ["task eval:health", "task verify:eval-triggers-relocation", "#1862"];
|
|
376
|
+
readonly placeholder: false;
|
|
377
|
+
};
|
|
367
378
|
readonly "task eval:run": {
|
|
368
379
|
readonly name: "task eval:run";
|
|
369
380
|
readonly summary: "Tier 2 golden corpus eval for a model";
|
|
@@ -508,7 +519,7 @@ export declare const registryData: {
|
|
|
508
519
|
readonly placeholder: false;
|
|
509
520
|
};
|
|
510
521
|
};
|
|
511
|
-
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:run", "task eval:report"]]];
|
|
522
|
+
readonly categoriesTriage: readonly [readonly ["Session-start", readonly ["task triage:summary", "task verify:cache-fresh"]], readonly ["State verbs (mutate audit log)", readonly ["task triage:accept", "task triage:defer", "task triage:reject", "task triage:needs-ac", "task triage:mark-duplicate", "task triage:reset", "task triage:status", "task triage:history"]], readonly ["Read verbs", readonly ["task triage:queue", "task triage:audit", "task triage:show", "task triage:scope", "task triage:scope-drift", "task triage:classify"]], readonly ["Lifecycle", readonly ["task triage:bootstrap", "task triage:welcome", "task triage:reconcile"]], readonly ["Bulk variants", readonly ["task triage:bulk-accept", "task triage:bulk-reject", "task triage:bulk-defer", "task triage:bulk-needs-ac", "task triage:refresh-active", "task triage:smoketest"]], readonly ["Subscription mutation", readonly ["task triage:subscribe", "task triage:unsubscribe"]], readonly ["Archive / rotation", readonly ["task triage:audit:prune", "task triage:archive-list", "task triage:restore-from-archive", "task triage:audit-log:rotate", "task triage:metrics"]], readonly ["Framework eval (#1703)", readonly ["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"]]];
|
|
512
523
|
readonly categoriesScope: readonly [readonly ["Promote / demote", readonly ["task scope:promote", "task scope:demote"]], readonly ["Activate / complete", readonly ["task scope:activate", "task scope:complete", "task scope:fail", "task scope:cancel", "task scope:block", "task scope:unblock"]], readonly ["Reversibility", readonly ["task scope:undo", "task scope:restore"]], readonly ["Decomposition", readonly ["task scope:decompose"]]];
|
|
513
524
|
readonly scriptSubcommandMap: {
|
|
514
525
|
readonly triage_actions: {
|
|
@@ -548,6 +548,24 @@ export const registryData = {
|
|
|
548
548
|
see_also: ["task eval:run", "task eval:report", "#1703"],
|
|
549
549
|
placeholder: false,
|
|
550
550
|
},
|
|
551
|
+
"task eval:triggers": {
|
|
552
|
+
name: "task eval:triggers",
|
|
553
|
+
summary: "Trigger routing coverage for Skills Index rules",
|
|
554
|
+
refs: "(#1586)",
|
|
555
|
+
description: "Offline skill-pi-trigger-eval compatible routing check: grades evals/trigger-cases.jsonl against REFERENCES.md Skills Index triggers (should-fire / should-not-fire per skill).",
|
|
556
|
+
usage: "task eval:triggers [-- --json] [--project-root PATH]",
|
|
557
|
+
flags: [
|
|
558
|
+
["--json", "(off)", "Emit the TriggerEvalReport JSON."],
|
|
559
|
+
[
|
|
560
|
+
"--project-root PATH",
|
|
561
|
+
"(cwd)",
|
|
562
|
+
"Project root override (Taskfile threads USER_WORKING_DIR).",
|
|
563
|
+
],
|
|
564
|
+
],
|
|
565
|
+
examples: ["task eval:triggers", "task eval:triggers -- --json"],
|
|
566
|
+
see_also: ["task eval:health", "task verify:eval-triggers-relocation", "#1862"],
|
|
567
|
+
placeholder: false,
|
|
568
|
+
},
|
|
551
569
|
"task eval:run": {
|
|
552
570
|
name: "task eval:run",
|
|
553
571
|
summary: "Tier 2 golden corpus eval for a model",
|
|
@@ -829,7 +847,10 @@ export const registryData = {
|
|
|
829
847
|
"task triage:metrics",
|
|
830
848
|
],
|
|
831
849
|
],
|
|
832
|
-
[
|
|
850
|
+
[
|
|
851
|
+
"Framework eval (#1703)",
|
|
852
|
+
["task eval:health", "task eval:triggers", "task eval:run", "task eval:report"],
|
|
853
|
+
],
|
|
833
854
|
],
|
|
834
855
|
categoriesScope: [
|
|
835
856
|
["Promote / demote", ["task scope:promote", "task scope:demote"]],
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
|
+
import { ensureTriageCacheHydrated } from "../../cache/empty-populate.js";
|
|
2
3
|
import { maybeSelfHealCache } from "../../cache/fetch.js";
|
|
3
4
|
import { FIRST_TIME_NUDGE, INCOMPLETE_NUDGE_TEMPLATE } from "./constants.js";
|
|
4
5
|
import { classifyOnboarding, detectPriorState } from "./prior-state.js";
|
|
@@ -32,6 +33,7 @@ export function runDefaultMode(projectRoot, options = {}) {
|
|
|
32
33
|
};
|
|
33
34
|
const heal = options.selfHealFn ??
|
|
34
35
|
((root) => {
|
|
36
|
+
ensureTriageCacheHydrated(resolve(root));
|
|
35
37
|
maybeSelfHealCache(resolve(root));
|
|
36
38
|
});
|
|
37
39
|
heal(projectRoot);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
+
import { ensureTriageCacheHydrated } from "../../cache/empty-populate.js";
|
|
3
4
|
import { maybeSelfHealCache } from "../../cache/fetch.js";
|
|
4
5
|
import { resolveProjectDefinitionPath } from "../../layout/resolve.js";
|
|
5
6
|
import { DEFAULT_WIP_CAP, SUBSCRIPTION_PRESETS, TRIAGE_SKILL_PATH } from "./constants.js";
|
|
@@ -57,6 +58,7 @@ export function runOnboardMode(projectRoot, options = {}) {
|
|
|
57
58
|
}
|
|
58
59
|
const heal = options.selfHealFn ??
|
|
59
60
|
((root) => {
|
|
61
|
+
ensureTriageCacheHydrated(resolve(root));
|
|
60
62
|
maybeSelfHealCache(resolve(root));
|
|
61
63
|
});
|
|
62
64
|
heal(projectRoot);
|
|
@@ -41,6 +41,11 @@ export interface RunTsLaneOptions {
|
|
|
41
41
|
readonly runner?: LaneRunner;
|
|
42
42
|
/** Injected sink for human-facing notices (defaults to stdout). */
|
|
43
43
|
readonly out?: (message: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Env used to resolve release coverage-debt before sanitizeTsLaneEnv strips
|
|
46
|
+
* DEFT_RELEASE_PREFLIGHT (#2618). Defaults to process.env.
|
|
47
|
+
*/
|
|
48
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
44
49
|
}
|
|
45
50
|
/** Windows command shims (.cmd/.bat) need a shell; native executables do not. */
|
|
46
51
|
export declare function shouldUseShellForCommand(command: string, platform?: NodeJS.Platform): boolean;
|
|
@@ -20,6 +20,7 @@ import { spawnSync } from "node:child_process";
|
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
21
|
import { posix, win32 } from "node:path";
|
|
22
22
|
import { BRANCH_GATE_BYPASS_ENV, RELEASE_PREFLIGHT_ENV } from "../release/constants.js";
|
|
23
|
+
import { resolveCoverageDebtIssue } from "../vitest-runner/coverage-debt.js";
|
|
23
24
|
/** Release Step-5 vars that must not leak into vitest via inherited pnpm env (#2434). */
|
|
24
25
|
const TS_LANE_POISON_ENV_KEYS = [BRANCH_GATE_BYPASS_ENV, RELEASE_PREFLIGHT_ENV];
|
|
25
26
|
/**
|
|
@@ -96,12 +97,20 @@ export function runTsLane(projectRoot, options) {
|
|
|
96
97
|
const { pnpm } = options;
|
|
97
98
|
const runner = options.runner ?? defaultRunner;
|
|
98
99
|
const out = options.out ?? ((message) => process.stdout.write(`${message}\n`));
|
|
100
|
+
// Resolve debt from the pre-sanitize env: sanitizeTsLaneEnv strips
|
|
101
|
+
// DEFT_RELEASE_PREFLIGHT (required for the env-based debt path), so forward
|
|
102
|
+
// --allow-coverage-debt=#N on the vitest argv instead (#2618 / #2573).
|
|
103
|
+
const debt = resolveCoverageDebtIssue([], options.env ?? process.env);
|
|
104
|
+
const debtIssue = debt.kind === "valid" ? debt.issue : null;
|
|
99
105
|
if (!pnpm) {
|
|
100
106
|
out(SKIP_NOTICE);
|
|
101
107
|
return 0;
|
|
102
108
|
}
|
|
103
109
|
for (const command of LANE_COMMANDS) {
|
|
104
110
|
const argv = [pnpm, ...command];
|
|
111
|
+
if (debtIssue !== null && command[1] === "test") {
|
|
112
|
+
argv.push("--", `--allow-coverage-debt=${debtIssue}`);
|
|
113
|
+
}
|
|
105
114
|
const result = runner(argv, projectRoot);
|
|
106
115
|
const code = result.status;
|
|
107
116
|
// A null status means the child was terminated by a signal (SIGKILL / OOM /
|
|
@@ -14,6 +14,9 @@ export interface ReconcileLabelsOptions {
|
|
|
14
14
|
readonly repo?: string | null;
|
|
15
15
|
readonly dryRun?: boolean;
|
|
16
16
|
readonly client?: LabelClient;
|
|
17
|
+
/** Opt in to mutating issues outside the resolved project repo (#2601). */
|
|
18
|
+
readonly allowCrossRepo?: boolean;
|
|
19
|
+
readonly repoAllowlist?: readonly string[];
|
|
17
20
|
}
|
|
18
21
|
export declare function reconcileLabels(projectRoot: string, options?: ReconcileLabelsOptions): [number, ReconcileLabelsOutcome];
|
|
19
22
|
export declare function renderLabelsReport(outcome: ReconcileLabelsOutcome): string;
|
|
@@ -4,6 +4,7 @@ import { hasArtifactSuffix, resolveLifecycleRoot, stripArtifactSuffix } from "..
|
|
|
4
4
|
import { call } from "../scm/call.js";
|
|
5
5
|
import { extractIssueRef } from "../triage/reconcile/parse-uri.js";
|
|
6
6
|
import { depResolved, RESOLVED_FOLDERS } from "./graph.js";
|
|
7
|
+
import { isRepoMutationAllowed } from "./repo-guard.js";
|
|
7
8
|
import { allScopeIds, asStrList } from "./swarm-deps.js";
|
|
8
9
|
export const SCAN_FOLDERS = ["proposed", "pending", "active"];
|
|
9
10
|
export const MANAGED_LABELS = ["status:blocked", "epic", "status:tracker", "rfc"];
|
|
@@ -163,6 +164,19 @@ export function reconcileLabels(projectRoot, options = {}) {
|
|
|
163
164
|
? metadata.swarm
|
|
164
165
|
: {};
|
|
165
166
|
const desired = computeDesiredLabels(plan, hasUnresolvedDeps(swarm, knownIds));
|
|
167
|
+
// Guard before any SCM read so cross-repo refusal is explicit (#2601 / Greptile P1).
|
|
168
|
+
const mutateGate = isRepoMutationAllowed(effectiveRepo, root, {
|
|
169
|
+
allowCrossRepo: options.allowCrossRepo,
|
|
170
|
+
allowlist: options.repoAllowlist,
|
|
171
|
+
explicitRepo: options.repo ?? null,
|
|
172
|
+
});
|
|
173
|
+
if (!mutateGate.allowed) {
|
|
174
|
+
outcome.errors.push({
|
|
175
|
+
story_id: storyId,
|
|
176
|
+
message: mutateGate.reason ?? `refusing cross-repo mutation on ${effectiveRepo}`,
|
|
177
|
+
});
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
166
180
|
let current;
|
|
167
181
|
try {
|
|
168
182
|
current = client.fetchLabels(effectiveRepo, number);
|
|
@@ -28,6 +28,8 @@ function parseOptions(rest) {
|
|
|
28
28
|
}
|
|
29
29
|
else if (arg === "--force")
|
|
30
30
|
opts.force = true;
|
|
31
|
+
else if (arg === "--allow-cross-repo")
|
|
32
|
+
opts.allowCrossRepo = true;
|
|
31
33
|
else if (arg === "--dry-run")
|
|
32
34
|
opts.dryRun = true;
|
|
33
35
|
else if (arg === "--json")
|
|
@@ -46,7 +48,7 @@ function parseOptions(rest) {
|
|
|
46
48
|
return opts;
|
|
47
49
|
}
|
|
48
50
|
export function usage() {
|
|
49
|
-
process.stderr.write("usage: vbrief-reconcile <graph|labels|umbrellas> [--project-root PATH] [--dry-run] [--json] [--force] [--repo OWNER/NAME]\n" +
|
|
51
|
+
process.stderr.write("usage: vbrief-reconcile <graph|labels|umbrellas> [--project-root PATH] [--dry-run] [--json] [--force] [--repo OWNER/NAME] [--allow-cross-repo]\n" +
|
|
50
52
|
" vbrief-reconcile parity --scenario NAME [--fixture-root PATH]\n" +
|
|
51
53
|
" vbrief-reconcile parity --all [--fixture-root PATH]\n");
|
|
52
54
|
}
|
|
@@ -70,7 +72,11 @@ export function runGraph(opts) {
|
|
|
70
72
|
}
|
|
71
73
|
export function runLabels(opts) {
|
|
72
74
|
const root = resolve(opts.projectRoot ?? ".");
|
|
73
|
-
const [code, outcome] = reconcileLabels(root, {
|
|
75
|
+
const [code, outcome] = reconcileLabels(root, {
|
|
76
|
+
repo: opts.repo,
|
|
77
|
+
dryRun: opts.dryRun,
|
|
78
|
+
allowCrossRepo: opts.allowCrossRepo,
|
|
79
|
+
});
|
|
74
80
|
if (code === 2) {
|
|
75
81
|
if (opts.json) {
|
|
76
82
|
process.stdout.write(`${JSON.stringify({ error: "no vbrief/ directory found" })}\n`);
|
|
@@ -88,7 +94,11 @@ export function runLabels(opts) {
|
|
|
88
94
|
}
|
|
89
95
|
export function runUmbrellas(opts) {
|
|
90
96
|
const root = resolve(opts.projectRoot ?? ".");
|
|
91
|
-
const [code, outcome] = reconcileUmbrellas(root, {
|
|
97
|
+
const [code, outcome] = reconcileUmbrellas(root, {
|
|
98
|
+
repo: opts.repo,
|
|
99
|
+
dryRun: opts.dryRun,
|
|
100
|
+
allowCrossRepo: opts.allowCrossRepo,
|
|
101
|
+
});
|
|
92
102
|
if (code === 2) {
|
|
93
103
|
if (opts.json) {
|
|
94
104
|
process.stdout.write(`${JSON.stringify({ error: "no vbrief/ directory found" })}\n`);
|
|
@@ -291,7 +291,11 @@ export function runParityScenario(name, options) {
|
|
|
291
291
|
],
|
|
292
292
|
});
|
|
293
293
|
const client = options.labelClient ?? new MemoryLabelClient();
|
|
294
|
-
const [code, outcome] = reconcileLabels(root, {
|
|
294
|
+
const [code, outcome] = reconcileLabels(root, {
|
|
295
|
+
client,
|
|
296
|
+
dryRun: true,
|
|
297
|
+
repo: "deftai/directive",
|
|
298
|
+
});
|
|
295
299
|
return {
|
|
296
300
|
scenario: name,
|
|
297
301
|
ok: true,
|
|
@@ -314,7 +318,11 @@ export function runParityScenario(name, options) {
|
|
|
314
318
|
],
|
|
315
319
|
});
|
|
316
320
|
const client = options.labelClient ?? new MemoryLabelClient();
|
|
317
|
-
const [code, outcome] = reconcileLabels(root, {
|
|
321
|
+
const [code, outcome] = reconcileLabels(root, {
|
|
322
|
+
client,
|
|
323
|
+
dryRun: true,
|
|
324
|
+
repo: "deftai/directive",
|
|
325
|
+
});
|
|
318
326
|
return {
|
|
319
327
|
scenario: name,
|
|
320
328
|
ok: true,
|
|
@@ -342,6 +350,7 @@ export function runParityScenario(name, options) {
|
|
|
342
350
|
client,
|
|
343
351
|
dryRun: true,
|
|
344
352
|
now: FIXED_NOW.toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
353
|
+
repo: "deftai/directive",
|
|
345
354
|
});
|
|
346
355
|
return {
|
|
347
356
|
scenario: name,
|
|
@@ -367,8 +376,13 @@ export function runParityScenario(name, options) {
|
|
|
367
376
|
});
|
|
368
377
|
const client = options.umbrellaClient ?? SHARED_UMBRELLA_CLIENT;
|
|
369
378
|
const now = FIXED_NOW.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
370
|
-
reconcileUmbrellas(root, { client, dryRun: false, now });
|
|
371
|
-
const [code, outcome] = reconcileUmbrellas(root, {
|
|
379
|
+
reconcileUmbrellas(root, { client, dryRun: false, now, repo: "deftai/directive" });
|
|
380
|
+
const [code, outcome] = reconcileUmbrellas(root, {
|
|
381
|
+
client,
|
|
382
|
+
dryRun: true,
|
|
383
|
+
now,
|
|
384
|
+
repo: "deftai/directive",
|
|
385
|
+
});
|
|
372
386
|
return {
|
|
373
387
|
scenario: name,
|
|
374
388
|
ok: true,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface RepoMutateGuardOptions {
|
|
2
|
+
readonly allowCrossRepo?: boolean;
|
|
3
|
+
readonly allowlist?: readonly string[];
|
|
4
|
+
readonly explicitRepo?: string | null;
|
|
5
|
+
}
|
|
6
|
+
/** Normalize owner/name slugs for case-insensitive comparison. */
|
|
7
|
+
export declare function normalizeRepoSlug(repo: string): string;
|
|
8
|
+
/**
|
|
9
|
+
* Fail-closed gate before SCM label/comment mutations (#2601).
|
|
10
|
+
* Cross-repo targets are refused unless explicitly opted in or allowlisted.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isRepoMutationAllowed(targetRepo: string, projectRoot: string, options?: RepoMutateGuardOptions): {
|
|
13
|
+
readonly allowed: boolean;
|
|
14
|
+
readonly projectRepo: string | null;
|
|
15
|
+
readonly reason?: string;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=repo-guard.d.ts.map
|