@deftai/directive-core 0.100.0 → 0.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authz/classify.js +193 -27
- package/dist/authz/decompose-apply.d.ts +14 -0
- package/dist/authz/decompose-apply.js +93 -18
- package/dist/check/cached-orchestrator.d.ts +30 -5
- package/dist/check/cached-orchestrator.js +297 -10
- package/dist/check/cli-native-gates.d.ts +43 -0
- package/dist/check/cli-native-gates.js +84 -0
- package/dist/check/gate-lists.d.ts +10 -4
- package/dist/check/gate-lists.js +19 -5
- package/dist/check/index.d.ts +4 -2
- package/dist/check/index.js +3 -1
- package/dist/check/named-cause.d.ts +45 -0
- package/dist/check/named-cause.js +131 -0
- package/dist/check/orchestrator.js +6 -1
- package/dist/doctor/checks.d.ts +5 -6
- package/dist/doctor/checks.js +13 -56
- package/dist/doctor/help.d.ts +5 -0
- package/dist/doctor/help.js +31 -0
- package/dist/doctor/index.d.ts +2 -0
- package/dist/doctor/index.js +2 -0
- package/dist/doctor/main.js +58 -0
- package/dist/doctor/session-coda.d.ts +82 -0
- package/dist/doctor/session-coda.js +151 -0
- package/dist/doctor/types.d.ts +16 -0
- package/dist/eval/later-graduation.d.ts +39 -0
- package/dist/eval/later-graduation.js +91 -0
- package/dist/eval/report.d.ts +6 -0
- package/dist/eval/report.js +27 -8
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/init-deposit/gitignore.js +2 -0
- package/dist/intake/issue-ingest.js +49 -0
- package/dist/policy/ac-pass-banking.d.ts +58 -0
- package/dist/policy/ac-pass-banking.js +150 -0
- package/dist/policy/ceremony-dial-escalation.d.ts +52 -0
- package/dist/policy/ceremony-dial-escalation.js +92 -0
- package/dist/policy/ceremony-dial.d.ts +32 -1
- package/dist/policy/ceremony-dial.js +128 -1
- package/dist/policy/check-resume.d.ts +9 -38
- package/dist/policy/check-resume.js +18 -156
- package/dist/policy/coverage-debt.d.ts +18 -37
- package/dist/policy/coverage-debt.js +38 -147
- package/dist/policy/index.d.ts +2 -1
- package/dist/policy/index.js +16 -2
- package/dist/preflight-cache/evaluate.d.ts +3 -0
- package/dist/preflight-cache/evaluate.js +3 -0
- package/dist/product-first-done-gate/acceptance.d.ts +25 -0
- package/dist/product-first-done-gate/acceptance.js +269 -0
- package/dist/product-first-done-gate/check-mode.d.ts +45 -0
- package/dist/product-first-done-gate/check-mode.js +148 -0
- package/dist/product-first-done-gate/empty-resolution.d.ts +26 -0
- package/dist/product-first-done-gate/empty-resolution.js +38 -0
- package/dist/product-first-done-gate/evaluate.d.ts +76 -0
- package/dist/product-first-done-gate/evaluate.js +411 -0
- package/dist/product-first-done-gate/index.d.ts +12 -0
- package/dist/product-first-done-gate/index.js +12 -0
- package/dist/product-first-done-gate/types.d.ts +67 -0
- package/dist/product-first-done-gate/types.js +54 -0
- package/dist/run-summary/emit.d.ts +66 -0
- package/dist/run-summary/emit.js +217 -0
- package/dist/run-summary/index.d.ts +5 -0
- package/dist/run-summary/index.js +5 -0
- package/dist/run-summary/path.d.ts +25 -0
- package/dist/run-summary/path.js +78 -0
- package/dist/run-summary/share.d.ts +25 -0
- package/dist/run-summary/share.js +106 -0
- package/dist/run-summary/types.d.ts +134 -0
- package/dist/run-summary/types.js +28 -0
- package/dist/scope/acceptance-activate-gate.d.ts +23 -0
- package/dist/scope/acceptance-activate-gate.js +70 -0
- package/dist/scope/acceptance-evidence.d.ts +34 -0
- package/dist/scope/acceptance-evidence.js +84 -10
- package/dist/scope/decompose.d.ts +5 -0
- package/dist/scope/decompose.js +11 -2
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/transition.js +5 -0
- package/dist/session/ac-pass-banking.d.ts +221 -0
- package/dist/session/ac-pass-banking.js +761 -0
- package/dist/session/deposit-sha.d.ts +49 -0
- package/dist/session/deposit-sha.js +121 -0
- package/dist/session/effort-budget.d.ts +6 -0
- package/dist/session/effort-budget.js +21 -3
- package/dist/session/index.d.ts +5 -1
- package/dist/session/index.js +5 -1
- package/dist/session/orientation-compression.d.ts +127 -0
- package/dist/session/orientation-compression.js +435 -0
- package/dist/session/orientation-state.d.ts +29 -0
- package/dist/session/orientation-state.js +85 -0
- package/dist/session/session-start.d.ts +31 -0
- package/dist/session/session-start.js +219 -22
- package/dist/session/toolchain-preflight.d.ts +79 -0
- package/dist/session/toolchain-preflight.js +211 -0
- package/dist/triage/bootstrap/gitignore.d.ts +1 -1
- package/dist/triage/bootstrap/gitignore.js +8 -2
- package/dist/triage/bootstrap/index.js +15 -3
- package/dist/triage/classify/index.d.ts +1 -0
- package/dist/triage/classify/index.js +2 -0
- package/dist/triage/classify/label-mirror.js +16 -0
- package/dist/triage/classify/mirror-discovery-tip.d.ts +95 -0
- package/dist/triage/classify/mirror-discovery-tip.js +234 -0
- package/dist/triage/reconcile/reconcile.js +28 -6
- package/dist/triage/summary/index.js +12 -4
- package/dist/triage/welcome/default-mode.js +16 -0
- package/dist/ts-check-lane/run-lane.js +21 -4
- package/dist/vbrief-validate/conformance.js +7 -0
- package/dist/verify-ac/clauses.d.ts +51 -0
- package/dist/verify-ac/clauses.js +490 -0
- package/dist/verify-ac/evaluate.d.ts +55 -0
- package/dist/verify-ac/evaluate.js +145 -0
- package/dist/verify-ac/flag.d.ts +35 -0
- package/dist/verify-ac/flag.js +104 -0
- package/dist/verify-ac/index.d.ts +10 -0
- package/dist/verify-ac/index.js +10 -0
- package/dist/vitest-runner/coverage-debt-teardown.js +16 -4
- package/package.json +15 -3
- package/dist/policy/coverage-check-resume-presets.d.ts +0 -46
- package/dist/policy/coverage-check-resume-presets.js +0 -228
- package/dist/session/coverage-check-resume-nudge.d.ts +0 -34
- package/dist/session/coverage-check-resume-nudge.js +0 -66
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Named-cause + remedy formatting for check gate failures (#3282).
|
|
3
|
+
*
|
|
4
|
+
* Gate failures must never be bare exit 1: report gate name, cause, and remedy
|
|
5
|
+
* without embedding env values.
|
|
6
|
+
*/
|
|
7
|
+
/** Per-gate remedy hints (static; no env interpolation). */
|
|
8
|
+
const GATE_REMEDIES = {
|
|
9
|
+
"verify:branch": "Create a feature branch (`git switch -c feat/<name>`) or set plan.policy.allowDirectCommitsToMaster with confirmation",
|
|
10
|
+
"verify:encoding": "Fix non-ASCII / encoding issues flagged by the gate; re-run task verify:encoding",
|
|
11
|
+
"verify:cache-fresh": "Run task cache:fetch-all or task triage:bootstrap to refresh the cache",
|
|
12
|
+
"verify:orphan-active": "Complete or cancel active xBRIEFs whose issues are closed / PRs merged (task scope:complete / scope:cancel)",
|
|
13
|
+
"verify:wip-cap": "Demote stale pending scopes (task scope:demote) or raise plan.policy.wipCap deliberately",
|
|
14
|
+
doctor: "Run task doctor and follow the named recovery steps",
|
|
15
|
+
"toolchain:check": "Install missing maintainer tools reported by the gate (go, uv, git, gh, node, pnpm)",
|
|
16
|
+
"toolchain:check-consumer": "Install missing consumer tools: go-task, git, gh, node, pnpm (corepack enable && corepack prepare pnpm@latest --activate)",
|
|
17
|
+
"ts:check-lane": "Fix lint/type/test failures; re-run task ts:check-lane",
|
|
18
|
+
"vbrief:validate": "Fix xBRIEF/vBRIEF schema errors reported by the gate",
|
|
19
|
+
"verify-strategy-output": "Re-run strategy output or fix non-conformant scope filenames / PROJECT-DEFINITION",
|
|
20
|
+
"verify:test-boundary": "Move tests to the allowed placement or update plan.policy test-boundary allowlist",
|
|
21
|
+
"verify:scope-provenance": "Record approved scope provenance for the failing paths",
|
|
22
|
+
"verify:consumer-check-contract": "Align consumer Taskfile includes with the required gate graph",
|
|
23
|
+
"verify:forward-coverage": "Add tests covering new source files (task verify:forward-coverage)",
|
|
24
|
+
"verify:scm-boundary": "Move SCM mutations off GraphQL-heavy paths or wait for rate-limit reset",
|
|
25
|
+
"verify:license-sync": "Sync LICENSE / package license fields",
|
|
26
|
+
"verify:agents-md-budget": "Trim AGENTS.md managed section or raise plan.policy.agentsMdBudget deliberately",
|
|
27
|
+
};
|
|
28
|
+
const SPAWN_ERROR_REMEDY = "Install go-task (https://taskfile.dev/installation/) and ensure `task` is on PATH; then re-run task check";
|
|
29
|
+
const CLI_SPAWN_ERROR_REMEDY = "Install: npm i -g @deftai/directive@latest";
|
|
30
|
+
/**
|
|
31
|
+
* Extract a short cause from gate stdout/stderr without leaking env values.
|
|
32
|
+
* Strips lines that look like KEY=value assignments.
|
|
33
|
+
*/
|
|
34
|
+
export function extractGateCause(stdout, stderr, exitCode, spawnError) {
|
|
35
|
+
if (spawnError !== undefined && spawnError.length > 0) {
|
|
36
|
+
// Normalize common missing-binary messages without path dumps.
|
|
37
|
+
if (/ENOENT|not found|not recognized/i.test(spawnError)) {
|
|
38
|
+
if (/\b(deft|directive)(\.cmd)?\b/i.test(spawnError)) {
|
|
39
|
+
return "global deft/directive CLI not found on PATH";
|
|
40
|
+
}
|
|
41
|
+
return "task binary not found on PATH (cannot spawn go-task)";
|
|
42
|
+
}
|
|
43
|
+
return sanitizeCauseLine(spawnError);
|
|
44
|
+
}
|
|
45
|
+
const combined = `${stderr}\n${stdout}`
|
|
46
|
+
.split(/\r?\n/)
|
|
47
|
+
.map((l) => l.trim())
|
|
48
|
+
.filter((l) => l.length > 0);
|
|
49
|
+
for (const line of combined) {
|
|
50
|
+
if (looksLikeEnvLeak(line))
|
|
51
|
+
continue;
|
|
52
|
+
if (line.startsWith("check:"))
|
|
53
|
+
continue;
|
|
54
|
+
return sanitizeCauseLine(line);
|
|
55
|
+
}
|
|
56
|
+
return `gate exited ${exitCode} without a diagnostic message`;
|
|
57
|
+
}
|
|
58
|
+
function looksLikeEnvLeak(line) {
|
|
59
|
+
// Avoid echoing DEFT_*= or generic env dumps.
|
|
60
|
+
if (/^[A-Z][A-Z0-9_]*=/.test(line))
|
|
61
|
+
return true;
|
|
62
|
+
if (/\bDEFT_[A-Z0-9_]+\b/.test(line) && line.includes("="))
|
|
63
|
+
return true;
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
function sanitizeCauseLine(line) {
|
|
67
|
+
// Cap length; collapse absolute home paths lightly.
|
|
68
|
+
let out = line.replace(/\r/g, "").trim();
|
|
69
|
+
if (out.length > 240) {
|
|
70
|
+
out = `${out.slice(0, 237)}...`;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
export function remedyForGate(gateId, cause) {
|
|
75
|
+
if (/global deft\/directive CLI not found/i.test(cause)) {
|
|
76
|
+
return CLI_SPAWN_ERROR_REMEDY;
|
|
77
|
+
}
|
|
78
|
+
if (/task binary not found|cannot spawn go-task/i.test(cause)) {
|
|
79
|
+
if (gateId.startsWith("verify:") || gateId.startsWith("verify-") || gateId === "doctor") {
|
|
80
|
+
return CLI_SPAWN_ERROR_REMEDY;
|
|
81
|
+
}
|
|
82
|
+
return SPAWN_ERROR_REMEDY;
|
|
83
|
+
}
|
|
84
|
+
if (/pnpm binary not found|pnpm: NOT FOUND/i.test(cause)) {
|
|
85
|
+
return "Enable pnpm: corepack enable && corepack prepare pnpm@latest --activate";
|
|
86
|
+
}
|
|
87
|
+
return (GATE_REMEDIES[gateId] ??
|
|
88
|
+
`Re-run the gate for details: task ${gateId} (or task check); fix the reported product/process defect`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Format named-cause failure lines for a single gate.
|
|
92
|
+
*/
|
|
93
|
+
export function formatNamedCauseFailure(input) {
|
|
94
|
+
const cause = extractGateCause(input.stdout ?? "", input.stderr ?? "", input.exitCode, input.spawnError);
|
|
95
|
+
const remedy = remedyForGate(input.gateId, cause);
|
|
96
|
+
const lines = [
|
|
97
|
+
`check: gate ${input.gateId} failed (exit ${input.exitCode})`,
|
|
98
|
+
` cause: ${cause}`,
|
|
99
|
+
` remedy: ${remedy}`,
|
|
100
|
+
];
|
|
101
|
+
return {
|
|
102
|
+
gateId: input.gateId,
|
|
103
|
+
exitCode: input.exitCode,
|
|
104
|
+
cause,
|
|
105
|
+
remedy,
|
|
106
|
+
lines,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Format degraded-mode skip report (which gates skipped and why).
|
|
111
|
+
*/
|
|
112
|
+
export function formatDegradedSkipReport(input) {
|
|
113
|
+
const exitCode = input.exitCode ?? 2;
|
|
114
|
+
const lines = [
|
|
115
|
+
`check: degraded mode — ${input.reason}`,
|
|
116
|
+
`check: skipped ${input.skipped.length} gate(s) due to missing framework toolchain (#3282):`,
|
|
117
|
+
];
|
|
118
|
+
for (const gate of input.skipped) {
|
|
119
|
+
lines.push(` - ${gate.id}: cause: ${gate.cause}; remedy: ${gate.remedy}`);
|
|
120
|
+
}
|
|
121
|
+
if (input.ran !== undefined && input.ran.length > 0) {
|
|
122
|
+
lines.push(`check: ran: ${input.ran.join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
if (input.failed !== undefined && input.failed.length > 0) {
|
|
125
|
+
lines.push(`check: failed: ${input.failed.join(", ")}`);
|
|
126
|
+
}
|
|
127
|
+
lines.push(`check: exit ${exitCode} (degraded/config) — skipped required gates are not a green pass; ` +
|
|
128
|
+
"install missing tooling (see remedies above) and re-run task check");
|
|
129
|
+
return lines;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=named-cause.js.map
|
|
@@ -57,7 +57,12 @@ export function dispatchTaskCheck(frameworkRoot, projectRoot, seams = {}) {
|
|
|
57
57
|
timeoutMs: seams.timeoutMs,
|
|
58
58
|
});
|
|
59
59
|
if (result.error !== undefined) {
|
|
60
|
-
|
|
60
|
+
// #3282: named cause + remedy instead of bare spawn failure.
|
|
61
|
+
const detail = result.error.message;
|
|
62
|
+
const missingTask = /ENOENT|not found|not recognized/i.test(detail);
|
|
63
|
+
process.stderr.write(`check: gate ${target} failed (exit 2)\n` +
|
|
64
|
+
` cause: ${missingTask ? "task binary not found on PATH (cannot spawn go-task)" : detail}\n` +
|
|
65
|
+
" remedy: Install go-task (https://taskfile.dev/installation/) and ensure `task` is on PATH; then re-run task check\n");
|
|
61
66
|
return 2;
|
|
62
67
|
}
|
|
63
68
|
if (result.signal === "SIGTERM" && result.status === null && seams.timeoutMs !== undefined) {
|
package/dist/doctor/checks.d.ts
CHANGED
|
@@ -87,12 +87,6 @@ export declare function checkTypescript7SideBySide(projectRoot: string, seams?:
|
|
|
87
87
|
* `directive init` or `directive update`).
|
|
88
88
|
*/
|
|
89
89
|
export declare function checkGitignoreCoverage(projectRoot: string, seams?: CheckSeams): CheckResult;
|
|
90
|
-
/**
|
|
91
|
-
* Surface undecided / invalid coverageDebt + checkResume policy (#3189).
|
|
92
|
-
* Advisory skip when undecided; never hard-fails doctor / check:consumer.
|
|
93
|
-
* Decided-off is quiet; dismiss-with-reason is pass with reason in detail.
|
|
94
|
-
* Invalid typed blocks resolve fail-closed and surface via source=default-on-error.
|
|
95
|
-
*/
|
|
96
90
|
/**
|
|
97
91
|
* Fail closed when completed/ plan.status disagrees with the folder (#3242 AC1 / epic #3237 Q4).
|
|
98
92
|
* Open plan.items under completed/ are reported by `checkCompletedOpenItems` (exit-exempt
|
|
@@ -104,6 +98,11 @@ export declare function checkCompletedLifecycleConsistency(projectRoot: string):
|
|
|
104
98
|
* historical pre-#2862 corpora do not red-light doctor; scope:complete fails closed.
|
|
105
99
|
*/
|
|
106
100
|
export declare function checkCompletedOpenItems(projectRoot: string): CheckResult;
|
|
101
|
+
/**
|
|
102
|
+
* Surface invalid coverageDebt + checkResume typed blocks (#3314).
|
|
103
|
+
* Absent / default / valid typed values pass. Invalid blocks skip (advisory,
|
|
104
|
+
* fail-closed resolution). Doctor no longer reports "undecided".
|
|
105
|
+
*/
|
|
107
106
|
export declare function checkCoverageCheckResumePolicy(projectRoot: string): CheckResult;
|
|
108
107
|
export declare function deriveExitCode(checks: readonly CheckResult[], errors: readonly string[]): number;
|
|
109
108
|
export declare function runChecksImpl(projectRoot: string, seams?: CheckSeams & {
|
package/dist/doctor/checks.js
CHANGED
|
@@ -8,7 +8,6 @@ import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
|
8
8
|
import { scanCompletedLifecycleConsistency } from "../lifecycle/completed-consistency.js";
|
|
9
9
|
import { resolveCheckResume } from "../policy/check-resume.js";
|
|
10
10
|
import { resolveCoverageDebt } from "../policy/coverage-debt.js";
|
|
11
|
-
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
12
11
|
import { classifyXbriefSchemaDistance } from "../staleness-tickler/probe-xbrief.js";
|
|
13
12
|
import { findSkillPathsInText } from "../text/redos-safe.js";
|
|
14
13
|
import { stripGitignoreInlineComment } from "../triage/bootstrap/gitignore.js";
|
|
@@ -1070,12 +1069,6 @@ export function checkGitignoreCoverage(projectRoot, seams = {}) {
|
|
|
1070
1069
|
},
|
|
1071
1070
|
};
|
|
1072
1071
|
}
|
|
1073
|
-
/**
|
|
1074
|
-
* Surface undecided / invalid coverageDebt + checkResume policy (#3189).
|
|
1075
|
-
* Advisory skip when undecided; never hard-fails doctor / check:consumer.
|
|
1076
|
-
* Decided-off is quiet; dismiss-with-reason is pass with reason in detail.
|
|
1077
|
-
* Invalid typed blocks resolve fail-closed and surface via source=default-on-error.
|
|
1078
|
-
*/
|
|
1079
1072
|
/**
|
|
1080
1073
|
* Fail closed when completed/ plan.status disagrees with the folder (#3242 AC1 / epic #3237 Q4).
|
|
1081
1074
|
* Open plan.items under completed/ are reported by `checkCompletedOpenItems` (exit-exempt
|
|
@@ -1152,6 +1145,11 @@ export function checkCompletedOpenItems(projectRoot) {
|
|
|
1152
1145
|
},
|
|
1153
1146
|
};
|
|
1154
1147
|
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Surface invalid coverageDebt + checkResume typed blocks (#3314).
|
|
1150
|
+
* Absent / default / valid typed values pass. Invalid blocks skip (advisory,
|
|
1151
|
+
* fail-closed resolution). Doctor no longer reports "undecided".
|
|
1152
|
+
*/
|
|
1155
1153
|
export function checkCoverageCheckResumePolicy(projectRoot) {
|
|
1156
1154
|
const debt = resolveCoverageDebt(projectRoot);
|
|
1157
1155
|
const resume = resolveCheckResume(projectRoot);
|
|
@@ -1160,66 +1158,25 @@ export function checkCoverageCheckResumePolicy(projectRoot) {
|
|
|
1160
1158
|
name: "coverage-check-resume-policy",
|
|
1161
1159
|
status: "skip",
|
|
1162
1160
|
detail: "advisory: coverageDebt and/or checkResume block is invalid; " +
|
|
1163
|
-
"resolution is fail-closed (mode off, localStamp off
|
|
1161
|
+
"resolution is fail-closed (mode off, localStamp off). " +
|
|
1164
1162
|
`coverageDebt.error=${JSON.stringify(debt.error)}; ` +
|
|
1165
1163
|
`checkResume.error=${JSON.stringify(resume.error)}. ` +
|
|
1166
|
-
"Fix the typed block
|
|
1164
|
+
"Fix the typed block in PROJECT-DEFINITION.",
|
|
1167
1165
|
data: {
|
|
1168
|
-
coverageDebt: {
|
|
1169
|
-
checkResume: {
|
|
1166
|
+
coverageDebt: { mode: debt.mode, source: debt.source, error: debt.error },
|
|
1167
|
+
checkResume: { localStamp: resume.localStamp, source: resume.source, error: resume.error },
|
|
1170
1168
|
advisory: true,
|
|
1171
1169
|
invalid: true,
|
|
1172
1170
|
},
|
|
1173
1171
|
};
|
|
1174
1172
|
}
|
|
1175
|
-
const undecided = debt.status === "unset" || resume.status === "unset";
|
|
1176
|
-
if (!undecided) {
|
|
1177
|
-
const dismissParts = [];
|
|
1178
|
-
if (debt.dismissReason) {
|
|
1179
|
-
dismissParts.push(`coverageDebt.dismissReason=${JSON.stringify(debt.dismissReason)}`);
|
|
1180
|
-
}
|
|
1181
|
-
if (resume.dismissReason) {
|
|
1182
|
-
dismissParts.push(`checkResume.dismissReason=${JSON.stringify(resume.dismissReason)}`);
|
|
1183
|
-
}
|
|
1184
|
-
const dismissNote = dismissParts.length > 0 ? ` Dismissed: ${dismissParts.join("; ")}.` : "";
|
|
1185
|
-
return {
|
|
1186
|
-
name: "coverage-check-resume-policy",
|
|
1187
|
-
status: "pass",
|
|
1188
|
-
detail: `coverageDebt status=${debt.status} mode=${debt.mode}; ` +
|
|
1189
|
-
`checkResume status=${resume.status} localStamp=${resume.localStamp}; ` +
|
|
1190
|
-
`ciTrustsLocalStamp=false (fixed v1).${dismissNote}`,
|
|
1191
|
-
data: {
|
|
1192
|
-
coverageDebt: {
|
|
1193
|
-
status: debt.status,
|
|
1194
|
-
mode: debt.mode,
|
|
1195
|
-
autoFile: debt.autoFile,
|
|
1196
|
-
dismissReason: debt.dismissReason,
|
|
1197
|
-
},
|
|
1198
|
-
checkResume: {
|
|
1199
|
-
status: resume.status,
|
|
1200
|
-
localStamp: resume.localStamp,
|
|
1201
|
-
ciTrustsLocalStamp: false,
|
|
1202
|
-
dismissReason: resume.dismissReason,
|
|
1203
|
-
},
|
|
1204
|
-
},
|
|
1205
|
-
};
|
|
1206
|
-
}
|
|
1207
|
-
// Advisory only (status=skip): never hard-fails doctor / check:consumer (#3189).
|
|
1208
|
-
// Behavior remains fail-closed while unset; session-start nudge carries the ask.
|
|
1209
1173
|
return {
|
|
1210
1174
|
name: "coverage-check-resume-policy",
|
|
1211
|
-
status: "
|
|
1212
|
-
detail:
|
|
1213
|
-
"Behavior stays fail-closed (no hatch soft-pass, no local suite stamp, CI never trusts stamps). " +
|
|
1214
|
-
"Choose Strict / Hatch-aware on the next interactive session-start nudge, or record " +
|
|
1215
|
-
"dismiss-with-reason. Inspect: " +
|
|
1216
|
-
`\`${policyColonInvocation("show", " --field=coverageDebt")}\` / ` +
|
|
1217
|
-
`\`${policyColonInvocation("show", " --field=checkResume")}\`.`,
|
|
1175
|
+
status: "pass",
|
|
1176
|
+
detail: `coverageDebt.mode=${debt.mode}; checkResume.localStamp=${resume.localStamp}.`,
|
|
1218
1177
|
data: {
|
|
1219
|
-
coverageDebt: {
|
|
1220
|
-
checkResume: {
|
|
1221
|
-
suggested_fix: policyColonInvocation("show", " --field=coverageDebt"),
|
|
1222
|
-
advisory: true,
|
|
1178
|
+
coverageDebt: { mode: debt.mode, autoFile: debt.autoFile, source: debt.source },
|
|
1179
|
+
checkResume: { localStamp: resume.localStamp, source: resume.source },
|
|
1223
1180
|
},
|
|
1224
1181
|
};
|
|
1225
1182
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor --help / -h text (#2712 session coda env docs + flag surface).
|
|
3
|
+
*/
|
|
4
|
+
import { DOCTOR_ALLOWED_FLAGS } from "./constants.js";
|
|
5
|
+
import { sessionCodaHelpText } from "./session-coda.js";
|
|
6
|
+
export function formatDoctorHelp() {
|
|
7
|
+
const flags = DOCTOR_ALLOWED_FLAGS.join(", ");
|
|
8
|
+
return [
|
|
9
|
+
"Usage: deft doctor [options]",
|
|
10
|
+
"",
|
|
11
|
+
"Diagnose the Directive install and print the one next step.",
|
|
12
|
+
"",
|
|
13
|
+
"Options:",
|
|
14
|
+
" --session Session-oriented probe",
|
|
15
|
+
" --fix, --repair Attempt safe repairs (Taskfile / OpenClaw pins)",
|
|
16
|
+
" --json Machine-readable JSON (no human footer / no coda)",
|
|
17
|
+
" --quiet Suppress mid-run chatter; final summary still prints",
|
|
18
|
+
" --full Bypass throttle; re-probe fully",
|
|
19
|
+
" --network Allow offline-tier network checks (payload staleness)",
|
|
20
|
+
" --project-root <path> Project root (default: cwd)",
|
|
21
|
+
" --force Replace divergent OpenClaw pins during --fix",
|
|
22
|
+
" --openclaw-all-agents Wire always-pins into every OpenClaw workspace seat",
|
|
23
|
+
" -h, --help Show this help",
|
|
24
|
+
"",
|
|
25
|
+
`Allowed flags: ${flags}`,
|
|
26
|
+
"",
|
|
27
|
+
sessionCodaHelpText(),
|
|
28
|
+
"",
|
|
29
|
+
].join("\n");
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=help.js.map
|
package/dist/doctor/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./checks.js";
|
|
|
3
3
|
export * from "./constants.js";
|
|
4
4
|
export * from "./doctor-state.js";
|
|
5
5
|
export * from "./flags.js";
|
|
6
|
+
export * from "./help.js";
|
|
6
7
|
export * from "./json.js";
|
|
7
8
|
export * from "./main.js";
|
|
8
9
|
export * from "./manifest.js";
|
|
@@ -13,6 +14,7 @@ export * from "./openclaw-soft-rebind.js";
|
|
|
13
14
|
export * from "./paths.js";
|
|
14
15
|
export * from "./payload-staleness.js";
|
|
15
16
|
export * from "./release-availability.js";
|
|
17
|
+
export * from "./session-coda.js";
|
|
16
18
|
export * from "./taskfile.js";
|
|
17
19
|
export * from "./types.js";
|
|
18
20
|
export * from "./which.js";
|
package/dist/doctor/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./checks.js";
|
|
|
3
3
|
export * from "./constants.js";
|
|
4
4
|
export * from "./doctor-state.js";
|
|
5
5
|
export * from "./flags.js";
|
|
6
|
+
export * from "./help.js";
|
|
6
7
|
export * from "./json.js";
|
|
7
8
|
export * from "./main.js";
|
|
8
9
|
export * from "./manifest.js";
|
|
@@ -13,6 +14,7 @@ export * from "./openclaw-soft-rebind.js";
|
|
|
13
14
|
export * from "./paths.js";
|
|
14
15
|
export * from "./payload-staleness.js";
|
|
15
16
|
export * from "./release-availability.js";
|
|
17
|
+
export * from "./session-coda.js";
|
|
16
18
|
export * from "./taskfile.js";
|
|
17
19
|
export * from "./types.js";
|
|
18
20
|
export * from "./which.js";
|
package/dist/doctor/main.js
CHANGED
|
@@ -16,6 +16,7 @@ import { checkXbriefEnvelopeMajorVersion, runChecks, XBRIEF_ENVELOPE_MAJOR_CHECK
|
|
|
16
16
|
import { CONSUMER_FRAMEWORK_DIRS, EXPECTED_CONTENT_DIRS, EXPECTED_FRAMEWORK_DIRS, NETWORK_DISCLOSURE_LINE, PAYLOAD_STALENESS_OFFLINE_SKIP_MESSAGE, TASKFILE_INCLUDE_SNIPPET, UV_INSTALL_URL, } from "./constants.js";
|
|
17
17
|
import { decideThrottle, formatIsoZ, readState, renderDoctorStatusLine, writeState, } from "./doctor-state.js";
|
|
18
18
|
import { formatAllowedFlagsHint, formatUnknownFlagsError, parseDoctorFlags } from "./flags.js";
|
|
19
|
+
import { formatDoctorHelp } from "./help.js";
|
|
19
20
|
import { pythonJsonDump } from "./json.js";
|
|
20
21
|
import { parseInstallRootFromAgentsMd } from "./manifest.js";
|
|
21
22
|
import { runNpmRegistryMirrorCheck } from "./npm-registry.js";
|
|
@@ -25,6 +26,7 @@ import { runOpenClawSoftRebindCheck } from "./openclaw-soft-rebind.js";
|
|
|
25
26
|
import { createPlainSink } from "./output.js";
|
|
26
27
|
import { readTextSafe, resolveFrameworkRootForProject, resolvePath, resolveVersion, runningInsideDeftRepo, } from "./paths.js";
|
|
27
28
|
import { runPayloadStalenessCheck } from "./payload-staleness.js";
|
|
29
|
+
import { loadSessionCodas, projectRootRealpath, resolveSessionCodaLine, SESSION_CODA_ENV, sessionCodaMode, shouldEmitSessionCodaLine, utcDateYmd, } from "./session-coda.js";
|
|
28
30
|
import { runLocalSignpostChecks } from "./signpost-checks.js";
|
|
29
31
|
import { classifyTaskfileInclude, formatGatesSurfaceDualRemediation, formatMissingIncludeSnippet, GATES_SURFACE_DEFT_REMEDIATION, includesBlockHasDeftTaskfile, resolveConsumerTaskfile, } from "./taskfile.js";
|
|
30
32
|
import { defaultWhich } from "./which.js";
|
|
@@ -58,6 +60,10 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
58
60
|
sink.info(formatAllowedFlagsHint());
|
|
59
61
|
return 2;
|
|
60
62
|
}
|
|
63
|
+
if (flags.help) {
|
|
64
|
+
process.stdout.write(formatDoctorHelp());
|
|
65
|
+
return 0;
|
|
66
|
+
}
|
|
61
67
|
const sessionMode = flags.session;
|
|
62
68
|
const fixMode = flags.fix && !sessionMode;
|
|
63
69
|
const jsonMode = flags.json;
|
|
@@ -489,6 +495,15 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
489
495
|
sink.blank();
|
|
490
496
|
if (errorCount === 0 && warningCount === 0) {
|
|
491
497
|
sink.finalSuccess("System check passed!");
|
|
498
|
+
emitSessionCodaAfterFinalSuccess({
|
|
499
|
+
projectRoot,
|
|
500
|
+
frameworkRoot,
|
|
501
|
+
jsonMode,
|
|
502
|
+
exitOk: true,
|
|
503
|
+
now: nowFn(),
|
|
504
|
+
seams,
|
|
505
|
+
sink,
|
|
506
|
+
});
|
|
492
507
|
return 0;
|
|
493
508
|
}
|
|
494
509
|
if (errorCount) {
|
|
@@ -498,8 +513,51 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
498
513
|
return 1;
|
|
499
514
|
}
|
|
500
515
|
sink.finalWarn(`System check completed with ${warningCount} warning(s).`);
|
|
516
|
+
emitSessionCodaAfterFinalSuccess({
|
|
517
|
+
projectRoot,
|
|
518
|
+
frameworkRoot,
|
|
519
|
+
jsonMode,
|
|
520
|
+
exitOk: true,
|
|
521
|
+
now: nowFn(),
|
|
522
|
+
seams,
|
|
523
|
+
sink,
|
|
524
|
+
});
|
|
501
525
|
return 0;
|
|
502
526
|
}
|
|
527
|
+
/**
|
|
528
|
+
* After the final human success footer only (D2): optional off-hint or ✦ coda.
|
|
529
|
+
* Never mid-run Next command: block; never JSON; never hard-fail path (D1).
|
|
530
|
+
*/
|
|
531
|
+
function emitSessionCodaAfterFinalSuccess(input) {
|
|
532
|
+
const stdoutIsTty = input.seams.stdoutIsTty ?? (() => process.stdout.isTTY === true);
|
|
533
|
+
// `in` so tests can force unset via `{ sessionCodaEnv: undefined }` without
|
|
534
|
+
// leaking the host process env.
|
|
535
|
+
const envRaw = "sessionCodaEnv" in input.seams ? input.seams.sessionCodaEnv : process.env[SESSION_CODA_ENV];
|
|
536
|
+
const ciRaw = "ciEnv" in input.seams ? input.seams.ciEnv : process.env.CI;
|
|
537
|
+
const ci = Boolean(ciRaw && ciRaw !== "0" && String(ciRaw).toLowerCase() !== "false");
|
|
538
|
+
const shouldEmit = shouldEmitSessionCodaLine({
|
|
539
|
+
tty: stdoutIsTty(),
|
|
540
|
+
ci,
|
|
541
|
+
json: input.jsonMode,
|
|
542
|
+
exitOk: input.exitOk,
|
|
543
|
+
});
|
|
544
|
+
const mode = sessionCodaMode(envRaw);
|
|
545
|
+
const codas = mode === "on"
|
|
546
|
+
? (input.seams.loadSessionCodas?.(input.frameworkRoot) ??
|
|
547
|
+
loadSessionCodas(input.frameworkRoot, input.seams.readText))
|
|
548
|
+
: [];
|
|
549
|
+
const line = resolveSessionCodaLine({
|
|
550
|
+
mode,
|
|
551
|
+
shouldEmit,
|
|
552
|
+
date: utcDateYmd(input.now),
|
|
553
|
+
projectRoot: projectRootRealpath(input.projectRoot),
|
|
554
|
+
codas,
|
|
555
|
+
});
|
|
556
|
+
if (line === null)
|
|
557
|
+
return;
|
|
558
|
+
input.sink.blank();
|
|
559
|
+
input.sink.raw(line);
|
|
560
|
+
}
|
|
503
561
|
export function runAgentHooksHealthCheck(projectRoot, consumerContext, sink, addFinding, seams) {
|
|
504
562
|
const checkName = "agent-hooks-registration";
|
|
505
563
|
if (!consumerContext) {
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional deterministic session coda for interactive doctor success (#2712).
|
|
3
|
+
*
|
|
4
|
+
* Human TTY affordance only: never JSON, never AGENTS/skills, never exit-code
|
|
5
|
+
* changes. Env three-state DEFT_SESSION_CODA (unset / 1 / 0). Copy lives in
|
|
6
|
+
* content/doctor/session-coda.json — not embedded as an engine literal.
|
|
7
|
+
*/
|
|
8
|
+
/** Env var name (env-only v1; no USER.md key). */
|
|
9
|
+
export declare const SESSION_CODA_ENV = "DEFT_SESSION_CODA";
|
|
10
|
+
/**
|
|
11
|
+
* Fixed discoverability line when env is unset and gates pass (D3).
|
|
12
|
+
* Snapshot-stable; not selected from the pack; no ✦ prefix.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SESSION_CODA_OFF_HINT = "Session coda: off (set DEFT_SESSION_CODA=1 to enable)";
|
|
15
|
+
/** U+2726 FOUR RAYED STAR + space — stable; do not localize. */
|
|
16
|
+
export declare const SESSION_CODA_STAR_PREFIX = "\u2726 ";
|
|
17
|
+
/** Max body length; longer pack entries are skipped (fail-open). */
|
|
18
|
+
export declare const SESSION_CODA_MAX_CHARS = 100;
|
|
19
|
+
/** Relative path under contentRoot (source: content/doctor/…; deposit: doctor/…). */
|
|
20
|
+
export declare const SESSION_CODA_REL_PATH: string;
|
|
21
|
+
export type SessionCodaMode = "unset" | "on" | "off";
|
|
22
|
+
/**
|
|
23
|
+
* Map DEFT_SESSION_CODA to the three-state mode (D3).
|
|
24
|
+
* - undefined / "" → unset (discoverability off-hint)
|
|
25
|
+
* - "1" → on (✦ coda)
|
|
26
|
+
* - "0" → off (silent)
|
|
27
|
+
* - any other non-empty value → unset (show correct enable form)
|
|
28
|
+
*/
|
|
29
|
+
export declare function sessionCodaMode(envValue: string | undefined): SessionCodaMode;
|
|
30
|
+
/**
|
|
31
|
+
* Whether any extra line (off-hint or coda) is allowed (D1 + surface gates).
|
|
32
|
+
* TTY = stdout.isTTY; CI truthy blocks; json blocks; exitOk = errorCount === 0.
|
|
33
|
+
*/
|
|
34
|
+
export declare function shouldEmitSessionCodaLine(input: {
|
|
35
|
+
readonly tty: boolean;
|
|
36
|
+
readonly ci: boolean;
|
|
37
|
+
readonly json: boolean;
|
|
38
|
+
readonly exitOk: boolean;
|
|
39
|
+
}): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* FNV-1a 32-bit (#2712). Documented choice for hash_u32(date + "\\0" + root).
|
|
42
|
+
* No Math.random; same inputs ⇒ same u32 on every platform.
|
|
43
|
+
*/
|
|
44
|
+
export declare function hashU32(input: string): number;
|
|
45
|
+
/** UTC calendar date yyyy-mm-dd from a Date (stable for 24h across a team day). */
|
|
46
|
+
export declare function utcDateYmd(date: Date): string;
|
|
47
|
+
/** Best-effort realpath; falls back to the given path when realpath fails. */
|
|
48
|
+
export declare function projectRootRealpath(projectRoot: string): string;
|
|
49
|
+
/**
|
|
50
|
+
* Select one coda body from the pack (enabled path only).
|
|
51
|
+
* index = hash_u32(utc_date + "\\0" + project_root_realpath) % len(codas)
|
|
52
|
+
* Empty list → null (omit ✦; doctor still 0).
|
|
53
|
+
*/
|
|
54
|
+
export declare function selectSessionCoda(input: {
|
|
55
|
+
readonly date: string;
|
|
56
|
+
readonly projectRoot: string;
|
|
57
|
+
readonly codas: readonly string[];
|
|
58
|
+
}): string | null;
|
|
59
|
+
/** Skip non-strings and bodies over SESSION_CODA_MAX_CHARS. */
|
|
60
|
+
export declare function filterCodaEntries(raw: unknown): string[];
|
|
61
|
+
/**
|
|
62
|
+
* Load + filter coda lines from content. Fail-open: missing/unreadable/invalid
|
|
63
|
+
* / empty after filter → [] (omit ✦; no error noise).
|
|
64
|
+
*/
|
|
65
|
+
export declare function loadSessionCodas(frameworkRoot: string, readText?: (path: string) => string | null): string[];
|
|
66
|
+
/** Format enabled coda line: ✦ + space + body. */
|
|
67
|
+
export declare function formatSessionCodaEnabledLine(body: string): string;
|
|
68
|
+
export interface ResolveSessionCodaLineInput {
|
|
69
|
+
readonly mode: SessionCodaMode;
|
|
70
|
+
readonly shouldEmit: boolean;
|
|
71
|
+
readonly date: string;
|
|
72
|
+
readonly projectRoot: string;
|
|
73
|
+
readonly codas: readonly string[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Resolve the single extra line (or null) after final success footer.
|
|
77
|
+
* Does not print; caller places after blank line post footer (D2).
|
|
78
|
+
*/
|
|
79
|
+
export declare function resolveSessionCodaLine(input: ResolveSessionCodaLineInput): string | null;
|
|
80
|
+
/** Help / env-docs fragment for DEFT_SESSION_CODA three-state (#2712). */
|
|
81
|
+
export declare function sessionCodaHelpText(): string;
|
|
82
|
+
//# sourceMappingURL=session-coda.d.ts.map
|