@deftai/directive-core 0.88.0 → 0.90.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/actions.d.ts +55 -0
- package/dist/authz/actions.js +125 -0
- package/dist/authz/classify.d.ts +23 -0
- package/dist/authz/classify.js +217 -0
- package/dist/authz/closed-verb.d.ts +42 -0
- package/dist/authz/closed-verb.js +279 -0
- package/dist/authz/evaluate.d.ts +41 -0
- package/dist/authz/evaluate.js +298 -0
- package/dist/authz/index.d.ts +15 -0
- package/dist/authz/index.js +15 -0
- package/dist/authz/origin.d.ts +28 -0
- package/dist/authz/origin.js +64 -0
- package/dist/authz/paths.d.ts +6 -0
- package/dist/authz/paths.js +19 -0
- package/dist/authz/store.d.ts +40 -0
- package/dist/authz/store.js +317 -0
- package/dist/authz/templates.d.ts +139 -0
- package/dist/authz/templates.js +241 -0
- package/dist/authz/types.d.ts +111 -0
- package/dist/authz/types.js +41 -0
- package/dist/authz/verb-classification.d.ts +44 -0
- package/dist/authz/verb-classification.js +237 -0
- package/dist/branch/evaluate.js +6 -1
- package/dist/cache/fetch.js +10 -5
- package/dist/cache/io.d.ts +9 -2
- package/dist/cache/io.js +30 -10
- package/dist/cache/task-cache/store.js +11 -7
- package/dist/capacity/backfill.js +10 -4
- package/dist/category-b-namespace/index.js +10 -4
- package/dist/codebase/default-extractor.js +3 -0
- package/dist/codebase/map.js +11 -4
- package/dist/doctor/constants.d.ts +1 -1
- package/dist/doctor/constants.js +2 -0
- package/dist/doctor/doctor-state.js +28 -4
- package/dist/doctor/flags.js +21 -1
- package/dist/doctor/index.d.ts +1 -0
- package/dist/doctor/index.js +1 -0
- package/dist/doctor/main.d.ts +10 -0
- package/dist/doctor/main.js +182 -0
- package/dist/doctor/openclaw-skills.d.ts +109 -0
- package/dist/doctor/openclaw-skills.js +463 -0
- package/dist/doctor/types.d.ts +22 -0
- package/dist/escalation/actions.d.ts +63 -0
- package/dist/escalation/actions.js +137 -0
- package/dist/escalation/index.d.ts +8 -0
- package/dist/escalation/index.js +8 -0
- package/dist/escalation/paths.d.ts +3 -0
- package/dist/escalation/paths.js +10 -0
- package/dist/escalation/store.d.ts +17 -0
- package/dist/escalation/store.js +172 -0
- package/dist/escalation/types.d.ts +73 -0
- package/dist/escalation/types.js +43 -0
- package/dist/eval/crud-telemetry.js +30 -10
- package/dist/eval/health.js +22 -7
- package/dist/eval/readback.js +11 -10
- package/dist/eval/run.js +32 -16
- package/dist/events/attribution-enrichment.js +10 -4
- package/dist/finish-loop/directive-finish-loop.d.ts +37 -0
- package/dist/finish-loop/directive-finish-loop.js +239 -0
- package/dist/finish-loop/grant-gate.d.ts +31 -0
- package/dist/finish-loop/grant-gate.js +169 -0
- package/dist/finish-loop/index.d.ts +11 -0
- package/dist/finish-loop/index.js +11 -0
- package/dist/finish-loop/main.d.ts +35 -0
- package/dist/finish-loop/main.js +365 -0
- package/dist/finish-loop/pr-finish-loop.d.ts +34 -0
- package/dist/finish-loop/pr-finish-loop.js +217 -0
- package/dist/finish-loop/progress.d.ts +17 -0
- package/dist/finish-loop/progress.js +45 -0
- package/dist/finish-loop/queue.d.ts +16 -0
- package/dist/finish-loop/queue.js +65 -0
- package/dist/finish-loop/types.d.ts +52 -0
- package/dist/finish-loop/types.js +10 -0
- package/dist/fs/contained-write.d.ts +101 -0
- package/dist/fs/contained-write.js +281 -0
- package/dist/hooks/classify/classify.d.ts +10 -0
- package/dist/hooks/classify/classify.js +37 -0
- package/dist/hooks/classify/index.d.ts +14 -0
- package/dist/hooks/classify/index.js +13 -0
- package/dist/hooks/classify/paths.d.ts +22 -0
- package/dist/hooks/classify/paths.js +72 -0
- package/dist/hooks/classify/payload.d.ts +16 -0
- package/dist/hooks/classify/payload.js +45 -0
- package/dist/hooks/classify/stdin.d.ts +12 -0
- package/dist/hooks/classify/stdin.js +63 -0
- package/dist/hooks/classify/tool-name.d.ts +18 -0
- package/dist/hooks/classify/tool-name.js +85 -0
- package/dist/hooks/classify/types.d.ts +41 -0
- package/dist/hooks/classify/types.js +7 -0
- package/dist/hooks/dispatcher.d.ts +21 -18
- package/dist/hooks/dispatcher.js +232 -168
- package/dist/hooks/fixtures/cases.d.ts +44 -0
- package/dist/hooks/fixtures/cases.js +595 -0
- package/dist/hooks/fixtures/index.d.ts +2 -0
- package/dist/hooks/fixtures/index.js +2 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -0
- package/dist/init-deposit/agent-hooks.js +27 -8
- package/dist/init-deposit/gitignore.js +11 -3
- package/dist/init-deposit/headless-manifest.js +12 -4
- package/dist/init-deposit/migrate.d.ts +1 -1
- package/dist/init-deposit/migrate.js +13 -3
- package/dist/init-deposit/prettierignore.js +10 -3
- package/dist/init-deposit/scaffold.js +35 -26
- package/dist/init-deposit/xbrief-projections.js +11 -4
- package/dist/intake/candidates-log.js +46 -35
- package/dist/intake/github-body-cli.d.ts +6 -0
- package/dist/intake/github-body-cli.js +17 -0
- package/dist/intake/github-body.d.ts +46 -3
- package/dist/intake/github-body.js +191 -20
- package/dist/intake/issue-emit.d.ts +1 -0
- package/dist/intake/issue-emit.js +41 -23
- package/dist/lifecycle/event-detect.js +12 -4
- package/dist/lifecycle/events.js +31 -20
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/lifecycle/lifecycle-hygiene.js +9 -3
- package/dist/lifecycle/stats.d.ts +53 -0
- package/dist/lifecycle/stats.js +286 -0
- package/dist/packs/pack-render.js +19 -6
- package/dist/plan-sequence/store.js +11 -4
- package/dist/policy/hotfix-criteria.d.ts +79 -0
- package/dist/policy/hotfix-criteria.js +197 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.js +31 -1
- package/dist/policy/intent-ceiling.d.ts +79 -0
- package/dist/policy/intent-ceiling.js +181 -0
- package/dist/policy/no-deft-directive.js +10 -3
- package/dist/policy/org-force-on-migration.js +9 -5
- package/dist/policy/require-human-merge.d.ts +75 -0
- package/dist/policy/require-human-merge.js +324 -0
- package/dist/policy/resolve.js +17 -6
- package/dist/policy/runtime-authority.d.ts +15 -2
- package/dist/policy/runtime-authority.js +23 -3
- package/dist/policy/write-fence.d.ts +78 -0
- package/dist/policy/write-fence.js +164 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +10 -0
- package/dist/pr-wait-mergeable/cascade.js +28 -0
- package/dist/preflight/evaluate.js +10 -0
- package/dist/product-signal/consent.js +21 -5
- package/dist/product-signal/submit.js +22 -12
- package/dist/release/build-dist-runner.js +5 -2
- package/dist/release/build-dist.d.ts +19 -1
- package/dist/release/build-dist.js +50 -2
- package/dist/release/native-steps.js +7 -2
- package/dist/release/spawn.js +13 -0
- package/dist/release-e2e/git-ops.d.ts +7 -0
- package/dist/release-e2e/git-ops.js +35 -2
- package/dist/release-publish/pipeline.d.ts +13 -0
- package/dist/release-publish/pipeline.js +46 -1
- package/dist/scope/audit-log.js +19 -24
- package/dist/scope/decompose.js +10 -5
- package/dist/scope/decomposed-refs.js +18 -3
- package/dist/scope/demote.js +9 -2
- package/dist/scope/undo.js +9 -2
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/process-cost-constants.d.ts +9 -0
- package/dist/session/process-cost-constants.js +11 -0
- package/dist/session/process-cost.d.ts +53 -0
- package/dist/session/process-cost.js +82 -0
- package/dist/session/release-availability.js +26 -6
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +31 -13
- package/dist/session/session-ready.d.ts +67 -0
- package/dist/session/session-ready.js +264 -0
- package/dist/session/session-start.d.ts +56 -1
- package/dist/session/session-start.js +384 -23
- package/dist/session/verify-session-ritual.d.ts +8 -0
- package/dist/session/verify-session-ritual.js +95 -35
- package/dist/staleness-tickler/state.js +26 -6
- package/dist/swarm/launch.js +13 -3
- package/dist/swarm/routing.js +9 -3
- package/dist/task-surface/index.js +24 -9
- package/dist/tool-events/classify.d.ts +20 -0
- package/dist/tool-events/classify.js +634 -0
- package/dist/tool-events/index.d.ts +10 -0
- package/dist/tool-events/index.js +10 -0
- package/dist/tool-events/summarize.d.ts +34 -0
- package/dist/tool-events/summarize.js +93 -0
- package/dist/tool-events/types.d.ts +52 -0
- package/dist/tool-events/types.js +13 -0
- package/dist/triage/bootstrap/gitignore.js +53 -10
- package/dist/triage/cache-path.js +10 -3
- package/dist/triage/welcome/summary.js +11 -4
- package/dist/triage/welcome/writers.js +45 -16
- package/dist/validate-content/validate-links.js +5 -0
- package/dist/value/readback.js +11 -6
- package/dist/vbrief-activate/activate.js +12 -4
- package/dist/vbrief-build/project-definition-io.js +14 -6
- package/dist/verify-source/contained-writes.d.ts +59 -0
- package/dist/verify-source/contained-writes.js +272 -0
- package/dist/verify-source/index.d.ts +1 -0
- package/dist/verify-source/index.js +1 -0
- package/dist/verify-source/openclaw-tier1.js +14 -1
- package/dist/verify-source/verify-stubs.js +3 -0
- package/dist/xbrief-migrate/migrate-project.js +26 -12
- package/dist/xbrief-migrate/transforms.d.ts +4 -2
- package/dist/xbrief-migrate/transforms.js +37 -14
- package/package.json +19 -3
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified path write fence (#516 / #2443 / #2948 Wave 3).
|
|
3
|
+
*
|
|
4
|
+
* Single evaluation SoT: path checks always go through
|
|
5
|
+
* `evaluateRuntimeAuthorityPath` / `evaluateRuntimeAuthorityDirectWrite`.
|
|
6
|
+
* This module only **resolves** a RuntimeAuthorityPolicy by intersecting
|
|
7
|
+
* project `runtimeAuthority.allowPaths` / `denyPaths` with optional story
|
|
8
|
+
* `plan.metadata.swarm.file_scope` (and a read-time `writeScope` alias).
|
|
9
|
+
*
|
|
10
|
+
* There is no second independent writeScope evaluation engine.
|
|
11
|
+
*/
|
|
12
|
+
import { readFileSync } from "node:fs";
|
|
13
|
+
import { DEFAULT_RUNTIME_AUTHORITY_POLICY, DEFAULT_RUNTIME_AUTHORITY_SCOPES, } from "./runtime-authority.js";
|
|
14
|
+
/** Clean non-empty path glob strings. */
|
|
15
|
+
function cleanGlobs(raw) {
|
|
16
|
+
if (raw === null || raw === undefined)
|
|
17
|
+
return [];
|
|
18
|
+
return raw.filter((s) => typeof s === "string" && s.trim().length > 0);
|
|
19
|
+
}
|
|
20
|
+
function cleanGlobArray(raw) {
|
|
21
|
+
if (!Array.isArray(raw))
|
|
22
|
+
return [];
|
|
23
|
+
return raw.filter((s) => typeof s === "string" && s.trim().length > 0);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Normalize a legacy #2443 `writeScope` alias into `file_scope` + deny globs.
|
|
27
|
+
* Accepts:
|
|
28
|
+
* - `string[]` → allow list
|
|
29
|
+
* - `{ allow?: string[], deny?: string[] }`
|
|
30
|
+
* - `{ file_scope?: string[], denyPaths?: string[] }` (already-normalized shape)
|
|
31
|
+
*
|
|
32
|
+
* Callers MUST feed the result into {@link resolveWriteFence}; do not evaluate
|
|
33
|
+
* writeScope with a separate matcher.
|
|
34
|
+
*/
|
|
35
|
+
export function normalizeStoryWriteScope(raw) {
|
|
36
|
+
if (raw === null || raw === undefined) {
|
|
37
|
+
return { fileScope: [], denyPaths: [] };
|
|
38
|
+
}
|
|
39
|
+
if (Array.isArray(raw)) {
|
|
40
|
+
return { fileScope: cleanGlobArray(raw), denyPaths: [] };
|
|
41
|
+
}
|
|
42
|
+
if (typeof raw !== "object") {
|
|
43
|
+
return { fileScope: [], denyPaths: [] };
|
|
44
|
+
}
|
|
45
|
+
const rec = raw;
|
|
46
|
+
const allow = cleanGlobArray(rec.allow ?? rec.file_scope ?? rec.fileScope);
|
|
47
|
+
const deny = cleanGlobArray(rec.deny ?? rec.denyPaths);
|
|
48
|
+
return { fileScope: allow, denyPaths: deny };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract story write fence from an xBRIEF/vBRIEF document.
|
|
52
|
+
* Prefer `plan.metadata.swarm.file_scope`; fall back to normalized `writeScope`
|
|
53
|
+
* on `plan.metadata.swarm` or `plan.metadata` (read-time alias only).
|
|
54
|
+
*/
|
|
55
|
+
export function extractStoryFileScope(storyData) {
|
|
56
|
+
if (typeof storyData !== "object" || storyData === null || Array.isArray(storyData)) {
|
|
57
|
+
return { fileScope: [], denyPaths: [] };
|
|
58
|
+
}
|
|
59
|
+
const plan = storyData.plan;
|
|
60
|
+
if (typeof plan !== "object" || plan === null || Array.isArray(plan)) {
|
|
61
|
+
return { fileScope: [], denyPaths: [] };
|
|
62
|
+
}
|
|
63
|
+
const metadata = plan.metadata;
|
|
64
|
+
if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
|
|
65
|
+
return { fileScope: [], denyPaths: [] };
|
|
66
|
+
}
|
|
67
|
+
const meta = metadata;
|
|
68
|
+
const swarmRaw = meta.swarm;
|
|
69
|
+
const swarm = typeof swarmRaw === "object" && swarmRaw !== null && !Array.isArray(swarmRaw)
|
|
70
|
+
? swarmRaw
|
|
71
|
+
: null;
|
|
72
|
+
const fromFileScope = cleanGlobArray(swarm?.file_scope ?? swarm?.fileScope);
|
|
73
|
+
const fromWriteScope = normalizeStoryWriteScope(swarm?.writeScope ?? swarm?.write_scope ?? meta.writeScope ?? meta.write_scope);
|
|
74
|
+
// file_scope is SoT when present; writeScope alias only when file_scope empty.
|
|
75
|
+
if (fromFileScope.length > 0) {
|
|
76
|
+
// Still merge writeScope.deny so legacy deny lists are not lost.
|
|
77
|
+
return {
|
|
78
|
+
fileScope: fromFileScope,
|
|
79
|
+
denyPaths: fromWriteScope.denyPaths,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return fromWriteScope;
|
|
83
|
+
}
|
|
84
|
+
/** Load story fence from an active xBRIEF path (fail-open on IO/parse errors). */
|
|
85
|
+
export function loadStoryWriteFenceFromPath(scopePath) {
|
|
86
|
+
if (scopePath === null || scopePath === undefined || scopePath.trim().length === 0) {
|
|
87
|
+
return { fileScope: [], denyPaths: [] };
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const text = readFileSync(scopePath, "utf8");
|
|
91
|
+
const data = JSON.parse(text);
|
|
92
|
+
return extractStoryFileScope(data);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Residual: host/session may not surface a readable active story.
|
|
96
|
+
// Fail open at story layer (project policy still applies if enabled).
|
|
97
|
+
return { fileScope: [], denyPaths: [] };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Intersect project `runtimeAuthority` with optional story `file_scope`.
|
|
102
|
+
*
|
|
103
|
+
* Rules:
|
|
104
|
+
* - Empty project `allowPaths` when enabled = all paths until story narrows.
|
|
105
|
+
* - Empty story scope = project policy only.
|
|
106
|
+
* - Story `file_scope` alone enables a fence even when project policy is disabled.
|
|
107
|
+
* - `denyPaths` always win (project + story-normalized denys).
|
|
108
|
+
* - When both project allowPaths and story file_scope are non-empty, a path must
|
|
109
|
+
* match **both** layers (AND / intersection semantics via dual allow layers).
|
|
110
|
+
*/
|
|
111
|
+
export function resolveWriteFence(projectPolicy, storyFileScope, options) {
|
|
112
|
+
const storyAllow = cleanGlobs(storyFileScope);
|
|
113
|
+
const storyDeny = cleanGlobs(options?.storyDenyPaths);
|
|
114
|
+
const projectActive = projectPolicy.enabled;
|
|
115
|
+
const storyActive = storyAllow.length > 0 || storyDeny.length > 0;
|
|
116
|
+
if (!projectActive && !storyActive) {
|
|
117
|
+
return {
|
|
118
|
+
policy: DEFAULT_RUNTIME_AUTHORITY_POLICY,
|
|
119
|
+
sources: [],
|
|
120
|
+
fenceActive: false,
|
|
121
|
+
storyAllowPaths: [],
|
|
122
|
+
storyDenyPaths: [],
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const sources = [];
|
|
126
|
+
if (projectActive)
|
|
127
|
+
sources.push("project");
|
|
128
|
+
if (storyActive)
|
|
129
|
+
sources.push("story");
|
|
130
|
+
// Project denyPaths + story denyPaths; deny always wins at evaluation time.
|
|
131
|
+
const denyPaths = [...projectPolicy.denyPaths, ...storyDeny];
|
|
132
|
+
// Project allow layer only when project policy is enabled (empty = unrestricted until story).
|
|
133
|
+
const allowPaths = projectActive ? [...projectPolicy.allowPaths] : [];
|
|
134
|
+
// Story-only: enable fence with default scopes (edits true) so direct writes are gated.
|
|
135
|
+
const scopes = projectActive
|
|
136
|
+
? projectPolicy.scopes
|
|
137
|
+
: { ...DEFAULT_RUNTIME_AUTHORITY_SCOPES, edits: true };
|
|
138
|
+
const policy = {
|
|
139
|
+
enabled: true,
|
|
140
|
+
allowPaths,
|
|
141
|
+
denyPaths,
|
|
142
|
+
scopes,
|
|
143
|
+
storyAllowPaths: storyAllow,
|
|
144
|
+
fenceSources: sources,
|
|
145
|
+
};
|
|
146
|
+
return {
|
|
147
|
+
policy,
|
|
148
|
+
sources,
|
|
149
|
+
fenceActive: true,
|
|
150
|
+
storyAllowPaths: storyAllow,
|
|
151
|
+
storyDenyPaths: storyDeny,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Resolve the write fence for PreToolUse / product sinks.
|
|
156
|
+
* Composes project policy with story fence extracted from an active xBRIEF path.
|
|
157
|
+
*/
|
|
158
|
+
export function resolveWriteFenceForScope(projectPolicy, scopePath, loadStory = loadStoryWriteFenceFromPath) {
|
|
159
|
+
const story = loadStory(scopePath);
|
|
160
|
+
return resolveWriteFence(projectPolicy, story.fileScope, {
|
|
161
|
+
storyDenyPaths: story.denyPaths,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=write-fence.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AgentMergeEvaluateResult } from "../policy/require-human-merge.js";
|
|
1
2
|
import { type SemanticGreenFn } from "./semantic-green.js";
|
|
2
3
|
import type { MergeFn, MonitorFn, ProtectedCheckFn, WaitMergeableResult } from "./types.js";
|
|
3
4
|
export interface WaitMergeableOptions {
|
|
@@ -11,6 +12,15 @@ export interface WaitMergeableOptions {
|
|
|
11
12
|
readonly requireMasterCiGreen?: boolean;
|
|
12
13
|
/** Target branch override for stale-base comparison (defaults to PR base.ref). */
|
|
13
14
|
readonly baseBranch?: string | null;
|
|
15
|
+
/**
|
|
16
|
+
* Project root for human-merge + intent-ceiling preflight (#1193).
|
|
17
|
+
* Defaults to process.cwd(). Pass explicitly in tests.
|
|
18
|
+
*/
|
|
19
|
+
readonly projectRoot?: string;
|
|
20
|
+
/** Inject agent-merge evaluator (unit tests). */
|
|
21
|
+
readonly agentMergeFn?: (projectRoot: string) => AgentMergeEvaluateResult;
|
|
22
|
+
/** When true, skip human-merge / intent preflight (tests only). */
|
|
23
|
+
readonly skipHumanMergeGate?: boolean;
|
|
14
24
|
}
|
|
15
25
|
/** Run protected-check -> wait -> merge cascade (#1369). */
|
|
16
26
|
export declare function waitMergeableAndMerge(prNumber: number, repo: string, options: {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
|
|
2
|
+
import { evaluateAgentMerge, } from "../policy/require-human-merge.js";
|
|
1
3
|
import { classifyMonitorOutcome, parseMonitorPayload } from "./classify.js";
|
|
2
4
|
import { EXIT_CONFIG_ERROR, EXIT_MERGED, EXIT_TIMEOUT_OR_ESCALATION } from "./constants.js";
|
|
3
5
|
import { makeResult } from "./result.js";
|
|
@@ -17,6 +19,32 @@ export function waitMergeableAndMerge(prNumber, repo, options) {
|
|
|
17
19
|
const mergeFn = options.mergeFn ?? runGhMerge;
|
|
18
20
|
const semanticGreenFn = options.semanticGreenFn ?? evaluateSemanticGreen;
|
|
19
21
|
const protectedIssues = options.protected;
|
|
22
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
23
|
+
// #1193 surface (1): refuse agent merge when requireHumanMerge is true,
|
|
24
|
+
// and refuse when slash-command intent ceiling does not authorize merge.
|
|
25
|
+
if (options.skipHumanMergeGate !== true) {
|
|
26
|
+
const intent = evaluateIntentCeilingFromEnv("merge");
|
|
27
|
+
if (!intent.allowed) {
|
|
28
|
+
return makeResult({
|
|
29
|
+
prNumber,
|
|
30
|
+
repo,
|
|
31
|
+
outcome: "config-error",
|
|
32
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
33
|
+
error: intent.reason,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
const agentMergeFn = options.agentMergeFn ?? evaluateAgentMerge;
|
|
37
|
+
const hm = agentMergeFn(projectRoot);
|
|
38
|
+
if (!hm.allowed) {
|
|
39
|
+
return makeResult({
|
|
40
|
+
prNumber,
|
|
41
|
+
repo,
|
|
42
|
+
outcome: "config-error",
|
|
43
|
+
exitCode: EXIT_CONFIG_ERROR,
|
|
44
|
+
error: hm.message,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
20
48
|
let protectedCheckPayload = {};
|
|
21
49
|
if (protectedIssues.length > 0) {
|
|
22
50
|
const [prcRc, prcStdout, prcStderr] = protectedFn(prNumber, repo, protectedIssues);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { basename, dirname } from "node:path";
|
|
3
|
+
import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
|
|
3
4
|
/** Canonical eligibility folder — only vbrief/active/ may spawn implementation. */
|
|
4
5
|
export const ACTIVE_FOLDER = "active";
|
|
5
6
|
/** Canonical eligibility status — only `running` signals an active handoff. */
|
|
@@ -124,6 +125,15 @@ export function evaluate(vbriefPath) {
|
|
|
124
125
|
message: buildReject(path, `plan.status is '${status}' -- only '${ELIGIBLE_STATUS}' is eligible for implementation.`),
|
|
125
126
|
};
|
|
126
127
|
}
|
|
128
|
+
// Slash-command intent containment (#1193 / extends #810): non-implement session
|
|
129
|
+
// verbs must not authorize implementation preflight even when the xBRIEF is active.
|
|
130
|
+
const intent = evaluateIntentCeilingFromEnv("implement");
|
|
131
|
+
if (!intent.allowed) {
|
|
132
|
+
return {
|
|
133
|
+
exitCode: 1,
|
|
134
|
+
message: buildReject(path, intent.reason),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
127
137
|
return {
|
|
128
138
|
exitCode: 0,
|
|
129
139
|
message: `OK ${path} -- ready for implementation.`,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
4
5
|
import { DEFAULT_PRODUCT_SIGNAL_SINK_REPO } from "../policy/product-signal.js";
|
|
5
6
|
import { platformUserConfigDir } from "../user-config/resolve-user-md.js";
|
|
6
7
|
export const PRODUCT_SIGNAL_CONSENT_FILENAME = "product-signal-consent.json";
|
|
@@ -142,8 +143,15 @@ export function grantProductSignalConsent(options = {}) {
|
|
|
142
143
|
sinkRepo,
|
|
143
144
|
};
|
|
144
145
|
const path = resolveProductSignalConsentPath(options);
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
147
|
+
const dir = dirname(path);
|
|
148
|
+
mkdirSync(dir, { recursive: true });
|
|
149
|
+
containedWrite({
|
|
150
|
+
root: resolve(dir),
|
|
151
|
+
target: basename(path),
|
|
152
|
+
data: `${JSON.stringify(record, null, 2)}\n`,
|
|
153
|
+
mode: "replace",
|
|
154
|
+
});
|
|
147
155
|
return record;
|
|
148
156
|
}
|
|
149
157
|
/** Revoke consent by setting revokedAt (#2693 D2). */
|
|
@@ -167,7 +175,15 @@ export function revokeProductSignalConsent(options = {}) {
|
|
|
167
175
|
...existing,
|
|
168
176
|
revokedAt: now.toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
169
177
|
};
|
|
170
|
-
|
|
178
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
179
|
+
const dir = dirname(path);
|
|
180
|
+
mkdirSync(dir, { recursive: true });
|
|
181
|
+
containedWrite({
|
|
182
|
+
root: resolve(dir),
|
|
183
|
+
target: basename(path),
|
|
184
|
+
data: `${JSON.stringify(revoked, null, 2)}\n`,
|
|
185
|
+
mode: "replace",
|
|
186
|
+
});
|
|
171
187
|
return true;
|
|
172
188
|
}
|
|
173
189
|
//# sourceMappingURL=consent.js.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { ContainedWriteError, containedWrite } from "../fs/contained-write.js";
|
|
4
|
+
import { ProjectionContainmentError } from "../fs/projection-containment.js";
|
|
4
5
|
import { enableProductSignal, formatProductSignalStatusLine, resolveProductSignal, } from "../policy/product-signal.js";
|
|
5
6
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
6
7
|
import { resolveActorName } from "./actor-name.js";
|
|
@@ -15,17 +16,26 @@ import { bootstrapProductSignalSink } from "./sink-bootstrap.js";
|
|
|
15
16
|
export const PRODUCT_SIGNAL_LAST_SUBMIT_REL = join(".deft-cache", "product-signal-last-submit.json");
|
|
16
17
|
function recordLastSubmit(projectRoot, outcome, url) {
|
|
17
18
|
const path = resolve(projectRoot, PRODUCT_SIGNAL_LAST_SUBMIT_REL);
|
|
18
|
-
assertWriteTargetSafe(projectRoot, path);
|
|
19
19
|
try {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
21
|
+
const root = resolve(projectRoot);
|
|
22
|
+
containedWrite({
|
|
23
|
+
root,
|
|
24
|
+
target: path,
|
|
25
|
+
data: `${JSON.stringify({
|
|
26
|
+
outcome,
|
|
27
|
+
issueUrl: url,
|
|
28
|
+
at: new Date().toISOString().replace(/\.\d{3}Z$/, "Z"),
|
|
29
|
+
})}\n`,
|
|
30
|
+
mode: "append",
|
|
31
|
+
});
|
|
26
32
|
}
|
|
27
|
-
catch {
|
|
28
|
-
//
|
|
33
|
+
catch (err) {
|
|
34
|
+
// Containment refusals must fail closed (tests + product path) (#2807 / #2980).
|
|
35
|
+
if (err instanceof ContainedWriteError || err instanceof ProjectionContainmentError) {
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
// other observability I/O failures only
|
|
29
39
|
}
|
|
30
40
|
}
|
|
31
41
|
function readLastSubmitSummary(projectRoot) {
|
|
@@ -134,7 +144,7 @@ export async function submitProductSignal(options) {
|
|
|
134
144
|
recordLastSubmit(root, result.outcome, result.issueUrl ?? null);
|
|
135
145
|
}
|
|
136
146
|
catch (err) {
|
|
137
|
-
if (err instanceof ProjectionContainmentError) {
|
|
147
|
+
if (err instanceof ProjectionContainmentError || err instanceof ContainedWriteError) {
|
|
138
148
|
return {
|
|
139
149
|
outcome: "error-config",
|
|
140
150
|
exitCode: 2,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Subprocess entry for sync callers (release pipeline, task build).
|
|
3
3
|
* Top-level await keeps the process alive until the archive is written.
|
|
4
|
+
* Progress ticks go to stderr so pipeline callers can still capture the
|
|
5
|
+
* archive path on stdout (#2953).
|
|
4
6
|
*/
|
|
5
|
-
import { buildArchive, selectFormat } from "./build-dist.js";
|
|
7
|
+
import { buildArchive, emitBuildProgress, selectFormat } from "./build-dist.js";
|
|
6
8
|
const version = process.argv[2];
|
|
7
9
|
const root = process.argv[3];
|
|
8
10
|
if (!version || !root) {
|
|
@@ -11,7 +13,8 @@ if (!version || !root) {
|
|
|
11
13
|
}
|
|
12
14
|
const fmt = selectFormat(process.env.DEFT_BUILD_FORMAT);
|
|
13
15
|
try {
|
|
14
|
-
|
|
16
|
+
process.stderr.write(`build-dist-runner: start version=${version} format=${fmt}\n`);
|
|
17
|
+
const out = await buildArchive(root, version, fmt, { onProgress: emitBuildProgress });
|
|
15
18
|
process.stdout.write(`${out}\n`);
|
|
16
19
|
}
|
|
17
20
|
catch (err) {
|
|
@@ -8,7 +8,25 @@ export declare function iterSourceFiles(root: string, excludes?: ReadonlySet<str
|
|
|
8
8
|
}>;
|
|
9
9
|
export declare function selectFormat(arg: string | null | undefined): "tar" | "zip";
|
|
10
10
|
export declare function outputPath(root: string, version: string, fmt: "tar" | "zip"): string;
|
|
11
|
-
export
|
|
11
|
+
export type BuildProgress = {
|
|
12
|
+
readonly stage: string;
|
|
13
|
+
readonly detail?: string;
|
|
14
|
+
readonly current?: number;
|
|
15
|
+
readonly total?: number;
|
|
16
|
+
};
|
|
17
|
+
export type BuildArchiveOptions = {
|
|
18
|
+
readonly extraExcludes?: readonly string[];
|
|
19
|
+
/** Progress sink; default silent so unit tests stay quiet (#2953). */
|
|
20
|
+
readonly onProgress?: (p: BuildProgress) => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Emit human-readable stage progress for long release packaging runs (#2953).
|
|
24
|
+
* Writes to stderr so stdout stays the archive path for pipeline capture.
|
|
25
|
+
*/
|
|
26
|
+
export declare function emitBuildProgress(progress: BuildProgress, stream?: {
|
|
27
|
+
write: (chunk: string) => unknown;
|
|
28
|
+
}): void;
|
|
29
|
+
export declare function buildArchive(root: string, version: string, fmt: "tar" | "zip", options?: readonly string[] | BuildArchiveOptions): Promise<string>;
|
|
12
30
|
export declare function parseExtraExcludes(raw: string): string[];
|
|
13
31
|
export declare function main(argv: readonly string[]): Promise<number>;
|
|
14
32
|
//# sourceMappingURL=build-dist.d.ts.map
|
|
@@ -16,6 +16,9 @@ export const DEFAULT_EXCLUDES = new Set([
|
|
|
16
16
|
".ruff_cache",
|
|
17
17
|
".coverage",
|
|
18
18
|
"coverage",
|
|
19
|
+
// Scratch worktrees are never shippable framework content (#2953).
|
|
20
|
+
".deft-scratch",
|
|
21
|
+
"swarm-worktrees",
|
|
19
22
|
]);
|
|
20
23
|
export const DEFAULT_EXCLUDED_PATH_PREFIXES = [
|
|
21
24
|
"history/archive",
|
|
@@ -97,14 +100,46 @@ export function outputPath(root, version, fmt) {
|
|
|
97
100
|
const suffix = fmt === "zip" ? "zip" : "tar.gz";
|
|
98
101
|
return join(root, "dist", `deft-${version}.${suffix}`);
|
|
99
102
|
}
|
|
100
|
-
|
|
103
|
+
/**
|
|
104
|
+
* Emit human-readable stage progress for long release packaging runs (#2953).
|
|
105
|
+
* Writes to stderr so stdout stays the archive path for pipeline capture.
|
|
106
|
+
*/
|
|
107
|
+
export function emitBuildProgress(progress, stream = process.stderr) {
|
|
108
|
+
const { stage, detail, current, total } = progress;
|
|
109
|
+
let line = `build-dist: ${stage}`;
|
|
110
|
+
if (typeof current === "number" && typeof total === "number" && total > 0) {
|
|
111
|
+
const pct = Math.min(100, Math.floor((current / total) * 100));
|
|
112
|
+
line += ` ${current}/${total} (${pct}%)`;
|
|
113
|
+
}
|
|
114
|
+
if (detail)
|
|
115
|
+
line += ` - ${detail}`;
|
|
116
|
+
stream.write(`${line}\n`);
|
|
117
|
+
}
|
|
118
|
+
function isExtraExcludesList(options) {
|
|
119
|
+
return Array.isArray(options);
|
|
120
|
+
}
|
|
121
|
+
function resolveBuildOptions(options = {}) {
|
|
122
|
+
// Backward-compatible: 4th arg may be a bare extra-excludes array (pre-#2953).
|
|
123
|
+
if (isExtraExcludesList(options)) {
|
|
124
|
+
return { extraExcludes: options, onProgress: () => { } };
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
extraExcludes: options.extraExcludes ?? [],
|
|
128
|
+
onProgress: options.onProgress ?? (() => { }),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export async function buildArchive(root, version, fmt, options = {}) {
|
|
132
|
+
const { extraExcludes, onProgress } = resolveBuildOptions(options);
|
|
101
133
|
const excludes = new Set([...DEFAULT_EXCLUDES, ...extraExcludes]);
|
|
102
134
|
const output = outputPath(root, version, fmt);
|
|
103
135
|
mkdirSync(dirname(output), { recursive: true });
|
|
104
136
|
if (existsSync(output)) {
|
|
105
137
|
unlinkSync(output);
|
|
106
138
|
}
|
|
139
|
+
onProgress({ stage: "scan", detail: "enumerating source files" });
|
|
107
140
|
const entries = iterSourceFiles(root, excludes);
|
|
141
|
+
const total = entries.length;
|
|
142
|
+
onProgress({ stage: "scan", detail: `found ${total} files`, current: total, total });
|
|
108
143
|
await new Promise((resolvePromise, reject) => {
|
|
109
144
|
const out = createWriteStream(output);
|
|
110
145
|
const archive = fmt === "zip"
|
|
@@ -114,11 +149,21 @@ export async function buildArchive(root, version, fmt, extraExcludes = []) {
|
|
|
114
149
|
out.on("error", (err) => reject(err));
|
|
115
150
|
archive.on("error", (err) => reject(err));
|
|
116
151
|
archive.pipe(out);
|
|
152
|
+
onProgress({ stage: "pack", detail: `format=${fmt}`, current: 0, total });
|
|
153
|
+
// Progress ticks every ~5% (or every 250 files) so long packs do not look hung.
|
|
154
|
+
const tickEvery = Math.max(1, Math.min(250, Math.ceil(total / 20)));
|
|
155
|
+
let i = 0;
|
|
117
156
|
for (const { absPath, archiveRel } of entries) {
|
|
118
157
|
archive.file(absPath, { name: `${ARCHIVE_ROOT}/${archiveRel}` });
|
|
158
|
+
i += 1;
|
|
159
|
+
if (i === total || i % tickEvery === 0) {
|
|
160
|
+
onProgress({ stage: "pack", current: i, total });
|
|
161
|
+
}
|
|
119
162
|
}
|
|
163
|
+
onProgress({ stage: "finalize", detail: output });
|
|
120
164
|
void archive.finalize();
|
|
121
165
|
});
|
|
166
|
+
onProgress({ stage: "done", detail: output, current: total, total });
|
|
122
167
|
return output;
|
|
123
168
|
}
|
|
124
169
|
export function parseExtraExcludes(raw) {
|
|
@@ -159,7 +204,10 @@ export async function main(argv) {
|
|
|
159
204
|
}
|
|
160
205
|
const fmt = selectFormat(fmtArg);
|
|
161
206
|
try {
|
|
162
|
-
const out = await buildArchive(projectRoot, version, fmt,
|
|
207
|
+
const out = await buildArchive(projectRoot, version, fmt, {
|
|
208
|
+
extraExcludes: parseExtraExcludes(extra),
|
|
209
|
+
onProgress: emitBuildProgress,
|
|
210
|
+
});
|
|
163
211
|
const printable = relative(projectRoot, out) || out;
|
|
164
212
|
process.stdout.write(`Created ${printable}\n`);
|
|
165
213
|
return 0;
|
|
@@ -79,16 +79,21 @@ export function runBuildNative(projectRoot, version, _seams = {}) {
|
|
|
79
79
|
return [false, "build requires a release version"];
|
|
80
80
|
}
|
|
81
81
|
const env = { ...process.env, DEFT_RELEASE_VERSION: version };
|
|
82
|
+
// Inherit stderr so build-dist-runner progress ticks stream live during
|
|
83
|
+
// release Step 8; capture stdout only for the archive path (#2953).
|
|
82
84
|
const result = spawnSync(process.execPath, [BUILD_DIST_RUNNER, version, projectRoot], {
|
|
83
85
|
cwd: projectRoot,
|
|
84
86
|
encoding: "utf8",
|
|
85
87
|
env,
|
|
88
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
86
89
|
});
|
|
87
90
|
if (result.status !== 0) {
|
|
88
|
-
const
|
|
91
|
+
const captured = (typeof result.stderr === "string" ? result.stderr : "") ||
|
|
92
|
+
(typeof result.stdout === "string" ? result.stdout : "");
|
|
93
|
+
const msg = captured.trim() || `exit ${result.status ?? 1}`;
|
|
89
94
|
return [false, `build failed: ${msg}`];
|
|
90
95
|
}
|
|
91
|
-
const out = (result.stdout
|
|
96
|
+
const out = (typeof result.stdout === "string" ? result.stdout : "").trim();
|
|
92
97
|
return [true, `build ran clean (DEFT_RELEASE_VERSION=${version}) -> ${out}`];
|
|
93
98
|
}
|
|
94
99
|
//# sourceMappingURL=native-steps.js.map
|
package/dist/release/spawn.js
CHANGED
|
@@ -24,6 +24,19 @@ export function spawnText(cmd, args, options = {}) {
|
|
|
24
24
|
if (status === null) {
|
|
25
25
|
if (result.signal !== null && result.signal !== undefined) {
|
|
26
26
|
status = 128;
|
|
27
|
+
// Timeout kills leave stderr empty; surface timeout so e2e never reports
|
|
28
|
+
// "failed: " with a blank reason (#3004 / #1867).
|
|
29
|
+
if (stderr.trim().length === 0) {
|
|
30
|
+
if (result.error) {
|
|
31
|
+
stderr = result.error.message;
|
|
32
|
+
}
|
|
33
|
+
else if (options.timeoutMs !== undefined) {
|
|
34
|
+
stderr = `process terminated by signal ${result.signal} (timeout ${options.timeoutMs}ms)`;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
stderr = `process terminated by signal ${result.signal}`;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
}
|
|
28
41
|
else if (result.error) {
|
|
29
42
|
status = 2;
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
+
import type { SpawnResult } from "../release/types.js";
|
|
1
2
|
import type { E2ESeams } from "./types.js";
|
|
3
|
+
/** Mirror push can exceed the generic release `runGit` 30s cap (#3004). Align with clone. */
|
|
4
|
+
export declare const PUSH_MIRROR_TIMEOUT_MS = 300000;
|
|
5
|
+
/** Prefer stderr; never leave a blank reason after "failed:" (#3004). */
|
|
6
|
+
export declare function formatGitOpFailure(op: string, result: SpawnResult, options?: {
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
}): string;
|
|
2
9
|
export declare function cloneRepoToTemp(projectRoot: string, targetDir: string, seams?: E2ESeams): [boolean, string];
|
|
3
10
|
export declare function setOriginToTempRepo(cloneDir: string, owner: string, slug: string, seams?: E2ESeams): [boolean, string];
|
|
4
11
|
export declare function pushMirror(cloneDir: string, seams?: E2ESeams): [boolean, string];
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import { runGit as releaseRunGit } from "../release/git.js";
|
|
2
2
|
import { spawnText } from "../release/spawn.js";
|
|
3
|
+
/** Mirror push can exceed the generic release `runGit` 30s cap (#3004). Align with clone. */
|
|
4
|
+
export const PUSH_MIRROR_TIMEOUT_MS = 300_000;
|
|
3
5
|
function defaultRunGit(projectRoot, args, env, seams = {}) {
|
|
4
6
|
if (seams.runGit) {
|
|
5
7
|
return seams.runGit(projectRoot, args, env);
|
|
6
8
|
}
|
|
7
9
|
return releaseRunGit(projectRoot, args, { spawnText: seams.spawnText ?? spawnText }, env);
|
|
8
10
|
}
|
|
11
|
+
/** Prefer stderr; never leave a blank reason after "failed:" (#3004). */
|
|
12
|
+
export function formatGitOpFailure(op, result, options = {}) {
|
|
13
|
+
const stderr = result.stderr.trim();
|
|
14
|
+
if (stderr.length > 0) {
|
|
15
|
+
return `${op} failed: ${stderr}`;
|
|
16
|
+
}
|
|
17
|
+
const timeoutHint = options.timeoutMs !== undefined ? `; possible timeout after ${options.timeoutMs}ms` : "";
|
|
18
|
+
return `${op} failed: no stderr (exit ${result.status ?? "null"}${timeoutHint})`;
|
|
19
|
+
}
|
|
9
20
|
export function cloneRepoToTemp(projectRoot, targetDir, seams = {}) {
|
|
10
21
|
const env = { ...process.env, DEFT_PROJECT_ROOT: targetDir };
|
|
11
22
|
const spawn = seams.spawnText ?? spawnText;
|
|
@@ -27,9 +38,31 @@ export function setOriginToTempRepo(cloneDir, owner, slug, seams = {}) {
|
|
|
27
38
|
return [true, `origin -> ${url}`];
|
|
28
39
|
}
|
|
29
40
|
export function pushMirror(cloneDir, seams = {}) {
|
|
30
|
-
|
|
41
|
+
// Do not use default release runGit (30s) — full heads+tags mirror of directive
|
|
42
|
+
// routinely exceeds that on first push to an empty temp repo (#3004).
|
|
43
|
+
const pushArgs = [
|
|
44
|
+
"push",
|
|
45
|
+
"origin",
|
|
46
|
+
"refs/heads/*:refs/heads/*",
|
|
47
|
+
"refs/tags/*:refs/tags/*",
|
|
48
|
+
];
|
|
49
|
+
let result;
|
|
50
|
+
if (seams.runGit) {
|
|
51
|
+
result = seams.runGit(cloneDir, [...pushArgs], undefined);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const spawn = seams.spawnText ?? spawnText;
|
|
55
|
+
result = spawn("git", ["-C", cloneDir, ...pushArgs], {
|
|
56
|
+
timeoutMs: PUSH_MIRROR_TIMEOUT_MS,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
31
59
|
if (result.status !== 0) {
|
|
32
|
-
return [
|
|
60
|
+
return [
|
|
61
|
+
false,
|
|
62
|
+
formatGitOpFailure("git push (heads+tags refspecs)", result, {
|
|
63
|
+
timeoutMs: PUSH_MIRROR_TIMEOUT_MS,
|
|
64
|
+
}),
|
|
65
|
+
];
|
|
33
66
|
}
|
|
34
67
|
return [true, "pushed heads + tags to temp origin"];
|
|
35
68
|
}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
+
import { type EnvMap } from "../authz/closed-verb.js";
|
|
2
|
+
import type { ClosedVerbDecision, HumanOriginGrant } from "../authz/types.js";
|
|
1
3
|
import type { PublishConfig, ReleasePublishSeams } from "./types.js";
|
|
2
4
|
export declare function emit(label: string, status: string): void;
|
|
5
|
+
/**
|
|
6
|
+
* Fail-closed gate before draft→public (#1095 Wave 4).
|
|
7
|
+
* Allow only DEFT_ALLOW_RELEASE_PUBLISH=1 or a matching human-origin grant.
|
|
8
|
+
* Loads conventions/verb-classification.json from projectRoot when present.
|
|
9
|
+
* Test seam: pass grants / env via optional overrides on seams when present.
|
|
10
|
+
*/
|
|
11
|
+
export declare function evaluateReleasePublishGate(version: string, projectRoot: string, options?: {
|
|
12
|
+
readonly grants?: readonly HumanOriginGrant[];
|
|
13
|
+
readonly env?: EnvMap;
|
|
14
|
+
readonly repo?: string | null;
|
|
15
|
+
}): ClosedVerbDecision;
|
|
3
16
|
export declare function runPublish(config: PublishConfig, seams?: ReleasePublishSeams): number;
|
|
4
17
|
//# sourceMappingURL=pipeline.d.ts.map
|