@deftai/directive-core 0.70.0 → 0.71.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/branch/evaluate.js +7 -0
- package/dist/doctor/doctor-state.js +2 -2
- package/dist/eval/crud-telemetry.d.ts +59 -0
- package/dist/eval/crud-telemetry.js +307 -0
- package/dist/eval/health.d.ts +52 -0
- package/dist/eval/health.js +240 -0
- package/dist/eval/readback.d.ts +38 -0
- package/dist/eval/readback.js +209 -0
- package/dist/eval/report.d.ts +53 -0
- package/dist/eval/report.js +161 -0
- package/dist/eval/run.d.ts +79 -0
- package/dist/eval/run.js +309 -0
- package/dist/events/attribution-constants.d.ts +13 -0
- package/dist/events/attribution-constants.js +18 -0
- package/dist/events/attribution-ledger.d.ts +43 -0
- package/dist/events/attribution-ledger.js +61 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/init-deposit/gitignore.js +5 -5
- package/dist/layout/resolve.d.ts +2 -2
- package/dist/layout/resolve.js +2 -2
- package/dist/lifecycle/events.js +2 -0
- package/dist/policy/index.d.ts +2 -1
- package/dist/policy/index.js +15 -2
- package/dist/policy/value-feedback.d.ts +56 -0
- package/dist/policy/value-feedback.js +284 -0
- package/dist/preflight-cache/evaluate.js +4 -3
- package/dist/scope/audit-log.js +3 -3
- package/dist/scope/transition.js +16 -5
- package/dist/session/session-start.js +20 -0
- package/dist/slice/record.js +3 -3
- package/dist/triage/actions/candidates-log.d.ts +4 -4
- package/dist/triage/actions/candidates-log.js +4 -7
- package/dist/triage/bootstrap/gitignore.d.ts +7 -3
- package/dist/triage/bootstrap/gitignore.js +67 -37
- package/dist/triage/bootstrap/index.d.ts +1 -1
- package/dist/triage/bootstrap/index.js +4 -3
- package/dist/triage/bulk/index.js +2 -2
- package/dist/triage/cache-path.d.ts +39 -0
- package/dist/triage/cache-path.js +98 -0
- package/dist/triage/help/registry-data.d.ts +48 -15
- package/dist/triage/help/registry-data.js +86 -15
- package/dist/triage/index.d.ts +2 -0
- package/dist/triage/index.js +2 -0
- package/dist/triage/queue/audit.js +2 -2
- package/dist/triage/queue/cache.js +2 -2
- package/dist/triage/reconcile/reconcile.js +4 -3
- package/dist/triage/scope/mutations-core.js +3 -2
- package/dist/triage/subscribe/index.d.ts +1 -1
- package/dist/triage/subscribe/index.js +4 -3
- package/dist/triage/summary/index.d.ts +2 -2
- package/dist/triage/summary/index.js +4 -3
- package/dist/triage/summary/reconcilable.d.ts +1 -1
- package/dist/triage/summary/reconcilable.js +3 -2
- package/dist/triage/welcome/constants.d.ts +2 -2
- package/dist/triage/welcome/constants.js +2 -2
- package/dist/triage/welcome/prior-state.js +3 -2
- package/dist/triage/welcome/summary.js +5 -4
- package/dist/value/adoption-emit.d.ts +14 -0
- package/dist/value/adoption-emit.js +67 -0
- package/dist/value/adoption-registry.d.ts +62 -0
- package/dist/value/adoption-registry.js +214 -0
- package/dist/value/feedback-file.d.ts +67 -0
- package/dist/value/feedback-file.js +353 -0
- package/dist/value/friction-emit.d.ts +10 -0
- package/dist/value/friction-emit.js +24 -0
- package/dist/value/readback.d.ts +86 -0
- package/dist/value/readback.js +503 -0
- package/dist/vbrief-validate/decomposition.js +1 -1
- package/dist/wip-cap/evaluate.js +3 -0
- package/package.json +35 -3
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { DEFAULT_EVENT_LOG, emit } from "../lifecycle/events.js";
|
|
3
|
+
import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
|
|
4
|
+
import { ATTRIBUTION_EVENT_NAMES } from "./attribution-constants.js";
|
|
5
|
+
export { ALL_ATTRIBUTION_EVENT_NAMES, ATTRIBUTION_EVENT_NAMES, ATTRIBUTION_REQUIRED_PAYLOAD, } from "./attribution-constants.js";
|
|
6
|
+
function resolveLedgerLogPath(projectRoot, logPath) {
|
|
7
|
+
if (logPath !== undefined && logPath !== null) {
|
|
8
|
+
return resolve(logPath);
|
|
9
|
+
}
|
|
10
|
+
return resolve(projectRoot, DEFAULT_EVENT_LOG);
|
|
11
|
+
}
|
|
12
|
+
function signalClassForEvent(name) {
|
|
13
|
+
const prefix = name.split(":")[0];
|
|
14
|
+
if (prefix === "value" || prefix === "bypass" || prefix === "adoption" || prefix === "friction") {
|
|
15
|
+
return prefix;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`attribution event '${name}' has unknown signal-class prefix`);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Append an attribution ledger entry when valueFeedback emitEvents is allowed.
|
|
21
|
+
* Returns null when gated OFF (no disk write).
|
|
22
|
+
*/
|
|
23
|
+
export function emitAttributionSignal(name, payload, options) {
|
|
24
|
+
try {
|
|
25
|
+
const policy = options.policyOverride ?? resolveValueFeedback(options.projectRoot);
|
|
26
|
+
if (!isValueFeedbackPathAllowed("emitEvents", policy)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const signalClass = signalClassForEvent(name);
|
|
30
|
+
const logPath = resolveLedgerLogPath(options.projectRoot, options.logPath);
|
|
31
|
+
return emit(name, {
|
|
32
|
+
signal_class: signalClass,
|
|
33
|
+
...payload,
|
|
34
|
+
}, { logPath });
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Telemetry is best-effort; any failure must not interrupt gate callers (#1709).
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Record a detection-bound gate catch (value class). */
|
|
42
|
+
export function recordGateCatch(projectRoot, source, detail, options = {}) {
|
|
43
|
+
return emitAttributionSignal(ATTRIBUTION_EVENT_NAMES.valueGateCatch, { source, detail }, { projectRoot, ...options });
|
|
44
|
+
}
|
|
45
|
+
/** Record a WIP-cap protect refusal (value class). */
|
|
46
|
+
export function recordWipCapProtect(projectRoot, count, cap, options = {}) {
|
|
47
|
+
return emitAttributionSignal(ATTRIBUTION_EVENT_NAMES.valueWipCapProtect, { source: "verify:wip-cap", count, cap }, { projectRoot, ...options });
|
|
48
|
+
}
|
|
49
|
+
/** Record a bypass/off-flow signal. */
|
|
50
|
+
export function recordBypassSignal(projectRoot, source, detail, options = {}) {
|
|
51
|
+
return emitAttributionSignal(ATTRIBUTION_EVENT_NAMES.bypassOffFlow, { source, detail }, { projectRoot, ...options });
|
|
52
|
+
}
|
|
53
|
+
/** Record an adoption/unused-capability signal. */
|
|
54
|
+
export function recordAdoptionSignal(projectRoot, capability, detail, options = {}) {
|
|
55
|
+
return emitAttributionSignal(ATTRIBUTION_EVENT_NAMES.adoptionUnusedCapability, { source: "adoption-registry", capability, detail }, { projectRoot, ...options });
|
|
56
|
+
}
|
|
57
|
+
/** Record a friction/directive-gap signal. */
|
|
58
|
+
export function recordFrictionSignal(projectRoot, source, detail, options = {}) {
|
|
59
|
+
return emitAttributionSignal(ATTRIBUTION_EVENT_NAMES.frictionDirectiveGap, { source, detail }, { projectRoot, ...options });
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=attribution-ledger.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export * as capacity from "./capacity/index.js";
|
|
|
15
15
|
export * as codebase from "./codebase/index.js";
|
|
16
16
|
export * as doctor from "./doctor/index.js";
|
|
17
17
|
export * from "./encoding/index.js";
|
|
18
|
+
export * as evalCrud from "./eval/crud-telemetry.js";
|
|
19
|
+
export * as evalHealth from "./eval/health.js";
|
|
20
|
+
export * as evalReport from "./eval/report.js";
|
|
21
|
+
export * as evalRun from "./eval/run.js";
|
|
22
|
+
export * as events from "./events/attribution-ledger.js";
|
|
18
23
|
export * from "./forward-coverage/evaluate.js";
|
|
19
24
|
export * as intake from "./intake/index.js";
|
|
20
25
|
export * as layout from "./layout/index.js";
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,11 @@ export * as capacity from "./capacity/index.js";
|
|
|
15
15
|
export * as codebase from "./codebase/index.js";
|
|
16
16
|
export * as doctor from "./doctor/index.js";
|
|
17
17
|
export * from "./encoding/index.js";
|
|
18
|
+
export * as evalCrud from "./eval/crud-telemetry.js";
|
|
19
|
+
export * as evalHealth from "./eval/health.js";
|
|
20
|
+
export * as evalReport from "./eval/report.js";
|
|
21
|
+
export * as evalRun from "./eval/run.js";
|
|
22
|
+
export * as events from "./events/attribution-ledger.js";
|
|
18
23
|
export * from "./forward-coverage/evaluate.js";
|
|
19
24
|
export * as intake from "./intake/index.js";
|
|
20
25
|
export * as layout from "./layout/index.js";
|
|
@@ -26,11 +26,11 @@ export const CANONICAL_GITIGNORE_BASELINE = [
|
|
|
26
26
|
".deft/ritual-state.json",
|
|
27
27
|
".deft/last-session.json",
|
|
28
28
|
".deft/routing.local.json",
|
|
29
|
-
"vbrief/.
|
|
30
|
-
"vbrief/.
|
|
31
|
-
"vbrief/.
|
|
32
|
-
"vbrief/.
|
|
33
|
-
"vbrief/.
|
|
29
|
+
"vbrief/.triage-cache/candidates.jsonl",
|
|
30
|
+
"vbrief/.triage-cache/summary-history.jsonl",
|
|
31
|
+
"vbrief/.triage-cache/scope-lifecycle.jsonl",
|
|
32
|
+
"vbrief/.triage-cache/decompositions/",
|
|
33
|
+
"vbrief/.triage-cache/doctor-state.json",
|
|
34
34
|
"vbrief/*.lock",
|
|
35
35
|
".deft/core.bak-*/",
|
|
36
36
|
".deft/*.bak-*",
|
package/dist/layout/resolve.d.ts
CHANGED
|
@@ -51,9 +51,9 @@ export declare function resolveLifecycleRoot(projectRoot: string): string;
|
|
|
51
51
|
* (e.g. `<root>/<xbrief|vbrief>/active`). Layout-aware (#2109 part 2a).
|
|
52
52
|
*/
|
|
53
53
|
export declare function resolveLifecycleFolder(projectRoot: string, folder: string): string;
|
|
54
|
-
/** Absolute path to the
|
|
54
|
+
/** Absolute path to the layout-aware `.eval/` directory for version-eval results (#1703). */
|
|
55
55
|
export declare function resolveEvalDir(projectRoot: string): string;
|
|
56
|
-
/** Absolute path
|
|
56
|
+
/** Absolute path under `.eval/` for version-eval artefacts (not triage working-set). */
|
|
57
57
|
export declare function resolveEvalPath(projectRoot: string, ...segments: string[]): string;
|
|
58
58
|
/** Absolute path to the resolved lifecycle `.audit` directory (#2109 part 2a). */
|
|
59
59
|
export declare function resolveAuditDir(projectRoot: string): string;
|
package/dist/layout/resolve.js
CHANGED
|
@@ -101,11 +101,11 @@ export function resolveLifecycleRoot(projectRoot) {
|
|
|
101
101
|
export function resolveLifecycleFolder(projectRoot, folder) {
|
|
102
102
|
return join(resolveLifecycleRoot(projectRoot), folder);
|
|
103
103
|
}
|
|
104
|
-
/** Absolute path to the
|
|
104
|
+
/** Absolute path to the layout-aware `.eval/` directory for version-eval results (#1703). */
|
|
105
105
|
export function resolveEvalDir(projectRoot) {
|
|
106
106
|
return join(resolveLifecycleRoot(projectRoot), ".eval");
|
|
107
107
|
}
|
|
108
|
-
/** Absolute path
|
|
108
|
+
/** Absolute path under `.eval/` for version-eval artefacts (not triage working-set). */
|
|
109
109
|
export function resolveEvalPath(projectRoot, ...segments) {
|
|
110
110
|
return join(resolveEvalDir(projectRoot), ...segments);
|
|
111
111
|
}
|
package/dist/lifecycle/events.js
CHANGED
|
@@ -3,10 +3,12 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync } from "node:fs";
|
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { contentRoot } from "../content-root.js";
|
|
6
|
+
import { ATTRIBUTION_REQUIRED_PAYLOAD } from "../events/attribution-constants.js";
|
|
6
7
|
/** Default event log location (project-local). */
|
|
7
8
|
export const DEFAULT_EVENT_LOG = join(".deft-cache", "events.jsonl");
|
|
8
9
|
const BEHAVIORAL_CATEGORY = "behavioral";
|
|
9
10
|
const REQUIRED_BEHAVIORAL_PAYLOAD = {
|
|
11
|
+
...ATTRIBUTION_REQUIRED_PAYLOAD,
|
|
10
12
|
"session:interrupted": ["session_id", "reason"],
|
|
11
13
|
"session:resumed": ["session_id", "interrupted_id"],
|
|
12
14
|
"plan:approved": ["plan_ref", "approver"],
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from "./decisions.js";
|
|
|
5
5
|
export * from "./disclosure.js";
|
|
6
6
|
export * from "./plan-extensions.js";
|
|
7
7
|
export * from "./resolve.js";
|
|
8
|
+
export * from "./value-feedback.js";
|
|
8
9
|
export * from "./wip.js";
|
|
9
10
|
export declare const FIELD_ALLOW_DIRECT_COMMITS = "plan.policy.allowDirectCommitsToMaster";
|
|
10
11
|
export declare const FIELD_WIP_CAP = "plan.policy.wipCap";
|
|
@@ -29,7 +30,7 @@ export interface PolicyField {
|
|
|
29
30
|
}
|
|
30
31
|
/** Walk registered inspectors and return one row per field (#1148). */
|
|
31
32
|
export declare function inspectAllPolicies(projectRoot: string): PolicyField[];
|
|
32
|
-
/** Look up a single registered field by canonical dotted-path name. */
|
|
33
|
+
/** Look up a single registered field by canonical dotted-path name (or CLI alias). */
|
|
33
34
|
export declare function inspectOnePolicy(name: string, projectRoot: string): PolicyField | null;
|
|
34
35
|
/** Return canonical names of every registered typed-policy field. */
|
|
35
36
|
export declare function registeredPolicyNames(): string[];
|
package/dist/policy/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readPlanPolicy } from "./plan-extensions.js";
|
|
2
2
|
import { coerceLegacyNarrative, LEGACY_NARRATIVE_KEY, loadProjectDefinition } from "./resolve.js";
|
|
3
|
+
import { FIELD_VALUE_FEEDBACK, FIELD_VALUE_FEEDBACK_CLI_ALIAS, inspectValueFeedback, } from "./value-feedback.js";
|
|
3
4
|
import { DEFAULT_WIP_CAP } from "./wip.js";
|
|
4
5
|
export * from "./agents-md-advisory.js";
|
|
5
6
|
export * from "./autonomy.js";
|
|
@@ -8,6 +9,7 @@ export * from "./decisions.js";
|
|
|
8
9
|
export * from "./disclosure.js";
|
|
9
10
|
export * from "./plan-extensions.js";
|
|
10
11
|
export * from "./resolve.js";
|
|
12
|
+
export * from "./value-feedback.js";
|
|
11
13
|
export * from "./wip.js";
|
|
12
14
|
export const FIELD_ALLOW_DIRECT_COMMITS = "plan.policy.allowDirectCommitsToMaster";
|
|
13
15
|
export const FIELD_WIP_CAP = "plan.policy.wipCap";
|
|
@@ -213,6 +215,15 @@ function inspectSwarmSubagentBackend(data) {
|
|
|
213
215
|
source: "default-on-error",
|
|
214
216
|
};
|
|
215
217
|
}
|
|
218
|
+
function inspectValueFeedbackField(data) {
|
|
219
|
+
const field = inspectValueFeedback(data);
|
|
220
|
+
return {
|
|
221
|
+
name: field.name,
|
|
222
|
+
current: field.current,
|
|
223
|
+
default: field.default,
|
|
224
|
+
source: field.source,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
216
227
|
const REGISTERED_POLICIES = [
|
|
217
228
|
inspectAllowDirectCommits,
|
|
218
229
|
inspectWipCap,
|
|
@@ -225,16 +236,18 @@ const REGISTERED_POLICIES = [
|
|
|
225
236
|
emptyIsTyped: true,
|
|
226
237
|
}),
|
|
227
238
|
inspectSwarmSubagentBackend,
|
|
239
|
+
inspectValueFeedbackField,
|
|
228
240
|
];
|
|
229
241
|
/** Walk registered inspectors and return one row per field (#1148). */
|
|
230
242
|
export function inspectAllPolicies(projectRoot) {
|
|
231
243
|
const [data] = loadProjectDefinition(projectRoot);
|
|
232
244
|
return REGISTERED_POLICIES.map((inspect) => inspect(data));
|
|
233
245
|
}
|
|
234
|
-
/** Look up a single registered field by canonical dotted-path name. */
|
|
246
|
+
/** Look up a single registered field by canonical dotted-path name (or CLI alias). */
|
|
235
247
|
export function inspectOnePolicy(name, projectRoot) {
|
|
248
|
+
const normalized = name === FIELD_VALUE_FEEDBACK_CLI_ALIAS ? FIELD_VALUE_FEEDBACK : name;
|
|
236
249
|
for (const field of inspectAllPolicies(projectRoot)) {
|
|
237
|
-
if (field.name ===
|
|
250
|
+
if (field.name === normalized)
|
|
238
251
|
return field;
|
|
239
252
|
}
|
|
240
253
|
return null;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
2
|
+
export declare const FIELD_VALUE_FEEDBACK = "plan.policy.valueFeedback";
|
|
3
|
+
/** Short alias accepted by `policy:show --field=valueFeedback` (#1709). */
|
|
4
|
+
export declare const FIELD_VALUE_FEEDBACK_CLI_ALIAS = "valueFeedback";
|
|
5
|
+
export declare const DEFAULT_VALUE_FEEDBACK_ENABLED = false;
|
|
6
|
+
/** Sub-flag defaults applied when the master flag is enabled (#1709 tiered-cost decision). */
|
|
7
|
+
export declare const VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED: {
|
|
8
|
+
readonly emitEvents: true;
|
|
9
|
+
readonly sessionLine: true;
|
|
10
|
+
readonly upstreamPrompt: false;
|
|
11
|
+
};
|
|
12
|
+
export type ValueFeedbackSubFlag = keyof typeof VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED;
|
|
13
|
+
export interface ValueFeedbackConfig {
|
|
14
|
+
readonly enabled: boolean;
|
|
15
|
+
readonly emitEvents: boolean;
|
|
16
|
+
readonly sessionLine: boolean;
|
|
17
|
+
readonly upstreamPrompt: boolean;
|
|
18
|
+
}
|
|
19
|
+
export type ValueFeedbackSource = "typed" | "default" | "default-on-error";
|
|
20
|
+
export interface ValueFeedbackResolved extends ValueFeedbackConfig {
|
|
21
|
+
readonly source: ValueFeedbackSource;
|
|
22
|
+
readonly error: string | null;
|
|
23
|
+
}
|
|
24
|
+
export declare const VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE: string;
|
|
25
|
+
/** Validate a `plan.policy.valueFeedback` payload. */
|
|
26
|
+
export declare function validateValueFeedback(value: unknown): string[];
|
|
27
|
+
/** Resolve `plan.policy.valueFeedback` from PROJECT-DEFINITION (#1709). */
|
|
28
|
+
export declare function resolveValueFeedback(projectRoot: string): ValueFeedbackResolved;
|
|
29
|
+
/** Master gate: when `enabled` is false, every downstream path is rejected. */
|
|
30
|
+
export declare function isValueFeedbackPathAllowed(path: ValueFeedbackSubFlag, policy: ValueFeedbackResolved): boolean;
|
|
31
|
+
/** Resolved per-path gate booleans for policy:show and enable status output. */
|
|
32
|
+
export declare function valueFeedbackPathGates(policy: ValueFeedbackResolved): Record<ValueFeedbackSubFlag, boolean>;
|
|
33
|
+
/** Human-readable status line for CLI enable/show surfaces. */
|
|
34
|
+
export declare function formatValueFeedbackStatusLine(policy: ValueFeedbackResolved): string;
|
|
35
|
+
export interface ValueFeedbackPolicyField {
|
|
36
|
+
readonly name: typeof FIELD_VALUE_FEEDBACK;
|
|
37
|
+
readonly current: ValueFeedbackConfig;
|
|
38
|
+
readonly default: ValueFeedbackConfig;
|
|
39
|
+
readonly source: string;
|
|
40
|
+
}
|
|
41
|
+
/** Inspector row for `policy:show --field=valueFeedback`. */
|
|
42
|
+
export declare function inspectValueFeedback(data: Record<string, unknown> | null): ValueFeedbackPolicyField;
|
|
43
|
+
export interface EnableValueFeedbackOptions {
|
|
44
|
+
readonly confirm: boolean;
|
|
45
|
+
readonly actor?: string;
|
|
46
|
+
readonly note?: string;
|
|
47
|
+
readonly subFlags?: Partial<Record<ValueFeedbackSubFlag, boolean>>;
|
|
48
|
+
}
|
|
49
|
+
export interface EnableValueFeedbackResult {
|
|
50
|
+
readonly exitCode: 0 | 1 | 2;
|
|
51
|
+
readonly stdout: string;
|
|
52
|
+
readonly changed: boolean;
|
|
53
|
+
}
|
|
54
|
+
/** Persist `valueFeedback.enabled=true` after capability-cost disclosure (#1709). */
|
|
55
|
+
export declare function enableValueFeedback(projectRoot: string, options: EnableValueFeedbackOptions): EnableValueFeedbackResult;
|
|
56
|
+
//# sourceMappingURL=value-feedback.d.ts.map
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
3
|
+
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
4
|
+
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
5
|
+
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
6
|
+
export const FIELD_VALUE_FEEDBACK = "plan.policy.valueFeedback";
|
|
7
|
+
/** Short alias accepted by `policy:show --field=valueFeedback` (#1709). */
|
|
8
|
+
export const FIELD_VALUE_FEEDBACK_CLI_ALIAS = "valueFeedback";
|
|
9
|
+
export const DEFAULT_VALUE_FEEDBACK_ENABLED = false;
|
|
10
|
+
/** Sub-flag defaults applied when the master flag is enabled (#1709 tiered-cost decision). */
|
|
11
|
+
export const VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED = {
|
|
12
|
+
emitEvents: true,
|
|
13
|
+
sessionLine: true,
|
|
14
|
+
upstreamPrompt: false,
|
|
15
|
+
};
|
|
16
|
+
export const VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost disclosure -- enabling value feedback opts into " +
|
|
17
|
+
"attributed awareness surfaces that may consume session context and nudge budget.\n" +
|
|
18
|
+
" \u2022 Local emit-only ledger writes to the gitignored `.deft-cache/events.jsonl` " +
|
|
19
|
+
"(no network by default).\n" +
|
|
20
|
+
" \u2022 A budgeted session one-liner may appear when concrete attributed value exists.\n" +
|
|
21
|
+
" \u2022 Upstream gap-escalation prompts stay OFF unless you explicitly enable " +
|
|
22
|
+
"`upstreamPrompt` (GitHub attention + token cost).\n" +
|
|
23
|
+
" \u2022 Inspect current state: `task policy:show --field=valueFeedback`.\n" +
|
|
24
|
+
" \u2022 Reversible: set `enabled: false` under the typed policy block in PROJECT-DEFINITION.\n" +
|
|
25
|
+
" \u2022 Changes are recorded to meta/policy-changes.log for auditability.";
|
|
26
|
+
function defaultResolved(source, error = null) {
|
|
27
|
+
return {
|
|
28
|
+
enabled: DEFAULT_VALUE_FEEDBACK_ENABLED,
|
|
29
|
+
emitEvents: false,
|
|
30
|
+
sessionLine: false,
|
|
31
|
+
upstreamPrompt: false,
|
|
32
|
+
source,
|
|
33
|
+
error,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function readSubFlag(block, key, masterEnabled) {
|
|
37
|
+
if (!masterEnabled) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (key in block && typeof block[key] === "boolean") {
|
|
41
|
+
return block[key];
|
|
42
|
+
}
|
|
43
|
+
return VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED[key];
|
|
44
|
+
}
|
|
45
|
+
/** Validate a `plan.policy.valueFeedback` payload. */
|
|
46
|
+
export function validateValueFeedback(value) {
|
|
47
|
+
if (value === null || value === undefined) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
51
|
+
return [`${FIELD_VALUE_FEEDBACK} must be an object; got ${typeof value}`];
|
|
52
|
+
}
|
|
53
|
+
const rec = value;
|
|
54
|
+
const errors = [];
|
|
55
|
+
for (const key of ["enabled", "emitEvents", "sessionLine", "upstreamPrompt"]) {
|
|
56
|
+
if (key in rec && typeof rec[key] !== "boolean") {
|
|
57
|
+
errors.push(`${FIELD_VALUE_FEEDBACK}.${key} must be a boolean`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return errors;
|
|
61
|
+
}
|
|
62
|
+
function resolveFromPolicyBlock(raw) {
|
|
63
|
+
const errors = validateValueFeedback(raw);
|
|
64
|
+
if (errors.length > 0) {
|
|
65
|
+
return defaultResolved("default-on-error", errors[0] ?? "invalid valueFeedback block");
|
|
66
|
+
}
|
|
67
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
68
|
+
return defaultResolved("default");
|
|
69
|
+
}
|
|
70
|
+
const block = raw;
|
|
71
|
+
const enabled = typeof block.enabled === "boolean" ? block.enabled : DEFAULT_VALUE_FEEDBACK_ENABLED;
|
|
72
|
+
if (!enabled) {
|
|
73
|
+
return {
|
|
74
|
+
enabled: false,
|
|
75
|
+
emitEvents: false,
|
|
76
|
+
sessionLine: false,
|
|
77
|
+
upstreamPrompt: false,
|
|
78
|
+
source: "typed",
|
|
79
|
+
error: null,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
enabled: true,
|
|
84
|
+
emitEvents: readSubFlag(block, "emitEvents", true),
|
|
85
|
+
sessionLine: readSubFlag(block, "sessionLine", true),
|
|
86
|
+
upstreamPrompt: readSubFlag(block, "upstreamPrompt", true),
|
|
87
|
+
source: "typed",
|
|
88
|
+
error: null,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Resolve `plan.policy.valueFeedback` from PROJECT-DEFINITION (#1709). */
|
|
92
|
+
export function resolveValueFeedback(projectRoot) {
|
|
93
|
+
const [data, err] = loadProjectDefinition(projectRoot);
|
|
94
|
+
if (data === null) {
|
|
95
|
+
return defaultResolved("default-on-error", err);
|
|
96
|
+
}
|
|
97
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
98
|
+
if (typeof policyBlock !== "object" ||
|
|
99
|
+
policyBlock === null ||
|
|
100
|
+
Array.isArray(policyBlock) ||
|
|
101
|
+
!("valueFeedback" in policyBlock)) {
|
|
102
|
+
return defaultResolved("default");
|
|
103
|
+
}
|
|
104
|
+
return resolveFromPolicyBlock(policyBlock.valueFeedback);
|
|
105
|
+
}
|
|
106
|
+
/** Master gate: when `enabled` is false, every downstream path is rejected. */
|
|
107
|
+
export function isValueFeedbackPathAllowed(path, policy) {
|
|
108
|
+
if (!policy.enabled) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
return policy[path];
|
|
112
|
+
}
|
|
113
|
+
/** Resolved per-path gate booleans for policy:show and enable status output. */
|
|
114
|
+
export function valueFeedbackPathGates(policy) {
|
|
115
|
+
return {
|
|
116
|
+
emitEvents: isValueFeedbackPathAllowed("emitEvents", policy),
|
|
117
|
+
sessionLine: isValueFeedbackPathAllowed("sessionLine", policy),
|
|
118
|
+
upstreamPrompt: isValueFeedbackPathAllowed("upstreamPrompt", policy),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/** Human-readable status line for CLI enable/show surfaces. */
|
|
122
|
+
export function formatValueFeedbackStatusLine(policy) {
|
|
123
|
+
const gates = valueFeedbackPathGates(policy);
|
|
124
|
+
return (`[deft policy] valueFeedback enabled=${String(policy.enabled)} ` +
|
|
125
|
+
`emitEvents=${String(policy.emitEvents)} (path=${String(gates.emitEvents)}) ` +
|
|
126
|
+
`sessionLine=${String(policy.sessionLine)} (path=${String(gates.sessionLine)}) ` +
|
|
127
|
+
`upstreamPrompt=${String(policy.upstreamPrompt)} (path=${String(gates.upstreamPrompt)}).`);
|
|
128
|
+
}
|
|
129
|
+
function defaultInspectorConfig() {
|
|
130
|
+
const defaults = defaultResolved("default");
|
|
131
|
+
return {
|
|
132
|
+
enabled: defaults.enabled,
|
|
133
|
+
emitEvents: defaults.emitEvents,
|
|
134
|
+
sessionLine: defaults.sessionLine,
|
|
135
|
+
upstreamPrompt: defaults.upstreamPrompt,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function buildDefaultInspectorField() {
|
|
139
|
+
return {
|
|
140
|
+
name: FIELD_VALUE_FEEDBACK,
|
|
141
|
+
current: defaultInspectorConfig(),
|
|
142
|
+
default: {
|
|
143
|
+
enabled: DEFAULT_VALUE_FEEDBACK_ENABLED,
|
|
144
|
+
emitEvents: false,
|
|
145
|
+
sessionLine: false,
|
|
146
|
+
upstreamPrompt: false,
|
|
147
|
+
},
|
|
148
|
+
source: "default",
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** Inspector row for `policy:show --field=valueFeedback`. */
|
|
152
|
+
export function inspectValueFeedback(data) {
|
|
153
|
+
if (data === null) {
|
|
154
|
+
return buildDefaultInspectorField();
|
|
155
|
+
}
|
|
156
|
+
const policyBlock = readPlanPolicy(data.plan);
|
|
157
|
+
if (typeof policyBlock !== "object" ||
|
|
158
|
+
policyBlock === null ||
|
|
159
|
+
Array.isArray(policyBlock) ||
|
|
160
|
+
!("valueFeedback" in policyBlock)) {
|
|
161
|
+
return buildDefaultInspectorField();
|
|
162
|
+
}
|
|
163
|
+
const resolved = resolveFromPolicyBlock(policyBlock.valueFeedback);
|
|
164
|
+
return {
|
|
165
|
+
name: FIELD_VALUE_FEEDBACK,
|
|
166
|
+
current: {
|
|
167
|
+
enabled: resolved.enabled,
|
|
168
|
+
emitEvents: resolved.emitEvents,
|
|
169
|
+
sessionLine: resolved.sessionLine,
|
|
170
|
+
upstreamPrompt: resolved.upstreamPrompt,
|
|
171
|
+
},
|
|
172
|
+
default: {
|
|
173
|
+
enabled: DEFAULT_VALUE_FEEDBACK_ENABLED,
|
|
174
|
+
emitEvents: false,
|
|
175
|
+
sessionLine: false,
|
|
176
|
+
upstreamPrompt: false,
|
|
177
|
+
},
|
|
178
|
+
source: resolved.source,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/** Persist `valueFeedback.enabled=true` after capability-cost disclosure (#1709). */
|
|
182
|
+
export function enableValueFeedback(projectRoot, options) {
|
|
183
|
+
if (!options.confirm) {
|
|
184
|
+
return {
|
|
185
|
+
exitCode: 1,
|
|
186
|
+
stdout: `${VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE}\n\n` +
|
|
187
|
+
"Re-run with --confirm to apply: task policy:enable-value-feedback -- --confirm\n",
|
|
188
|
+
changed: false,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const path = projectDefinitionPath(projectRoot);
|
|
192
|
+
try {
|
|
193
|
+
const { changed } = projectDefinitionMutationLock(projectRoot, () => {
|
|
194
|
+
const parsed = JSON.parse(readFileSync(path, { encoding: "utf8" }));
|
|
195
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
196
|
+
throw new Error(`PROJECT-DEFINITION at ${path} top-level value is not a JSON object`);
|
|
197
|
+
}
|
|
198
|
+
const data = parsed;
|
|
199
|
+
if (typeof data.plan !== "object" || data.plan === null || Array.isArray(data.plan)) {
|
|
200
|
+
if (data.plan === undefined) {
|
|
201
|
+
data.plan = {};
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
throw new Error("PROJECT-DEFINITION 'plan' is not an object");
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const plan = data.plan;
|
|
208
|
+
migrateLegacyPolicyKey(plan);
|
|
209
|
+
const existingPolicy = plan[PLAN_POLICY_KEY];
|
|
210
|
+
if (typeof existingPolicy !== "object" ||
|
|
211
|
+
existingPolicy === null ||
|
|
212
|
+
Array.isArray(existingPolicy)) {
|
|
213
|
+
if (existingPolicy === undefined) {
|
|
214
|
+
plan[PLAN_POLICY_KEY] = {};
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
throw new Error("plan.policy is not an object");
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
const policyBlock = plan[PLAN_POLICY_KEY];
|
|
221
|
+
const previous = policyBlock.valueFeedback;
|
|
222
|
+
const prevObj = typeof previous === "object" && previous !== null && !Array.isArray(previous)
|
|
223
|
+
? previous
|
|
224
|
+
: {};
|
|
225
|
+
const sub = options.subFlags ?? {};
|
|
226
|
+
const readPersistedSubFlag = (key) => {
|
|
227
|
+
if (key in sub && typeof sub[key] === "boolean") {
|
|
228
|
+
return sub[key];
|
|
229
|
+
}
|
|
230
|
+
if (key in prevObj && typeof prevObj[key] === "boolean") {
|
|
231
|
+
return prevObj[key];
|
|
232
|
+
}
|
|
233
|
+
return VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED[key];
|
|
234
|
+
};
|
|
235
|
+
const nextBlock = {
|
|
236
|
+
enabled: true,
|
|
237
|
+
emitEvents: readPersistedSubFlag("emitEvents"),
|
|
238
|
+
sessionLine: readPersistedSubFlag("sessionLine"),
|
|
239
|
+
upstreamPrompt: readPersistedSubFlag("upstreamPrompt"),
|
|
240
|
+
};
|
|
241
|
+
const previousNormalized = resolveFromPolicyBlock(previous);
|
|
242
|
+
const changedFlag = previousNormalized.enabled !== nextBlock.enabled ||
|
|
243
|
+
previousNormalized.emitEvents !== nextBlock.emitEvents ||
|
|
244
|
+
previousNormalized.sessionLine !== nextBlock.sessionLine ||
|
|
245
|
+
previousNormalized.upstreamPrompt !== nextBlock.upstreamPrompt;
|
|
246
|
+
policyBlock.valueFeedback = nextBlock;
|
|
247
|
+
if (changedFlag) {
|
|
248
|
+
atomicWriteProjectDefinition(path, data);
|
|
249
|
+
}
|
|
250
|
+
const actor = options.actor ?? "task policy:enable-value-feedback";
|
|
251
|
+
const note = options.note ?? "";
|
|
252
|
+
const parts = [
|
|
253
|
+
`actor=${actor}`,
|
|
254
|
+
"valueFeedback.enabled=true",
|
|
255
|
+
`emitEvents=${String(nextBlock.emitEvents)}`,
|
|
256
|
+
`sessionLine=${String(nextBlock.sessionLine)}`,
|
|
257
|
+
`upstreamPrompt=${String(nextBlock.upstreamPrompt)}`,
|
|
258
|
+
`previous=${JSON.stringify(previous ?? null)}`,
|
|
259
|
+
];
|
|
260
|
+
if (note) {
|
|
261
|
+
parts.push(`note=${note.replace(/\n/g, " ").replace(/\r/g, " ")}`);
|
|
262
|
+
}
|
|
263
|
+
appendAuditLog(projectRoot, parts.join(" "));
|
|
264
|
+
return { changed: changedFlag };
|
|
265
|
+
});
|
|
266
|
+
const resolved = resolveValueFeedback(projectRoot);
|
|
267
|
+
const lines = [
|
|
268
|
+
`\u2713 ${FIELD_VALUE_FEEDBACK}.enabled=true (value-feedback ON).`,
|
|
269
|
+
changed
|
|
270
|
+
? " audit: meta/policy-changes.log updated."
|
|
271
|
+
: " no-op: value already matched (audit entry still appended for trail).",
|
|
272
|
+
formatValueFeedbackStatusLine(resolved),
|
|
273
|
+
];
|
|
274
|
+
return { exitCode: 0, stdout: `${lines.join("\n")}\n`, changed };
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
278
|
+
if (message.includes("PROJECT-DEFINITION not found")) {
|
|
279
|
+
return { exitCode: 2, stdout: `\u274c ${message}\n`, changed: false };
|
|
280
|
+
}
|
|
281
|
+
return { exitCode: 2, stdout: `\u274c Config error: ${message}\n`, changed: false };
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=value-feedback.js.map
|
|
@@ -14,15 +14,16 @@ import { execFileSync } from "node:child_process";
|
|
|
14
14
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
15
15
|
import { join, relative } from "node:path";
|
|
16
16
|
import { probeCacheDrift } from "../cache/fetch.js";
|
|
17
|
-
import {
|
|
17
|
+
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
18
18
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
19
19
|
import { latestDecisionForIssue as auditLatestDecisionForIssue } from "../triage/actions/candidates-log.js";
|
|
20
|
+
import { resolveCandidatesLogPath } from "../triage/cache-path.js";
|
|
20
21
|
// ---------------------------------------------------------------------------
|
|
21
22
|
// Public constants (mirror preflight_cache.py)
|
|
22
23
|
// ---------------------------------------------------------------------------
|
|
23
24
|
export const CACHE_DIR_NAME = ".deft-cache";
|
|
24
25
|
export const DEFAULT_SOURCE = "github-issue";
|
|
25
|
-
export const CANDIDATES_RELPATH = join("vbrief", ".
|
|
26
|
+
export const CANDIDATES_RELPATH = join("vbrief", ".triage-cache", "candidates.jsonl");
|
|
26
27
|
export const DEFAULT_MAX_AGE_HOURS = 24;
|
|
27
28
|
export const ENV_MAX_AGE_HOURS = "DEFT_CACHE_MAX_AGE_HOURS";
|
|
28
29
|
export const ENV_TRIAGE_REPO = "DEFT_TRIAGE_REPO";
|
|
@@ -335,7 +336,7 @@ export function evaluate(projectRoot, options = {}) {
|
|
|
335
336
|
DEFAULT_MAX_AGE_HOURS;
|
|
336
337
|
const cacheRoot = join(projectRoot, CACHE_DIR_NAME);
|
|
337
338
|
// Layout-aware (#2109 part 2a): resolve under xbrief/.eval when migrated.
|
|
338
|
-
const candidatesPath =
|
|
339
|
+
const candidatesPath = resolveCandidatesLogPath(projectRoot);
|
|
339
340
|
// Step 1: Resolve repo slug
|
|
340
341
|
const resolvedRepo = resolveRepo(projectRoot, cacheRoot, source, options.repo ?? null);
|
|
341
342
|
// Step 2: Check cache dir exists
|
package/dist/scope/audit-log.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync, writeSync, } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { resolveTriageCachePath } from "../triage/cache-path.js";
|
|
5
5
|
import { utcNowIso } from "./vbrief-json.js";
|
|
6
6
|
export class ScopeAuditLogError extends Error {
|
|
7
7
|
constructor(message) {
|
|
@@ -94,8 +94,8 @@ export function newDecisionId() {
|
|
|
94
94
|
return randomUUID();
|
|
95
95
|
}
|
|
96
96
|
export function canonicalLogPath(projectRoot) {
|
|
97
|
-
// Layout-aware (#
|
|
98
|
-
return
|
|
97
|
+
// Layout-aware (#1703): triage working-set under `.triage-cache/`.
|
|
98
|
+
return resolveTriageCachePath(resolve(projectRoot), "scope-lifecycle.jsonl");
|
|
99
99
|
}
|
|
100
100
|
function withAppendLock(logPath, fn) {
|
|
101
101
|
const lockPath = `${logPath}.lock`;
|
package/dist/scope/transition.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, renameSync
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync } from "node:fs";
|
|
2
2
|
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { InstrumentedVbriefCrud, persistCrudMetrics } from "../eval/crud-telemetry.js";
|
|
3
4
|
import { hasArtifactSuffix } from "../layout/resolve.js";
|
|
4
5
|
import { append, canonicalLogPath, newDecisionId } from "./audit-log.js";
|
|
5
6
|
import { stampCompletionMetadata } from "./capacity-stamp.js";
|
|
@@ -78,14 +79,24 @@ export function runTransition(action, filePath, now = new Date()) {
|
|
|
78
79
|
const nowIso = utcNowIso(now);
|
|
79
80
|
planObj.status = targetStatus;
|
|
80
81
|
planObj.updated = nowIso;
|
|
82
|
+
const vbriefRoot = dirname(dirname(resolvedPath));
|
|
83
|
+
const projectRoot = dirname(vbriefRoot);
|
|
81
84
|
if (act === "complete") {
|
|
82
|
-
const vbriefRoot = dirname(dirname(resolvedPath));
|
|
83
|
-
const projectRoot = dirname(vbriefRoot);
|
|
84
85
|
stampCompletionMetadata(planObj, projectRoot, nowIso);
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
+
const formatted = formatVbriefJson(data);
|
|
88
|
+
const crud = new InstrumentedVbriefCrud({ now: () => now });
|
|
89
|
+
const writeResult = crud.update(resolvedPath, formatted, { trustedWrite: true });
|
|
90
|
+
if (!writeResult.ok) {
|
|
91
|
+
return { ok: false, message: writeResult.error ?? `CRUD update failed for ${resolvedPath}` };
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
persistCrudMetrics(projectRoot, crud.getMetrics());
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* best-effort telemetry persistence */
|
|
98
|
+
}
|
|
87
99
|
if (targetFolder !== null) {
|
|
88
|
-
const vbriefRoot = dirname(dirname(resolvedPath));
|
|
89
100
|
const destDir = join(vbriefRoot, targetFolder);
|
|
90
101
|
mkdirSync(destDir, { recursive: true });
|
|
91
102
|
const destPath = join(destDir, basename);
|