instar 1.3.25 → 1.3.27
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/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +11 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/types.d.ts +17 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/feedback-factory/dispatch/handlers.d.ts +44 -0
- package/dist/feedback-factory/dispatch/handlers.d.ts.map +1 -0
- package/dist/feedback-factory/dispatch/handlers.js +101 -0
- package/dist/feedback-factory/dispatch/handlers.js.map +1 -0
- package/dist/feedback-factory/store/FeedbackStore.d.ts +17 -0
- package/dist/feedback-factory/store/FeedbackStore.d.ts.map +1 -1
- package/dist/feedback-factory/store/FeedbackStore.js +14 -0
- package/dist/feedback-factory/store/FeedbackStore.js.map +1 -1
- package/dist/monitoring/FailureAnalyzer.d.ts +47 -0
- package/dist/monitoring/FailureAnalyzer.d.ts.map +1 -0
- package/dist/monitoring/FailureAnalyzer.js +77 -0
- package/dist/monitoring/FailureAnalyzer.js.map +1 -0
- package/dist/monitoring/FailureAttributionEngine.d.ts +89 -0
- package/dist/monitoring/FailureAttributionEngine.d.ts.map +1 -0
- package/dist/monitoring/FailureAttributionEngine.js +106 -0
- package/dist/monitoring/FailureAttributionEngine.js.map +1 -0
- package/dist/monitoring/FailureLedger.d.ts +207 -0
- package/dist/monitoring/FailureLedger.d.ts.map +1 -0
- package/dist/monitoring/FailureLedger.js +524 -0
- package/dist/monitoring/FailureLedger.js.map +1 -0
- package/dist/monitoring/FailureLoopDriver.d.ts +92 -0
- package/dist/monitoring/FailureLoopDriver.d.ts.map +1 -0
- package/dist/monitoring/FailureLoopDriver.js +101 -0
- package/dist/monitoring/FailureLoopDriver.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +7 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.d.ts.map +1 -1
- package/dist/scheduler/AgentMdJobLoader.js +33 -4
- package/dist/scheduler/AgentMdJobLoader.js.map +1 -1
- package/dist/server/AgentServer.d.ts +2 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +53 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +4 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +149 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +47 -47
- package/src/scaffold/templates/jobs/instar/failure-analyzer.md +32 -0
- package/src/scaffold/templates.ts +7 -0
- package/upgrades/1.3.26.md +25 -0
- package/upgrades/1.3.27.md +45 -0
- package/upgrades/side-effects/agentmd-frontmatter-scheduling-vocabulary.md +66 -0
- package/upgrades/side-effects/failure-learning-loop.md +131 -0
- package/upgrades/side-effects/feedback-factory-dispatch-handlers.md +32 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FailureAnalyzer — the pattern layer (spec §4.4).
|
|
3
|
+
*
|
|
4
|
+
* Deterministic core: groups attributed failures by category, and emits an
|
|
5
|
+
* InsightRecord only when a cluster crosses the support + SOURCE-DIVERSITY gate
|
|
6
|
+
* (≥ minSupport records AND ≥ minDistinctSessions distinct filers AND
|
|
7
|
+
* ≥ minDistinctCauseCommits distinct cause-commits). The diversity gate is what
|
|
8
|
+
* stops a single misbehaving session — or a flaky/flapping source — from
|
|
9
|
+
* manufacturing a "pattern" (§4.4 M4/M5). Diversity is computed conservatively
|
|
10
|
+
* from the deduped records (one filer/cause per dedupeKey), so it UNDERcounts —
|
|
11
|
+
* biasing toward NOT firing, which is the safe direction for a process-change
|
|
12
|
+
* recommendation.
|
|
13
|
+
*
|
|
14
|
+
* Recommendations are TEMPLATE-KEYED on the detected category (spec §4.4 / BL-3):
|
|
15
|
+
* never free-LLM prose piped into an auto-opened item. The §4.4 Tier-1 LLM
|
|
16
|
+
* supervisor (declared on the analyzer job) sanity-checks the rendered finding
|
|
17
|
+
* before any push surface; this module is the deterministic substrate it wraps.
|
|
18
|
+
*
|
|
19
|
+
* Signal-only: discovers + records insights; it never opens tracked items,
|
|
20
|
+
* blocks, or grades. The closed-loop step is the FailureLoopDriver.
|
|
21
|
+
*/
|
|
22
|
+
import type { FailureLedger, InsightRecord } from './FailureLedger.js';
|
|
23
|
+
export interface AnalyzerGates {
|
|
24
|
+
minSupport: number;
|
|
25
|
+
minDistinctSessions: number;
|
|
26
|
+
minDistinctCauseCommits: number;
|
|
27
|
+
/** Only consider failures within this many days (0/undefined = all). */
|
|
28
|
+
windowDays?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_GATES: AnalyzerGates;
|
|
31
|
+
export interface AnalyzeResult {
|
|
32
|
+
insightsDiscovered: InsightRecord[];
|
|
33
|
+
clustersConsidered: number;
|
|
34
|
+
clustersBelowThreshold: number;
|
|
35
|
+
}
|
|
36
|
+
export declare class FailureAnalyzer {
|
|
37
|
+
private readonly ledger;
|
|
38
|
+
private readonly gates;
|
|
39
|
+
constructor(ledger: FailureLedger, gates?: AnalyzerGates);
|
|
40
|
+
/**
|
|
41
|
+
* Scan attributed failures, detect category clusters that cross the gate, and
|
|
42
|
+
* upsert an InsightRecord for each. Returns the insights touched. Re-running
|
|
43
|
+
* is idempotent (upsert on a content-stable identityKey).
|
|
44
|
+
*/
|
|
45
|
+
analyze(): AnalyzeResult;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=FailureAnalyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FailureAnalyzer.d.ts","sourceRoot":"","sources":["../../src/monitoring/FailureAnalyzer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAkC,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEvG,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,uBAAuB,EAAE,MAAM,CAAC;IAChC,wEAAwE;IACxE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,aAI3B,CAAC;AAaF,MAAM,WAAW,aAAa;IAC5B,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;IAAiB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAA7C,MAAM,EAAE,aAAa,EAAmB,KAAK,GAAE,aAA6B;IAEzG;;;;OAIG;IACH,OAAO,IAAI,aAAa;CAgDzB"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export const DEFAULT_GATES = {
|
|
2
|
+
minSupport: 4,
|
|
3
|
+
minDistinctSessions: 3,
|
|
4
|
+
minDistinctCauseCommits: 3,
|
|
5
|
+
};
|
|
6
|
+
/** Template recommendations keyed on category — NOT free LLM text (§4.4 / BL-3). */
|
|
7
|
+
const RECOMMENDATION_BY_CATEGORY = {
|
|
8
|
+
concurrency: 'Recurring concurrency failures — give the adversarial review pass a concurrency/race checklist and require a concurrent-path test.',
|
|
9
|
+
'config-parse': 'Recurring config-parse failures — add a config-schema validation gate to the build skill before merge.',
|
|
10
|
+
wiring: 'Recurring wiring failures — require a wiring-integrity test (assert the component is constructed AND started) for every dependency-injected feature.',
|
|
11
|
+
logic: 'Recurring logic failures — strengthen the both-sides-of-the-boundary test requirement in the spec review.',
|
|
12
|
+
migration: 'Recurring migration failures — add a migration-parity check (existing-agent path) to the review checklist.',
|
|
13
|
+
'test-gap': 'Recurring test-gap failures — require the 3-tier test set (incl. the production storage path) before merge.',
|
|
14
|
+
unknown: 'Recurring uncategorized failures — improve the failure-categorization step so these become actionable.',
|
|
15
|
+
};
|
|
16
|
+
export class FailureAnalyzer {
|
|
17
|
+
ledger;
|
|
18
|
+
gates;
|
|
19
|
+
constructor(ledger, gates = DEFAULT_GATES) {
|
|
20
|
+
this.ledger = ledger;
|
|
21
|
+
this.gates = gates;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Scan attributed failures, detect category clusters that cross the gate, and
|
|
25
|
+
* upsert an InsightRecord for each. Returns the insights touched. Re-running
|
|
26
|
+
* is idempotent (upsert on a content-stable identityKey).
|
|
27
|
+
*/
|
|
28
|
+
analyze() {
|
|
29
|
+
const sinceMs = this.gates.windowDays && this.gates.windowDays > 0
|
|
30
|
+
? Date.now() - this.gates.windowDays * 86400_000
|
|
31
|
+
: undefined;
|
|
32
|
+
// Only attributed records feed a process-change recommendation (§4.3): a
|
|
33
|
+
// guess (`inferred`) is excluded until confirmed.
|
|
34
|
+
const records = this.ledger
|
|
35
|
+
.list({ sinceMs, limit: 1000 })
|
|
36
|
+
.filter((r) => r.attribution === 'automatic' || r.attribution === 'one-tap');
|
|
37
|
+
const byCategory = new Map();
|
|
38
|
+
for (const r of records) {
|
|
39
|
+
const arr = byCategory.get(r.category) ?? [];
|
|
40
|
+
arr.push(r);
|
|
41
|
+
byCategory.set(r.category, arr);
|
|
42
|
+
}
|
|
43
|
+
const discovered = [];
|
|
44
|
+
let belowThreshold = 0;
|
|
45
|
+
for (const [category, cluster] of byCategory) {
|
|
46
|
+
if (category === 'unknown')
|
|
47
|
+
continue; // unknown is a coverage signal, not a pattern
|
|
48
|
+
const distinctSessions = new Set(cluster.map((r) => r.filedBy)).size;
|
|
49
|
+
const distinctCauseCommits = new Set(cluster.map((r) => r.causeCommitOid ?? r.id)).size;
|
|
50
|
+
const crosses = cluster.length >= this.gates.minSupport &&
|
|
51
|
+
distinctSessions >= this.gates.minDistinctSessions &&
|
|
52
|
+
distinctCauseCommits >= this.gates.minDistinctCauseCommits;
|
|
53
|
+
if (!crosses) {
|
|
54
|
+
belowThreshold++;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const insight = this.ledger.upsertInsight({
|
|
58
|
+
identityKey: `category:${category}`,
|
|
59
|
+
summary: `${cluster.length} attributed ${category} failures across ${distinctSessions} sessions / ${distinctCauseCommits} cause-commits`,
|
|
60
|
+
recommendation: RECOMMENDATION_BY_CATEGORY[category],
|
|
61
|
+
supportingFailureIds: cluster.map((r) => r.id),
|
|
62
|
+
distinctSessions,
|
|
63
|
+
distinctCauseCommits,
|
|
64
|
+
targetCategory: category,
|
|
65
|
+
baselineRate: cluster.length,
|
|
66
|
+
});
|
|
67
|
+
if (insight)
|
|
68
|
+
discovered.push(insight);
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
insightsDiscovered: discovered,
|
|
72
|
+
clustersConsidered: byCategory.size,
|
|
73
|
+
clustersBelowThreshold: belowThreshold,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=FailureAnalyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FailureAnalyzer.js","sourceRoot":"","sources":["../../src/monitoring/FailureAnalyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FailureAttributionEngine — joins a failure to the feature that produced it.
|
|
3
|
+
*
|
|
4
|
+
* Part of the Failure-Learning Loop (docs/specs/FAILURE-LEARNING-LOOP-SPEC.md
|
|
5
|
+
* §4.2 #A/#B, §4.3). First-slice sources:
|
|
6
|
+
*
|
|
7
|
+
* - #A bugfix-commit: parse a `Fixes-Feature: <initiativeId>` / `Fixes: <FAIL-id>`
|
|
8
|
+
* trailer, then CROSS-CHECK that the fix commit's touched files actually
|
|
9
|
+
* intersect the named initiative's `coveredFiles`. A forged/mistaken trailer
|
|
10
|
+
* that points at an unrelated initiative fails the overlap check and is
|
|
11
|
+
* downgraded to `inferred` (needs attribution) — never accepted as fact
|
|
12
|
+
* (spec §4.2 #A, M7). Trailer omission is reported as a coverage bucket, not
|
|
13
|
+
* silently dropped.
|
|
14
|
+
*
|
|
15
|
+
* - #B agent-diagnosed: a server-validated one-tap. The cited `initiativeId`
|
|
16
|
+
* MUST exist; a caller-supplied `causeCommitOid` NEVER upgrades the record to
|
|
17
|
+
* `automatic` (spec §4.2 #B, B6) — it stays `one-tap` and is excluded from
|
|
18
|
+
* toolchain-blame aggregates by the analyzer.
|
|
19
|
+
*
|
|
20
|
+
* Pure logic with injected dependencies (initiative lookup + git file-overlap)
|
|
21
|
+
* so it is unit-testable without a live InitiativeTracker or git.
|
|
22
|
+
*/
|
|
23
|
+
import type { FailureCategory, AttributionMode } from './FailureLedger.js';
|
|
24
|
+
/** Minimal view of an initiative the engine needs to attribute against. */
|
|
25
|
+
export interface InitiativeView {
|
|
26
|
+
id: string;
|
|
27
|
+
coveredFiles?: string[];
|
|
28
|
+
mergeCommitOid?: string;
|
|
29
|
+
parentProjectId?: string;
|
|
30
|
+
specPath?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AttributionDeps {
|
|
33
|
+
/** Resolve an initiative by id (from InitiativeTracker). Returns null if absent. */
|
|
34
|
+
getInitiative: (id: string) => InitiativeView | null;
|
|
35
|
+
/** Files touched by a commit (from git). Returns [] if unknown. */
|
|
36
|
+
commitTouchedFiles: (commitOid: string) => string[];
|
|
37
|
+
}
|
|
38
|
+
export interface AttributionVerdict {
|
|
39
|
+
attribution: AttributionMode;
|
|
40
|
+
attributionConfidence: number;
|
|
41
|
+
initiativeId?: string;
|
|
42
|
+
projectId?: string;
|
|
43
|
+
specPath?: string;
|
|
44
|
+
causeCommitOid?: string;
|
|
45
|
+
/** Human-readable note explaining the verdict (for the audit/needs-attribution surface). */
|
|
46
|
+
note: string;
|
|
47
|
+
/** True when the trailer was absent — feeds the "no-feature-link" coverage bucket (§4.2 #A). */
|
|
48
|
+
noFeatureLink?: boolean;
|
|
49
|
+
}
|
|
50
|
+
export interface ParsedTrailers {
|
|
51
|
+
fixesFeature?: string;
|
|
52
|
+
fixesFailId?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare class FailureAttributionEngine {
|
|
55
|
+
private readonly deps;
|
|
56
|
+
constructor(deps: AttributionDeps);
|
|
57
|
+
/** Parse the bugfix-commit trailers from a commit message. */
|
|
58
|
+
static parseTrailers(commitMessage: string): ParsedTrailers;
|
|
59
|
+
/** Normalize a path for overlap comparison (strip leading ./, lowercase drive-irrelevant). */
|
|
60
|
+
private static norm;
|
|
61
|
+
/**
|
|
62
|
+
* Attribute a bugfix commit. The trailer is a HINT that must be reconciled
|
|
63
|
+
* against reality: the cited initiative must exist AND the fix commit's
|
|
64
|
+
* touched files must intersect its coveredFiles. Verified overlap → automatic;
|
|
65
|
+
* trailer-without-overlap (or missing initiative) → inferred (needs attribution).
|
|
66
|
+
*/
|
|
67
|
+
attributeBugfixCommit(input: {
|
|
68
|
+
commitOid: string;
|
|
69
|
+
commitMessage: string;
|
|
70
|
+
}): AttributionVerdict;
|
|
71
|
+
/**
|
|
72
|
+
* Validate an agent-diagnosed one-tap filing. The cited initiative must
|
|
73
|
+
* exist. A caller-supplied causeCommitOid NEVER upgrades to automatic (B6);
|
|
74
|
+
* the verdict stays one-tap.
|
|
75
|
+
*/
|
|
76
|
+
validateAgentDiagnosed(input: {
|
|
77
|
+
initiativeId: string;
|
|
78
|
+
causeCommitOid?: string;
|
|
79
|
+
}): {
|
|
80
|
+
ok: true;
|
|
81
|
+
verdict: AttributionVerdict;
|
|
82
|
+
} | {
|
|
83
|
+
ok: false;
|
|
84
|
+
reason: string;
|
|
85
|
+
};
|
|
86
|
+
/** Pick a category enum value defensively — never trust free text (§4.4). */
|
|
87
|
+
static coerceCategory(value: string | undefined): FailureCategory;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=FailureAttributionEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FailureAttributionEngine.d.ts","sourceRoot":"","sources":["../../src/monitoring/FailureAttributionEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE3E,2EAA2E;AAC3E,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,oFAAoF;IACpF,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,cAAc,GAAG,IAAI,CAAC;IACrD,mEAAmE;IACnE,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,eAAe,CAAC;IAC7B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAKD,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,eAAe;IAElD,8DAA8D;IAC9D,MAAM,CAAC,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,cAAc;IAS3D,8FAA8F;IAC9F,OAAO,CAAC,MAAM,CAAC,IAAI;IAInB;;;;;OAKG;IACH,qBAAqB,CAAC,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB;IAoD9F;;;;OAIG;IACH,sBAAsB,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAC3E;QAAE,EAAE,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,kBAAkB,CAAA;KAAE,GACzC;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;IAmBjC,6EAA6E;IAC7E,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe;CAMlE"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const TRAILER_FEATURE = /^\s*Fixes-Feature:\s*(\S+)\s*$/im;
|
|
2
|
+
const TRAILER_FAIL = /^\s*Fixes:\s*(FAIL-\S+)\s*$/im;
|
|
3
|
+
export class FailureAttributionEngine {
|
|
4
|
+
deps;
|
|
5
|
+
constructor(deps) {
|
|
6
|
+
this.deps = deps;
|
|
7
|
+
}
|
|
8
|
+
/** Parse the bugfix-commit trailers from a commit message. */
|
|
9
|
+
static parseTrailers(commitMessage) {
|
|
10
|
+
const out = {};
|
|
11
|
+
const f = commitMessage.match(TRAILER_FEATURE);
|
|
12
|
+
if (f)
|
|
13
|
+
out.fixesFeature = f[1].trim();
|
|
14
|
+
const x = commitMessage.match(TRAILER_FAIL);
|
|
15
|
+
if (x)
|
|
16
|
+
out.fixesFailId = x[1].trim();
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
/** Normalize a path for overlap comparison (strip leading ./, lowercase drive-irrelevant). */
|
|
20
|
+
static norm(p) {
|
|
21
|
+
return p.replace(/^\.\//, '').trim();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Attribute a bugfix commit. The trailer is a HINT that must be reconciled
|
|
25
|
+
* against reality: the cited initiative must exist AND the fix commit's
|
|
26
|
+
* touched files must intersect its coveredFiles. Verified overlap → automatic;
|
|
27
|
+
* trailer-without-overlap (or missing initiative) → inferred (needs attribution).
|
|
28
|
+
*/
|
|
29
|
+
attributeBugfixCommit(input) {
|
|
30
|
+
const trailers = FailureAttributionEngine.parseTrailers(input.commitMessage);
|
|
31
|
+
if (!trailers.fixesFeature) {
|
|
32
|
+
// Trailer omission is a measured coverage gap, not a silent drop (§4.2 #A).
|
|
33
|
+
return {
|
|
34
|
+
attribution: 'inferred',
|
|
35
|
+
attributionConfidence: 0,
|
|
36
|
+
causeCommitOid: input.commitOid,
|
|
37
|
+
noFeatureLink: true,
|
|
38
|
+
note: 'no Fixes-Feature trailer — counted in the no-feature-link coverage bucket',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const initiative = this.deps.getInitiative(trailers.fixesFeature);
|
|
42
|
+
if (!initiative) {
|
|
43
|
+
return {
|
|
44
|
+
attribution: 'inferred',
|
|
45
|
+
attributionConfidence: 0.1,
|
|
46
|
+
causeCommitOid: input.commitOid,
|
|
47
|
+
note: `trailer cites unknown initiative "${trailers.fixesFeature}" — needs attribution`,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const touched = new Set(this.deps.commitTouchedFiles(input.commitOid).map(FailureAttributionEngine.norm));
|
|
51
|
+
const covered = (initiative.coveredFiles ?? []).map(FailureAttributionEngine.norm);
|
|
52
|
+
const overlap = covered.some((f) => touched.has(f));
|
|
53
|
+
if (!overlap) {
|
|
54
|
+
// Forged/mistaken trailer: the fix doesn't touch the feature's code (§4.2 M7).
|
|
55
|
+
return {
|
|
56
|
+
attribution: 'inferred',
|
|
57
|
+
attributionConfidence: 0.2,
|
|
58
|
+
initiativeId: initiative.id,
|
|
59
|
+
projectId: initiative.parentProjectId,
|
|
60
|
+
specPath: initiative.specPath,
|
|
61
|
+
causeCommitOid: input.commitOid,
|
|
62
|
+
note: `trailer cites "${initiative.id}" but fix touches none of its coveredFiles — needs attribution (possible mis-blame)`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return {
|
|
66
|
+
attribution: 'automatic',
|
|
67
|
+
attributionConfidence: 0.9,
|
|
68
|
+
initiativeId: initiative.id,
|
|
69
|
+
projectId: initiative.parentProjectId,
|
|
70
|
+
specPath: initiative.specPath,
|
|
71
|
+
causeCommitOid: input.commitOid,
|
|
72
|
+
note: `trailer cross-checked: fix overlaps ${initiative.id} coveredFiles`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Validate an agent-diagnosed one-tap filing. The cited initiative must
|
|
77
|
+
* exist. A caller-supplied causeCommitOid NEVER upgrades to automatic (B6);
|
|
78
|
+
* the verdict stays one-tap.
|
|
79
|
+
*/
|
|
80
|
+
validateAgentDiagnosed(input) {
|
|
81
|
+
const initiative = this.deps.getInitiative(input.initiativeId);
|
|
82
|
+
if (!initiative) {
|
|
83
|
+
return { ok: false, reason: `initiative "${input.initiativeId}" does not exist` };
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
ok: true,
|
|
87
|
+
verdict: {
|
|
88
|
+
attribution: 'one-tap',
|
|
89
|
+
attributionConfidence: 0.5,
|
|
90
|
+
initiativeId: initiative.id,
|
|
91
|
+
projectId: initiative.parentProjectId,
|
|
92
|
+
specPath: initiative.specPath,
|
|
93
|
+
causeCommitOid: input.causeCommitOid, // recorded but does NOT raise confidence/attribution
|
|
94
|
+
note: 'agent-diagnosed one-tap — excluded from toolchain-blame aggregates',
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** Pick a category enum value defensively — never trust free text (§4.4). */
|
|
99
|
+
static coerceCategory(value) {
|
|
100
|
+
const allowed = [
|
|
101
|
+
'concurrency', 'config-parse', 'wiring', 'logic', 'migration', 'test-gap', 'unknown',
|
|
102
|
+
];
|
|
103
|
+
return allowed.includes(value) ? value : 'unknown';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=FailureAttributionEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FailureAttributionEngine.js","sourceRoot":"","sources":["../../src/monitoring/FailureAttributionEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
export type FailureSource = 'bugfix-commit' | 'agent-diagnosed' | 'ci' | 'revert' | 'regression' | 'degradation';
|
|
2
|
+
export type FailureSeverity = 'low' | 'medium' | 'high';
|
|
3
|
+
export type FailureCategory = 'concurrency' | 'config-parse' | 'wiring' | 'logic' | 'migration' | 'test-gap' | 'unknown';
|
|
4
|
+
export type AttributionMode = 'automatic' | 'one-tap' | 'inferred';
|
|
5
|
+
export type FailureStatus = 'open' | 'attributed' | 'analyzed' | 'resolved' | 'reopened';
|
|
6
|
+
/** Internal-only severity-split detail (spec §4.8). `full` never leaves via HTTP. */
|
|
7
|
+
export interface FailureDetail {
|
|
8
|
+
redacted: string;
|
|
9
|
+
full: string;
|
|
10
|
+
}
|
|
11
|
+
export interface FailureRecord {
|
|
12
|
+
id: string;
|
|
13
|
+
dedupeKey: string;
|
|
14
|
+
occurrenceCount: number;
|
|
15
|
+
detectedAt: string;
|
|
16
|
+
filedBy: string;
|
|
17
|
+
source: FailureSource;
|
|
18
|
+
severity: FailureSeverity;
|
|
19
|
+
summary: string;
|
|
20
|
+
detail: FailureDetail;
|
|
21
|
+
category: FailureCategory;
|
|
22
|
+
initiativeId?: string;
|
|
23
|
+
projectId?: string;
|
|
24
|
+
specPath?: string;
|
|
25
|
+
causeCommitOid?: string;
|
|
26
|
+
fixCommitOid?: string;
|
|
27
|
+
prNumber?: number;
|
|
28
|
+
toolchainRef?: string;
|
|
29
|
+
buildSkill?: string;
|
|
30
|
+
provenance?: 'verified' | 'claimed' | 'unknown';
|
|
31
|
+
attribution: AttributionMode;
|
|
32
|
+
attributionConfidence: number;
|
|
33
|
+
status: FailureStatus;
|
|
34
|
+
learningId?: string;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
updatedAt: string;
|
|
37
|
+
version: number;
|
|
38
|
+
}
|
|
39
|
+
/** The shape returned over HTTP / to the dashboard — detail.full stripped. */
|
|
40
|
+
export interface FailureRecordApiView extends Omit<FailureRecord, 'detail'> {
|
|
41
|
+
detail: {
|
|
42
|
+
redacted: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export type InsightStatus = 'discovered' | 'acted-on' | 'verified-effective' | 'verified-ineffective' | 'inconclusive' | 'dismissed';
|
|
46
|
+
export type VerifiedOutcome = 'pending' | 'effective' | 'ineffective' | 'insufficient-exposure' | 'n/a';
|
|
47
|
+
/** A discovered process-gap insight (spec §4.4 / §4.6.1). */
|
|
48
|
+
export interface InsightRecord {
|
|
49
|
+
id: string;
|
|
50
|
+
/** Content-stable across analyzer runs (so the same pattern updates, never re-announces). */
|
|
51
|
+
identityKey: string;
|
|
52
|
+
discoveredAt: string;
|
|
53
|
+
summary: string;
|
|
54
|
+
recommendation: string;
|
|
55
|
+
supportingFailureIds: string[];
|
|
56
|
+
distinctSessions: number;
|
|
57
|
+
distinctCauseCommits: number;
|
|
58
|
+
status: InsightStatus;
|
|
59
|
+
actedOnVia?: string;
|
|
60
|
+
verifyWindowStart?: string;
|
|
61
|
+
verifyWindowEnd?: string;
|
|
62
|
+
targetCategory?: string;
|
|
63
|
+
baselineRate?: number;
|
|
64
|
+
reopenCount: number;
|
|
65
|
+
verifiedOutcome: VerifiedOutcome;
|
|
66
|
+
/** True once this insight has been pushed to the system topic (push exactly once). */
|
|
67
|
+
announced: boolean;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
updatedAt: string;
|
|
70
|
+
version: number;
|
|
71
|
+
}
|
|
72
|
+
export interface UpsertInsightInput {
|
|
73
|
+
identityKey: string;
|
|
74
|
+
summary: string;
|
|
75
|
+
recommendation: string;
|
|
76
|
+
supportingFailureIds: string[];
|
|
77
|
+
distinctSessions: number;
|
|
78
|
+
distinctCauseCommits: number;
|
|
79
|
+
targetCategory?: string;
|
|
80
|
+
baselineRate?: number;
|
|
81
|
+
}
|
|
82
|
+
export interface OpenFailureInput {
|
|
83
|
+
detectedAt?: string;
|
|
84
|
+
filedBy: string;
|
|
85
|
+
source: FailureSource;
|
|
86
|
+
severity: FailureSeverity;
|
|
87
|
+
summary: string;
|
|
88
|
+
detail: FailureDetail;
|
|
89
|
+
category?: FailureCategory;
|
|
90
|
+
initiativeId?: string;
|
|
91
|
+
projectId?: string;
|
|
92
|
+
specPath?: string;
|
|
93
|
+
causeCommitOid?: string;
|
|
94
|
+
prNumber?: number;
|
|
95
|
+
toolchainRef?: string;
|
|
96
|
+
buildSkill?: string;
|
|
97
|
+
provenance?: 'verified' | 'claimed' | 'unknown';
|
|
98
|
+
attribution: AttributionMode;
|
|
99
|
+
attributionConfidence?: number;
|
|
100
|
+
}
|
|
101
|
+
export interface ListFilter {
|
|
102
|
+
source?: FailureSource;
|
|
103
|
+
category?: FailureCategory;
|
|
104
|
+
initiativeId?: string;
|
|
105
|
+
attribution?: AttributionMode;
|
|
106
|
+
status?: FailureStatus;
|
|
107
|
+
sinceMs?: number;
|
|
108
|
+
limit?: number;
|
|
109
|
+
}
|
|
110
|
+
export type UpdateResult = {
|
|
111
|
+
ok: true;
|
|
112
|
+
record: FailureRecord;
|
|
113
|
+
} | {
|
|
114
|
+
ok: false;
|
|
115
|
+
conflict: true;
|
|
116
|
+
current?: FailureRecord;
|
|
117
|
+
} | {
|
|
118
|
+
ok: false;
|
|
119
|
+
conflict: false;
|
|
120
|
+
reason: string;
|
|
121
|
+
};
|
|
122
|
+
export type InsightUpdateResult = {
|
|
123
|
+
ok: true;
|
|
124
|
+
record: InsightRecord;
|
|
125
|
+
} | {
|
|
126
|
+
ok: false;
|
|
127
|
+
conflict: true;
|
|
128
|
+
current?: InsightRecord;
|
|
129
|
+
} | {
|
|
130
|
+
ok: false;
|
|
131
|
+
conflict: false;
|
|
132
|
+
reason: string;
|
|
133
|
+
};
|
|
134
|
+
export interface DistinctCounts {
|
|
135
|
+
sessions: number;
|
|
136
|
+
causeCommits: number;
|
|
137
|
+
}
|
|
138
|
+
export interface FailureLedgerOptions {
|
|
139
|
+
dbPath: string;
|
|
140
|
+
/** Stable machine identifier for machine-scoped IDs (spec §4.2 M2). */
|
|
141
|
+
machineId?: string;
|
|
142
|
+
/** Sink for fail-open write errors (defaults to console.error). */
|
|
143
|
+
onError?: (where: string, err: unknown) => void;
|
|
144
|
+
}
|
|
145
|
+
export declare class FailureLedger {
|
|
146
|
+
private db;
|
|
147
|
+
private readonly machineId;
|
|
148
|
+
private readonly onError;
|
|
149
|
+
constructor(opts: FailureLedgerOptions);
|
|
150
|
+
/** Deterministic dedupe key (spec §4.2 M5). */
|
|
151
|
+
static dedupeKey(source: FailureSource, causeCommitOid: string | undefined, category: FailureCategory): string;
|
|
152
|
+
private nextId;
|
|
153
|
+
/**
|
|
154
|
+
* Open (or upsert) a failure record. A repeat on the same dedupeKey
|
|
155
|
+
* increments occurrenceCount and logs a distinct occurrence rather than
|
|
156
|
+
* duplicating. Fail-open: returns null on any storage error.
|
|
157
|
+
*/
|
|
158
|
+
open(input: OpenFailureInput): FailureRecord | null;
|
|
159
|
+
get(id: string): FailureRecord | null;
|
|
160
|
+
getByDedupeKey(dedupeKey: string): FailureRecord | null;
|
|
161
|
+
list(filter?: ListFilter): FailureRecord[];
|
|
162
|
+
/**
|
|
163
|
+
* Mutate a record. `ifMatch` is MANDATORY (spec §4.2 M4) — a stale version
|
|
164
|
+
* loses with {ok:false, conflict:true}. Caller retries (bounded).
|
|
165
|
+
*/
|
|
166
|
+
update(id: string, patch: Partial<Pick<FailureRecord, 'status' | 'category' | 'fixCommitOid' | 'learningId' | 'attribution' | 'attributionConfidence' | 'provenance' | 'buildSkill' | 'initiativeId' | 'projectId' | 'specPath' | 'toolchainRef'>>, ifMatch: number): UpdateResult;
|
|
167
|
+
/** Distinct sessions + cause-commits for the diversity gate (spec §4.4). */
|
|
168
|
+
distinctCounts(dedupeKey: string): DistinctCounts;
|
|
169
|
+
/**
|
|
170
|
+
* Analyzer query path (spec §4.4): indexed group-bys directly in SQL, never a
|
|
171
|
+
* full cache-rebuild + JS filter. Toolchain-blame counts are restricted to
|
|
172
|
+
* `verified`-provenance, `automatic`-attribution records (claimed / one-tap /
|
|
173
|
+
* inferred are excluded from blame aggregates). Also reports coverage so a
|
|
174
|
+
* low-attribution rate reads as low-confidence, not as the rate.
|
|
175
|
+
*/
|
|
176
|
+
analyze(opts?: {
|
|
177
|
+
sinceMs?: number;
|
|
178
|
+
}): {
|
|
179
|
+
total: number;
|
|
180
|
+
attributed: number;
|
|
181
|
+
byCategory: Record<string, number>;
|
|
182
|
+
byBuildSkill: Record<string, number>;
|
|
183
|
+
unknownToolchainByAuthor: Record<string, number>;
|
|
184
|
+
noFeatureLink: number;
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Upsert an insight keyed on its content-stable identityKey. A re-discovery
|
|
188
|
+
* UPDATES the existing record (refreshes evidence + counts) but PRESERVES its
|
|
189
|
+
* lifecycle (status/actedOnVia/verify window/announced) — so a pattern is
|
|
190
|
+
* never re-announced and an in-flight loop is never reset (spec §4.5 stable-key).
|
|
191
|
+
* Returns the resulting insight (fail-open: null on storage error).
|
|
192
|
+
*/
|
|
193
|
+
upsertInsight(input: UpsertInsightInput): InsightRecord | null;
|
|
194
|
+
private nextInsightSeq;
|
|
195
|
+
getInsight(id: string): InsightRecord | null;
|
|
196
|
+
getInsightByIdentity(identityKey: string): InsightRecord | null;
|
|
197
|
+
listInsights(filter?: {
|
|
198
|
+
status?: InsightStatus;
|
|
199
|
+
}): InsightRecord[];
|
|
200
|
+
updateInsight(id: string, patch: Partial<Pick<InsightRecord, 'status' | 'actedOnVia' | 'verifyWindowStart' | 'verifyWindowEnd' | 'targetCategory' | 'baselineRate' | 'reopenCount' | 'verifiedOutcome' | 'announced'>>, ifMatch: number): InsightUpdateResult;
|
|
201
|
+
private rowToInsight;
|
|
202
|
+
/** Strip detail.full — the ONLY shape that may cross an HTTP boundary (spec §4.8). */
|
|
203
|
+
static toApiView(record: FailureRecord): FailureRecordApiView;
|
|
204
|
+
close(): void;
|
|
205
|
+
private rowToRecord;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=FailureLedger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FailureLedger.d.ts","sourceRoot":"","sources":["../../src/monitoring/FailureLedger.ts"],"names":[],"mappings":"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"}
|