@telora/daemon 0.18.40 → 0.18.55

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 (139) hide show
  1. package/build-info.json +12 -3
  2. package/dist/completion/advanceability.d.ts +66 -0
  3. package/dist/completion/advanceability.d.ts.map +1 -0
  4. package/dist/completion/advanceability.js +112 -0
  5. package/dist/completion/advanceability.js.map +1 -0
  6. package/dist/completion/event.d.ts +28 -52
  7. package/dist/completion/event.d.ts.map +1 -1
  8. package/dist/completion/event.js +79 -99
  9. package/dist/completion/event.js.map +1 -1
  10. package/dist/completion/progress-snapshot.d.ts +15 -4
  11. package/dist/completion/progress-snapshot.d.ts.map +1 -1
  12. package/dist/completion/progress-snapshot.js +20 -4
  13. package/dist/completion/progress-snapshot.js.map +1 -1
  14. package/dist/completion/team-completion.d.ts.map +1 -1
  15. package/dist/completion/team-completion.js +32 -2
  16. package/dist/completion/team-completion.js.map +1 -1
  17. package/dist/crash-recovery.d.ts +48 -0
  18. package/dist/crash-recovery.d.ts.map +1 -1
  19. package/dist/crash-recovery.js +166 -30
  20. package/dist/crash-recovery.js.map +1 -1
  21. package/dist/delivery-advance-block.d.ts +17 -1
  22. package/dist/delivery-advance-block.d.ts.map +1 -1
  23. package/dist/delivery-advance-block.js +31 -8
  24. package/dist/delivery-advance-block.js.map +1 -1
  25. package/dist/escalation-provenance.d.ts +5 -1
  26. package/dist/escalation-provenance.d.ts.map +1 -1
  27. package/dist/escalation-provenance.js +46 -4
  28. package/dist/escalation-provenance.js.map +1 -1
  29. package/dist/focus-executor.d.ts +7 -2
  30. package/dist/focus-executor.d.ts.map +1 -1
  31. package/dist/focus-executor.js +21 -9
  32. package/dist/focus-executor.js.map +1 -1
  33. package/dist/focus-lifecycle.d.ts.map +1 -1
  34. package/dist/focus-lifecycle.js +20 -0
  35. package/dist/focus-lifecycle.js.map +1 -1
  36. package/dist/git-activity.d.ts +15 -0
  37. package/dist/git-activity.d.ts.map +1 -1
  38. package/dist/git-activity.js +24 -0
  39. package/dist/git-activity.js.map +1 -1
  40. package/dist/git-genesis.d.ts +100 -0
  41. package/dist/git-genesis.d.ts.map +1 -0
  42. package/dist/git-genesis.js +231 -0
  43. package/dist/git-genesis.js.map +1 -0
  44. package/dist/git.d.ts.map +1 -1
  45. package/dist/git.js +28 -5
  46. package/dist/git.js.map +1 -1
  47. package/dist/loop-engine-types.d.ts +40 -0
  48. package/dist/loop-engine-types.d.ts.map +1 -0
  49. package/dist/loop-engine-types.js +11 -0
  50. package/dist/loop-engine-types.js.map +1 -0
  51. package/dist/loop-engine.d.ts +52 -0
  52. package/dist/loop-engine.d.ts.map +1 -1
  53. package/dist/loop-engine.js +91 -10
  54. package/dist/loop-engine.js.map +1 -1
  55. package/dist/loop-llm-flag.d.ts +32 -0
  56. package/dist/loop-llm-flag.d.ts.map +1 -0
  57. package/dist/loop-llm-flag.js +35 -0
  58. package/dist/loop-llm-flag.js.map +1 -0
  59. package/dist/loop-operational-block.d.ts +44 -0
  60. package/dist/loop-operational-block.d.ts.map +1 -0
  61. package/dist/loop-operational-block.js +47 -0
  62. package/dist/loop-operational-block.js.map +1 -0
  63. package/dist/merge-back-loop.d.ts.map +1 -1
  64. package/dist/merge-back-loop.js +1 -0
  65. package/dist/merge-back-loop.js.map +1 -1
  66. package/dist/nexus-intake.d.ts +26 -3
  67. package/dist/nexus-intake.d.ts.map +1 -1
  68. package/dist/nexus-intake.js +62 -10
  69. package/dist/nexus-intake.js.map +1 -1
  70. package/dist/output-monitor.js +5 -8
  71. package/dist/output-monitor.js.map +1 -1
  72. package/dist/prd-controller.d.ts +0 -2
  73. package/dist/prd-controller.d.ts.map +1 -1
  74. package/dist/prd-controller.js +14 -35
  75. package/dist/prd-controller.js.map +1 -1
  76. package/dist/prd-divergence.d.ts +19 -8
  77. package/dist/prd-divergence.d.ts.map +1 -1
  78. package/dist/prd-divergence.js +7 -26
  79. package/dist/prd-divergence.js.map +1 -1
  80. package/dist/prd-viability.d.ts +14 -15
  81. package/dist/prd-viability.d.ts.map +1 -1
  82. package/dist/prd-viability.js +21 -34
  83. package/dist/prd-viability.js.map +1 -1
  84. package/dist/queries/prd.d.ts +24 -2
  85. package/dist/queries/prd.d.ts.map +1 -1
  86. package/dist/queries/prd.js +17 -0
  87. package/dist/queries/prd.js.map +1 -1
  88. package/dist/queries/schemas.d.ts +28 -0
  89. package/dist/queries/schemas.d.ts.map +1 -1
  90. package/dist/queries/schemas.js +26 -0
  91. package/dist/queries/schemas.js.map +1 -1
  92. package/dist/queries/sessions.d.ts +14 -0
  93. package/dist/queries/sessions.d.ts.map +1 -1
  94. package/dist/queries/sessions.js +19 -0
  95. package/dist/queries/sessions.js.map +1 -1
  96. package/dist/queries/starter-repo.d.ts +27 -0
  97. package/dist/queries/starter-repo.d.ts.map +1 -0
  98. package/dist/queries/starter-repo.js +32 -0
  99. package/dist/queries/starter-repo.js.map +1 -0
  100. package/dist/session-lineage.d.ts +25 -0
  101. package/dist/session-lineage.d.ts.map +1 -1
  102. package/dist/session-lineage.js +23 -0
  103. package/dist/session-lineage.js.map +1 -1
  104. package/dist/session-stamp.d.ts +63 -0
  105. package/dist/session-stamp.d.ts.map +1 -0
  106. package/dist/session-stamp.js +65 -0
  107. package/dist/session-stamp.js.map +1 -0
  108. package/dist/spawner/helpers.d.ts.map +1 -1
  109. package/dist/spawner/helpers.js +5 -8
  110. package/dist/spawner/helpers.js.map +1 -1
  111. package/dist/supabase.d.ts +2 -2
  112. package/dist/supabase.d.ts.map +1 -1
  113. package/dist/supabase.js +1 -1
  114. package/dist/supabase.js.map +1 -1
  115. package/dist/templates/claude-md.d.ts +1 -1
  116. package/dist/templates/claude-md.d.ts.map +1 -1
  117. package/dist/templates/claude-md.js +6 -3
  118. package/dist/templates/claude-md.js.map +1 -1
  119. package/dist/types/focus.d.ts +24 -0
  120. package/dist/types/focus.d.ts.map +1 -1
  121. package/dist/types/session.d.ts +24 -0
  122. package/dist/types/session.d.ts.map +1 -1
  123. package/package.json +2 -2
  124. package/dist/crash-recovery-cleanup.d.ts +0 -21
  125. package/dist/crash-recovery-cleanup.d.ts.map +0 -1
  126. package/dist/crash-recovery-cleanup.js +0 -205
  127. package/dist/crash-recovery-cleanup.js.map +0 -1
  128. package/dist/crash-recovery-scan.d.ts +0 -19
  129. package/dist/crash-recovery-scan.d.ts.map +0 -1
  130. package/dist/crash-recovery-scan.js +0 -145
  131. package/dist/crash-recovery-scan.js.map +0 -1
  132. package/dist/crash-recovery-types.d.ts +0 -49
  133. package/dist/crash-recovery-types.d.ts.map +0 -1
  134. package/dist/crash-recovery-types.js +0 -13
  135. package/dist/crash-recovery-types.js.map +0 -1
  136. package/dist/prd-clearance-engine.d.ts +0 -121
  137. package/dist/prd-clearance-engine.d.ts.map +0 -1
  138. package/dist/prd-clearance-engine.js +0 -164
  139. package/dist/prd-clearance-engine.js.map +0 -1
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Starter-repo genesis: seed an EMPTY product repo from a registered, polished
3
+ * starter the active PRD selected, instead of scaffolding a frontend from zero
4
+ * (focus 80ecc233).
5
+ *
6
+ * When a product's git repo has no commits AND its active PRD carries a
7
+ * `starterRepoId`, the daemon clones the starter's pinned ref, squashes its
8
+ * working tree into a fresh INITIAL commit (starter history is NOT inherited),
9
+ * and records provenance (git URL + resolved sha) both as a `.telora`
10
+ * metadata file and in the genesis commit trailers. The PRD then runs normally
11
+ * against that base -- the agent starts ABOVE the UX floor.
12
+ *
13
+ * It is a strict NO-OP (returns seeded:false with a reason) when the repo
14
+ * already has history (greenfield guard), no active PRD selects a starter, or
15
+ * the selected starter cannot be resolved/cloned -- in every such case the
16
+ * caller falls back to the empty-initial-commit path.
17
+ *
18
+ * Private starters clone via the daemon process's inherited git credentials
19
+ * (ssh-agent / credential helper) -- no special handling here; `git clone`
20
+ * runs in the daemon's environment.
21
+ *
22
+ * All filesystem + git effects are injectable (GenesisDeps) so the path is
23
+ * unit-tested against a local fixture starter repo without a live backend.
24
+ */
25
+ import { mkdtempSync, rmSync, cpSync, writeFileSync, mkdirSync, existsSync, } from 'node:fs';
26
+ import { tmpdir } from 'node:os';
27
+ import { join, sep } from 'node:path';
28
+ import { runGitSync } from './git-types.js';
29
+ import { repoHasCommits } from './git-branch.js';
30
+ import { loadActivePrds, hasPendingStarterSelection } from './queries/prd.js';
31
+ import { loadStarterRepo, } from './queries/starter-repo.js';
32
+ const defaultDeps = {
33
+ hasCommits: repoHasCommits,
34
+ loadActivePrds,
35
+ loadStarterRepo,
36
+ hasPendingStarterSelection,
37
+ runGit: runGitSync,
38
+ log: (msg) => console.log(msg),
39
+ };
40
+ /**
41
+ * The placeholder initial-commit message written when an empty repo has no
42
+ * starter to seed from. Shared so callers can recognise the placeholder.
43
+ */
44
+ export const PLACEHOLDER_COMMIT_MESSAGE = 'chore: initialize repository for Telora';
45
+ // Cloning a polished app (full clone so an arbitrary sha is reachable) can take
46
+ // a while; give it a generous ceiling distinct from the 60s default.
47
+ const CLONE_TIMEOUT_MS = 300_000;
48
+ /**
49
+ * Seed an empty product repo's genesis commit from the starter its active PRD
50
+ * selected. Returns a typed result; the caller decides the fallback.
51
+ */
52
+ export async function seedGenesisFromStarter(config, deps = {}) {
53
+ const d = { ...defaultDeps, ...deps };
54
+ // Greenfield guard: only an empty repo is seeded. A repo with history is left
55
+ // untouched -- a starter is the INITIAL commit, never grafted onto a history.
56
+ if (d.hasCommits(config.repoPath)) {
57
+ return { seeded: false, reason: 'repo-has-history' };
58
+ }
59
+ // Resolve the starter the active PRD selected (first active PRD that carries
60
+ // a selection wins).
61
+ let starterRepoId = null;
62
+ try {
63
+ const prds = await d.loadActivePrds(config.productId);
64
+ starterRepoId = prds.find((p) => p.starterRepoId)?.starterRepoId ?? null;
65
+ }
66
+ catch (err) {
67
+ d.log(`[genesis] could not load active PRDs for product ${config.productId}: ${err.message} -- skipping starter genesis`);
68
+ return { seeded: false, reason: 'no-starter-selected' };
69
+ }
70
+ if (!starterRepoId) {
71
+ d.log('[genesis] no active PRD selects a starter -- using empty genesis');
72
+ return { seeded: false, reason: 'no-starter-selected' };
73
+ }
74
+ // Resolve the starter's clone coordinates.
75
+ let coords = null;
76
+ try {
77
+ coords = await d.loadStarterRepo(starterRepoId);
78
+ }
79
+ catch (err) {
80
+ d.log(`[genesis] could not load starter ${starterRepoId}: ${err.message}`);
81
+ }
82
+ if (!coords) {
83
+ d.log(`[genesis] starter ${starterRepoId} not found/unclonable -- using empty genesis`);
84
+ return { seeded: false, reason: 'starter-not-found', starterRepoId };
85
+ }
86
+ const tmpRoot = mkdtempSync(join(tmpdir(), 'telora-starter-'));
87
+ const cloneDir = join(tmpRoot, 'starter');
88
+ try {
89
+ // Full clone (so a pinned sha is reachable), then check out the exact ref.
90
+ const clone = d.runGit(['clone', coords.gitUrl, cloneDir], tmpRoot, {
91
+ timeoutMs: CLONE_TIMEOUT_MS,
92
+ });
93
+ if (!clone.success) {
94
+ d.log(`[genesis] clone of ${coords.gitUrl} failed: ${clone.error} -- using empty genesis`);
95
+ return { seeded: false, reason: 'clone-failed', starterRepoId, gitUrl: coords.gitUrl, ref: coords.ref };
96
+ }
97
+ const checkout = d.runGit(['checkout', coords.ref], cloneDir, { timeoutMs: CLONE_TIMEOUT_MS });
98
+ if (!checkout.success) {
99
+ d.log(`[genesis] checkout of ref ${coords.ref} failed: ${checkout.error} -- using empty genesis`);
100
+ return { seeded: false, reason: 'clone-failed', starterRepoId, gitUrl: coords.gitUrl, ref: coords.ref };
101
+ }
102
+ const resolved = d.runGit(['rev-parse', 'HEAD'], cloneDir);
103
+ const resolvedSha = resolved.success ? resolved.output.trim() : coords.ref;
104
+ // Copy the starter working tree (MINUS its .git) over the empty product
105
+ // repo. Excluding .git is what squashes the result to a single fresh
106
+ // initial commit -- starter history is not inherited.
107
+ const starterGitDir = join(cloneDir, '.git');
108
+ cpSync(cloneDir, config.repoPath, {
109
+ recursive: true,
110
+ filter: (src) => src !== starterGitDir && !src.startsWith(starterGitDir + sep),
111
+ });
112
+ // Record provenance as repo metadata (force-added past any .telora gitignore)
113
+ // so a product traces back to exactly what it started from.
114
+ const provenance = {
115
+ starterRepoId,
116
+ name: coords.name,
117
+ gitUrl: coords.gitUrl,
118
+ ref: coords.ref,
119
+ resolvedSha,
120
+ seededAt: new Date().toISOString(),
121
+ };
122
+ const teloraDir = join(config.repoPath, '.telora');
123
+ if (!existsSync(teloraDir))
124
+ mkdirSync(teloraDir, { recursive: true });
125
+ writeFileSync(join(teloraDir, 'starter-provenance.json'), `${JSON.stringify(provenance, null, 2)}\n`);
126
+ d.runGit(['add', '-A'], config.repoPath);
127
+ // Force-add the provenance file in case the product's .gitignore drops
128
+ // .telora -- the provenance must land in the genesis commit.
129
+ d.runGit(['add', '-f', '.telora/starter-provenance.json'], config.repoPath);
130
+ const message = `chore: initialize from starter ${coords.name || coords.ref}\n\n` +
131
+ 'Seeded the product genesis commit from a registered starter repo ' +
132
+ '(squashed -- starter history not inherited).\n\n' +
133
+ `Starter-Repo-Id: ${starterRepoId}\n` +
134
+ `Starter-Name: ${coords.name}\n` +
135
+ `Starter-Git-Url: ${coords.gitUrl}\n` +
136
+ `Starter-Ref: ${coords.ref}\n` +
137
+ `Starter-Resolved-Sha: ${resolvedSha}\n`;
138
+ let commit = d.runGit(['commit', '-m', message], config.repoPath);
139
+ if (!commit.success) {
140
+ // The daemon host may have no committer identity configured. Retry with an
141
+ // explicit one scoped to this command only.
142
+ commit = d.runGit([
143
+ '-c',
144
+ 'user.name=Telora Factory',
145
+ '-c',
146
+ 'user.email=factory@telora.local',
147
+ 'commit',
148
+ '-m',
149
+ message,
150
+ ], config.repoPath);
151
+ }
152
+ if (!commit.success) {
153
+ d.log(`[genesis] genesis commit failed: ${commit.error} -- using empty genesis`);
154
+ return {
155
+ seeded: false,
156
+ reason: 'commit-failed',
157
+ starterRepoId,
158
+ gitUrl: coords.gitUrl,
159
+ ref: coords.ref,
160
+ resolvedSha,
161
+ };
162
+ }
163
+ d.log(`[genesis] seeded product genesis from starter "${coords.name}" ` +
164
+ `(${coords.gitUrl} @ ${coords.ref} -> ${resolvedSha.slice(0, 12)})`);
165
+ return {
166
+ seeded: true,
167
+ reason: 'seeded',
168
+ starterRepoId,
169
+ gitUrl: coords.gitUrl,
170
+ ref: coords.ref,
171
+ resolvedSha,
172
+ };
173
+ }
174
+ finally {
175
+ rmSync(tmpRoot, { recursive: true, force: true });
176
+ }
177
+ }
178
+ /**
179
+ * Ensure an empty product repo has its initial commit, preferring a starter
180
+ * genesis. This is the single decision point both daemon startup (initGit) and
181
+ * the per-poll worktree-provisioning safety net share, so the
182
+ * defer-rather-than-foreclose rule is applied consistently (focus 80ecc233).
183
+ *
184
+ * Order:
185
+ * 1. Repo already has commits -> nothing to do.
186
+ * 2. Try to seed from the active PRD's starter.
187
+ * 3. No active starter, BUT a starter-selecting PRD is still draft -> DEFER:
188
+ * leave the repo empty (no foreclosing placeholder). The worktree-provisioning
189
+ * safety net re-runs this once the PRD activates and seeds the genesis then.
190
+ * 4. Otherwise (no starter anywhere, or the clone genuinely failed) -> write an
191
+ * empty placeholder commit so git operations are never wedged.
192
+ */
193
+ export async function ensureInitialCommit(config, deps = {}) {
194
+ const d = { ...defaultDeps, ...deps };
195
+ if (d.hasCommits(config.repoPath)) {
196
+ return { committed: true, seeded: false, deferred: false };
197
+ }
198
+ const genesis = await seedGenesisFromStarter(config, d);
199
+ if (genesis.seeded) {
200
+ return { committed: true, seeded: true, deferred: false, genesis };
201
+ }
202
+ // Defer ONLY for the genuinely-pending case. A clone/commit failure on an
203
+ // active selection (reason !== 'no-starter-selected') must still fall through
204
+ // to a placeholder -- never leave the product permanently without a commit.
205
+ if (genesis.reason === 'no-starter-selected') {
206
+ let pending = false;
207
+ try {
208
+ pending = await d.hasPendingStarterSelection(config.productId);
209
+ }
210
+ catch (err) {
211
+ d.log(`[genesis] could not check pending starter PRDs: ${err.message}`);
212
+ }
213
+ if (pending) {
214
+ d.log('[genesis] a starter-selecting PRD exists but is not active yet -- ' +
215
+ 'deferring the initial commit so genesis can seed once it activates');
216
+ return { committed: false, seeded: false, deferred: true, genesis };
217
+ }
218
+ }
219
+ // Fall back to an empty placeholder initial commit.
220
+ let commit = d.runGit(['commit', '--allow-empty', '-m', PLACEHOLDER_COMMIT_MESSAGE], config.repoPath);
221
+ if (!commit.success) {
222
+ // Retry with an explicit committer identity scoped to this command only.
223
+ commit = d.runGit([
224
+ '-c', 'user.name=Telora Factory',
225
+ '-c', 'user.email=factory@telora.local',
226
+ 'commit', '--allow-empty', '-m', PLACEHOLDER_COMMIT_MESSAGE,
227
+ ], config.repoPath);
228
+ }
229
+ return { committed: commit.success, seeded: false, deferred: false, genesis };
230
+ }
231
+ //# sourceMappingURL=git-genesis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-genesis.js","sourceRoot":"","sources":["../src/git-genesis.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAc/C,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGlJ,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,2BAA2B,EAC3B,kCAAkC,EAClC,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,wBAAwB,EACxB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,6BAA6B,EAClC,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+DjE"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAe/C,OAAO,EAAE,UAAU,EAAE,sBAAsB,EAAE,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,KAAK,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGlJ,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,cAAc,GACf,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,uBAAuB,EACvB,cAAc,EACd,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,kBAAkB,EAClB,cAAc,EACd,6BAA6B,GAC9B,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,uBAAuB,GACxB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,cAAc,EACd,2BAA2B,EAC3B,kCAAkC,EAClC,6BAA6B,EAC7B,6BAA6B,EAC7B,6BAA6B,EAC7B,wBAAwB,EACxB,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,2BAA2B,EAC3B,KAAK,6BAA6B,EAClC,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AAIvB;;GAEG;AACH,wBAAsB,OAAO,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFjE"}
package/dist/git.js CHANGED
@@ -17,6 +17,7 @@ import { configureMergeLock } from './git-merge-lock.js';
17
17
  import { ensureIntegrationBranch, repoHasCommits } from './git-branch.js';
18
18
  import { syncIntegrationWithMain } from './git-integration.js';
19
19
  import { runGitSync } from './git-types.js';
20
+ import { ensureInitialCommit } from './git-genesis.js';
20
21
  // ── Re-exports from sub-modules ─────────────────────────────────
21
22
  // git-types.ts: shared types and utilities
22
23
  export { runGitSync, DEFAULT_GIT_TIMEOUT_MS } from './git-types.js';
@@ -59,15 +60,37 @@ export async function initGit(config) {
59
60
  pruneWorktrees(config.repoPath);
60
61
  // Empty repos need an initial commit before branches or worktrees can be created.
61
62
  // Create one automatically so the full git subsystem initializes immediately.
63
+ // When the product's active PRD selects a starter repo, seed the genesis
64
+ // commit from that polished starter instead of an empty commit (focus
65
+ // 80ecc233) -- the agent then starts ABOVE the UX floor. Falls back to the
66
+ // empty commit when there is no starter / it cannot be cloned.
62
67
  if (!repoHasCommits(config.repoPath)) {
63
- console.log('Repository has no commits -- creating initial commit for Telora...');
64
- const result = runGitSync(['commit', '--allow-empty', '-m', 'chore: initialize repository for Telora'], config.repoPath);
65
- if (!result.success) {
66
- console.error(`Failed to create initial commit: ${result.error}`);
68
+ const init = await ensureInitialCommit({
69
+ repoPath: config.repoPath,
70
+ productId: config.productId,
71
+ });
72
+ if (init.seeded) {
73
+ const g = init.genesis;
74
+ console.log(`Genesis commit seeded from starter ${g?.starterRepoId} ` +
75
+ `(${g?.gitUrl} @ ${g?.ref} -> ${g?.resolvedSha?.slice(0, 12)})`);
76
+ }
77
+ else if (init.deferred) {
78
+ // A starter-selecting PRD is still draft -- leave the repo empty rather
79
+ // than foreclose genesis with a placeholder. The worktree-provisioning
80
+ // safety net seeds it once the PRD activates.
81
+ console.log('Repository has no commits -- deferring initial commit until a ' +
82
+ 'starter-selecting PRD activates (genesis will seed it then)');
83
+ console.log('Git subsystem initialized (limited -- waiting for starter PRD / first commit)');
84
+ return;
85
+ }
86
+ else if (!init.committed) {
87
+ console.error('Failed to create initial commit');
67
88
  console.log('Git subsystem initialized (limited -- waiting for first commit)');
68
89
  return;
69
90
  }
70
- console.log('Initial commit created');
91
+ else {
92
+ console.log(`Repository has no commits (${init.genesis?.reason}) -- created empty initial commit for Telora`);
93
+ }
71
94
  }
72
95
  // Ensure integration branch exists
73
96
  await ensureIntegrationBranch(config);
package/dist/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * API-response shapes consumed by the LoopEngine (loop-engine.ts).
3
+ *
4
+ * Extracted from loop-engine.ts to keep that file under the daemon structural
5
+ * size lint -- the same reason the operational-block guard and the LLM-flag
6
+ * resolver live in their own modules. These are the minimal projections the
7
+ * engine reads from focus_list / loop_document_list / loop_question_list /
8
+ * reality_metrics_focus.
9
+ */
10
+ export interface FocusWithLoopData {
11
+ id: string;
12
+ productId: string;
13
+ organizationId: string;
14
+ name: string;
15
+ status: string;
16
+ assignedAgentRoleId: string | null;
17
+ targetDate: string | null;
18
+ }
19
+ export interface LoopDocumentItem {
20
+ id: string;
21
+ title: string;
22
+ content: string;
23
+ sortOrder: number;
24
+ }
25
+ export interface LoopQuestionItem {
26
+ id: string;
27
+ text: string;
28
+ questionType: string;
29
+ sortOrder: number | null;
30
+ isRetired: boolean;
31
+ }
32
+ export interface RealityMetricsResponse {
33
+ focusId: string;
34
+ metrics: Record<string, unknown> | null;
35
+ projections: {
36
+ completion: Record<string, unknown> | null;
37
+ cost: Record<string, unknown> | null;
38
+ };
39
+ }
40
+ //# sourceMappingURL=loop-engine-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-engine-types.d.ts","sourceRoot":"","sources":["../src/loop-engine-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE;QACX,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;QAC3C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;KACtC,CAAC;CACH"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * API-response shapes consumed by the LoopEngine (loop-engine.ts).
3
+ *
4
+ * Extracted from loop-engine.ts to keep that file under the daemon structural
5
+ * size lint -- the same reason the operational-block guard and the LLM-flag
6
+ * resolver live in their own modules. These are the minimal projections the
7
+ * engine reads from focus_list / loop_document_list / loop_question_list /
8
+ * reality_metrics_focus.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=loop-engine-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop-engine-types.js","sourceRoot":"","sources":["../src/loop-engine-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -14,6 +14,10 @@
14
14
  * 5. shutdown() -- no-op (no processes to kill)
15
15
  */
16
16
  import type { ExecutionEngine, EngineHealth, ResourceUsage, BaseConfig, ResourceGovernor } from '@telora/daemon-core';
17
+ import { type LoopIntentData, type RealityData, type VarianceResult } from './loop-variance.js';
18
+ import { isFocusOperationallyBlocked } from './loop-operational-block.js';
19
+ import { isLoopLlmReCurationEnabled } from './loop-llm-flag.js';
20
+ import type { FocusWithLoopData } from './loop-engine-types.js';
17
21
  export interface LoopEventTriggerConfig {
18
22
  /** Trigger on delivery completion. Default: true. */
19
23
  deliveryCompleted: boolean;
@@ -36,6 +40,41 @@ export interface LoopEngineConfig {
36
40
  /** Event triggers that cause immediate re-evaluation. Default: all enabled. */
37
41
  eventTriggers: LoopEventTriggerConfig;
38
42
  }
43
+ export { isFocusOperationallyBlocked };
44
+ export type { OperationalBlockDeps } from './loop-operational-block.js';
45
+ export { isLoopLlmReCurationEnabled };
46
+ /**
47
+ * Injectable collaborators for {@link LoopEngine.processFocus}.
48
+ *
49
+ * Every field defaults to the engine's real implementation, so an omitted (or
50
+ * partial) override is byte-identical to production. The seam exists ONLY to
51
+ * make the operational-block suppression behavior testable WITHOUT module
52
+ * mocking: `loop-engine.ts`'s transitive graph (queries/shared, worktree-safety,
53
+ * git-types) runtime-imports `@telora/daemon-core`, and `mock.module(...)` is
54
+ * incompatible with that under tsx's experimental resolver (documented in
55
+ * focus-engine-characterization.test.ts). Injecting these collaborators lets a
56
+ * test drive processFocus with the REAL modules and assert that the LLM update
57
+ * is suppressed while a focus is operationally blocked, runs once the block
58
+ * clears, and that the variance snapshot is still persisted while blocked.
59
+ */
60
+ export interface ProcessFocusDeps {
61
+ /** Load intent (loop documents/questions/persona) for the focus. */
62
+ loadIntentData: (focusId: string) => Promise<LoopIntentData>;
63
+ /** Load forecasted-reality (metrics + projections) for the focus. */
64
+ loadRealityData: (focusId: string) => Promise<RealityData>;
65
+ /** Persist the computed variance snapshot (cheap; kept even while blocked). */
66
+ persistVarianceSnapshot: (focus: FocusWithLoopData, variance: VarianceResult) => Promise<void>;
67
+ /** True when the focus is operationally blocked (LLM drift must be suppressed). */
68
+ isFocusOperationallyBlocked: (focusId: string) => Promise<boolean>;
69
+ /**
70
+ * Opt-in gate for the whole LLM re-curation concern (the block guard + the
71
+ * update). Default reads TELORA_LOOP_LLM via isLoopLlmReCurationEnabled (OFF).
72
+ * Injected so a test can exercise the on-path without mutating process.env.
73
+ */
74
+ isLoopLlmReCurationEnabled: () => boolean;
75
+ /** Run the LLM drift update -- the call suppressed while a focus is blocked. */
76
+ runLLMUpdate: (focus: FocusWithLoopData, intent: LoopIntentData, reality: RealityData, variance: VarianceResult) => Promise<void>;
77
+ }
39
78
  export declare class LoopEngine implements ExecutionEngine {
40
79
  readonly name = "loop";
41
80
  private config;
@@ -53,6 +92,19 @@ export declare class LoopEngine implements ExecutionEngine {
53
92
  private lastVariance;
54
93
  /** Focuses that have been triggered by lifecycle events (pending immediate evaluation). */
55
94
  private triggeredFocuses;
95
+ /**
96
+ * Focuses currently in an operational-block episode (LLM drift suppressed).
97
+ * Tracked so we log ONCE per episode (on entry) and resume ONCE (on exit),
98
+ * rather than every poll tick.
99
+ */
100
+ private blockedFocuses;
101
+ /**
102
+ * Focuses for which the "LLM re-curation disabled by default" line has already
103
+ * been logged. The opt-in is a persistent config (not a clearing episode), so
104
+ * the default-OFF skip is logged at most once per focus -- observable without
105
+ * per-tick spam.
106
+ */
107
+ private llmDisabledLoggedFocuses;
56
108
  /** Cleanup function for event bus subscription. */
57
109
  private unsubscribeEvents;
58
110
  setGovernor(gov: ResourceGovernor): void;
@@ -1 +1 @@
1
- {"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../src/loop-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,aAAa,EACb,UAAU,EACV,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAa7B,MAAM,WAAW,sBAAsB;IACrC,qDAAqD;IACrD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,qDAAqD;IACrD,kBAAkB,EAAE,OAAO,CAAC;IAC5B,mDAAmD;IACnD,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,yDAAyD;IACzD,OAAO,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,gBAAgB,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,uFAAuF;IACvF,uBAAuB,EAAE,MAAM,CAAC;IAChC,kGAAkG;IAClG,gBAAgB,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,aAAa,EAAE,sBAAsB,CAAC;CACvC;AA2DD,qBAAa,UAAW,YAAW,eAAe;IAChD,QAAQ,CAAC,IAAI,UAAU;IAEvB,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,UAAU,CAAgD;IAClE,OAAO,CAAC,QAAQ,CAAiC;IAEjD,OAAO,CAAC,WAAW,CAA8C;IACjE,OAAO,CAAC,YAAY,CAAS;IAE7B,8DAA8D;IAC9D,OAAO,CAAC,gBAAgB,CAAK;IAE7B,gDAAgD;IAChD,OAAO,CAAC,UAAU,CAAqB;IAEvC,+CAA+C;IAC/C,OAAO,CAAC,gBAAgB,CAAkC;IAE1D,mEAAmE;IACnE,OAAO,CAAC,YAAY,CAA0C;IAE9D,2FAA2F;IAC3F,OAAO,CAAC,gBAAgB,CAA0B;IAElD,mDAAmD;IACnD,OAAO,CAAC,iBAAiB,CAA6B;IAMtD,WAAW,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IASlC,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAuCjE,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B,IAAI,IAAI,IAAI;IAgBN,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ/B,WAAW,IAAI,YAAY;IAkB3B,gBAAgB,IAAI,aAAa;YAWnB,YAAY;IAqB1B;;;OAGG;YACW,WAAW;IA0CzB;;;OAGG;YACW,YAAY;IAgC1B;;OAEG;YACW,cAAc;IA6C5B;;OAEG;YACW,eAAe;IAe7B;;OAEG;YACW,uBAAuB;IAerC;;;;;;OAMG;IACH,OAAO,CAAC,sBAAsB;IAoE9B;;OAEG;YACW,YAAY;IAqC1B;;OAEG;YACW,gBAAgB;IAsD9B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAqBzB"}
1
+ {"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../src/loop-engine.ts"],"names":[],"mappings":"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"}
@@ -19,6 +19,11 @@ import { computeVariance } from './loop-variance.js';
19
19
  import { buildLoopUpdatePrompt, parseLoopUpdateResponse } from './loop-prompt-builder.js';
20
20
  import { callLoopLLM } from './loop-llm-client.js';
21
21
  import { onLoopTrigger } from './loop-event-bus.js';
22
+ import { hasOpenWorktreeDirtyEscalation } from './queries/issues.js';
23
+ import { getFocusWorktree } from './focus-worktree-state.js';
24
+ import { worktreeHasUncommittedChanges } from './worktree-safety.js';
25
+ import { isFocusOperationallyBlocked } from './loop-operational-block.js';
26
+ import { isLoopLlmReCurationEnabled } from './loop-llm-flag.js';
22
27
  const DEFAULT_EVENT_TRIGGERS = {
23
28
  deliveryCompleted: true,
24
29
  focusStatusChanged: true,
@@ -32,6 +37,15 @@ const DEFAULT_LOOP_CONFIG = {
32
37
  staleThresholdMs: 86_400_000,
33
38
  eventTriggers: { ...DEFAULT_EVENT_TRIGGERS },
34
39
  };
40
+ // Operational-block guard (OperationalBlockDeps + isFocusOperationallyBlocked)
41
+ // lives in ./loop-operational-block.ts; imported above for processFocus's default
42
+ // wiring and re-exported here so existing importers (e.g.
43
+ // loop-engine-operational-block.test.ts) are unaffected.
44
+ export { isFocusOperationallyBlocked };
45
+ // LLM re-curation opt-in flag (default OFF). Extracted to ./loop-llm-flag.ts to
46
+ // keep this file under the structural size lint; re-exported so existing
47
+ // importers (and the flag's tests) reach it from loop-engine.
48
+ export { isLoopLlmReCurationEnabled };
35
49
  // ---------------------------------------------------------------------------
36
50
  // LoopEngine
37
51
  // ---------------------------------------------------------------------------
@@ -52,6 +66,19 @@ export class LoopEngine {
52
66
  lastVariance = new Map();
53
67
  /** Focuses that have been triggered by lifecycle events (pending immediate evaluation). */
54
68
  triggeredFocuses = new Set();
69
+ /**
70
+ * Focuses currently in an operational-block episode (LLM drift suppressed).
71
+ * Tracked so we log ONCE per episode (on entry) and resume ONCE (on exit),
72
+ * rather than every poll tick.
73
+ */
74
+ blockedFocuses = new Set();
75
+ /**
76
+ * Focuses for which the "LLM re-curation disabled by default" line has already
77
+ * been logged. The opt-in is a persistent config (not a clearing episode), so
78
+ * the default-OFF skip is logged at most once per focus -- observable without
79
+ * per-tick spam.
80
+ */
81
+ llmDisabledLoggedFocuses = new Set();
55
82
  /** Cleanup function for event bus subscription. */
56
83
  unsubscribeEvents = null;
57
84
  // -----------------------------------------------------------------------
@@ -222,26 +249,80 @@ export class LoopEngine {
222
249
  * Process a single focus: load intent + reality, compute variance,
223
250
  * optionally call LLM for updates, persist variance snapshot.
224
251
  */
225
- async processFocus(focus) {
252
+ async processFocus(focus, depsOverride) {
253
+ // Resolve injectable collaborators; each defaults to the real
254
+ // implementation so an omitted override is byte-identical to production.
255
+ // The default block-check binds the exact module trio the inline call used.
256
+ const loadIntent = depsOverride?.loadIntentData ?? ((id) => this.loadIntentData(id));
257
+ const loadReality = depsOverride?.loadRealityData ?? ((id) => this.loadRealityData(id));
258
+ const persistVariance = depsOverride?.persistVarianceSnapshot ??
259
+ ((f, v) => this.persistVarianceSnapshot(f, v));
260
+ const checkBlocked = depsOverride?.isFocusOperationallyBlocked ??
261
+ ((id) => isFocusOperationallyBlocked(id, {
262
+ hasOpenWorktreeDirtyEscalation,
263
+ getFocusWorktree,
264
+ worktreeHasUncommittedChanges,
265
+ }));
266
+ const runUpdate = depsOverride?.runLLMUpdate ??
267
+ ((f, i, r, v) => this.runLLMUpdate(f, i, r, v));
268
+ const llmEnabled = depsOverride?.isLoopLlmReCurationEnabled ?? (() => isLoopLlmReCurationEnabled());
226
269
  // 1. Load intent data (loop documents + questions + answers)
227
- const intent = await this.loadIntentData(focus.id);
270
+ const intent = await loadIntent(focus.id);
228
271
  // Skip focuses with no loop data (not yet initialized)
229
272
  if (intent.documents.length === 0 && intent.questions.length === 0) {
230
273
  return;
231
274
  }
232
275
  // 2. Load reality data (metrics + projections)
233
- const reality = await this.loadRealityData(focus.id);
276
+ const reality = await loadReality(focus.id);
234
277
  // 3. Compute variance (pass previous for trend direction)
235
278
  const previousVariance = this.lastVariance.get(focus.id) ?? null;
236
279
  const variance = computeVariance(intent, reality, focus.targetDate ?? null, previousVariance);
237
280
  // 4. Persist variance snapshot
238
- await this.persistVarianceSnapshot(focus, variance);
239
- // 5. Determine if LLM update is needed
240
- const shouldUpdate = this.shouldTriggerLLMUpdate(focus.id, variance, reality);
241
- if (shouldUpdate) {
242
- console.log(`[Loop] Triggering LLM update for focus "${focus.name}" (${focus.id.slice(0, 8)}) -- lock: ${variance.lockStatus}`);
243
- await this.runLLMUpdate(focus, intent, reality, variance);
244
- this.lastEvaluationAt.set(focus.id, Date.now());
281
+ await persistVariance(focus, variance);
282
+ // 4b. LLM re-curation opt-in gate (default OFF -- the OUTER guard). The
283
+ // deterministic variance/lock snapshot above is the load-bearing
284
+ // differential signal and has already been computed + persisted. The LLM
285
+ // re-curation pass (the operational-block check AND the update below) is
286
+ // empirically net-negative even on healthy focuses, so it is suppressed
287
+ // entirely unless TELORA_LOOP_LLM opts in. lastVariance is still stamped
288
+ // (step 6) regardless, so trend comparison stays correct if the pass is
289
+ // later re-enabled. The default-off skip is logged at most once per focus.
290
+ if (llmEnabled()) {
291
+ // 4c. Operational-block guard. Intent-vs-reality drift cannot be reconciled
292
+ // by an LLM while reality is frozen by an operational block (an open
293
+ // advance-block / worktree-dirty escalation, or a dirty worktree). Skip the
294
+ // LLM call entirely while blocked -- otherwise the loop fires repeated no-op
295
+ // "drift" sessions on a stuck focus. The variance snapshot above is kept
296
+ // (cheap + useful), and lastVariance is still stamped (step 6) so trend
297
+ // comparison stays correct when the block clears.
298
+ const blocked = await checkBlocked(focus.id);
299
+ if (blocked) {
300
+ if (!this.blockedFocuses.has(focus.id)) {
301
+ this.blockedFocuses.add(focus.id);
302
+ console.log(`[Loop] Focus "${focus.name}" (${focus.id.slice(0, 8)}) is operationally blocked ` +
303
+ `(open advance-block/worktree-dirty escalation or dirty worktree); ` +
304
+ `suppressing LLM drift update until it clears.`);
305
+ }
306
+ this.lastVariance.set(focus.id, variance);
307
+ return;
308
+ }
309
+ if (this.blockedFocuses.delete(focus.id)) {
310
+ console.log(`[Loop] Focus "${focus.name}" (${focus.id.slice(0, 8)}) operational block cleared; ` +
311
+ `resuming LLM drift updates.`);
312
+ }
313
+ // 5. Determine if LLM update is needed
314
+ const shouldUpdate = this.shouldTriggerLLMUpdate(focus.id, variance, reality);
315
+ if (shouldUpdate) {
316
+ console.log(`[Loop] Triggering LLM update for focus "${focus.name}" (${focus.id.slice(0, 8)}) -- lock: ${variance.lockStatus}`);
317
+ await runUpdate(focus, intent, reality, variance);
318
+ this.lastEvaluationAt.set(focus.id, Date.now());
319
+ }
320
+ }
321
+ else if (!this.llmDisabledLoggedFocuses.has(focus.id)) {
322
+ this.llmDisabledLoggedFocuses.add(focus.id);
323
+ console.log(`[Loop] LLM re-curation disabled by default (TELORA_LOOP_LLM unset) for focus ` +
324
+ `"${focus.name}" (${focus.id.slice(0, 8)}); variance snapshot persisted, ` +
325
+ `skipping LLM drift update.`);
245
326
  }
246
327
  // 6. Store variance for threshold comparison
247
328
  this.lastVariance.set(focus.id, variance);