@deftai/directive-core 0.104.0 → 0.106.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 +1108 -67
- package/dist/check/cached-orchestrator.d.ts +1 -1
- package/dist/check/cached-orchestrator.js +2 -2
- package/dist/check/named-cause.d.ts +1 -1
- package/dist/check/named-cause.js +34 -4
- package/dist/codebase/default-extractor.d.ts +5 -0
- package/dist/codebase/default-extractor.js +7 -15
- package/dist/deposit/copy-tree.js +60 -0
- package/dist/deposit/python-free.js +26 -18
- package/dist/doctor/checks.d.ts +8 -0
- package/dist/doctor/checks.js +37 -25
- package/dist/doctor/doctor-state.d.ts +4 -0
- package/dist/doctor/doctor-state.js +7 -1
- package/dist/doctor/main.js +9 -19
- package/dist/doctor/types.d.ts +9 -0
- package/dist/forward-coverage/diff-coverage.d.ts +39 -0
- package/dist/forward-coverage/diff-coverage.js +112 -0
- package/dist/forward-coverage/diff-lines.d.ts +19 -0
- package/dist/forward-coverage/diff-lines.js +173 -0
- package/dist/forward-coverage/evaluate.d.ts +24 -9
- package/dist/forward-coverage/evaluate.js +125 -19
- package/dist/fs/contained-write.d.ts +115 -1
- package/dist/fs/contained-write.js +241 -5
- package/dist/fs/mutation-ledger.d.ts +54 -0
- package/dist/fs/mutation-ledger.js +133 -0
- package/dist/fs/non-product-dirs.d.ts +79 -0
- package/dist/fs/non-product-dirs.js +96 -0
- package/dist/hooks/dispatcher.d.ts +3 -1
- package/dist/hooks/dispatcher.js +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +18 -16
- package/dist/init-deposit/core-guard-pin-content.d.ts +7 -0
- package/dist/init-deposit/core-guard-pin-content.js +393 -0
- package/dist/init-deposit/gitignore.js +9 -0
- package/dist/init-deposit/hygiene.d.ts +47 -11
- package/dist/init-deposit/hygiene.js +172 -37
- package/dist/init-deposit/legacy-detect.js +6 -2
- package/dist/init-deposit/refresh.d.ts +21 -1
- package/dist/init-deposit/refresh.js +106 -9
- package/dist/init-deposit/scaffold.js +28 -279
- package/dist/init-deposit/slash-deposit.js +13 -7
- package/dist/init-deposit/xbrief-projections.d.ts +8 -1
- package/dist/init-deposit/xbrief-projections.js +24 -11
- package/dist/intake/clause-derivation.d.ts +47 -1
- package/dist/intake/clause-derivation.js +238 -9
- package/dist/intake/issue-ingest.d.ts +3 -1
- package/dist/intake/issue-ingest.js +35 -6
- package/dist/lifecycle/completed-tracked-on-delivery.d.ts +5 -0
- package/dist/lifecycle/completed-tracked-on-delivery.js +85 -18
- package/dist/lifecycle-visible/evaluate.d.ts +98 -0
- package/dist/lifecycle-visible/evaluate.js +710 -0
- package/dist/lifecycle-visible/index.d.ts +2 -0
- package/dist/lifecycle-visible/index.js +2 -0
- package/dist/literal-acceptance/capture.d.ts +10 -0
- package/dist/literal-acceptance/capture.js +226 -77
- package/dist/literal-acceptance/evaluate.d.ts +29 -4
- package/dist/literal-acceptance/evaluate.js +70 -2
- package/dist/literal-acceptance/index.d.ts +3 -3
- package/dist/literal-acceptance/index.js +3 -3
- package/dist/literal-acceptance/safety.d.ts +41 -0
- package/dist/literal-acceptance/safety.js +126 -2
- package/dist/literal-acceptance/types.d.ts +8 -0
- package/dist/orchestration/judgment-policy.d.ts +13 -0
- package/dist/orchestration/judgment-policy.js +15 -0
- package/dist/orchestration/probe-session.js +13 -8
- package/dist/orchestration/verify-judgment-gates.d.ts +4 -0
- package/dist/orchestration/verify-judgment-gates.js +43 -11
- package/dist/orphan-active/evaluate.d.ts +7 -1
- package/dist/orphan-active/evaluate.js +102 -23
- package/dist/platform/platform-capabilities.js +2 -3
- package/dist/policy/base-branch.d.ts +37 -0
- package/dist/policy/base-branch.js +114 -0
- package/dist/policy/branch-sync.d.ts +80 -0
- package/dist/policy/branch-sync.js +211 -0
- package/dist/policy/ceremony-dial-escalation.js +1 -0
- package/dist/policy/ceremony-dial.js +11 -21
- package/dist/policy/delivery-branch.d.ts +5 -3
- package/dist/policy/delivery-branch.js +7 -3
- package/dist/policy/file-size-thresholds.d.ts +24 -0
- package/dist/policy/file-size-thresholds.js +23 -0
- package/dist/policy/forge-outage-retry.d.ts +47 -0
- package/dist/policy/forge-outage-retry.js +157 -0
- package/dist/policy/host-hooks.d.ts +20 -0
- package/dist/policy/host-hooks.js +121 -2
- package/dist/policy/index.d.ts +7 -0
- package/dist/policy/index.js +66 -7
- package/dist/policy/org-force-on-migration.js +2 -1
- package/dist/policy/plan-extensions.d.ts +1 -1
- package/dist/policy/plan-extensions.js +2 -0
- package/dist/policy/product-signal.js +6 -7
- package/dist/policy/project-invariants.d.ts +50 -0
- package/dist/policy/project-invariants.js +205 -0
- package/dist/policy/require-human-merge.js +8 -6
- package/dist/policy/resolve.d.ts +7 -2
- package/dist/policy/resolve.js +25 -8
- package/dist/policy/sync-default.d.ts +129 -0
- package/dist/policy/sync-default.js +452 -0
- package/dist/policy/sync-max-files.d.ts +93 -0
- package/dist/policy/sync-max-files.js +192 -0
- package/dist/policy/value-feedback.js +7 -8
- package/dist/pr-monitor/absent-required.d.ts +11 -0
- package/dist/pr-monitor/absent-required.js +61 -0
- package/dist/pr-monitor/constants.d.ts +7 -0
- package/dist/pr-monitor/constants.js +7 -0
- package/dist/pr-monitor/index.d.ts +2 -1
- package/dist/pr-monitor/index.js +2 -1
- package/dist/pr-monitor/main.js +4 -0
- package/dist/pr-monitor/monitor.js +24 -1
- package/dist/pr-wait-mergeable/classify.js +6 -0
- package/dist/preflight/evaluate.d.ts +4 -0
- package/dist/preflight/evaluate.js +35 -1
- package/dist/preflight/index.d.ts +4 -0
- package/dist/preflight/index.js +2 -0
- package/dist/preflight/intended-placement.d.ts +62 -0
- package/dist/preflight/intended-placement.js +275 -0
- package/dist/preflight/project-invariants-gate.d.ts +29 -0
- package/dist/preflight/project-invariants-gate.js +82 -0
- package/dist/preflight-cache/evaluate.d.ts +12 -0
- package/dist/preflight-cache/evaluate.js +15 -1
- package/dist/preflight-cache/index.d.ts +1 -1
- package/dist/preflight-cache/index.js +1 -1
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +128 -0
- package/dist/product-first-done-gate/acceptance-resolver.js +144 -0
- package/dist/product-first-done-gate/acceptance.d.ts +7 -4
- package/dist/product-first-done-gate/acceptance.js +79 -8
- package/dist/product-first-done-gate/empty-resolution.d.ts +4 -1
- package/dist/product-first-done-gate/empty-resolution.js +12 -1
- package/dist/product-first-done-gate/evaluate.d.ts +16 -0
- package/dist/product-first-done-gate/evaluate.js +298 -34
- package/dist/product-first-done-gate/index.d.ts +2 -0
- package/dist/product-first-done-gate/index.js +1 -0
- package/dist/release/build-dist.d.ts +101 -0
- package/dist/release/build-dist.js +428 -46
- package/dist/release/closed-verb-gate.d.ts +12 -0
- package/dist/release/closed-verb-gate.js +51 -0
- package/dist/release/index.d.ts +1 -0
- package/dist/release/index.js +1 -0
- package/dist/release/pipeline.js +7 -0
- package/dist/release/types.d.ts +12 -0
- package/dist/render/constants.d.ts +2 -1
- package/dist/render/constants.js +2 -1
- package/dist/render/framework-commands.js +4 -0
- package/dist/render/spec-validate.js +23 -12
- package/dist/run-summary/emit.d.ts +16 -10
- package/dist/run-summary/emit.js +26 -15
- package/dist/run-summary/index.d.ts +3 -2
- package/dist/run-summary/index.js +2 -1
- package/dist/run-summary/session-id.d.ts +21 -0
- package/dist/run-summary/session-id.js +92 -0
- package/dist/run-summary/types.d.ts +33 -4
- package/dist/run-summary/types.js +1 -0
- package/dist/scope/acceptance-evidence.d.ts +19 -7
- package/dist/scope/acceptance-evidence.js +38 -11
- package/dist/scope/delivery-evidence.d.ts +3 -3
- package/dist/scope/delivery-evidence.js +22 -27
- package/dist/scope/index.d.ts +1 -0
- package/dist/scope/index.js +1 -0
- package/dist/scope/main.js +15 -5
- package/dist/scope/parent-lineage.js +7 -6
- package/dist/scope/project-invariant-coverage.d.ts +54 -0
- package/dist/scope/project-invariant-coverage.js +322 -0
- package/dist/scope/promote-from-issue.js +5 -5
- package/dist/scope/transition.js +1 -1
- package/dist/scope-provenance/compare-intent.d.ts +27 -0
- package/dist/scope-provenance/compare-intent.js +233 -0
- package/dist/scope-provenance/digest.d.ts +18 -1
- package/dist/scope-provenance/digest.js +16 -11
- package/dist/scope-provenance/evaluate.d.ts +5 -1
- package/dist/scope-provenance/evaluate.js +67 -7
- package/dist/scope-provenance/extract-intent.d.ts +43 -0
- package/dist/scope-provenance/extract-intent.js +262 -0
- package/dist/scope-provenance/index.d.ts +7 -1
- package/dist/scope-provenance/index.js +7 -1
- package/dist/scope-provenance/intent-digest.d.ts +12 -0
- package/dist/scope-provenance/intent-digest.js +33 -0
- package/dist/scope-provenance/intent-evaluate.d.ts +43 -0
- package/dist/scope-provenance/intent-evaluate.js +149 -0
- package/dist/scope-provenance/json-tokenizer.d.ts +19 -0
- package/dist/scope-provenance/json-tokenizer.js +225 -0
- package/dist/scope-provenance/known-machine.d.ts +38 -0
- package/dist/scope-provenance/known-machine.js +157 -0
- package/dist/scope-provenance/mint-artifacts.d.ts +102 -0
- package/dist/scope-provenance/mint-artifacts.js +425 -0
- package/dist/session/ac-pass-banking.d.ts +9 -36
- package/dist/session/ac-pass-banking.js +33 -79
- package/dist/session/ac-pass-reuse.d.ts +53 -0
- package/dist/session/ac-pass-reuse.js +95 -0
- package/dist/session/git.js +19 -4
- package/dist/session/index.d.ts +4 -0
- package/dist/session/index.js +4 -0
- package/dist/session/occupancy-stress-worker.d.ts +7 -0
- package/dist/session/occupancy-stress-worker.js +36 -0
- package/dist/session/occupancy.d.ts +97 -0
- package/dist/session/occupancy.js +422 -0
- package/dist/session/openclaw-soft-rebind-deposit.js +6 -6
- package/dist/session/process-cost.d.ts +32 -10
- package/dist/session/process-cost.js +189 -1
- package/dist/session/product-state-hash.d.ts +26 -0
- package/dist/session/product-state-hash.js +700 -0
- package/dist/session/ritual-entrypoint.js +16 -1
- package/dist/session/session-ready.d.ts +2 -0
- package/dist/session/session-ready.js +52 -30
- package/dist/session/session-start.d.ts +13 -0
- package/dist/session/session-start.js +103 -9
- package/dist/session/verify-ac-session-cache.d.ts +42 -0
- package/dist/session/verify-ac-session-cache.js +126 -0
- package/dist/session/verify-session-ritual.d.ts +28 -0
- package/dist/session/verify-session-ritual.js +97 -5
- package/dist/slash/openclaw-deposit.js +13 -9
- package/dist/slice/constants.d.ts +2 -2
- package/dist/slice/constants.js +2 -2
- package/dist/slice/existing.js +9 -3
- package/dist/slice/lock.d.ts +16 -1
- package/dist/slice/lock.js +137 -11
- package/dist/story-ready/evaluate.d.ts +2 -0
- package/dist/story-ready/evaluate.js +13 -0
- package/dist/swarm/complete-cohort.d.ts +3 -0
- package/dist/swarm/complete-cohort.js +59 -11
- package/dist/swarm/finalize-cohort-cli.d.ts +7 -2
- package/dist/swarm/finalize-cohort-cli.js +165 -35
- package/dist/swarm/finalize-cohort.d.ts +1 -0
- package/dist/swarm/finalize-cohort.js +47 -4
- package/dist/swarm/launch.d.ts +22 -0
- package/dist/swarm/launch.js +155 -0
- package/dist/swarm/routing-set-cli.d.ts +11 -1
- package/dist/swarm/routing-set-cli.js +15 -3
- package/dist/swarm/routing-verify.d.ts +13 -1
- package/dist/swarm/routing-verify.js +60 -21
- package/dist/swarm/routing.d.ts +14 -0
- package/dist/swarm/routing.js +53 -3
- package/dist/telemetry-coverage/fake-trial.js +10 -0
- package/dist/telemetry-coverage/kinds.js +2 -0
- package/dist/triage/actions/index.js +4 -1
- package/dist/triage/bootstrap/index.js +6 -6
- package/dist/triage/help/registry-data.d.ts +1 -1
- package/dist/triage/help/registry-data.js +1 -1
- package/dist/triage/welcome/writers.d.ts +1 -1
- package/dist/triage/welcome/writers.js +10 -5
- package/dist/ts-check-lane/index.d.ts +3 -0
- package/dist/ts-check-lane/index.js +2 -0
- package/dist/ts-check-lane/progress-reporter.d.ts +17 -0
- package/dist/ts-check-lane/progress-reporter.js +38 -0
- package/dist/ts-check-lane/progress.d.ts +32 -0
- package/dist/ts-check-lane/progress.js +57 -0
- package/dist/ts-check-lane/run-lane.d.ts +3 -1
- package/dist/ts-check-lane/run-lane.js +8 -6
- package/dist/validate-content/validate-links.d.ts +8 -0
- package/dist/validate-content/validate-links.js +9 -15
- package/dist/value/feedback-file.js +12 -2
- package/dist/value/readback.d.ts +4 -1
- package/dist/value/readback.js +51 -12
- package/dist/vbrief-build/index.d.ts +1 -0
- package/dist/vbrief-build/index.js +1 -0
- package/dist/vbrief-build/project-invariants-io.d.ts +16 -0
- package/dist/vbrief-build/project-invariants-io.js +41 -0
- package/dist/vbrief-reconcile/graph.js +18 -1
- package/dist/vbrief-reconcile/index.d.ts +1 -1
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +13 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.js +261 -26
- package/dist/vbrief-validate/index.d.ts +1 -1
- package/dist/vbrief-validate/index.js +1 -1
- package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
- package/dist/vbrief-validate/plan-hooks.js +68 -0
- package/dist/vbrief-validate/schema.d.ts +4 -0
- package/dist/vbrief-validate/schema.js +1 -1
- package/dist/verify-ac/evaluate.d.ts +7 -1
- package/dist/verify-ac/evaluate.js +34 -26
- package/dist/verify-ac/flag.d.ts +4 -0
- package/dist/verify-ac/flag.js +16 -0
- package/dist/verify-ac/index.d.ts +2 -2
- package/dist/verify-ac/index.js +2 -2
- package/dist/verify-env/agent-hook-readiness.d.ts +2 -2
- package/dist/verify-env/agent-hook-readiness.js +12 -8
- package/dist/verify-env/agent-hooks-live-probe.d.ts +5 -1
- package/dist/verify-env/agent-hooks-live-probe.js +26 -5
- package/dist/verify-env/agent-hooks.js +3 -4
- package/dist/verify-source/contained-writes.js +2 -0
- package/dist/verify-source/verify-stubs.d.ts +15 -1
- package/dist/verify-source/verify-stubs.js +20 -16
- package/dist/vitest-runner/win32-coverage-tmp-setup.d.ts +5 -6
- package/dist/vitest-runner/win32-coverage-tmp-setup.js +5 -6
- package/dist/xbrief-migrate/migrate-project.js +2 -5
- package/package.json +7 -3
|
@@ -15,7 +15,7 @@ export interface CachedCheckOptions extends CheckOrchestratorSeams {
|
|
|
15
15
|
* Pass `null` to skip preflight entirely (legacy test paths).
|
|
16
16
|
*/
|
|
17
17
|
readonly preflight?: ToolchainPreflightResult | null;
|
|
18
|
-
/** #3282: session id for run-summary lines (default:
|
|
18
|
+
/** #3282 / #3399: session id for run-summary lines (default: resolver). */
|
|
19
19
|
readonly sessionId?: string;
|
|
20
20
|
/** #3282: disable run-summary emission (tests). */
|
|
21
21
|
readonly emitRunSummary?: boolean;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { join, resolve } from "node:path";
|
|
4
3
|
import { lintShippedRegistry, resolveTaskContract, runWithCache, } from "../cache/task-cache/index.js";
|
|
5
4
|
import { defaultWhich } from "../doctor/which.js";
|
|
@@ -73,7 +72,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
|
|
|
73
72
|
const baseGates = gatesForCheckTarget(target);
|
|
74
73
|
const gates = applyProductFirstGateMode(baseGates, modeResolution.mode, checkGateId);
|
|
75
74
|
const codeVersion = readCorePackageVersion();
|
|
76
|
-
const sessionId = options.sessionId
|
|
75
|
+
const sessionId = options.sessionId;
|
|
77
76
|
const gateOutcomes = [];
|
|
78
77
|
const emitSummary = (exitCode, degraded) => {
|
|
79
78
|
if (options.emitRunSummary === false)
|
|
@@ -84,6 +83,7 @@ export function dispatchCachedTaskCheck(frameworkRoot, projectRoot, options = {}
|
|
|
84
83
|
sessionId,
|
|
85
84
|
frameworkVersion: codeVersion,
|
|
86
85
|
env: options.env,
|
|
86
|
+
component: "check",
|
|
87
87
|
});
|
|
88
88
|
emitter.emitCheckInvocation({
|
|
89
89
|
target,
|
|
@@ -15,7 +15,7 @@ export interface NamedCauseMessage {
|
|
|
15
15
|
* Extract a short cause from gate stdout/stderr without leaking env values.
|
|
16
16
|
* Strips lines that look like KEY=value assignments.
|
|
17
17
|
*/
|
|
18
|
-
export declare function extractGateCause(stdout: string, stderr: string, exitCode: number, spawnError?: string): string;
|
|
18
|
+
export declare function extractGateCause(stdout: string, stderr: string, exitCode: number, spawnError?: string, gateId?: string): string;
|
|
19
19
|
export declare function remedyForGate(gateId: string, cause: string): string;
|
|
20
20
|
/**
|
|
21
21
|
* Format named-cause failure lines for a single gate.
|
|
@@ -20,7 +20,7 @@ const GATE_REMEDIES = {
|
|
|
20
20
|
"verify:test-boundary": "Move tests to the allowed placement or update plan.policy test-boundary allowlist",
|
|
21
21
|
"verify:scope-provenance": "Record approved scope provenance for the failing paths",
|
|
22
22
|
"verify:consumer-check-contract": "Align consumer Taskfile includes with the required gate graph",
|
|
23
|
-
"verify:forward-coverage": "Add tests
|
|
23
|
+
"verify:forward-coverage": "Add tests for new source files and uncovered changed branches (task verify:forward-coverage)",
|
|
24
24
|
"verify:scm-boundary": "Move SCM mutations off GraphQL-heavy paths or wait for rate-limit reset",
|
|
25
25
|
"verify:license-sync": "Sync LICENSE / package license fields",
|
|
26
26
|
"verify:agents-md-budget": "Trim AGENTS.md managed section or raise plan.policy.agentsMdBudget deliberately",
|
|
@@ -31,7 +31,7 @@ const CLI_SPAWN_ERROR_REMEDY = "Install: npm i -g @deftai/directive@latest";
|
|
|
31
31
|
* Extract a short cause from gate stdout/stderr without leaking env values.
|
|
32
32
|
* Strips lines that look like KEY=value assignments.
|
|
33
33
|
*/
|
|
34
|
-
export function extractGateCause(stdout, stderr, exitCode, spawnError) {
|
|
34
|
+
export function extractGateCause(stdout, stderr, exitCode, spawnError, gateId) {
|
|
35
35
|
if (spawnError !== undefined && spawnError.length > 0) {
|
|
36
36
|
// Normalize common missing-binary messages without path dumps.
|
|
37
37
|
if (/ENOENT|not found|not recognized/i.test(spawnError)) {
|
|
@@ -46,15 +46,45 @@ export function extractGateCause(stdout, stderr, exitCode, spawnError) {
|
|
|
46
46
|
.split(/\r?\n/)
|
|
47
47
|
.map((l) => l.trim())
|
|
48
48
|
.filter((l) => l.length > 0);
|
|
49
|
+
const useful = [];
|
|
49
50
|
for (const line of combined) {
|
|
50
51
|
if (looksLikeEnvLeak(line))
|
|
51
52
|
continue;
|
|
52
53
|
if (line.startsWith("check:"))
|
|
53
54
|
continue;
|
|
54
|
-
|
|
55
|
+
if (isGoTaskWrapperNoise(line))
|
|
56
|
+
continue;
|
|
57
|
+
useful.push(line);
|
|
58
|
+
}
|
|
59
|
+
const gateHint = gateId?.trim() ?? "";
|
|
60
|
+
if (gateHint.length > 0) {
|
|
61
|
+
const named = useful.find((line) => line.includes(gateHint));
|
|
62
|
+
if (named !== undefined) {
|
|
63
|
+
return sanitizeCauseLine(named);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (useful.length > 0) {
|
|
67
|
+
return sanitizeCauseLine(useful[0]);
|
|
55
68
|
}
|
|
56
69
|
return `gate exited ${exitCode} without a diagnostic message`;
|
|
57
70
|
}
|
|
71
|
+
/** go-task echoes `task: [engine:_ts-build] set -eu` plus the script body (#3449). */
|
|
72
|
+
function isGoTaskWrapperNoise(line) {
|
|
73
|
+
if (/^task: \[/.test(line))
|
|
74
|
+
return true;
|
|
75
|
+
if (/^set -eu$/.test(line))
|
|
76
|
+
return true;
|
|
77
|
+
if (/^: #/.test(line))
|
|
78
|
+
return true;
|
|
79
|
+
if (/^# /.test(line) || /^#\t/.test(line))
|
|
80
|
+
return true;
|
|
81
|
+
if (/^(bin|root_pkg|is_buildable_source|first_token|is_runtime_verb|global_cli)=/.test(line)) {
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
if (/^(if |elif |else$|fi$|then$)/.test(line))
|
|
85
|
+
return true;
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
58
88
|
function looksLikeEnvLeak(line) {
|
|
59
89
|
// Avoid echoing DEFT_*= or generic env dumps.
|
|
60
90
|
if (/^[A-Z][A-Z0-9_]*=/.test(line))
|
|
@@ -91,7 +121,7 @@ export function remedyForGate(gateId, cause) {
|
|
|
91
121
|
* Format named-cause failure lines for a single gate.
|
|
92
122
|
*/
|
|
93
123
|
export function formatNamedCauseFailure(input) {
|
|
94
|
-
const cause = extractGateCause(input.stdout ?? "", input.stderr ?? "", input.exitCode, input.spawnError);
|
|
124
|
+
const cause = extractGateCause(input.stdout ?? "", input.stderr ?? "", input.exitCode, input.spawnError, input.gateId);
|
|
95
125
|
const remedy = remedyForGate(input.gateId, cause);
|
|
96
126
|
const lines = [
|
|
97
127
|
`check: gate ${input.gateId} failed (exit ${input.exitCode})`,
|
|
@@ -3,6 +3,11 @@ export declare const DEFAULT_PROVIDER_VERSION = "0.1";
|
|
|
3
3
|
export declare const MAX_IMPORT_SCAN_BYTES = 262144;
|
|
4
4
|
export declare const MAX_FILES_PER_MODULE = 100;
|
|
5
5
|
export declare const MAX_EVIDENCE_PER_EDGE = 5;
|
|
6
|
+
/**
|
|
7
|
+
* Shared "not product source" core (#3487) plus `build`, which only the
|
|
8
|
+
* codebase extractor treats as non-source. Agent worktrees are not source
|
|
9
|
+
* modules (#2953, #1656).
|
|
10
|
+
*/
|
|
6
11
|
export declare const SKIP_DIRS: Set<string>;
|
|
7
12
|
export declare const LANGUAGE_BY_SUFFIX: Readonly<Record<string, string>>;
|
|
8
13
|
export declare const ENTRYPOINT_NAMES: Set<string>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, globSync, readdirSync, readFileSync, realpathSync, statSync, } from "node:fs";
|
|
3
3
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { NON_PRODUCT_DIRS } from "../fs/non-product-dirs.js";
|
|
4
5
|
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
5
6
|
import { extractCodeStructure, loadJsonFile } from "../verify-source/code-structure-validate.js";
|
|
6
7
|
import { sortedStringifyPretty } from "./json.js";
|
|
@@ -10,21 +11,12 @@ export const DEFAULT_PROVIDER_VERSION = "0.1";
|
|
|
10
11
|
export const MAX_IMPORT_SCAN_BYTES = 262_144;
|
|
11
12
|
export const MAX_FILES_PER_MODULE = 100;
|
|
12
13
|
export const MAX_EVIDENCE_PER_EDGE = 5;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"__pycache__",
|
|
20
|
-
"build",
|
|
21
|
-
"dist",
|
|
22
|
-
"node_modules",
|
|
23
|
-
// Swarm / agent worktrees are not source modules (#2953, #1656).
|
|
24
|
-
".deft-scratch",
|
|
25
|
-
// Legacy swarm worktree root name (pre-.deft-scratch layout).
|
|
26
|
-
"swarm-worktrees",
|
|
27
|
-
]);
|
|
14
|
+
/**
|
|
15
|
+
* Shared "not product source" core (#3487) plus `build`, which only the
|
|
16
|
+
* codebase extractor treats as non-source. Agent worktrees are not source
|
|
17
|
+
* modules (#2953, #1656).
|
|
18
|
+
*/
|
|
19
|
+
export const SKIP_DIRS = new Set([...NON_PRODUCT_DIRS, "build"]);
|
|
28
20
|
export const LANGUAGE_BY_SUFFIX = {
|
|
29
21
|
".go": "Go",
|
|
30
22
|
".js": "JavaScript",
|
|
@@ -14,6 +14,8 @@ import { constants } from "node:fs";
|
|
|
14
14
|
import { lstat, mkdir, mkdtemp, open, readdir, readFile, rename, rm, stat } from "node:fs/promises";
|
|
15
15
|
import { tmpdir } from "node:os";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
|
+
import { containedRemove, containedWrite } from "../fs/contained-write.js";
|
|
18
|
+
import { isPortRecordMode, recordActiveMutation } from "../fs/mutation-ledger.js";
|
|
17
19
|
const DEFAULT_FILE_MODE = 0o644;
|
|
18
20
|
const DEFAULT_DIR_MODE = 0o755;
|
|
19
21
|
async function assertDestinationIsNotSymlink(path, label = "copyTree") {
|
|
@@ -103,6 +105,51 @@ async function pathExists(path) {
|
|
|
103
105
|
throw err;
|
|
104
106
|
}
|
|
105
107
|
}
|
|
108
|
+
/** Posix-relative file and symlink paths under `root` (directories are walked). */
|
|
109
|
+
async function listRelativeFilePaths(root) {
|
|
110
|
+
const out = [];
|
|
111
|
+
async function walk(dir, rel) {
|
|
112
|
+
let entries;
|
|
113
|
+
try {
|
|
114
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const entry of entries) {
|
|
120
|
+
const childRel = rel.length > 0 ? `${rel}/${entry.name}` : entry.name;
|
|
121
|
+
if (entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
122
|
+
await walk(join(dir, entry.name), childRel);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
out.push(childRel.replace(/\\/g, "/"));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
await walk(root, "");
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
async function destOnlyRelativeFiles(src, dst) {
|
|
133
|
+
const srcSet = new Set(await listRelativeFilePaths(src));
|
|
134
|
+
return (await listRelativeFilePaths(dst)).filter((rel) => !srcSet.has(rel));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Ledger dest-only deletes and dest writes after a successful swap (#3392).
|
|
138
|
+
* Observational: dest is already live. Failures must not reject replaceTree.
|
|
139
|
+
*/
|
|
140
|
+
async function ledgerReplacedTree(dst, destOnly) {
|
|
141
|
+
try {
|
|
142
|
+
for (const rel of destOnly) {
|
|
143
|
+
recordActiveMutation("deleted", join(dst, ...rel.split("/")));
|
|
144
|
+
}
|
|
145
|
+
for (const rel of await listRelativeFilePaths(dst)) {
|
|
146
|
+
recordActiveMutation("wrote", join(dst, ...rel.split("/")));
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// Ledger is observational; dest is already live.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
106
153
|
/**
|
|
107
154
|
* Move `src` to `dst`, falling back to copy+remove across devices (EXDEV),
|
|
108
155
|
* mirroring Go `movePayload` used by `swapInCore`.
|
|
@@ -143,15 +190,27 @@ export async function replaceTree(src, dst) {
|
|
|
143
190
|
throw new Error(`replaceTree: source ${src} is not a directory`);
|
|
144
191
|
}
|
|
145
192
|
await assertDestinationIsNotSymlink(dst, "replaceTree");
|
|
193
|
+
if (isPortRecordMode()) {
|
|
194
|
+
const destOnly = (await pathExists(dst)) ? await destOnlyRelativeFiles(src, dst) : [];
|
|
195
|
+
for (const rel of destOnly) {
|
|
196
|
+
containedRemove({ root: dst, target: rel });
|
|
197
|
+
}
|
|
198
|
+
for (const rel of await listRelativeFilePaths(src)) {
|
|
199
|
+
containedWrite({ root: dst, target: rel, data: "", mode: "replace" });
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
146
203
|
const parent = dirname(dst);
|
|
147
204
|
await mkdir(parent, { recursive: true, mode: DEFAULT_DIR_MODE });
|
|
148
205
|
const staging = await mkdtemp(join(tmpdir(), "deft-core-stage-"));
|
|
149
206
|
let backup = null;
|
|
150
207
|
/** When true, leave `backup` on disk so an operator can recover after dual failure. */
|
|
151
208
|
let preserveBackupOnExit = false;
|
|
209
|
+
let destOnly = [];
|
|
152
210
|
try {
|
|
153
211
|
await copyDirContents(src, staging);
|
|
154
212
|
if (await pathExists(dst)) {
|
|
213
|
+
destOnly = await destOnlyRelativeFiles(src, dst);
|
|
155
214
|
backup = await mkdtemp(join(tmpdir(), "deft-core-bak-"));
|
|
156
215
|
// mkdtemp created an empty dir; remove it so moveTree can rename onto the path.
|
|
157
216
|
await rm(backup, { recursive: true, force: true });
|
|
@@ -206,6 +265,7 @@ export async function replaceTree(src, dst) {
|
|
|
206
265
|
await rm(backup, { recursive: true, force: true }).catch(() => undefined);
|
|
207
266
|
backup = null;
|
|
208
267
|
}
|
|
268
|
+
await ledgerReplacedTree(dst, destOnly);
|
|
209
269
|
}
|
|
210
270
|
finally {
|
|
211
271
|
await rm(staging, { recursive: true, force: true }).catch(() => undefined);
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* `scripts/` tree, `.py` files, and Python `run` shims.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
9
|
-
import { readdir, readFile,
|
|
9
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
10
10
|
import { join, relative } from "node:path";
|
|
11
|
+
import { containedRemove } from "../fs/contained-write.js";
|
|
11
12
|
const PY_SUFFIX = ".py";
|
|
12
13
|
const PYC_SUFFIX = ".pyc";
|
|
13
14
|
function isPythonRunShim(_path, head) {
|
|
@@ -81,19 +82,24 @@ export function isRepoRootPythonRunShim(projectDir) {
|
|
|
81
82
|
return true;
|
|
82
83
|
}
|
|
83
84
|
}
|
|
85
|
+
function removeContained(projectDir, target, recursive = false) {
|
|
86
|
+
return containedRemove({ root: projectDir, target, recursive }).removed;
|
|
87
|
+
}
|
|
84
88
|
/** Remove Python-only trees/files from the consumer deposit and repo-root run shim. */
|
|
85
89
|
export async function prunePythonArtifactsFromDeposit(depositDir, projectDir, io) {
|
|
86
90
|
let removed = 0;
|
|
87
91
|
const scriptsDir = join(depositDir, "scripts");
|
|
88
92
|
try {
|
|
89
93
|
if ((await stat(scriptsDir)).isDirectory()) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
94
|
+
if (removeContained(projectDir, scriptsDir, true)) {
|
|
95
|
+
removed += 1;
|
|
96
|
+
io?.printf("Removed Python scripts/ tree from the consumer deposit (#2022 Phase 3).\n");
|
|
97
|
+
}
|
|
93
98
|
}
|
|
94
99
|
}
|
|
95
|
-
catch {
|
|
96
|
-
|
|
100
|
+
catch (err) {
|
|
101
|
+
if (err.code !== "ENOENT")
|
|
102
|
+
throw err;
|
|
97
103
|
}
|
|
98
104
|
async function walkRemovePy(root) {
|
|
99
105
|
let entries;
|
|
@@ -107,8 +113,8 @@ export async function prunePythonArtifactsFromDeposit(depositDir, projectDir, io
|
|
|
107
113
|
const full = join(root, entry.name);
|
|
108
114
|
if (entry.isDirectory()) {
|
|
109
115
|
if (entry.name === "__pycache__") {
|
|
110
|
-
|
|
111
|
-
|
|
116
|
+
if (removeContained(projectDir, full, true))
|
|
117
|
+
removed += 1;
|
|
112
118
|
continue;
|
|
113
119
|
}
|
|
114
120
|
await walkRemovePy(full);
|
|
@@ -117,8 +123,8 @@ export async function prunePythonArtifactsFromDeposit(depositDir, projectDir, io
|
|
|
117
123
|
if (!entry.isFile())
|
|
118
124
|
continue;
|
|
119
125
|
if (entry.name.endsWith(PY_SUFFIX) || entry.name.endsWith(PYC_SUFFIX)) {
|
|
120
|
-
|
|
121
|
-
|
|
126
|
+
if (removeContained(projectDir, full))
|
|
127
|
+
removed += 1;
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
}
|
|
@@ -128,21 +134,23 @@ export async function prunePythonArtifactsFromDeposit(depositDir, projectDir, io
|
|
|
128
134
|
try {
|
|
129
135
|
const head = await readFile(depositRun, "utf8");
|
|
130
136
|
if (isPythonRunShim(depositRun, head.slice(0, 200))) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
137
|
+
if (removeContained(projectDir, depositRun)) {
|
|
138
|
+
removed += 1;
|
|
139
|
+
io?.printf("Removed Python run shim from .deft/core (#2022 Phase 3).\n");
|
|
140
|
+
}
|
|
134
141
|
}
|
|
135
142
|
}
|
|
136
143
|
catch {
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
if (removeContained(projectDir, depositRun))
|
|
145
|
+
removed += 1;
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
const projectRun = join(projectDir, "run");
|
|
142
149
|
if (isRepoRootPythonRunShim(projectDir)) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
if (removeContained(projectDir, projectRun)) {
|
|
151
|
+
removed += 1;
|
|
152
|
+
io?.printf("Removed repo-root Python run shim (#2022 Phase 3).\n");
|
|
153
|
+
}
|
|
146
154
|
}
|
|
147
155
|
return removed;
|
|
148
156
|
}
|
package/dist/doctor/checks.d.ts
CHANGED
|
@@ -4,6 +4,14 @@ import type { CheckResult } from "./types.js";
|
|
|
4
4
|
export declare const XBRIEF_ENVELOPE_MIGRATE_COMMAND: "deft migrate:xbrief";
|
|
5
5
|
/** Doctor check name for envelope major mismatch (Q5 Option 2, #3243). */
|
|
6
6
|
export declare const XBRIEF_ENVELOPE_MAJOR_CHECK: "xbrief-envelope-version";
|
|
7
|
+
/**
|
|
8
|
+
* One SoT for advisory (exit-exempt) check fails (#3379).
|
|
9
|
+
* Union of the previous `deriveExitCode` / `cmdDoctor` name lists.
|
|
10
|
+
* A fail is advisory when `data.advisory === true` or the name is in this set.
|
|
11
|
+
*/
|
|
12
|
+
export declare const DOCTOR_ADVISORY_FAIL_CHECKS: ReadonlySet<string>;
|
|
13
|
+
/** True when a check fail must stay a warning (not lastErrorCount / exit 1). */
|
|
14
|
+
export declare function isDoctorAdvisoryFail(name: string, data?: Readonly<Record<string, unknown>> | null): boolean;
|
|
7
15
|
export interface CheckSeams {
|
|
8
16
|
readonly readText?: (path: string) => string | null;
|
|
9
17
|
readonly isFile?: (path: string) => boolean;
|
package/dist/doctor/checks.js
CHANGED
|
@@ -21,6 +21,28 @@ import { readTextSafe } from "./paths.js";
|
|
|
21
21
|
export const XBRIEF_ENVELOPE_MIGRATE_COMMAND = "deft migrate:xbrief";
|
|
22
22
|
/** Doctor check name for envelope major mismatch (Q5 Option 2, #3243). */
|
|
23
23
|
export const XBRIEF_ENVELOPE_MAJOR_CHECK = "xbrief-envelope-version";
|
|
24
|
+
/**
|
|
25
|
+
* One SoT for advisory (exit-exempt) check fails (#3379).
|
|
26
|
+
* Union of the previous `deriveExitCode` / `cmdDoctor` name lists.
|
|
27
|
+
* A fail is advisory when `data.advisory === true` or the name is in this set.
|
|
28
|
+
*/
|
|
29
|
+
export const DOCTOR_ADVISORY_FAIL_CHECKS = new Set([
|
|
30
|
+
"legacy-layout",
|
|
31
|
+
"canonical-vendored-npm-signpost",
|
|
32
|
+
"manifest-version-reportable",
|
|
33
|
+
"gitignore-coverage",
|
|
34
|
+
"stale-xbrief-schema-deposit",
|
|
35
|
+
"typescript-7-side-by-side",
|
|
36
|
+
"completed-open-items",
|
|
37
|
+
"coverage-check-resume-policy",
|
|
38
|
+
]);
|
|
39
|
+
/** True when a check fail must stay a warning (not lastErrorCount / exit 1). */
|
|
40
|
+
export function isDoctorAdvisoryFail(name, data) {
|
|
41
|
+
return data?.advisory === true || DOCTOR_ADVISORY_FAIL_CHECKS.has(name);
|
|
42
|
+
}
|
|
43
|
+
function stampAdvisory(data) {
|
|
44
|
+
return { ...data, advisory: true };
|
|
45
|
+
}
|
|
24
46
|
/** True when an fs error means the path is cleanly absent (not unreadable). */
|
|
25
47
|
function isEnoentError(err) {
|
|
26
48
|
return (typeof err === "object" &&
|
|
@@ -650,13 +672,13 @@ export function checkLegacyLayout(projectRoot, seams = {}) {
|
|
|
650
672
|
name: "legacy-layout",
|
|
651
673
|
status: "fail",
|
|
652
674
|
detail: dualLayoutSignpostLine(dualLayout),
|
|
653
|
-
data: {
|
|
675
|
+
data: stampAdvisory({
|
|
654
676
|
legacy_layout: true,
|
|
655
677
|
legacy_layout_kind: dualLayout.kind,
|
|
656
678
|
evidence: [...dualLayout.evidence],
|
|
657
679
|
upgrading_doc_url: UPGRADING_DOC_URL,
|
|
658
680
|
go_bridge_releases_url: GO_BRIDGE_RELEASES_URL,
|
|
659
|
-
},
|
|
681
|
+
}),
|
|
660
682
|
};
|
|
661
683
|
}
|
|
662
684
|
const detection = detectLegacyLayout(projectRoot, legacySeams);
|
|
@@ -672,13 +694,13 @@ export function checkLegacyLayout(projectRoot, seams = {}) {
|
|
|
672
694
|
name: "legacy-layout",
|
|
673
695
|
status: "fail",
|
|
674
696
|
detail: legacyLayoutSignpostLine(detection),
|
|
675
|
-
data: {
|
|
697
|
+
data: stampAdvisory({
|
|
676
698
|
legacy_layout: true,
|
|
677
699
|
legacy_layout_kind: detection.kind,
|
|
678
700
|
evidence: [...detection.evidence],
|
|
679
701
|
upgrading_doc_url: UPGRADING_DOC_URL,
|
|
680
702
|
go_bridge_releases_url: GO_BRIDGE_RELEASES_URL,
|
|
681
|
-
},
|
|
703
|
+
}),
|
|
682
704
|
};
|
|
683
705
|
}
|
|
684
706
|
/**
|
|
@@ -723,14 +745,14 @@ export function checkCanonicalVendoredNpmSignpost(projectRoot, seams = {}) {
|
|
|
723
745
|
name: "canonical-vendored-npm-signpost",
|
|
724
746
|
status: "fail",
|
|
725
747
|
detail,
|
|
726
|
-
data: {
|
|
748
|
+
data: stampAdvisory({
|
|
727
749
|
canonical_vendored: true,
|
|
728
750
|
npm_managed: false,
|
|
729
751
|
manifest_path: manifestPath,
|
|
730
752
|
sentinel_key: NPM_MANAGED_SENTINEL_KEY,
|
|
731
753
|
sentinel_value: NPM_MANAGED_SENTINEL_VALUE,
|
|
732
754
|
upgrading_doc_url: UPGRADING_DOC_URL,
|
|
733
|
-
},
|
|
755
|
+
}),
|
|
734
756
|
};
|
|
735
757
|
}
|
|
736
758
|
/**
|
|
@@ -786,14 +808,14 @@ export function checkManifestVersionReportable(projectRoot, installRoot, seams =
|
|
|
786
808
|
"so the framework version is unreportable. This happens on legacy `deft-install` deposits " +
|
|
787
809
|
`made without a release pin (#2294). Run \`${CANONICAL_UPGRADE_COMMAND}\` then \`directive update\` ` +
|
|
788
810
|
`to obtain a pinned npm-managed manifest. See ${UPGRADING_DOC_URL}.`,
|
|
789
|
-
data: {
|
|
811
|
+
data: stampAdvisory({
|
|
790
812
|
manifest_path: manifestPath,
|
|
791
813
|
version: null,
|
|
792
814
|
source: reportable.source,
|
|
793
815
|
sha: reportable.sha,
|
|
794
816
|
upgrade_command: CANONICAL_UPGRADE_COMMAND,
|
|
795
817
|
upgrading_doc_url: UPGRADING_DOC_URL,
|
|
796
|
-
},
|
|
818
|
+
}),
|
|
797
819
|
};
|
|
798
820
|
}
|
|
799
821
|
return {
|
|
@@ -875,11 +897,11 @@ export function checkStaleXbriefSchemaDeposit(projectRoot, seams = {}) {
|
|
|
875
897
|
status: "fail",
|
|
876
898
|
detail: `Stale deposited schema at ${STALE_VBRIEF_CORE_SCHEMA_REL} still carries the legacy ${LEGACY_INFO_ROOT_KEY} root key on an already-migrated xbrief/ layout. ` +
|
|
877
899
|
"Run `directive update` to refresh deposited schema files — not `deft migrate:xbrief` (no vbrief/ tree remains to convert).",
|
|
878
|
-
data: {
|
|
900
|
+
data: stampAdvisory({
|
|
879
901
|
schema_path: schemaPath,
|
|
880
902
|
convergence_state: convergence.state,
|
|
881
903
|
suggested_fix: "directive update",
|
|
882
|
-
},
|
|
904
|
+
}),
|
|
883
905
|
};
|
|
884
906
|
}
|
|
885
907
|
const TS7_SIDE_BY_SIDE_CHECK = "typescript-7-side-by-side";
|
|
@@ -1011,11 +1033,11 @@ export function checkTypescript7SideBySide(projectRoot, seams = {}) {
|
|
|
1011
1033
|
detail: "typescript resolves to 7.x without the @typescript/typescript6 alias while typescript-eslint and eslint are present. " +
|
|
1012
1034
|
"Until TS 7.1, use the side-by-side alias pattern in languages/typescript.md § TypeScript 7 side-by-side (pre-7.1) " +
|
|
1013
1035
|
"(Cartograph #111: keep typescript on npm:@typescript/typescript6 and install TS 7 under @typescript/native).",
|
|
1014
|
-
data: {
|
|
1036
|
+
data: stampAdvisory({
|
|
1015
1037
|
package_json_path: packageJsonPath,
|
|
1016
1038
|
typescript: typescriptValue,
|
|
1017
1039
|
suggested_fix: "languages/typescript.md#typescript-7-side-by-side-pre-71",
|
|
1018
|
-
},
|
|
1040
|
+
}),
|
|
1019
1041
|
};
|
|
1020
1042
|
}
|
|
1021
1043
|
/**
|
|
@@ -1062,11 +1084,11 @@ export function checkGitignoreCoverage(projectRoot, seams = {}) {
|
|
|
1062
1084
|
`missing (${missing.slice(0, 3).join(", ")}${missing.length > 3 ? ", …" : ""}). ` +
|
|
1063
1085
|
"Run `directive update` to add the missing entries (idempotent, safe on re-run). " +
|
|
1064
1086
|
`Full missing list: ${JSON.stringify(missing)}.`,
|
|
1065
|
-
data: {
|
|
1087
|
+
data: stampAdvisory({
|
|
1066
1088
|
gitignore_path: gitignorePath,
|
|
1067
1089
|
missing,
|
|
1068
1090
|
suggested_fix: "directive update",
|
|
1069
|
-
},
|
|
1091
|
+
}),
|
|
1070
1092
|
};
|
|
1071
1093
|
}
|
|
1072
1094
|
/**
|
|
@@ -1181,20 +1203,10 @@ export function checkCoverageCheckResumePolicy(projectRoot) {
|
|
|
1181
1203
|
};
|
|
1182
1204
|
}
|
|
1183
1205
|
export function deriveExitCode(checks, errors) {
|
|
1184
|
-
const exitExempt = new Set([
|
|
1185
|
-
"canonical-vendored-npm-signpost",
|
|
1186
|
-
"manifest-version-reportable",
|
|
1187
|
-
"gitignore-coverage",
|
|
1188
|
-
"stale-xbrief-schema-deposit",
|
|
1189
|
-
"typescript-7-side-by-side",
|
|
1190
|
-
"coverage-check-resume-policy",
|
|
1191
|
-
// Historical pre-#2862 completed/ corpora; hard fail is scope:complete (#3242).
|
|
1192
|
-
"completed-open-items",
|
|
1193
|
-
]);
|
|
1194
1206
|
if (errors.length > 0 || checks.some((c) => c.status === "error")) {
|
|
1195
1207
|
return 2;
|
|
1196
1208
|
}
|
|
1197
|
-
if (checks.some((c) => c.status === "fail" && !
|
|
1209
|
+
if (checks.some((c) => c.status === "fail" && !isDoctorAdvisoryFail(c.name, c.data))) {
|
|
1198
1210
|
return 1;
|
|
1199
1211
|
}
|
|
1200
1212
|
return 0;
|
|
@@ -11,6 +11,10 @@ export declare function writeState(projectRoot: string, payload: {
|
|
|
11
11
|
export declare function decideThrottle(state: DoctorState | null, now?: Date): ThrottleDecision;
|
|
12
12
|
export declare function formatUtcIso(when: Date): string;
|
|
13
13
|
export declare function formatIsoZ(when: Date | null): string;
|
|
14
|
+
/** Dirty doctor / ritual copy: --full re-probes only; do not prescribe a write (#3379). */
|
|
15
|
+
export declare const DOCTOR_HARD_ERROR_NOTE = "these are hard errors (advisory warnings do not block writes)";
|
|
16
|
+
export declare const DOCTOR_DIRTY_REPROBE_HINT = "run `deft doctor --full` to re-probe only";
|
|
17
|
+
export declare function dirtyDoctorHint(): string;
|
|
14
18
|
export declare function renderDoctorStatusLine(decision: ThrottleDecision, now?: Date): string;
|
|
15
19
|
export {};
|
|
16
20
|
//# sourceMappingURL=doctor-state.d.ts.map
|
|
@@ -126,6 +126,12 @@ export function formatIsoZ(when) {
|
|
|
126
126
|
}
|
|
127
127
|
return formatUtcIso(when);
|
|
128
128
|
}
|
|
129
|
+
/** Dirty doctor / ritual copy: --full re-probes only; do not prescribe a write (#3379). */
|
|
130
|
+
export const DOCTOR_HARD_ERROR_NOTE = "these are hard errors (advisory warnings do not block writes)";
|
|
131
|
+
export const DOCTOR_DIRTY_REPROBE_HINT = "run `deft doctor --full` to re-probe only";
|
|
132
|
+
export function dirtyDoctorHint() {
|
|
133
|
+
return `${DOCTOR_HARD_ERROR_NOTE}; ${DOCTOR_DIRTY_REPROBE_HINT}`;
|
|
134
|
+
}
|
|
129
135
|
export function renderDoctorStatusLine(decision, now = new Date()) {
|
|
130
136
|
const ageH = Math.max(Math.floor(decision.ageHours), 0);
|
|
131
137
|
if (decision.dirty) {
|
|
@@ -133,7 +139,7 @@ export function renderDoctorStatusLine(decision, now = new Date()) {
|
|
|
133
139
|
const warns = Math.max(decision.lastFindingCount - decision.lastErrorCount, 0);
|
|
134
140
|
const errPhrase = `${errs} error${errs !== 1 ? "s" : ""}`;
|
|
135
141
|
const warnPhrase = `${warns} warning${warns !== 1 ? "s" : ""}`;
|
|
136
|
-
return `[doctor] ran ${ageH}h ago, ${errPhrase} / ${warnPhrase} -- UNRESOLVED;
|
|
142
|
+
return `[doctor] ran ${ageH}h ago, ${errPhrase} / ${warnPhrase} -- UNRESOLVED; ${DOCTOR_HARD_ERROR_NOTE}. ${DOCTOR_DIRTY_REPROBE_HINT}.`;
|
|
137
143
|
}
|
|
138
144
|
const remainingMs = (decision.nextEligibleAt?.getTime() ?? now.getTime()) - now.getTime();
|
|
139
145
|
const remainingH = Math.max(Math.floor(remainingMs / 3_600_000), 0);
|
package/dist/doctor/main.js
CHANGED
|
@@ -12,9 +12,9 @@ import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
|
12
12
|
import { evaluateAgentHooks } from "../verify-env/agent-hooks.js";
|
|
13
13
|
import { probeAgentHooksLive } from "../verify-env/agent-hooks-live-probe.js";
|
|
14
14
|
import { agentsRefreshPlan, hasV3ManagedMarker } from "./agents-md.js";
|
|
15
|
-
import { checkXbriefEnvelopeMajorVersion, runChecks, XBRIEF_ENVELOPE_MAJOR_CHECK, XBRIEF_ENVELOPE_MIGRATE_COMMAND, } from "./checks.js";
|
|
15
|
+
import { checkXbriefEnvelopeMajorVersion, DOCTOR_ADVISORY_FAIL_CHECKS, runChecks, XBRIEF_ENVELOPE_MAJOR_CHECK, XBRIEF_ENVELOPE_MIGRATE_COMMAND, } from "./checks.js";
|
|
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
|
-
import { decideThrottle, formatIsoZ, readState, renderDoctorStatusLine, writeState, } from "./doctor-state.js";
|
|
17
|
+
import { decideThrottle, dirtyDoctorHint, formatIsoZ, readState, renderDoctorStatusLine, writeState, } from "./doctor-state.js";
|
|
18
18
|
import { formatAllowedFlagsHint, formatUnknownFlagsError, parseDoctorFlags } from "./flags.js";
|
|
19
19
|
import { formatDoctorHelp } from "./help.js";
|
|
20
20
|
import { pythonJsonDump } from "./json.js";
|
|
@@ -166,9 +166,7 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
166
166
|
runLocalSignpostChecks(projectRoot, throttleSink, (finding) => {
|
|
167
167
|
throttleFindings.push(finding);
|
|
168
168
|
}, seams);
|
|
169
|
-
const hint = decision.dirty
|
|
170
|
-
? "run `deft doctor --full` to re-probe or address findings"
|
|
171
|
-
: "--full forces";
|
|
169
|
+
const hint = decision.dirty ? dirtyDoctorHint() : "--full forces";
|
|
172
170
|
if (jsonMode) {
|
|
173
171
|
const payload = {
|
|
174
172
|
status: "throttle-skipped",
|
|
@@ -700,22 +698,14 @@ function runInstallIntegrityChecks(projectRoot, sink, addFinding, seams) {
|
|
|
700
698
|
return null;
|
|
701
699
|
}
|
|
702
700
|
});
|
|
703
|
-
const result = runChecks(projectRoot, { isDir, isFile, readText });
|
|
704
|
-
//
|
|
705
|
-
// (gated ritual / PreToolUse).
|
|
706
|
-
const warningOnFail = new Set([
|
|
707
|
-
"legacy-layout",
|
|
708
|
-
"canonical-vendored-npm-signpost",
|
|
709
|
-
"manifest-version-reportable",
|
|
710
|
-
"gitignore-coverage",
|
|
711
|
-
"stale-xbrief-schema-deposit",
|
|
712
|
-
"typescript-7-side-by-side",
|
|
713
|
-
"completed-open-items",
|
|
714
|
-
]);
|
|
701
|
+
const result = (seams.runChecks ?? runChecks)(projectRoot, { isDir, isFile, readText });
|
|
702
|
+
// Advisory fails stay warnings so lastErrorCount stays 0
|
|
703
|
+
// (gated ritual / PreToolUse). Hard stays hard: completed-lifecycle-consistency.
|
|
715
704
|
for (const entry of result.checks ?? []) {
|
|
716
705
|
const name = String(entry.name ?? "install-integrity");
|
|
717
706
|
const status = String(entry.status ?? "");
|
|
718
707
|
const detail = String(entry.detail ?? "");
|
|
708
|
+
const data = entry.data ?? {};
|
|
719
709
|
// Authoritative envelope-major emission is runXbriefEnvelopeVersionCheck
|
|
720
710
|
// (runs for framework repo + consumers). Skip install-integrity duplicate.
|
|
721
711
|
if (name === XBRIEF_ENVELOPE_MAJOR_CHECK) {
|
|
@@ -729,7 +719,7 @@ function runInstallIntegrityChecks(projectRoot, sink, addFinding, seams) {
|
|
|
729
719
|
sink.info(`${name}: skip -- ${detail}`);
|
|
730
720
|
continue;
|
|
731
721
|
}
|
|
732
|
-
if (
|
|
722
|
+
if (status === "fail" && (data.advisory === true || DOCTOR_ADVISORY_FAIL_CHECKS.has(name))) {
|
|
733
723
|
sink.warn(`${name}: ${detail}`);
|
|
734
724
|
addFinding({
|
|
735
725
|
severity: "warning",
|
|
@@ -737,7 +727,7 @@ function runInstallIntegrityChecks(projectRoot, sink, addFinding, seams) {
|
|
|
737
727
|
check: `install-integrity:${name}`,
|
|
738
728
|
install_check: name,
|
|
739
729
|
status,
|
|
740
|
-
data
|
|
730
|
+
data,
|
|
741
731
|
});
|
|
742
732
|
continue;
|
|
743
733
|
}
|
package/dist/doctor/types.d.ts
CHANGED
|
@@ -166,6 +166,15 @@ export interface DoctorSeams {
|
|
|
166
166
|
* its `plan` object; returns [] when no project definition is present.
|
|
167
167
|
*/
|
|
168
168
|
readonly detectPlanExtensionShadows?: (projectRoot: string) => readonly ShadowedPlanExtension[];
|
|
169
|
+
/**
|
|
170
|
+
* Override install-integrity `runChecks` for tests (#3379). Default is
|
|
171
|
+
* `runChecks` from `./checks.js`.
|
|
172
|
+
*/
|
|
173
|
+
readonly runChecks?: (projectRoot: string, seams?: {
|
|
174
|
+
readonly readText?: (path: string) => string | null;
|
|
175
|
+
readonly isFile?: (path: string) => boolean;
|
|
176
|
+
readonly isDir?: (path: string) => boolean;
|
|
177
|
+
}) => Record<string, unknown>;
|
|
169
178
|
/** Read-only agent-host hook registration probe (#2438). */
|
|
170
179
|
readonly evaluateAgentHooks?: (projectRoot: string) => AgentHookHealthResult;
|
|
171
180
|
/** Live hook spawn probe for doctor --full (#2852). */
|