Skip to content

Commit 379e55c

Browse files
committed
Track D: structure Pine revision field summaries
1 parent c04324a commit 379e55c

File tree

9 files changed

+156
-5
lines changed

9 files changed

+156
-5
lines changed

docs/CHAT_CONTINUITY_IMPLEMENTATION_PLAN.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ The findings below are grounded in current repo seams, especially:
13391339

13401340
### Track A — Intent-safe reliability rewrites
13411341

1342-
**Status:** First slice completed in working tree
1342+
**Status:** Second slice completed in working tree
13431343

13441344
**Delivered so far**
13451345
- hardened `extractRequestedAppName(...)` in `src/main/ai-service.js` so passive open-state phrasing no longer gets treated as app-launch intent
@@ -1679,6 +1679,7 @@ This is the next Pine-facing implementation slice after the current Logs / Profi
16791679
**Delivered so far**
16801680
- extended `src/main/tradingview/pine-workflows.js` with a `provenance-summary` evidence mode for `pine-version-history`
16811681
- Version History metadata requests such as `summarize the top visible revision metadata` now preserve or auto-append bounded `get_text` provenance-summary readback
1682+
- `get_text` provenance-summary results now attach deterministic visible revision metadata such as latest visible revision label, latest visible relative time, visible revision count, and visible recency signal
16821683
- extended prompt/seam/execution coverage in:
16831684
- `src/main/ai-service/message-builder.js`
16841685
- `scripts/test-tradingview-pine-data-workflows.js`

scripts/test-bug-fixes.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ test('pine workflow encodes diagnostics and compile-result evidence modes', () =
251251
assert(pineWorkflowContent.includes('diagnostics and warnings text'), 'Pine workflows should use diagnostics-specific readback wording');
252252
assert(pineWorkflowContent.includes('provenance-summary'), 'Pine workflows should support version-history provenance-summary evidence mode');
253253
assert(pineWorkflowContent.includes('top visible Pine Version History revision metadata'), 'Pine workflows should use provenance-summary-specific readback wording');
254+
assert(pineWorkflowContent.includes('pineSummaryFields'), 'Pine workflows should carry explicit structured summary fields for provenance summaries');
254255
});
255256

256257
test('system prompt includes Pine diagnostics guidance', () => {
@@ -262,6 +263,7 @@ test('system prompt includes Pine diagnostics guidance', () => {
262263
assert(systemPromptContent.includes('TradingView Pine diagnostics rule'), 'System prompt should include Pine diagnostics guidance');
263264
assert(systemPromptContent.includes('visible revision/provenance details'), 'System prompt should steer Pine provenance requests toward verified Version History text');
264265
assert(systemPromptContent.includes('treat visible Pine Version History entries as bounded audit/provenance evidence only'), 'Pine provenance guidance should prevent overclaiming from visible revision history');
266+
assert(systemPromptContent.includes('latest visible revision label'), 'Pine provenance guidance should mention structured visible revision fields');
265267
assert(systemPromptContent.includes('compile success'), 'System prompt should mention compile success bounds');
266268
assert(systemPromptContent.includes('realtime rollback'), 'System prompt should mention Pine execution-model caveats');
267269
});

scripts/test-pine-diagnostics-bounds.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ async function main() {
7070
assert(evidenceMessage, 'pine evidence block should be injected');
7171
assert(evidenceMessage.content.includes('requestKind: provenance-summary'));
7272
assert(evidenceMessage.content.includes('Treat Pine Version History as bounded provenance evidence only'));
73+
assert(evidenceMessage.content.includes('latest visible revision label, latest visible relative time, visible revision count, and visible recency signal'));
7374
assert(evidenceMessage.content.includes('Do not infer hidden diffs, full script history, authorship, or runtime/chart behavior from the visible revision list alone.'));
7475
});
7576
}

scripts/test-tradingview-pine-data-workflows.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,13 @@ test('open pine version history and summarize visible revision metadata stays ve
262262
assert.strictEqual(rewritten[4].type, 'get_text');
263263
assert.strictEqual(rewritten[4].text, 'Pine Version History');
264264
assert.strictEqual(rewritten[4].pineEvidenceMode, 'provenance-summary');
265+
assert.deepStrictEqual(rewritten[4].pineSummaryFields, [
266+
'latest-revision-label',
267+
'latest-relative-time',
268+
'visible-revision-count',
269+
'visible-recency-signal',
270+
'top-visible-revisions'
271+
]);
265272
assert(/top visible Pine Version History revision metadata/i.test(rewritten[4].reason), 'version-history metadata readback should use provenance-summary wording');
266273
});
267274

@@ -338,6 +345,13 @@ test('pine version history metadata workflow preserves trailing get_text read st
338345
assert.strictEqual(readSteps.length, 1, 'explicit version-history metadata readback step should be preserved without duplication');
339346
assert.strictEqual(readSteps[0].text, 'Pine Version History');
340347
assert.strictEqual(readSteps[0].pineEvidenceMode, 'provenance-summary');
348+
assert.deepStrictEqual(readSteps[0].pineSummaryFields, [
349+
'latest-revision-label',
350+
'latest-relative-time',
351+
'visible-revision-count',
352+
'visible-recency-signal',
353+
'top-visible-revisions'
354+
]);
341355
assert.strictEqual(rewritten[2].verify.target, 'pine-version-history');
342356
});
343357

scripts/test-windows-observation-flow.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,14 @@ async function run() {
578578
assert.deepStrictEqual(evidenceModes, ['provenance-summary'], 'Version History metadata workflow should preserve provenance-summary evidence mode');
579579
assert.strictEqual(execResult.observationCheckpoints[0].verified, true, 'Pine Version History panel observation should pass');
580580
assert.strictEqual(execResult.results[2].text, 'Revision 18 saved 2m ago; Revision 17 saved 18m ago; showing 2 visible revisions', 'Version History metadata text should be preserved on the get_text result');
581+
assert.strictEqual(execResult.results[2].pineStructuredSummary.latestVisibleRevisionLabel, 'Revision 18', 'Version History metadata summary should expose the latest visible revision label');
582+
assert.strictEqual(execResult.results[2].pineStructuredSummary.latestVisibleRelativeTime, '2m ago', 'Version History metadata summary should expose the latest visible relative time');
583+
assert.strictEqual(execResult.results[2].pineStructuredSummary.visibleRevisionCount, 2, 'Version History metadata summary should expose the visible revision count');
584+
assert.strictEqual(execResult.results[2].pineStructuredSummary.visibleRecencySignal, 'recent-churn-visible', 'Version History metadata summary should expose a bounded visible recency signal');
585+
assert.deepStrictEqual(execResult.results[2].pineStructuredSummary.topVisibleRevisions, [
586+
{ label: 'Revision 18', relativeTime: '2m ago', revisionNumber: 18 },
587+
{ label: 'Revision 17', relativeTime: '18m ago', revisionNumber: 17 }
588+
], 'Version History metadata summary should expose compact top visible revisions');
581589
assert(!execResult.screenshotCaptured, 'Pine Version History metadata gathering should not require a screenshot loop');
582590
});
583591
});

src/main/ai-service/message-builder.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ function buildPineEvidenceConstraint({ foreground, userMessage }) {
8585

8686
if (requestKind === 'provenance-summary') {
8787
lines.push('- Rule: Treat Pine Version History as bounded provenance evidence only; summarize only the top visible revision labels, relative times, and other metadata that are directly visible.');
88+
lines.push('- Rule: When possible, structure the summary into compact visible fields such as latest visible revision label, latest visible relative time, visible revision count, and visible recency signal.');
8889
lines.push('- Rule: Do not infer hidden diffs, full script history, authorship, or runtime/chart behavior from the visible revision list alone.');
8990
}
9091

src/main/ai-service/system-prompt.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ When the user asks you to DO something, respond with a JSON action block:
229229
- **Continuity rule**: if the active page title or recent action output indicates the requested browser objective is already achieved, acknowledge completion and avoid proposing additional screenshot steps.
230230
- **TradingView Pine evidence rule**: if the user wants concrete Pine output, errors, profiler-style evidence, visible Pine Editor status/output, or visible revision/provenance details, prefer \`open/show Pine Editor, Logs, Profiler, or Version History\` + verified panel opening + \`get_text\` before relying on screenshot analysis.
231231
- **TradingView Pine diagnostics rule**: treat visible Pine Editor compile results, compiler errors, warnings, and diagnostics as bounded text evidence. Do not turn \`no errors\` into claims about runtime correctness, market validity, or trading edge.
232-
- **TradingView Pine provenance rule**: treat visible Pine Version History entries as bounded audit/provenance evidence only. Summarize top visible revision labels, relative times, and other directly visible metadata, but do not infer hidden diffs, full script history, authorship, or runtime/chart behavior from the visible list alone.
232+
- **TradingView Pine provenance rule**: treat visible Pine Version History entries as bounded audit/provenance evidence only. Summarize top visible revision labels, latest visible revision label, latest visible relative time, visible revision count, visible recency signal, and other directly visible metadata, but do not infer hidden diffs, full script history, authorship, or runtime/chart behavior from the visible list alone.
233233
- **TradingView Pine line-budget rule**: Pine scripts are limited to 500 lines. Do not propose pasting or generating Pine scripts longer than 500 lines; prefer bounded edits, read visible line/status hints first when needed, and mention the limit explicitly when it affects read/write guidance.
234234
- **If you need to interact with web content inside an app** (like VS Code panels, browser tabs): Use keyboard shortcuts or coordinate-based clicks since web UI may not appear in UIA tree
235235

src/main/system-automation.js

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,105 @@ function executePowerShell(command) {
312312
});
313313
}
314314

315+
function normalizeCompactText(value, maxLength = 240) {
316+
return String(value || '').replace(/\s+/g, ' ').trim().slice(0, maxLength) || null;
317+
}
318+
319+
function parseRelativeTimeToMinutes(value) {
320+
const text = normalizeCompactText(value, 80);
321+
if (!text) return null;
322+
const match = text.match(/(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|wk|wks|week|weeks)\s+ago/i);
323+
if (!match) return null;
324+
325+
const amount = Number(match[1]);
326+
const unit = match[2].toLowerCase();
327+
if (!Number.isFinite(amount)) return null;
328+
329+
if (unit.startsWith('s')) return Math.max(1, amount / 60);
330+
if (unit.startsWith('m')) return amount;
331+
if (unit.startsWith('h')) return amount * 60;
332+
if (unit.startsWith('d')) return amount * 60 * 24;
333+
if (unit.startsWith('w')) return amount * 60 * 24 * 7;
334+
return null;
335+
}
336+
337+
function inferVisibleRevisionRecencySignal(minutes) {
338+
if (!Number.isFinite(minutes)) return 'unknown-visible-recency';
339+
if (minutes <= 60) return 'recent-churn-visible';
340+
if (minutes <= 1440) return 'same-day-visible';
341+
if (minutes >= 10080) return 'stable-visible';
342+
return 'moderate-visible';
343+
}
344+
345+
function buildPineVersionHistoryStructuredSummary(text, summaryFields = []) {
346+
const rawText = normalizeCompactText(text, 2000);
347+
if (!rawText) return null;
348+
349+
const revisionSegments = rawText
350+
.split(/[;\n]+/)
351+
.map((segment) => normalizeCompactText(segment, 280))
352+
.filter(Boolean);
353+
354+
const visibleRevisions = revisionSegments
355+
.map((segment) => {
356+
const match = segment.match(/^(Revision\s+#?\s*\d+)\b(?:.*?\b(?:saved|updated|created)\s+(.+?ago))?$/i);
357+
if (!match) return null;
358+
359+
const label = normalizeCompactText(match[1], 80);
360+
const relativeTime = normalizeCompactText(match[2], 80);
361+
const revisionNumberMatch = label ? label.match(/(\d+)/) : null;
362+
const revisionNumber = revisionNumberMatch ? Number(revisionNumberMatch[1]) : null;
363+
364+
return {
365+
label,
366+
revisionNumber: Number.isFinite(revisionNumber) ? revisionNumber : null,
367+
relativeTime,
368+
recencyMinutes: parseRelativeTimeToMinutes(relativeTime)
369+
};
370+
})
371+
.filter(Boolean)
372+
.slice(0, 5);
373+
374+
const visibleCountMatch = rawText.match(/showing\s+(\d+)\s+visible\s+revisions?/i);
375+
const visibleRevisionCount = visibleCountMatch
376+
? Number(visibleCountMatch[1])
377+
: visibleRevisions.length;
378+
379+
const latestVisibleRevision = visibleRevisions[0] || null;
380+
const compactSummary = [
381+
latestVisibleRevision?.label ? `latest=${latestVisibleRevision.label}` : null,
382+
latestVisibleRevision?.relativeTime ? `saved=${latestVisibleRevision.relativeTime}` : null,
383+
Number.isFinite(visibleRevisionCount) ? `visible=${visibleRevisionCount}` : null,
384+
latestVisibleRevision ? `signal=${inferVisibleRevisionRecencySignal(latestVisibleRevision.recencyMinutes)}` : null
385+
].filter(Boolean).join(' | ');
386+
387+
const fullSummary = {
388+
latestVisibleRevisionLabel: latestVisibleRevision?.label || null,
389+
latestVisibleRevisionNumber: Number.isFinite(latestVisibleRevision?.revisionNumber) ? latestVisibleRevision.revisionNumber : null,
390+
latestVisibleRelativeTime: latestVisibleRevision?.relativeTime || null,
391+
visibleRevisionCount: Number.isFinite(visibleRevisionCount) ? visibleRevisionCount : null,
392+
visibleRecencySignal: latestVisibleRevision ? inferVisibleRevisionRecencySignal(latestVisibleRevision.recencyMinutes) : 'unknown-visible-recency',
393+
topVisibleRevisions: visibleRevisions.map((entry) => ({
394+
label: entry.label,
395+
relativeTime: entry.relativeTime,
396+
revisionNumber: entry.revisionNumber
397+
})),
398+
compactSummary: compactSummary || null
399+
};
400+
401+
if (!Array.isArray(summaryFields) || summaryFields.length === 0) {
402+
return fullSummary;
403+
}
404+
405+
const structured = { compactSummary: fullSummary.compactSummary };
406+
if (summaryFields.includes('latest-revision-label')) structured.latestVisibleRevisionLabel = fullSummary.latestVisibleRevisionLabel;
407+
if (summaryFields.includes('latest-relative-time')) structured.latestVisibleRelativeTime = fullSummary.latestVisibleRelativeTime;
408+
if (summaryFields.includes('visible-revision-count')) structured.visibleRevisionCount = fullSummary.visibleRevisionCount;
409+
if (summaryFields.includes('visible-recency-signal')) structured.visibleRecencySignal = fullSummary.visibleRecencySignal;
410+
if (summaryFields.includes('top-visible-revisions')) structured.topVisibleRevisions = fullSummary.topVisibleRevisions;
411+
return structured;
412+
}
413+
315414
/**
316415
* Focus the desktop / unfocus Electron windows before sending keyboard input
317416
* This is critical for SendKeys/SendInput to reach the correct target
@@ -2388,8 +2487,13 @@ async function executeAction(action) {
23882487
action.criteria || { text: action.text, automationId: action.automationId, controlType: action.controlType }
23892488
);
23902489
result = { ...result, ...gtResult };
2490+
if (gtResult.success
2491+
&& action?.pineEvidenceMode === 'provenance-summary'
2492+
&& /pine version history/i.test(String(action?.text || action?.criteria?.text || ''))) {
2493+
result.pineStructuredSummary = buildPineVersionHistoryStructuredSummary(gtResult.text, action.pineSummaryFields);
2494+
}
23912495
result.message = gtResult.success
2392-
? `Got text via ${gtResult.method}: "${(gtResult.text || '').slice(0, 50)}"`
2496+
? `Got text via ${gtResult.method}: "${(gtResult.text || '').slice(0, 50)}"${result.pineStructuredSummary?.compactSummary ? ` [${result.pineStructuredSummary.compactSummary}]` : ''}`
23932497
: `Get text failed: ${gtResult.error}`;
23942498
break;
23952499
}

src/main/tradingview/pine-workflows.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ function mergeUnique(values = []) {
1515
.filter(Boolean)));
1616
}
1717

18+
const PINE_VERSION_HISTORY_SUMMARY_FIELDS = Object.freeze([
19+
'latest-revision-label',
20+
'latest-relative-time',
21+
'visible-revision-count',
22+
'visible-recency-signal',
23+
'top-visible-revisions'
24+
]);
25+
1826
function inferPineEvidenceReadIntent(raw = '', surfaceTarget = '') {
1927
const normalized = normalizeTextForMatch(raw);
2028
if (!normalized) return false;
@@ -109,14 +117,18 @@ function buildPineReadbackStep(surfaceTarget, evidenceMode = null) {
109117

110118
if (surfaceTarget === 'pine-version-history') {
111119
const mode = evidenceMode || 'generic-provenance';
112-
return {
120+
const step = {
113121
type: 'get_text',
114122
text: 'Pine Version History',
115123
reason: mode === 'provenance-summary'
116-
? 'Read top visible Pine Version History revision metadata for a bounded provenance summary'
124+
? 'Read top visible Pine Version History revision metadata for a bounded structured provenance summary'
117125
: 'Read visible Pine Version History entries for bounded provenance gathering',
118126
pineEvidenceMode: mode
119127
};
128+
if (mode === 'provenance-summary') {
129+
step.pineSummaryFields = [...PINE_VERSION_HISTORY_SUMMARY_FIELDS];
130+
}
131+
return step;
120132
}
121133

122134
return null;
@@ -243,6 +255,14 @@ function buildTradingViewPineWorkflowActions(intent = {}, actions = []) {
243255
const trailing = actions.slice(intent.openerIndex + 1)
244256
.filter((action) => action && typeof action === 'object' && action.type !== 'screenshot');
245257

258+
if (intent.surfaceTarget === 'pine-version-history' && intent.pineEvidenceMode === 'provenance-summary') {
259+
trailing.forEach((action) => {
260+
if (action?.type === 'get_text' && !Array.isArray(action.pineSummaryFields)) {
261+
action.pineSummaryFields = [...PINE_VERSION_HISTORY_SUMMARY_FIELDS];
262+
}
263+
});
264+
}
265+
246266
const hasExplicitReadbackStep = trailing.some((action) => action?.type === 'get_text' || action?.type === 'find_element');
247267

248268
if (intent.wantsEvidenceReadback && !hasExplicitReadbackStep) {

0 commit comments

Comments
 (0)