feat(FN-4394): complete Step 1 — add heartbeat scope discipline types and defaults
Fusion-Task-Id: FN-4394 Fusion-Task-Lineage: 1e7ad660-2cd2-40ba-b7b8-7ef552c6fa13
This commit is contained in:
@@ -89,6 +89,12 @@ describe("settings key parity", () => {
|
||||
expect(isGlobalSettingsKey("autoClaimCandidatesInPrompt")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps heartbeatScopeDiscipline project-scoped with strict default", () => {
|
||||
expect(DEFAULT_PROJECT_SETTINGS.heartbeatScopeDiscipline).toBe("strict");
|
||||
expect(isProjectSettingsKey("heartbeatScopeDiscipline")).toBe(true);
|
||||
expect(isGlobalSettingsKey("heartbeatScopeDiscipline")).toBe(false);
|
||||
});
|
||||
|
||||
it("documents autoClaimCandidatesInPrompt expected integer range", () => {
|
||||
const inRange = [0, 1, 5, 10];
|
||||
const outOfRange = [-1, 11, 100];
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
validateDirectMergeCommitStrategy,
|
||||
validateGithubAuthMode,
|
||||
validateGithubRepoSlug,
|
||||
validateHeartbeatScopeDisciplineMode,
|
||||
validateUnavailableNodePolicy,
|
||||
} from "../settings-validation.js";
|
||||
|
||||
@@ -47,6 +48,20 @@ describe("settings-validation", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateHeartbeatScopeDisciplineMode", () => {
|
||||
it("accepts supported modes", () => {
|
||||
expect(validateHeartbeatScopeDisciplineMode("strict")).toBe("strict");
|
||||
expect(validateHeartbeatScopeDisciplineMode("lite")).toBe("lite");
|
||||
expect(validateHeartbeatScopeDisciplineMode("off")).toBe("off");
|
||||
});
|
||||
|
||||
it("returns undefined for invalid values", () => {
|
||||
expect(validateHeartbeatScopeDisciplineMode("minimal")).toBeUndefined();
|
||||
expect(validateHeartbeatScopeDisciplineMode(123)).toBeUndefined();
|
||||
expect(validateHeartbeatScopeDisciplineMode(undefined)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validateGithubRepoSlug", () => {
|
||||
it("accepts valid owner/repo slugs", () => {
|
||||
expect(validateGithubRepoSlug("owner/repo")).toBe("owner/repo");
|
||||
|
||||
@@ -172,6 +172,7 @@ export const DEFAULT_PROJECT_SETTINGS = {
|
||||
pollIntervalMs: 15000,
|
||||
heartbeatMultiplier: 1,
|
||||
autoClaimCandidatesInPrompt: 5,
|
||||
heartbeatScopeDiscipline: "strict",
|
||||
groupOverlappingFiles: true,
|
||||
overlapIgnorePaths: [],
|
||||
autoMerge: true,
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import type { DirectMergeCommitStrategy, GithubAuthMode, UnavailableNodePolicy } from "./types.js";
|
||||
import type {
|
||||
DirectMergeCommitStrategy,
|
||||
GithubAuthMode,
|
||||
HeartbeatScopeDisciplineMode,
|
||||
UnavailableNodePolicy,
|
||||
} from "./types.js";
|
||||
|
||||
const UNAVAILABLE_NODE_POLICIES: readonly UnavailableNodePolicy[] = ["block", "fallback-local"] as const;
|
||||
const DIRECT_MERGE_COMMIT_STRATEGIES: readonly DirectMergeCommitStrategy[] = ["auto", "always-squash", "always-rebase"] as const;
|
||||
const GITHUB_AUTH_MODES: readonly GithubAuthMode[] = ["gh-cli", "token"] as const;
|
||||
const GITHUB_REPO_SLUG_PATTERN = /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/;
|
||||
const HEARTBEAT_SCOPE_DISCIPLINE_MODES: readonly HeartbeatScopeDisciplineMode[] = [
|
||||
"strict",
|
||||
"lite",
|
||||
"off",
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Validates a project unavailable-node routing policy value.
|
||||
@@ -60,3 +70,16 @@ export function validateGithubRepoSlug(value: unknown): string | undefined {
|
||||
}
|
||||
return GITHUB_REPO_SLUG_PATTERN.test(trimmed) ? trimmed : undefined;
|
||||
}
|
||||
|
||||
/** Returns a validated heartbeat scope-discipline mode for project/agent settings, otherwise undefined. */
|
||||
export function validateHeartbeatScopeDisciplineMode(value: unknown): HeartbeatScopeDisciplineMode | undefined {
|
||||
if (value === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (typeof value !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
return (HEARTBEAT_SCOPE_DISCIPLINE_MODES as readonly string[]).includes(value)
|
||||
? (value as HeartbeatScopeDisciplineMode)
|
||||
: undefined;
|
||||
}
|
||||
|
||||
@@ -1665,6 +1665,7 @@ export interface ResolvedEvalSettings {
|
||||
}
|
||||
|
||||
export type AgentMemoryInclusionMode = "full" | "index" | "off";
|
||||
export type HeartbeatScopeDisciplineMode = "strict" | "lite" | "off";
|
||||
|
||||
export interface GlobalSettings {
|
||||
/** Theme mode preference: dark, light, or system (follows OS). Default: "dark". */
|
||||
@@ -2095,6 +2096,12 @@ export interface ProjectSettings {
|
||||
heartbeatMultiplier?: number;
|
||||
/** Number of auto-claim candidates rendered in no-task heartbeat prompts. Range: 0-10. Default: 5. */
|
||||
autoClaimCandidatesInPrompt?: number;
|
||||
/** Heartbeat scope-discipline procedure mode.
|
||||
* - "strict": coordination-focused scope discipline (default)
|
||||
* - "lite": pre-FN-3884 behavior
|
||||
* - "off": minimal procedure with no scope-classification step
|
||||
*/
|
||||
heartbeatScopeDiscipline?: HeartbeatScopeDisciplineMode;
|
||||
groupOverlappingFiles: boolean;
|
||||
/** File/directory paths to ignore when evaluating overlap serialization.
|
||||
* Entries are project-relative paths (for example: `docs/README.md`, `docs/`, `generated/*`).
|
||||
@@ -4689,6 +4696,8 @@ export interface AgentHeartbeatConfig {
|
||||
budgetConfig?: AgentBudgetConfig;
|
||||
/** Per-agent override for memory prompt inclusion mode. */
|
||||
agentMemoryInclusionMode?: AgentMemoryInclusionMode;
|
||||
/** Per-agent override for heartbeat scope-discipline procedure mode. */
|
||||
heartbeatScopeDiscipline?: HeartbeatScopeDisciplineMode;
|
||||
/** Last resolved memory inclusion mode recorded by engine for transition logging. */
|
||||
lastAgentMemoryInclusionMode?: AgentMemoryInclusionMode;
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user