@deftai/directive-core 0.88.0 → 0.89.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/doctor-state.js +28 -4
- package/dist/doctor/main.d.ts +10 -0
- package/dist/doctor/main.js +160 -0
- package/dist/doctor/types.d.ts +11 -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 +212 -163
- 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 +4 -0
- package/dist/index.js +5 -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 +29 -20
- package/dist/lifecycle/lifecycle-hygiene.js +9 -3
- 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-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/release-availability.js +26 -6
- package/dist/session/ritual-sentinel.js +19 -12
- package/dist/session/session-start.js +7 -0
- 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/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 +15 -3
package/dist/branch/evaluate.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { recordBypassSignal, recordGateCatch } from "../events/attribution-ledger.js";
|
|
2
2
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
3
3
|
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
4
|
+
import { humanMergeBranchNote, resolveHumanMergePolicy } from "../policy/require-human-merge.js";
|
|
4
5
|
import { ENV_BYPASS, resolvePolicy } from "../policy/resolve.js";
|
|
5
6
|
import { currentBranch, GitNotFoundError } from "./git.js";
|
|
6
7
|
export const DEFAULT_BRANCHES = new Set(["master", "main"]);
|
|
@@ -93,9 +94,13 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
93
94
|
};
|
|
94
95
|
}
|
|
95
96
|
if (!isDefaultBranch(branch, defaultBranches)) {
|
|
97
|
+
// Surface (2) human-merge gate: advisory note on feature branches (#1193).
|
|
98
|
+
const hm = resolveHumanMergePolicy(projectRoot);
|
|
99
|
+
const hmNote = humanMergeBranchNote(hm);
|
|
100
|
+
const base = `✓ deft branch-protection: feature branch '${branch}' -- proceeding.`;
|
|
96
101
|
return {
|
|
97
102
|
exitCode: 0,
|
|
98
|
-
message:
|
|
103
|
+
message: hmNote !== null ? `${base}\n${hmNote}` : base,
|
|
99
104
|
};
|
|
100
105
|
}
|
|
101
106
|
const result = resolvePolicy(projectRoot);
|
package/dist/cache/fetch.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { ContainedWriteError, containedWrite } from "../fs/contained-write.js";
|
|
4
4
|
import { GhRestError, InvalidRepoError, restIssueView, runGhApi, } from "../scm/gh-rest.js";
|
|
5
5
|
import { DEFAULT_BATCH_SIZE, DEFAULT_DELAY_MS } from "./constants.js";
|
|
6
6
|
import { CacheError, CacheFetchError } from "./errors.js";
|
|
@@ -622,8 +622,13 @@ function readSelfHealState(cacheRoot) {
|
|
|
622
622
|
}
|
|
623
623
|
function writeSelfHealState(projectRoot, cacheRoot, when) {
|
|
624
624
|
const path = join(resolve(cacheRoot), SELF_HEAL_STATE_FILENAME);
|
|
625
|
-
|
|
626
|
-
|
|
625
|
+
// #2980 wave C: product write sink routes through containedWrite.
|
|
626
|
+
containedWrite({
|
|
627
|
+
root: resolve(projectRoot),
|
|
628
|
+
target: path,
|
|
629
|
+
data: `${JSON.stringify({ last_reconcile_at: when.toISOString() })}\n`,
|
|
630
|
+
mode: "replace",
|
|
631
|
+
});
|
|
627
632
|
}
|
|
628
633
|
/** TTL-bounded closed-reconcile for session ritual / triage:welcome self-healing (#1886). */
|
|
629
634
|
export function maybeSelfHealCache(projectRoot, options = {}) {
|
|
@@ -679,7 +684,7 @@ export function maybeSelfHealCache(projectRoot, options = {}) {
|
|
|
679
684
|
writeSelfHealState(projectRoot, cacheRoot, now);
|
|
680
685
|
}
|
|
681
686
|
catch (err) {
|
|
682
|
-
if (err instanceof
|
|
687
|
+
if (err instanceof ContainedWriteError) {
|
|
683
688
|
return { skipped: true, skipReason: "containment-refused", drift, refresh: null };
|
|
684
689
|
}
|
|
685
690
|
throw err;
|
package/dist/cache/io.d.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import { tmpdir } from "node:os";
|
|
2
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* Write text via tempfile + rename (mirrors Python `_atomic_write_text`).
|
|
4
|
+
* #2951 Phase 2: temp payload write uses containedWrite under the parent dir.
|
|
5
|
+
*/
|
|
3
6
|
export declare function atomicWriteText(path: string, text: string): void;
|
|
4
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Append one JSON audit record (mirrors `_append_audit`).
|
|
9
|
+
* #2951 Phase 2: product write sink routes through containedWrite.
|
|
10
|
+
* Create cacheRoot first — containedWrite requires the containment root to exist.
|
|
11
|
+
*/
|
|
5
12
|
export declare function appendAudit(record: Record<string, unknown>, cacheRoot: string): void;
|
|
6
13
|
/** Touch mtime for LRU signal; failures swallowed. */
|
|
7
14
|
export declare function touchMtime(path: string): void;
|
package/dist/cache/io.js
CHANGED
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { mkdirSync, renameSync, rmSync, statSync, utimesSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
5
6
|
import { pythonJsonLine } from "./json.js";
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Write text via tempfile + rename (mirrors Python `_atomic_write_text`).
|
|
9
|
+
* #2951 Phase 2: temp payload write uses containedWrite under the parent dir.
|
|
10
|
+
*/
|
|
7
11
|
export function atomicWriteText(path, text) {
|
|
8
|
-
|
|
9
|
-
|
|
12
|
+
const dir = dirname(path);
|
|
13
|
+
mkdirSync(dir, { recursive: true });
|
|
14
|
+
const tmpName = `${basename(path)}.${randomBytes(4).toString("hex")}.tmp`;
|
|
15
|
+
const tmp = join(dir, tmpName);
|
|
10
16
|
try {
|
|
11
|
-
|
|
17
|
+
containedWrite({
|
|
18
|
+
root: resolve(dir),
|
|
19
|
+
target: tmpName,
|
|
20
|
+
data: text,
|
|
21
|
+
mode: "create",
|
|
22
|
+
});
|
|
12
23
|
renameSync(tmp, path);
|
|
13
24
|
}
|
|
14
25
|
catch (err) {
|
|
@@ -21,11 +32,20 @@ export function atomicWriteText(path, text) {
|
|
|
21
32
|
throw err;
|
|
22
33
|
}
|
|
23
34
|
}
|
|
24
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* Append one JSON audit record (mirrors `_append_audit`).
|
|
37
|
+
* #2951 Phase 2: product write sink routes through containedWrite.
|
|
38
|
+
* Create cacheRoot first — containedWrite requires the containment root to exist.
|
|
39
|
+
*/
|
|
25
40
|
export function appendAudit(record, cacheRoot) {
|
|
26
|
-
const
|
|
27
|
-
mkdirSync(
|
|
28
|
-
|
|
41
|
+
const root = resolve(cacheRoot);
|
|
42
|
+
mkdirSync(root, { recursive: true });
|
|
43
|
+
containedWrite({
|
|
44
|
+
root,
|
|
45
|
+
target: "quarantine-audit.jsonl",
|
|
46
|
+
data: `${pythonJsonLine(record)}\n`,
|
|
47
|
+
mode: "append",
|
|
48
|
+
});
|
|
29
49
|
}
|
|
30
50
|
/** Touch mtime for LRU signal; failures swallowed. */
|
|
31
51
|
export function touchMtime(path) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync, rmSync } from "node:fs";
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
|
+
import { containedWrite } from "../../fs/contained-write.js";
|
|
4
4
|
import { DEFAULT_TASK_CACHE_ROOT, TASK_CACHE_MANIFEST } from "./constants.js";
|
|
5
5
|
function entryDir(projectRoot, cacheKey, cacheRoot = DEFAULT_TASK_CACHE_ROOT) {
|
|
6
6
|
return join(resolve(projectRoot), cacheRoot, cacheKey.slice(0, 2), cacheKey);
|
|
@@ -22,12 +22,16 @@ export function writeCachedTaskRecord(projectRoot, cacheKey, record, cacheRoot =
|
|
|
22
22
|
if (record.exitCode !== 0) {
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
+
const root = resolve(projectRoot);
|
|
25
26
|
const dir = entryDir(projectRoot, cacheKey, cacheRoot);
|
|
26
27
|
const manifest = join(dir, TASK_CACHE_MANIFEST);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// #2951 Phase 2: product write sink routes through containedWrite (mkdir + replace).
|
|
29
|
+
containedWrite({
|
|
30
|
+
root,
|
|
31
|
+
target: manifest,
|
|
32
|
+
data: `${JSON.stringify(record, null, 2)}\n`,
|
|
33
|
+
mode: "replace",
|
|
34
|
+
});
|
|
31
35
|
}
|
|
32
36
|
export function clearTaskCache(projectRoot, cacheRoot = DEFAULT_TASK_CACHE_ROOT) {
|
|
33
37
|
const root = join(resolve(projectRoot), cacheRoot);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, readdirSync, readFileSync
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
4
|
import { referenceTypeMatches } from "@deftai/directive-types";
|
|
5
5
|
import { sortedStringifyCompact } from "../codebase/json.js";
|
|
6
|
-
import {
|
|
6
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
7
7
|
import { hasArtifactSuffix, resolveLifecycleFolder } from "../layout/resolve.js";
|
|
8
8
|
import { classifyBucket, loadBucketMatchers, resolveCapacityAllocation, SOURCE_MATCH, } from "../policy/capacity.js";
|
|
9
9
|
export const COMPLETED_FOLDER = "completed";
|
|
@@ -139,7 +139,7 @@ function inWindow(completedAt, windowDays, now) {
|
|
|
139
139
|
return ageDays >= 0 && ageDays <= windowDays;
|
|
140
140
|
}
|
|
141
141
|
function writeMetadata(projectRoot, path, data, plan, metadata, options) {
|
|
142
|
-
|
|
142
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
143
143
|
if (typeof plan.metadata !== "object" || plan.metadata === null || Array.isArray(plan.metadata)) {
|
|
144
144
|
plan.metadata = metadata;
|
|
145
145
|
}
|
|
@@ -152,7 +152,13 @@ function writeMetadata(projectRoot, path, data, plan, metadata, options) {
|
|
|
152
152
|
metadata.capacityBucketSource = options.source;
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
const root = resolve(projectRoot);
|
|
156
|
+
containedWrite({
|
|
157
|
+
root,
|
|
158
|
+
target: path,
|
|
159
|
+
data: `${JSON.stringify(data, null, 2)}\n`,
|
|
160
|
+
mode: "replace",
|
|
161
|
+
});
|
|
156
162
|
}
|
|
157
163
|
/** Backfill capacityBucket / completedAt on completed vBRIEFs. */
|
|
158
164
|
export async function backfill(projectRoot, options = {}) {
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* order-preserving (the namespaced key takes the legacy key's slot, keeping
|
|
10
10
|
* artifact diffs minimal).
|
|
11
11
|
*/
|
|
12
|
-
import { existsSync, lstatSync, readdirSync, readFileSync
|
|
12
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "node:fs";
|
|
13
13
|
import { join, relative, resolve } from "node:path";
|
|
14
|
-
import {
|
|
14
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
15
|
+
import { assertDirectoryNotSymlink } from "../fs/projection-containment.js";
|
|
15
16
|
import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
16
17
|
import { LEGACY_PLAN_COMPLETED_NOTE_KEY, LEGACY_PLAN_POLICY_KEY, PLAN_COMPLETED_NOTE_KEY, PLAN_POLICY_KEY, } from "../policy/plan-extensions.js";
|
|
17
18
|
/** The locked Category B renames (#1650): directive's own config, not xbrief data. */
|
|
@@ -141,7 +142,13 @@ export function migrateCategoryBCorpus(projectRoot) {
|
|
|
141
142
|
}
|
|
142
143
|
if (result.changed) {
|
|
143
144
|
try {
|
|
144
|
-
|
|
145
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
146
|
+
containedWrite({
|
|
147
|
+
root,
|
|
148
|
+
target: file,
|
|
149
|
+
data: `${JSON.stringify(result.doc, null, 2)}\n`,
|
|
150
|
+
mode: "replace",
|
|
151
|
+
});
|
|
145
152
|
}
|
|
146
153
|
catch (err) {
|
|
147
154
|
conflicts.push({
|
|
@@ -150,7 +157,6 @@ export function migrateCategoryBCorpus(projectRoot) {
|
|
|
150
157
|
});
|
|
151
158
|
continue;
|
|
152
159
|
}
|
|
153
|
-
writeFileSync(file, `${JSON.stringify(result.doc, null, 2)}\n`, "utf8");
|
|
154
160
|
changed.push(relPath);
|
|
155
161
|
}
|
|
156
162
|
}
|
|
@@ -20,6 +20,9 @@ export const SKIP_DIRS = new Set([
|
|
|
20
20
|
"build",
|
|
21
21
|
"dist",
|
|
22
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).
|
|
23
26
|
"swarm-worktrees",
|
|
24
27
|
]);
|
|
25
28
|
export const LANGUAGE_BY_SUFFIX = {
|
package/dist/codebase/map.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync,
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
3
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
3
4
|
import { assertProjectionContained } from "../fs/projection-containment.js";
|
|
4
5
|
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
5
6
|
import { extractCodeStructure, loadJsonFile } from "../verify-source/code-structure-validate.js";
|
|
@@ -249,8 +250,14 @@ export function writeCodebaseMap(projectRoot, options) {
|
|
|
249
250
|
artifactPath: options.artifactPath,
|
|
250
251
|
});
|
|
251
252
|
const document = renderCodebaseMap(selection.artifact);
|
|
252
|
-
|
|
253
|
-
|
|
253
|
+
// #2980 wave D: product write sink routes through containedWrite.
|
|
254
|
+
const root = resolve(projectRoot);
|
|
255
|
+
containedWrite({
|
|
256
|
+
root,
|
|
257
|
+
target: resolvedOutput,
|
|
258
|
+
data: document,
|
|
259
|
+
mode: "replace",
|
|
260
|
+
});
|
|
254
261
|
return resolvedOutput;
|
|
255
262
|
}
|
|
256
263
|
export function runCodebaseMapCli(argv) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { mkdirSync, readFileSync
|
|
1
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
4
5
|
import { resolveTriageCachePath } from "../triage/cache-path.js";
|
|
5
6
|
import { CLEAN_WINDOW_HOURS, DIRTY_WINDOW_HOURS, ENV_STATE_PATH } from "./constants.js";
|
|
6
7
|
const STATE_FILENAME = "doctor-state.json";
|
|
@@ -57,8 +58,31 @@ export function writeState(projectRoot, payload) {
|
|
|
57
58
|
};
|
|
58
59
|
const path = statePath(projectRoot);
|
|
59
60
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
// #2980 wave C: product write sink routes through containedWrite.
|
|
62
|
+
// Default path is under the project triage cache; ENV_STATE_PATH may point outside.
|
|
63
|
+
const projectAbs = resolve(projectRoot);
|
|
64
|
+
const pathAbs = resolve(path);
|
|
65
|
+
const rel = relative(projectAbs, pathAbs);
|
|
66
|
+
const nested = rel.length > 0 && !rel.startsWith("..") && !isAbsolute(rel);
|
|
67
|
+
const payload = `${JSON.stringify(body, null, 2)}\n`;
|
|
68
|
+
if (nested) {
|
|
69
|
+
containedWrite({
|
|
70
|
+
root: projectAbs,
|
|
71
|
+
target: pathAbs,
|
|
72
|
+
data: payload,
|
|
73
|
+
mode: "replace",
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const parent = dirname(pathAbs);
|
|
78
|
+
mkdirSync(parent, { recursive: true });
|
|
79
|
+
containedWrite({
|
|
80
|
+
root: resolve(parent),
|
|
81
|
+
target: basename(pathAbs),
|
|
82
|
+
data: payload,
|
|
83
|
+
mode: "replace",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
62
86
|
return path;
|
|
63
87
|
}
|
|
64
88
|
catch {
|
package/dist/doctor/main.d.ts
CHANGED
|
@@ -4,6 +4,16 @@ import type { DoctorSeams, Finding, ResolutionSummary } from "./types.js";
|
|
|
4
4
|
export declare function cmdDoctor(args: readonly string[], seams?: DoctorSeams): number;
|
|
5
5
|
export declare function runAgentHooksHealthCheck(projectRoot: string, consumerContext: boolean, sink: ReturnType<typeof createPlainSink>, addFinding: (finding: Finding) => void, seams: DoctorSeams): boolean;
|
|
6
6
|
export declare function runAgentHooksLiveProbeCheck(projectRoot: string, sink: ReturnType<typeof createPlainSink>, addFinding: (finding: Finding) => void, seams: DoctorSeams): void;
|
|
7
|
+
/**
|
|
8
|
+
* Fail closed when PROJECT-DEFINITION under an xbrief/ layout still declares
|
|
9
|
+
* envelope 0.6 while the framework schema is 0.8 (#2971). Greenfield (no
|
|
10
|
+
* project definition yet) and current 0.8 envelopes pass. Unreadable paths
|
|
11
|
+
* (test seams / permission) skip rather than false-positive fail. Behind-major
|
|
12
|
+
* records `deft migrate:xbrief` as the next action — layout rename alone is not
|
|
13
|
+
* enough. Distinct from deposited-schema (`stale-xbrief-schema-deposit`) which
|
|
14
|
+
* must NOT route to migrate:xbrief when only framework schema files are stale.
|
|
15
|
+
*/
|
|
16
|
+
export declare function runXbriefEnvelopeVersionCheck(projectRoot: string, sink: ReturnType<typeof createPlainSink>, addFinding: (f: Finding) => void, seams: DoctorSeams): void;
|
|
7
17
|
/**
|
|
8
18
|
* Never emit a bare `task ...` remediation in a project without Taskfile wiring
|
|
9
19
|
* (#2267 / #2893). The `directive`/`deft` surface always works; `task deft:X` is
|
package/dist/doctor/main.js
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
|
+
import { VBRIEF_VERSION } from "@deftai/directive-types";
|
|
3
4
|
import { evaluate as evaluateAgentsMdAdvisory } from "../agents-md-advisory/evaluate.js";
|
|
4
5
|
import { contentRoot } from "../content-root.js";
|
|
6
|
+
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
5
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";
|
|
6
8
|
import { describeShadowedPlanExtension, detectShadowedPlanExtensions, } from "../policy/plan-extensions.js";
|
|
7
9
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
8
10
|
import { checkLocalEngineIntegrity, classify, detectPackageManager, evaluateSkew, reconcileVersions, plan as resolvePlan, } from "../resolution/index.js";
|
|
11
|
+
import { classifyXbriefSchemaDistance } from "../staleness-tickler/probe-xbrief.js";
|
|
9
12
|
import { resolveUserMdPath } from "../user-config/resolve-user-md.js";
|
|
10
13
|
import { evaluateAgentHooks } from "../verify-env/agent-hooks.js";
|
|
11
14
|
import { probeAgentHooksLive } from "../verify-env/agent-hooks-live-probe.js";
|
|
15
|
+
import { readDeclaredArtifactVersion } from "../xbrief-migrate/transforms.js";
|
|
12
16
|
import { agentsRefreshPlan, hasV3ManagedMarker } from "./agents-md.js";
|
|
13
17
|
import { runChecks } from "./checks.js";
|
|
14
18
|
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";
|
|
@@ -24,6 +28,8 @@ import { runLocalSignpostChecks } from "./signpost-checks.js";
|
|
|
24
28
|
import { classifyTaskfileInclude, formatGatesSurfaceDualRemediation, formatMissingIncludeSnippet, GATES_SURFACE_DEFT_REMEDIATION, includesBlockHasDeftTaskfile, resolveConsumerTaskfile, } from "./taskfile.js";
|
|
25
29
|
import { defaultWhich } from "./which.js";
|
|
26
30
|
const DEFAULT_RESOLUTION_PLATFORMS = ["linux", "darwin", "win32"];
|
|
31
|
+
/** Next-action command for project-envelope behind-major (#2971). */
|
|
32
|
+
const XBRIEF_ENVELOPE_MIGRATE_COMMAND = "deft migrate:xbrief";
|
|
27
33
|
/**
|
|
28
34
|
* Read the `packageManager` field (Corepack) from a project package.json, or
|
|
29
35
|
* null when absent/unreadable. Lets the doctor detect a pnpm project that has
|
|
@@ -341,6 +347,11 @@ export function cmdDoctor(args, seams = {}) {
|
|
|
341
347
|
}
|
|
342
348
|
sink.info("Checking gates-surface readiness (Taskfile include for deep-think agent gates)...");
|
|
343
349
|
runTaskfileIncludeCheck(projectRoot, fixMode, jsonMode, sink, addFinding, seams);
|
|
350
|
+
if (!jsonMode) {
|
|
351
|
+
sink.blank();
|
|
352
|
+
}
|
|
353
|
+
sink.info("Checking xBRIEF project envelope version...");
|
|
354
|
+
runXbriefEnvelopeVersionCheck(projectRoot, sink, addFinding, seams);
|
|
344
355
|
let resolution = null;
|
|
345
356
|
if (!runningInsideDeftRepo(projectRoot, seams)) {
|
|
346
357
|
if (!jsonMode) {
|
|
@@ -770,6 +781,155 @@ function runTaskfileIncludeCheck(projectRoot, fixMode, jsonMode, sink, addFindin
|
|
|
770
781
|
suggestion: GATES_SURFACE_DEFT_REMEDIATION,
|
|
771
782
|
});
|
|
772
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* Fail closed when PROJECT-DEFINITION under an xbrief/ layout still declares
|
|
786
|
+
* envelope 0.6 while the framework schema is 0.8 (#2971). Greenfield (no
|
|
787
|
+
* project definition yet) and current 0.8 envelopes pass. Unreadable paths
|
|
788
|
+
* (test seams / permission) skip rather than false-positive fail. Behind-major
|
|
789
|
+
* records `deft migrate:xbrief` as the next action — layout rename alone is not
|
|
790
|
+
* enough. Distinct from deposited-schema (`stale-xbrief-schema-deposit`) which
|
|
791
|
+
* must NOT route to migrate:xbrief when only framework schema files are stale.
|
|
792
|
+
*/
|
|
793
|
+
export function runXbriefEnvelopeVersionCheck(projectRoot, sink, addFinding, seams) {
|
|
794
|
+
const checkName = "xbrief-envelope-version";
|
|
795
|
+
const isFile = seams.isFile ?? ((p) => existsSync(p));
|
|
796
|
+
const readText = seams.readText ?? readTextSafe;
|
|
797
|
+
const targetVersion = VBRIEF_VERSION;
|
|
798
|
+
if (seams.probeXbriefEnvelope) {
|
|
799
|
+
const probe = seams.probeXbriefEnvelope(projectRoot);
|
|
800
|
+
emitXbriefEnvelopeFinding(checkName, probe, sink, addFinding);
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
let definitionPath;
|
|
804
|
+
try {
|
|
805
|
+
definitionPath = resolveProjectDefinitionPath(projectRoot);
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
// Pure vbrief/-only trees are already covered by layout migrate signposts.
|
|
809
|
+
const skipMessage = `${checkName}: skip -- legacy-only layout (use layout migrate first)`;
|
|
810
|
+
sink.info(skipMessage);
|
|
811
|
+
addFinding({
|
|
812
|
+
severity: "skip",
|
|
813
|
+
message: skipMessage,
|
|
814
|
+
check: checkName,
|
|
815
|
+
status: "skip",
|
|
816
|
+
reason: "legacy-only-layout",
|
|
817
|
+
});
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (!isFile(definitionPath)) {
|
|
821
|
+
const skipMessage = `${checkName}: skip -- no PROJECT-DEFINITION yet (greenfield)`;
|
|
822
|
+
sink.info(skipMessage);
|
|
823
|
+
addFinding({
|
|
824
|
+
severity: "skip",
|
|
825
|
+
message: skipMessage,
|
|
826
|
+
check: checkName,
|
|
827
|
+
status: "skip",
|
|
828
|
+
reason: "no-project-definition",
|
|
829
|
+
});
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
const text = readText(definitionPath);
|
|
833
|
+
if (text === null) {
|
|
834
|
+
// Unreadable path (permissions or injectable seams) is not proof of 0.6.
|
|
835
|
+
const skipMessage = `${checkName}: skip -- PROJECT-DEFINITION unreadable`;
|
|
836
|
+
sink.info(skipMessage);
|
|
837
|
+
addFinding({
|
|
838
|
+
severity: "skip",
|
|
839
|
+
message: skipMessage,
|
|
840
|
+
check: checkName,
|
|
841
|
+
status: "skip",
|
|
842
|
+
reason: "unreadable",
|
|
843
|
+
});
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
let declaredVersion = null;
|
|
847
|
+
try {
|
|
848
|
+
const parsed = JSON.parse(text);
|
|
849
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
850
|
+
declaredVersion = readDeclaredArtifactVersion(parsed);
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
const skipMessage = `${checkName}: skip -- PROJECT-DEFINITION is not a JSON object`;
|
|
854
|
+
sink.info(skipMessage);
|
|
855
|
+
addFinding({
|
|
856
|
+
severity: "skip",
|
|
857
|
+
message: skipMessage,
|
|
858
|
+
check: checkName,
|
|
859
|
+
status: "skip",
|
|
860
|
+
reason: "invalid-json-shape",
|
|
861
|
+
});
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
catch {
|
|
866
|
+
const skipMessage = `${checkName}: skip -- PROJECT-DEFINITION JSON parse failed`;
|
|
867
|
+
sink.info(skipMessage);
|
|
868
|
+
addFinding({
|
|
869
|
+
severity: "skip",
|
|
870
|
+
message: skipMessage,
|
|
871
|
+
check: checkName,
|
|
872
|
+
status: "skip",
|
|
873
|
+
reason: "parse-error",
|
|
874
|
+
});
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
const distance = classifyXbriefSchemaDistance(declaredVersion, targetVersion);
|
|
878
|
+
emitXbriefEnvelopeFinding(checkName, {
|
|
879
|
+
declaredVersion,
|
|
880
|
+
targetVersion,
|
|
881
|
+
distance,
|
|
882
|
+
stale: distance !== "current",
|
|
883
|
+
}, sink, addFinding);
|
|
884
|
+
}
|
|
885
|
+
function emitXbriefEnvelopeFinding(checkName, probe, sink, addFinding) {
|
|
886
|
+
if (probe.distance === "current") {
|
|
887
|
+
const okMessage = `${checkName}: current -- xBRIEFInfo@${probe.declaredVersion ?? probe.targetVersion} ` +
|
|
888
|
+
`(framework ${probe.targetVersion})`;
|
|
889
|
+
sink.success(okMessage);
|
|
890
|
+
addFinding({
|
|
891
|
+
severity: "skip",
|
|
892
|
+
message: okMessage,
|
|
893
|
+
check: checkName,
|
|
894
|
+
status: "current",
|
|
895
|
+
declared_version: probe.declaredVersion,
|
|
896
|
+
target_version: probe.targetVersion,
|
|
897
|
+
});
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
if (probe.distance === "behind-minor") {
|
|
901
|
+
const warnMessage = `${checkName}: behind-minor -- declared ${probe.declaredVersion ?? "unknown"}, ` +
|
|
902
|
+
`framework ${probe.targetVersion}. Prefer \`deft migrate:xbrief\` when convenient.`;
|
|
903
|
+
sink.warn(warnMessage);
|
|
904
|
+
addFinding({
|
|
905
|
+
severity: "warning",
|
|
906
|
+
message: warnMessage,
|
|
907
|
+
check: checkName,
|
|
908
|
+
status: "behind-minor",
|
|
909
|
+
suggestion: XBRIEF_ENVELOPE_MIGRATE_COMMAND,
|
|
910
|
+
declared_version: probe.declaredVersion,
|
|
911
|
+
target_version: probe.targetVersion,
|
|
912
|
+
next_command: XBRIEF_ENVELOPE_MIGRATE_COMMAND,
|
|
913
|
+
});
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
// behind-major (declared 0.6 vs framework 0.8, dual half-state, missing version)
|
|
917
|
+
const nextCommand = XBRIEF_ENVELOPE_MIGRATE_COMMAND;
|
|
918
|
+
const message = `${checkName}: behind-major -- declared ${probe.declaredVersion ?? "unknown"}, ` +
|
|
919
|
+
`framework ${probe.targetVersion}. Next action: run \`${nextCommand}\` to bump project ` +
|
|
920
|
+
`JSON envelopes to xBRIEFInfo@${probe.targetVersion} (layout rename alone is not enough).`;
|
|
921
|
+
sink.error(message);
|
|
922
|
+
addFinding({
|
|
923
|
+
severity: "error",
|
|
924
|
+
message,
|
|
925
|
+
check: checkName,
|
|
926
|
+
status: "behind-major",
|
|
927
|
+
suggestion: nextCommand,
|
|
928
|
+
declared_version: probe.declaredVersion,
|
|
929
|
+
target_version: probe.targetVersion,
|
|
930
|
+
next_command: nextCommand,
|
|
931
|
+
});
|
|
932
|
+
}
|
|
773
933
|
/**
|
|
774
934
|
* Surface the resolved USER.md path + which search rung matched (#2271) so the
|
|
775
935
|
* resolution boundary is visible in doctor output. Uses the shared first-hit-
|
package/dist/doctor/types.d.ts
CHANGED
|
@@ -150,5 +150,16 @@ export interface DoctorSeams {
|
|
|
150
150
|
readonly evaluateAgentHooks?: (projectRoot: string) => AgentHookHealthResult;
|
|
151
151
|
/** Live hook spawn probe for doctor --full (#2852). */
|
|
152
152
|
readonly probeAgentHooksLive?: (projectRoot: string) => AgentHookLiveProbeResult;
|
|
153
|
+
/**
|
|
154
|
+
* xBRIEF project-envelope staleness probe (#2971). Injected so doctor can
|
|
155
|
+
* fail closed on 0.6 project JSON under an xbrief/ layout without re-deriving
|
|
156
|
+
* the shared `probeXbriefStaleness` path in tests.
|
|
157
|
+
*/
|
|
158
|
+
readonly probeXbriefEnvelope?: (projectRoot: string) => {
|
|
159
|
+
readonly declaredVersion: string | null;
|
|
160
|
+
readonly targetVersion: string;
|
|
161
|
+
readonly distance: "current" | "behind-minor" | "behind-major";
|
|
162
|
+
readonly stale: boolean;
|
|
163
|
+
};
|
|
153
164
|
}
|
|
154
165
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operator-facing escalation actions: file, list, resolve, batch-approve (#518).
|
|
3
|
+
*/
|
|
4
|
+
import { type EscalationEvent, type EscalationType } from "./types.js";
|
|
5
|
+
export interface FileEscalationInput {
|
|
6
|
+
readonly projectRoot: string;
|
|
7
|
+
readonly type: string;
|
|
8
|
+
readonly title: string;
|
|
9
|
+
readonly body?: string;
|
|
10
|
+
readonly agentId?: string;
|
|
11
|
+
readonly contextRefs?: readonly string[];
|
|
12
|
+
readonly slaHours?: number;
|
|
13
|
+
readonly dangerous?: boolean;
|
|
14
|
+
readonly id?: string;
|
|
15
|
+
readonly now?: Date;
|
|
16
|
+
}
|
|
17
|
+
export declare function fileEscalation(input: FileEscalationInput): EscalationEvent;
|
|
18
|
+
export interface ResolveEscalationInput {
|
|
19
|
+
readonly projectRoot: string;
|
|
20
|
+
readonly id: string;
|
|
21
|
+
readonly decision: string;
|
|
22
|
+
readonly actor?: string;
|
|
23
|
+
readonly note?: string | null;
|
|
24
|
+
readonly answer?: string | null;
|
|
25
|
+
readonly now?: Date;
|
|
26
|
+
}
|
|
27
|
+
export type ResolveEscalationResult = {
|
|
28
|
+
readonly ok: true;
|
|
29
|
+
readonly event: EscalationEvent;
|
|
30
|
+
} | {
|
|
31
|
+
readonly ok: false;
|
|
32
|
+
readonly code: "not-found" | "already-resolved" | "invalid-decision";
|
|
33
|
+
readonly message: string;
|
|
34
|
+
};
|
|
35
|
+
export declare function resolveEscalation(input: ResolveEscalationInput): ResolveEscalationResult;
|
|
36
|
+
export interface BatchApproveInput {
|
|
37
|
+
readonly projectRoot: string;
|
|
38
|
+
/** Limit to these ids; empty/undefined = all open batch-eligible. */
|
|
39
|
+
readonly ids?: readonly string[];
|
|
40
|
+
readonly actor?: string;
|
|
41
|
+
readonly note?: string | null;
|
|
42
|
+
/** When false (default), skip `dangerous: true` items. */
|
|
43
|
+
readonly includeDangerous?: boolean;
|
|
44
|
+
readonly now?: Date;
|
|
45
|
+
}
|
|
46
|
+
export interface BatchApproveResult {
|
|
47
|
+
readonly approved: EscalationEvent[];
|
|
48
|
+
readonly skipped: Array<{
|
|
49
|
+
id: string;
|
|
50
|
+
reason: string;
|
|
51
|
+
}>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Bulk-approve only `cmd_approval` and `question` open items.
|
|
55
|
+
* design_decision / approval / resource / external must use individual resolve.
|
|
56
|
+
* Dangerous items are skipped unless `includeDangerous` is true.
|
|
57
|
+
*/
|
|
58
|
+
export declare function batchApproveEscalations(input: BatchApproveInput): BatchApproveResult;
|
|
59
|
+
export declare function listEscalationsFiltered(projectRoot: string, opts?: {
|
|
60
|
+
openOnly?: boolean;
|
|
61
|
+
type?: EscalationType;
|
|
62
|
+
}): EscalationEvent[];
|
|
63
|
+
//# sourceMappingURL=actions.d.ts.map
|