@deftai/directive-core 0.93.0 → 0.95.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 +485 -0
- package/dist/cache/fetch.d.ts +29 -0
- package/dist/cache/fetch.js +131 -4
- package/dist/cache/operations.d.ts +8 -0
- package/dist/cache/operations.js +67 -4
- package/dist/check/cached-orchestrator.js +11 -0
- package/dist/check/consumer-gate-integrity.d.ts +68 -0
- package/dist/check/consumer-gate-integrity.js +265 -0
- package/dist/check/index.d.ts +1 -0
- package/dist/check/index.js +1 -0
- package/dist/check/orchestrator.js +10 -0
- package/dist/content-contracts/skills/greptile-detector.d.ts +9 -0
- package/dist/content-contracts/skills/greptile-detector.js +9 -2
- package/dist/doctor/main.js +98 -9
- package/dist/doctor/types.d.ts +2 -2
- package/dist/finish-loop/pr-finish-loop.js +1 -0
- package/dist/freshness/bind.d.ts +67 -0
- package/dist/freshness/bind.js +201 -0
- package/dist/freshness/cli.d.ts +30 -0
- package/dist/freshness/cli.js +180 -0
- package/dist/freshness/compare.d.ts +14 -0
- package/dist/freshness/compare.js +134 -0
- package/dist/freshness/generation.d.ts +44 -0
- package/dist/freshness/generation.js +172 -0
- package/dist/freshness/index.d.ts +13 -0
- package/dist/freshness/index.js +13 -0
- package/dist/freshness/report.d.ts +43 -0
- package/dist/freshness/report.js +139 -0
- package/dist/freshness/types.d.ts +70 -0
- package/dist/freshness/types.js +30 -0
- package/dist/handoff-evidence/index.d.ts +8 -0
- package/dist/handoff-evidence/index.js +7 -0
- package/dist/handoff-evidence/validate.d.ts +105 -0
- package/dist/handoff-evidence/validate.js +423 -0
- package/dist/hooks/dispatcher.d.ts +27 -2
- package/dist/hooks/dispatcher.js +134 -14
- package/dist/hooks/readonly.d.ts +14 -0
- package/dist/hooks/readonly.js +126 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/init-deposit/agent-hooks.d.ts +1 -1
- package/dist/init-deposit/agent-hooks.js +5 -2
- package/dist/init-deposit/init-deposit.d.ts +7 -1
- package/dist/init-deposit/init-deposit.js +24 -4
- package/dist/init-deposit/refresh.d.ts +9 -1
- package/dist/init-deposit/refresh.js +48 -5
- package/dist/intake/issue-ingest.d.ts +20 -0
- package/dist/intake/issue-ingest.js +58 -0
- package/dist/policy/index.d.ts +4 -0
- package/dist/policy/index.js +50 -1
- package/dist/policy/min-greptile-confidence.d.ts +57 -0
- package/dist/policy/min-greptile-confidence.js +123 -0
- package/dist/pr-merge-readiness/compute.d.ts +11 -1
- package/dist/pr-merge-readiness/compute.js +17 -4
- package/dist/pr-merge-readiness/evaluate.d.ts +8 -1
- package/dist/pr-merge-readiness/evaluate.js +7 -4
- package/dist/pr-merge-readiness/mergeability.d.ts +7 -1
- package/dist/pr-merge-readiness/mergeability.js +14 -2
- package/dist/pr-monitor/main.d.ts +2 -0
- package/dist/pr-monitor/main.js +31 -1
- package/dist/pr-monitor/monitor.js +5 -1
- package/dist/pr-monitor/readiness.js +3 -1
- package/dist/pr-monitor/types.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.d.ts +8 -0
- package/dist/pr-wait-mergeable/cascade.js +28 -1
- package/dist/pr-wait-mergeable/main.d.ts +2 -0
- package/dist/pr-wait-mergeable/main.js +16 -0
- package/dist/pr-wait-mergeable/types.d.ts +3 -1
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +3 -0
- package/dist/pr-watch/constants.d.ts +4 -1
- package/dist/pr-watch/constants.js +5 -2
- package/dist/pr-watch/main.js +1 -0
- package/dist/pr-watch/probe.d.ts +1 -1
- package/dist/pr-watch/probe.js +4 -1
- package/dist/pr-watch/types.d.ts +7 -1
- package/dist/pr-watch/watch.js +3 -2
- package/dist/release-e2e/greenfield-python-free-smoke.js +7 -3
- package/dist/review-monitor/index.d.ts +1 -0
- package/dist/review-monitor/index.js +1 -0
- package/dist/review-monitor/l4-owner.d.ts +46 -0
- package/dist/review-monitor/l4-owner.js +262 -0
- package/dist/session/ritual-entrypoint.d.ts +6 -2
- package/dist/session/ritual-entrypoint.js +15 -2
- package/dist/session/session-ready.js +31 -14
- package/dist/session/session-start.d.ts +2 -1
- package/dist/session/session-start.js +66 -3
- package/dist/session/verify-session-ritual.d.ts +4 -2
- package/dist/session/verify-session-ritual.js +11 -11
- package/dist/slash/product-set.js +4 -1
- package/dist/swarm/verify-review-clean.d.ts +4 -3
- package/dist/swarm/verify-review-clean.js +28 -73
- package/dist/tool-events/classify.js +1 -0
- package/dist/triage/classify/index.d.ts +9 -0
- package/dist/triage/classify/index.js +34 -2
- package/dist/triage/classify/label-mirror.d.ts +143 -0
- package/dist/triage/classify/label-mirror.js +684 -0
- package/dist/triage/help/registry-data.d.ts +7 -7
- package/dist/triage/help/registry-data.js +27 -7
- package/dist/triage/queue/show.d.ts +1 -1
- package/dist/triage/queue/show.js +4 -2
- package/dist/triage/welcome/writers.js +16 -7
- package/dist/umbrella-current-shape/index.d.ts +45 -0
- package/dist/umbrella-current-shape/index.js +162 -8
- package/dist/vbrief-reconcile/index.d.ts +2 -2
- package/dist/vbrief-reconcile/index.js +1 -1
- package/dist/vbrief-reconcile/types.d.ts +14 -0
- package/dist/vbrief-reconcile/umbrellas.d.ts +31 -2
- package/dist/vbrief-reconcile/umbrellas.js +231 -19
- package/dist/vbrief-validate/project-definition.js +1 -1
- package/dist/verify-env/agent-hook-readiness.d.ts +42 -0
- package/dist/verify-env/agent-hook-readiness.js +150 -0
- package/dist/verify-env/agent-hooks-live-probe.d.ts +16 -4
- package/dist/verify-env/agent-hooks-live-probe.js +120 -107
- package/dist/verify-env/agent-hooks.js +11 -3
- package/dist/verify-env/index.d.ts +1 -0
- package/dist/verify-env/index.js +1 -0
- package/package.json +7 -3
package/dist/hooks/readonly.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/** Session posture env for assist/research low-ceremony writes (#1802). */
|
|
2
|
+
export declare const ASSIST_SESSION_POSTURE_ENV = "DEFT_SESSION_POSTURE";
|
|
1
3
|
/** Best-effort read-only explore signal from host payload (#1185). */
|
|
2
4
|
export declare function hookReadOnlyFromPayload(payload: unknown): boolean;
|
|
3
5
|
/** Read-only hook context: env override or host payload marker (#1185). */
|
|
4
6
|
export declare function isReadOnlyHookContext(payload: unknown, environ?: NodeJS.ProcessEnv): boolean;
|
|
5
7
|
/** Explore sub-agent spawns are exempt from the implementation gate stack (#1185). */
|
|
6
8
|
export declare function isExploreSpawn(payload: unknown): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Assist / research / ephemeral session posture for low-ceremony scratch writes (#1802).
|
|
11
|
+
* Structural markers only (env, payload posture fields, worker_role/subagent_type).
|
|
12
|
+
* Absent marker → false (fail closed). Compose with allowlisted scratch path fence.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isAssistPosture(payload: unknown, environ?: NodeJS.ProcessEnv): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Ephemeral / assist / docs spawns skip active-xBRIEF implementation gates (#3080).
|
|
17
|
+
* True only with an explicit allowlisted marker. Absent marker → false (fail closed).
|
|
18
|
+
* When an ephemeral marker conflicts with implement envelope signals, implement wins.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isEphemeralSpawn(payload: unknown): boolean;
|
|
7
21
|
//# sourceMappingURL=readonly.d.ts.map
|
package/dist/hooks/readonly.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { READ_ONLY_HOOK_ENV } from "./tools.js";
|
|
2
2
|
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
3
|
+
/** Session posture env for assist/research low-ceremony writes (#1802). */
|
|
4
|
+
export const ASSIST_SESSION_POSTURE_ENV = "DEFT_SESSION_POSTURE";
|
|
5
|
+
/** Explicit non-lifecycle assist markers (#3080 / #1802). Primary: ephemeral; aliases: docs, assist. */
|
|
6
|
+
const EPHEMERAL_ROLE_MARKERS = new Set(["ephemeral", "docs", "assist"]);
|
|
7
|
+
/** Assist/research session posture tokens (structural; not free-text NLP) (#1802). */
|
|
8
|
+
const ASSIST_POSTURE_MARKERS = new Set([
|
|
9
|
+
"assist",
|
|
10
|
+
"ephemeral",
|
|
11
|
+
"docs",
|
|
12
|
+
"research",
|
|
13
|
+
"research-notes",
|
|
14
|
+
"research_notes",
|
|
15
|
+
"scratch",
|
|
16
|
+
]);
|
|
3
17
|
function record(value) {
|
|
4
18
|
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
5
19
|
? value
|
|
@@ -28,6 +42,9 @@ function isReadOnlyCapability(value) {
|
|
|
28
42
|
const normalized = value.toLowerCase().replace(/[_\s-]/g, "");
|
|
29
43
|
return normalized === "readonly";
|
|
30
44
|
}
|
|
45
|
+
function normalizePostureToken(value) {
|
|
46
|
+
return value.toLowerCase().replace(/[_\s]/g, "-");
|
|
47
|
+
}
|
|
31
48
|
/** Best-effort read-only explore signal from host payload (#1185). */
|
|
32
49
|
export function hookReadOnlyFromPayload(payload) {
|
|
33
50
|
const input = record(payload);
|
|
@@ -80,4 +97,113 @@ export function isExploreSpawn(payload) {
|
|
|
80
97
|
fieldString(input, "workerRole");
|
|
81
98
|
return workerRole?.toLowerCase() === "explore";
|
|
82
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Assist / research / ephemeral session posture for low-ceremony scratch writes (#1802).
|
|
102
|
+
* Structural markers only (env, payload posture fields, worker_role/subagent_type).
|
|
103
|
+
* Absent marker → false (fail closed). Compose with allowlisted scratch path fence.
|
|
104
|
+
*/
|
|
105
|
+
export function isAssistPosture(payload, environ = process.env) {
|
|
106
|
+
const envPosture = (environ[ASSIST_SESSION_POSTURE_ENV] ?? "").trim();
|
|
107
|
+
if (envPosture.length > 0 && ASSIST_POSTURE_MARKERS.has(normalizePostureToken(envPosture))) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
// Explicit assist env override (truthy), separate from posture token names.
|
|
111
|
+
if (envTruthy(environ, "DEFT_HOOK_ASSIST"))
|
|
112
|
+
return true;
|
|
113
|
+
const input = record(payload);
|
|
114
|
+
if (input === null)
|
|
115
|
+
return false;
|
|
116
|
+
const toolInput = toolInputRecord(input) ?? input;
|
|
117
|
+
const posture = fieldString(toolInput, "posture") ??
|
|
118
|
+
fieldString(toolInput, "session_posture") ??
|
|
119
|
+
fieldString(toolInput, "sessionPosture") ??
|
|
120
|
+
fieldString(input, "posture") ??
|
|
121
|
+
fieldString(input, "session_posture") ??
|
|
122
|
+
fieldString(input, "sessionPosture");
|
|
123
|
+
if (posture !== null && ASSIST_POSTURE_MARKERS.has(normalizePostureToken(posture))) {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
// Shared taxonomy with #3080 ephemeral spawn markers.
|
|
127
|
+
if (isEphemeralSpawn(payload))
|
|
128
|
+
return true;
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Structural implement signals that win over an ephemeral marker (fail closed, #3080).
|
|
133
|
+
* Detected from envelope fields only — never free-text prompt heuristics.
|
|
134
|
+
*/
|
|
135
|
+
function hasImplementConflictSignal(toolInput, input) {
|
|
136
|
+
const driveTo = fieldString(toolInput, "drive_to") ??
|
|
137
|
+
fieldString(toolInput, "driveTo") ??
|
|
138
|
+
fieldString(toolInput, "drive-to") ??
|
|
139
|
+
fieldString(input, "drive_to") ??
|
|
140
|
+
fieldString(input, "driveTo") ??
|
|
141
|
+
fieldString(input, "drive-to");
|
|
142
|
+
if (driveTo !== null) {
|
|
143
|
+
const normalized = driveTo.toLowerCase().replace(/[_\s]/g, "-");
|
|
144
|
+
if (normalized === "merge-ready" ||
|
|
145
|
+
normalized === "merge" ||
|
|
146
|
+
normalized.startsWith("merge-") ||
|
|
147
|
+
normalized.includes("implement")) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// Non-ephemeral worker roles that imply product implementation / swarm leaf.
|
|
152
|
+
// Skip when this field is itself the ephemeral marker (conflict is via other fields).
|
|
153
|
+
const workerRole = fieldString(toolInput, "worker_role") ??
|
|
154
|
+
fieldString(toolInput, "workerRole") ??
|
|
155
|
+
fieldString(input, "worker_role") ??
|
|
156
|
+
fieldString(input, "workerRole");
|
|
157
|
+
if (workerRole !== null && !EPHEMERAL_ROLE_MARKERS.has(workerRole.toLowerCase())) {
|
|
158
|
+
const role = workerRole.toLowerCase().replace(/[_\s]/g, "-");
|
|
159
|
+
if (role === "leaf-implementation" ||
|
|
160
|
+
role === "implementation" ||
|
|
161
|
+
role === "implement" ||
|
|
162
|
+
role.includes("implement")) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const dispatchKind = fieldString(toolInput, "dispatch_kind") ??
|
|
167
|
+
fieldString(toolInput, "dispatchKind") ??
|
|
168
|
+
fieldString(input, "dispatch_kind") ??
|
|
169
|
+
fieldString(input, "dispatchKind");
|
|
170
|
+
if (dispatchKind !== null) {
|
|
171
|
+
const kind = dispatchKind.toLowerCase().replace(/[_\s]/g, "-");
|
|
172
|
+
if (kind === "swarm-cohort" || kind === "swarm-leaf" || kind.includes("implement")) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return false;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Ephemeral / assist / docs spawns skip active-xBRIEF implementation gates (#3080).
|
|
180
|
+
* True only with an explicit allowlisted marker. Absent marker → false (fail closed).
|
|
181
|
+
* When an ephemeral marker conflicts with implement envelope signals, implement wins.
|
|
182
|
+
*/
|
|
183
|
+
export function isEphemeralSpawn(payload) {
|
|
184
|
+
const input = record(payload);
|
|
185
|
+
if (input === null)
|
|
186
|
+
return false;
|
|
187
|
+
const toolInput = toolInputRecord(input) ?? input;
|
|
188
|
+
const subagentType = fieldString(toolInput, "subagent_type") ??
|
|
189
|
+
fieldString(toolInput, "subagentType") ??
|
|
190
|
+
fieldString(input, "subagent_type") ??
|
|
191
|
+
fieldString(input, "subagentType");
|
|
192
|
+
const workerRole = fieldString(toolInput, "worker_role") ??
|
|
193
|
+
fieldString(toolInput, "workerRole") ??
|
|
194
|
+
fieldString(input, "worker_role") ??
|
|
195
|
+
fieldString(input, "workerRole");
|
|
196
|
+
const marker = (workerRole !== null && EPHEMERAL_ROLE_MARKERS.has(workerRole.toLowerCase())
|
|
197
|
+
? workerRole.toLowerCase()
|
|
198
|
+
: null) ??
|
|
199
|
+
(subagentType !== null && EPHEMERAL_ROLE_MARKERS.has(subagentType.toLowerCase())
|
|
200
|
+
? subagentType.toLowerCase()
|
|
201
|
+
: null);
|
|
202
|
+
if (marker === null)
|
|
203
|
+
return false;
|
|
204
|
+
// Implement signals win over ephemeral markers (fail closed).
|
|
205
|
+
if (hasImplementConflictSignal(toolInput, input))
|
|
206
|
+
return false;
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
83
209
|
//# sourceMappingURL=readonly.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -24,8 +24,10 @@ export * as evalReport from "./eval/report.js";
|
|
|
24
24
|
export * as evalRun from "./eval/run.js";
|
|
25
25
|
export * as events from "./events/attribution-ledger.js";
|
|
26
26
|
export * from "./forward-coverage/evaluate.js";
|
|
27
|
+
export * as freshness from "./freshness/index.js";
|
|
27
28
|
export { ContainedWriteError, ContainedWriteErrorCode, type ContainedWriteInput, type ContainedWriteMode, type ContainedWriteResult, containedWrite, resolveContainedTarget, } from "./fs/contained-write.js";
|
|
28
29
|
export { assertDestinationNotSymlink, assertDirectoryNotSymlink, assertProjectionContained, assertWriteTargetSafe, PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, walkDirectoryRejectSymlinks, } from "./fs/projection-containment.js";
|
|
30
|
+
export * as handoffEvidence from "./handoff-evidence/index.js";
|
|
29
31
|
export * as intake from "./intake/index.js";
|
|
30
32
|
export * as layout from "./layout/index.js";
|
|
31
33
|
export * as legacyBridge from "./legacy-bridge/index.js";
|
package/dist/index.js
CHANGED
|
@@ -24,9 +24,11 @@ export * as evalReport from "./eval/report.js";
|
|
|
24
24
|
export * as evalRun from "./eval/run.js";
|
|
25
25
|
export * as events from "./events/attribution-ledger.js";
|
|
26
26
|
export * from "./forward-coverage/evaluate.js";
|
|
27
|
+
export * as freshness from "./freshness/index.js";
|
|
27
28
|
// #2951 Phase 1: contained-write API for product sinks.
|
|
28
29
|
export { ContainedWriteError, ContainedWriteErrorCode, containedWrite, resolveContainedTarget, } from "./fs/contained-write.js";
|
|
29
30
|
export { assertDestinationNotSymlink, assertDirectoryNotSymlink, assertProjectionContained, assertWriteTargetSafe, PROJECTION_CONTAINMENT_REFUSED_EXIT_CODE, ProjectionContainmentError, walkDirectoryRejectSymlinks, } from "./fs/projection-containment.js";
|
|
31
|
+
export * as handoffEvidence from "./handoff-evidence/index.js";
|
|
30
32
|
export * as intake from "./intake/index.js";
|
|
31
33
|
export * as layout from "./layout/index.js";
|
|
32
34
|
export * as legacyBridge from "./legacy-bridge/index.js";
|
|
@@ -6,7 +6,7 @@ export declare const DEFT_HOOK_COMMAND_MARKER = "deft-hook";
|
|
|
6
6
|
export declare const LEGACY_DEFT_HOOK_COMMAND_MARKER = "deft hook:dispatch";
|
|
7
7
|
export declare const AGENT_HOOK_PATHS: readonly [".claude/settings.json", ".grok/hooks/deft.json", ".cursor/hooks.json", ".codex/hooks.json"];
|
|
8
8
|
export type AgentHookPath = (typeof AGENT_HOOK_PATHS)[number];
|
|
9
|
-
export type AgentHookRegistrationStatus = "healthy" | "missing" | "drifted";
|
|
9
|
+
export type AgentHookRegistrationStatus = "healthy" | "disabled" | "missing" | "drifted";
|
|
10
10
|
/** Whether the host receives a compact/resume hook deposit (#2113). */
|
|
11
11
|
export type AgentHookCompactSupport = "deposited" | "unsupported";
|
|
12
12
|
export interface AgentHookInspection {
|
|
@@ -312,7 +312,10 @@ function hasNestedRegistration(config, host, options = {}) {
|
|
|
312
312
|
const sessionCommand = command(host, "session.start");
|
|
313
313
|
const toolCommand = command(host, "tool.before");
|
|
314
314
|
const compactCommand = command(host, "session.compact");
|
|
315
|
-
const base = session.some((entry) =>
|
|
315
|
+
const base = session.some((entry) => {
|
|
316
|
+
const group = object(entry);
|
|
317
|
+
return group?.matcher === undefined && nestedCommands(entry).includes(sessionCommand);
|
|
318
|
+
}) &&
|
|
316
319
|
preTool.some((entry) => {
|
|
317
320
|
const group = object(entry);
|
|
318
321
|
return (group?.matcher === DIRECT_WRITE_HOOK_MATCHER && nestedCommands(entry).includes(toolCommand));
|
|
@@ -409,7 +412,7 @@ export function inspectAgentHookDeposit(projectRoot, hostHooksPolicy = loadHostH
|
|
|
409
412
|
return {
|
|
410
413
|
host: definition.host,
|
|
411
414
|
path: definition.path,
|
|
412
|
-
status: "
|
|
415
|
+
status: "disabled",
|
|
413
416
|
compactSupport: definition.compactSupport,
|
|
414
417
|
detail: `plan.policy.hostHooks.${definition.host} is false — Directive hook deposit is skipped for this host.`,
|
|
415
418
|
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* scaffold, skills/githooks/#1430 neutralization, and Taskfile wiring.
|
|
6
6
|
* Refs #1942, #11, #1430.
|
|
7
7
|
*/
|
|
8
|
+
import { type AgentHookReadinessResult } from "../verify-env/agent-hook-readiness.js";
|
|
8
9
|
import { type LegacyLayoutDetection } from "./legacy-detect.js";
|
|
9
10
|
import { type InitDepositIo, writeConsumerGitHooks } from "./scaffold.js";
|
|
10
11
|
export interface InitDepositArgs {
|
|
@@ -28,11 +29,16 @@ export interface InitDepositSeams {
|
|
|
28
29
|
nowIso?: () => string;
|
|
29
30
|
gitHooks?: Parameters<typeof writeConsumerGitHooks>[3];
|
|
30
31
|
detectLegacy?: (projectDir: string) => LegacyLayoutDetection;
|
|
32
|
+
evaluateAgentHookReadiness?: (projectRoot: string) => AgentHookReadinessResult;
|
|
31
33
|
}
|
|
32
34
|
export declare function parseInitArgv(canonicalArgv: readonly string[], userArgv?: readonly string[]): InitDepositArgs;
|
|
33
35
|
export declare function userConfigDir(): string;
|
|
34
36
|
export declare function createUserConfigDir(io: InitDepositIo): string;
|
|
35
|
-
export declare function buildInstallSummaryJson(
|
|
37
|
+
export declare function buildInstallSummaryJson(input: {
|
|
38
|
+
result: InitDepositResult;
|
|
39
|
+
options: InitDepositArgs;
|
|
40
|
+
readiness: AgentHookReadinessResult | undefined;
|
|
41
|
+
}): Record<string, unknown>;
|
|
36
42
|
export declare function printNextSteps(result: InitDepositResult, io: InitDepositIo): void;
|
|
37
43
|
/**
|
|
38
44
|
* Ensure a minimal render-ready PROJECT-DEFINITION exists after lifecycle dirs
|
|
@@ -13,8 +13,10 @@ import { copyTree } from "../deposit/copy-tree.js";
|
|
|
13
13
|
import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
14
14
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
15
15
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
16
|
+
import { stampLiveGeneration } from "../freshness/generation.js";
|
|
16
17
|
import { renderProjectDefinition } from "../render/project-render.js";
|
|
17
18
|
import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
|
|
19
|
+
import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
|
|
18
20
|
import { removeStaleMigratedFrameworkNarrative } from "../xbrief-migrate/migrate-project.js";
|
|
19
21
|
import { writeAgentHookDeposit } from "./agent-hooks.js";
|
|
20
22
|
import { ensureInitGitignoreLines, reconstituteDepositFromContent } from "./gitignore.js";
|
|
@@ -81,9 +83,12 @@ function readContentVersion(contentRoot, readVersion = readCorePackageVersion) {
|
|
|
81
83
|
}
|
|
82
84
|
return readVersion();
|
|
83
85
|
}
|
|
84
|
-
export function buildInstallSummaryJson(
|
|
86
|
+
export function buildInstallSummaryJson(input) {
|
|
87
|
+
const { result, options, readiness } = input;
|
|
85
88
|
return {
|
|
86
|
-
success: true,
|
|
89
|
+
success: readiness ? readiness.code === 0 : true,
|
|
90
|
+
deposit_completed: true,
|
|
91
|
+
...(readiness ? { agent_hook_readiness: agentHookReadinessJson(readiness) } : {}),
|
|
87
92
|
action: "install",
|
|
88
93
|
version: readCorePackageVersion(),
|
|
89
94
|
project_dir: result.projectDir,
|
|
@@ -210,6 +215,14 @@ export async function runInitDeposit(args, io, seams = {}) {
|
|
|
210
215
|
includeTaskfile: taskfileWired,
|
|
211
216
|
});
|
|
212
217
|
printCommitGuidance(io, stagePaths, staged);
|
|
218
|
+
// #3117: stamp live generation only after required init projections succeed.
|
|
219
|
+
// Stamping earlier would advance authority for a failed/partial init (Greptile).
|
|
220
|
+
stampLiveGeneration(projectDir, {
|
|
221
|
+
contentVersion: version,
|
|
222
|
+
stampedBy: "directive-init",
|
|
223
|
+
increment: true,
|
|
224
|
+
nowIso: nowIso(),
|
|
225
|
+
});
|
|
213
226
|
return {
|
|
214
227
|
projectDir,
|
|
215
228
|
deftDir,
|
|
@@ -234,14 +247,21 @@ export async function runInitDepositCli(options) {
|
|
|
234
247
|
};
|
|
235
248
|
try {
|
|
236
249
|
const result = await runInitDeposit(options, io, options.seams);
|
|
250
|
+
const readiness = evaluateAgentHookReadinessSafely(result.projectDir, options.seams?.evaluateAgentHookReadiness ?? evaluateAgentHookReadiness);
|
|
237
251
|
if (options.jsonOut) {
|
|
238
|
-
options.writeOut(`${JSON.stringify(buildInstallSummaryJson(result, options), null, 2)}\n`);
|
|
252
|
+
options.writeOut(`${JSON.stringify(buildInstallSummaryJson({ result, options, readiness }), null, 2)}\n`);
|
|
239
253
|
printNextSteps(result, { printf: options.writeErr });
|
|
240
254
|
}
|
|
241
255
|
else {
|
|
242
256
|
printNextSteps(result, io);
|
|
243
257
|
}
|
|
244
|
-
|
|
258
|
+
const readinessOut = options.jsonOut
|
|
259
|
+
? options.writeErr
|
|
260
|
+
: readiness.stream === "stderr"
|
|
261
|
+
? options.writeErr
|
|
262
|
+
: options.writeOut;
|
|
263
|
+
readinessOut(`\n${readiness.message}\n`);
|
|
264
|
+
return readiness.code;
|
|
245
265
|
}
|
|
246
266
|
catch (cause) {
|
|
247
267
|
if (cause instanceof LegacyLayoutRefusedError) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ResolutionFacts, ResolutionPlan } from "@deftai/directive-types";
|
|
11
11
|
import { type ClassifySeams, type EngineInstallRunner, type EngineResolution, type LadderFacts, type ReprojectRunner } from "../resolution/index.js";
|
|
12
|
+
import { type AgentHookReadinessResult } from "../verify-env/agent-hook-readiness.js";
|
|
12
13
|
import { type GitLsFiles } from "./gitignore.js";
|
|
13
14
|
import { type InitDepositArgs } from "./init-deposit.js";
|
|
14
15
|
import { type LegacyLayoutDetection } from "./legacy-detect.js";
|
|
@@ -49,6 +50,8 @@ export interface RefreshDepositSeams {
|
|
|
49
50
|
gitHooks?: GitHooksSeams;
|
|
50
51
|
/** #2822: optional seam for trusted-org policy force-on migration. */
|
|
51
52
|
runOrgForceOn?: (projectRoot: string) => void;
|
|
53
|
+
/** Post-deposit functional readiness gate (#3100). */
|
|
54
|
+
evaluateAgentHookReadiness?: (projectRoot: string) => AgentHookReadinessResult;
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
54
57
|
* The four states `directive update` classifies an EXISTING install into BEFORE
|
|
@@ -123,7 +126,12 @@ export interface RefreshSideEffects {
|
|
|
123
126
|
export declare function frameworkRefreshSideEffects(projectDir: string, options?: FrameworkRefreshSideEffectsOptions): RefreshSideEffects;
|
|
124
127
|
export declare function printRefreshSideEffects(io: InitDepositIo, effects: RefreshSideEffects): void;
|
|
125
128
|
export declare function buildVersionSkewNotice(engineVersion: string, contentVersion: string, previousDepositVersion: string | null): string | null;
|
|
126
|
-
export declare function buildUpdateSummaryJson(
|
|
129
|
+
export declare function buildUpdateSummaryJson(input: {
|
|
130
|
+
result: RefreshDepositResult;
|
|
131
|
+
options: RefreshDepositArgs;
|
|
132
|
+
updateState: UpdateState | undefined;
|
|
133
|
+
readiness: AgentHookReadinessResult | undefined;
|
|
134
|
+
}): Record<string, unknown>;
|
|
127
135
|
export declare function printUpdateComplete(result: RefreshDepositResult, io: InitDepositIo, updateState?: UpdateState): void;
|
|
128
136
|
export declare function runRefreshDeposit(args: RefreshDepositArgs, io: InitDepositIo, seams?: RefreshDepositSeams): Promise<RefreshDepositResult>;
|
|
129
137
|
export interface RunRefreshDepositCliOptions extends RefreshDepositArgs {
|
|
@@ -17,12 +17,14 @@ import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
|
17
17
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
18
18
|
import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
19
19
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
20
|
+
import { readLiveGeneration, stampLiveGeneration } from "../freshness/generation.js";
|
|
20
21
|
import { resolveLifecycleRoot } from "../layout/resolve.js";
|
|
21
22
|
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";
|
|
22
23
|
import { runOrgForceOnMigration } from "../policy/org-force-on-migration.js";
|
|
23
24
|
import { checkLocalEngineIntegrity, classify, ENGINE_PACKAGE, plan, renderGlobalInstall, resolveEngine, } from "../resolution/index.js";
|
|
24
25
|
import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
|
|
25
26
|
import { gitPorcelain } from "../story-ready/git.js";
|
|
27
|
+
import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
|
|
26
28
|
import { removeStaleMigratedFrameworkNarrative } from "../xbrief-migrate/migrate-project.js";
|
|
27
29
|
import { writeAgentHookDeposit } from "./agent-hooks.js";
|
|
28
30
|
import { ensureInitGitignoreLines, isDepositTrackedInGit } from "./gitignore.js";
|
|
@@ -332,9 +334,12 @@ export function buildVersionSkewNotice(engineVersion, contentVersion, previousDe
|
|
|
332
334
|
}
|
|
333
335
|
return null;
|
|
334
336
|
}
|
|
335
|
-
export function buildUpdateSummaryJson(
|
|
337
|
+
export function buildUpdateSummaryJson(input) {
|
|
338
|
+
const { result, options, updateState, readiness } = input;
|
|
336
339
|
return {
|
|
337
|
-
success: true,
|
|
340
|
+
success: readiness ? readiness.code === 0 : true,
|
|
341
|
+
deposit_completed: true,
|
|
342
|
+
...(readiness ? { agent_hook_readiness: agentHookReadinessJson(readiness) } : {}),
|
|
338
343
|
action: "upgrade",
|
|
339
344
|
...(updateState ? { update_state: updateState } : {}),
|
|
340
345
|
version: result.engineVersion,
|
|
@@ -416,6 +421,27 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
416
421
|
// VERSION already matches (e.g. pre-#2804 additive deposits). Does not re-stamp.
|
|
417
422
|
await reconcileDepositToContentPackage(deftDir, contentRoot, io);
|
|
418
423
|
migrateLegacyInstallManifest(projectDir, join(deftDir, "VERSION"));
|
|
424
|
+
// #3117: ensure a readable live generation token exists without advancing
|
|
425
|
+
// when the payload did not swap (already-current). stampLiveGeneration is a
|
|
426
|
+
// no-op write when the token already matches (keeps git clean under #2118).
|
|
427
|
+
// If generation is missing or behind VERSION (e.g. prior stamp failed after
|
|
428
|
+
// VERSION rewrite), fail closed — do not report success with a stale token.
|
|
429
|
+
const priorGen = readLiveGeneration(projectDir);
|
|
430
|
+
const generationMatches = priorGen !== null &&
|
|
431
|
+
normalizeVersion(priorGen.contentVersion) === normalizeVersion(contentVersion);
|
|
432
|
+
try {
|
|
433
|
+
stampLiveGeneration(projectDir, {
|
|
434
|
+
contentVersion,
|
|
435
|
+
stampedBy: "directive-update",
|
|
436
|
+
increment: false,
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
catch (err) {
|
|
440
|
+
if (!generationMatches) {
|
|
441
|
+
throw err;
|
|
442
|
+
}
|
|
443
|
+
// Token already matches content; ensure write failure is non-fatal noise.
|
|
444
|
+
}
|
|
419
445
|
}
|
|
420
446
|
else {
|
|
421
447
|
// Full-tree replace (or injected seam). Additive copy is no longer the default.
|
|
@@ -426,12 +452,13 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
426
452
|
// additive seams. Throws => no VERSION rewrite (refuse stamp until clean).
|
|
427
453
|
await reconcileDepositToContentPackage(deftDir, contentRoot, io);
|
|
428
454
|
const nowIso = seams.nowIso ?? (() => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"));
|
|
455
|
+
const stampedAt = nowIso();
|
|
429
456
|
const manifestFields = {
|
|
430
457
|
ref: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
431
458
|
sha: "content-package",
|
|
432
459
|
tag: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
433
460
|
installRoot: CANONICAL_INSTALL_ROOT,
|
|
434
|
-
fetchedAt:
|
|
461
|
+
fetchedAt: stampedAt,
|
|
435
462
|
fetchedBy: "directive-update",
|
|
436
463
|
...(previousManagedBy ? { managedBy: previousManagedBy } : {}),
|
|
437
464
|
};
|
|
@@ -440,6 +467,15 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
440
467
|
// .deft/core/VERSION has been rewritten (folded in from install-upgrade so no
|
|
441
468
|
// manifest behavior is lost by the redirect). Best-effort; never fatal.
|
|
442
469
|
migrateLegacyInstallManifest(projectDir, writtenManifestPath);
|
|
470
|
+
// #3117: monotonic live generation MUST advance after a successful payload
|
|
471
|
+
// swap. Suppressing stamp failure would leave a prior bound/live match
|
|
472
|
+
// reporting `current` while the on-disk payload already changed (Greptile P1).
|
|
473
|
+
stampLiveGeneration(projectDir, {
|
|
474
|
+
contentVersion,
|
|
475
|
+
stampedBy: "directive-update",
|
|
476
|
+
increment: true,
|
|
477
|
+
nowIso: stampedAt,
|
|
478
|
+
});
|
|
443
479
|
}
|
|
444
480
|
// #2595: payload freshness and consumer derivative freshness are independent.
|
|
445
481
|
// Always repair these cheap projections, including on the #2118 no-op path.
|
|
@@ -653,17 +689,24 @@ export async function runRefreshDepositCli(options) {
|
|
|
653
689
|
}
|
|
654
690
|
try {
|
|
655
691
|
const result = await runRefreshDeposit(options, io, options.seams);
|
|
692
|
+
const readiness = evaluateAgentHookReadinessSafely(result.projectDir, options.seams?.evaluateAgentHookReadiness ?? evaluateAgentHookReadiness);
|
|
656
693
|
const state = result.alreadyCurrent
|
|
657
694
|
? "current"
|
|
658
695
|
: classification?.state;
|
|
659
696
|
if (options.jsonOut) {
|
|
660
|
-
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson(result, options, state), null, 2)}\n`);
|
|
697
|
+
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson({ result, options, updateState: state, readiness }), null, 2)}\n`);
|
|
661
698
|
printUpdateComplete(result, { printf: options.writeErr }, state);
|
|
662
699
|
}
|
|
663
700
|
else {
|
|
664
701
|
printUpdateComplete(result, io, state);
|
|
665
702
|
}
|
|
666
|
-
|
|
703
|
+
const readinessOut = options.jsonOut
|
|
704
|
+
? options.writeErr
|
|
705
|
+
: readiness.stream === "stderr"
|
|
706
|
+
? options.writeErr
|
|
707
|
+
: options.writeOut;
|
|
708
|
+
readinessOut(`\n${readiness.message}\n`);
|
|
709
|
+
return readiness.code;
|
|
667
710
|
}
|
|
668
711
|
catch (cause) {
|
|
669
712
|
if (cause instanceof LegacyLayoutRefusedError) {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type ScanFlag } from "../cache/scanner.js";
|
|
2
|
+
import { type IssueComment as ShapeIssueComment } from "../umbrella-current-shape/index.js";
|
|
2
3
|
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY } from "../xbrief-migrate/constants.js";
|
|
3
4
|
import { type ScmCallFn } from "./reconcile-issues.js";
|
|
5
|
+
/** Reference type pointing at the canonical current-shape comment permalink (#1870). */
|
|
6
|
+
export declare const CURRENT_SHAPE_REF_TYPE: "x-xbrief/current-shape";
|
|
4
7
|
export declare const INGEST_STATUSES: readonly ["proposed", "pending", "active"];
|
|
5
8
|
export type IngestStatus = (typeof INGEST_STATUSES)[number];
|
|
6
9
|
/**
|
|
@@ -21,9 +24,26 @@ export interface IssueComment {
|
|
|
21
24
|
readonly login?: string;
|
|
22
25
|
};
|
|
23
26
|
readonly created_at?: string;
|
|
27
|
+
readonly updated_at?: string;
|
|
28
|
+
readonly html_url?: string;
|
|
29
|
+
readonly author_association?: string;
|
|
24
30
|
}
|
|
25
31
|
/** Enriched on issues after `fetchIssue` when the comment thread is non-empty (#2143). */
|
|
26
32
|
export declare const ISSUE_COMMENT_THREAD_KEY: "issueCommentThread";
|
|
33
|
+
/**
|
|
34
|
+
* Map ingest/REST comment rows onto the umbrella-current-shape selector input
|
|
35
|
+
* so #1152 pass-N + #2307 maintainer provenance rules apply (#1870).
|
|
36
|
+
*/
|
|
37
|
+
export declare function mapIngestCommentsToShapeComments(comments: readonly IssueComment[]): ShapeIssueComment[];
|
|
38
|
+
/**
|
|
39
|
+
* Select the canonical current-shape comment from an ingest comment thread.
|
|
40
|
+
* Returns null when none is maintainer-authored / header-matched.
|
|
41
|
+
*/
|
|
42
|
+
export declare function selectIngestCurrentShape(comments: readonly IssueComment[]): (ShapeIssueComment & {
|
|
43
|
+
pass: number;
|
|
44
|
+
}) | null;
|
|
45
|
+
/** Count maintainer current-shape comments (lint surface for !=1). */
|
|
46
|
+
export declare function countIngestCurrentShapeComments(comments: readonly IssueComment[]): number;
|
|
27
47
|
export declare function bodyControlCharacterLabels(body: string): string[];
|
|
28
48
|
export declare function warnBodyControlCharacters(number: number, body: string): void;
|
|
29
49
|
export declare function extractPlanItems(body: string): Record<string, string>[];
|
|
@@ -7,11 +7,14 @@ import { hasArtifactSuffix, resolveLifecycleRoot } from "../layout/resolve.js";
|
|
|
7
7
|
import { call } from "../scm/call.js";
|
|
8
8
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
9
9
|
import { resolveProjectRepo } from "../slice/project-context.js";
|
|
10
|
+
import { countMaintainerCurrentShapeComments, mapIssueCommentEntry, RAW_ISSUE_COMMENTS_KEY, selectCurrentShapeComment, } from "../umbrella-current-shape/index.js";
|
|
10
11
|
import { slugify, TODAY } from "../vbrief-build/build.js";
|
|
11
12
|
import { EMITTED_VBRIEF_VERSION } from "../vbrief-build/constants.js";
|
|
12
13
|
import { LEGACY_ARTIFACT_SUFFIX, LEGACY_INFO_ROOT_KEY, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, MIGRATED_INFO_ROOT_KEY, VBRIEF_VERSION, } from "../xbrief-migrate/constants.js";
|
|
13
14
|
import { findAcHeading, parseCheckboxItems, parseListItems, sliceAcSection, stripCodeBlocks, stripFencedCodeBlocks, } from "./markdown-scanners.js";
|
|
14
15
|
import { detectRepo, extractReferencesFromVbrief, fetchOpenIssues, GITHUB_ISSUE_REF_TYPES, LIFECYCLE_FOLDERS, parseIssueNumber, } from "./reconcile-issues.js";
|
|
16
|
+
/** Reference type pointing at the canonical current-shape comment permalink (#1870). */
|
|
17
|
+
export const CURRENT_SHAPE_REF_TYPE = "x-xbrief/current-shape";
|
|
15
18
|
export const INGEST_STATUSES = ["proposed", "pending", "active"];
|
|
16
19
|
/**
|
|
17
20
|
* Thrown when the quarantine scanner hard-fails (credential-shaped content) on
|
|
@@ -36,6 +39,31 @@ export class ScannerHardFailError extends Error {
|
|
|
36
39
|
}
|
|
37
40
|
/** Enriched on issues after `fetchIssue` when the comment thread is non-empty (#2143). */
|
|
38
41
|
export const ISSUE_COMMENT_THREAD_KEY = "issueCommentThread";
|
|
42
|
+
/**
|
|
43
|
+
* Map ingest/REST comment rows onto the umbrella-current-shape selector input
|
|
44
|
+
* so #1152 pass-N + #2307 maintainer provenance rules apply (#1870).
|
|
45
|
+
*/
|
|
46
|
+
export function mapIngestCommentsToShapeComments(comments) {
|
|
47
|
+
const out = [];
|
|
48
|
+
for (const comment of comments) {
|
|
49
|
+
const mapped = mapIssueCommentEntry(comment);
|
|
50
|
+
if (mapped !== null) {
|
|
51
|
+
out.push(mapped);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Select the canonical current-shape comment from an ingest comment thread.
|
|
58
|
+
* Returns null when none is maintainer-authored / header-matched.
|
|
59
|
+
*/
|
|
60
|
+
export function selectIngestCurrentShape(comments) {
|
|
61
|
+
return selectCurrentShapeComment(mapIngestCommentsToShapeComments(comments));
|
|
62
|
+
}
|
|
63
|
+
/** Count maintainer current-shape comments (lint surface for !=1). */
|
|
64
|
+
export function countIngestCurrentShapeComments(comments) {
|
|
65
|
+
return countMaintainerCurrentShapeComments(mapIngestCommentsToShapeComments(comments));
|
|
66
|
+
}
|
|
39
67
|
const STATUS_MAP = {
|
|
40
68
|
proposed: ["proposed", "proposed"],
|
|
41
69
|
pending: ["pending", "pending"],
|
|
@@ -326,6 +354,11 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
326
354
|
const bodyStr = typeof bodyRaw === "string" && bodyRaw.length > 0 ? bodyRaw : "";
|
|
327
355
|
const commentThread = issueCommentThread(issue);
|
|
328
356
|
const overviewSource = commentThread.length > 0 ? composeOverviewWithComments(bodyStr, commentThread) : bodyStr;
|
|
357
|
+
// #1870 / #1152: materialize the canonical Current shape comment as its own
|
|
358
|
+
// narrative so agents cannot plan umbrellas from the stale body alone. Prefer
|
|
359
|
+
// the same selector as `task umbrella:current-shape` (highest pass-N,
|
|
360
|
+
// maintainer-authored only — #2307).
|
|
361
|
+
const currentShape = selectIngestCurrentShape(commentThread);
|
|
329
362
|
const labelsRaw = issue.labels;
|
|
330
363
|
const labelNames = [];
|
|
331
364
|
if (Array.isArray(labelsRaw)) {
|
|
@@ -366,6 +399,10 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
366
399
|
}
|
|
367
400
|
narratives.Overview = scanResult.transformed_content;
|
|
368
401
|
}
|
|
402
|
+
if (currentShape !== null) {
|
|
403
|
+
// Scan shape body under the same fail-closed contract as Overview (#2306).
|
|
404
|
+
narratives.CurrentShape = scanUntrustedIngestText(number, currentShape.body);
|
|
405
|
+
}
|
|
369
406
|
if (labelNames.length > 0) {
|
|
370
407
|
narratives.Labels = labelNames.join(", ");
|
|
371
408
|
}
|
|
@@ -392,6 +429,16 @@ export function buildIssueVbrief(issue, status, repoUrl, options = {}) {
|
|
|
392
429
|
title: `Issue #${number}: ${title}`,
|
|
393
430
|
},
|
|
394
431
|
];
|
|
432
|
+
if (currentShape !== null) {
|
|
433
|
+
const shapeUri = currentShape.htmlUrl.length > 0
|
|
434
|
+
? currentShape.htmlUrl
|
|
435
|
+
: `${url}#issuecomment-${currentShape.id}`;
|
|
436
|
+
references.push({
|
|
437
|
+
uri: shapeUri,
|
|
438
|
+
type: CURRENT_SHAPE_REF_TYPE,
|
|
439
|
+
title: `Current shape (pass-${currentShape.pass}) for #${number}`,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
395
442
|
if (overviewSource.length > 0 && repoUrl.length > 0) {
|
|
396
443
|
references.push(...extractCrossRefs(overviewSource, repoUrl, new Set([number])));
|
|
397
444
|
}
|
|
@@ -446,6 +493,12 @@ export function fetchFromCache(repo, number, options = {}) {
|
|
|
446
493
|
// the quarantine transform. When content.md is absent (e.g. a credential
|
|
447
494
|
// hard-fail deleted it), the raw body falls through and is re-scanned in
|
|
448
495
|
// buildIssueVbrief.
|
|
496
|
+
//
|
|
497
|
+
// #1870 note: content.md may also include the appended Canonical current
|
|
498
|
+
// shape section. That is intentional for agent-facing cache reads. When
|
|
499
|
+
// raw.comments is present we also restore the comment thread so
|
|
500
|
+
// narratives.CurrentShape + the current-shape reference can be materialised
|
|
501
|
+
// offline (without a live comments fetch).
|
|
449
502
|
if (result.contentPath !== null) {
|
|
450
503
|
try {
|
|
451
504
|
// #2314: content.md is `renderContent`-prefixed with a `# #<n>: <title>`
|
|
@@ -458,6 +511,11 @@ export function fetchFromCache(repo, number, options = {}) {
|
|
|
458
511
|
// fall back to the raw body (re-scanned downstream)
|
|
459
512
|
}
|
|
460
513
|
}
|
|
514
|
+
// #1870: restore comment thread from cache raw so CurrentShape survives
|
|
515
|
+
// offline / no-live-gh paths (fetchIssue still prefers live comments).
|
|
516
|
+
if (!issueCommentsAlreadyFetched(issue) && Array.isArray(issue[RAW_ISSUE_COMMENTS_KEY])) {
|
|
517
|
+
return attachIssueCommentThread(issue, issue[RAW_ISSUE_COMMENTS_KEY]);
|
|
518
|
+
}
|
|
461
519
|
return issue;
|
|
462
520
|
}
|
|
463
521
|
catch {
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from "./host-hooks.js";
|
|
|
9
9
|
export * from "./host-slash-commands.js";
|
|
10
10
|
export * from "./hotfix-criteria.js";
|
|
11
11
|
export * from "./intent-ceiling.js";
|
|
12
|
+
export * from "./min-greptile-confidence.js";
|
|
12
13
|
export * from "./no-deft-directive.js";
|
|
13
14
|
export * from "./org-force-on-migration.js";
|
|
14
15
|
export * from "./plan-extensions.js";
|
|
@@ -30,12 +31,15 @@ export declare const FIELD_TRIAGE_SCOPE_IGNORES = "plan.policy.triageScopeIgnore
|
|
|
30
31
|
export declare const FIELD_TRIAGE_RANKING_LABELS = "plan.policy.triageRankingLabels";
|
|
31
32
|
export declare const FIELD_TRIAGE_AUTO_CLASSIFY = "plan.policy.triageAutoClassify";
|
|
32
33
|
export declare const FIELD_TRIAGE_HOLD_MARKERS = "plan.policy.triageHoldMarkers";
|
|
34
|
+
export declare const FIELD_TRIAGE_LABEL_MIRROR = "plan.policy.triageLabelMirror";
|
|
33
35
|
export declare const FIELD_SWARM_SUBAGENT_BACKEND = "plan.policy.swarmSubagentBackend";
|
|
34
36
|
export declare const DEFAULT_SESSION_RITUAL_STALENESS_HOURS = 4;
|
|
35
37
|
export declare const DEFAULT_TRIAGE_SCOPE_VALUE: readonly Record<string, unknown>[];
|
|
36
38
|
export declare const DEFAULT_TRIAGE_SCOPE_IGNORES_VALUE: readonly unknown[];
|
|
37
39
|
export declare const DEFAULT_TRIAGE_RANKING_LABELS_VALUE: readonly string[];
|
|
38
40
|
export declare const DEFAULT_TRIAGE_AUTO_CLASSIFY_VALUE: readonly unknown[];
|
|
41
|
+
/** Default for #1423 Tier-1 label mirror: enabled with `triaged` idempotency marker. */
|
|
42
|
+
export declare const DEFAULT_TRIAGE_LABEL_MIRROR_VALUE: Readonly<Record<string, unknown>>;
|
|
39
43
|
export declare const KNOWN_SUBAGENT_BACKEND_IDS: Set<string>;
|
|
40
44
|
export interface PolicyField {
|
|
41
45
|
readonly name: string;
|