@karmaniverous/jeeves-meta 0.15.5 → 0.15.7

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.
@@ -10788,6 +10788,22 @@ function rankPhaseCandidates(metas, depthWeight) {
10788
10788
  function selectPhaseCandidate(metas, depthWeight) {
10789
10789
  return rankPhaseCandidates(metas, depthWeight)[0] ?? null;
10790
10790
  }
10791
+ /**
10792
+ * Select the stalest all-fresh, non-disabled, non-locked meta for Tier 2
10793
+ * invalidation. These are metas that Tier 1 considers fully fresh but may
10794
+ * have structural or steer changes detectable only via I/O.
10795
+ *
10796
+ * @param metas - Phase candidate inputs (after Tier 1 filtering).
10797
+ * @returns The stalest all-fresh candidate, or null if none exist.
10798
+ */
10799
+ function selectTier2Candidate(metas) {
10800
+ const eligible = metas
10801
+ .filter((m) => !m.locked && !m.disabled && isFullyFresh(m.phaseState))
10802
+ .sort((a, b) => b.actualStaleness - a.actualStaleness);
10803
+ if (eligible.length === 0)
10804
+ return null;
10805
+ return { node: eligible[0].node, meta: eligible[0].meta };
10806
+ }
10791
10807
 
10792
10808
  /**
10793
10809
  * Shared error utilities.
@@ -11169,7 +11185,8 @@ async function orchestratePhase(config, executor, watcher, targetPath, onProgres
11169
11185
  // Select best phase candidate
11170
11186
  const winner = selectPhaseCandidate(candidates, config.depthWeight);
11171
11187
  if (!winner) {
11172
- return { executed: false };
11188
+ // ── Tier 2 fallback: deep invalidation on stalest all-fresh meta ──
11189
+ return orchestrateTier2(candidates, config, executor, watcher, onProgress, logger);
11173
11190
  }
11174
11191
  // Acquire lock
11175
11192
  if (!acquireLock(winner.node.metaPath)) {
@@ -11235,6 +11252,48 @@ async function orchestrateTargeted(config, executor, watcher, targetPath, onProg
11235
11252
  releaseLock(node.metaPath);
11236
11253
  }
11237
11254
  }
11255
+ /**
11256
+ * Tier 2 invalidation fallback: pick the stalest all-fresh meta,
11257
+ * run computeInvalidation (structure hash, steer, cross-refs), and
11258
+ * either execute the owed phase or bump _generatedAt.
11259
+ */
11260
+ async function orchestrateTier2(candidates, config, executor, watcher, onProgress, logger) {
11261
+ const tier2 = selectTier2Candidate(candidates);
11262
+ if (!tier2)
11263
+ return { executed: false };
11264
+ if (!acquireLock(tier2.node.metaPath)) {
11265
+ logger?.debug({ path: tier2.node.metaPath }, 'Tier 2 candidate is locked, skipping');
11266
+ return { executed: false };
11267
+ }
11268
+ try {
11269
+ const currentMeta = await readMetaJson(tier2.node.metaPath);
11270
+ const { scopeFiles } = await getScopeFiles(tier2.node, watcher);
11271
+ const { phaseState, structureHash } = await computeInvalidation(currentMeta, scopeFiles, config, tier2.node);
11272
+ const owedPhase = getOwedPhase(phaseState);
11273
+ if (owedPhase) {
11274
+ // Something changed — persist invalidated state and execute owed phase
11275
+ await persistPhaseState({
11276
+ metaPath: tier2.node.metaPath,
11277
+ current: currentMeta,
11278
+ config,
11279
+ structureHash,
11280
+ }, phaseState, {});
11281
+ return await executePhase(tier2.node, currentMeta, phaseState, owedPhase, config, executor, watcher, structureHash, onProgress, logger);
11282
+ }
11283
+ // Nothing changed — bump _generatedAt to delay re-checking
11284
+ await persistPhaseState({
11285
+ metaPath: tier2.node.metaPath,
11286
+ current: currentMeta,
11287
+ config,
11288
+ structureHash,
11289
+ }, phaseState, { _generatedAt: new Date().toISOString() });
11290
+ logger?.debug({ path: tier2.node.ownerPath }, 'Tier 2: no invalidation detected, bumped _generatedAt');
11291
+ return { executed: false };
11292
+ }
11293
+ finally {
11294
+ releaseLock(tier2.node.metaPath);
11295
+ }
11296
+ }
11238
11297
  /**
11239
11298
  * Execute exactly one phase on a meta.
11240
11299
  */
package/dist/index.js CHANGED
@@ -10339,6 +10339,22 @@ function rankPhaseCandidates(metas, depthWeight) {
10339
10339
  function selectPhaseCandidate(metas, depthWeight) {
10340
10340
  return rankPhaseCandidates(metas, depthWeight)[0] ?? null;
10341
10341
  }
10342
+ /**
10343
+ * Select the stalest all-fresh, non-disabled, non-locked meta for Tier 2
10344
+ * invalidation. These are metas that Tier 1 considers fully fresh but may
10345
+ * have structural or steer changes detectable only via I/O.
10346
+ *
10347
+ * @param metas - Phase candidate inputs (after Tier 1 filtering).
10348
+ * @returns The stalest all-fresh candidate, or null if none exist.
10349
+ */
10350
+ function selectTier2Candidate(metas) {
10351
+ const eligible = metas
10352
+ .filter((m) => !m.locked && !m.disabled && isFullyFresh(m.phaseState))
10353
+ .sort((a, b) => b.actualStaleness - a.actualStaleness);
10354
+ if (eligible.length === 0)
10355
+ return null;
10356
+ return { node: eligible[0].node, meta: eligible[0].meta };
10357
+ }
10342
10358
 
10343
10359
  /**
10344
10360
  * Shared error utilities.
@@ -10720,7 +10736,8 @@ async function orchestratePhase(config, executor, watcher, targetPath, onProgres
10720
10736
  // Select best phase candidate
10721
10737
  const winner = selectPhaseCandidate(candidates, config.depthWeight);
10722
10738
  if (!winner) {
10723
- return { executed: false };
10739
+ // ── Tier 2 fallback: deep invalidation on stalest all-fresh meta ──
10740
+ return orchestrateTier2(candidates, config, executor, watcher, onProgress, logger);
10724
10741
  }
10725
10742
  // Acquire lock
10726
10743
  if (!acquireLock(winner.node.metaPath)) {
@@ -10786,6 +10803,48 @@ async function orchestrateTargeted(config, executor, watcher, targetPath, onProg
10786
10803
  releaseLock(node.metaPath);
10787
10804
  }
10788
10805
  }
10806
+ /**
10807
+ * Tier 2 invalidation fallback: pick the stalest all-fresh meta,
10808
+ * run computeInvalidation (structure hash, steer, cross-refs), and
10809
+ * either execute the owed phase or bump _generatedAt.
10810
+ */
10811
+ async function orchestrateTier2(candidates, config, executor, watcher, onProgress, logger) {
10812
+ const tier2 = selectTier2Candidate(candidates);
10813
+ if (!tier2)
10814
+ return { executed: false };
10815
+ if (!acquireLock(tier2.node.metaPath)) {
10816
+ logger?.debug({ path: tier2.node.metaPath }, 'Tier 2 candidate is locked, skipping');
10817
+ return { executed: false };
10818
+ }
10819
+ try {
10820
+ const currentMeta = await readMetaJson(tier2.node.metaPath);
10821
+ const { scopeFiles } = await getScopeFiles(tier2.node, watcher);
10822
+ const { phaseState, structureHash } = await computeInvalidation(currentMeta, scopeFiles, config, tier2.node);
10823
+ const owedPhase = getOwedPhase(phaseState);
10824
+ if (owedPhase) {
10825
+ // Something changed — persist invalidated state and execute owed phase
10826
+ await persistPhaseState({
10827
+ metaPath: tier2.node.metaPath,
10828
+ current: currentMeta,
10829
+ config,
10830
+ structureHash,
10831
+ }, phaseState, {});
10832
+ return await executePhase(tier2.node, currentMeta, phaseState, owedPhase, config, executor, watcher, structureHash, onProgress, logger);
10833
+ }
10834
+ // Nothing changed — bump _generatedAt to delay re-checking
10835
+ await persistPhaseState({
10836
+ metaPath: tier2.node.metaPath,
10837
+ current: currentMeta,
10838
+ config,
10839
+ structureHash,
10840
+ }, phaseState, { _generatedAt: new Date().toISOString() });
10841
+ logger?.debug({ path: tier2.node.ownerPath }, 'Tier 2: no invalidation detected, bumped _generatedAt');
10842
+ return { executed: false };
10843
+ }
10844
+ finally {
10845
+ releaseLock(tier2.node.metaPath);
10846
+ }
10847
+ }
10789
10848
  /**
10790
10849
  * Execute exactly one phase on a meta.
10791
10850
  */
@@ -5,5 +5,5 @@
5
5
  */
6
6
  export { type DerivationInputs, derivePhaseState } from './derivePhaseState.js';
7
7
  export { type ArchitectInvalidator, computeInvalidation, type InvalidationResult, type StalenessInputs, } from './invalidate.js';
8
- export { buildPhaseCandidates, type PhaseCandidate, type PhaseCandidateInput, rankPhaseCandidates, selectPhaseCandidate, } from './phaseScheduler.js';
8
+ export { buildPhaseCandidates, type PhaseCandidate, type PhaseCandidateInput, rankPhaseCandidates, selectPhaseCandidate, selectTier2Candidate, type Tier2Candidate, } from './phaseScheduler.js';
9
9
  export { architectSuccess, builderSuccess, criticSuccess, enforceInvariant, freshPhaseState, getOwedPhase, getPriorityBand, initialPhaseState, invalidateArchitect, invalidateBuilder, isFullyFresh, phaseFailed, phaseRunning, retryAllFailed, retryPhase, } from './phaseTransitions.js';
@@ -55,3 +55,17 @@ export declare function rankPhaseCandidates(metas: PhaseCandidateInput[], depthW
55
55
  * @returns The winning candidate, or null if no phase is ready.
56
56
  */
57
57
  export declare function selectPhaseCandidate(metas: PhaseCandidateInput[], depthWeight: number): PhaseCandidate | null;
58
+ /** Result of Tier 2 candidate selection. */
59
+ export interface Tier2Candidate {
60
+ node: MetaNode;
61
+ meta: MetaJson;
62
+ }
63
+ /**
64
+ * Select the stalest all-fresh, non-disabled, non-locked meta for Tier 2
65
+ * invalidation. These are metas that Tier 1 considers fully fresh but may
66
+ * have structural or steer changes detectable only via I/O.
67
+ *
68
+ * @param metas - Phase candidate inputs (after Tier 1 filtering).
69
+ * @returns The stalest all-fresh candidate, or null if none exist.
70
+ */
71
+ export declare function selectTier2Candidate(metas: PhaseCandidateInput[]): Tier2Candidate | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@karmaniverous/jeeves-meta",
3
- "version": "0.15.5",
3
+ "version": "0.15.7",
4
4
  "author": "Jason Williscroft",
5
5
  "description": "Fastify HTTP service for the Jeeves Meta synthesis engine",
6
6
  "license": "BSD-3-Clause",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@karmaniverous/jeeves": "^0.5.10",
45
- "@karmaniverous/jeeves-meta-core": "^0.1.0",
45
+ "@karmaniverous/jeeves-meta-core": "^0.1.1",
46
46
  "commander": "^14",
47
47
  "croner": "^10",
48
48
  "fastify": "^5.8",