swipium 1.0.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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/appMap/automationLink.js +74 -0
- package/dist/appMap/automationLink.js.map +1 -0
- package/dist/appMap/build.js +193 -0
- package/dist/appMap/build.js.map +1 -0
- package/dist/appMap/codeIndex.js +73 -0
- package/dist/appMap/codeIndex.js.map +1 -0
- package/dist/appMap/featureModel.js +83 -0
- package/dist/appMap/featureModel.js.map +1 -0
- package/dist/appMap/firstRunApply.js +131 -0
- package/dist/appMap/firstRunApply.js.map +1 -0
- package/dist/appMap/fsWalk.js +75 -0
- package/dist/appMap/fsWalk.js.map +1 -0
- package/dist/appMap/issues.js +227 -0
- package/dist/appMap/issues.js.map +1 -0
- package/dist/appMap/migrations.js +87 -0
- package/dist/appMap/migrations.js.map +1 -0
- package/dist/appMap/prelaunch.js +66 -0
- package/dist/appMap/prelaunch.js.map +1 -0
- package/dist/appMap/projectRegistry.js +70 -0
- package/dist/appMap/projectRegistry.js.map +1 -0
- package/dist/appMap/provenance.js +52 -0
- package/dist/appMap/provenance.js.map +1 -0
- package/dist/appMap/query.js +118 -0
- package/dist/appMap/query.js.map +1 -0
- package/dist/appMap/runtimeMerge.js +207 -0
- package/dist/appMap/runtimeMerge.js.map +1 -0
- package/dist/appMap/schema.js +65 -0
- package/dist/appMap/schema.js.map +1 -0
- package/dist/appMap/screenMatch.js +75 -0
- package/dist/appMap/screenMatch.js.map +1 -0
- package/dist/appMap/staticScan.js +614 -0
- package/dist/appMap/staticScan.js.map +1 -0
- package/dist/appMap/store.js +173 -0
- package/dist/appMap/store.js.map +1 -0
- package/dist/appMap/tsAstScan.js +184 -0
- package/dist/appMap/tsAstScan.js.map +1 -0
- package/dist/artifacts/bundletool.js +199 -0
- package/dist/artifacts/bundletool.js.map +1 -0
- package/dist/artifacts/resolve.js +388 -0
- package/dist/artifacts/resolve.js.map +1 -0
- package/dist/automationGen/appiumModel.js +129 -0
- package/dist/automationGen/appiumModel.js.map +1 -0
- package/dist/automationGen/ciEmitter.js +80 -0
- package/dist/automationGen/ciEmitter.js.map +1 -0
- package/dist/automationGen/jsEmitter.js +440 -0
- package/dist/automationGen/jsEmitter.js.map +1 -0
- package/dist/automationGen/packagePatch.js +56 -0
- package/dist/automationGen/packagePatch.js.map +1 -0
- package/dist/automationGen/projectProfile.js +317 -0
- package/dist/automationGen/projectProfile.js.map +1 -0
- package/dist/automationGen/pythonEmitter.js +386 -0
- package/dist/automationGen/pythonEmitter.js.map +1 -0
- package/dist/automationGen/readmeEmitter.js +66 -0
- package/dist/automationGen/readmeEmitter.js.map +1 -0
- package/dist/automationGen/suitePlan.js +94 -0
- package/dist/automationGen/suitePlan.js.map +1 -0
- package/dist/automationGen/validation.js +150 -0
- package/dist/automationGen/validation.js.map +1 -0
- package/dist/build/parseBuildLog.js +119 -0
- package/dist/build/parseBuildLog.js.map +1 -0
- package/dist/build/plan.js +150 -0
- package/dist/build/plan.js.map +1 -0
- package/dist/ci/preflight.js +85 -0
- package/dist/ci/preflight.js.map +1 -0
- package/dist/cli/init.js +105 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/scan.js +85 -0
- package/dist/cli/scan.js.map +1 -0
- package/dist/cli/suite.js +85 -0
- package/dist/cli/suite.js.map +1 -0
- package/dist/cli/verify.js +62 -0
- package/dist/cli/verify.js.map +1 -0
- package/dist/consent/consent.js +43 -0
- package/dist/consent/consent.js.map +1 -0
- package/dist/context/detect.js +156 -0
- package/dist/context/detect.js.map +1 -0
- package/dist/context/findApps.js +198 -0
- package/dist/context/findApps.js.map +1 -0
- package/dist/context/projectRoot.js +41 -0
- package/dist/context/projectRoot.js.map +1 -0
- package/dist/context/scan.js +167 -0
- package/dist/context/scan.js.map +1 -0
- package/dist/core/target.js +115 -0
- package/dist/core/target.js.map +1 -0
- package/dist/core/targetPlan.js +140 -0
- package/dist/core/targetPlan.js.map +1 -0
- package/dist/drivers/DirectDriver.js +228 -0
- package/dist/drivers/DirectDriver.js.map +1 -0
- package/dist/drivers/Driver.js +4 -0
- package/dist/drivers/Driver.js.map +1 -0
- package/dist/drivers/SimctlDriver.js +127 -0
- package/dist/drivers/SimctlDriver.js.map +1 -0
- package/dist/drivers/WdaDriver.js +250 -0
- package/dist/drivers/WdaDriver.js.map +1 -0
- package/dist/explore/candidates.js +93 -0
- package/dist/explore/candidates.js.map +1 -0
- package/dist/explore/graph.js +116 -0
- package/dist/explore/graph.js.map +1 -0
- package/dist/explore/memory.js +35 -0
- package/dist/explore/memory.js.map +1 -0
- package/dist/explore/planner.js +105 -0
- package/dist/explore/planner.js.map +1 -0
- package/dist/explore/policy.js +85 -0
- package/dist/explore/policy.js.map +1 -0
- package/dist/explore/runner.js +475 -0
- package/dist/explore/runner.js.map +1 -0
- package/dist/explore/signatures.js +35 -0
- package/dist/explore/signatures.js.map +1 -0
- package/dist/explore/suite.js +55 -0
- package/dist/explore/suite.js.map +1 -0
- package/dist/featureTesting/executionBootstrap.js +140 -0
- package/dist/featureTesting/executionBootstrap.js.map +1 -0
- package/dist/firstRun/authStateMachine.js +83 -0
- package/dist/firstRun/authStateMachine.js.map +1 -0
- package/dist/firstRun/classifyScreen.js +201 -0
- package/dist/firstRun/classifyScreen.js.map +1 -0
- package/dist/firstRun/firstRunPlanner.js +207 -0
- package/dist/firstRun/firstRunPlanner.js.map +1 -0
- package/dist/firstRun/firstRunRunner.js +331 -0
- package/dist/firstRun/firstRunRunner.js.map +1 -0
- package/dist/firstRun/generatedDataPolicy.js +215 -0
- package/dist/firstRun/generatedDataPolicy.js.map +1 -0
- package/dist/firstRun/inputPlanner.js +67 -0
- package/dist/firstRun/inputPlanner.js.map +1 -0
- package/dist/firstRun/onboardingStateMachine.js +46 -0
- package/dist/firstRun/onboardingStateMachine.js.map +1 -0
- package/dist/firstRun/paywallPolicy.js +54 -0
- package/dist/firstRun/paywallPolicy.js.map +1 -0
- package/dist/firstRun/types.js +7 -0
- package/dist/firstRun/types.js.map +1 -0
- package/dist/fixtures/catalog.js +131 -0
- package/dist/fixtures/catalog.js.map +1 -0
- package/dist/flows/discover.js +18 -0
- package/dist/flows/discover.js.map +1 -0
- package/dist/flows/generate.js +211 -0
- package/dist/flows/generate.js.map +1 -0
- package/dist/flows/lint.js +97 -0
- package/dist/flows/lint.js.map +1 -0
- package/dist/flows/pack.js +68 -0
- package/dist/flows/pack.js.map +1 -0
- package/dist/flows/run.js +738 -0
- package/dist/flows/run.js.map +1 -0
- package/dist/flows/schema.js +240 -0
- package/dist/flows/schema.js.map +1 -0
- package/dist/flows/seedExec.js +108 -0
- package/dist/flows/seedExec.js.map +1 -0
- package/dist/flows/templates.js +155 -0
- package/dist/flows/templates.js.map +1 -0
- package/dist/flows/waitGuards.js +12 -0
- package/dist/flows/waitGuards.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/ios/signing.js +182 -0
- package/dist/ios/signing.js.map +1 -0
- package/dist/issues/classify.js +186 -0
- package/dist/issues/classify.js.map +1 -0
- package/dist/issues/fingerprint.js +130 -0
- package/dist/issues/fingerprint.js.map +1 -0
- package/dist/issues/index.js +240 -0
- package/dist/issues/index.js.map +1 -0
- package/dist/issues/metrics.js +155 -0
- package/dist/issues/metrics.js.map +1 -0
- package/dist/issues/recurrence.js +193 -0
- package/dist/issues/recurrence.js.map +1 -0
- package/dist/issues/report.js +110 -0
- package/dist/issues/report.js.map +1 -0
- package/dist/issues/reportBridge.js +119 -0
- package/dist/issues/reportBridge.js.map +1 -0
- package/dist/issues/schema.js +90 -0
- package/dist/issues/schema.js.map +1 -0
- package/dist/issues/sourceRevision.js +108 -0
- package/dist/issues/sourceRevision.js.map +1 -0
- package/dist/issues/store.js +155 -0
- package/dist/issues/store.js.map +1 -0
- package/dist/lib/android.js +278 -0
- package/dist/lib/android.js.map +1 -0
- package/dist/lib/commandTemplate.js +55 -0
- package/dist/lib/commandTemplate.js.map +1 -0
- package/dist/lib/coordSpace.js +23 -0
- package/dist/lib/coordSpace.js.map +1 -0
- package/dist/lib/device.js +87 -0
- package/dist/lib/device.js.map +1 -0
- package/dist/lib/gitignore.js +22 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/image.js +97 -0
- package/dist/lib/image.js.map +1 -0
- package/dist/lib/logger.js +8 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/metroState.js +45 -0
- package/dist/lib/metroState.js.map +1 -0
- package/dist/lib/needsInput.js +201 -0
- package/dist/lib/needsInput.js.map +1 -0
- package/dist/lib/png.js +103 -0
- package/dist/lib/png.js.map +1 -0
- package/dist/lib/redact.js +21 -0
- package/dist/lib/redact.js.map +1 -0
- package/dist/lib/result.js +87 -0
- package/dist/lib/result.js.map +1 -0
- package/dist/lib/schemaHash.js +65 -0
- package/dist/lib/schemaHash.js.map +1 -0
- package/dist/lib/sensitive.js +13 -0
- package/dist/lib/sensitive.js.map +1 -0
- package/dist/lib/simctl.js +149 -0
- package/dist/lib/simctl.js.map +1 -0
- package/dist/lib/spawn.js +144 -0
- package/dist/lib/spawn.js.map +1 -0
- package/dist/lib/wda.js +347 -0
- package/dist/lib/wda.js.map +1 -0
- package/dist/lib/wdaConfig.js +69 -0
- package/dist/lib/wdaConfig.js.map +1 -0
- package/dist/lib/wdaTune.js +58 -0
- package/dist/lib/wdaTune.js.map +1 -0
- package/dist/oracle/auth.js +22 -0
- package/dist/oracle/auth.js.map +1 -0
- package/dist/oracle/failures.js +193 -0
- package/dist/oracle/failures.js.map +1 -0
- package/dist/oracle/health.js +123 -0
- package/dist/oracle/health.js.map +1 -0
- package/dist/oracle/record.js +36 -0
- package/dist/oracle/record.js.map +1 -0
- package/dist/orchestration/envelope.js +52 -0
- package/dist/orchestration/envelope.js.map +1 -0
- package/dist/orchestration/goal.js +98 -0
- package/dist/orchestration/goal.js.map +1 -0
- package/dist/plan/plan.js +124 -0
- package/dist/plan/plan.js.map +1 -0
- package/dist/prompts/index.js +63 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/report/artifactManifest.js +44 -0
- package/dist/report/artifactManifest.js.map +1 -0
- package/dist/report/coverage.js +60 -0
- package/dist/report/coverage.js.map +1 -0
- package/dist/report/evidence.js +84 -0
- package/dist/report/evidence.js.map +1 -0
- package/dist/report/export.js +447 -0
- package/dist/report/export.js.map +1 -0
- package/dist/report/flake.js +61 -0
- package/dist/report/flake.js.map +1 -0
- package/dist/report/history.js +564 -0
- package/dist/report/history.js.map +1 -0
- package/dist/report/policy.js +98 -0
- package/dist/report/policy.js.map +1 -0
- package/dist/report/qaLevel.js +96 -0
- package/dist/report/qaLevel.js.map +1 -0
- package/dist/report/readiness.js +213 -0
- package/dist/report/readiness.js.map +1 -0
- package/dist/report/summary.js +42 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/report/testCatalog.js +93 -0
- package/dist/report/testCatalog.js.map +1 -0
- package/dist/report/timing.js +37 -0
- package/dist/report/timing.js.map +1 -0
- package/dist/server.js +180 -0
- package/dist/server.js.map +1 -0
- package/dist/services/automationGenerate.js +60 -0
- package/dist/services/automationGenerate.js.map +1 -0
- package/dist/services/build.js +66 -0
- package/dist/services/build.js.map +1 -0
- package/dist/services/preflight.js +51 -0
- package/dist/services/preflight.js.map +1 -0
- package/dist/services/prepareAndroid.js +183 -0
- package/dist/services/prepareAndroid.js.map +1 -0
- package/dist/services/prepareIos.js +162 -0
- package/dist/services/prepareIos.js.map +1 -0
- package/dist/services/report.js +537 -0
- package/dist/services/report.js.map +1 -0
- package/dist/services/smoke.js +120 -0
- package/dist/services/smoke.js.map +1 -0
- package/dist/services/suiteGenerate.js +123 -0
- package/dist/services/suiteGenerate.js.map +1 -0
- package/dist/services/testSuiteKnowledge.js +150 -0
- package/dist/services/testSuiteKnowledge.js.map +1 -0
- package/dist/session/attach.js +47 -0
- package/dist/session/attach.js.map +1 -0
- package/dist/session/progress.js +56 -0
- package/dist/session/progress.js.map +1 -0
- package/dist/session/store.js +329 -0
- package/dist/session/store.js.map +1 -0
- package/dist/snapshot/overlays.js +132 -0
- package/dist/snapshot/overlays.js.map +1 -0
- package/dist/snapshot/parse.js +204 -0
- package/dist/snapshot/parse.js.map +1 -0
- package/dist/snapshot/present.js +12 -0
- package/dist/snapshot/present.js.map +1 -0
- package/dist/snapshot/settle.js +36 -0
- package/dist/snapshot/settle.js.map +1 -0
- package/dist/state/profile.js +318 -0
- package/dist/state/profile.js.map +1 -0
- package/dist/suite/compile.js +176 -0
- package/dist/suite/compile.js.map +1 -0
- package/dist/suite/lint.js +45 -0
- package/dist/suite/lint.js.map +1 -0
- package/dist/suite/pom.js +285 -0
- package/dist/suite/pom.js.map +1 -0
- package/dist/suite/testcase.js +87 -0
- package/dist/suite/testcase.js.map +1 -0
- package/dist/testSuite/generator.js +222 -0
- package/dist/testSuite/generator.js.map +1 -0
- package/dist/testSuite/history.js +44 -0
- package/dist/testSuite/history.js.map +1 -0
- package/dist/testSuite/issueLinks.js +96 -0
- package/dist/testSuite/issueLinks.js.map +1 -0
- package/dist/testSuite/merge.js +190 -0
- package/dist/testSuite/merge.js.map +1 -0
- package/dist/testSuite/schema.js +86 -0
- package/dist/testSuite/schema.js.map +1 -0
- package/dist/testSuite/store.js +116 -0
- package/dist/testSuite/store.js.map +1 -0
- package/dist/testSuite/traceability.js +55 -0
- package/dist/testSuite/traceability.js.map +1 -0
- package/dist/tools/act.js +457 -0
- package/dist/tools/act.js.map +1 -0
- package/dist/tools/agent.js +225 -0
- package/dist/tools/agent.js.map +1 -0
- package/dist/tools/appMap.js +317 -0
- package/dist/tools/appMap.js.map +1 -0
- package/dist/tools/assertVisual.js +74 -0
- package/dist/tools/assertVisual.js.map +1 -0
- package/dist/tools/automationGenerate.js +394 -0
- package/dist/tools/automationGenerate.js.map +1 -0
- package/dist/tools/capabilities.js +114 -0
- package/dist/tools/capabilities.js.map +1 -0
- package/dist/tools/clearOverlay.js +150 -0
- package/dist/tools/clearOverlay.js.map +1 -0
- package/dist/tools/detectContext.js +41 -0
- package/dist/tools/detectContext.js.map +1 -0
- package/dist/tools/doctor.js +116 -0
- package/dist/tools/doctor.js.map +1 -0
- package/dist/tools/explore.js +289 -0
- package/dist/tools/explore.js.map +1 -0
- package/dist/tools/firstRun.js +136 -0
- package/dist/tools/firstRun.js.map +1 -0
- package/dist/tools/flow.js +331 -0
- package/dist/tools/flow.js.map +1 -0
- package/dist/tools/flowGenerate.js +55 -0
- package/dist/tools/flowGenerate.js.map +1 -0
- package/dist/tools/getArtifact.js +39 -0
- package/dist/tools/getArtifact.js.map +1 -0
- package/dist/tools/health.js +41 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/ios.js +492 -0
- package/dist/tools/ios.js.map +1 -0
- package/dist/tools/jobs.js +20 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/network.js +50 -0
- package/dist/tools/network.js.map +1 -0
- package/dist/tools/note.js +69 -0
- package/dist/tools/note.js.map +1 -0
- package/dist/tools/plan.js +49 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/prepareIosTarget.js +99 -0
- package/dist/tools/prepareIosTarget.js.map +1 -0
- package/dist/tools/prepareTarget.js +257 -0
- package/dist/tools/prepareTarget.js.map +1 -0
- package/dist/tools/report.js +42 -0
- package/dist/tools/report.js.map +1 -0
- package/dist/tools/screenRecord.js +24 -0
- package/dist/tools/screenRecord.js.map +1 -0
- package/dist/tools/screenshot.js +63 -0
- package/dist/tools/screenshot.js.map +1 -0
- package/dist/tools/smoke.js +39 -0
- package/dist/tools/smoke.js.map +1 -0
- package/dist/tools/snapshot.js +129 -0
- package/dist/tools/snapshot.js.map +1 -0
- package/dist/tools/startSession.js +169 -0
- package/dist/tools/startSession.js.map +1 -0
- package/dist/tools/state.js +87 -0
- package/dist/tools/state.js.map +1 -0
- package/dist/tools/suite.js +412 -0
- package/dist/tools/suite.js.map +1 -0
- package/dist/tools/testThis.js +774 -0
- package/dist/tools/testThis.js.map +1 -0
- package/dist/tools/wda.js +477 -0
- package/dist/tools/wda.js.map +1 -0
- package/dist/version.js +62 -0
- package/dist/version.js.map +1 -0
- package/dist/visual/ocr.js +72 -0
- package/dist/visual/ocr.js.map +1 -0
- package/dist/visual/provider.js +75 -0
- package/dist/visual/provider.js.map +1 -0
- package/docs/README.md +19 -0
- package/docs/assets/swipium-lockup-ink-on-light.png +0 -0
- package/docs/mcp-server.md +135 -0
- package/docs/tools.md +130 -0
- package/package.json +70 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// SWIPIUM Issue Log — high-level service + query layer (SWIPIUM-REQ-07).
|
|
2
|
+
//
|
|
3
|
+
// This is the single entry point callers (health oracle, reports, explore, mobile audit, tools)
|
|
4
|
+
// use to record observations and mutate lifecycle. It ties together fingerprinting, classification,
|
|
5
|
+
// the append-only store, and the lifecycle reducer, then keeps the derived index in sync.
|
|
6
|
+
//
|
|
7
|
+
// The id helpers are deterministic given (issueId, createdAt, eventType, seq) so events are stable
|
|
8
|
+
// and tests are reproducible without a clock.
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
10
|
+
import { classifyObservation } from './classify.js';
|
|
11
|
+
import { fingerprint, issueIdFromFingerprint } from './fingerprint.js';
|
|
12
|
+
import { buildRecurrenceMessage, foldEvent } from './recurrence.js';
|
|
13
|
+
import { appendEvents, eventCountForIssue, findRecord, getIndex, loadPolicy, saveIndex, } from './store.js';
|
|
14
|
+
import { ISSUE_SCHEMA_VERSION } from './schema.js';
|
|
15
|
+
/** Deterministic, collision-resistant event id from its content. */
|
|
16
|
+
export function makeEventId(issueId, createdAt, eventType, seq = 0) {
|
|
17
|
+
const h = createHash('sha256').update(`${issueId}|${createdAt}|${eventType}|${seq}`).digest('hex').slice(0, 12);
|
|
18
|
+
return `evt_${h}`;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Record one observation: fingerprint → classify → decide lifecycle → append event(s) → update
|
|
22
|
+
* index. Reopens a fixed issue (with recurrence message) when its fingerprint is seen again.
|
|
23
|
+
*/
|
|
24
|
+
export function recordObservation(root, observation, now, ctx = {}, meta = {}) {
|
|
25
|
+
const policy = loadPolicy(root);
|
|
26
|
+
const fp = fingerprint({
|
|
27
|
+
failureCode: observation.failureCode,
|
|
28
|
+
platform: meta.platform,
|
|
29
|
+
appId: meta.appId,
|
|
30
|
+
observation,
|
|
31
|
+
});
|
|
32
|
+
const issueId = issueIdFromFingerprint(fp);
|
|
33
|
+
const classification = classifyObservation(observation, { ...ctx, policy: mergePolicy(policy, ctx) });
|
|
34
|
+
const index = getIndex(root, now, meta.appId);
|
|
35
|
+
const prev = findRecord(index, { issueId, fingerprint: fp }) ?? null;
|
|
36
|
+
const wasFixed = prev?.state === 'fixed';
|
|
37
|
+
// The observation count is a monotonic per-issue sequence (each observation increments it), so it
|
|
38
|
+
// gives distinct event ids for two same-timestamp observations WITHOUT re-reading the whole log.
|
|
39
|
+
let seq = prev?.observationCount ?? 0;
|
|
40
|
+
const observed = {
|
|
41
|
+
schemaVersion: ISSUE_SCHEMA_VERSION,
|
|
42
|
+
eventId: makeEventId(issueId, now, 'observed', seq++),
|
|
43
|
+
issueId,
|
|
44
|
+
fingerprint: fp,
|
|
45
|
+
eventType: 'observed',
|
|
46
|
+
createdAt: now,
|
|
47
|
+
appId: meta.appId,
|
|
48
|
+
appName: meta.appName,
|
|
49
|
+
platform: meta.platform,
|
|
50
|
+
environment: meta.environment,
|
|
51
|
+
sourceRevision: meta.sourceRevision,
|
|
52
|
+
run: meta.run,
|
|
53
|
+
observation,
|
|
54
|
+
classification,
|
|
55
|
+
links: meta.links,
|
|
56
|
+
};
|
|
57
|
+
const events = [observed];
|
|
58
|
+
let record = foldEvent(prev, observed);
|
|
59
|
+
// Recurrence: append an explicit `reopened` event so the audit trail is honest (spec §Recurrence).
|
|
60
|
+
let reopened = false;
|
|
61
|
+
let recurrenceMessage;
|
|
62
|
+
if (wasFixed && record.state === 'reopened') {
|
|
63
|
+
reopened = true;
|
|
64
|
+
recurrenceMessage = record.lastRecurrenceMessage ?? buildRecurrenceMessage(record);
|
|
65
|
+
const reopenEvent = {
|
|
66
|
+
schemaVersion: ISSUE_SCHEMA_VERSION,
|
|
67
|
+
eventId: makeEventId(issueId, now, 'reopened', seq++),
|
|
68
|
+
issueId,
|
|
69
|
+
fingerprint: fp,
|
|
70
|
+
eventType: 'reopened',
|
|
71
|
+
createdAt: now,
|
|
72
|
+
platform: meta.platform,
|
|
73
|
+
environment: meta.environment,
|
|
74
|
+
lifecycle: { state: 'reopened', reopenedAt: now, recurrenceMessage },
|
|
75
|
+
};
|
|
76
|
+
events.push(reopenEvent);
|
|
77
|
+
record = foldEvent(record, reopenEvent);
|
|
78
|
+
}
|
|
79
|
+
appendEvents(root, events);
|
|
80
|
+
upsertRecord(index, record);
|
|
81
|
+
index.updatedAt = now;
|
|
82
|
+
saveIndex(root, index);
|
|
83
|
+
return { record, events, isNew: prev == null, reopened, recurrenceMessage, fingerprint: fp, issueId };
|
|
84
|
+
}
|
|
85
|
+
/** Append a `fixed` lifecycle event and update the index. */
|
|
86
|
+
export function markFixed(root, key, patch, now) {
|
|
87
|
+
return applyLifecycle(root, key, now, 'fixed', {
|
|
88
|
+
state: 'fixed',
|
|
89
|
+
fixedAt: now,
|
|
90
|
+
fixedInCommit: patch.fixedInCommit ?? patch.sourceRevision?.commit,
|
|
91
|
+
fixedInVersion: patch.fixedInVersion ?? patch.sourceRevision?.buildVersion,
|
|
92
|
+
howFixed: patch.howFixed,
|
|
93
|
+
fixedBy: patch.fixedBy,
|
|
94
|
+
}, patch.sourceRevision);
|
|
95
|
+
}
|
|
96
|
+
/** Append a `triaged` lifecycle event (change category/severity/owner/note). */
|
|
97
|
+
export function triageIssue(root, key, patch, now) {
|
|
98
|
+
return applyLifecycle(root, key, now, 'triaged', {
|
|
99
|
+
category: patch.category,
|
|
100
|
+
severity: patch.severity,
|
|
101
|
+
owner: patch.owner,
|
|
102
|
+
note: patch.note,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
/** Append a `suppressed` lifecycle event. Suppressed issues stay visible under known-noise. */
|
|
106
|
+
export function suppressIssue(root, key, patch, now) {
|
|
107
|
+
return applyLifecycle(root, key, now, 'suppressed', {
|
|
108
|
+
state: 'suppressed',
|
|
109
|
+
suppressionReason: patch.reason,
|
|
110
|
+
suppressedUntil: patch.until,
|
|
111
|
+
suppressionScope: patch.scope ?? 'fingerprint',
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Append a `linked_run` event tying a run (test case / audit check) to an issue with a relationship
|
|
116
|
+
* (observed / verified_fixed / regressed / suppressed) and optional evidence. The issue ledger stays
|
|
117
|
+
* the source of truth; this records HOW a run related to the issue (REQ-08).
|
|
118
|
+
*/
|
|
119
|
+
export function linkRun(root, key, opts, now) {
|
|
120
|
+
const index = getIndex(root, now);
|
|
121
|
+
const existing = findRecord(index, key);
|
|
122
|
+
if (!existing)
|
|
123
|
+
return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
|
|
124
|
+
const event = {
|
|
125
|
+
schemaVersion: ISSUE_SCHEMA_VERSION,
|
|
126
|
+
eventId: makeEventId(existing.issueId, now, 'linked_run', eventCountForIssue(root, existing.issueId)),
|
|
127
|
+
issueId: existing.issueId,
|
|
128
|
+
fingerprint: existing.fingerprint,
|
|
129
|
+
eventType: 'linked_run',
|
|
130
|
+
createdAt: now,
|
|
131
|
+
sourceRevision: opts.sourceRevision,
|
|
132
|
+
relationship: opts.relationship,
|
|
133
|
+
run: opts.reportUri || opts.reportPath || opts.testCaseId ? { reportUri: opts.reportUri, reportPath: opts.reportPath, testCaseId: opts.testCaseId } : undefined,
|
|
134
|
+
links: {
|
|
135
|
+
evidenceRefs: opts.evidenceUris?.map((uri) => ({ kind: 'evidence', uri })),
|
|
136
|
+
testRefs: opts.testCaseId ? [{ testCaseId: opts.testCaseId }] : undefined,
|
|
137
|
+
appMapRefs: opts.appMapRefs,
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
appendEvents(root, [event]);
|
|
141
|
+
const updated = foldEvent(existing, event);
|
|
142
|
+
upsertRecord(index, updated);
|
|
143
|
+
index.updatedAt = now;
|
|
144
|
+
saveIndex(root, index);
|
|
145
|
+
return { ok: true, record: updated };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Verify a FIXED issue with current-run evidence (REQ-08 `qa_issue_verify_fixed`). Requires the issue
|
|
149
|
+
* to be in state `fixed` and at least one evidence reference (report/test/audit). Appends a
|
|
150
|
+
* `verified_fixed` linked_run event so reports can honestly claim "verified this run".
|
|
151
|
+
*/
|
|
152
|
+
export function verifyFixed(root, key, opts, now) {
|
|
153
|
+
const index = getIndex(root, now);
|
|
154
|
+
const existing = findRecord(index, key);
|
|
155
|
+
if (!existing)
|
|
156
|
+
return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
|
|
157
|
+
if (existing.state !== 'fixed')
|
|
158
|
+
return { ok: false, reason: `Issue ${existing.issueId} is "${existing.state}", not "fixed" — only a fixed issue can be verified` };
|
|
159
|
+
const hasEvidence = Boolean(opts.reportUri || opts.testCaseId || opts.auditCheckId || (opts.evidenceUris && opts.evidenceUris.length));
|
|
160
|
+
if (!hasEvidence)
|
|
161
|
+
return { ok: false, reason: 'verify_fixed needs current-run evidence: a reportUri, testCaseId, auditCheckId, or evidenceUris' };
|
|
162
|
+
return linkRun(root, { issueId: existing.issueId }, {
|
|
163
|
+
relationship: 'verified_fixed',
|
|
164
|
+
reportUri: opts.reportUri,
|
|
165
|
+
testCaseId: opts.testCaseId ?? opts.auditCheckId,
|
|
166
|
+
evidenceUris: opts.evidenceUris,
|
|
167
|
+
sourceRevision: opts.sourceRevision,
|
|
168
|
+
}, now);
|
|
169
|
+
}
|
|
170
|
+
function applyLifecycle(root, key, now, eventType, lifecycle, sourceRevision) {
|
|
171
|
+
const index = getIndex(root, now);
|
|
172
|
+
const existing = findRecord(index, key);
|
|
173
|
+
if (!existing) {
|
|
174
|
+
return { ok: false, reason: `No issue found for ${key.issueId ?? key.fingerprint ?? '(no key)'}` };
|
|
175
|
+
}
|
|
176
|
+
const event = {
|
|
177
|
+
schemaVersion: ISSUE_SCHEMA_VERSION,
|
|
178
|
+
eventId: makeEventId(existing.issueId, now, eventType, eventCountForIssue(root, existing.issueId)),
|
|
179
|
+
issueId: existing.issueId,
|
|
180
|
+
fingerprint: existing.fingerprint,
|
|
181
|
+
eventType,
|
|
182
|
+
createdAt: now,
|
|
183
|
+
sourceRevision,
|
|
184
|
+
lifecycle,
|
|
185
|
+
};
|
|
186
|
+
appendEvents(root, [event]);
|
|
187
|
+
const updated = foldEvent(existing, event);
|
|
188
|
+
upsertRecord(index, updated);
|
|
189
|
+
index.updatedAt = now;
|
|
190
|
+
saveIndex(root, index);
|
|
191
|
+
return { ok: true, record: updated };
|
|
192
|
+
}
|
|
193
|
+
function upsertRecord(index, record) {
|
|
194
|
+
const i = index.records.findIndex((r) => r.issueId === record.issueId);
|
|
195
|
+
if (i >= 0)
|
|
196
|
+
index.records[i] = record;
|
|
197
|
+
else
|
|
198
|
+
index.records.push(record);
|
|
199
|
+
}
|
|
200
|
+
function mergePolicy(policy, ctx) {
|
|
201
|
+
// ctx.policy (in-memory) takes precedence; otherwise use the on-disk policy's classifiers.
|
|
202
|
+
if (ctx.policy?.classifiers)
|
|
203
|
+
return ctx.policy;
|
|
204
|
+
if (policy.classifiers)
|
|
205
|
+
return policy;
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
export function queryIssues(root, now, query = {}) {
|
|
209
|
+
const index = getIndex(root, now);
|
|
210
|
+
let records = index.records.slice();
|
|
211
|
+
if (!query.includeSuppressed)
|
|
212
|
+
records = records.filter((r) => r.state !== 'suppressed');
|
|
213
|
+
if (query.state)
|
|
214
|
+
records = records.filter((r) => r.state === query.state);
|
|
215
|
+
if (query.category)
|
|
216
|
+
records = records.filter((r) => r.category === query.category);
|
|
217
|
+
if (query.severity)
|
|
218
|
+
records = records.filter((r) => r.severity === query.severity);
|
|
219
|
+
if (query.platform)
|
|
220
|
+
records = records.filter((r) => r.platform === query.platform);
|
|
221
|
+
if (query.since)
|
|
222
|
+
records = records.filter((r) => r.lastSeenAt >= query.since);
|
|
223
|
+
const counts = {
|
|
224
|
+
byState: tally(records, (r) => r.state),
|
|
225
|
+
byCategory: tally(records, (r) => r.category),
|
|
226
|
+
bySeverity: tally(records, (r) => r.severity),
|
|
227
|
+
total: records.length,
|
|
228
|
+
};
|
|
229
|
+
const recurrenceCandidates = records.filter((r) => r.state === 'reopened' || r.lastRecurrenceMessage);
|
|
230
|
+
return { records, counts, recurrenceCandidates };
|
|
231
|
+
}
|
|
232
|
+
function tally(rows, key) {
|
|
233
|
+
return rows.reduce((a, r) => ((a[key(r)] = (a[key(r)] ?? 0) + 1), a), {});
|
|
234
|
+
}
|
|
235
|
+
export * from './schema.js';
|
|
236
|
+
export { fingerprint, issueIdFromFingerprint } from './fingerprint.js';
|
|
237
|
+
export { classifyObservation } from './classify.js';
|
|
238
|
+
export { resolveSourceRevision } from './sourceRevision.js';
|
|
239
|
+
export { buildRecurrenceMessage } from './recurrence.js';
|
|
240
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/issues/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// SWIPIUM-REQ-08 — issue quality metrics, derived from the append-only EVENT log (not just current
|
|
2
|
+
// index state) so trends are honest: opened / fixed / reopened / verified-fixed counts, issue aging,
|
|
3
|
+
// reopen rate (fixed issues that later reopened), fix-verification rate (fixed issues with a
|
|
4
|
+
// verified_fixed event), and a time/version/category series. PURE — `until` is the reference clock.
|
|
5
|
+
import { rebuildRecords } from './recurrence.js';
|
|
6
|
+
function inRange(at, since, until) {
|
|
7
|
+
if (since && at < since)
|
|
8
|
+
return false;
|
|
9
|
+
if (until && at > until)
|
|
10
|
+
return false;
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
function isoWeek(iso) {
|
|
14
|
+
const d = new Date(iso);
|
|
15
|
+
if (Number.isNaN(d.getTime()))
|
|
16
|
+
return iso.slice(0, 10);
|
|
17
|
+
const day = (d.getUTCDay() + 6) % 7; // Mon=0
|
|
18
|
+
const thursday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day + 3));
|
|
19
|
+
const firstThursday = new Date(Date.UTC(thursday.getUTCFullYear(), 0, 4));
|
|
20
|
+
const week = 1 + Math.round(((thursday.getTime() - firstThursday.getTime()) / 86400000 - 3 + ((firstThursday.getUTCDay() + 6) % 7)) / 7);
|
|
21
|
+
return `${thursday.getUTCFullYear()}-W${String(week).padStart(2, '0')}`;
|
|
22
|
+
}
|
|
23
|
+
function bucketKey(ev, groupBy) {
|
|
24
|
+
switch (groupBy) {
|
|
25
|
+
case 'day':
|
|
26
|
+
return ev.createdAt.slice(0, 10);
|
|
27
|
+
case 'week':
|
|
28
|
+
return isoWeek(ev.createdAt);
|
|
29
|
+
case 'version':
|
|
30
|
+
return ev.sourceRevision?.buildVersion ?? 'unknown';
|
|
31
|
+
case 'commit':
|
|
32
|
+
return ev.sourceRevision?.commit ?? 'unknown';
|
|
33
|
+
case 'category':
|
|
34
|
+
return ev.classification?.category ?? 'unknown';
|
|
35
|
+
case 'owner':
|
|
36
|
+
return ev.classification?.owner ?? 'unknown';
|
|
37
|
+
case 'screen':
|
|
38
|
+
return ev.links?.appMapRefs?.[0]?.screenId ?? ev.observation?.screenId ?? 'unknown';
|
|
39
|
+
case 'feature':
|
|
40
|
+
return ev.links?.appMapRefs?.[0]?.featureId ?? 'unknown';
|
|
41
|
+
default:
|
|
42
|
+
return ev.createdAt.slice(0, 10);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function ageDays(fromIso, untilIso) {
|
|
46
|
+
const a = new Date(fromIso).getTime();
|
|
47
|
+
const b = new Date(untilIso).getTime();
|
|
48
|
+
if (Number.isNaN(a) || Number.isNaN(b))
|
|
49
|
+
return 0;
|
|
50
|
+
return Math.max(0, (b - a) / 86400000);
|
|
51
|
+
}
|
|
52
|
+
function percentile(sorted, p) {
|
|
53
|
+
if (sorted.length === 0)
|
|
54
|
+
return 0;
|
|
55
|
+
const idx = Math.min(sorted.length - 1, Math.ceil((p / 100) * sorted.length) - 1);
|
|
56
|
+
return sorted[Math.max(0, idx)];
|
|
57
|
+
}
|
|
58
|
+
/** Compute issue metrics from the full event stream. `until` defaults to the latest event time. */
|
|
59
|
+
export function computeIssueMetrics(events, opts = {}) {
|
|
60
|
+
const groupBy = opts.groupBy ?? 'week';
|
|
61
|
+
const until = opts.until ?? events.reduce((m, e) => (e.createdAt > m ? e.createdAt : m), '1970-01-01T00:00:00.000Z');
|
|
62
|
+
const records = rebuildRecords(events);
|
|
63
|
+
// First-observation EVENT per issue → "opened". Track the event id (not just the timestamp) so
|
|
64
|
+
// two same-timestamp observations of one fingerprint count as a single open (REQ-08 follow-up).
|
|
65
|
+
const firstObserved = new Map();
|
|
66
|
+
for (const e of events) {
|
|
67
|
+
if (e.eventType !== 'observed')
|
|
68
|
+
continue;
|
|
69
|
+
const prev = firstObserved.get(e.issueId);
|
|
70
|
+
if (!prev || e.createdAt < prev.at)
|
|
71
|
+
firstObserved.set(e.issueId, { at: e.createdAt, eventId: e.eventId });
|
|
72
|
+
}
|
|
73
|
+
let opened = 0;
|
|
74
|
+
for (const [, first] of firstObserved)
|
|
75
|
+
if (inRange(first.at, opts.since, opts.until))
|
|
76
|
+
opened++;
|
|
77
|
+
let fixed = 0;
|
|
78
|
+
let reopened = 0;
|
|
79
|
+
let verifiedFixed = 0;
|
|
80
|
+
let suppressed = 0;
|
|
81
|
+
const everFixed = new Set();
|
|
82
|
+
const fixedThenReopened = new Set();
|
|
83
|
+
const verifiedFixedIssues = new Set();
|
|
84
|
+
const fixedAtById = new Map();
|
|
85
|
+
const series = new Map();
|
|
86
|
+
const bump = (key, field) => {
|
|
87
|
+
const b = series.get(key) ?? { opened: 0, fixed: 0, reopened: 0, verifiedFixed: 0 };
|
|
88
|
+
b[field]++;
|
|
89
|
+
series.set(key, b);
|
|
90
|
+
};
|
|
91
|
+
for (const e of events) {
|
|
92
|
+
const within = inRange(e.createdAt, opts.since, opts.until);
|
|
93
|
+
if (e.eventType === 'observed' && firstObserved.get(e.issueId)?.eventId === e.eventId && within)
|
|
94
|
+
bump(bucketKey(e, groupBy), 'opened');
|
|
95
|
+
if (e.eventType === 'fixed') {
|
|
96
|
+
everFixed.add(e.issueId);
|
|
97
|
+
fixedAtById.set(e.issueId, e.createdAt);
|
|
98
|
+
if (within) {
|
|
99
|
+
fixed++;
|
|
100
|
+
bump(bucketKey(e, groupBy), 'fixed');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (e.eventType === 'reopened') {
|
|
104
|
+
// a reopen counts toward reopen-rate only if it followed a fix.
|
|
105
|
+
if (everFixed.has(e.issueId))
|
|
106
|
+
fixedThenReopened.add(e.issueId);
|
|
107
|
+
if (within) {
|
|
108
|
+
reopened++;
|
|
109
|
+
bump(bucketKey(e, groupBy), 'reopened');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (e.eventType === 'linked_run' && e.relationship === 'verified_fixed') {
|
|
113
|
+
verifiedFixedIssues.add(e.issueId);
|
|
114
|
+
if (within) {
|
|
115
|
+
verifiedFixed++;
|
|
116
|
+
bump(bucketKey(e, groupBy), 'verifiedFixed');
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (e.eventType === 'suppressed' && within)
|
|
120
|
+
suppressed++;
|
|
121
|
+
}
|
|
122
|
+
const noiseIssues = records.filter((r) => (opts.includeSuppressed || r.state !== 'suppressed') && (r.category === 'environment_noise' || r.state === 'expected_environment_noise'));
|
|
123
|
+
const openRecords = records.filter((r) => r.state === 'open' || r.state === 'observed_again' || r.state === 'reopened' || r.state === 'needs_triage');
|
|
124
|
+
const ages = openRecords.map((r) => ageDays(r.firstSeenAt, until)).sort((a, b) => a - b);
|
|
125
|
+
const avgAgeDays = ages.length ? Number((ages.reduce((s, v) => s + v, 0) / ages.length).toFixed(2)) : 0;
|
|
126
|
+
const p95AgeDays = Number(percentile(ages, 95).toFixed(2));
|
|
127
|
+
const reopenRatePct = everFixed.size ? Number(((fixedThenReopened.size / everFixed.size) * 100).toFixed(1)) : 0;
|
|
128
|
+
const fixVerificationRatePct = everFixed.size ? Number(((verifiedFixedIssues.size / everFixed.size) * 100).toFixed(1)) : 0;
|
|
129
|
+
const topRecurringIssues = records
|
|
130
|
+
.filter((r) => r.lastRecurrenceMessage || r.state === 'reopened')
|
|
131
|
+
.sort((a, b) => (b.lastSeenAt > a.lastSeenAt ? 1 : -1))
|
|
132
|
+
.slice(0, 5);
|
|
133
|
+
const topAgingIssues = openRecords
|
|
134
|
+
.slice()
|
|
135
|
+
.sort((a, b) => ageDays(b.firstSeenAt, until) - ageDays(a.firstSeenAt, until))
|
|
136
|
+
.slice(0, 5);
|
|
137
|
+
return {
|
|
138
|
+
opened,
|
|
139
|
+
fixed,
|
|
140
|
+
reopened,
|
|
141
|
+
verifiedFixed,
|
|
142
|
+
suppressed,
|
|
143
|
+
environmentNoise: noiseIssues.length,
|
|
144
|
+
avgAgeDays,
|
|
145
|
+
p95AgeDays,
|
|
146
|
+
reopenRatePct,
|
|
147
|
+
fixVerificationRatePct,
|
|
148
|
+
blockerOpenCount: openRecords.filter((r) => r.severity === 'blocker').length,
|
|
149
|
+
highOpenCount: openRecords.filter((r) => r.severity === 'high').length,
|
|
150
|
+
topRecurringIssues,
|
|
151
|
+
topAgingIssues,
|
|
152
|
+
series: [...series.entries()].sort((a, b) => (a[0] < b[0] ? -1 : 1)).map(([bucket, v]) => ({ bucket, ...v })),
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metrics.js","sourceRoot":"","sources":["../../src/issues/metrics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// SWIPIUM Issue Log — lifecycle folding + recurrence messages (SWIPIUM-REQ-07 "Recurrence Behavior").
|
|
2
|
+
//
|
|
3
|
+
// PURE. `foldEvent` is the single reducer that turns the append-only event stream into the derived
|
|
4
|
+
// index record — so the index can always be rebuilt from `issues-log.jsonl`. `buildRecurrenceMessage`
|
|
5
|
+
// produces the product-facing text shown in reports when a fixed issue reappears.
|
|
6
|
+
import { ISSUE_SCHEMA_VERSION } from './schema.js';
|
|
7
|
+
/** Format an ISO timestamp as a YYYY-MM-DD date for human-facing recurrence text. */
|
|
8
|
+
export function isoDate(iso) {
|
|
9
|
+
if (!iso)
|
|
10
|
+
return 'an unknown date';
|
|
11
|
+
const m = iso.match(/^(\d{4}-\d{2}-\d{2})/);
|
|
12
|
+
return m ? m[1] : iso;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Build the report-ready recurrence message for an issue that was observed again.
|
|
16
|
+
* Three variants per the spec: fixed-with-commit, fixed-without-commit, never-fixed.
|
|
17
|
+
*/
|
|
18
|
+
export function buildRecurrenceMessage(record) {
|
|
19
|
+
const summary = record.summary || record.title;
|
|
20
|
+
if (record.fixedAt && record.fixedInCommit) {
|
|
21
|
+
const note = record.howFixed ? ` Previous fix note: ${record.howFixed}.` : '';
|
|
22
|
+
return `This error ${summary} appeared again. It was first reported on ${isoDate(record.firstSeenAt)} and fixed on ${isoDate(record.fixedAt)} in ${record.fixedInCommit}.${note}`;
|
|
23
|
+
}
|
|
24
|
+
if (record.fixedAt) {
|
|
25
|
+
return `This error ${summary} appeared again. It was first reported on ${isoDate(record.firstSeenAt)} and fixed on ${isoDate(record.fixedAt)}. No fixed commit was recorded.`;
|
|
26
|
+
}
|
|
27
|
+
return `This error ${summary} was observed again. It was first reported on ${isoDate(record.firstSeenAt)} and has appeared ${record.observationCount} times.`;
|
|
28
|
+
}
|
|
29
|
+
function mergeRefs(existing, incoming, key) {
|
|
30
|
+
if (!existing && !incoming)
|
|
31
|
+
return undefined;
|
|
32
|
+
const seen = new Map();
|
|
33
|
+
for (const r of existing ?? [])
|
|
34
|
+
seen.set(key(r), r);
|
|
35
|
+
for (const r of incoming ?? [])
|
|
36
|
+
seen.set(key(r), r);
|
|
37
|
+
return [...seen.values()];
|
|
38
|
+
}
|
|
39
|
+
function mergeLinks(record, links) {
|
|
40
|
+
if (!links)
|
|
41
|
+
return;
|
|
42
|
+
record.appMapRefs = mergeRefs(record.appMapRefs, links.appMapRefs, (r) => `${r.screenId ?? ''}|${r.featureId ?? ''}`);
|
|
43
|
+
record.testRefs = mergeRefs(record.testRefs, links.testRefs, (r) => r.testCaseId);
|
|
44
|
+
record.reportRefs = mergeRefs(record.reportRefs, links.reportRefs, (r) => `${r.reportPath ?? ''}|${r.reportUri ?? ''}`);
|
|
45
|
+
record.evidenceRefs = mergeRefs(record.evidenceRefs, links.evidenceRefs, (r) => `${r.kind}|${r.uri ?? r.path ?? ''}`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Fold a single event onto the running index record. Returns the updated record (a new object).
|
|
49
|
+
* `prev` is null when this is the first event for the issue. This reducer is the canonical
|
|
50
|
+
* definition of "what state is this issue in", and is replayed in order to rebuild the index.
|
|
51
|
+
*/
|
|
52
|
+
export function foldEvent(prev, event) {
|
|
53
|
+
const obs = event.observation;
|
|
54
|
+
const cls = event.classification;
|
|
55
|
+
const lc = event.lifecycle;
|
|
56
|
+
const record = prev != null
|
|
57
|
+
? { ...prev }
|
|
58
|
+
: {
|
|
59
|
+
schemaVersion: ISSUE_SCHEMA_VERSION,
|
|
60
|
+
issueId: event.issueId,
|
|
61
|
+
fingerprint: event.fingerprint,
|
|
62
|
+
title: obs?.title ?? 'Untitled issue',
|
|
63
|
+
summary: obs?.summary ?? obs?.title ?? 'issue',
|
|
64
|
+
state: 'needs_triage',
|
|
65
|
+
category: cls?.category ?? 'mcp_limitation',
|
|
66
|
+
severity: cls?.severity ?? 'low',
|
|
67
|
+
confidence: cls?.confidence ?? 0.5,
|
|
68
|
+
owner: cls?.owner,
|
|
69
|
+
platform: event.platform,
|
|
70
|
+
environment: event.environment,
|
|
71
|
+
firstSeenAt: event.createdAt,
|
|
72
|
+
lastSeenAt: event.createdAt,
|
|
73
|
+
observationCount: 0,
|
|
74
|
+
};
|
|
75
|
+
// Keep the most descriptive title/summary if a later observation has one.
|
|
76
|
+
if (obs?.title)
|
|
77
|
+
record.title = obs.title;
|
|
78
|
+
if (obs?.summary)
|
|
79
|
+
record.summary = obs.summary;
|
|
80
|
+
if (event.platform && record.platform == null)
|
|
81
|
+
record.platform = event.platform;
|
|
82
|
+
if (event.environment && record.environment == null)
|
|
83
|
+
record.environment = event.environment;
|
|
84
|
+
// Classification updates (latest classification wins for category/severity/owner/confidence).
|
|
85
|
+
if (cls) {
|
|
86
|
+
record.category = cls.category;
|
|
87
|
+
record.severity = cls.severity;
|
|
88
|
+
record.owner = cls.owner;
|
|
89
|
+
record.confidence = cls.confidence;
|
|
90
|
+
}
|
|
91
|
+
mergeLinks(record, event.links);
|
|
92
|
+
if (event.run) {
|
|
93
|
+
const reportRef = { reportPath: event.run.reportPath, reportUri: event.run.reportUri };
|
|
94
|
+
if (reportRef.reportPath || reportRef.reportUri) {
|
|
95
|
+
record.reportRefs = mergeRefs(record.reportRefs, [reportRef], (r) => `${r.reportPath ?? ''}|${r.reportUri ?? ''}`);
|
|
96
|
+
}
|
|
97
|
+
if (event.run.testCaseId) {
|
|
98
|
+
record.testRefs = mergeRefs(record.testRefs, [{ testCaseId: event.run.testCaseId }], (r) => r.testCaseId);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
switch (event.eventType) {
|
|
102
|
+
case 'observed': {
|
|
103
|
+
record.observationCount += 1;
|
|
104
|
+
record.lastSeenAt = event.createdAt;
|
|
105
|
+
// Recurrence: an observation of a FIXED issue reopens it.
|
|
106
|
+
if (record.state === 'fixed') {
|
|
107
|
+
record.state = 'reopened';
|
|
108
|
+
record.reopenedAt = event.createdAt;
|
|
109
|
+
record.lastRecurrenceMessage = buildRecurrenceMessage(record);
|
|
110
|
+
}
|
|
111
|
+
else if (record.state === 'open' || record.state === 'observed_again') {
|
|
112
|
+
record.state = 'observed_again';
|
|
113
|
+
}
|
|
114
|
+
else if (record.state === 'needs_triage') {
|
|
115
|
+
// first observation of a noise/gate stays in its classified lane; otherwise open.
|
|
116
|
+
record.state = isNoiseCategory(record) ? 'expected_environment_noise' : 'open';
|
|
117
|
+
}
|
|
118
|
+
else if (record.state === 'reopened') {
|
|
119
|
+
// stays reopened, just bump counts
|
|
120
|
+
}
|
|
121
|
+
else if (record.state === 'suppressed' || record.state === 'expected_environment_noise') {
|
|
122
|
+
// suppressed/noise: keep the lane (still visible in known-noise), bump counts only.
|
|
123
|
+
}
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case 'classified': {
|
|
127
|
+
// pure re-classification of an existing record; lane derived from category.
|
|
128
|
+
if (record.state === 'needs_triage')
|
|
129
|
+
record.state = isNoiseCategory(record) ? 'expected_environment_noise' : 'open';
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case 'fixed': {
|
|
133
|
+
record.state = 'fixed';
|
|
134
|
+
record.fixedAt = lc?.fixedAt ?? event.createdAt;
|
|
135
|
+
record.fixedInCommit = lc?.fixedInCommit ?? event.sourceRevision?.commit ?? record.fixedInCommit;
|
|
136
|
+
record.fixedInVersion = lc?.fixedInVersion ?? event.sourceRevision?.buildVersion ?? record.fixedInVersion;
|
|
137
|
+
record.fixedBy = lc?.fixedBy ?? record.fixedBy;
|
|
138
|
+
record.howFixed = lc?.howFixed ?? record.howFixed;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
case 'reopened': {
|
|
142
|
+
record.state = 'reopened';
|
|
143
|
+
record.reopenedAt = lc?.reopenedAt ?? event.createdAt;
|
|
144
|
+
record.lastRecurrenceMessage = lc?.recurrenceMessage ?? buildRecurrenceMessage(record);
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
case 'suppressed': {
|
|
148
|
+
record.state = 'suppressed';
|
|
149
|
+
record.suppressedUntil = lc?.suppressedUntil;
|
|
150
|
+
record.suppressionReason = lc?.suppressionReason;
|
|
151
|
+
record.suppressionScope = lc?.suppressionScope;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case 'triaged': {
|
|
155
|
+
if (lc?.category)
|
|
156
|
+
record.category = lc.category;
|
|
157
|
+
if (lc?.severity)
|
|
158
|
+
record.severity = lc.severity;
|
|
159
|
+
if (lc?.owner)
|
|
160
|
+
record.owner = lc.owner;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
case 'linked_run': {
|
|
164
|
+
// Evidence from a run (test case / audit check), not a state change — links already merged.
|
|
165
|
+
// A `verified_fixed` link records that a passing run confirmed the fix still holds (REQ-08).
|
|
166
|
+
if (event.relationship === 'verified_fixed' && record.state === 'fixed') {
|
|
167
|
+
record.lastVerifiedFixedAt = event.createdAt;
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
case 'note_added':
|
|
172
|
+
// links already merged above; no state change.
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
// Apply any explicit state override carried on a lifecycle patch last.
|
|
176
|
+
if (lc?.state)
|
|
177
|
+
record.state = lc.state;
|
|
178
|
+
return record;
|
|
179
|
+
}
|
|
180
|
+
function isNoiseCategory(record) {
|
|
181
|
+
return record.category === 'environment_noise';
|
|
182
|
+
}
|
|
183
|
+
/** Replay an ordered event stream into the full set of index records (rebuild). */
|
|
184
|
+
export function rebuildRecords(events) {
|
|
185
|
+
const byId = new Map();
|
|
186
|
+
for (const ev of events) {
|
|
187
|
+
const prev = byId.get(ev.issueId) ?? null;
|
|
188
|
+
byId.set(ev.issueId, foldEvent(prev, ev));
|
|
189
|
+
}
|
|
190
|
+
return [...byId.values()];
|
|
191
|
+
}
|
|
192
|
+
export const ALL_STATES_FOR_TEST = ['open', 'fixed', 'reopened'];
|
|
193
|
+
//# sourceMappingURL=recurrence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recurrence.js","sourceRoot":"","sources":["../../src/issues/recurrence.ts"],"names":[],"mappings":"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"}
|