auto-dispatch: silence expected registry fallback on non-auto commands
sf headless query and sf headless status call resolveDispatch() without going through auto-mode startup, so the rule-registry singleton is never initialized. The previous code caught getRegistry()'s init error and logged a warning on every call — noise on the normal path: [sf:dispatch] WARN: registry dispatch failed, falling back to inline rules: RuleRegistry not initialized — call initRegistry() or setRegistry() first. Now: hasRegistry() probe first. When unset, skip straight to the inline rule loop without warning (it's the intended behavior outside auto). When the registry IS set and evaluateDispatch() genuinely throws, log the warning so real bugs still surface. Adds hasRegistry() as a public helper for any other hot-path caller that wants to branch on init without try/catch overhead. Verified end-to-end: sf headless query and sf headless status in dr-repo now run clean, no false warning. All 25 rule-registry tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
56130c2e39
commit
3bea082f20
2 changed files with 19 additions and 8 deletions
|
|
@ -900,7 +900,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
|
|||
},
|
||||
];
|
||||
|
||||
import { getRegistry } from "./rule-registry.js";
|
||||
import { getRegistry, hasRegistry } from "./rule-registry.js";
|
||||
|
||||
// ─── Resolver ─────────────────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -915,13 +915,18 @@ import { getRegistry } from "./rule-registry.js";
|
|||
export async function resolveDispatch(
|
||||
ctx: DispatchContext,
|
||||
): Promise<DispatchAction> {
|
||||
// Delegate to registry when available
|
||||
try {
|
||||
const registry = getRegistry();
|
||||
return await registry.evaluateDispatch(ctx);
|
||||
} catch (err) {
|
||||
// Registry not initialized — fall back to inline loop
|
||||
logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
|
||||
// Delegate to registry when available. Callers that run outside auto-mode
|
||||
// (e.g. `sf headless query`, `sf headless status`) never initialize the
|
||||
// registry — falling through to inline rules is the intended behavior,
|
||||
// not an error, so we silent-probe instead of warning on every call.
|
||||
if (hasRegistry()) {
|
||||
try {
|
||||
return await getRegistry().evaluateDispatch(ctx);
|
||||
} catch (err) {
|
||||
// Genuine registry evaluation failure (rule threw, etc.) — log so we
|
||||
// surface real bugs, then fall back.
|
||||
logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
|
||||
}
|
||||
}
|
||||
|
||||
for (const rule of DISPATCH_RULES) {
|
||||
|
|
|
|||
|
|
@ -561,6 +561,12 @@ export class RuleRegistry {
|
|||
|
||||
let _registry: RuleRegistry | null = null;
|
||||
|
||||
/** True when the singleton registry has been initialized. Lets callers branch
|
||||
* without paying for try/catch on a hot path. */
|
||||
export function hasRegistry(): boolean {
|
||||
return _registry !== null;
|
||||
}
|
||||
|
||||
/** Get the singleton registry. Throws if not initialized. */
|
||||
export function getRegistry(): RuleRegistry {
|
||||
if (!_registry) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue