@aplaytest/heal 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
package/dist/ledger.js ADDED
@@ -0,0 +1,154 @@
1
+ /**
2
+ * The heal ledger.
3
+ *
4
+ * One git-tracked JSON file per heal, written when a patch is applied. It is
5
+ * the audit trail, and it belongs in the repository alongside the change it
6
+ * justifies: six months later, "why is this selector called that?" should be
7
+ * answerable from the repo rather than from a dashboard nobody kept.
8
+ *
9
+ * The record stores the ORIGINAL FILE TEXT rather than a diff. Reverting from
10
+ * a stored diff means re-deriving context that may have moved on; storing the
11
+ * text makes revert exact and total, which is the property you want from an
12
+ * undo button for automated edits.
13
+ */
14
+ import { createHash } from 'node:crypto';
15
+ import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises';
16
+ import { join } from 'node:path';
17
+ import { patchConstant } from './patch.js';
18
+ export const DEFAULT_LEDGER_DIR = '.atest/heals';
19
+ export const HEAL_SCHEMA_VERSION = 1;
20
+ /** Derived, not random: the same heal always resolves to the same id. */
21
+ export function healId(evidenceId, from, to) {
22
+ const digest = createHash('sha256').update(`${evidenceId} ${from} ${to}`).digest('hex');
23
+ return `heal_${digest.slice(0, 10)}`;
24
+ }
25
+ export function buildRecord(proposal, context) {
26
+ const patch = proposal.patch;
27
+ const chosen = proposal.chosen;
28
+ if (patch === null || chosen === null || patch.after === null)
29
+ return null;
30
+ const from = patch.from;
31
+ return {
32
+ schemaVersion: HEAL_SCHEMA_VERSION,
33
+ healId: healId(proposal.evidenceId, from, chosen.value),
34
+ createdAt: new Date().toISOString(),
35
+ atestVersion: context.atestVersion,
36
+ evidenceId: proposal.evidenceId,
37
+ test: { title: proposal.testTitle, file: context.testFile, project: context.project },
38
+ failureKind: context.failureKind,
39
+ strategy: 'selector',
40
+ tier: proposal.tierOne?.used === true ? 1 : 0,
41
+ model: proposal.tierOne?.model ?? null,
42
+ patch: {
43
+ file: patch.file,
44
+ from,
45
+ to: chosen.value,
46
+ constants: patch.touched.map(t => t.path),
47
+ stabilityDelta: chosen.stabilityDelta,
48
+ },
49
+ candidatesConsidered: proposal.candidates.length,
50
+ semanticDistance: chosen.semanticDistance,
51
+ validation: proposal.validation,
52
+ status: 'applied',
53
+ revertText: patch.before,
54
+ revertedAt: null,
55
+ };
56
+ }
57
+ export async function writeRecord(record, dir = DEFAULT_LEDGER_DIR) {
58
+ await mkdir(dir, { recursive: true });
59
+ const path = join(dir, `${record.healId}.json`);
60
+ await writeFile(path, `${JSON.stringify(record, null, 2)}\n`, 'utf8');
61
+ return path;
62
+ }
63
+ const JSON_EXTENSION = '.json';
64
+ function isRecord(value) {
65
+ return typeof value === 'object' && value !== null;
66
+ }
67
+ function isHealStatus(value) {
68
+ return value === 'applied' || value === 'reverted';
69
+ }
70
+ function isHealRecord(value) {
71
+ if (!isRecord(value))
72
+ return false;
73
+ return (value['schemaVersion'] === HEAL_SCHEMA_VERSION &&
74
+ typeof value['healId'] === 'string' &&
75
+ typeof value['createdAt'] === 'string' &&
76
+ isHealStatus(value['status']) &&
77
+ isRecord(value['patch']) &&
78
+ typeof value['patch']['file'] === 'string' &&
79
+ typeof value['revertText'] === 'string');
80
+ }
81
+ function parseHealRecord(raw) {
82
+ try {
83
+ const parsed = JSON.parse(raw);
84
+ return isHealRecord(parsed) ? parsed : null;
85
+ }
86
+ catch {
87
+ return null;
88
+ }
89
+ }
90
+ export async function readRecords(dir = DEFAULT_LEDGER_DIR) {
91
+ const files = (await readdir(dir).catch(() => [])).filter(file => file.endsWith(JSON_EXTENSION));
92
+ const records = [];
93
+ for (const file of files) {
94
+ const raw = await readFile(join(dir, file), 'utf8').catch(() => null);
95
+ if (raw === null)
96
+ continue;
97
+ const record = parseHealRecord(raw);
98
+ // A malformed ledger entry must not hide the rest of the ledger.
99
+ if (record !== null)
100
+ records.push(record);
101
+ }
102
+ return records.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
103
+ }
104
+ /**
105
+ * Restore the file exactly as it was before the heal.
106
+ *
107
+ * Refuses when the file has changed since — a revert that silently discards
108
+ * someone's later edit is worse than the heal it is undoing.
109
+ */
110
+ export async function revertHeal(id, options = {}) {
111
+ const dir = options.dir ?? DEFAULT_LEDGER_DIR;
112
+ const records = await readRecords(dir);
113
+ const record = records.find(r => r.healId === id);
114
+ if (record === undefined) {
115
+ return { status: 'not-found', message: `No heal ${id} in ${dir}.`, record: null };
116
+ }
117
+ if (record.status === 'reverted') {
118
+ return {
119
+ status: 'already-reverted',
120
+ message: `${id} was already reverted on ${record.revertedAt ?? 'an earlier date'}.`,
121
+ record,
122
+ };
123
+ }
124
+ const current = await readFile(record.patch.file, 'utf8').catch(() => null);
125
+ if (current === null) {
126
+ return { status: 'not-found', message: `Cannot read ${record.patch.file}.`, record };
127
+ }
128
+ // Replay the original patch, quote-aware and for every shared literal.
129
+ // `String.replace` would only change the first occurrence and ignore quotes,
130
+ // so a two-constant heal would look "changed" and refuse to revert.
131
+ const replayed = patchConstant(record.revertText, {
132
+ file: record.patch.file,
133
+ from: record.patch.from,
134
+ to: record.patch.to,
135
+ });
136
+ const expected = replayed.after ?? record.revertText;
137
+ if (options.force !== true && current !== expected) {
138
+ return {
139
+ status: 'file-changed',
140
+ message: `${record.patch.file} has changed since this heal was applied. Reverting would discard ` +
141
+ 'those edits. Re-apply the original value by hand, or pass --force.',
142
+ record,
143
+ };
144
+ }
145
+ await writeFile(record.patch.file, record.revertText, 'utf8');
146
+ const updated = { ...record, status: 'reverted', revertedAt: new Date().toISOString() };
147
+ await writeRecord(updated, dir);
148
+ return {
149
+ status: 'reverted',
150
+ message: `Restored ${record.patch.file} to its state before ${id}.`,
151
+ record: updated,
152
+ };
153
+ }
154
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../src/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Selector patching.
3
+ *
4
+ * Heals target the CONSTANTS file, not the spec. In a suite where every
5
+ * selector is a named string in `<feature>.constants.ts`, a selector heal is a
6
+ * one-line diff in a file with no logic in it — a reviewer can judge it in ten
7
+ * seconds, which is what makes proposing heals at all defensible.
8
+ *
9
+ * Done with ts-morph rather than text replacement for one concrete reason: a
10
+ * single literal is routinely bound to more than one constant. In a real
11
+ * suite `'gym-card-name'` appears as BOTH `TEST_IDS.cardName` and
12
+ * `GYM_CARD_TEST_IDS.name`, used by different page objects. A regex would fix
13
+ * one, leave the other, and produce a patch that passes its own validation
14
+ * while breaking the file's other tests.
15
+ */
16
+ export type PatchStatus = 'applied' | 'not-found' | 'unchanged';
17
+ export interface TouchedConstant {
18
+ /** e.g. "TEST_IDS.cardName", or "SEARCH_INPUT" for a bare const. */
19
+ readonly path: string;
20
+ readonly line: number;
21
+ }
22
+ export interface PatchResult {
23
+ readonly status: PatchStatus;
24
+ readonly file: string;
25
+ /** Literal that was searched for. Stored so revert does not scrape `message`. */
26
+ readonly from: string;
27
+ readonly to: string;
28
+ readonly before: string;
29
+ readonly after: string | null;
30
+ readonly touched: readonly TouchedConstant[];
31
+ readonly message: string;
32
+ }
33
+ export interface PatchInput {
34
+ readonly file: string;
35
+ /** The literal value to replace, e.g. 'gym-card-name'. */
36
+ readonly from: string;
37
+ readonly to: string;
38
+ }
39
+ /**
40
+ * Replace every occurrence of a literal value in a constants file.
41
+ *
42
+ * Pure with respect to disk: the caller decides whether to write `after`, so
43
+ * a dry run and a real apply take exactly the same code path.
44
+ */
45
+ export declare function patchConstant(sourceText: string, input: PatchInput): PatchResult;
46
+ /**
47
+ * Locate a literal without changing anything — used to report where a
48
+ * selector is defined, which is the difference between "the selector broke"
49
+ * and "the selector broke, here is the line".
50
+ */
51
+ export declare function findConstant(sourceText: string, file: string, value: string): TouchedConstant[];
52
+ //# sourceMappingURL=patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.d.ts","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAkCD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,WAAW,CA2DhF;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,CAQ/F"}
package/dist/patch.js ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Selector patching.
3
+ *
4
+ * Heals target the CONSTANTS file, not the spec. In a suite where every
5
+ * selector is a named string in `<feature>.constants.ts`, a selector heal is a
6
+ * one-line diff in a file with no logic in it — a reviewer can judge it in ten
7
+ * seconds, which is what makes proposing heals at all defensible.
8
+ *
9
+ * Done with ts-morph rather than text replacement for one concrete reason: a
10
+ * single literal is routinely bound to more than one constant. In a real
11
+ * suite `'gym-card-name'` appears as BOTH `TEST_IDS.cardName` and
12
+ * `GYM_CARD_TEST_IDS.name`, used by different page objects. A regex would fix
13
+ * one, leave the other, and produce a patch that passes its own validation
14
+ * while breaking the file's other tests.
15
+ */
16
+ import { Node, Project, SyntaxKind } from 'ts-morph';
17
+ /** Re-emit a value using the same quote character the original literal used. */
18
+ function requote(originalText, value) {
19
+ const quote = originalText.charAt(0);
20
+ if (quote !== "'" && quote !== '"' && quote !== '`')
21
+ return JSON.stringify(value);
22
+ const escaped = value.replace(/\\/g, '\\\\').replace(new RegExp(quote, 'g'), `\\${quote}`);
23
+ return `${quote}${escaped}${quote}`;
24
+ }
25
+ /** Build a readable path for a string literal: `OBJECT.property` or `CONST`. */
26
+ function describeLocation(node) {
27
+ const property = node.getFirstAncestorByKind(SyntaxKind.PropertyAssignment);
28
+ if (property !== undefined) {
29
+ const declaration = property.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
30
+ const owner = declaration?.getName();
31
+ const name = property.getName().replace(/['"`]/g, '');
32
+ return owner === undefined ? name : `${owner}.${name}`;
33
+ }
34
+ const declaration = node.getFirstAncestorByKind(SyntaxKind.VariableDeclaration);
35
+ return declaration?.getName() ?? null;
36
+ }
37
+ function stringLiteralsEqualTo(source, value) {
38
+ return [
39
+ ...source.getDescendantsOfKind(SyntaxKind.StringLiteral),
40
+ ...source.getDescendantsOfKind(SyntaxKind.NoSubstitutionTemplateLiteral),
41
+ ].filter(node => {
42
+ const literal = node.asKind(SyntaxKind.StringLiteral) ?? node.asKind(SyntaxKind.NoSubstitutionTemplateLiteral);
43
+ return literal?.getLiteralText() === value;
44
+ });
45
+ }
46
+ /**
47
+ * Replace every occurrence of a literal value in a constants file.
48
+ *
49
+ * Pure with respect to disk: the caller decides whether to write `after`, so
50
+ * a dry run and a real apply take exactly the same code path.
51
+ */
52
+ export function patchConstant(sourceText, input) {
53
+ if (input.from === input.to) {
54
+ return {
55
+ status: 'unchanged',
56
+ file: input.file,
57
+ from: input.from,
58
+ to: input.to,
59
+ before: sourceText,
60
+ after: null,
61
+ touched: [],
62
+ message: 'The replacement is identical to the original.',
63
+ };
64
+ }
65
+ const project = new Project({ useInMemoryFileSystem: true });
66
+ const source = project.createSourceFile(input.file, sourceText, { overwrite: true });
67
+ const matches = stringLiteralsEqualTo(source, input.from);
68
+ if (matches.length === 0) {
69
+ return {
70
+ status: 'not-found',
71
+ file: input.file,
72
+ from: input.from,
73
+ to: input.to,
74
+ before: sourceText,
75
+ after: null,
76
+ touched: [],
77
+ message: `No literal "${input.from}" in ${input.file}.`,
78
+ };
79
+ }
80
+ const touched = [];
81
+ for (const node of matches) {
82
+ const path = describeLocation(node);
83
+ touched.push({
84
+ path: path ?? '(anonymous)',
85
+ line: source.getLineAndColumnAtPos(node.getStart()).line,
86
+ });
87
+ // Reuse the literal's own quote style rather than emitting JSON. A patch
88
+ // that switches a single-quoted file to double quotes fights the
89
+ // formatter and turns a one-word change into a noisy diff — which is
90
+ // exactly the kind of friction that gets a proposal rejected on sight.
91
+ node.replaceWithText(requote(node.getText(), input.to));
92
+ }
93
+ return {
94
+ status: 'applied',
95
+ file: input.file,
96
+ from: input.from,
97
+ to: input.to,
98
+ before: sourceText,
99
+ after: source.getFullText(),
100
+ touched,
101
+ message: touched.length === 1
102
+ ? `Replaced "${input.from}" with "${input.to}" in ${touched[0]?.path ?? ''}.`
103
+ : `Replaced "${input.from}" with "${input.to}" in ${touched.length} constants sharing ` +
104
+ `that literal: ${touched.map(t => t.path).join(', ')}.`,
105
+ };
106
+ }
107
+ /**
108
+ * Locate a literal without changing anything — used to report where a
109
+ * selector is defined, which is the difference between "the selector broke"
110
+ * and "the selector broke, here is the line".
111
+ */
112
+ export function findConstant(sourceText, file, value) {
113
+ const project = new Project({ useInMemoryFileSystem: true });
114
+ const source = project.createSourceFile(file, sourceText, { overwrite: true });
115
+ return stringLiteralsEqualTo(source, value).map(node => ({
116
+ path: describeLocation(node) ?? '(anonymous)',
117
+ line: source.getLineAndColumnAtPos(node.getStart()).line,
118
+ }));
119
+ }
120
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * The heal pipeline, end to end.
3
+ *
4
+ * Order is load-bearing, and two of the gates are the whole reason this is
5
+ * defensible at all:
6
+ *
7
+ * 1. NEVER_HEAL kinds are refused outright. A schema violation or an
8
+ * uncaught application error IS the bug; "repairing" it would delete the
9
+ * most valuable signal a suite produces.
10
+ * 2. Known-flaky tests are refused. Healing a flake is the worst outcome
11
+ * available: a permanent code change made to chase noise, the flake
12
+ * continues, and now the selector is wrong too. Flaky and broken are
13
+ * different problems, and the engine must decide which it is FIRST.
14
+ *
15
+ * Only then does it generate candidates, and only then does Playwright decide.
16
+ */
17
+ import { type EvidenceBundle, type LocatorStrategy } from '@aplaytest/core';
18
+ import { type CandidateOptions, type HealCandidate } from './candidates.js';
19
+ import { type PatchResult } from './patch.js';
20
+ import { type ValidationRecord } from './validate.js';
21
+ export type ProposalStatus = 'proposed' | 'refused-never-heal' | 'refused-flaky' | 'refused-ineligible' | 'no-candidates' | 'no-constant' | 'rejected';
22
+ export interface TierOneRecord {
23
+ readonly used: boolean;
24
+ readonly model: string | null;
25
+ readonly outcome: string;
26
+ readonly reasoning: string | null;
27
+ readonly confidence: number | null;
28
+ readonly usd: number;
29
+ /** True when Tier 1 picked something other than Tier 0's top candidate. */
30
+ readonly changedChoice: boolean;
31
+ }
32
+ export interface HealProposal {
33
+ readonly status: ProposalStatus;
34
+ readonly evidenceId: string;
35
+ readonly testTitle: string;
36
+ readonly reason: string;
37
+ readonly intendedSelector: string | null;
38
+ readonly candidates: readonly HealCandidate[];
39
+ readonly chosen: HealCandidate | null;
40
+ readonly patch: PatchResult | null;
41
+ readonly validation: ValidationRecord | null;
42
+ readonly tierOne: TierOneRecord | null;
43
+ }
44
+ export interface ProposeOptions {
45
+ readonly cwd: string;
46
+ /**
47
+ * File that defines the selector. Optional: when omitted the engine walks
48
+ * `targetGlobs` (constants, page objects, specs) and prefers the most
49
+ * reviewable hit. `--constants` still wins when the caller knows the file.
50
+ */
51
+ readonly constantsFile?: string | undefined;
52
+ readonly constantsText?: string | undefined;
53
+ readonly targetGlobs?: readonly string[] | undefined;
54
+ readonly allowedStrategies?: readonly LocatorStrategy[] | undefined;
55
+ readonly specFile: string;
56
+ readonly config?: string | undefined;
57
+ readonly project?: string | undefined;
58
+ readonly validationRuns: number;
59
+ readonly checkCollateral: boolean;
60
+ readonly candidateOptions?: CandidateOptions | undefined;
61
+ /** Flake score for this test, when history is available. */
62
+ readonly flakeScore?: number | undefined;
63
+ readonly flakeThreshold?: number | undefined;
64
+ /** Skip the re-run — for `--dry-run`, which must never claim validation. */
65
+ readonly skipValidation?: boolean | undefined;
66
+ readonly timeoutMs?: number | undefined;
67
+ /**
68
+ * Optional Tier-1 ranker. Given the same verified candidates, it may reorder
69
+ * them by intent — and nothing else. Its choice is still validated by a
70
+ * re-run, so a wrong answer costs a rejected proposal, never a bad patch.
71
+ */
72
+ readonly rankCandidates?: ((input: {
73
+ readonly candidates: readonly HealCandidate[];
74
+ readonly missingTestId: string;
75
+ }) => Promise<TierOneRecord & {
76
+ readonly chosen: string | null;
77
+ readonly realBug: boolean;
78
+ }>) | undefined;
79
+ }
80
+ export declare const DEFAULT_HEAL_OPTIONS: {
81
+ validationRuns: number;
82
+ checkCollateral: boolean;
83
+ flakeThreshold: number;
84
+ };
85
+ /**
86
+ * Run the heal pipeline against one evidence bundle.
87
+ *
88
+ * Order is load-bearing: `NEVER_HEAL` kinds and known-flaky tests are refused
89
+ * before any candidate is generated. Playwright, not the ranking score,
90
+ * decides whether a patch is proposable.
91
+ *
92
+ * @param bundle - Captured failure (selector, ARIA, test-id index).
93
+ * @param options - Working directory, spec to re-run, and validation policy.
94
+ * @returns A proposal whose `status` is `proposed`, `rejected`, or one of the
95
+ * `refused-*` / `no-*` outcomes. Never throws on a refused heal.
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const proposal = await proposeHeal(bundle, {
100
+ * cwd: process.cwd(),
101
+ * specFile: 'tests/gyms.spec.ts',
102
+ * validationRuns: 3,
103
+ * checkCollateral: true,
104
+ * });
105
+ * if (proposal.status === 'proposed') {
106
+ * console.log(proposal.chosen?.value);
107
+ * }
108
+ * ```
109
+ */
110
+ export declare function proposeHeal(bundle: EvidenceBundle, options: ProposeOptions): Promise<HealProposal>;
111
+ //# sourceMappingURL=propose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propose.d.ts","sourceRoot":"","sources":["../src/propose.ts"],"names":[],"mappings":"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"}