@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
|
@@ -5,23 +5,40 @@ import { MIGRATE_COMPLETION_NUDGE, shouldEmitMigrateNudge } from "../init-deposi
|
|
|
5
5
|
import { detectEnvironmentContext, environmentContextToDict, formatEnvironmentContext, } from "../platform/shell-context.js";
|
|
6
6
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
7
7
|
import { detectNoDeftDirective, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_FLAG_NAME, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, NO_DEFT_DIRECTIVE_INCONSISTENT_POLICY, } from "../policy/no-deft-directive.js";
|
|
8
|
+
import { humanMergeDisclosureLine, resolveHumanMergePolicy, } from "../policy/require-human-merge.js";
|
|
8
9
|
import { resolvePolicy } from "../policy/resolve.js";
|
|
9
10
|
import { maybeFormatProductSignalConsentPrompt } from "../product-signal/consent-prompt.js";
|
|
11
|
+
import { formatFrameworkCommand } from "../render/framework-commands.js";
|
|
10
12
|
import { maybeRunStalenessTickler } from "../staleness-tickler/run.js";
|
|
11
13
|
import { runDefaultMode } from "../triage/welcome/default-mode.js";
|
|
12
14
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
13
15
|
import { emitSessionValueReadback } from "../value/readback.js";
|
|
14
16
|
import { verifyRequiredTools } from "../verify-env/verify-tools.js";
|
|
15
|
-
import { defaultGitRunner, gitHead, worktreePath } from "./git.js";
|
|
17
|
+
import { defaultGitRunner, gitHead, gitIsAncestor, worktreePath } from "./git.js";
|
|
18
|
+
import { emitSessionStartProcessCost } from "./process-cost.js";
|
|
16
19
|
import { probeSessionReleaseAvailability, } from "./release-availability.js";
|
|
17
|
-
import { newRitualStatePayload, ritualStatePath, ritualStep, writeRitualState, } from "./ritual-sentinel.js";
|
|
20
|
+
import { newRitualStatePayload, readRitualState, ritualStatePath, ritualStep, writeRitualState, } from "./ritual-sentinel.js";
|
|
18
21
|
export const SESSION_POSTURES = ["read-only", "mutation"];
|
|
19
22
|
export const READ_ONLY_POSTURE = "read-only";
|
|
20
23
|
export const MUTATION_POSTURE = "mutation";
|
|
21
24
|
export const READ_ONLY_ALIGNMENT_MESSAGE = "Deft Directive active -- AGENTS.md loaded.";
|
|
22
25
|
export const READ_ONLY_RESULT_MESSAGE = "read-only session posture (alignment only; no ritual-state write)";
|
|
26
|
+
/** Cold (full) vs re-arm (clock/HEAD refresh) ceremony tiers (#2992). */
|
|
27
|
+
export const SESSION_CEREMONY_TIERS = ["cold", "rearm"];
|
|
28
|
+
export const COLD_CEREMONY_TIER = "cold";
|
|
29
|
+
export const REARM_CEREMONY_TIER = "rearm";
|
|
23
30
|
export const QUICK_STEPS = ["alignment", "branch_policy", "triage_welcome"];
|
|
24
31
|
export const GATED_STEPS = ["doctor", "cache_fresh"];
|
|
32
|
+
/** Env opt-in for optional session:start network (release probe + triage cache hydrate) (#2991). */
|
|
33
|
+
export const ENV_SESSION_START_NETWORK = "DEFT_SESSION_START_NETWORK";
|
|
34
|
+
/** Human-readable skip notice when optional network is off the hot path (#2991). */
|
|
35
|
+
export const OPTIONAL_NETWORK_SKIPPED_MESSAGE = "[deft session] optional network skipped (release probe, triage cache hydrate/self-heal); " +
|
|
36
|
+
"pass --with-network or set DEFT_SESSION_START_NETWORK=1 to enable.";
|
|
37
|
+
/** Re-arm skips fat cold-path work when prior ritual is still structurally valid (#2992). */
|
|
38
|
+
export const REARM_SKIPPED_FAT_PATH_MESSAGE = "[deft session] re-arm tier: refreshed ritual clock + HEAD/worktree bind; " +
|
|
39
|
+
"skipped verify:tools, triage welcome, release probe, and staleness tickler.";
|
|
40
|
+
/** When --rearm is requested but state requires a full cold ceremony (#2992). */
|
|
41
|
+
export const REARM_INELIGIBLE_PREFIX = "session re-arm refused — full cold session:start required:";
|
|
25
42
|
const STEP_ALIASES = {
|
|
26
43
|
branch: "branch_policy",
|
|
27
44
|
"branch-policy": "branch_policy",
|
|
@@ -30,6 +47,89 @@ const STEP_ALIASES = {
|
|
|
30
47
|
triage: "triage_welcome",
|
|
31
48
|
"triage-welcome": "triage_welcome",
|
|
32
49
|
};
|
|
50
|
+
/** Format preferred `session:start` recovery command for cold vs re-arm (#2992). */
|
|
51
|
+
export function formatSessionStartRecoveryCommand(tier = "cold") {
|
|
52
|
+
if (tier === "rearm") {
|
|
53
|
+
return formatFrameworkCommand(["session:start", "--rearm"]);
|
|
54
|
+
}
|
|
55
|
+
return formatFrameworkCommand(["session:start"]);
|
|
56
|
+
}
|
|
57
|
+
function stepPassesForRearm(step) {
|
|
58
|
+
if (!step || typeof step !== "object")
|
|
59
|
+
return false;
|
|
60
|
+
if (step.deferred_reason)
|
|
61
|
+
return true;
|
|
62
|
+
return step.ok === true;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Whether a prior ritual can be re-armed without a full cold ceremony (#2992).
|
|
66
|
+
* Requires valid state, same worktree, continuous (or identical) HEAD, and
|
|
67
|
+
* previously-passing quick steps.
|
|
68
|
+
*/
|
|
69
|
+
export function assessRearmEligibility(projectRoot, options = {}) {
|
|
70
|
+
const runGit = options.runGit ?? defaultGitRunner;
|
|
71
|
+
const [state, err] = readRitualState(projectRoot);
|
|
72
|
+
if (state === null) {
|
|
73
|
+
return {
|
|
74
|
+
eligible: false,
|
|
75
|
+
reason: err ?? "ritual state missing (first install / cold path required)",
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const { head: currentHead, error: headError } = gitHead(projectRoot, runGit);
|
|
79
|
+
if (currentHead === null) {
|
|
80
|
+
return { eligible: false, reason: headError ?? "could not resolve git HEAD" };
|
|
81
|
+
}
|
|
82
|
+
const currentWorktree = worktreePath(projectRoot, runGit);
|
|
83
|
+
if (state.worktreePath !== currentWorktree) {
|
|
84
|
+
return {
|
|
85
|
+
eligible: false,
|
|
86
|
+
reason: `ritual state belongs to a different worktree (${state.worktreePath})`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if (state.gitHead !== currentHead) {
|
|
90
|
+
const forward = gitIsAncestor(projectRoot, state.gitHead, currentHead, runGit);
|
|
91
|
+
if (forward === null) {
|
|
92
|
+
return { eligible: false, reason: "could not verify git history for session re-arm" };
|
|
93
|
+
}
|
|
94
|
+
if (!forward) {
|
|
95
|
+
return {
|
|
96
|
+
eligible: false,
|
|
97
|
+
reason: "git HEAD changed discontinuously (full cold session:start required)",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const stepName of QUICK_STEPS) {
|
|
102
|
+
if (!stepPassesForRearm(state.quickSteps[stepName])) {
|
|
103
|
+
return {
|
|
104
|
+
eligible: false,
|
|
105
|
+
reason: `quick step '${stepName}' is missing or failed (full cold session:start required)`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return { eligible: true, state, currentHead, currentWorktree };
|
|
110
|
+
}
|
|
111
|
+
function restampSteps(steps, now) {
|
|
112
|
+
const out = {};
|
|
113
|
+
for (const [name, step] of Object.entries(steps)) {
|
|
114
|
+
out[name] = {
|
|
115
|
+
...step,
|
|
116
|
+
ts: ritualStep({ ok: step.ok === true, ts: now }).ts,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
/** Resolve whether optional session:start network work is enabled (#2991). */
|
|
122
|
+
export function resolveSessionStartOptionalNetwork(options = {}) {
|
|
123
|
+
if (options.allowOptionalNetwork === true)
|
|
124
|
+
return true;
|
|
125
|
+
if (options.allowOptionalNetwork === false)
|
|
126
|
+
return false;
|
|
127
|
+
const env = options.env ?? process.env;
|
|
128
|
+
return env[ENV_SESSION_START_NETWORK] === "1";
|
|
129
|
+
}
|
|
130
|
+
function elapsedMs(started) {
|
|
131
|
+
return Math.max(0, Math.round(performance.now() - started));
|
|
132
|
+
}
|
|
33
133
|
function normaliseStepName(name) {
|
|
34
134
|
return STEP_ALIASES[name] ?? name;
|
|
35
135
|
}
|
|
@@ -228,12 +328,174 @@ function runReadOnlySessionStart(projectRoot, options, instant, environment) {
|
|
|
228
328
|
};
|
|
229
329
|
return { code: 0, payload: resultPayload, lines };
|
|
230
330
|
}
|
|
331
|
+
function runSessionRearm(projectRoot, options, instant, environment) {
|
|
332
|
+
const overallStarted = performance.now();
|
|
333
|
+
const runGit = options.runGit ?? defaultGitRunner;
|
|
334
|
+
const eligibility = assessRearmEligibility(projectRoot, { runGit });
|
|
335
|
+
if (!eligibility.eligible) {
|
|
336
|
+
const coldCmd = formatSessionStartRecoveryCommand("cold");
|
|
337
|
+
const message = `${REARM_INELIGIBLE_PREFIX} ${eligibility.reason}. Run \`${coldCmd}\`.`;
|
|
338
|
+
// #2994: still record failed attempt duration for process-cost rollups.
|
|
339
|
+
emitSessionStartProcessCost({
|
|
340
|
+
ceremonyTier: REARM_CEREMONY_TIER,
|
|
341
|
+
durationMs: elapsedMs(overallStarted),
|
|
342
|
+
exitCode: 1,
|
|
343
|
+
ready: false,
|
|
344
|
+
optionalNetwork: false,
|
|
345
|
+
}, { projectRoot });
|
|
346
|
+
return {
|
|
347
|
+
code: 1,
|
|
348
|
+
payload: {
|
|
349
|
+
ready: false,
|
|
350
|
+
exit_code: 1,
|
|
351
|
+
ceremony_tier: REARM_CEREMONY_TIER,
|
|
352
|
+
rearm_eligible: false,
|
|
353
|
+
environment: environmentContextToDict(environment),
|
|
354
|
+
message,
|
|
355
|
+
},
|
|
356
|
+
lines: [formatEnvironmentContext(environment), message],
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
const resolveUserMd = options.resolveUserMd ?? ((root) => resolveUserMdPath({ projectRoot: root }));
|
|
360
|
+
const userMd = resolveUserMd(projectRoot);
|
|
361
|
+
const safePath = userMd.path.replace(/\r?\n/g, " ");
|
|
362
|
+
const safeDiagnostic = userMd.diagnostic.replace(/\r?\n/g, " ");
|
|
363
|
+
const userMdLine = userMd.found
|
|
364
|
+
? `USER.md resolved (${userMd.rung}): ${safePath}`
|
|
365
|
+
: safeDiagnostic;
|
|
366
|
+
const alignmentMessage = `${READ_ONLY_ALIGNMENT_MESSAGE} ${userMdLine}`;
|
|
367
|
+
const lines = [
|
|
368
|
+
READ_ONLY_ALIGNMENT_MESSAGE,
|
|
369
|
+
userMdLine,
|
|
370
|
+
formatEnvironmentContext(environment),
|
|
371
|
+
REARM_SKIPPED_FAT_PATH_MESSAGE,
|
|
372
|
+
];
|
|
373
|
+
// Light branch-policy disclosure (local only) so re-arm still surfaces policy state.
|
|
374
|
+
const policyResult = resolvePolicy(projectRoot);
|
|
375
|
+
const policyMessage = disclosureLine(policyResult);
|
|
376
|
+
lines.push(policyMessage);
|
|
377
|
+
const humanMerge = resolveHumanMergePolicy(projectRoot);
|
|
378
|
+
const humanMergeLine = humanMergeDisclosureLine(humanMerge);
|
|
379
|
+
if (humanMergeLine !== null) {
|
|
380
|
+
lines.push(humanMergeLine);
|
|
381
|
+
}
|
|
382
|
+
const priorQuick = eligibility.state.quickSteps;
|
|
383
|
+
const priorTriage = priorQuick.triage_welcome ?? ritualStep({ ok: true, ts: instant });
|
|
384
|
+
const policyOk = policyResult.error === null || policyResult.source === "default-fail-closed";
|
|
385
|
+
const quickSteps = {
|
|
386
|
+
alignment: ritualStep({
|
|
387
|
+
ok: true,
|
|
388
|
+
ts: instant,
|
|
389
|
+
message: alignmentMessage,
|
|
390
|
+
durationMs: 0,
|
|
391
|
+
}),
|
|
392
|
+
branch_policy: ritualStep({
|
|
393
|
+
ok: policyOk,
|
|
394
|
+
ts: instant,
|
|
395
|
+
message: policyMessage,
|
|
396
|
+
exitCode: policyOk ? 0 : 2,
|
|
397
|
+
durationMs: 0,
|
|
398
|
+
}),
|
|
399
|
+
// Preserve prior triage outcome; do not re-run welcome / self-heal on re-arm.
|
|
400
|
+
triage_welcome: {
|
|
401
|
+
...priorTriage,
|
|
402
|
+
ts: ritualStep({ ok: priorTriage.ok === true, ts: instant }).ts,
|
|
403
|
+
message: typeof priorTriage.message === "string"
|
|
404
|
+
? priorTriage.message
|
|
405
|
+
: "triage welcome preserved on re-arm",
|
|
406
|
+
},
|
|
407
|
+
};
|
|
408
|
+
const gatedSteps = restampSteps(eligibility.state.gatedSteps, instant);
|
|
409
|
+
const writeStarted = performance.now();
|
|
410
|
+
// Fresh payload (no rearm_needed / compact_resume_at) clears compact markers (#2992).
|
|
411
|
+
const writePayload = {
|
|
412
|
+
...newRitualStatePayload({
|
|
413
|
+
sessionId: (options.newSessionId ?? randomUUID)(),
|
|
414
|
+
gitHead: eligibility.currentHead,
|
|
415
|
+
worktreePath: eligibility.currentWorktree,
|
|
416
|
+
startedAt: instant,
|
|
417
|
+
quickSteps,
|
|
418
|
+
gatedSteps,
|
|
419
|
+
}),
|
|
420
|
+
ceremony_tier: REARM_CEREMONY_TIER,
|
|
421
|
+
};
|
|
422
|
+
let statePath;
|
|
423
|
+
try {
|
|
424
|
+
statePath = writeRitualState(projectRoot, writePayload);
|
|
425
|
+
}
|
|
426
|
+
catch (cause) {
|
|
427
|
+
// #2994: still record failed attempt when ritual-state write throws.
|
|
428
|
+
emitSessionStartProcessCost({
|
|
429
|
+
ceremonyTier: REARM_CEREMONY_TIER,
|
|
430
|
+
durationMs: elapsedMs(overallStarted),
|
|
431
|
+
exitCode: 2,
|
|
432
|
+
ready: false,
|
|
433
|
+
optionalNetwork: false,
|
|
434
|
+
steps: [
|
|
435
|
+
{ name: "alignment", duration_ms: 0 },
|
|
436
|
+
{ name: "branch_policy", duration_ms: 0 },
|
|
437
|
+
{ name: "verify_tools", duration_ms: 0, skipped: true },
|
|
438
|
+
{ name: "triage_welcome", duration_ms: 0, skipped: true },
|
|
439
|
+
{ name: "release_probe", duration_ms: 0, skipped: true },
|
|
440
|
+
{ name: "ritual_write", duration_ms: elapsedMs(writeStarted) },
|
|
441
|
+
],
|
|
442
|
+
}, { projectRoot });
|
|
443
|
+
throw cause;
|
|
444
|
+
}
|
|
445
|
+
const stepTimings = [
|
|
446
|
+
{ name: "alignment", duration_ms: 0 },
|
|
447
|
+
{ name: "branch_policy", duration_ms: 0 },
|
|
448
|
+
{ name: "verify_tools", duration_ms: 0, skipped: true },
|
|
449
|
+
{ name: "triage_welcome", duration_ms: 0, skipped: true },
|
|
450
|
+
{ name: "release_probe", duration_ms: 0, skipped: true },
|
|
451
|
+
{ name: "ritual_write", duration_ms: elapsedMs(writeStarted) },
|
|
452
|
+
];
|
|
453
|
+
const totalMs = elapsedMs(overallStarted);
|
|
454
|
+
const failed = Object.entries(quickSteps)
|
|
455
|
+
.filter(([, step]) => !step.ok && !step.deferred_reason)
|
|
456
|
+
.map(([name]) => name);
|
|
457
|
+
const code = failed.length > 0 ? 1 : 0;
|
|
458
|
+
// #2994: local process-cost event (best-effort; never blocks ceremony).
|
|
459
|
+
emitSessionStartProcessCost({
|
|
460
|
+
ceremonyTier: REARM_CEREMONY_TIER,
|
|
461
|
+
durationMs: totalMs,
|
|
462
|
+
exitCode: code,
|
|
463
|
+
ready: code === 0,
|
|
464
|
+
optionalNetwork: false,
|
|
465
|
+
steps: stepTimings,
|
|
466
|
+
}, { projectRoot });
|
|
467
|
+
return {
|
|
468
|
+
code,
|
|
469
|
+
payload: {
|
|
470
|
+
ready: code === 0,
|
|
471
|
+
exit_code: code,
|
|
472
|
+
ceremony_tier: REARM_CEREMONY_TIER,
|
|
473
|
+
rearm_eligible: true,
|
|
474
|
+
state_path: statePath,
|
|
475
|
+
quick_steps: quickSteps,
|
|
476
|
+
gated_steps: gatedSteps,
|
|
477
|
+
steps: stepTimings,
|
|
478
|
+
duration_ms: totalMs,
|
|
479
|
+
optional_network: false,
|
|
480
|
+
user_md: {
|
|
481
|
+
path: userMd.path,
|
|
482
|
+
rung: userMd.rung,
|
|
483
|
+
found: userMd.found,
|
|
484
|
+
diagnostic: userMd.diagnostic,
|
|
485
|
+
},
|
|
486
|
+
environment: environmentContextToDict(environment),
|
|
487
|
+
message: code === 0 ? "session ritual re-armed" : "session ritual re-arm failed",
|
|
488
|
+
},
|
|
489
|
+
lines,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
231
492
|
export function runSessionStart(projectRoot, options = {}) {
|
|
232
493
|
const posture = options.posture ?? MUTATION_POSTURE;
|
|
233
494
|
const instant = options.now ?? new Date();
|
|
234
495
|
const deferrals = options.deferrals ?? {};
|
|
235
496
|
const runGit = options.runGit ?? defaultGitRunner;
|
|
236
497
|
const environment = (options.probeEnvironment ?? detectEnvironmentContext)();
|
|
498
|
+
const ceremonyTier = options.ceremonyTier ?? COLD_CEREMONY_TIER;
|
|
237
499
|
// #2926: official root opt-out wins locally — skip Directive session ritual.
|
|
238
500
|
// disabled = skip ritual (exit 0 clean / 1 inconsistent). ready stays false so
|
|
239
501
|
// automation does not treat opt-out as "session fully initialized for work".
|
|
@@ -266,13 +528,30 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
266
528
|
if (posture === READ_ONLY_POSTURE) {
|
|
267
529
|
return runReadOnlySessionStart(projectRoot, options, instant, environment);
|
|
268
530
|
}
|
|
531
|
+
// #2992: re-arm path refreshes clock/bind without fat cold ceremony.
|
|
532
|
+
if (ceremonyTier === REARM_CEREMONY_TIER) {
|
|
533
|
+
return runSessionRearm(projectRoot, options, instant, environment);
|
|
534
|
+
}
|
|
535
|
+
const overallStarted = performance.now();
|
|
536
|
+
const stepTimings = [];
|
|
537
|
+
const allowOptionalNetwork = resolveSessionStartOptionalNetwork(options);
|
|
269
538
|
const { head: gitHeadValue, error: gitError } = gitHead(projectRoot, runGit);
|
|
270
539
|
if (gitHeadValue === null) {
|
|
271
540
|
const payload = {
|
|
272
541
|
ready: false,
|
|
542
|
+
exit_code: 2,
|
|
543
|
+
ceremony_tier: COLD_CEREMONY_TIER,
|
|
273
544
|
environment: environmentContextToDict(environment),
|
|
274
545
|
message: gitError ?? "could not resolve git HEAD",
|
|
275
546
|
};
|
|
547
|
+
// #2994: still record failed attempt duration for process-cost rollups.
|
|
548
|
+
emitSessionStartProcessCost({
|
|
549
|
+
ceremonyTier: COLD_CEREMONY_TIER,
|
|
550
|
+
durationMs: elapsedMs(overallStarted),
|
|
551
|
+
exitCode: 2,
|
|
552
|
+
ready: false,
|
|
553
|
+
optionalNetwork: allowOptionalNetwork,
|
|
554
|
+
}, { projectRoot });
|
|
276
555
|
return {
|
|
277
556
|
code: 2,
|
|
278
557
|
payload,
|
|
@@ -289,6 +568,7 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
289
568
|
const resolveUserMd = options.resolveUserMd ?? ((root) => resolveUserMdPath({ projectRoot: root }));
|
|
290
569
|
const userMd = resolveUserMd(projectRoot);
|
|
291
570
|
if (!quickSteps.alignment) {
|
|
571
|
+
const stepStarted = performance.now();
|
|
292
572
|
const message = "Deft Directive active -- AGENTS.md loaded.";
|
|
293
573
|
// Sanitize newlines on the data-derived path/diagnostic before they land in
|
|
294
574
|
// the ritual-step message / terminal output (matches doctor's CWE-116
|
|
@@ -299,42 +579,66 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
299
579
|
const userMdLine = userMd.found
|
|
300
580
|
? `USER.md resolved (${userMd.rung}): ${safePath}`
|
|
301
581
|
: safeDiagnostic;
|
|
582
|
+
const durationMs = elapsedMs(stepStarted);
|
|
302
583
|
quickSteps.alignment = ritualStep({
|
|
303
584
|
ok: true,
|
|
304
585
|
ts: instant,
|
|
305
586
|
message: `${message} ${userMdLine}`,
|
|
587
|
+
durationMs,
|
|
306
588
|
});
|
|
589
|
+
stepTimings.push({ name: "alignment", duration_ms: durationMs });
|
|
307
590
|
lines.push(message);
|
|
308
591
|
lines.push(userMdLine);
|
|
309
592
|
}
|
|
593
|
+
else {
|
|
594
|
+
stepTimings.push({ name: "alignment", duration_ms: 0, skipped: true });
|
|
595
|
+
}
|
|
310
596
|
lines.push(formatEnvironmentContext(environment));
|
|
311
597
|
if (!quickSteps.branch_policy) {
|
|
598
|
+
const stepStarted = performance.now();
|
|
312
599
|
const result = resolvePolicy(projectRoot);
|
|
313
600
|
const message = disclosureLine(result);
|
|
314
601
|
const ok = result.error === null || result.source === "default-fail-closed";
|
|
602
|
+
lines.push(message);
|
|
603
|
+
// Human merge gate disclosure (#1193) — surface when ON (or env bypass active).
|
|
604
|
+
const humanMerge = resolveHumanMergePolicy(projectRoot);
|
|
605
|
+
const humanMergeLine = humanMergeDisclosureLine(humanMerge);
|
|
606
|
+
if (humanMergeLine !== null) {
|
|
607
|
+
lines.push(humanMergeLine);
|
|
608
|
+
}
|
|
609
|
+
const branchSync = defaultBranchSync(projectRoot, runGit);
|
|
610
|
+
if (branchSync.warning) {
|
|
611
|
+
lines.push(branchSync.warning);
|
|
612
|
+
}
|
|
613
|
+
const durationMs = elapsedMs(stepStarted);
|
|
315
614
|
quickSteps.branch_policy = ritualStep({
|
|
316
615
|
ok,
|
|
317
616
|
ts: instant,
|
|
318
617
|
message,
|
|
319
618
|
exitCode: ok ? 0 : 2,
|
|
619
|
+
durationMs,
|
|
320
620
|
});
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
621
|
+
stepTimings.push({ name: "branch_policy", duration_ms: durationMs });
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
stepTimings.push({ name: "branch_policy", duration_ms: 0, skipped: true });
|
|
625
|
+
}
|
|
626
|
+
{
|
|
627
|
+
const stepStarted = performance.now();
|
|
628
|
+
const verifyToolsFn = options.verifyTools ??
|
|
629
|
+
((output) => {
|
|
630
|
+
const toolLines = [];
|
|
631
|
+
const result = verifyRequiredTools({ outputFn: (line) => toolLines.push(line) });
|
|
632
|
+
for (const line of toolLines) {
|
|
633
|
+
output(line);
|
|
634
|
+
}
|
|
635
|
+
return { exitCode: result.exitCode };
|
|
636
|
+
});
|
|
637
|
+
verifyToolsFn((line) => lines.push(line));
|
|
638
|
+
stepTimings.push({ name: "verify_tools", duration_ms: elapsedMs(stepStarted) });
|
|
326
639
|
}
|
|
327
|
-
const verifyToolsFn = options.verifyTools ??
|
|
328
|
-
((output) => {
|
|
329
|
-
const toolLines = [];
|
|
330
|
-
const result = verifyRequiredTools({ outputFn: (line) => toolLines.push(line) });
|
|
331
|
-
for (const line of toolLines) {
|
|
332
|
-
output(line);
|
|
333
|
-
}
|
|
334
|
-
return { exitCode: result.exitCode };
|
|
335
|
-
});
|
|
336
|
-
verifyToolsFn((line) => lines.push(line));
|
|
337
640
|
if (!quickSteps.triage_welcome) {
|
|
641
|
+
const stepStarted = performance.now();
|
|
338
642
|
const captured = [];
|
|
339
643
|
const triageCommand = ["triage_welcome.run_default_mode", "--project-root", projectRoot];
|
|
340
644
|
try {
|
|
@@ -343,6 +647,13 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
343
647
|
const outcome = runDefaultMode(root, {
|
|
344
648
|
output: welcomeOpts.output,
|
|
345
649
|
writeHistory: welcomeOpts.writeHistory,
|
|
650
|
+
now: welcomeOpts.now,
|
|
651
|
+
// #2991: default hot path skips ensureTriageCacheHydrated / maybeSelfHealCache.
|
|
652
|
+
selfHealFn: allowOptionalNetwork
|
|
653
|
+
? undefined
|
|
654
|
+
: () => {
|
|
655
|
+
/* no network cache work on hot path */
|
|
656
|
+
},
|
|
346
657
|
});
|
|
347
658
|
return { exitCode: outcome.exitCode };
|
|
348
659
|
});
|
|
@@ -353,33 +664,51 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
353
664
|
});
|
|
354
665
|
const ok = outcome.exitCode === 0;
|
|
355
666
|
const message = captured.join("\n").trim() || "triage welcome completed";
|
|
667
|
+
const durationMs = elapsedMs(stepStarted);
|
|
356
668
|
quickSteps.triage_welcome = ritualStep({
|
|
357
669
|
ok,
|
|
358
670
|
ts: instant,
|
|
359
671
|
message,
|
|
360
672
|
exitCode: outcome.exitCode,
|
|
361
673
|
command: triageCommand,
|
|
674
|
+
durationMs,
|
|
362
675
|
});
|
|
676
|
+
stepTimings.push({ name: "triage_welcome", duration_ms: durationMs });
|
|
363
677
|
lines.push(...captured);
|
|
364
678
|
}
|
|
365
679
|
catch (exc) {
|
|
366
680
|
const message = `triage welcome failed: ${String(exc)}`;
|
|
681
|
+
const durationMs = elapsedMs(stepStarted);
|
|
367
682
|
quickSteps.triage_welcome = ritualStep({
|
|
368
683
|
ok: false,
|
|
369
684
|
ts: instant,
|
|
370
685
|
message,
|
|
371
686
|
exitCode: 2,
|
|
372
687
|
command: triageCommand,
|
|
688
|
+
durationMs,
|
|
373
689
|
});
|
|
690
|
+
stepTimings.push({ name: "triage_welcome", duration_ms: durationMs });
|
|
374
691
|
lines.push(message);
|
|
375
692
|
}
|
|
376
693
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
lines.push(...releaseAvailability.lines);
|
|
694
|
+
else {
|
|
695
|
+
stepTimings.push({ name: "triage_welcome", duration_ms: 0, skipped: true });
|
|
380
696
|
}
|
|
381
|
-
|
|
382
|
-
|
|
697
|
+
// #2991: npm release probe is optional network — off by default so ritual write is not blocked.
|
|
698
|
+
if (allowOptionalNetwork) {
|
|
699
|
+
const stepStarted = performance.now();
|
|
700
|
+
try {
|
|
701
|
+
const releaseAvailability = (options.probeReleaseAvailability ?? probeSessionReleaseAvailability)(projectRoot, { now: instant });
|
|
702
|
+
lines.push(...releaseAvailability.lines);
|
|
703
|
+
}
|
|
704
|
+
catch {
|
|
705
|
+
// Release availability is a best-effort operator advisory, never a session blocker.
|
|
706
|
+
}
|
|
707
|
+
stepTimings.push({ name: "release_probe", duration_ms: elapsedMs(stepStarted) });
|
|
708
|
+
}
|
|
709
|
+
else {
|
|
710
|
+
stepTimings.push({ name: "release_probe", duration_ms: 0, skipped: true });
|
|
711
|
+
lines.push(OPTIONAL_NETWORK_SKIPPED_MESSAGE);
|
|
383
712
|
}
|
|
384
713
|
try {
|
|
385
714
|
const tickler = (options.runStalenessTickler ?? maybeRunStalenessTickler)(projectRoot, {
|
|
@@ -415,6 +744,7 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
415
744
|
if (consentPrompt.length > 0) {
|
|
416
745
|
lines.push(consentPrompt.trimEnd());
|
|
417
746
|
}
|
|
747
|
+
const writeStarted = performance.now();
|
|
418
748
|
const payload = newRitualStatePayload({
|
|
419
749
|
sessionId: (options.newSessionId ?? randomUUID)(),
|
|
420
750
|
gitHead: gitHeadValue,
|
|
@@ -423,17 +753,39 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
423
753
|
quickSteps,
|
|
424
754
|
gatedSteps,
|
|
425
755
|
});
|
|
426
|
-
|
|
756
|
+
let statePath;
|
|
757
|
+
try {
|
|
758
|
+
statePath = writeRitualState(projectRoot, payload);
|
|
759
|
+
}
|
|
760
|
+
catch (cause) {
|
|
761
|
+
// #2994: still record failed attempt when ritual-state write throws.
|
|
762
|
+
stepTimings.push({ name: "ritual_write", duration_ms: elapsedMs(writeStarted) });
|
|
763
|
+
emitSessionStartProcessCost({
|
|
764
|
+
ceremonyTier: COLD_CEREMONY_TIER,
|
|
765
|
+
durationMs: elapsedMs(overallStarted),
|
|
766
|
+
exitCode: 2,
|
|
767
|
+
ready: false,
|
|
768
|
+
optionalNetwork: allowOptionalNetwork,
|
|
769
|
+
steps: stepTimings,
|
|
770
|
+
}, { projectRoot });
|
|
771
|
+
throw cause;
|
|
772
|
+
}
|
|
773
|
+
stepTimings.push({ name: "ritual_write", duration_ms: elapsedMs(writeStarted) });
|
|
427
774
|
const failed = Object.entries(quickSteps)
|
|
428
775
|
.filter(([, step]) => !step.ok && !step.deferred_reason)
|
|
429
776
|
.map(([name]) => name);
|
|
430
777
|
const code = failed.length > 0 ? 1 : 0;
|
|
778
|
+
const totalMs = elapsedMs(overallStarted);
|
|
431
779
|
const resultPayload = {
|
|
432
780
|
ready: code === 0,
|
|
433
781
|
exit_code: code,
|
|
782
|
+
ceremony_tier: COLD_CEREMONY_TIER,
|
|
434
783
|
state_path: statePath,
|
|
435
784
|
quick_steps: quickSteps,
|
|
436
785
|
gated_steps: gatedSteps,
|
|
786
|
+
steps: stepTimings,
|
|
787
|
+
duration_ms: totalMs,
|
|
788
|
+
optional_network: allowOptionalNetwork,
|
|
437
789
|
user_md: {
|
|
438
790
|
path: userMd.path,
|
|
439
791
|
rung: userMd.rung,
|
|
@@ -443,6 +795,15 @@ export function runSessionStart(projectRoot, options = {}) {
|
|
|
443
795
|
environment: environmentContextToDict(environment),
|
|
444
796
|
message: code === 0 ? "session ritual recorded" : "session ritual failed",
|
|
445
797
|
};
|
|
798
|
+
// #2994: local process-cost event (best-effort; never blocks ceremony).
|
|
799
|
+
emitSessionStartProcessCost({
|
|
800
|
+
ceremonyTier: COLD_CEREMONY_TIER,
|
|
801
|
+
durationMs: totalMs,
|
|
802
|
+
exitCode: code,
|
|
803
|
+
ready: code === 0,
|
|
804
|
+
optionalNetwork: allowOptionalNetwork,
|
|
805
|
+
steps: stepTimings,
|
|
806
|
+
}, { projectRoot });
|
|
446
807
|
return { code, payload: resultPayload, lines };
|
|
447
808
|
}
|
|
448
809
|
export { ritualStatePath };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type GitRunner } from "./git.js";
|
|
2
2
|
import { type DirectivePosture } from "./posture.js";
|
|
3
|
+
import { type SessionCeremonyTier } from "./session-start.js";
|
|
3
4
|
export { formatCacheFetchAllRecoveryCommand, recoveryHintForStaleFailure, } from "./cache-recovery.js";
|
|
4
5
|
export declare const ENV_SKIP = "DEFT_SESSION_RITUAL_SKIP";
|
|
5
6
|
export { ENTRYPOINT_TIMEOUT_EXIT_CODE, ENTRYPOINT_TIMEOUT_SECONDS, } from "./ritual-entrypoint.js";
|
|
@@ -13,12 +14,19 @@ export interface VerifyResult {
|
|
|
13
14
|
readonly wouldFailCode: number | null;
|
|
14
15
|
readonly posture: DirectivePosture;
|
|
15
16
|
readonly ritualStateRequired: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Preferred session:start recovery when code !== 0 (#2992).
|
|
19
|
+
* `rearm` when age/compact stale on an otherwise valid bind; `cold` otherwise.
|
|
20
|
+
*/
|
|
21
|
+
readonly recoveryTier?: SessionCeremonyTier | null;
|
|
16
22
|
}
|
|
17
23
|
export type RitualRunner = (command: readonly string[], projectRoot: string) => {
|
|
18
24
|
code: number;
|
|
19
25
|
stdout: string;
|
|
20
26
|
stderr: string;
|
|
21
27
|
};
|
|
28
|
+
/** Human recovery line after a failed ritual probe (#2992 / #2993). */
|
|
29
|
+
export declare function formatRitualRecoveryInstruction(tier?: SessionCeremonyTier): string;
|
|
22
30
|
export interface VerifySessionRitualOptions {
|
|
23
31
|
readonly tier?: "quick" | "gated";
|
|
24
32
|
readonly now?: Date;
|