crewly 1.20.377 → 1.20.379

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 (68) hide show
  1. package/config/hooks/destructive-git-guard/guard.mjs +33 -4
  2. package/config/hooks/destructive-git-guard/local-gates.json +3 -2
  3. package/config/skills/agent/core/record-tl-verification/SKILL.md +39 -0
  4. package/config/skills/agent/core/record-tl-verification/execute.sh +71 -0
  5. package/config/skills/agent/core/record-tl-verification/execute.test.sh +56 -0
  6. package/dist/backend/backend/src/constants.d.ts +60 -0
  7. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  8. package/dist/backend/backend/src/constants.js +60 -0
  9. package/dist/backend/backend/src/constants.js.map +1 -1
  10. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.d.ts +17 -0
  11. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.d.ts.map +1 -1
  12. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.js +49 -0
  13. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.controller.js.map +1 -1
  14. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.routes.d.ts +2 -0
  15. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.routes.d.ts.map +1 -1
  16. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.routes.js +5 -1
  17. package/dist/backend/backend/src/controllers/owner-receipt/owner-receipt.routes.js.map +1 -1
  18. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.d.ts.map +1 -1
  19. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js +3 -0
  20. package/dist/backend/backend/src/controllers/project-tickets/project-tickets.controller.js.map +1 -1
  21. package/dist/backend/backend/src/index.d.ts.map +1 -1
  22. package/dist/backend/backend/src/index.js +23 -0
  23. package/dist/backend/backend/src/index.js.map +1 -1
  24. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
  26. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  27. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts +22 -0
  28. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.d.ts.map +1 -1
  29. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js +64 -3
  30. package/dist/backend/backend/src/services/project-tickets/project-ticket-workflow.service.js.map +1 -1
  31. package/dist/backend/backend/src/services/project-tickets/ticket-routine-nudge.d.ts +59 -0
  32. package/dist/backend/backend/src/services/project-tickets/ticket-routine-nudge.d.ts.map +1 -0
  33. package/dist/backend/backend/src/services/project-tickets/ticket-routine-nudge.js +123 -0
  34. package/dist/backend/backend/src/services/project-tickets/ticket-routine-nudge.js.map +1 -0
  35. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.d.ts +173 -0
  36. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.d.ts.map +1 -0
  37. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.js +263 -0
  38. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.js.map +1 -0
  39. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.loader.d.ts +35 -0
  40. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.loader.d.ts.map +1 -0
  41. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.loader.js +47 -0
  42. package/dist/backend/backend/src/services/v3/owner-receipt/owner-pulls-weekly.loader.js.map +1 -0
  43. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.d.ts +39 -0
  44. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.d.ts.map +1 -1
  45. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.js +93 -2
  46. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.service.js.map +1 -1
  47. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.d.ts +2 -0
  48. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.d.ts.map +1 -1
  49. package/dist/backend/backend/src/services/v3/owner-receipt/owner-receipt.types.js.map +1 -1
  50. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.d.ts +110 -0
  51. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.js +243 -0
  53. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.js.map +1 -0
  54. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.loader.d.ts +27 -0
  55. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.loader.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.loader.js +45 -0
  57. package/dist/backend/backend/src/services/v3/owner-receipt/verified-spot-check.loader.js.map +1 -0
  58. package/dist/backend/build-info.json +2 -2
  59. package/dist/cli/backend/src/constants.d.ts +60 -0
  60. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  61. package/dist/cli/backend/src/constants.js +60 -0
  62. package/dist/cli/backend/src/constants.js.map +1 -1
  63. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  64. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +5 -0
  65. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  66. package/frontend/dist/assets/{index-b4db6abe.js → index-19c47468.js} +76 -76
  67. package/frontend/dist/index.html +1 -1
  68. package/package.json +1 -1
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Verified-work spot-check (#1315, phase 1) — once a day, pick a few WorkItems
3
+ * that were verified in the last 24 hours and re-check that every file
4
+ * artifact recorded as their evidence still exists.
5
+ *
6
+ * Read-only: it never changes a WorkItem and never re-runs a command (that is
7
+ * a later phase). Evidence that is not a local file (URLs, commands, blocked
8
+ * reports) is counted as skipped, not as a pass or a failure. Pure apart from
9
+ * the injected file check and random source, so a day's sample is repeatable.
10
+ *
11
+ * @module services/v3/owner-receipt/verified-spot-check
12
+ */
13
+ import * as path from 'path';
14
+ import { fileURLToPath } from 'url';
15
+ import { OWNER_RECEIPT_CONSTANTS, WORKTREE_CONSTANTS } from '../../../constants.js';
16
+ import { artifactBaseDirs } from '../../task-pool/completion-evidence.service.js';
17
+ /**
18
+ * Deterministic random source (mulberry32).
19
+ *
20
+ * @param seed - Any integer
21
+ * @returns Generator of numbers in [0, 1)
22
+ */
23
+ export function seededRandom(seed) {
24
+ let a = seed >>> 0;
25
+ return () => {
26
+ a = (a + 0x6d2b79f5) >>> 0;
27
+ let t = a;
28
+ t = Math.imul(t ^ (t >>> 15), t | 1);
29
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
30
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
31
+ };
32
+ }
33
+ /**
34
+ * Seed for a calendar day, so a second run on the same day picks the same items.
35
+ *
36
+ * @param now - The run time
37
+ * @returns Integer seed
38
+ */
39
+ export function daySeed(now) {
40
+ const day = now.toISOString().slice(0, 10);
41
+ let h = 2166136261;
42
+ for (let i = 0; i < day.length; i++)
43
+ h = Math.imul(h ^ day.charCodeAt(i), 16777619);
44
+ return h >>> 0;
45
+ }
46
+ /**
47
+ * Split a WorkItem's recorded evidence into file artifacts to look at and a
48
+ * count of entries that are not checked.
49
+ *
50
+ * @param wi - WorkItem
51
+ * @param homeDir - Home directory for `~/`
52
+ * @returns Targets and the skipped count
53
+ */
54
+ export function fileTargetsOf(wi, homeDir) {
55
+ const evidence = wi.output?.['evidence'];
56
+ const targets = [];
57
+ let skipped = 0;
58
+ if (!Array.isArray(evidence))
59
+ return { targets, skipped };
60
+ const bases = artifactBaseDirs(wi);
61
+ for (const entry of evidence) {
62
+ const e = (typeof entry === 'object' && entry !== null ? entry : {});
63
+ const raw = e['type'] === 'artifact' && typeof e['path'] === 'string' ? e['path'].trim() : '';
64
+ if (!raw) {
65
+ skipped += 1; // a command or a blocked report
66
+ continue;
67
+ }
68
+ const scheme = /^([a-z][a-z0-9+.-]*:)\/\//i.exec(raw)?.[1]?.toLowerCase();
69
+ let local = raw;
70
+ if (scheme === 'file:') {
71
+ try {
72
+ local = fileURLToPath(raw);
73
+ }
74
+ catch {
75
+ skipped += 1;
76
+ continue;
77
+ }
78
+ }
79
+ else if (scheme) {
80
+ skipped += 1; // http(s) or another URL: not a file on this machine
81
+ continue;
82
+ }
83
+ else if (raw === '~' || raw.startsWith('~/')) {
84
+ local = path.join(homeDir, raw.slice(1));
85
+ }
86
+ targets.push({ raw, candidates: path.isAbsolute(local) ? [local] : bases.map((b) => path.resolve(b, local)) });
87
+ }
88
+ return { targets, skipped };
89
+ }
90
+ /**
91
+ * The worktree root an absolute path lies in: the WorkItem's recorded
92
+ * worktree, else the directory after a known worktree marker.
93
+ *
94
+ * @param abs - Absolute artifact path
95
+ * @param wi - The WorkItem
96
+ * @returns The worktree root, or null when the path is outside any worktree
97
+ */
98
+ export function worktreeRootOf(abs, wi) {
99
+ for (const base of artifactBaseDirsOfWorktree(wi)) {
100
+ if (abs === base || abs.startsWith(base + path.sep))
101
+ return base;
102
+ }
103
+ const norm = abs.split(path.sep).join('/');
104
+ for (const marker of OWNER_RECEIPT_CONSTANTS.VERIFIED_SPOT_CHECK.WORKTREE_MARKERS) {
105
+ const needle = `/${marker}/`;
106
+ const at = norm.indexOf(needle);
107
+ if (at < 0)
108
+ continue;
109
+ const rest = norm.slice(at + needle.length).split('/')[0];
110
+ if (rest)
111
+ return norm.slice(0, at + needle.length) + rest;
112
+ }
113
+ return null;
114
+ }
115
+ /**
116
+ * Recorded worktree paths of a WorkItem (`metadata.worktree.path` / `workdir`).
117
+ *
118
+ * @param wi - The WorkItem
119
+ * @returns Absolute paths
120
+ */
121
+ function artifactBaseDirsOfWorktree(wi) {
122
+ const wt = (wi.metadata ?? {})[WORKTREE_CONSTANTS.METADATA_KEY];
123
+ if (typeof wt !== 'object' || wt === null)
124
+ return [];
125
+ const rec = wt;
126
+ return ['path', 'workdir'].map((k) => rec[k]).filter((v) => typeof v === 'string' && path.isAbsolute(v));
127
+ }
128
+ /**
129
+ * Run the spot-check.
130
+ *
131
+ * @param input - WorkItems, clock, team lookup, file check
132
+ * @returns What was sampled and what is missing
133
+ */
134
+ export async function runSpotCheck(input) {
135
+ const C = OWNER_RECEIPT_CONSTANTS.VERIFIED_SPOT_CHECK;
136
+ const to = input.now.getTime();
137
+ const from = to - C.WINDOW_MS;
138
+ const homeDir = input.homeDir ?? '';
139
+ const eligible = [];
140
+ for (const wi of input.workItems) {
141
+ if (wi.status !== 'verified' || !wi.completedAt)
142
+ continue;
143
+ const t = Date.parse(wi.completedAt);
144
+ if (!Number.isFinite(t) || t < from || t > to)
145
+ continue;
146
+ const { targets, skipped } = fileTargetsOf(wi, homeDir);
147
+ if (targets.length === 0)
148
+ continue;
149
+ eligible.push({ wi, targets, skipped });
150
+ }
151
+ // Stable order first, so the same seed picks the same items.
152
+ eligible.sort((a, b) => a.wi.id.localeCompare(b.wi.id));
153
+ const rand = input.random ?? seededRandom(daySeed(input.now));
154
+ const picked = [...eligible];
155
+ for (let i = picked.length - 1; i > 0; i--) {
156
+ const j = Math.floor(rand() * (i + 1));
157
+ [picked[i], picked[j]] = [picked[j], picked[i]];
158
+ }
159
+ const sample = picked.slice(0, Math.max(0, input.sampleSize ?? C.SAMPLE_SIZE));
160
+ const result = {
161
+ at: input.now.toISOString(),
162
+ eligible: eligible.length,
163
+ sampled: sample.length,
164
+ artifactsChecked: 0,
165
+ skipped: 0,
166
+ worktreeCleaned: 0,
167
+ discrepancies: [],
168
+ };
169
+ for (const { wi, targets, skipped } of sample) {
170
+ result.skipped += skipped;
171
+ const team = (wi.target ? input.teamOf(wi.target) : null) ?? OWNER_RECEIPT_CONSTANTS.UNASSIGNED_TEAM;
172
+ for (const target of targets) {
173
+ if (target.candidates.length === 0) {
174
+ result.skipped += 1; // relative path and no project or worktree to resolve it against
175
+ continue;
176
+ }
177
+ result.artifactsChecked += 1;
178
+ let found = false;
179
+ for (const c of target.candidates) {
180
+ try {
181
+ if (await input.exists(c)) {
182
+ found = true;
183
+ break;
184
+ }
185
+ }
186
+ catch {
187
+ // an unreadable path counts as not found
188
+ }
189
+ }
190
+ if (!found) {
191
+ // The janitor removes worktrees after merge: a file inside a worktree
192
+ // that is itself gone was cleaned, not lost.
193
+ let cleaned = true;
194
+ for (const c of target.candidates) {
195
+ const root = worktreeRootOf(c, wi);
196
+ let rootGone = false;
197
+ if (root) {
198
+ try {
199
+ rootGone = !(await input.exists(root));
200
+ }
201
+ catch {
202
+ rootGone = false;
203
+ }
204
+ }
205
+ if (!rootGone) {
206
+ cleaned = false; // outside any worktree, or its worktree still exists: really missing
207
+ break;
208
+ }
209
+ }
210
+ if (cleaned) {
211
+ result.artifactsChecked -= 1;
212
+ result.skipped += 1;
213
+ result.worktreeCleaned += 1;
214
+ continue;
215
+ }
216
+ }
217
+ if (!found)
218
+ result.discrepancies.push({ workItemId: wi.id, team, artifactPath: target.raw });
219
+ }
220
+ }
221
+ return result;
222
+ }
223
+ /**
224
+ * The owner line: always present, zero is said out loud. No WorkItem ids.
225
+ *
226
+ * @param r - The run
227
+ * @returns Slack mrkdwn, one to five lines
228
+ */
229
+ export function renderSpotCheck(r) {
230
+ const d = r.discrepancies.length;
231
+ const noun = d === 1 ? 'discrepancy' : 'discrepancies';
232
+ const head = `*Spot-check of verified work:* ${r.sampled} sampled, ${d} ${noun}`;
233
+ if (r.sampled === 0)
234
+ return `${head} (nothing verified in the last day had a file to look at)`;
235
+ if (d === 0)
236
+ return head;
237
+ const max = OWNER_RECEIPT_CONSTANTS.VERIFIED_SPOT_CHECK.MAX_LISTED;
238
+ const lines = r.discrepancies.slice(0, max).map((x) => `• ${x.team}: \`${x.artifactPath}\` is gone`);
239
+ if (d > max)
240
+ lines.push(`• +${d - max} more`);
241
+ return [head, ...lines].join('\n');
242
+ }
243
+ //# sourceMappingURL=verified-spot-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-spot-check.js","sourceRoot":"","sources":["../../../../../../../backend/src/services/v3/owner-receipt/verified-spot-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Loader for the verified-work spot-check (#1315): reads the WorkItems and the
3
+ * team index, checks files on the real disk, and hands them to the pure
4
+ * {@link runSpotCheck}.
5
+ *
6
+ * @module services/v3/owner-receipt/verified-spot-check.loader
7
+ */
8
+ import type { WorkItem } from '../../../types/v2/work-item.types.js';
9
+ import { type SpotCheckResult } from './verified-spot-check.js';
10
+ /** Where the loader reads from. */
11
+ export interface SpotCheckLoaderDeps {
12
+ listWorkItems: () => Promise<WorkItem[]>;
13
+ /** session → team name */
14
+ loadTeamIndex: () => Promise<Map<string, string>>;
15
+ /** File check, injectable for tests; default is `fs.stat` */
16
+ exists?: (absolutePath: string) => Promise<boolean>;
17
+ /** Random source, injectable for tests */
18
+ random?: () => number;
19
+ }
20
+ /**
21
+ * Build the `loadSpotCheck` dependency of the owner receipt service.
22
+ *
23
+ * @param deps - Data sources
24
+ * @returns Function that runs the spot-check as of `now`
25
+ */
26
+ export declare function createSpotCheckLoader(deps: SpotCheckLoaderDeps): (now: Date) => Promise<SpotCheckResult>;
27
+ //# sourceMappingURL=verified-spot-check.loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-spot-check.loader.d.ts","sourceRoot":"","sources":["../../../../../../../backend/src/services/v3/owner-receipt/verified-spot-check.loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE9E,mCAAmC;AACnC,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,0BAA0B;IAC1B,aAAa,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,6DAA6D;IAC7D,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpD,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAiBD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAAC,eAAe,CAAC,CAYxG"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Loader for the verified-work spot-check (#1315): reads the WorkItems and the
3
+ * team index, checks files on the real disk, and hands them to the pure
4
+ * {@link runSpotCheck}.
5
+ *
6
+ * @module services/v3/owner-receipt/verified-spot-check.loader
7
+ */
8
+ import * as fs from 'fs/promises';
9
+ import * as os from 'os';
10
+ import { runSpotCheck } from './verified-spot-check.js';
11
+ /**
12
+ * Whether anything exists at a path.
13
+ *
14
+ * @param p - Absolute path
15
+ * @returns True when `fs.stat` succeeds
16
+ */
17
+ async function statExists(p) {
18
+ try {
19
+ await fs.stat(p);
20
+ return true;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ /**
27
+ * Build the `loadSpotCheck` dependency of the owner receipt service.
28
+ *
29
+ * @param deps - Data sources
30
+ * @returns Function that runs the spot-check as of `now`
31
+ */
32
+ export function createSpotCheckLoader(deps) {
33
+ return async (now) => {
34
+ const [workItems, teams] = await Promise.all([deps.listWorkItems(), deps.loadTeamIndex()]);
35
+ return runSpotCheck({
36
+ workItems,
37
+ now,
38
+ teamOf: (session) => teams.get(session) ?? null,
39
+ exists: deps.exists ?? statExists,
40
+ homeDir: os.homedir(),
41
+ ...(deps.random ? { random: deps.random } : {}),
42
+ });
43
+ };
44
+ }
45
+ //# sourceMappingURL=verified-spot-check.loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verified-spot-check.loader.js","sourceRoot":"","sources":["../../../../../../../backend/src/services/v3/owner-receipt/verified-spot-check.loader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AAEzB,OAAO,EAAE,YAAY,EAAwB,MAAM,0BAA0B,CAAC;AAa9E;;;;;GAKG;AACH,KAAK,UAAU,UAAU,CAAC,CAAS;IACjC,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAyB;IAC7D,OAAO,KAAK,EAAE,GAAS,EAA4B,EAAE;QACnD,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,EAAE,EAAE,IAAI,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;QAC3F,OAAO,YAAY,CAAC;YAClB,SAAS;YACT,GAAG;YACH,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI;YAC/C,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,UAAU;YACjC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE;YACrB,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "8eaede93a5735d244056190d70d9f8557e9bc159",
3
- "builtAt": "2026-10-21T11:26:55.209Z"
2
+ "commit": "c731030d3bef63a4105605f4bdc0f8b5711f6d36",
3
+ "builtAt": "2026-10-21T12:10:51.826Z"
4
4
  }
@@ -6176,6 +6176,45 @@ export declare const OWNER_RECEIPT_CONSTANTS: {
6176
6176
  readonly OPTION_ON: "Turn on nightly — this format";
6177
6177
  readonly OPTION_PER_ASK: "Keep per-ask format — every ask, one line each";
6178
6178
  };
6179
+ /**
6180
+ * The weekly "how often agents pulled the owner in" line (#1387, first
6181
+ * slice). Specs: specs/owner-pulls-weekly.md.
6182
+ */
6183
+ readonly PULLS_WEEKLY: {
6184
+ /** Local weekday it goes out with the receipt (0 = Sunday) */
6185
+ readonly REPORT_WEEKDAY: 0;
6186
+ /** The week counted: the 7 days before the report; compared with the 7 before that (ms) */
6187
+ readonly WINDOW_MS: number;
6188
+ /** Teams named on the line before "+N more" */
6189
+ readonly MAX_TEAMS: 3;
6190
+ /** Traces read for the owner-touch count (most recent first) */
6191
+ readonly MAX_TRACES: 500;
6192
+ };
6193
+ /**
6194
+ * Daily spot-check of verified WorkItems (#1315, phase 1): re-check that the
6195
+ * file artifacts recorded as evidence still exist. Never changes a WorkItem.
6196
+ */
6197
+ readonly VERIFIED_SPOT_CHECK: {
6198
+ /** WorkItems sampled per day */
6199
+ readonly SAMPLE_SIZE: 5;
6200
+ /** Only items completed this recently are eligible (ms) */
6201
+ readonly WINDOW_MS: number;
6202
+ /** Discrepancies named on the owner line before "+N more" */
6203
+ readonly MAX_LISTED: 3;
6204
+ /**
6205
+ * Directory markers of agent worktrees the disk janitor removes after merge
6206
+ * (the segment after a marker is the worktree root). A missing file inside
6207
+ * a worktree that is gone is "worktree cleaned", not a discrepancy.
6208
+ */
6209
+ readonly WORKTREE_MARKERS: readonly string[];
6210
+ };
6211
+ };
6212
+ /** How each pull kind reads on the owner's weekly line (#1387). */
6213
+ export declare const PULLS_WEEKLY_KIND_LABELS: {
6214
+ readonly decision: "decision needed";
6215
+ readonly review: "review or approval";
6216
+ readonly fyi: "FYI that did not need an answer";
6217
+ readonly stuck: "agent stuck or error";
6179
6218
  };
6180
6219
  /**
6181
6220
  * Hours a ticket waits in 待验收 with no objection before silence accepts it
@@ -8843,6 +8882,27 @@ export declare const TICKET_HYGIENE_CONSTANTS: {
8843
8882
  /** Entries in the per-ticket "already sent" memory are dropped after this long (days) */
8844
8883
  readonly STATE_RETENTION_DAYS: 30;
8845
8884
  };
8885
+ /**
8886
+ * Routine nudge (crewly#1034): when a ticket is verified done and its result
8887
+ * describes recurring work, the verifying lead gets ONE internal WorkItem
8888
+ * telling it to set a routine or say why not. Never creates the cron itself.
8889
+ */
8890
+ export declare const TICKET_ROUTINE_NUDGE_CONSTANTS: {
8891
+ /** Kill switch: `0` turns the nudge off */
8892
+ readonly ENV_SWITCH: "CREWLY_TICKET_ROUTINE_NUDGE";
8893
+ /** `metadata.kind` of the nudge WorkItem */
8894
+ readonly METADATA_KIND: "ticket_routine_nudge";
8895
+ /** Prefix of the ticket Log line that marks the nudge as sent (also the dedupe key) */
8896
+ readonly LOG_MARKER: "routine nudge sent to";
8897
+ /** Actor written in the ticket Log */
8898
+ readonly ACTOR: "crewly-routine-nudge";
8899
+ /** Max characters of the result text scanned for a cadence */
8900
+ readonly MAX_SCAN_CHARS: 6000;
8901
+ /** Max characters of the matched sentence quoted in the nudge */
8902
+ readonly MAX_QUOTE_CHARS: 240;
8903
+ /** Max characters of the ticket title in the nudge */
8904
+ readonly TITLE_MAX_CHARS: 120;
8905
+ };
8846
8906
  /**
8847
8907
  * Drive mode v3 status briefing (specs/2026-10-09-drive-mode-v3.md §1): the
8848
8908
  * compact per-team / per-agent snapshot this machine keeps up to date and