@ai-sdlc/orchestrator 0.6.0 → 0.10.0
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.
- package/dist/__test-helpers/git-env.d.ts +40 -0
- package/dist/__test-helpers/git-env.js +73 -0
- package/dist/admission-composite.d.ts +89 -0
- package/dist/admission-composite.js +241 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +341 -0
- package/dist/admission-hc.d.ts +81 -0
- package/dist/admission-hc.js +94 -0
- package/dist/admission-score.d.ts +109 -5
- package/dist/admission-score.js +90 -11
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +74 -0
- package/dist/cli/commands/git-remote.js +170 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init-features.d.ts +208 -0
- package/dist/cli/commands/init-features.js +473 -0
- package/dist/cli/commands/init-templates.d.ts +104 -0
- package/dist/cli/commands/init-templates.js +399 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.js +322 -23
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/design-authority.d.ts +53 -0
- package/dist/design-authority.js +84 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +188 -15
- package/dist/fix-ci.js +8 -1
- package/dist/fix-review.js +8 -1
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/models/classifier.d.ts +78 -0
- package/dist/models/classifier.js +277 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +85 -0
- package/dist/pillar-breakdown.js +162 -0
- package/dist/priority.d.ts +5 -0
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +4 -0
- package/dist/review.js +12 -2
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +30 -66
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +3 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/review-agent.d.ts +20 -0
- package/dist/runners/review-agent.js +171 -41
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +39 -15
- package/dist/runners/types.d.ts +6 -0
- package/dist/runtime/attestations.d.ts +766 -0
- package/dist/runtime/attestations.js +1195 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +28 -0
- package/dist/runtime/parallelism-flag.js +39 -0
- package/dist/runtime/port-allocator.d.ts +32 -0
- package/dist/runtime/port-allocator.js +96 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/schema.d.ts +5 -1
- package/dist/state/schema.js +223 -1
- package/dist/state/store.d.ts +55 -1
- package/dist/state/store.js +342 -7
- package/dist/state/types.d.ts +139 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/package.json +3 -2
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { readFile, realpath, stat } from 'node:fs/promises';
|
|
2
|
+
import { dirname, isAbsolute, join, resolve, sep } from 'node:path';
|
|
3
|
+
const SLUG_INVALID = /[^a-zA-Z0-9._-]+/g;
|
|
4
|
+
const SLUG_DEDUPE = /-+/g;
|
|
5
|
+
export class WorktreeOwnershipError extends Error {
|
|
6
|
+
worktreePath;
|
|
7
|
+
expectedClone;
|
|
8
|
+
actualClone;
|
|
9
|
+
constructor(message, worktreePath, expectedClone, actualClone) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.worktreePath = worktreePath;
|
|
12
|
+
this.expectedClone = expectedClone;
|
|
13
|
+
this.actualClone = actualClone;
|
|
14
|
+
this.name = 'WorktreeOwnershipError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function slugifyBranch(branch) {
|
|
18
|
+
if (!branch) {
|
|
19
|
+
throw new Error('Branch name cannot be empty');
|
|
20
|
+
}
|
|
21
|
+
const slug = branch
|
|
22
|
+
.replace(SLUG_INVALID, '-')
|
|
23
|
+
.replace(SLUG_DEDUPE, '-')
|
|
24
|
+
.replace(/^-+|-+$/g, '');
|
|
25
|
+
if (!slug) {
|
|
26
|
+
throw new Error(`Branch name has no slug-safe characters: ${branch}`);
|
|
27
|
+
}
|
|
28
|
+
return slug;
|
|
29
|
+
}
|
|
30
|
+
export function worktreePath(rootDir, branch) {
|
|
31
|
+
return join(rootDir, slugifyBranch(branch));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Verify a worktree at `worktreePath` was created from the clone at `expectedClonePath`.
|
|
35
|
+
*
|
|
36
|
+
* A git worktree contains a `.git` *file* (not directory) that points at the parent clone's
|
|
37
|
+
* `.git/worktrees/<name>/` directory. We parse the pointer and confirm it resolves under
|
|
38
|
+
* `expectedClonePath/.git/worktrees/`. Any mismatch returns owned=false with a structured reason.
|
|
39
|
+
*/
|
|
40
|
+
export async function verifyOwnership(worktreePathArg, expectedClonePath) {
|
|
41
|
+
const expectedWorktreesDir = resolve(expectedClonePath, '.git', 'worktrees');
|
|
42
|
+
// Canonicalize the expected dir to follow symlinks (e.g., macOS /var → /private/var).
|
|
43
|
+
// realpath fails if the path doesn't exist; fall back to resolve() in that case.
|
|
44
|
+
let canonicalExpected = expectedWorktreesDir;
|
|
45
|
+
try {
|
|
46
|
+
canonicalExpected = await realpath(expectedWorktreesDir);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
/* directory doesn't exist yet — keep the resolved (non-canonical) path */
|
|
50
|
+
}
|
|
51
|
+
const dotGitPath = join(worktreePathArg, '.git');
|
|
52
|
+
let pointer;
|
|
53
|
+
try {
|
|
54
|
+
const buf = await readFile(dotGitPath);
|
|
55
|
+
pointer = buf.toString('utf8').trim();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return {
|
|
59
|
+
owned: false,
|
|
60
|
+
expectedClone: canonicalExpected,
|
|
61
|
+
actualClone: null,
|
|
62
|
+
reason: 'pointer-missing',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// Pointer file format per git docs: `gitdir: <absolute-path>` or `gitdir: <relative-path>`.
|
|
66
|
+
const match = pointer.match(/^gitdir:\s*(.+)$/);
|
|
67
|
+
if (!match) {
|
|
68
|
+
return {
|
|
69
|
+
owned: false,
|
|
70
|
+
expectedClone: canonicalExpected,
|
|
71
|
+
actualClone: pointer || null,
|
|
72
|
+
reason: 'pointer-malformed',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const declared = match[1].trim();
|
|
76
|
+
const declaredAbs = isAbsolute(declared) ? declared : resolve(dirname(dotGitPath), declared);
|
|
77
|
+
// Canonicalize the declared path the same way; if it doesn't exist, the worktree is
|
|
78
|
+
// detached and we can fall back to the resolved string.
|
|
79
|
+
let canonicalDeclared = resolve(declaredAbs);
|
|
80
|
+
try {
|
|
81
|
+
canonicalDeclared = await realpath(declaredAbs);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
/* declared dir doesn't exist — use the resolved string for comparison */
|
|
85
|
+
}
|
|
86
|
+
// The pointer should land inside `<expectedClone>/.git/worktrees/<name>/`.
|
|
87
|
+
const owned = canonicalDeclared === canonicalExpected ||
|
|
88
|
+
canonicalDeclared.startsWith(canonicalExpected + sep);
|
|
89
|
+
return {
|
|
90
|
+
owned,
|
|
91
|
+
expectedClone: canonicalExpected,
|
|
92
|
+
actualClone: canonicalDeclared,
|
|
93
|
+
reason: owned ? 'ok' : 'cross-clone',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
export async function assertOwnership(worktreePathArg, expectedClonePath) {
|
|
97
|
+
const result = await verifyOwnership(worktreePathArg, expectedClonePath);
|
|
98
|
+
if (!result.owned) {
|
|
99
|
+
throw new WorktreeOwnershipError(`Worktree ownership verification failed for ${worktreePathArg}: ${result.reason}`, worktreePathArg, result.expectedClone, result.actualClone);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export async function isExistingWorktree(worktreePathArg) {
|
|
103
|
+
try {
|
|
104
|
+
const dotGit = await stat(join(worktreePathArg, '.git'));
|
|
105
|
+
return dotGit.isFile();
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=worktree.js.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 weight auto-calibration (RFC-0008 Addendum B §B.8).
|
|
3
|
+
*
|
|
4
|
+
* Reads trailing-90-day precision from `SAFeedbackStore`, shifts the
|
|
5
|
+
* Phase 3 (w_structural, w_llm) pair in the direction of the better-
|
|
6
|
+
* performing layer, and persists the result to `sa_phase_weights`.
|
|
7
|
+
*
|
|
8
|
+
* CR-2: `w_structural ≥ 0.20` floor enforced on the output, as is a
|
|
9
|
+
* symmetric `w_llm ≥ 0.20` floor — extreme single-layer dominance is
|
|
10
|
+
* never written.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent: if the computed weights round-trip to the currently
|
|
13
|
+
* persisted pair, the row is not written and the caller's diff is
|
|
14
|
+
* empty. Rolling 90-day window by default.
|
|
15
|
+
*/
|
|
16
|
+
import type { SaDimension } from '../state/types.js';
|
|
17
|
+
import type { StateStore } from '../state/store.js';
|
|
18
|
+
import type { SAFeedbackStore } from './feedback-store.js';
|
|
19
|
+
import type { PhaseWeights } from './composite.js';
|
|
20
|
+
export declare const WEIGHT_FLOOR = 0.2;
|
|
21
|
+
export declare const WEIGHT_CEILING: number;
|
|
22
|
+
export declare const DEFAULT_SHIFT_SIZE = 0.05;
|
|
23
|
+
export declare const PRECISION_DELTA_THRESHOLD = 0.1;
|
|
24
|
+
/** Default calibration window — 90 days. */
|
|
25
|
+
export declare const DEFAULT_WINDOW_DAYS = 90;
|
|
26
|
+
/** Phase 2c default weights — used as starting point if none persisted. */
|
|
27
|
+
export declare const DEFAULT_STARTING_WEIGHTS: PhaseWeights;
|
|
28
|
+
export interface PrecisionPair {
|
|
29
|
+
structural: number;
|
|
30
|
+
llm: number;
|
|
31
|
+
}
|
|
32
|
+
export interface CalibrationDecision {
|
|
33
|
+
/** 'toward-structural' | 'toward-llm' | 'hold' */
|
|
34
|
+
direction: 'toward-structural' | 'toward-llm' | 'hold';
|
|
35
|
+
/** Magnitude of the LLM−structural precision difference. */
|
|
36
|
+
delta: number;
|
|
37
|
+
}
|
|
38
|
+
export declare function decideCalibrationDirection(precision: PrecisionPair): CalibrationDecision;
|
|
39
|
+
export interface ComputePhase3WeightsInput {
|
|
40
|
+
current: PhaseWeights;
|
|
41
|
+
precision: PrecisionPair;
|
|
42
|
+
shiftSize?: number;
|
|
43
|
+
}
|
|
44
|
+
export declare function computePhase3Weights(input: ComputePhase3WeightsInput): PhaseWeights;
|
|
45
|
+
export interface AutoCalibrateDeps {
|
|
46
|
+
feedback: SAFeedbackStore;
|
|
47
|
+
stateStore: StateStore;
|
|
48
|
+
/** Clock injection for deterministic tests. */
|
|
49
|
+
now?: () => number;
|
|
50
|
+
/** Override the window (default 90d). */
|
|
51
|
+
windowDays?: number;
|
|
52
|
+
/** Starting weights when none are persisted. */
|
|
53
|
+
startingWeights?: PhaseWeights;
|
|
54
|
+
/** Adjustment step size per calibration run. */
|
|
55
|
+
shiftSize?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface DimensionDiff {
|
|
58
|
+
dimension: SaDimension;
|
|
59
|
+
precision: PrecisionPair;
|
|
60
|
+
previous: PhaseWeights;
|
|
61
|
+
next: PhaseWeights;
|
|
62
|
+
changed: boolean;
|
|
63
|
+
}
|
|
64
|
+
export interface AutoCalibrateResult {
|
|
65
|
+
diffs: DimensionDiff[];
|
|
66
|
+
}
|
|
67
|
+
export declare function autoCalibratePhaseWeights(deps: AutoCalibrateDeps): Promise<AutoCalibrateResult>;
|
|
68
|
+
export declare function renderCalibrationDiff(result: AutoCalibrateResult): string;
|
|
69
|
+
//# sourceMappingURL=auto-calibrate.d.ts.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 3 weight auto-calibration (RFC-0008 Addendum B §B.8).
|
|
3
|
+
*
|
|
4
|
+
* Reads trailing-90-day precision from `SAFeedbackStore`, shifts the
|
|
5
|
+
* Phase 3 (w_structural, w_llm) pair in the direction of the better-
|
|
6
|
+
* performing layer, and persists the result to `sa_phase_weights`.
|
|
7
|
+
*
|
|
8
|
+
* CR-2: `w_structural ≥ 0.20` floor enforced on the output, as is a
|
|
9
|
+
* symmetric `w_llm ≥ 0.20` floor — extreme single-layer dominance is
|
|
10
|
+
* never written.
|
|
11
|
+
*
|
|
12
|
+
* Idempotent: if the computed weights round-trip to the currently
|
|
13
|
+
* persisted pair, the row is not written and the caller's diff is
|
|
14
|
+
* empty. Rolling 90-day window by default.
|
|
15
|
+
*/
|
|
16
|
+
import { W_STRUCTURAL_FLOOR } from './composite.js';
|
|
17
|
+
// ── Defaults and constants ──────────────────────────────────────────
|
|
18
|
+
export const WEIGHT_FLOOR = W_STRUCTURAL_FLOOR; // 0.20
|
|
19
|
+
export const WEIGHT_CEILING = 1 - WEIGHT_FLOOR; // 0.80
|
|
20
|
+
export const DEFAULT_SHIFT_SIZE = 0.05;
|
|
21
|
+
export const PRECISION_DELTA_THRESHOLD = 0.1;
|
|
22
|
+
/** Default calibration window — 90 days. */
|
|
23
|
+
export const DEFAULT_WINDOW_DAYS = 90;
|
|
24
|
+
/** Phase 2c default weights — used as starting point if none persisted. */
|
|
25
|
+
export const DEFAULT_STARTING_WEIGHTS = Object.freeze({
|
|
26
|
+
wStructural: 0.35,
|
|
27
|
+
wLlm: 0.65,
|
|
28
|
+
});
|
|
29
|
+
export function decideCalibrationDirection(precision) {
|
|
30
|
+
const delta = precision.llm - precision.structural;
|
|
31
|
+
if (delta > PRECISION_DELTA_THRESHOLD)
|
|
32
|
+
return { direction: 'toward-llm', delta };
|
|
33
|
+
if (delta < -PRECISION_DELTA_THRESHOLD)
|
|
34
|
+
return { direction: 'toward-structural', delta };
|
|
35
|
+
return { direction: 'hold', delta };
|
|
36
|
+
}
|
|
37
|
+
export function computePhase3Weights(input) {
|
|
38
|
+
const shift = input.shiftSize ?? DEFAULT_SHIFT_SIZE;
|
|
39
|
+
const decision = decideCalibrationDirection(input.precision);
|
|
40
|
+
let wStructural = input.current.wStructural;
|
|
41
|
+
if (decision.direction === 'toward-llm')
|
|
42
|
+
wStructural -= shift;
|
|
43
|
+
else if (decision.direction === 'toward-structural')
|
|
44
|
+
wStructural += shift;
|
|
45
|
+
wStructural = Math.max(WEIGHT_FLOOR, Math.min(WEIGHT_CEILING, wStructural));
|
|
46
|
+
return { wStructural, wLlm: 1 - wStructural };
|
|
47
|
+
}
|
|
48
|
+
export async function autoCalibratePhaseWeights(deps) {
|
|
49
|
+
const nowMs = (deps.now ?? (() => Date.now()))();
|
|
50
|
+
const windowDays = deps.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
51
|
+
const since = new Date(nowMs - windowDays * 24 * 60 * 60 * 1000).toISOString();
|
|
52
|
+
const startingWeights = deps.startingWeights ?? DEFAULT_STARTING_WEIGHTS;
|
|
53
|
+
const diffs = [];
|
|
54
|
+
for (const dimension of ['SA-1', 'SA-2']) {
|
|
55
|
+
const existing = deps.stateStore.getSaPhaseWeights(dimension);
|
|
56
|
+
const current = existing
|
|
57
|
+
? { wStructural: existing.wStructural, wLlm: existing.wLlm }
|
|
58
|
+
: startingWeights;
|
|
59
|
+
const structural = deps.feedback.structuralPrecision({ dimension, since });
|
|
60
|
+
const llm = deps.feedback.llmPrecision({ dimension, since });
|
|
61
|
+
const precision = {
|
|
62
|
+
structural: structural.precision,
|
|
63
|
+
llm: llm.precision,
|
|
64
|
+
};
|
|
65
|
+
const next = computePhase3Weights({
|
|
66
|
+
current,
|
|
67
|
+
precision,
|
|
68
|
+
shiftSize: deps.shiftSize,
|
|
69
|
+
});
|
|
70
|
+
const changed = !weightsEqual(current, next) || !existing; /* first-run write */
|
|
71
|
+
if (changed) {
|
|
72
|
+
deps.stateStore.upsertSaPhaseWeights({
|
|
73
|
+
dimension,
|
|
74
|
+
wStructural: next.wStructural,
|
|
75
|
+
wLlm: next.wLlm,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
diffs.push({
|
|
79
|
+
dimension,
|
|
80
|
+
precision,
|
|
81
|
+
previous: current,
|
|
82
|
+
next,
|
|
83
|
+
// `changed` is true when we actually wrote: either the weight pair
|
|
84
|
+
// shifted, OR this was the mandatory first-run initialisation.
|
|
85
|
+
changed,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { diffs };
|
|
89
|
+
}
|
|
90
|
+
function weightsEqual(a, b) {
|
|
91
|
+
return Math.abs(a.wStructural - b.wStructural) < 1e-9 && Math.abs(a.wLlm - b.wLlm) < 1e-9;
|
|
92
|
+
}
|
|
93
|
+
// ── Diff rendering (for CLI) ─────────────────────────────────────────
|
|
94
|
+
export function renderCalibrationDiff(result) {
|
|
95
|
+
const lines = [];
|
|
96
|
+
lines.push('SA phase-weight calibration diff');
|
|
97
|
+
lines.push('--------------------------------');
|
|
98
|
+
for (const d of result.diffs) {
|
|
99
|
+
lines.push(`${d.dimension}:`);
|
|
100
|
+
lines.push(` previous : w_structural=${d.previous.wStructural.toFixed(3)} w_llm=${d.previous.wLlm.toFixed(3)}`);
|
|
101
|
+
lines.push(` next : w_structural=${d.next.wStructural.toFixed(3)} w_llm=${d.next.wLlm.toFixed(3)}`);
|
|
102
|
+
lines.push(` precision: structural=${(d.precision.structural * 100).toFixed(1)}% llm=${(d.precision.llm * 100).toFixed(1)}%`);
|
|
103
|
+
lines.push(` changed : ${d.changed ? 'yes' : 'no'}`);
|
|
104
|
+
}
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=auto-calibrate.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C1 — SA-2 computable component (RFC-0008 v4 §5.2).
|
|
3
|
+
*
|
|
4
|
+
* SA-2 ("soul alignment with design system") has two halves:
|
|
5
|
+
* - Computable: deterministic formula over DSB status fields
|
|
6
|
+
* - LLM: structured assessment of principle coverage (M5)
|
|
7
|
+
*
|
|
8
|
+
* This module implements the computable half only. LLM is returned as
|
|
9
|
+
* `null` until M5 (AISDLC-59/60) replaces it.
|
|
10
|
+
*
|
|
11
|
+
* Formula (§5.2):
|
|
12
|
+
* computableComponent = 0.3 × tokenCompliance + 0.2 × catalogHealth
|
|
13
|
+
*
|
|
14
|
+
* Where `tokenCompliance` and `catalogHealth` are normalized to [0, 1]
|
|
15
|
+
* from the DSB's published status.
|
|
16
|
+
*/
|
|
17
|
+
import type { DesignIntentDocument, DesignSystemBinding } from '@ai-sdlc/reference';
|
|
18
|
+
export interface Sa2ComputableResult {
|
|
19
|
+
/** Normalized token-compliance value in [0, 1]. */
|
|
20
|
+
tokenCompliance: number;
|
|
21
|
+
/** Normalized catalog-health coverage in [0, 1]. */
|
|
22
|
+
catalogHealth: number;
|
|
23
|
+
/** 0.3 × tokenCompliance + 0.2 × catalogHealth. */
|
|
24
|
+
computableComponent: number;
|
|
25
|
+
/** LLM component — filled by M5. Always `null` in Phase 1. */
|
|
26
|
+
llmComponent: null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Compute the SA-2 computable half from a DID and its resolved DSB.
|
|
30
|
+
*
|
|
31
|
+
* Returns `undefined` (caller falls back to label-based `soulAlignment`)
|
|
32
|
+
* when:
|
|
33
|
+
* - `dsb` is undefined, OR
|
|
34
|
+
* - Neither `tokenCompliance.currentCoverage` nor `catalogHealth.coveragePercent`
|
|
35
|
+
* is present on the DSB status (can't compute either term).
|
|
36
|
+
*
|
|
37
|
+
* The DID is accepted for API symmetry and future extension (e.g. when
|
|
38
|
+
* individual principles select which DSB metrics to include); §5.2
|
|
39
|
+
* itself only reads DSB status.
|
|
40
|
+
*/
|
|
41
|
+
export declare function computeSa2Computable(did: DesignIntentDocument, dsb: DesignSystemBinding | undefined): Sa2ComputableResult | undefined;
|
|
42
|
+
//# sourceMappingURL=c1-sa2-computable.d.ts.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C1 — SA-2 computable component (RFC-0008 v4 §5.2).
|
|
3
|
+
*
|
|
4
|
+
* SA-2 ("soul alignment with design system") has two halves:
|
|
5
|
+
* - Computable: deterministic formula over DSB status fields
|
|
6
|
+
* - LLM: structured assessment of principle coverage (M5)
|
|
7
|
+
*
|
|
8
|
+
* This module implements the computable half only. LLM is returned as
|
|
9
|
+
* `null` until M5 (AISDLC-59/60) replaces it.
|
|
10
|
+
*
|
|
11
|
+
* Formula (§5.2):
|
|
12
|
+
* computableComponent = 0.3 × tokenCompliance + 0.2 × catalogHealth
|
|
13
|
+
*
|
|
14
|
+
* Where `tokenCompliance` and `catalogHealth` are normalized to [0, 1]
|
|
15
|
+
* from the DSB's published status.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Compute the SA-2 computable half from a DID and its resolved DSB.
|
|
19
|
+
*
|
|
20
|
+
* Returns `undefined` (caller falls back to label-based `soulAlignment`)
|
|
21
|
+
* when:
|
|
22
|
+
* - `dsb` is undefined, OR
|
|
23
|
+
* - Neither `tokenCompliance.currentCoverage` nor `catalogHealth.coveragePercent`
|
|
24
|
+
* is present on the DSB status (can't compute either term).
|
|
25
|
+
*
|
|
26
|
+
* The DID is accepted for API symmetry and future extension (e.g. when
|
|
27
|
+
* individual principles select which DSB metrics to include); §5.2
|
|
28
|
+
* itself only reads DSB status.
|
|
29
|
+
*/
|
|
30
|
+
export function computeSa2Computable(did, dsb) {
|
|
31
|
+
void did;
|
|
32
|
+
if (!dsb?.status)
|
|
33
|
+
return undefined;
|
|
34
|
+
const rawTokenCoverage = dsb.status.tokenCompliance?.currentCoverage;
|
|
35
|
+
const rawCatalogCoverage = dsb.status.catalogHealth?.coveragePercent;
|
|
36
|
+
if (rawTokenCoverage === undefined && rawCatalogCoverage === undefined) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
const tokenCompliance = normalizeCoverage(rawTokenCoverage);
|
|
40
|
+
const catalogHealth = normalizeCoverage(rawCatalogCoverage);
|
|
41
|
+
const computableComponent = 0.3 * tokenCompliance + 0.2 * catalogHealth;
|
|
42
|
+
return {
|
|
43
|
+
tokenCompliance,
|
|
44
|
+
catalogHealth,
|
|
45
|
+
computableComponent,
|
|
46
|
+
llmComponent: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Normalize a coverage value to [0, 1]. Accepts percentages (0–100) or
|
|
51
|
+
* ratios (0–1). Missing values are treated as 0.
|
|
52
|
+
*/
|
|
53
|
+
function normalizeCoverage(value) {
|
|
54
|
+
if (value === undefined || Number.isNaN(value))
|
|
55
|
+
return 0;
|
|
56
|
+
const clamped = value > 1 ? value / 100 : value;
|
|
57
|
+
return Math.min(1, Math.max(0, clamped));
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=c1-sa2-computable.js.map
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-1 / SA-2 composite (RFC-0008 Addendum B §B.7).
|
|
3
|
+
*
|
|
4
|
+
* Combines Layer 1 deterministic results, Layer 2 BM25 structural
|
|
5
|
+
* scores, and Layer 3 LLM structured assessments into the two
|
|
6
|
+
* soul-alignment dimensions used by the admission composite.
|
|
7
|
+
*
|
|
8
|
+
* SA-1 per §B.7.1:
|
|
9
|
+
* hard gate: scopeGate core → SA-1 = 0.0 (STOP)
|
|
10
|
+
* coreConflictPenalty = min(0.8, coreViolationCount × 0.4)
|
|
11
|
+
* evolvingConflictPenalty = min(0.3, evolvingViolationCount × 0.1)
|
|
12
|
+
* conflictPenalty = 1.0 − coreConflictPenalty − evolvingConflictPenalty
|
|
13
|
+
* subtleMult = 0.5 if any high-severity conflict, else 1.0
|
|
14
|
+
* blended = w_s × domainRelevance + w_l × domainIntent × subtleMult
|
|
15
|
+
* SA-1 = blended × conflictPenalty
|
|
16
|
+
*
|
|
17
|
+
* SA-2 per corrected §B.7.2 (CR-1 — no self-multiplication):
|
|
18
|
+
* computableScore = 0.3 × tokenCompliance + 0.2 × catalogHealth
|
|
19
|
+
* designConflictPenalty = 1.0 − min(0.6, coreAp × 0.3 + evolvingAp × 0.1)
|
|
20
|
+
* llmTerm = w_l × principleAlignment × subtleMult
|
|
21
|
+
* blendedScore = w_s × principleCoverage + llmTerm
|
|
22
|
+
* llmComponent = blendedScore × designConflictPenalty
|
|
23
|
+
* SA-2 = computableScore + 0.5 × llmComponent
|
|
24
|
+
*
|
|
25
|
+
* Phase weights (§B.7.3):
|
|
26
|
+
* 2a shadow → (0, 0) — computed but not used in ranking
|
|
27
|
+
* 2b blended → (0.20, 0.80)
|
|
28
|
+
* 2c calibrating → (0.35, 0.65)
|
|
29
|
+
* 3 calibrated → flywheel-driven; w_structural floored at 0.20 (CR-2)
|
|
30
|
+
*/
|
|
31
|
+
import type { SubtleConflict, SubtleDesignConflict } from './layer3-llm.js';
|
|
32
|
+
export type SaPhase = '2a' | '2b' | '2c' | '3';
|
|
33
|
+
export interface PhaseWeights {
|
|
34
|
+
wStructural: number;
|
|
35
|
+
wLlm: number;
|
|
36
|
+
}
|
|
37
|
+
/** CR-2: Phase 3 must never drop w_structural below this floor. */
|
|
38
|
+
export declare const W_STRUCTURAL_FLOOR = 0.2;
|
|
39
|
+
export declare function getPhaseWeights(phase: SaPhase, calibrated?: PhaseWeights): PhaseWeights;
|
|
40
|
+
export interface Sa1Inputs {
|
|
41
|
+
/** True when Layer 1 scope gate flagged a core out-of-scope hit. */
|
|
42
|
+
hardGated: boolean;
|
|
43
|
+
coreViolationCount: number;
|
|
44
|
+
evolvingViolationCount: number;
|
|
45
|
+
/** Layer 2 SA-1 domainRelevance in [0, 1]. */
|
|
46
|
+
domainRelevance: number;
|
|
47
|
+
/** Layer 3 SA-1 domainIntent in [0, 1] (already confidence-filtered). */
|
|
48
|
+
domainIntent: number;
|
|
49
|
+
/** Layer 3 SA-1 subtle conflicts (already confidence-filtered). */
|
|
50
|
+
subtleConflicts: SubtleConflict[];
|
|
51
|
+
}
|
|
52
|
+
export interface Sa2Inputs {
|
|
53
|
+
/** From DSB status, normalized to [0, 1]. */
|
|
54
|
+
tokenCompliance: number;
|
|
55
|
+
/** From DSB status, normalized to [0, 1]. */
|
|
56
|
+
catalogHealth: number;
|
|
57
|
+
/** Layer 2 SA-2 principleCoverage (weighted mean) in [0, 1]. */
|
|
58
|
+
principleCoverage: number;
|
|
59
|
+
/** Layer 3 SA-2 principleAlignment in [0, 1] (confidence-filtered). */
|
|
60
|
+
principleAlignment: number;
|
|
61
|
+
/** Count of core-classified design anti-pattern hits. */
|
|
62
|
+
coreDesignAntiPatternCount: number;
|
|
63
|
+
/** Count of evolving-classified design anti-pattern hits. */
|
|
64
|
+
evolvingDesignAntiPatternCount: number;
|
|
65
|
+
/** Layer 3 SA-2 subtle design conflicts (confidence-filtered). */
|
|
66
|
+
subtleDesignConflicts: SubtleDesignConflict[];
|
|
67
|
+
}
|
|
68
|
+
export interface Sa1Result {
|
|
69
|
+
sa1: number;
|
|
70
|
+
hardGated: boolean;
|
|
71
|
+
/** Structural contribution: w_s × domainRelevance. */
|
|
72
|
+
structuralContribution: number;
|
|
73
|
+
/** LLM contribution: w_l × domainIntent × subtleMult. */
|
|
74
|
+
llmContribution: number;
|
|
75
|
+
blended: number;
|
|
76
|
+
conflictPenalty: number;
|
|
77
|
+
subtleMult: number;
|
|
78
|
+
}
|
|
79
|
+
export interface Sa2Result {
|
|
80
|
+
sa2: number;
|
|
81
|
+
computableScore: number;
|
|
82
|
+
structuralContribution: number;
|
|
83
|
+
llmContribution: number;
|
|
84
|
+
blendedScore: number;
|
|
85
|
+
designConflictPenalty: number;
|
|
86
|
+
subtleMult: number;
|
|
87
|
+
llmComponent: number;
|
|
88
|
+
}
|
|
89
|
+
export interface SoulAlignmentResult {
|
|
90
|
+
phase: SaPhase;
|
|
91
|
+
weights: PhaseWeights;
|
|
92
|
+
/** True for Phase 2a — consumers must NOT use sa1/sa2 in ranking. */
|
|
93
|
+
shadowMode: boolean;
|
|
94
|
+
sa1: Sa1Result;
|
|
95
|
+
sa2: Sa2Result;
|
|
96
|
+
}
|
|
97
|
+
export declare function computeSa1(inputs: Sa1Inputs, weights: PhaseWeights): Sa1Result;
|
|
98
|
+
export declare function computeSa2(inputs: Sa2Inputs, weights: PhaseWeights): Sa2Result;
|
|
99
|
+
export interface SoulAlignmentInput {
|
|
100
|
+
phase: SaPhase;
|
|
101
|
+
/** Required for Phase 3 if calibrated weights differ from the 2c default. */
|
|
102
|
+
calibratedWeights?: PhaseWeights;
|
|
103
|
+
sa1: Sa1Inputs;
|
|
104
|
+
sa2: Sa2Inputs;
|
|
105
|
+
}
|
|
106
|
+
export declare function computeSoulAlignment(input: SoulAlignmentInput): SoulAlignmentResult;
|
|
107
|
+
//# sourceMappingURL=composite.d.ts.map
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SA-1 / SA-2 composite (RFC-0008 Addendum B §B.7).
|
|
3
|
+
*
|
|
4
|
+
* Combines Layer 1 deterministic results, Layer 2 BM25 structural
|
|
5
|
+
* scores, and Layer 3 LLM structured assessments into the two
|
|
6
|
+
* soul-alignment dimensions used by the admission composite.
|
|
7
|
+
*
|
|
8
|
+
* SA-1 per §B.7.1:
|
|
9
|
+
* hard gate: scopeGate core → SA-1 = 0.0 (STOP)
|
|
10
|
+
* coreConflictPenalty = min(0.8, coreViolationCount × 0.4)
|
|
11
|
+
* evolvingConflictPenalty = min(0.3, evolvingViolationCount × 0.1)
|
|
12
|
+
* conflictPenalty = 1.0 − coreConflictPenalty − evolvingConflictPenalty
|
|
13
|
+
* subtleMult = 0.5 if any high-severity conflict, else 1.0
|
|
14
|
+
* blended = w_s × domainRelevance + w_l × domainIntent × subtleMult
|
|
15
|
+
* SA-1 = blended × conflictPenalty
|
|
16
|
+
*
|
|
17
|
+
* SA-2 per corrected §B.7.2 (CR-1 — no self-multiplication):
|
|
18
|
+
* computableScore = 0.3 × tokenCompliance + 0.2 × catalogHealth
|
|
19
|
+
* designConflictPenalty = 1.0 − min(0.6, coreAp × 0.3 + evolvingAp × 0.1)
|
|
20
|
+
* llmTerm = w_l × principleAlignment × subtleMult
|
|
21
|
+
* blendedScore = w_s × principleCoverage + llmTerm
|
|
22
|
+
* llmComponent = blendedScore × designConflictPenalty
|
|
23
|
+
* SA-2 = computableScore + 0.5 × llmComponent
|
|
24
|
+
*
|
|
25
|
+
* Phase weights (§B.7.3):
|
|
26
|
+
* 2a shadow → (0, 0) — computed but not used in ranking
|
|
27
|
+
* 2b blended → (0.20, 0.80)
|
|
28
|
+
* 2c calibrating → (0.35, 0.65)
|
|
29
|
+
* 3 calibrated → flywheel-driven; w_structural floored at 0.20 (CR-2)
|
|
30
|
+
*/
|
|
31
|
+
/** CR-2: Phase 3 must never drop w_structural below this floor. */
|
|
32
|
+
export const W_STRUCTURAL_FLOOR = 0.2;
|
|
33
|
+
export function getPhaseWeights(phase, calibrated) {
|
|
34
|
+
switch (phase) {
|
|
35
|
+
case '2a':
|
|
36
|
+
return { wStructural: 0, wLlm: 0 };
|
|
37
|
+
case '2b':
|
|
38
|
+
return { wStructural: 0.2, wLlm: 0.8 };
|
|
39
|
+
case '2c':
|
|
40
|
+
return { wStructural: 0.35, wLlm: 0.65 };
|
|
41
|
+
case '3': {
|
|
42
|
+
if (!calibrated)
|
|
43
|
+
return { wStructural: 0.35, wLlm: 0.65 };
|
|
44
|
+
const wStructural = Math.max(W_STRUCTURAL_FLOOR, calibrated.wStructural);
|
|
45
|
+
// wLlm is whatever is left after clamping — keeps the pair summing to 1.0.
|
|
46
|
+
const wLlm = Math.max(0, 1 - wStructural);
|
|
47
|
+
return { wStructural, wLlm };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
52
|
+
function clamp01(value) {
|
|
53
|
+
if (Number.isNaN(value))
|
|
54
|
+
return 0;
|
|
55
|
+
return Math.min(1, Math.max(0, value));
|
|
56
|
+
}
|
|
57
|
+
function hasHighSeverity(conflicts) {
|
|
58
|
+
return conflicts.some((c) => c.severity === 'high');
|
|
59
|
+
}
|
|
60
|
+
function normalizeCoverage(value) {
|
|
61
|
+
if (value === undefined || Number.isNaN(value))
|
|
62
|
+
return 0;
|
|
63
|
+
const clamped = value > 1 ? value / 100 : value;
|
|
64
|
+
return clamp01(clamped);
|
|
65
|
+
}
|
|
66
|
+
// ── SA-1 ─────────────────────────────────────────────────────────────
|
|
67
|
+
export function computeSa1(inputs, weights) {
|
|
68
|
+
// Hard gate: core out-of-scope match forces SA-1 = 0 irrespective of
|
|
69
|
+
// Layer 2/3 output (§B.7.1 STOP condition).
|
|
70
|
+
if (inputs.hardGated) {
|
|
71
|
+
return {
|
|
72
|
+
sa1: 0,
|
|
73
|
+
hardGated: true,
|
|
74
|
+
structuralContribution: 0,
|
|
75
|
+
llmContribution: 0,
|
|
76
|
+
blended: 0,
|
|
77
|
+
conflictPenalty: 0,
|
|
78
|
+
subtleMult: 1,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const coreConflictPenalty = Math.min(0.8, inputs.coreViolationCount * 0.4);
|
|
82
|
+
const evolvingConflictPenalty = Math.min(0.3, inputs.evolvingViolationCount * 0.1);
|
|
83
|
+
const conflictPenalty = Math.max(0, 1 - coreConflictPenalty - evolvingConflictPenalty);
|
|
84
|
+
const subtleMult = hasHighSeverity(inputs.subtleConflicts) ? 0.5 : 1.0;
|
|
85
|
+
const structuralContribution = weights.wStructural * clamp01(inputs.domainRelevance);
|
|
86
|
+
const llmContribution = weights.wLlm * clamp01(inputs.domainIntent) * subtleMult;
|
|
87
|
+
const blended = structuralContribution + llmContribution;
|
|
88
|
+
const sa1 = clamp01(blended * conflictPenalty);
|
|
89
|
+
return {
|
|
90
|
+
sa1,
|
|
91
|
+
hardGated: false,
|
|
92
|
+
structuralContribution,
|
|
93
|
+
llmContribution,
|
|
94
|
+
blended,
|
|
95
|
+
conflictPenalty,
|
|
96
|
+
subtleMult,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// ── SA-2 ─────────────────────────────────────────────────────────────
|
|
100
|
+
export function computeSa2(inputs, weights) {
|
|
101
|
+
const tc = normalizeCoverage(inputs.tokenCompliance);
|
|
102
|
+
const ch = normalizeCoverage(inputs.catalogHealth);
|
|
103
|
+
// RFC §B.7.2: computableScore tops out at 0.5 by design — it's half
|
|
104
|
+
// the final SA-2 score. The other half comes from `0.5 × llmComponent`
|
|
105
|
+
// below, so perfect inputs on both layers yield SA-2 = 1.0. See the
|
|
106
|
+
// worked example in composite.test.ts: tc=0.88, ch=0.95 → 0.454.
|
|
107
|
+
const computableScore = 0.3 * tc + 0.2 * ch;
|
|
108
|
+
const rawPenalty = inputs.coreDesignAntiPatternCount * 0.3 + inputs.evolvingDesignAntiPatternCount * 0.1;
|
|
109
|
+
const designConflictPenalty = Math.max(0, 1 - Math.min(0.6, rawPenalty));
|
|
110
|
+
const subtleMult = hasHighSeverity(inputs.subtleDesignConflicts) ? 0.5 : 1.0;
|
|
111
|
+
const structuralContribution = weights.wStructural * clamp01(inputs.principleCoverage);
|
|
112
|
+
const llmContribution = weights.wLlm * clamp01(inputs.principleAlignment) * subtleMult;
|
|
113
|
+
const blendedScore = structuralContribution + llmContribution;
|
|
114
|
+
const llmComponent = blendedScore * designConflictPenalty;
|
|
115
|
+
const sa2 = clamp01(computableScore + 0.5 * llmComponent);
|
|
116
|
+
return {
|
|
117
|
+
sa2,
|
|
118
|
+
computableScore,
|
|
119
|
+
structuralContribution,
|
|
120
|
+
llmContribution,
|
|
121
|
+
blendedScore,
|
|
122
|
+
designConflictPenalty,
|
|
123
|
+
subtleMult,
|
|
124
|
+
llmComponent,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export function computeSoulAlignment(input) {
|
|
128
|
+
const weights = getPhaseWeights(input.phase, input.calibratedWeights);
|
|
129
|
+
const sa1 = computeSa1(input.sa1, weights);
|
|
130
|
+
const sa2 = computeSa2(input.sa2, weights);
|
|
131
|
+
return {
|
|
132
|
+
phase: input.phase,
|
|
133
|
+
weights,
|
|
134
|
+
shadowMode: input.phase === '2a',
|
|
135
|
+
sa1,
|
|
136
|
+
sa2,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=composite.js.map
|