Files
VianoBugAnalysis/src/viano_bug_analysis/analysis/tool_guidance.py
T

63 lines
3.9 KiB
Python

"""Versioned tool shapes and value-free repair instructions, no query rewriting."""
import copy
import re
HEADER = '\nTool argument contracts v1 (plain JSON output):\n'
FILTER_REQUIREMENT = ('filters must be a nonempty array of objects: '
'[{"column":"observed_column","operator":"eq","value":123}]; '
'use verified values, never a flat list.')
def finding_repair_instructions(context):
detail=context.get('previous_rejection_details',{})
if (not context.get('bootstrap_json_framing') or detail.get('expected_types')!=['object']
or not re.fullmatch(r'/update/working_brief/findings/[0-9]+',detail.get('validation_path',''))):
return ''
return ('\nFinding type repair: each findings element must be a JSON OBJECT, not a string, '
'array, tuple or encoded record. Shape example: '
'{"id":"B0001","direction":"neutral","text":"Scoped observation",'
'"scope":"Examined snapshot","evidence_refs":["E000001"]}. '
'These are illustrative values: preserve existing B IDs and use only actual visible E IDs '
'and evidence-grounded text/scope. Do not copy invented evidence or erase findings to bypass validation. '
'No $record/$table encoding in output. Repair the response without repeating research.\n')
def filter_feedback(details):
result = copy.deepcopy(details)
if re.fullmatch(r'/next/arguments/filters(?:/[0-9]+)?', result.get('validation_path', '')):
result['requirement'] = FILTER_REQUIREMENT
return result
def instructions(context):
if context.get('tool_guidance_version') != 1:
return ''
from .investigation_compact import preparing_review
if preparing_review(context) or context.get('lean_session', {}).get('checkpoint'):
return ''
bindings = context['tool_schema'].get('x-project-bindings', {})
text = (HEADER + 'Use operation="project.query" for these arguments. Prefer a matching larger tool '
'before separate schema/search/read calls; primitive tools remain available.\n')
if bindings.get('relation_profiles'):
text += ('database.relations arguments: {"kind":"database.relations",'
'"profile":"NAME_FROM_relation_profiles","value":123}. '
'Select the profile from the catalog and replace 123 with the observed start key '
'(integer for numeric IDs, string for textual keys). No table or SQL argument.\n')
if bindings.get('source_analysis'):
text += ('source.context arguments: {"kind":"source.context",'
'"text":"SYMBOL_OR_IDENTIFIER_FROM_EVIDENCE","path_prefix":""}. '
'Replace text with the actual term; empty path_prefix means ')
text += ('the configured scope prefixes. ' if bindings['source_analysis'].get('version')==2 else 'repository root. ')
text += 'No registration, offset, limit or command argument.\n'
if bindings['source_analysis'].get('version')==2:
text += ('source.context v2 optionally accepts entity_refs:["N0001"] from supplied case entities '
'and scope:"code"|"schema"|"historical_sql" (default code). Grounded names and scripts '
'are added by the controller; numeric coincidences are weak summaries, not identities. '
'Ranking is heuristic; supporting and other-location candidates may matter.\n')
text += ('For primitive database.rows/database.count, filters MUST be a nonempty array of objects, '
'for example [{"column":"observed_column","operator":"eq","value":123}]. '
'Replace the illustrative column/value with schema-verified, case-grounded inputs. '
'Never emit [] or flatten an object into ["column",...,"operator",...,"value",...]. '
'Input compression encodings do not apply to output: return ordinary JSON objects.\n')
return text