Setting the file. One moment.
Expectations Ledger · Wix Replatform · wix/skills · Skills Docs
ContentsBack to the top of the page Bundled file
lib/ expectations-ledger.js
JavaScript · 96 lines · 3 KB
surprising question, a mapping you expected, or an outcome that did not match your
12 intent. Write freely anywhere in this document.
13
14 You can also tell the agent: \` add to expectations: <your note> \` . It will preserve
15 your note and add safe references to the current migration context. Do not put passwords,
16 tokens, customer data, or other secrets here.
17 ` ;
18
19 function ledgerPath ( projectDir ) {
20 return path. join (path. resolve (projectDir), LEDGER_FILE );
21 }
22
23 async function ensureLedger ( projectDir ) {
24 const file = ledgerPath (projectDir);
25 await fs. mkdir (path. dirname (file), { recursive: true });
26 try {
27 await fs. writeFile (file, STARTER , { encoding: 'utf8' , flag: 'wx' });
28 return { file, created: true };
29 } catch (error) {
30 if (error.code === 'EEXIST' ) return { file, created: false };
31 throw error;
32 }
33 }
34
35 function redactSensitiveText ( value ) {
36 return String (value || '' )
37 . replace ( / \b (bearer) \s + [A-Za-z0-9._~+/=-] {8,} / gi , '$1 [REDACTED]' )
38 . replace ( / \b (password | passwd | secret | token | api [-_ ] ? key | authorization | cookie) \b \s * ( [:=] ) \s * [ ^ \s,;] + / gi , '$1$2[REDACTED]' );
39 }
40
41 function safeArtifactRef ( projectDir , ref ) {
42 if ( ! ref) return null ;
43 const root = path. resolve (projectDir);
44 const candidate = path. resolve (root, ref);
45 const relative = path. relative (root, candidate);
46 if (relative. startsWith ( '..' ) || path. isAbsolute (relative)) return null ;
47 return relative || '.' ;
48 }
49
50 function nextId ( contents ) {
51 const ids = [ ... contents. matchAll ( / ^ ## \s + E-( \d + ) \b / gm )]. map (( match ) => Number (match[ 1 ]));
52 return `E-${ String (( ids . length ? Math . max ( ... ids ) : 0 ) + 1 ). padStart ( 3 , '0' ) }` ;
53 }
54
55 function formatContext ( projectDir , context = {}) {
56 const lines = [];
57 for ( const [ label , value ] of [
58 [ 'Stage' , context.phase],
59 [ 'State' , context.state],
60 [ 'Active resource' , context.resource],
61 [ 'Source' , context.source],
62 [ 'Target' , context.target],
63 [ 'Decision or approval' , context.decision],
64 ]) {
65 if (value) lines. push ( `- ${ label }: ${ redactSensitiveText ( value ) }` );
66 }
67 for ( const ref of context.artifactRefs || []) {
68 const safeRef = safeArtifactRef (projectDir, ref);
69 if (safeRef) lines. push ( `- Artifact: \` ${ safeRef } \` ` );
70 }
71 return lines. length ? lines. join ( ' \n ' ) : '- No reliable run context was available when this was captured.' ;
72 }
73
74 async function appendExpectation ( projectDir , expectation , context = {}, now = new Date ()) {
75 if ( ! String (expectation || '' ). trim ()) throw new Error ( 'expectation is required' );
76 const { file } = await ensureLedger (projectDir);
77 const contents = await fs. readFile (file, 'utf8' );
78 const id = nextId (contents);
79 const original = String (expectation). trim ();
80 const note = redactSensitiveText (original). trim ();
81 const heading = note === original ? 'User expectation (verbatim)' : 'User expectation (sensitive values redacted)' ;
82 const entry = ` \n ## ${ id } — ${ now . toISOString () } \n\n ### ${ heading } \n\n ${ note } \n\n ### Context captured by the agent \n\n ${ formatContext ( projectDir , context ) } \n ` ;
83 await fs. appendFile (file, entry, 'utf8' );
84 return { file, id, redacted: note !== original };
85 }
86
87 module . exports = {
88 LEDGER_FILE,
89 STARTER,
90 ledgerPath,
91 ensureLedger,
92 redactSensitiveText,
93 safeArtifactRef,
94 nextId,
95 appendExpectation,
96 };