@deftai/directive-core 0.94.0 → 0.96.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/classify.js +485 -0
- package/dist/cache/archive.d.ts +134 -0
- package/dist/cache/archive.js +630 -0
- package/dist/cache/index.d.ts +1 -0
- package/dist/cache/index.js +1 -0
- package/dist/cache/main.js +298 -1
- package/dist/content-contracts/skills/helpers.d.ts +1 -1
- package/dist/content-contracts/skills/helpers.js +1 -0
- package/dist/doctor/main.js +41 -9
- package/dist/doctor/types.d.ts +2 -2
- package/dist/freshness/bind.d.ts +67 -0
- package/dist/freshness/bind.js +201 -0
- package/dist/freshness/cli.d.ts +30 -0
- package/dist/freshness/cli.js +180 -0
- package/dist/freshness/compare.d.ts +14 -0
- package/dist/freshness/compare.js +134 -0
- package/dist/freshness/generation.d.ts +44 -0
- package/dist/freshness/generation.js +172 -0
- package/dist/freshness/index.d.ts +13 -0
- package/dist/freshness/index.js +13 -0
- package/dist/freshness/report.d.ts +43 -0
- package/dist/freshness/report.js +139 -0
- package/dist/freshness/types.d.ts +70 -0
- package/dist/freshness/types.js +30 -0
- package/dist/handoff-evidence/index.d.ts +8 -0
- package/dist/handoff-evidence/index.js +7 -0
- package/dist/handoff-evidence/validate.d.ts +105 -0
- package/dist/handoff-evidence/validate.js +423 -0
- package/dist/hooks/dispatcher.d.ts +3 -0
- package/dist/hooks/dispatcher.js +12 -3
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/agent-hooks.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +5 -2
- package/dist/init-deposit/hygiene.d.ts +1 -1
- package/dist/init-deposit/hygiene.js +16 -4
- package/dist/init-deposit/init-deposit.d.ts +7 -1
- package/dist/init-deposit/init-deposit.js +24 -4
- package/dist/init-deposit/refresh.d.ts +9 -1
- package/dist/init-deposit/refresh.js +48 -5
- package/dist/init-deposit/scaffold.js +6 -5
- package/dist/parent-turn-shape/evaluate.d.ts +84 -0
- package/dist/parent-turn-shape/evaluate.js +353 -0
- package/dist/parent-turn-shape/index.d.ts +8 -0
- package/dist/parent-turn-shape/index.js +8 -0
- package/dist/policy/index.d.ts +3 -0
- package/dist/policy/index.js +35 -0
- package/dist/release-e2e/greenfield-python-free-smoke.js +7 -3
- package/dist/review-monitor/constants.js +3 -2
- package/dist/review-monitor/tier-detection.d.ts +7 -3
- package/dist/review-monitor/tier-detection.js +18 -1
- package/dist/review-monitor/verify.js +18 -0
- package/dist/scope/index.d.ts +2 -0
- package/dist/scope/index.js +2 -0
- package/dist/scope/main.d.ts +10 -0
- package/dist/scope/main.js +109 -24
- package/dist/scope/promote-from-issue.d.ts +49 -0
- package/dist/scope/promote-from-issue.js +367 -0
- package/dist/scope/promote-path.d.ts +39 -0
- package/dist/scope/promote-path.js +105 -0
- package/dist/session/ritual-entrypoint.d.ts +6 -2
- package/dist/session/ritual-entrypoint.js +15 -2
- package/dist/session/session-ready.js +31 -14
- package/dist/session/session-start.d.ts +2 -1
- package/dist/session/session-start.js +66 -3
- package/dist/session/verify-session-ritual.d.ts +4 -2
- package/dist/session/verify-session-ritual.js +11 -11
- package/dist/slash/product-set.js +4 -1
- package/dist/swarm/routing.d.ts +4 -2
- package/dist/swarm/routing.js +26 -4
- package/dist/swarm/verify-review-clean.d.ts +4 -3
- package/dist/swarm/verify-review-clean.js +28 -73
- package/dist/triage/actions/index.js +62 -2
- package/dist/triage/actions/types.d.ts +8 -1
- package/dist/triage/author-filter.d.ts +51 -0
- package/dist/triage/author-filter.js +152 -0
- package/dist/triage/classify/index.d.ts +9 -0
- package/dist/triage/classify/index.js +34 -2
- package/dist/triage/classify/label-mirror.d.ts +206 -0
- package/dist/triage/classify/label-mirror.js +914 -0
- package/dist/triage/help/registry-data.d.ts +49 -38
- package/dist/triage/help/registry-data.js +134 -39
- package/dist/triage/index.d.ts +1 -0
- package/dist/triage/index.js +1 -0
- package/dist/triage/queue/index.d.ts +1 -0
- package/dist/triage/queue/index.js +1 -0
- package/dist/triage/queue/render.d.ts +2 -0
- package/dist/triage/queue/render.js +6 -0
- package/dist/triage/queue/show.d.ts +1 -1
- package/dist/triage/queue/show.js +4 -2
- package/dist/vbrief-validate/project-definition.js +1 -1
- package/dist/verify-env/agent-hook-readiness.d.ts +42 -0
- package/dist/verify-env/agent-hook-readiness.js +150 -0
- package/dist/verify-env/agent-hooks-live-probe.d.ts +16 -4
- package/dist/verify-env/agent-hooks-live-probe.js +120 -107
- package/dist/verify-env/agent-hooks.js +11 -3
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/package.json +11 -3
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parent turn-shape hard-stop (FC14 / #3131).
|
|
3
|
+
*
|
|
4
|
+
* Machine-checkable gate so soft skill prose is not the sole mitigation for
|
|
5
|
+
* the OpenClaw parent text-repetition hang after leaf announce.
|
|
6
|
+
*/
|
|
7
|
+
export { countRepeatedUnitsInBlob, DEFAULT_MAX_IDENTICAL_WITHOUT_TOOL, evaluateParentTurnShape, isNearIdentical, isTextRepetitionHang, normalizeTurnText, PARENT_TURN_FAIL_FC14, splitTextUnits, } from "./evaluate.js";
|
|
8
|
+
//# sourceMappingURL=index.js.map
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -31,12 +31,15 @@ export declare const FIELD_TRIAGE_SCOPE_IGNORES = "plan.policy.triageScopeIgnore
|
|
|
31
31
|
export declare const FIELD_TRIAGE_RANKING_LABELS = "plan.policy.triageRankingLabels";
|
|
32
32
|
export declare const FIELD_TRIAGE_AUTO_CLASSIFY = "plan.policy.triageAutoClassify";
|
|
33
33
|
export declare const FIELD_TRIAGE_HOLD_MARKERS = "plan.policy.triageHoldMarkers";
|
|
34
|
+
export declare const FIELD_TRIAGE_LABEL_MIRROR = "plan.policy.triageLabelMirror";
|
|
34
35
|
export declare const FIELD_SWARM_SUBAGENT_BACKEND = "plan.policy.swarmSubagentBackend";
|
|
35
36
|
export declare const DEFAULT_SESSION_RITUAL_STALENESS_HOURS = 4;
|
|
36
37
|
export declare const DEFAULT_TRIAGE_SCOPE_VALUE: readonly Record<string, unknown>[];
|
|
37
38
|
export declare const DEFAULT_TRIAGE_SCOPE_IGNORES_VALUE: readonly unknown[];
|
|
38
39
|
export declare const DEFAULT_TRIAGE_RANKING_LABELS_VALUE: readonly string[];
|
|
39
40
|
export declare const DEFAULT_TRIAGE_AUTO_CLASSIFY_VALUE: readonly unknown[];
|
|
41
|
+
/** Default for #1423 Tier-1 label mirror: enabled with `triaged` idempotency marker. */
|
|
42
|
+
export declare const DEFAULT_TRIAGE_LABEL_MIRROR_VALUE: Readonly<Record<string, unknown>>;
|
|
40
43
|
export declare const KNOWN_SUBAGENT_BACKEND_IDS: Set<string>;
|
|
41
44
|
export interface PolicyField {
|
|
42
45
|
readonly name: string;
|
package/dist/policy/index.js
CHANGED
|
@@ -46,6 +46,7 @@ export const FIELD_TRIAGE_SCOPE_IGNORES = "plan.policy.triageScopeIgnores";
|
|
|
46
46
|
export const FIELD_TRIAGE_RANKING_LABELS = "plan.policy.triageRankingLabels";
|
|
47
47
|
export const FIELD_TRIAGE_AUTO_CLASSIFY = "plan.policy.triageAutoClassify";
|
|
48
48
|
export const FIELD_TRIAGE_HOLD_MARKERS = "plan.policy.triageHoldMarkers";
|
|
49
|
+
export const FIELD_TRIAGE_LABEL_MIRROR = "plan.policy.triageLabelMirror";
|
|
49
50
|
export const FIELD_SWARM_SUBAGENT_BACKEND = "plan.policy.swarmSubagentBackend";
|
|
50
51
|
// deliveryBranch also exported from delivery-branch.js via export *
|
|
51
52
|
export const DEFAULT_SESSION_RITUAL_STALENESS_HOURS = 4;
|
|
@@ -55,6 +56,13 @@ export const DEFAULT_TRIAGE_SCOPE_VALUE = [
|
|
|
55
56
|
export const DEFAULT_TRIAGE_SCOPE_IGNORES_VALUE = [];
|
|
56
57
|
export const DEFAULT_TRIAGE_RANKING_LABELS_VALUE = [];
|
|
57
58
|
export const DEFAULT_TRIAGE_AUTO_CLASSIFY_VALUE = [];
|
|
59
|
+
/** Default for #1423 Tier-1 label mirror: enabled with `triaged` idempotency marker. */
|
|
60
|
+
export const DEFAULT_TRIAGE_LABEL_MIRROR_VALUE = {
|
|
61
|
+
enabled: true,
|
|
62
|
+
idempotencyLabel: "triaged",
|
|
63
|
+
alwaysLabels: ["triaged"],
|
|
64
|
+
actionLabels: {},
|
|
65
|
+
};
|
|
58
66
|
export const KNOWN_SUBAGENT_BACKEND_IDS = new Set(["composer", "cursor-cloud", "grok-build"]);
|
|
59
67
|
const FALLBACK_HOLD_MARKERS = [
|
|
60
68
|
"do not implement",
|
|
@@ -139,6 +147,32 @@ function inspectWipCap(data) {
|
|
|
139
147
|
source: "default",
|
|
140
148
|
};
|
|
141
149
|
}
|
|
150
|
+
function inspectTriageLabelMirrorField(data) {
|
|
151
|
+
const policyBlock = getPolicyBlock(data);
|
|
152
|
+
if ("triageLabelMirror" in policyBlock) {
|
|
153
|
+
const raw = policyBlock.triageLabelMirror;
|
|
154
|
+
if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
|
|
155
|
+
return {
|
|
156
|
+
name: FIELD_TRIAGE_LABEL_MIRROR,
|
|
157
|
+
current: raw,
|
|
158
|
+
default: { ...DEFAULT_TRIAGE_LABEL_MIRROR_VALUE },
|
|
159
|
+
source: "typed",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
name: FIELD_TRIAGE_LABEL_MIRROR,
|
|
164
|
+
current: { ...DEFAULT_TRIAGE_LABEL_MIRROR_VALUE },
|
|
165
|
+
default: { ...DEFAULT_TRIAGE_LABEL_MIRROR_VALUE },
|
|
166
|
+
source: "default-on-error",
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
name: FIELD_TRIAGE_LABEL_MIRROR,
|
|
171
|
+
current: { ...DEFAULT_TRIAGE_LABEL_MIRROR_VALUE },
|
|
172
|
+
default: { ...DEFAULT_TRIAGE_LABEL_MIRROR_VALUE },
|
|
173
|
+
source: "default",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
142
176
|
function inspectSessionRitualStalenessHours(data) {
|
|
143
177
|
const policyBlock = getPolicyBlock(data);
|
|
144
178
|
if ("sessionRitualStalenessHours" in policyBlock) {
|
|
@@ -362,6 +396,7 @@ const REGISTERED_POLICIES = [
|
|
|
362
396
|
(data) => listFieldInspector(data, "triageHoldMarkers", FIELD_TRIAGE_HOLD_MARKERS, defaultHoldMarkers(), {
|
|
363
397
|
emptyIsTyped: true,
|
|
364
398
|
}),
|
|
399
|
+
inspectTriageLabelMirrorField,
|
|
365
400
|
inspectSwarmSubagentBackend,
|
|
366
401
|
inspectDeliveryBranchField,
|
|
367
402
|
inspectMinGreptileConfidenceField,
|
|
@@ -152,9 +152,14 @@ export function rehearseGreenfieldPythonFreeSmoke(repoRoot, seams = {}, options
|
|
|
152
152
|
return [false, `greenfield smoke: expected global deft at ${deft}`];
|
|
153
153
|
}
|
|
154
154
|
const pyFree = pythonFreePathEnv(envBase);
|
|
155
|
+
const installedBin = join(npmPrefix, "bin");
|
|
156
|
+
const installedEnv = {
|
|
157
|
+
...pyFree,
|
|
158
|
+
PATH: `${installedBin}:${pyFree.PATH ?? ""}`,
|
|
159
|
+
};
|
|
155
160
|
[ok, reason] = runStep(spawn, "directive init", deft, ["init", "--yes", "--repo-root", projectDir], {
|
|
156
161
|
cwd: work,
|
|
157
|
-
env:
|
|
162
|
+
env: installedEnv,
|
|
158
163
|
timeoutMs: 120_000,
|
|
159
164
|
}, onProgress);
|
|
160
165
|
if (!ok)
|
|
@@ -173,8 +178,7 @@ export function rehearseGreenfieldPythonFreeSmoke(repoRoot, seams = {}, options
|
|
|
173
178
|
return [false, "greenfield smoke: repo-root Python run shim present after init"];
|
|
174
179
|
}
|
|
175
180
|
const checkEnv = {
|
|
176
|
-
...
|
|
177
|
-
PATH: `${join(npmPrefix, "bin")}:${pyFree.PATH ?? ""}`,
|
|
181
|
+
...installedEnv,
|
|
178
182
|
DEFT_SESSION_RITUAL_SKIP: "1",
|
|
179
183
|
};
|
|
180
184
|
onProgress?.("greenfield smoke: running consumer task deft:check (engine-invoke path)");
|
|
@@ -40,7 +40,7 @@ export const REVIEW_MONITOR_HELP = "usage: task verify:review-monitor -- --pr <N
|
|
|
40
40
|
"\n" +
|
|
41
41
|
"Claim a lease after spawning Approach 1:\n" +
|
|
42
42
|
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> \\\n" +
|
|
43
|
-
" --platform-primitive cursor-task|spawn_subagent|start_agent|sessions_spawn \\\n" +
|
|
43
|
+
" --platform-primitive cursor-task|claude-agent|spawn_subagent|start_agent|sessions_spawn \\\n" +
|
|
44
44
|
" [--head-sha SHA] [--repo OWNER/REPO] [--force]\n" +
|
|
45
45
|
"\n" +
|
|
46
46
|
"Release when done:\n" +
|
|
@@ -55,7 +55,8 @@ export const REGISTER_HELP = "usage: task review-monitor:register -- --pr <N> --
|
|
|
55
55
|
" --pr N Pull request number\n" +
|
|
56
56
|
" --monitor-agent-id ID Stable poller agent id / Task handle\n" +
|
|
57
57
|
" --platform-primitive P start_agent | spawn_subagent | cursor-task |\n" +
|
|
58
|
-
"
|
|
58
|
+
" claude-agent (#3134) | sessions_spawn |\n" +
|
|
59
|
+
" openclaw-sessions-spawn (#2876)\n" +
|
|
59
60
|
"\n" +
|
|
60
61
|
"options:\n" +
|
|
61
62
|
" --repo OWNER/REPO Repository (default: origin / DEFT_TRIAGE_REPO)\n" +
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MONITORING_TIER_1, MONITORING_TIER_2, MONITORING_TIER_3 } from "./constants.js";
|
|
2
|
-
/** Canonical Approach-1 platform primitives for review-monitor register/verify (#2655 / #2876). */
|
|
3
|
-
export type PlatformPrimitive = "start_agent" | "spawn_subagent" | "cursor-task" | "sessions_spawn" | "openclaw-sessions-spawn";
|
|
2
|
+
/** Canonical Approach-1 platform primitives for review-monitor register/verify (#2655 / #2876 / #3134). */
|
|
3
|
+
export type PlatformPrimitive = "start_agent" | "spawn_subagent" | "cursor-task" | "claude-agent" | "sessions_spawn" | "openclaw-sessions-spawn";
|
|
4
4
|
/** Accepted `--platform-primitive` values (register CLI + help text). */
|
|
5
5
|
export declare const PLATFORM_PRIMITIVES: readonly PlatformPrimitive[];
|
|
6
6
|
export declare const PLATFORM_PRIMITIVE_SET: Set<string>;
|
|
@@ -11,8 +11,12 @@ export interface MonitoringTierProbe {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
14
|
-
* (#1877 / #2655 / #2876). Prefer `task platform:capabilities` when available (#1357);
|
|
14
|
+
* (#1877 / #2655 / #2876 / #3134). Prefer `task platform:capabilities` when available (#1357);
|
|
15
15
|
* this probe does not block MVP.
|
|
16
|
+
*
|
|
17
|
+
* Ordered env probe (must match skill matrix placement; Claude after Cursor so bare
|
|
18
|
+
* Task / CURSOR_* never misclassify Claude Code as cursor-composer):
|
|
19
|
+
* start_agent → WARP_* → Cursor → Claude Code → OpenClaw → grok-build → Tier2 → Tier3.
|
|
16
20
|
*/
|
|
17
21
|
export declare function probeMonitoringTier(environ?: NodeJS.ProcessEnv): MonitoringTierProbe;
|
|
18
22
|
export declare function isTier1(probe: MonitoringTierProbe): boolean;
|
|
@@ -5,6 +5,7 @@ export const PLATFORM_PRIMITIVES = [
|
|
|
5
5
|
"start_agent",
|
|
6
6
|
"spawn_subagent",
|
|
7
7
|
"cursor-task",
|
|
8
|
+
"claude-agent",
|
|
8
9
|
"sessions_spawn",
|
|
9
10
|
"openclaw-sessions-spawn",
|
|
10
11
|
];
|
|
@@ -28,8 +29,12 @@ function probeOverride(environ) {
|
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
31
|
* Inline Tier-1 detection aligned with the swarm Phase 3 / review-cycle matrix
|
|
31
|
-
* (#1877 / #2655 / #2876). Prefer `task platform:capabilities` when available (#1357);
|
|
32
|
+
* (#1877 / #2655 / #2876 / #3134). Prefer `task platform:capabilities` when available (#1357);
|
|
32
33
|
* this probe does not block MVP.
|
|
34
|
+
*
|
|
35
|
+
* Ordered env probe (must match skill matrix placement; Claude after Cursor so bare
|
|
36
|
+
* Task / CURSOR_* never misclassify Claude Code as cursor-composer):
|
|
37
|
+
* start_agent → WARP_* → Cursor → Claude Code → OpenClaw → grok-build → Tier2 → Tier3.
|
|
33
38
|
*/
|
|
34
39
|
export function probeMonitoringTier(environ = process.env) {
|
|
35
40
|
const override = probeOverride(environ);
|
|
@@ -53,6 +58,18 @@ export function probeMonitoringTier(environ = process.env) {
|
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
const runtime = (environ.DEFT_AGENT_RUNTIME ?? "").trim().toLowerCase();
|
|
61
|
+
// Claude Code: Claude-unique env signals only — never bare "Task" (#3134).
|
|
62
|
+
// CLAUDECODE is set in Claude Code tool/hook subprocesses (Anthropic docs).
|
|
63
|
+
// DEFT_PROBE_CLAUDE_CODE / DEFT_AGENT_RUNTIME=claude-code are explicit overrides.
|
|
64
|
+
// Cursor already short-circuited above, so CURSOR_* never falls into this branch.
|
|
65
|
+
if (envTruthy(environ, "DEFT_PROBE_CLAUDE_CODE") ||
|
|
66
|
+
envTruthy(environ, "DEFT_HAS_CLAUDE_AGENT") ||
|
|
67
|
+
envTruthy(environ, "CLAUDECODE") ||
|
|
68
|
+
envTruthy(environ, "CLAUDE_CODE") ||
|
|
69
|
+
runtime === "claude-code" ||
|
|
70
|
+
runtime === "claude") {
|
|
71
|
+
return { tier: MONITORING_TIER_1, primitive: "claude-agent", descriptor: "claude-code" };
|
|
72
|
+
}
|
|
56
73
|
// OpenClaw: sessions_spawn is the Tier-1 Approach 1 primitive (#2876).
|
|
57
74
|
// Alias openclaw-sessions-spawn accepted on register for explicit naming.
|
|
58
75
|
if (envTruthy(environ, "DEFT_PROBE_SESSIONS_SPAWN") ||
|
|
@@ -7,6 +7,24 @@ import { defaultSubagentStatusDir, fetchActiveMonitorFromGithub, readReviewMonit
|
|
|
7
7
|
import { isTier1, probeMonitoringTier } from "./tier-detection.js";
|
|
8
8
|
function spawnRedirect(probe) {
|
|
9
9
|
const primitive = probe.primitive ?? "sub-agent";
|
|
10
|
+
// Claude Code / Cursor nested-leaf boundary (#2797 / #3134): lead with leaf-safe
|
|
11
|
+
// ownership so implementation leaves never treat nested Task/Agent spawn as the
|
|
12
|
+
// default instruction. Top-level parents that own the primitive still get the
|
|
13
|
+
// Approach 1 background path second.
|
|
14
|
+
if (primitive === "claude-agent" || primitive === "cursor-task") {
|
|
15
|
+
return (`Ownership path for ${primitive} (#2797 / #3134):\n` +
|
|
16
|
+
" 1. Implementation leaf (drive-to: merge-ready): keep ownership in THIS process " +
|
|
17
|
+
"via blocking dual-invoke `pr:watch` (`deft pr:watch <N>` then `task deft:pr:watch -- <N>`). " +
|
|
18
|
+
`Do NOT nested-spawn another ${primitive} review-monitor.\n` +
|
|
19
|
+
" 2. Or scope leaf `stop-at: pr-open` so the parent/orchestrator that owns " +
|
|
20
|
+
`${primitive} spawns a sibling monitor and registers it.\n` +
|
|
21
|
+
" 3. Top-level parent/orchestrator only: spawn Approach 1 via " +
|
|
22
|
+
`${primitive} (background), include templates/agent-prompt-preamble.md and ` +
|
|
23
|
+
"templates/swarm-greptile-poller-prompt.md, then:\n" +
|
|
24
|
+
" task review-monitor:register -- --pr <N> --monitor-agent-id <id> " +
|
|
25
|
+
`--platform-primitive ${primitive}\n` +
|
|
26
|
+
"Re-run: task verify:review-monitor -- --pr <N>");
|
|
27
|
+
}
|
|
10
28
|
return (`Spawn an Approach 1 review-monitor via ${primitive} (background), include ` +
|
|
11
29
|
"`templates/agent-prompt-preamble.md` and `templates/swarm-greptile-poller-prompt.md`, " +
|
|
12
30
|
"then register:\n" +
|
package/dist/scope/index.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export * from "./demote.js";
|
|
|
7
7
|
export * from "./main.js";
|
|
8
8
|
export * from "./open-umbrella-warning.js";
|
|
9
9
|
export * from "./project-context.js";
|
|
10
|
+
export * from "./promote-from-issue.js";
|
|
11
|
+
export * from "./promote-path.js";
|
|
10
12
|
export * from "./transition.js";
|
|
11
13
|
export * from "./undo.js";
|
|
12
14
|
export * from "./vbrief-json.js";
|
package/dist/scope/index.js
CHANGED
|
@@ -7,6 +7,8 @@ export * from "./demote.js";
|
|
|
7
7
|
export * from "./main.js";
|
|
8
8
|
export * from "./open-umbrella-warning.js";
|
|
9
9
|
export * from "./project-context.js";
|
|
10
|
+
export * from "./promote-from-issue.js";
|
|
11
|
+
export * from "./promote-path.js";
|
|
10
12
|
export * from "./transition.js";
|
|
11
13
|
export * from "./undo.js";
|
|
12
14
|
export * from "./vbrief-json.js";
|
package/dist/scope/main.d.ts
CHANGED
|
@@ -6,6 +6,16 @@ export interface LifecycleArgs {
|
|
|
6
6
|
force?: boolean;
|
|
7
7
|
batch?: boolean;
|
|
8
8
|
batchFiles?: string[];
|
|
9
|
+
/** Promote from triage-cache issue number (#1136). */
|
|
10
|
+
fromIssue?: number;
|
|
11
|
+
/** Repo slug for --from-issue (#1136). */
|
|
12
|
+
repo?: string;
|
|
13
|
+
/** Missing decision hard-fails (#1136). */
|
|
14
|
+
strict?: boolean;
|
|
15
|
+
/** Skip triage reciprocity gate (#1136). */
|
|
16
|
+
forceNoCache?: boolean;
|
|
17
|
+
/** Disambiguate multiple proposed artifacts for --from-issue (#1136). */
|
|
18
|
+
pathFlag?: string;
|
|
9
19
|
/** Explicit non-delivery disposition for code-bearing complete (#3041). */
|
|
10
20
|
nonDeliveryDisposition?: NonDeliveryDisposition;
|
|
11
21
|
/** Optional delivery evidence flags for complete (#3041). */
|
package/dist/scope/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
|
-
import { dirname
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
3
|
import { maybeRunStalenessTickler } from "../staleness-tickler/run.js";
|
|
4
4
|
import { reconcileUmbrellas, renderUmbrellasReport } from "../vbrief-reconcile/umbrellas.js";
|
|
5
5
|
import { canonicalLogPath, readAll } from "./audit-log.js";
|
|
@@ -9,14 +9,18 @@ import { isNonDeliveryDisposition, } from "./delivery-evidence.js";
|
|
|
9
9
|
import { batchDemote, DEFAULT_OLDER_THAN_DAYS, demoteOne, resolveDemoteFilePath, resolveFilePath, resolveProjectRootStrict, } from "./demote.js";
|
|
10
10
|
import { completedPathForScopeMove, findOpenUmbrellaReferences, renderOpenUmbrellaWarning, } from "./open-umbrella-warning.js";
|
|
11
11
|
import { resolveProjectRoot } from "./project-context.js";
|
|
12
|
-
import {
|
|
12
|
+
import { promoteFromIssue } from "./promote-from-issue.js";
|
|
13
|
+
import { promotePath } from "./promote-path.js";
|
|
14
|
+
import { runTransition } from "./transition.js";
|
|
13
15
|
import { findByDecisionId, isAlreadyUndone, REVERSIBLE_ACTIONS, undoBatch, undoOne, } from "./undo.js";
|
|
14
|
-
import { checkWipCap, formatWipCapRefusal } from "./wip-cap-check.js";
|
|
15
16
|
const LIFECYCLE_USAGE_STDERR = "usage: scope_lifecycle.py [-h] [--project-root PROJECT_ROOT] [--force] [--batch]\n" +
|
|
17
|
+
" [--from-issue N] [--repo OWNER/NAME] [--strict] [--force-no-cache]\n" +
|
|
18
|
+
" [--path PATH]\n" +
|
|
16
19
|
" {activate,block,cancel,complete,fail,promote,restore,unblock}\n" +
|
|
17
20
|
" [file ...]\n" +
|
|
18
21
|
"scope_lifecycle.py: error: the following arguments are required: action, file\n" +
|
|
19
|
-
"(promote --batch may omit file and promotes all proposed/ scopes; #3011)\n"
|
|
22
|
+
"(promote --batch may omit file and promotes all proposed/ scopes; #3011)\n" +
|
|
23
|
+
"(promote --from-issue=N may omit file; #1136)\n";
|
|
20
24
|
function parseLifecycleArgv(argv) {
|
|
21
25
|
if (argv.length < 1) {
|
|
22
26
|
return { args: null, error: "usage" };
|
|
@@ -30,6 +34,10 @@ function parseLifecycleArgv(argv) {
|
|
|
30
34
|
let force = false;
|
|
31
35
|
let batch = false;
|
|
32
36
|
const batchFiles = [];
|
|
37
|
+
let fromIssue;
|
|
38
|
+
let strict = false;
|
|
39
|
+
let forceNoCache = false;
|
|
40
|
+
let pathFlag;
|
|
33
41
|
let nonDeliveryDisposition;
|
|
34
42
|
let prNumber;
|
|
35
43
|
let mergeCommit;
|
|
@@ -48,6 +56,37 @@ function parseLifecycleArgv(argv) {
|
|
|
48
56
|
else if (arg === "--batch") {
|
|
49
57
|
batch = true;
|
|
50
58
|
}
|
|
59
|
+
else if (arg === "--strict") {
|
|
60
|
+
strict = true;
|
|
61
|
+
}
|
|
62
|
+
else if (arg === "--force-no-cache") {
|
|
63
|
+
forceNoCache = true;
|
|
64
|
+
}
|
|
65
|
+
else if (arg === "--from-issue") {
|
|
66
|
+
const raw = argv[i + 1];
|
|
67
|
+
i += 1;
|
|
68
|
+
if (raw === undefined || !/^[1-9]\d*$/.test(raw)) {
|
|
69
|
+
return { args: null, error: "usage" };
|
|
70
|
+
}
|
|
71
|
+
fromIssue = Number.parseInt(raw, 10);
|
|
72
|
+
}
|
|
73
|
+
else if (arg?.startsWith("--from-issue=")) {
|
|
74
|
+
const raw = arg.slice("--from-issue=".length);
|
|
75
|
+
if (!/^[1-9]\d*$/.test(raw)) {
|
|
76
|
+
return { args: null, error: "usage" };
|
|
77
|
+
}
|
|
78
|
+
fromIssue = Number.parseInt(raw, 10);
|
|
79
|
+
}
|
|
80
|
+
else if (arg === "--path") {
|
|
81
|
+
pathFlag = argv[i + 1];
|
|
82
|
+
i += 1;
|
|
83
|
+
if (pathFlag === undefined) {
|
|
84
|
+
return { args: null, error: "usage" };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (arg?.startsWith("--path=")) {
|
|
88
|
+
pathFlag = arg.slice("--path=".length);
|
|
89
|
+
}
|
|
51
90
|
else if (arg === "--project-root") {
|
|
52
91
|
projectRoot = argv[i + 1];
|
|
53
92
|
i += 1;
|
|
@@ -127,18 +166,20 @@ function parseLifecycleArgv(argv) {
|
|
|
127
166
|
return { args: null, error: "usage" };
|
|
128
167
|
}
|
|
129
168
|
}
|
|
169
|
+
// Delivery evidence uses --repo as repository; --from-issue also uses --repo as triage slug.
|
|
170
|
+
// Prefer treating --repo as triage slug when --from-issue is set (#1136).
|
|
130
171
|
const deliveryEvidence = prNumber !== undefined ||
|
|
131
172
|
mergeCommit !== undefined ||
|
|
132
173
|
prBase !== undefined ||
|
|
133
174
|
deliveryBranch !== undefined ||
|
|
134
|
-
repo !== undefined ||
|
|
175
|
+
(repo !== undefined && fromIssue === undefined) ||
|
|
135
176
|
mergedAt !== undefined
|
|
136
177
|
? {
|
|
137
178
|
prNumber: prNumber !== undefined && Number.isFinite(prNumber) ? prNumber : null,
|
|
138
179
|
mergeCommit: mergeCommit ?? null,
|
|
139
180
|
prBase: prBase ?? null,
|
|
140
181
|
deliveryBranch: deliveryBranch ?? null,
|
|
141
|
-
repository: repo ?? null,
|
|
182
|
+
repository: fromIssue === undefined ? (repo ?? null) : null,
|
|
142
183
|
mergedAt: mergedAt ?? (mergeCommit !== undefined ? "supplied" : null),
|
|
143
184
|
verifier: "scope:complete",
|
|
144
185
|
}
|
|
@@ -158,6 +199,27 @@ function parseLifecycleArgv(argv) {
|
|
|
158
199
|
},
|
|
159
200
|
};
|
|
160
201
|
}
|
|
202
|
+
if (fromIssue !== undefined) {
|
|
203
|
+
if (action !== "promote") {
|
|
204
|
+
return { args: null, error: "usage" };
|
|
205
|
+
}
|
|
206
|
+
if (Number.isNaN(fromIssue) || fromIssue < 1) {
|
|
207
|
+
return { args: null, error: "usage" };
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
args: {
|
|
211
|
+
action,
|
|
212
|
+
file: file.length > 0 ? file : (pathFlag ?? ""),
|
|
213
|
+
projectRoot,
|
|
214
|
+
force,
|
|
215
|
+
fromIssue,
|
|
216
|
+
repo,
|
|
217
|
+
strict,
|
|
218
|
+
forceNoCache,
|
|
219
|
+
pathFlag: pathFlag ?? (file.length > 0 ? file : undefined),
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
161
223
|
if (file.length === 0) {
|
|
162
224
|
return { args: null, error: "usage" };
|
|
163
225
|
}
|
|
@@ -181,7 +243,7 @@ export function lifecycleMain(argv) {
|
|
|
181
243
|
}
|
|
182
244
|
return 2;
|
|
183
245
|
}
|
|
184
|
-
const { action, file, projectRoot, force, batch, batchFiles, nonDeliveryDisposition, deliveryEvidence, } = parsed.args;
|
|
246
|
+
const { action, file, projectRoot, force, batch, batchFiles, fromIssue, repo, strict, forceNoCache, pathFlag, nonDeliveryDisposition, deliveryEvidence, } = parsed.args;
|
|
185
247
|
if (batch === true && action === "promote") {
|
|
186
248
|
const result = batchPromote({
|
|
187
249
|
files: batchFiles && batchFiles.length > 0 ? batchFiles : undefined,
|
|
@@ -203,21 +265,54 @@ export function lifecycleMain(argv) {
|
|
|
203
265
|
}
|
|
204
266
|
return result.exitCode;
|
|
205
267
|
}
|
|
268
|
+
if (fromIssue !== undefined && action === "promote") {
|
|
269
|
+
const result = promoteFromIssue({
|
|
270
|
+
issueNumber: fromIssue,
|
|
271
|
+
repo: repo ?? null,
|
|
272
|
+
projectRoot,
|
|
273
|
+
force: force === true,
|
|
274
|
+
forceNoCache: forceNoCache === true,
|
|
275
|
+
strict: strict === true,
|
|
276
|
+
explicitPath: pathFlag,
|
|
277
|
+
});
|
|
278
|
+
for (const line of result.warnings) {
|
|
279
|
+
process.stderr.write(`${line}\n`);
|
|
280
|
+
}
|
|
281
|
+
if (result.ok) {
|
|
282
|
+
process.stdout.write(`${result.message}\n`);
|
|
283
|
+
if (result.wipCapOverride) {
|
|
284
|
+
process.stderr.write("\u26a0 WIP cap exceeded; promote allowed via --force. " +
|
|
285
|
+
"audit: scope-lifecycle.jsonl entry tagged wip_cap_override (#1124).\n");
|
|
286
|
+
}
|
|
287
|
+
return 0;
|
|
288
|
+
}
|
|
289
|
+
process.stderr.write(`Error: ${result.message}\n`);
|
|
290
|
+
return result.exitCode;
|
|
291
|
+
}
|
|
206
292
|
const [filePath, error] = resolveFilePath(file, projectRoot);
|
|
207
293
|
if (error !== null || filePath === null) {
|
|
208
294
|
process.stderr.write(`Error: ${error}\n`);
|
|
209
295
|
return 2;
|
|
210
296
|
}
|
|
211
|
-
|
|
297
|
+
// Path-based promote uses shared promotePath for WIP + optional audit consistency (#1136).
|
|
212
298
|
if (action === "promote") {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
299
|
+
const promoteResult = promotePath(filePath, {
|
|
300
|
+
projectRoot,
|
|
301
|
+
force: force === true,
|
|
302
|
+
});
|
|
303
|
+
if (promoteResult.ok) {
|
|
304
|
+
process.stdout.write(`${promoteResult.message}\n`);
|
|
305
|
+
if (promoteResult.wipCapOverride) {
|
|
306
|
+
process.stderr.write("\u26a0 WIP cap exceeded " +
|
|
307
|
+
`(promote allowed via --force). ` +
|
|
308
|
+
"audit: scope-lifecycle.jsonl entry tagged wip_cap_override (#1124).\n");
|
|
219
309
|
}
|
|
310
|
+
return 0;
|
|
220
311
|
}
|
|
312
|
+
process.stderr.write(promoteResult.exitCode === 1 && promoteResult.message.includes("WIP")
|
|
313
|
+
? `${promoteResult.message}\n`
|
|
314
|
+
: `Error: ${promoteResult.message}\n`);
|
|
315
|
+
return promoteResult.exitCode;
|
|
221
316
|
}
|
|
222
317
|
const transitionOptions = {
|
|
223
318
|
nonDeliveryDisposition,
|
|
@@ -226,16 +321,6 @@ export function lifecycleMain(argv) {
|
|
|
226
321
|
};
|
|
227
322
|
const result = runTransition(action, filePath, new Date(), transitionOptions);
|
|
228
323
|
if (result.ok) {
|
|
229
|
-
if (action === "promote" && capCheck !== null && capCheck.forceOverride) {
|
|
230
|
-
const rootForAudit = resolveProjectRoot(projectRoot);
|
|
231
|
-
if (rootForAudit !== null) {
|
|
232
|
-
const newPath = join(rootForAudit, "vbrief", "pending", filePath.split(/[/\\]/).pop() ?? "");
|
|
233
|
-
recordWipCapOverride(newPath, rootForAudit, capCheck);
|
|
234
|
-
}
|
|
235
|
-
process.stderr.write("\u26a0 WIP cap exceeded " +
|
|
236
|
-
`(count=${capCheck.count}, cap=${capCheck.cap}); promote allowed via --force. ` +
|
|
237
|
-
"audit: vbrief/.eval/scope-lifecycle.jsonl entry tagged wip_cap_override (#1124).\n");
|
|
238
|
-
}
|
|
239
324
|
process.stdout.write(`${result.message}\n`);
|
|
240
325
|
if (action === "complete") {
|
|
241
326
|
try {
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scope:promote --from-issue reciprocity gate (#1136 / D18).
|
|
3
|
+
*
|
|
4
|
+
* Gates promote of a proposed scope on the latest triage-cache decision for
|
|
5
|
+
* the issue, locates the proposed artifact via provenance scan, and runs the
|
|
6
|
+
* shared promotePath path (WIP rules unchanged).
|
|
7
|
+
*/
|
|
8
|
+
import type { AuditEntry } from "../triage/actions/types.js";
|
|
9
|
+
import { type PromotePathResult } from "./promote-path.js";
|
|
10
|
+
export interface PromoteFromIssueOptions {
|
|
11
|
+
readonly issueNumber: number;
|
|
12
|
+
readonly repo?: string | null;
|
|
13
|
+
readonly projectRoot?: string;
|
|
14
|
+
/** WIP-cap override. */
|
|
15
|
+
readonly force?: boolean;
|
|
16
|
+
/** Skip reciprocity gate; still audit that force was used. */
|
|
17
|
+
readonly forceNoCache?: boolean;
|
|
18
|
+
/** Missing decision becomes hard fail (default: soft warn + proceed). */
|
|
19
|
+
readonly strict?: boolean;
|
|
20
|
+
readonly actor?: string;
|
|
21
|
+
/** Explicit path when multiple proposed artifacts match the issue. */
|
|
22
|
+
readonly explicitPath?: string;
|
|
23
|
+
readonly now?: Date;
|
|
24
|
+
/** Optional candidates-log path override (tests). */
|
|
25
|
+
readonly candidatesLogPath?: string;
|
|
26
|
+
/** Inject latest decision (tests). */
|
|
27
|
+
readonly latestDecision?: AuditEntry | null;
|
|
28
|
+
}
|
|
29
|
+
export interface PromoteFromIssueResult extends PromotePathResult {
|
|
30
|
+
readonly warnings: string[];
|
|
31
|
+
readonly repo: string | null;
|
|
32
|
+
readonly matchedPaths: string[];
|
|
33
|
+
readonly cacheStateAtPromote: string | null;
|
|
34
|
+
readonly cacheDecisionId: string | null;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Locate lifecycle artifacts whose provenance owns ``issueNumber`` (optionally repo-scoped).
|
|
38
|
+
*/
|
|
39
|
+
export declare function findLifecycleArtifactsForIssue(projectRoot: string, issueNumber: number, options?: {
|
|
40
|
+
folder?: "proposed" | "pending";
|
|
41
|
+
repo?: string | null;
|
|
42
|
+
}): string[];
|
|
43
|
+
/** Locate proposed/ artifacts for issue N (repo-scoped when provided). */
|
|
44
|
+
export declare function findProposedArtifactsForIssue(projectRoot: string, issueNumber: number, repo?: string | null): string[];
|
|
45
|
+
/**
|
|
46
|
+
* Promote the proposed scope for an issue, gated on triage-cache latestDecision.
|
|
47
|
+
*/
|
|
48
|
+
export declare function promoteFromIssue(options: PromoteFromIssueOptions): PromoteFromIssueResult;
|
|
49
|
+
//# sourceMappingURL=promote-from-issue.d.ts.map
|