@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
package/dist/policy/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { FIELD_HOST_HOOKS, FIELD_HOST_HOOKS_CLI_ALIAS, inspectHostHooks } from "./host-hooks.js";
|
|
2
|
+
import { FIELD_HOTFIX_CRITERIA, FIELD_HOTFIX_CRITERIA_CLI_ALIAS, inspectHotfixCriteria, } from "./hotfix-criteria.js";
|
|
2
3
|
import { readPlanPolicy } from "./plan-extensions.js";
|
|
3
4
|
import { FIELD_PRODUCT_SIGNAL, FIELD_PRODUCT_SIGNAL_CLI_ALIAS, inspectProductSignal, } from "./product-signal.js";
|
|
5
|
+
import { FIELD_REQUIRE_HUMAN_MERGE, FIELD_REQUIRE_HUMAN_MERGE_CLI_ALIAS, inspectRequireHumanMerge, } from "./require-human-merge.js";
|
|
4
6
|
import { coerceLegacyNarrative, LEGACY_NARRATIVE_KEY, loadProjectDefinition } from "./resolve.js";
|
|
5
7
|
import { FIELD_RUNTIME_AUTHORITY, FIELD_RUNTIME_AUTHORITY_CLI_ALIAS, inspectRuntimeAuthority, } from "./runtime-authority.js";
|
|
6
8
|
import { FIELD_STALENESS_TICKLER, FIELD_STALENESS_TICKLER_CLI_ALIAS, inspectStalenessTickler, } from "./staleness-tickler.js";
|
|
@@ -12,17 +14,21 @@ export * from "./capacity.js";
|
|
|
12
14
|
export * from "./decisions.js";
|
|
13
15
|
export * from "./disclosure.js";
|
|
14
16
|
export * from "./host-hooks.js";
|
|
17
|
+
export * from "./hotfix-criteria.js";
|
|
18
|
+
export * from "./intent-ceiling.js";
|
|
15
19
|
export * from "./no-deft-directive.js";
|
|
16
20
|
export * from "./org-force-on-migration.js";
|
|
17
21
|
export * from "./plan-extensions.js";
|
|
18
22
|
export * from "./policy-invocation.js";
|
|
19
23
|
export * from "./product-signal.js";
|
|
24
|
+
export * from "./require-human-merge.js";
|
|
20
25
|
export * from "./resolve.js";
|
|
21
26
|
export * from "./runtime-authority.js";
|
|
22
27
|
export * from "./staleness-tickler.js";
|
|
23
28
|
export * from "./value-feedback.js";
|
|
24
29
|
export * from "./value-feedback-autoenable.js";
|
|
25
30
|
export * from "./wip.js";
|
|
31
|
+
export * from "./write-fence.js";
|
|
26
32
|
export const FIELD_ALLOW_DIRECT_COMMITS = "plan.policy.allowDirectCommitsToMaster";
|
|
27
33
|
export const FIELD_WIP_CAP = "plan.policy.wipCap";
|
|
28
34
|
export const FIELD_SESSION_RITUAL_STALENESS_HOURS = "plan.policy.sessionRitualStalenessHours";
|
|
@@ -272,6 +278,24 @@ function inspectHostHooksField(data) {
|
|
|
272
278
|
source: field.source,
|
|
273
279
|
};
|
|
274
280
|
}
|
|
281
|
+
function inspectRequireHumanMergeField(data, projectRoot) {
|
|
282
|
+
const field = inspectRequireHumanMerge(data, projectRoot);
|
|
283
|
+
return {
|
|
284
|
+
name: field.name,
|
|
285
|
+
current: field.current,
|
|
286
|
+
default: field.default,
|
|
287
|
+
source: field.source,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function inspectHotfixCriteriaField(data) {
|
|
291
|
+
const field = inspectHotfixCriteria(data);
|
|
292
|
+
return {
|
|
293
|
+
name: field.name,
|
|
294
|
+
current: field.current,
|
|
295
|
+
default: field.default,
|
|
296
|
+
source: field.source,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
275
299
|
const REGISTERED_POLICIES = [
|
|
276
300
|
inspectAllowDirectCommits,
|
|
277
301
|
inspectWipCap,
|
|
@@ -289,6 +313,8 @@ const REGISTERED_POLICIES = [
|
|
|
289
313
|
inspectRuntimeAuthorityField,
|
|
290
314
|
inspectProductSignalField,
|
|
291
315
|
inspectValueFeedbackField,
|
|
316
|
+
inspectRequireHumanMergeField,
|
|
317
|
+
inspectHotfixCriteriaField,
|
|
292
318
|
];
|
|
293
319
|
/** Walk registered inspectors and return one row per field (#1148). */
|
|
294
320
|
export function inspectAllPolicies(projectRoot) {
|
|
@@ -307,7 +333,11 @@ export function inspectOnePolicy(name, projectRoot) {
|
|
|
307
333
|
? FIELD_RUNTIME_AUTHORITY
|
|
308
334
|
: name === FIELD_HOST_HOOKS_CLI_ALIAS
|
|
309
335
|
? FIELD_HOST_HOOKS
|
|
310
|
-
: name
|
|
336
|
+
: name === FIELD_REQUIRE_HUMAN_MERGE_CLI_ALIAS
|
|
337
|
+
? FIELD_REQUIRE_HUMAN_MERGE
|
|
338
|
+
: name === FIELD_HOTFIX_CRITERIA_CLI_ALIAS
|
|
339
|
+
? FIELD_HOTFIX_CRITERIA
|
|
340
|
+
: name;
|
|
311
341
|
for (const field of inspectAllPolicies(projectRoot)) {
|
|
312
342
|
if (field.name === normalized)
|
|
313
343
|
return field;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash-command intent containment (#1193 / extends #810).
|
|
3
|
+
*
|
|
4
|
+
* When a session inherits a slash command as its verb, that verb is the *only*
|
|
5
|
+
* authorized intent for the session. Non-implement verbs must not authorize
|
|
6
|
+
* implement / push / PR / merge / deploy tool paths.
|
|
7
|
+
*
|
|
8
|
+
* Provenance: set `DEFT_SESSION_SLASH_VERB` (e.g. `/github-issue`, `github-issue`,
|
|
9
|
+
* `/build`) for the session, or pass `sessionVerb` explicitly to pure evaluators.
|
|
10
|
+
*/
|
|
11
|
+
/** Env var carrying the session's slash-command verb (#1193). */
|
|
12
|
+
export declare const ENV_SESSION_SLASH_VERB = "DEFT_SESSION_SLASH_VERB";
|
|
13
|
+
/**
|
|
14
|
+
* Free-text action verbs that authorize implementation under #810.
|
|
15
|
+
* Parallel list for slash forms lives in IMPLEMENT_SLASH_VERBS.
|
|
16
|
+
*/
|
|
17
|
+
export declare const FREE_TEXT_IMPLEMENT_VERBS: readonly ["build", "implement", "ship", "swarm", "run agents", "start agent"];
|
|
18
|
+
/**
|
|
19
|
+
* Slash-command stems that authorize implementation (#1193 R1).
|
|
20
|
+
* Matched after normalizing `/prefix` and `:namespace` forms.
|
|
21
|
+
*/
|
|
22
|
+
export declare const IMPLEMENT_SLASH_VERBS: Set<string>;
|
|
23
|
+
/**
|
|
24
|
+
* Explicit non-implement slash stems — always contained.
|
|
25
|
+
* Unknown stems default to non-implement (fail closed for lifecycle escalation).
|
|
26
|
+
*/
|
|
27
|
+
export declare const NON_IMPLEMENT_SLASH_VERBS: Set<string>;
|
|
28
|
+
/** Lifecycle ops gated by the intent ceiling. */
|
|
29
|
+
export declare const INTENT_CEILING_OPS: readonly ["implement", "push", "pr", "merge", "deploy"];
|
|
30
|
+
export type IntentCeilingOp = (typeof INTENT_CEILING_OPS)[number];
|
|
31
|
+
export type IntentCeilingCode = "intent-allow-no-slash" | "intent-allow-implement-verb" | "intent-deny-non-implement" | "intent-deny-unknown-slash";
|
|
32
|
+
export interface IntentCeilingDecision {
|
|
33
|
+
readonly allowed: boolean;
|
|
34
|
+
readonly code: IntentCeilingCode;
|
|
35
|
+
readonly reason: string;
|
|
36
|
+
/** Normalized slash stem (without leading `/`), or null when no slash provenance. */
|
|
37
|
+
readonly sessionVerb: string | null;
|
|
38
|
+
readonly requestedOp: IntentCeilingOp;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a slash command or free-text verb to a comparable stem.
|
|
42
|
+
* `/deft:directive:github-issue` → `github-issue`; `/build` → `build`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeSessionVerb(raw: string | null | undefined): string | null;
|
|
45
|
+
/** True when the normalized stem authorizes implementation. */
|
|
46
|
+
export declare function isImplementSlashVerb(stem: string | null): boolean;
|
|
47
|
+
/** True when the stem is an explicitly known non-implement command. */
|
|
48
|
+
export declare function isNonImplementSlashVerb(stem: string | null): boolean;
|
|
49
|
+
export interface EvaluateIntentCeilingInput {
|
|
50
|
+
readonly sessionVerb?: string | null;
|
|
51
|
+
readonly requestedOp: IntentCeilingOp;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pure intent-ceiling evaluator (#1193 R1).
|
|
55
|
+
*
|
|
56
|
+
* - No slash verb → allow (free-text #810 action-verb remains agent/AGENTS gate).
|
|
57
|
+
* - Implement slash verb → allow all lifecycle ops.
|
|
58
|
+
* - Non-implement / unknown slash verb → deny implement/push/pr/merge/deploy.
|
|
59
|
+
*/
|
|
60
|
+
export declare function evaluateIntentCeiling(input: EvaluateIntentCeilingInput): IntentCeilingDecision;
|
|
61
|
+
/** Read session slash verb from the environment. */
|
|
62
|
+
export declare function readSessionSlashVerbFromEnv(env?: NodeJS.ProcessEnv): string | null;
|
|
63
|
+
/**
|
|
64
|
+
* Evaluate intent ceiling for a lifecycle op using env-backed session verb.
|
|
65
|
+
* Convenience for preflight / hooks / merge gates.
|
|
66
|
+
*/
|
|
67
|
+
export declare function evaluateIntentCeilingFromEnv(requestedOp: IntentCeilingOp, env?: NodeJS.ProcessEnv): IntentCeilingDecision;
|
|
68
|
+
/**
|
|
69
|
+
* Map classifiable shell/MCP lifecycle signals onto intent-ceiling ops.
|
|
70
|
+
* Returns null when the tool path is not a gated lifecycle op.
|
|
71
|
+
*/
|
|
72
|
+
export declare function intentOpFromShellSignals(signals: {
|
|
73
|
+
readonly isPush?: boolean;
|
|
74
|
+
readonly isMerge?: boolean;
|
|
75
|
+
readonly isPr?: boolean;
|
|
76
|
+
readonly isDeploy?: boolean;
|
|
77
|
+
readonly isImplement?: boolean;
|
|
78
|
+
}): IntentCeilingOp | null;
|
|
79
|
+
//# sourceMappingURL=intent-ceiling.d.ts.map
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slash-command intent containment (#1193 / extends #810).
|
|
3
|
+
*
|
|
4
|
+
* When a session inherits a slash command as its verb, that verb is the *only*
|
|
5
|
+
* authorized intent for the session. Non-implement verbs must not authorize
|
|
6
|
+
* implement / push / PR / merge / deploy tool paths.
|
|
7
|
+
*
|
|
8
|
+
* Provenance: set `DEFT_SESSION_SLASH_VERB` (e.g. `/github-issue`, `github-issue`,
|
|
9
|
+
* `/build`) for the session, or pass `sessionVerb` explicitly to pure evaluators.
|
|
10
|
+
*/
|
|
11
|
+
/** Env var carrying the session's slash-command verb (#1193). */
|
|
12
|
+
export const ENV_SESSION_SLASH_VERB = "DEFT_SESSION_SLASH_VERB";
|
|
13
|
+
/**
|
|
14
|
+
* Free-text action verbs that authorize implementation under #810.
|
|
15
|
+
* Parallel list for slash forms lives in IMPLEMENT_SLASH_VERBS.
|
|
16
|
+
*/
|
|
17
|
+
export const FREE_TEXT_IMPLEMENT_VERBS = [
|
|
18
|
+
"build",
|
|
19
|
+
"implement",
|
|
20
|
+
"ship",
|
|
21
|
+
"swarm",
|
|
22
|
+
"run agents",
|
|
23
|
+
"start agent",
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Slash-command stems that authorize implementation (#1193 R1).
|
|
27
|
+
* Matched after normalizing `/prefix` and `:namespace` forms.
|
|
28
|
+
*/
|
|
29
|
+
export const IMPLEMENT_SLASH_VERBS = new Set([
|
|
30
|
+
"build",
|
|
31
|
+
"implement",
|
|
32
|
+
"ship",
|
|
33
|
+
"ship-hotfix",
|
|
34
|
+
"swarm",
|
|
35
|
+
"start-agent",
|
|
36
|
+
"start_agent",
|
|
37
|
+
"run-agents",
|
|
38
|
+
"run_agents",
|
|
39
|
+
]);
|
|
40
|
+
/**
|
|
41
|
+
* Explicit non-implement slash stems — always contained.
|
|
42
|
+
* Unknown stems default to non-implement (fail closed for lifecycle escalation).
|
|
43
|
+
*/
|
|
44
|
+
export const NON_IMPLEMENT_SLASH_VERBS = new Set([
|
|
45
|
+
"github-issue",
|
|
46
|
+
"github_issue",
|
|
47
|
+
"issue",
|
|
48
|
+
"triage",
|
|
49
|
+
"refine",
|
|
50
|
+
"refinement",
|
|
51
|
+
"discuss",
|
|
52
|
+
"research",
|
|
53
|
+
"probe",
|
|
54
|
+
"glossary",
|
|
55
|
+
"sync",
|
|
56
|
+
"continue",
|
|
57
|
+
"checkpoint",
|
|
58
|
+
"feedback",
|
|
59
|
+
"article-review",
|
|
60
|
+
"cost",
|
|
61
|
+
"setup",
|
|
62
|
+
"interview",
|
|
63
|
+
"map",
|
|
64
|
+
"yolo",
|
|
65
|
+
"speckit",
|
|
66
|
+
"change",
|
|
67
|
+
]);
|
|
68
|
+
/** Lifecycle ops gated by the intent ceiling. */
|
|
69
|
+
export const INTENT_CEILING_OPS = ["implement", "push", "pr", "merge", "deploy"];
|
|
70
|
+
/**
|
|
71
|
+
* Normalize a slash command or free-text verb to a comparable stem.
|
|
72
|
+
* `/deft:directive:github-issue` → `github-issue`; `/build` → `build`.
|
|
73
|
+
*/
|
|
74
|
+
export function normalizeSessionVerb(raw) {
|
|
75
|
+
if (raw === null || raw === undefined)
|
|
76
|
+
return null;
|
|
77
|
+
let s = raw.trim().toLowerCase();
|
|
78
|
+
if (s.length === 0)
|
|
79
|
+
return null;
|
|
80
|
+
// Strip leading slashes.
|
|
81
|
+
while (s.startsWith("/"))
|
|
82
|
+
s = s.slice(1);
|
|
83
|
+
// Drop product namespaces: deft:directive:github-issue → github-issue
|
|
84
|
+
if (s.includes(":")) {
|
|
85
|
+
const parts = s.split(":").filter((p) => p.length > 0);
|
|
86
|
+
s = parts[parts.length - 1] ?? s;
|
|
87
|
+
}
|
|
88
|
+
// Normalize underscores to hyphens for allowlist matching.
|
|
89
|
+
s = s.replace(/_/g, "-");
|
|
90
|
+
return s.length > 0 ? s : null;
|
|
91
|
+
}
|
|
92
|
+
/** True when the normalized stem authorizes implementation. */
|
|
93
|
+
export function isImplementSlashVerb(stem) {
|
|
94
|
+
if (stem === null)
|
|
95
|
+
return false;
|
|
96
|
+
if (IMPLEMENT_SLASH_VERBS.has(stem))
|
|
97
|
+
return true;
|
|
98
|
+
// Free-text multi-word forms already hyphenated by normalize (run agents → not here).
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
/** True when the stem is an explicitly known non-implement command. */
|
|
102
|
+
export function isNonImplementSlashVerb(stem) {
|
|
103
|
+
if (stem === null)
|
|
104
|
+
return false;
|
|
105
|
+
return NON_IMPLEMENT_SLASH_VERBS.has(stem);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Pure intent-ceiling evaluator (#1193 R1).
|
|
109
|
+
*
|
|
110
|
+
* - No slash verb → allow (free-text #810 action-verb remains agent/AGENTS gate).
|
|
111
|
+
* - Implement slash verb → allow all lifecycle ops.
|
|
112
|
+
* - Non-implement / unknown slash verb → deny implement/push/pr/merge/deploy.
|
|
113
|
+
*/
|
|
114
|
+
export function evaluateIntentCeiling(input) {
|
|
115
|
+
const stem = normalizeSessionVerb(input.sessionVerb ?? null);
|
|
116
|
+
const op = input.requestedOp;
|
|
117
|
+
if (stem === null) {
|
|
118
|
+
return {
|
|
119
|
+
allowed: true,
|
|
120
|
+
code: "intent-allow-no-slash",
|
|
121
|
+
reason: "No slash-command session verb; free-text #810 action-verb rules apply.",
|
|
122
|
+
sessionVerb: null,
|
|
123
|
+
requestedOp: op,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (isImplementSlashVerb(stem)) {
|
|
127
|
+
return {
|
|
128
|
+
allowed: true,
|
|
129
|
+
code: "intent-allow-implement-verb",
|
|
130
|
+
reason: `Slash verb '/${stem}' authorizes implementation lifecycle ops.`,
|
|
131
|
+
sessionVerb: stem,
|
|
132
|
+
requestedOp: op,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const knownNon = isNonImplementSlashVerb(stem);
|
|
136
|
+
const code = knownNon
|
|
137
|
+
? "intent-deny-non-implement"
|
|
138
|
+
: "intent-deny-unknown-slash";
|
|
139
|
+
return {
|
|
140
|
+
allowed: false,
|
|
141
|
+
code,
|
|
142
|
+
reason: `Slash-command intent ceiling (#1193): session verb '/${stem}' does not authorize ` +
|
|
143
|
+
`${op}. Non-implement verbs terminate at their named outcome; file a follow-up issue ` +
|
|
144
|
+
`instead of escalating to implement/push/PR/merge/deploy. ` +
|
|
145
|
+
`Implement verbs: /build, /ship, /ship-hotfix, /swarm, /implement.`,
|
|
146
|
+
sessionVerb: stem,
|
|
147
|
+
requestedOp: op,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Read session slash verb from the environment. */
|
|
151
|
+
export function readSessionSlashVerbFromEnv(env = process.env) {
|
|
152
|
+
return normalizeSessionVerb(env[ENV_SESSION_SLASH_VERB] ?? null);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Evaluate intent ceiling for a lifecycle op using env-backed session verb.
|
|
156
|
+
* Convenience for preflight / hooks / merge gates.
|
|
157
|
+
*/
|
|
158
|
+
export function evaluateIntentCeilingFromEnv(requestedOp, env = process.env) {
|
|
159
|
+
return evaluateIntentCeiling({
|
|
160
|
+
sessionVerb: readSessionSlashVerbFromEnv(env),
|
|
161
|
+
requestedOp,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Map classifiable shell/MCP lifecycle signals onto intent-ceiling ops.
|
|
166
|
+
* Returns null when the tool path is not a gated lifecycle op.
|
|
167
|
+
*/
|
|
168
|
+
export function intentOpFromShellSignals(signals) {
|
|
169
|
+
if (signals.isDeploy)
|
|
170
|
+
return "deploy";
|
|
171
|
+
if (signals.isMerge)
|
|
172
|
+
return "merge";
|
|
173
|
+
if (signals.isPr)
|
|
174
|
+
return "pr";
|
|
175
|
+
if (signals.isPush)
|
|
176
|
+
return "push";
|
|
177
|
+
if (signals.isImplement)
|
|
178
|
+
return "implement";
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=intent-ceiling.js.map
|
|
@@ -13,8 +13,9 @@
|
|
|
13
13
|
* mutating install/update paths **fail closed**.
|
|
14
14
|
* - Creating the flag does **not** delete an existing deposit.
|
|
15
15
|
*/
|
|
16
|
-
import { existsSync, statSync, unlinkSync
|
|
16
|
+
import { existsSync, statSync, unlinkSync } from "node:fs";
|
|
17
17
|
import { join, resolve } from "node:path";
|
|
18
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
18
19
|
import { assertWriteTargetSafe } from "../fs/projection-containment.js";
|
|
19
20
|
import { CANONICAL_INSTALL_ROOT } from "../init-deposit/constants.js";
|
|
20
21
|
/** Canonical root-only filename (lowercase). Presence = flag. */
|
|
@@ -75,13 +76,19 @@ export function isNoDeftDirectivePresent(projectRoot, seams = {}) {
|
|
|
75
76
|
*/
|
|
76
77
|
export function createNoDeftDirectiveFlag(projectRoot, options = {}) {
|
|
77
78
|
const path = noDeftDirectiveFlagPath(projectRoot);
|
|
78
|
-
assertWriteTargetSafe(projectRoot, path);
|
|
79
79
|
if (defaultIsDir(path)) {
|
|
80
80
|
throw new Error(`${NO_DEFT_DIRECTIVE_FLAG_NAME} exists as a directory at ${path}; remove it before creating the opt-out flag file.`);
|
|
81
81
|
}
|
|
82
82
|
const rationale = options.rationale?.trim() ?? "";
|
|
83
83
|
const body = rationale.length > 0 ? `# ${rationale}\n` : "";
|
|
84
|
-
|
|
84
|
+
// #2951: product write sinks route through containedWrite (create|replace).
|
|
85
|
+
// Use replace so re-running with a new rationale updates the flag file.
|
|
86
|
+
containedWrite({
|
|
87
|
+
root: resolve(projectRoot),
|
|
88
|
+
target: path,
|
|
89
|
+
data: body,
|
|
90
|
+
mode: "replace",
|
|
91
|
+
});
|
|
85
92
|
return path;
|
|
86
93
|
}
|
|
87
94
|
/**
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { existsSync,
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { sortKeysDeep } from "../codebase/json.js";
|
|
4
4
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
5
|
-
import {
|
|
5
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
6
6
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
7
7
|
import { isNoDeftDirectivePresent } from "./no-deft-directive.js";
|
|
8
8
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
@@ -88,9 +88,13 @@ export function readOrgForceOnMarker(projectRoot) {
|
|
|
88
88
|
}
|
|
89
89
|
function writeOrgForceOnMarker(projectRoot, marker) {
|
|
90
90
|
const path = markerPath(projectRoot);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
// #2951 Phase 2: product write sink routes through containedWrite.
|
|
92
|
+
containedWrite({
|
|
93
|
+
root: resolve(projectRoot),
|
|
94
|
+
target: path,
|
|
95
|
+
data: `${JSON.stringify(marker, null, 2)}\n`,
|
|
96
|
+
mode: "replace",
|
|
97
|
+
});
|
|
94
98
|
}
|
|
95
99
|
function readTypedBoolean(raw, key, fallback) {
|
|
96
100
|
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human merge gate (#1193 R3 / Wave 2 of #2948).
|
|
3
|
+
*
|
|
4
|
+
* When requireHumanMerge is true, agents may open PRs but must not merge.
|
|
5
|
+
* Three enforcement surfaces (mirror #747):
|
|
6
|
+
* (1) merge preflight refuses agent-initiated merge
|
|
7
|
+
* (2) verify:branch aggregate surfaces the policy
|
|
8
|
+
* (3) branch-protection / setup docs require ≥1 human reviewer
|
|
9
|
+
*
|
|
10
|
+
* Override: `task policy:allow-bot-merge -- --confirm` + `DEFT_ALLOW_BOT_MERGE=1`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const FIELD_REQUIRE_HUMAN_MERGE = "plan.policy.requireHumanMerge";
|
|
13
|
+
export declare const FIELD_REQUIRE_HUMAN_MERGE_CLI_ALIAS = "requireHumanMerge";
|
|
14
|
+
export declare const FIELD_AUTO_DEPLOY_ON_MERGE = "plan.policy.autoDeployOnMerge";
|
|
15
|
+
/** Emergency env-var bypass for agent merge (#1193). */
|
|
16
|
+
export declare const ENV_ALLOW_BOT_MERGE = "DEFT_ALLOW_BOT_MERGE";
|
|
17
|
+
/** Legacy narrative key recognized at read time with deprecation warning. */
|
|
18
|
+
export declare const LEGACY_REQUIRE_HUMAN_MERGE_KEY = "Require human merge";
|
|
19
|
+
export type HumanMergeSource = "typed" | "legacy-narrative" | "auto-deploy-default" | "env-bypass" | "default";
|
|
20
|
+
export interface HumanMergePolicyResult {
|
|
21
|
+
/** True when agents must not merge. */
|
|
22
|
+
readonly requireHumanMerge: boolean;
|
|
23
|
+
readonly source: HumanMergeSource;
|
|
24
|
+
readonly deprecationWarning: string | null;
|
|
25
|
+
readonly error: string | null;
|
|
26
|
+
readonly autoDeployOnMerge: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve effective human-merge policy (#1193).
|
|
30
|
+
*
|
|
31
|
+
* Order:
|
|
32
|
+
* 1. DEFT_ALLOW_BOT_MERGE=1 → requireHumanMerge effective false (env-bypass)
|
|
33
|
+
* 2. typed plan.policy.requireHumanMerge
|
|
34
|
+
* 3. legacy narrative "Require human merge"
|
|
35
|
+
* 4. autoDeployOnMerge true → default requireHumanMerge true
|
|
36
|
+
* 5. default false (no auto-deploy → agents may merge when other gates allow)
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveHumanMergePolicy(projectRoot: string, env?: NodeJS.ProcessEnv): HumanMergePolicyResult;
|
|
39
|
+
/** Session-start / AGENTS disclosure line when the human merge gate is ON. */
|
|
40
|
+
export declare function humanMergeDisclosureLine(result: HumanMergePolicyResult): string | null;
|
|
41
|
+
export interface AgentMergeEvaluateResult {
|
|
42
|
+
readonly exitCode: 0 | 1;
|
|
43
|
+
readonly allowed: boolean;
|
|
44
|
+
readonly message: string;
|
|
45
|
+
readonly policy: HumanMergePolicyResult;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Merge preflight: refuse agent-initiated merge when requireHumanMerge is true.
|
|
49
|
+
* Surface (1) of the three-surface enforcement pattern.
|
|
50
|
+
*/
|
|
51
|
+
export declare function evaluateAgentMerge(projectRoot: string, env?: NodeJS.ProcessEnv): AgentMergeEvaluateResult;
|
|
52
|
+
/**
|
|
53
|
+
* verify:branch aggregate surface (2): advisory line when gate is ON.
|
|
54
|
+
* Does not fail the branch gate on feature branches — merge refuse is surface (1).
|
|
55
|
+
*/
|
|
56
|
+
export declare function humanMergeBranchNote(result: HumanMergePolicyResult): string | null;
|
|
57
|
+
export interface HumanMergePolicyField {
|
|
58
|
+
readonly name: string;
|
|
59
|
+
readonly current: boolean;
|
|
60
|
+
readonly default: boolean;
|
|
61
|
+
readonly source: string;
|
|
62
|
+
}
|
|
63
|
+
/** Inspector for policy:show --field=requireHumanMerge. */
|
|
64
|
+
export declare function inspectRequireHumanMerge(data: Record<string, unknown> | null, projectRoot?: string): HumanMergePolicyField;
|
|
65
|
+
export declare const ALLOW_BOT_MERGE_CAPABILITY_COST: string;
|
|
66
|
+
/** Write requireHumanMerge=false (allow bot merge) with audit trail. */
|
|
67
|
+
export declare function setRequireHumanMerge(projectRoot: string, options: {
|
|
68
|
+
requireHumanMerge: boolean;
|
|
69
|
+
actor?: string;
|
|
70
|
+
note?: string;
|
|
71
|
+
}): {
|
|
72
|
+
changed: boolean;
|
|
73
|
+
auditEntry: string;
|
|
74
|
+
};
|
|
75
|
+
//# sourceMappingURL=require-human-merge.d.ts.map
|