@deftai/directive-core 0.96.0 → 0.97.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.
Files changed (73) hide show
  1. package/dist/cache/archive.js +10 -4
  2. package/dist/check/gate-lists.js +8 -0
  3. package/dist/consumer-check-contract/evaluate.d.ts +124 -0
  4. package/dist/consumer-check-contract/evaluate.js +699 -0
  5. package/dist/consumer-check-contract/index.d.ts +5 -0
  6. package/dist/consumer-check-contract/index.js +5 -0
  7. package/dist/delivery-attempt/disk-begin.d.ts +51 -0
  8. package/dist/delivery-attempt/disk-begin.js +68 -0
  9. package/dist/delivery-attempt/evaluate.d.ts +26 -0
  10. package/dist/delivery-attempt/evaluate.js +443 -0
  11. package/dist/delivery-attempt/fingerprint.d.ts +32 -0
  12. package/dist/delivery-attempt/fingerprint.js +100 -0
  13. package/dist/delivery-attempt/handoff.d.ts +25 -0
  14. package/dist/delivery-attempt/handoff.js +102 -0
  15. package/dist/delivery-attempt/index.d.ts +17 -0
  16. package/dist/delivery-attempt/index.js +17 -0
  17. package/dist/delivery-attempt/ledger.d.ts +169 -0
  18. package/dist/delivery-attempt/ledger.js +758 -0
  19. package/dist/delivery-attempt/material-delta.d.ts +38 -0
  20. package/dist/delivery-attempt/material-delta.js +126 -0
  21. package/dist/delivery-attempt/types.d.ts +210 -0
  22. package/dist/delivery-attempt/types.js +77 -0
  23. package/dist/doctor/index.d.ts +1 -0
  24. package/dist/doctor/index.js +1 -0
  25. package/dist/doctor/main.js +12 -0
  26. package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
  27. package/dist/doctor/openclaw-soft-rebind.js +164 -0
  28. package/dist/hooks/dispatcher.d.ts +2 -1
  29. package/dist/hooks/dispatcher.js +63 -9
  30. package/dist/index.d.ts +4 -0
  31. package/dist/index.js +4 -0
  32. package/dist/init-deposit/gitignore.js +7 -0
  33. package/dist/init-deposit/init-deposit.js +5 -0
  34. package/dist/init-deposit/refresh.js +3 -0
  35. package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
  36. package/dist/pr-merge-readiness/ci-gate.js +191 -24
  37. package/dist/pr-merge-readiness/compute.js +10 -1
  38. package/dist/pr-merge-readiness/index.d.ts +2 -1
  39. package/dist/pr-merge-readiness/index.js +2 -1
  40. package/dist/pr-merge-readiness/output.js +14 -0
  41. package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
  42. package/dist/pr-merge-readiness/platform-status.js +49 -0
  43. package/dist/pr-watch/constants.d.ts +10 -0
  44. package/dist/pr-watch/constants.js +12 -1
  45. package/dist/pr-watch/main.js +16 -1
  46. package/dist/pr-watch/probe.js +13 -1
  47. package/dist/pr-watch/types.d.ts +3 -2
  48. package/dist/pr-watch/watch.js +14 -7
  49. package/dist/scope-provenance/digest.d.ts +67 -0
  50. package/dist/scope-provenance/digest.js +188 -0
  51. package/dist/scope-provenance/evaluate.d.ts +82 -0
  52. package/dist/scope-provenance/evaluate.js +528 -0
  53. package/dist/scope-provenance/index.d.ts +6 -0
  54. package/dist/scope-provenance/index.js +6 -0
  55. package/dist/session/compact-ritual.d.ts +96 -0
  56. package/dist/session/compact-ritual.js +237 -0
  57. package/dist/session/compact-ritual.spec.d.ts +2 -0
  58. package/dist/session/compact-ritual.spec.js +21 -0
  59. package/dist/session/index.d.ts +2 -0
  60. package/dist/session/index.js +2 -0
  61. package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
  62. package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
  63. package/dist/test-boundary/evaluate.d.ts +54 -0
  64. package/dist/test-boundary/evaluate.js +368 -0
  65. package/dist/test-boundary/index.d.ts +6 -0
  66. package/dist/test-boundary/index.js +6 -0
  67. package/dist/test-boundary/policy.d.ts +52 -0
  68. package/dist/test-boundary/policy.js +182 -0
  69. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  70. package/dist/triage/bootstrap/gitignore.js +15 -1
  71. package/dist/vbrief-activate/activate.js +22 -6
  72. package/dist/xbrief/styles.js +33 -17
  73. package/package.json +7 -3
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Doctor check + --fix wire for OpenClaw soft AGENTS re-bind skill (#3171).
3
+ *
4
+ * Complements always-pins (#3001/#3008) and L2 product commands (#3064). This
5
+ * skill is the required durable soft post-amnesia surface for OpenClaw — not
6
+ * claimed via file-host PreCompact hooks alone.
7
+ */
8
+ import { existsSync, readFileSync, statSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { formatOpenClawSoftRebindSkillMarkdown, isManagedOpenClawSoftRebindSkill, OPENCLAW_SOFT_REBIND_SKILL_ID, } from "../session/compact-ritual.js";
12
+ import { assessOpenClawSoftRebindSkill, depositOpenClawSoftRebindSkill, } from "../session/openclaw-soft-rebind-deposit.js";
13
+ import { detectOpenClaw, listInScopeSkillsDirs } from "./openclaw-skills.js";
14
+ /** Stable doctor check id for JSON findings. */
15
+ export const OPENCLAW_SOFT_REBIND_CHECK = "openclaw-soft-agents-rebind";
16
+ const DOC_OPENCLAW_HOST = "docs/openclaw-agent-host.md";
17
+ const REMEDIATION_FIX = "deft doctor --fix";
18
+ function defaultIsDir(path) {
19
+ try {
20
+ return statSync(path).isDirectory();
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ function assessGaps(skillsDirs) {
27
+ const expected = formatOpenClawSoftRebindSkillMarkdown();
28
+ const missing = [];
29
+ const stale = [];
30
+ let present = 0;
31
+ for (const skillsDir of skillsDirs) {
32
+ const path = join(skillsDir, OPENCLAW_SOFT_REBIND_SKILL_ID, "SKILL.md");
33
+ if (!existsSync(path)) {
34
+ missing.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
35
+ continue;
36
+ }
37
+ let raw;
38
+ try {
39
+ raw = readFileSync(path, "utf8");
40
+ }
41
+ catch {
42
+ missing.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
43
+ continue;
44
+ }
45
+ // Unmanaged content at the required slug is incomplete health, not pass
46
+ // (Greptile P1 #3171). Doctor --fix overwrites via deposit.
47
+ if (!isManagedOpenClawSoftRebindSkill(raw) || raw !== expected) {
48
+ stale.push(`${skillsDir}/${OPENCLAW_SOFT_REBIND_SKILL_ID}`);
49
+ continue;
50
+ }
51
+ present += 1;
52
+ }
53
+ return { missing, stale, present };
54
+ }
55
+ /**
56
+ * Doctor check: OpenClaw soft AGENTS re-bind skill present and current.
57
+ * No-ops when OpenClaw is not detected. Fix mode deposits managed skill.
58
+ */
59
+ export function runOpenClawSoftRebindCheck(sink, addFinding, options) {
60
+ const seams = options.seams ?? {};
61
+ const env = seams.openclawEnv ?? process.env;
62
+ const homeDirFn = seams.openclawHomeDir ?? (() => homedir());
63
+ const isDir = seams.isDir ?? defaultIsDir;
64
+ const detect = detectOpenClaw(env, { homeDir: homeDirFn(), isDir });
65
+ if (!detect.detected) {
66
+ sink.info(`${OPENCLAW_SOFT_REBIND_CHECK}: skip -- OpenClaw not detected`);
67
+ addFinding({
68
+ severity: "skip",
69
+ message: "OpenClaw not detected",
70
+ check: OPENCLAW_SOFT_REBIND_CHECK,
71
+ status: "skip",
72
+ reason: "openclaw-not-detected",
73
+ });
74
+ return null;
75
+ }
76
+ const skillsDirs = listInScopeSkillsDirs(detect.stateDir, options.allAgents, {
77
+ isDir,
78
+ });
79
+ const gaps = assessGaps(skillsDirs);
80
+ if (gaps.missing.length === 0 && gaps.stale.length === 0) {
81
+ const scope = options.allAgents ? "main + workspace-* seats" : "main workspace";
82
+ const message = `${OPENCLAW_SOFT_REBIND_CHECK}: soft AGENTS re-bind skill present ` +
83
+ `(${OPENCLAW_SOFT_REBIND_SKILL_ID}) in ${scope}`;
84
+ sink.success(message);
85
+ addFinding({
86
+ severity: "skip",
87
+ message,
88
+ check: OPENCLAW_SOFT_REBIND_CHECK,
89
+ status: "present",
90
+ skill: OPENCLAW_SOFT_REBIND_SKILL_ID,
91
+ skills_dirs: skillsDirs,
92
+ });
93
+ return null;
94
+ }
95
+ if (options.fixMode) {
96
+ const depositResult = depositOpenClawSoftRebindSkill({
97
+ env,
98
+ homeDir: homeDirFn(),
99
+ allAgents: options.allAgents,
100
+ isDir,
101
+ skillsDirs,
102
+ });
103
+ const after = assessGaps(skillsDirs);
104
+ if (after.missing.length === 0 && after.stale.length === 0) {
105
+ const message = `${OPENCLAW_SOFT_REBIND_CHECK}: deposited soft AGENTS re-bind skill; ` +
106
+ `restart OpenClaw gateway or start a new session (${DOC_OPENCLAW_HOST})`;
107
+ sink.success(message);
108
+ addFinding({
109
+ severity: "skip",
110
+ message,
111
+ check: OPENCLAW_SOFT_REBIND_CHECK,
112
+ status: "fixed",
113
+ written: depositResult.writtenPaths,
114
+ });
115
+ return depositResult;
116
+ }
117
+ // Partial fix: report remaining gaps from post-deposit assessment (SLizard P2).
118
+ const partsAfter = [];
119
+ if (after.missing.length > 0) {
120
+ partsAfter.push(`missing=${after.missing.join(", ")}`);
121
+ }
122
+ if (after.stale.length > 0) {
123
+ partsAfter.push(`stale=${after.stale.join(", ")}`);
124
+ }
125
+ const messageAfter = `${OPENCLAW_SOFT_REBIND_CHECK}: OpenClaw soft AGENTS re-bind skill incomplete after fix ` +
126
+ `(${partsAfter.join("; ")}). Remediation: ${REMEDIATION_FIX} — see ${DOC_OPENCLAW_HOST}`;
127
+ sink.warn(messageAfter);
128
+ addFinding({
129
+ severity: "warning",
130
+ message: messageAfter,
131
+ check: OPENCLAW_SOFT_REBIND_CHECK,
132
+ status: "incomplete",
133
+ missing: after.missing,
134
+ stale: after.stale,
135
+ written: depositResult.writtenPaths,
136
+ suggestion: REMEDIATION_FIX,
137
+ docs: [DOC_OPENCLAW_HOST],
138
+ });
139
+ return depositResult;
140
+ }
141
+ const parts = [];
142
+ if (gaps.missing.length > 0) {
143
+ parts.push(`missing=${gaps.missing.join(", ")}`);
144
+ }
145
+ if (gaps.stale.length > 0) {
146
+ parts.push(`stale=${gaps.stale.join(", ")}`);
147
+ }
148
+ const message = `${OPENCLAW_SOFT_REBIND_CHECK}: OpenClaw soft AGENTS re-bind skill incomplete ` +
149
+ `(${parts.join("; ")}). Remediation: ${REMEDIATION_FIX} — see ${DOC_OPENCLAW_HOST}`;
150
+ sink.warn(message);
151
+ addFinding({
152
+ severity: "warning",
153
+ message,
154
+ check: OPENCLAW_SOFT_REBIND_CHECK,
155
+ status: "incomplete",
156
+ missing: gaps.missing,
157
+ stale: gaps.stale,
158
+ suggestion: REMEDIATION_FIX,
159
+ docs: [DOC_OPENCLAW_HOST],
160
+ });
161
+ return null;
162
+ }
163
+ export { assessOpenClawSoftRebindSkill, depositOpenClawSoftRebindSkill };
164
+ //# sourceMappingURL=openclaw-soft-rebind.js.map
@@ -140,7 +140,8 @@ export declare function decideHook(input: HookDispatchInput, seams?: HookPolicyS
140
140
  * Cursor deposits use `failClosed: true`. Cursor treats empty/null stdout as a
141
141
  * hook failure and blocks the tool — so Cursor allows must emit explicit
142
142
  * `{"permission":"allow"}`. Other hosts keep empty allow so the host permission
143
- * flow is unchanged.
143
+ * flow is unchanged — except session.start / session.compact soft re-bind
144
+ * injection (#3171), which surfaces checklist text without a write tool.
144
145
  *
145
146
  * Cursor stdout always includes `code` (stable machine-readable decision code)
146
147
  * so agents can distinguish policy denials from host-integration failures
@@ -7,6 +7,7 @@ import { evaluateIntentCeilingFromEnv } from "../policy/intent-ceiling.js";
7
7
  import { detectNoDeftDirective, NO_DEFT_DIRECTIVE_DISABLED_MESSAGE, NO_DEFT_DIRECTIVE_INCONSISTENT_MESSAGE, } from "../policy/no-deft-directive.js";
8
8
  import { classifyMcpTool, DEFAULT_RUNTIME_AUTHORITY_POLICY, evaluateRuntimeAuthorityDirectWrite, evaluateRuntimeAuthorityShellOp, listShellOps, loadRuntimeAuthorityFromProject, } from "../policy/runtime-authority.js";
9
9
  import { loadStoryWriteFenceFromPath, resolveWriteFence } from "../policy/write-fence.js";
10
+ import { appendSoftAgentsRebindToMessage, decisionCarriesSoftAgentsRebind, formatSoftAgentsRebindChecklist, } from "../session/compact-ritual.js";
10
11
  import { detectBranch } from "../session/git.js";
11
12
  import { emitSessionRitualBlockedProcessCost } from "../session/process-cost.js";
12
13
  import { markRitualStaleAfterCompact } from "../session/ritual-sentinel.js";
@@ -716,7 +717,8 @@ export function decideHook(input, seams = {}) {
716
717
  host: input.host,
717
718
  toolName: null,
718
719
  projectRoot,
719
- message: result.message,
720
+ // Soft checklist still surfaces after compact even when ritual was absent (#3171).
721
+ message: appendSoftAgentsRebindToMessage(result.message),
720
722
  scopePath: null,
721
723
  };
722
724
  }
@@ -727,7 +729,7 @@ export function decideHook(input, seams = {}) {
727
729
  host: input.host,
728
730
  toolName: null,
729
731
  projectRoot,
730
- message: result.message,
732
+ message: appendSoftAgentsRebindToMessage(result.message),
731
733
  scopePath: null,
732
734
  };
733
735
  }
@@ -739,8 +741,8 @@ export function decideHook(input, seams = {}) {
739
741
  host: input.host,
740
742
  toolName: null,
741
743
  projectRoot,
742
- message: "Directive compact re-arm bookkeeping failed on its non-blocking path: " +
743
- `${String(cause)}`,
744
+ message: appendSoftAgentsRebindToMessage("Directive compact re-arm bookkeeping failed on its non-blocking path: " +
745
+ `${String(cause)}`),
744
746
  scopePath: null,
745
747
  };
746
748
  }
@@ -775,8 +777,9 @@ export function decideHook(input, seams = {}) {
775
777
  host: input.host,
776
778
  toolName: null,
777
779
  projectRoot,
778
- message: `Directive SessionStart bookkeeping reported exit ${result.code} on its non-blocking path` +
779
- `${detail.length > 0 ? `: ${detail}` : "."}`,
780
+ // Best-effort soft cue on SessionStart (Codex gap + resume/amnesia) (#3171).
781
+ message: appendSoftAgentsRebindToMessage(`Directive SessionStart bookkeeping reported exit ${result.code} on its non-blocking path` +
782
+ `${detail.length > 0 ? `: ${detail}` : "."}`),
780
783
  scopePath: null,
781
784
  };
782
785
  }
@@ -789,7 +792,7 @@ export function decideHook(input, seams = {}) {
789
792
  host: input.host,
790
793
  toolName: null,
791
794
  projectRoot,
792
- message: `Directive SessionStart bookkeeping failed on its non-blocking path: ${String(cause)}`,
795
+ message: appendSoftAgentsRebindToMessage(`Directive SessionStart bookkeeping failed on its non-blocking path: ${String(cause)}`),
793
796
  scopePath: null,
794
797
  };
795
798
  }
@@ -800,7 +803,8 @@ export function decideHook(input, seams = {}) {
800
803
  host: input.host,
801
804
  toolName: null,
802
805
  projectRoot,
803
- message: "SessionStart bookkeeping completed on a non-blocking path.",
806
+ // Soft AGENTS re-bind on SessionStart without requiring a write tool (#3171 / #2769).
807
+ message: appendSoftAgentsRebindToMessage("SessionStart bookkeeping completed on a non-blocking path."),
804
808
  scopePath: null,
805
809
  };
806
810
  }
@@ -881,13 +885,27 @@ export function decideHook(input, seams = {}) {
881
885
  }
882
886
  return inspectMutationGates(input, toolName, seams, { proposedLifecycleExempt: true });
883
887
  }
888
+ /**
889
+ * Soft AGENTS re-bind text for host injection when decision carries soft path (#3171).
890
+ * Prefer the checklist portion of the decision message; fall back to SoT formatter.
891
+ */
892
+ function softAgentsRebindWireText(decision) {
893
+ if (!decisionCarriesSoftAgentsRebind({ event: decision.event, code: decision.code })) {
894
+ return null;
895
+ }
896
+ if (decision.message.includes("Soft AGENTS re-bind checklist:")) {
897
+ return decision.message;
898
+ }
899
+ return formatSoftAgentsRebindChecklist();
900
+ }
884
901
  /**
885
902
  * Render host-facing hook output.
886
903
  *
887
904
  * Cursor deposits use `failClosed: true`. Cursor treats empty/null stdout as a
888
905
  * hook failure and blocks the tool — so Cursor allows must emit explicit
889
906
  * `{"permission":"allow"}`. Other hosts keep empty allow so the host permission
890
- * flow is unchanged.
907
+ * flow is unchanged — except session.start / session.compact soft re-bind
908
+ * injection (#3171), which surfaces checklist text without a write tool.
891
909
  *
892
910
  * Cursor stdout always includes `code` (stable machine-readable decision code)
893
911
  * so agents can distinguish policy denials from host-integration failures
@@ -896,9 +914,45 @@ export function decideHook(input, seams = {}) {
896
914
  */
897
915
  export function renderHostDecision(host, decision) {
898
916
  if (decision.verdict === "allow") {
917
+ const soft = softAgentsRebindWireText(decision);
899
918
  if (host === "cursor") {
919
+ if (decision.event === "session.start" && soft !== null) {
920
+ // Cursor sessionStart injects additional_context into the conversation.
921
+ return JSON.stringify({
922
+ permission: "allow",
923
+ code: decision.code,
924
+ additional_context: soft,
925
+ });
926
+ }
927
+ if (decision.event === "session.compact" && soft !== null) {
928
+ // Cursor preCompact is observational; user_message surfaces the soft cue.
929
+ // Hard re-arm still ran via bookkeeping; agent path also gets SessionStart on resume.
930
+ return JSON.stringify({
931
+ user_message: soft,
932
+ code: decision.code,
933
+ });
934
+ }
900
935
  return JSON.stringify({ permission: "allow", code: decision.code });
901
936
  }
937
+ if (soft !== null &&
938
+ (decision.event === "session.start" || decision.event === "session.compact")) {
939
+ if (host === "claude" || host === "codex") {
940
+ const hookEventName = decision.event === "session.start" ? "SessionStart" : "PostCompact";
941
+ return JSON.stringify({
942
+ hookSpecificOutput: {
943
+ hookEventName,
944
+ additionalContext: soft,
945
+ },
946
+ });
947
+ }
948
+ if (host === "grok") {
949
+ return JSON.stringify({
950
+ decision: "allow",
951
+ reason: soft,
952
+ additional_context: soft,
953
+ });
954
+ }
955
+ }
902
956
  return "";
903
957
  }
904
958
  switch (host) {
package/dist/index.d.ts CHANGED
@@ -14,7 +14,9 @@ export * as branch from "./branch/index.js";
14
14
  export * as cache from "./cache/index.js";
15
15
  export * as capacity from "./capacity/index.js";
16
16
  export * as codebase from "./codebase/index.js";
17
+ export * as consumerCheckContract from "./consumer-check-contract/index.js";
17
18
  export * from "./coverage-hotspots/index.js";
19
+ export * as deliveryAttempt from "./delivery-attempt/index.js";
18
20
  export * as doctor from "./doctor/index.js";
19
21
  export * from "./encoding/index.js";
20
22
  export * as escalation from "./escalation/index.js";
@@ -52,11 +54,13 @@ export * as render from "./render/index.js";
52
54
  export * as resolution from "./resolution/index.js";
53
55
  export * as scm from "./scm/index.js";
54
56
  export * as scope from "./scope/index.js";
57
+ export * as scopeProvenance from "./scope-provenance/index.js";
55
58
  export * as session from "./session/index.js";
56
59
  export * as slash from "./slash/index.js";
57
60
  export * as slice from "./slice/index.js";
58
61
  export * as storyReady from "./story-ready/index.js";
59
62
  export * as swarm from "./swarm/index.js";
63
+ export * as testBoundary from "./test-boundary/index.js";
60
64
  export * as toolEvents from "./tool-events/index.js";
61
65
  export * as triage from "./triage/index.js";
62
66
  export * as userConfig from "./user-config/index.js";
package/dist/index.js CHANGED
@@ -14,7 +14,9 @@ export * as branch from "./branch/index.js";
14
14
  export * as cache from "./cache/index.js";
15
15
  export * as capacity from "./capacity/index.js";
16
16
  export * as codebase from "./codebase/index.js";
17
+ export * as consumerCheckContract from "./consumer-check-contract/index.js";
17
18
  export * from "./coverage-hotspots/index.js";
19
+ export * as deliveryAttempt from "./delivery-attempt/index.js";
18
20
  export * as doctor from "./doctor/index.js";
19
21
  export * from "./encoding/index.js";
20
22
  export * as escalation from "./escalation/index.js";
@@ -53,11 +55,13 @@ export * as render from "./render/index.js";
53
55
  export * as resolution from "./resolution/index.js";
54
56
  export * as scm from "./scm/index.js";
55
57
  export * as scope from "./scope/index.js";
58
+ export * as scopeProvenance from "./scope-provenance/index.js";
56
59
  export * as session from "./session/index.js";
57
60
  export * as slash from "./slash/index.js";
58
61
  export * as slice from "./slice/index.js";
59
62
  export * as storyReady from "./story-ready/index.js";
60
63
  export * as swarm from "./swarm/index.js";
64
+ export * as testBoundary from "./test-boundary/index.js";
61
65
  export * as toolEvents from "./tool-events/index.js";
62
66
  export * as triage from "./triage/index.js";
63
67
  export * as userConfig from "./user-config/index.js";
@@ -37,6 +37,11 @@ export const CANONICAL_GITIGNORE_BASELINE = [
37
37
  "vbrief/.triage-cache/scope-lifecycle.jsonl",
38
38
  "vbrief/.triage-cache/decompositions/",
39
39
  "vbrief/.triage-cache/doctor-state.json",
40
+ // Per-clone session state (#3146): throttle / release-availability JSON
41
+ // written under the lifecycle .triage-cache; selective ignore only (hybrid
42
+ // #1144 — never blanket-ignore the whole .triage-cache directory).
43
+ "vbrief/.triage-cache/staleness-tickler-state.json",
44
+ "vbrief/.triage-cache/release-availability-state.json",
40
45
  // Symmetric `xbrief/` layout entries (#2348). On the migrated `xbrief/` tree
41
46
  // the engine writes operator-private triage-cache files to
42
47
  // `xbrief/.triage-cache/`; without these the paths are trackable, violating
@@ -47,6 +52,8 @@ export const CANONICAL_GITIGNORE_BASELINE = [
47
52
  "xbrief/.triage-cache/scope-lifecycle.jsonl",
48
53
  "xbrief/.triage-cache/decompositions/",
49
54
  "xbrief/.triage-cache/doctor-state.json",
55
+ "xbrief/.triage-cache/staleness-tickler-state.json",
56
+ "xbrief/.triage-cache/release-availability-state.json",
50
57
  "vbrief/*.lock",
51
58
  ".deft/core.bak-*/",
52
59
  ".deft/*.bak-*",
@@ -15,6 +15,7 @@ import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
15
15
  import { readCorePackageVersion } from "../engine-version.js";
16
16
  import { stampLiveGeneration } from "../freshness/generation.js";
17
17
  import { renderProjectDefinition } from "../render/project-render.js";
18
+ import { depositOpenClawSoftRebindSkill } from "../session/openclaw-soft-rebind-deposit.js";
18
19
  import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
19
20
  import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
20
21
  import { removeStaleMigratedFrameworkNarrative } from "../xbrief-migrate/migrate-project.js";
@@ -201,6 +202,10 @@ export async function runInitDeposit(args, io, seams = {}) {
201
202
  writeConsumerGitHooks(projectDir, deftDir, io, seams.gitHooks);
202
203
  writeAgentHookDeposit(projectDir, io);
203
204
  writeSlashCommandDeposit(projectDir, io);
205
+ // #3171: OpenClaw soft AGENTS re-bind skill when OC signals present (fail-closed otherwise).
206
+ depositOpenClawSoftRebindSkill({
207
+ /* env defaults; skip when OpenClaw not detected */
208
+ });
204
209
  // #3064: OpenClaw L2 product-command skills when OC signals present (fail-closed otherwise).
205
210
  depositOpenClawL2ProductCommands({
206
211
  projectRoot: projectDir,
@@ -22,6 +22,7 @@ import { resolveLifecycleRoot } from "../layout/resolve.js";
22
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";
23
23
  import { runOrgForceOnMigration } from "../policy/org-force-on-migration.js";
24
24
  import { checkLocalEngineIntegrity, classify, ENGINE_PACKAGE, plan, renderGlobalInstall, resolveEngine, } from "../resolution/index.js";
25
+ import { depositOpenClawSoftRebindSkill } from "../session/openclaw-soft-rebind-deposit.js";
25
26
  import { depositOpenClawL2ProductCommands } from "../slash/openclaw-deposit.js";
26
27
  import { gitPorcelain } from "../story-ready/git.js";
27
28
  import { agentHookReadinessJson, evaluateAgentHookReadiness, evaluateAgentHookReadinessSafely, } from "../verify-env/agent-hook-readiness.js";
@@ -506,6 +507,8 @@ export async function runRefreshDeposit(args, io, seams = {}) {
506
507
  // #75 residual: multi-host thin skill discovery (mirror `.agents/skills` inventory).
507
508
  writeMultiHostSkillDiscovery(projectDir, io);
508
509
  writeSlashCommandDeposit(projectDir, io);
510
+ // #3171: OpenClaw soft AGENTS re-bind skill when OC signals present (fail-closed otherwise).
511
+ depositOpenClawSoftRebindSkill({});
509
512
  // #3064: OpenClaw L2 product-command skills when OC signals present (fail-closed otherwise).
510
513
  depositOpenClawL2ProductCommands({
511
514
  projectRoot: projectDir,
@@ -1,5 +1,21 @@
1
1
  import type { CheckRunRecord } from "./gh.js";
2
- export type CiReadyState = "ready" | "blocked" | "not_ready_yet" | "runner_capacity_stall" | "skipped";
2
+ /**
3
+ * Bot-reviewer check-runs are not CI workflow jobs. Presence of only these
4
+ * (or empty check-runs) is the `ci_never_scheduled` weather signal (#3167).
5
+ */
6
+ export declare function isBotReviewCheck(name: string): boolean;
7
+ /**
8
+ * Suite family for cancelled↔green sibling matching (#3167 / Greptile P1).
9
+ * Unrelated greens (CodeQL, Socket, …) must not clear a cancelled TypeScript/Go lane.
10
+ * Returns null when the check is not a TS/Go CI suite job.
11
+ */
12
+ export declare function suiteFamilyOf(name: string): "typescript" | "go" | null;
13
+ /**
14
+ * Branch-protection SoT aggregators only (#3168 map). Primary/failover lanes are
15
+ * not authoritative: a green primary must not clear a cancelled aggregator.
16
+ */
17
+ export declare function isAuthoritativeSuiteAggregator(name: string): boolean;
18
+ export type CiReadyState = "ready" | "blocked" | "ci_failures" | "not_ready_yet" | "runner_capacity_stall" | "ci_never_scheduled" | "ci_cancelled_no_failover" | "skipped";
3
19
  export interface CiGateOptions {
4
20
  readonly skipCi?: boolean;
5
21
  readonly ignoreCheckNames?: readonly string[];
@@ -23,6 +39,8 @@ export interface CiGateSummary {
23
39
  readonly pending_required: readonly string[];
24
40
  /** Required checks classified as capacity-stalled (#2672). */
25
41
  readonly capacity_stalled_required: readonly string[];
42
+ /** Required checks cancelled without a green failover sibling (#3167). */
43
+ readonly cancelled_required: readonly string[];
26
44
  readonly conclusions: readonly CiCheckConclusion[];
27
45
  }
28
46
  export interface CiGateResult {
@@ -30,5 +48,15 @@ export interface CiGateResult {
30
48
  readonly summary: CiGateSummary;
31
49
  }
32
50
  export declare function buildCiSummaryLine(summary: CiGateSummary): string;
51
+ /**
52
+ * Classify required CI weather for a HEAD check-run list (#2169 / #2672 / #3167).
53
+ *
54
+ * Distinct ready_state codes:
55
+ * - `ci_never_scheduled` — no CI workflow check-runs (bots alone / empty)
56
+ * - `runner_capacity_stall` — queued past budget, no runner claimed
57
+ * - `ci_failures` — completed failure / timed_out with product evidence
58
+ * - `ci_cancelled_no_failover` — only cancelled failures; no green failover
59
+ * - `not_ready_yet` / `ready` / `blocked` / `skipped` — legacy + residual
60
+ */
33
61
  export declare function evaluateCiGate(checkRuns: readonly CheckRunRecord[], options?: CiGateOptions): CiGateResult;
34
62
  //# sourceMappingURL=ci-gate.d.ts.map