@karmaniverous/jeeves-meta 0.15.6 → 0.15.8
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.
|
@@ -9356,6 +9356,8 @@ const serviceConfigSchema = metaConfigSchema.extend({
|
|
|
9356
9356
|
watcherHealthIntervalMs: z.number().int().min(0).default(60_000),
|
|
9357
9357
|
/** Logging configuration. */
|
|
9358
9358
|
logging: loggingSchema.default(() => loggingSchema.parse({})),
|
|
9359
|
+
/** Max number of all-fresh candidates to scan per tick in Tier 2 invalidation. */
|
|
9360
|
+
tier2ScanLimit: z.number().int().min(1).default(50),
|
|
9359
9361
|
/**
|
|
9360
9362
|
* Auto-seed policy: declarative rules for auto-creating .meta/ directories.
|
|
9361
9363
|
* Rules are evaluated in order; last match wins for steer/crossRefs.
|
|
@@ -10788,6 +10790,16 @@ function rankPhaseCandidates(metas, depthWeight) {
|
|
|
10788
10790
|
function selectPhaseCandidate(metas, depthWeight) {
|
|
10789
10791
|
return rankPhaseCandidates(metas, depthWeight)[0] ?? null;
|
|
10790
10792
|
}
|
|
10793
|
+
/**
|
|
10794
|
+
* Select all fully-fresh, non-disabled, non-locked metas sorted by staleness
|
|
10795
|
+
* (descending — stalest first) for Tier 2 invalidation scanning.
|
|
10796
|
+
*/
|
|
10797
|
+
function selectAllTier2Candidates(metas) {
|
|
10798
|
+
return metas
|
|
10799
|
+
.filter((m) => !m.locked && !m.disabled && isFullyFresh(m.phaseState))
|
|
10800
|
+
.sort((a, b) => b.actualStaleness - a.actualStaleness)
|
|
10801
|
+
.map((m) => ({ node: m.node, meta: m.meta }));
|
|
10802
|
+
}
|
|
10791
10803
|
|
|
10792
10804
|
/**
|
|
10793
10805
|
* Shared error utilities.
|
|
@@ -11169,6 +11181,8 @@ async function orchestratePhase(config, executor, watcher, targetPath, onProgres
|
|
|
11169
11181
|
// Select best phase candidate
|
|
11170
11182
|
const winner = selectPhaseCandidate(candidates, config.depthWeight);
|
|
11171
11183
|
if (!winner) {
|
|
11184
|
+
// Tier 2 is now handled by the scheduler; orchestratePhase only handles
|
|
11185
|
+
// targeted (override) paths and Tier 1 corpus-wide selection.
|
|
11172
11186
|
return { executed: false };
|
|
11173
11187
|
}
|
|
11174
11188
|
// Acquire lock
|
|
@@ -12212,7 +12226,7 @@ class Scheduler {
|
|
|
12212
12226
|
const candidates = buildPhaseCandidates(result.entries, this.config.architectEvery);
|
|
12213
12227
|
const winner = selectPhaseCandidate(candidates, this.config.depthWeight);
|
|
12214
12228
|
if (!winner)
|
|
12215
|
-
return
|
|
12229
|
+
return await this.discoverTier2Phase(candidates);
|
|
12216
12230
|
return {
|
|
12217
12231
|
path: winner.node.metaPath,
|
|
12218
12232
|
phase: winner.owedPhase,
|
|
@@ -12224,6 +12238,57 @@ class Scheduler {
|
|
|
12224
12238
|
return null;
|
|
12225
12239
|
}
|
|
12226
12240
|
}
|
|
12241
|
+
/**
|
|
12242
|
+
* Tier 2 invalidation: iterate all-fresh candidates (stalest first),
|
|
12243
|
+
* run computeInvalidation, and return the first that produces an owed phase.
|
|
12244
|
+
*/
|
|
12245
|
+
async discoverTier2Phase(candidates) {
|
|
12246
|
+
const allTier2 = selectAllTier2Candidates(candidates);
|
|
12247
|
+
const limit = this.config.tier2ScanLimit;
|
|
12248
|
+
const tier2Candidates = allTier2.slice(0, limit);
|
|
12249
|
+
if (allTier2.length > limit) {
|
|
12250
|
+
this.logger.debug({ total: allTier2.length, limit }, 'Tier 2 scan limit reached, scanning subset');
|
|
12251
|
+
}
|
|
12252
|
+
let dirty = false;
|
|
12253
|
+
for (const t2 of tier2Candidates) {
|
|
12254
|
+
if (!acquireLock(t2.node.metaPath))
|
|
12255
|
+
continue;
|
|
12256
|
+
try {
|
|
12257
|
+
const currentMeta = await readMetaJson(t2.node.metaPath);
|
|
12258
|
+
const { scopeFiles } = await getScopeFiles(t2.node, this.watcher);
|
|
12259
|
+
const result = await computeInvalidation(currentMeta, scopeFiles, this.config, t2.node);
|
|
12260
|
+
const owedPhase = getOwedPhase(result.phaseState);
|
|
12261
|
+
if (owedPhase) {
|
|
12262
|
+
await persistPhaseState({
|
|
12263
|
+
metaPath: t2.node.metaPath,
|
|
12264
|
+
current: currentMeta,
|
|
12265
|
+
config: this.config,
|
|
12266
|
+
structureHash: result.structureHash,
|
|
12267
|
+
}, result.phaseState, {});
|
|
12268
|
+
this.cache.invalidate();
|
|
12269
|
+
return {
|
|
12270
|
+
path: t2.node.metaPath,
|
|
12271
|
+
phase: owedPhase,
|
|
12272
|
+
band: getPriorityBand(result.phaseState),
|
|
12273
|
+
};
|
|
12274
|
+
}
|
|
12275
|
+
// No invalidation — bump _generatedAt to delay re-checking
|
|
12276
|
+
await persistPhaseState({
|
|
12277
|
+
metaPath: t2.node.metaPath,
|
|
12278
|
+
current: currentMeta,
|
|
12279
|
+
config: this.config,
|
|
12280
|
+
structureHash: result.structureHash,
|
|
12281
|
+
}, result.phaseState, { _generatedAt: new Date().toISOString() });
|
|
12282
|
+
dirty = true;
|
|
12283
|
+
}
|
|
12284
|
+
finally {
|
|
12285
|
+
releaseLock(t2.node.metaPath);
|
|
12286
|
+
}
|
|
12287
|
+
}
|
|
12288
|
+
if (dirty)
|
|
12289
|
+
this.cache.invalidate();
|
|
12290
|
+
return null;
|
|
12291
|
+
}
|
|
12227
12292
|
}
|
|
12228
12293
|
|
|
12229
12294
|
/**
|
package/dist/index.js
CHANGED
|
@@ -9050,6 +9050,8 @@ const serviceConfigSchema = metaConfigSchema.extend({
|
|
|
9050
9050
|
watcherHealthIntervalMs: z.number().int().min(0).default(60_000),
|
|
9051
9051
|
/** Logging configuration. */
|
|
9052
9052
|
logging: loggingSchema.default(() => loggingSchema.parse({})),
|
|
9053
|
+
/** Max number of all-fresh candidates to scan per tick in Tier 2 invalidation. */
|
|
9054
|
+
tier2ScanLimit: z.number().int().min(1).default(50),
|
|
9053
9055
|
/**
|
|
9054
9056
|
* Auto-seed policy: declarative rules for auto-creating .meta/ directories.
|
|
9055
9057
|
* Rules are evaluated in order; last match wins for steer/crossRefs.
|
|
@@ -10339,6 +10341,16 @@ function rankPhaseCandidates(metas, depthWeight) {
|
|
|
10339
10341
|
function selectPhaseCandidate(metas, depthWeight) {
|
|
10340
10342
|
return rankPhaseCandidates(metas, depthWeight)[0] ?? null;
|
|
10341
10343
|
}
|
|
10344
|
+
/**
|
|
10345
|
+
* Select all fully-fresh, non-disabled, non-locked metas sorted by staleness
|
|
10346
|
+
* (descending — stalest first) for Tier 2 invalidation scanning.
|
|
10347
|
+
*/
|
|
10348
|
+
function selectAllTier2Candidates(metas) {
|
|
10349
|
+
return metas
|
|
10350
|
+
.filter((m) => !m.locked && !m.disabled && isFullyFresh(m.phaseState))
|
|
10351
|
+
.sort((a, b) => b.actualStaleness - a.actualStaleness)
|
|
10352
|
+
.map((m) => ({ node: m.node, meta: m.meta }));
|
|
10353
|
+
}
|
|
10342
10354
|
|
|
10343
10355
|
/**
|
|
10344
10356
|
* Shared error utilities.
|
|
@@ -10720,6 +10732,8 @@ async function orchestratePhase(config, executor, watcher, targetPath, onProgres
|
|
|
10720
10732
|
// Select best phase candidate
|
|
10721
10733
|
const winner = selectPhaseCandidate(candidates, config.depthWeight);
|
|
10722
10734
|
if (!winner) {
|
|
10735
|
+
// Tier 2 is now handled by the scheduler; orchestratePhase only handles
|
|
10736
|
+
// targeted (override) paths and Tier 1 corpus-wide selection.
|
|
10723
10737
|
return { executed: false };
|
|
10724
10738
|
}
|
|
10725
10739
|
// Acquire lock
|
|
@@ -11763,7 +11777,7 @@ class Scheduler {
|
|
|
11763
11777
|
const candidates = buildPhaseCandidates(result.entries, this.config.architectEvery);
|
|
11764
11778
|
const winner = selectPhaseCandidate(candidates, this.config.depthWeight);
|
|
11765
11779
|
if (!winner)
|
|
11766
|
-
return
|
|
11780
|
+
return await this.discoverTier2Phase(candidates);
|
|
11767
11781
|
return {
|
|
11768
11782
|
path: winner.node.metaPath,
|
|
11769
11783
|
phase: winner.owedPhase,
|
|
@@ -11775,6 +11789,57 @@ class Scheduler {
|
|
|
11775
11789
|
return null;
|
|
11776
11790
|
}
|
|
11777
11791
|
}
|
|
11792
|
+
/**
|
|
11793
|
+
* Tier 2 invalidation: iterate all-fresh candidates (stalest first),
|
|
11794
|
+
* run computeInvalidation, and return the first that produces an owed phase.
|
|
11795
|
+
*/
|
|
11796
|
+
async discoverTier2Phase(candidates) {
|
|
11797
|
+
const allTier2 = selectAllTier2Candidates(candidates);
|
|
11798
|
+
const limit = this.config.tier2ScanLimit;
|
|
11799
|
+
const tier2Candidates = allTier2.slice(0, limit);
|
|
11800
|
+
if (allTier2.length > limit) {
|
|
11801
|
+
this.logger.debug({ total: allTier2.length, limit }, 'Tier 2 scan limit reached, scanning subset');
|
|
11802
|
+
}
|
|
11803
|
+
let dirty = false;
|
|
11804
|
+
for (const t2 of tier2Candidates) {
|
|
11805
|
+
if (!acquireLock(t2.node.metaPath))
|
|
11806
|
+
continue;
|
|
11807
|
+
try {
|
|
11808
|
+
const currentMeta = await readMetaJson(t2.node.metaPath);
|
|
11809
|
+
const { scopeFiles } = await getScopeFiles(t2.node, this.watcher);
|
|
11810
|
+
const result = await computeInvalidation(currentMeta, scopeFiles, this.config, t2.node);
|
|
11811
|
+
const owedPhase = getOwedPhase(result.phaseState);
|
|
11812
|
+
if (owedPhase) {
|
|
11813
|
+
await persistPhaseState({
|
|
11814
|
+
metaPath: t2.node.metaPath,
|
|
11815
|
+
current: currentMeta,
|
|
11816
|
+
config: this.config,
|
|
11817
|
+
structureHash: result.structureHash,
|
|
11818
|
+
}, result.phaseState, {});
|
|
11819
|
+
this.cache.invalidate();
|
|
11820
|
+
return {
|
|
11821
|
+
path: t2.node.metaPath,
|
|
11822
|
+
phase: owedPhase,
|
|
11823
|
+
band: getPriorityBand(result.phaseState),
|
|
11824
|
+
};
|
|
11825
|
+
}
|
|
11826
|
+
// No invalidation — bump _generatedAt to delay re-checking
|
|
11827
|
+
await persistPhaseState({
|
|
11828
|
+
metaPath: t2.node.metaPath,
|
|
11829
|
+
current: currentMeta,
|
|
11830
|
+
config: this.config,
|
|
11831
|
+
structureHash: result.structureHash,
|
|
11832
|
+
}, result.phaseState, { _generatedAt: new Date().toISOString() });
|
|
11833
|
+
dirty = true;
|
|
11834
|
+
}
|
|
11835
|
+
finally {
|
|
11836
|
+
releaseLock(t2.node.metaPath);
|
|
11837
|
+
}
|
|
11838
|
+
}
|
|
11839
|
+
if (dirty)
|
|
11840
|
+
this.cache.invalidate();
|
|
11841
|
+
return null;
|
|
11842
|
+
}
|
|
11778
11843
|
}
|
|
11779
11844
|
|
|
11780
11845
|
/**
|
|
@@ -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, selectAllTier2Candidates, 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,22 @@ 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;
|
|
72
|
+
/**
|
|
73
|
+
* Select all fully-fresh, non-disabled, non-locked metas sorted by staleness
|
|
74
|
+
* (descending — stalest first) for Tier 2 invalidation scanning.
|
|
75
|
+
*/
|
|
76
|
+
export declare function selectAllTier2Candidates(metas: PhaseCandidateInput[]): Tier2Candidate[];
|
|
@@ -63,4 +63,9 @@ export declare class Scheduler {
|
|
|
63
63
|
* with weighted staleness as tiebreaker within a band.
|
|
64
64
|
*/
|
|
65
65
|
private discoverNextPhase;
|
|
66
|
+
/**
|
|
67
|
+
* Tier 2 invalidation: iterate all-fresh candidates (stalest first),
|
|
68
|
+
* run computeInvalidation, and return the first that produces an owed phase.
|
|
69
|
+
*/
|
|
70
|
+
private discoverTier2Phase;
|
|
66
71
|
}
|
package/dist/schema/config.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export declare const serviceConfigSchema: z.ZodObject<{
|
|
|
44
44
|
level: z.ZodDefault<z.ZodString>;
|
|
45
45
|
file: z.ZodOptional<z.ZodString>;
|
|
46
46
|
}, z.core.$strip>>;
|
|
47
|
+
tier2ScanLimit: z.ZodDefault<z.ZodNumber>;
|
|
47
48
|
autoSeed: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
48
49
|
match: z.ZodString;
|
|
49
50
|
steer: z.ZodOptional<z.ZodString>;
|