Ground handoff map values through explicit canonical fact bindings
This commit is contained in:
1 parent
061f82e6f1
commit
c6f4ea9877
10 files changed
+257
-5
No files matched your search
@@ -120,7 +120,11 @@ class ChangeRequestBuilder:
|
||||
if hashlib.sha256(raw).hexdigest()!=entry['record']['content_sha256']:
|
||||
raise ChangeRequestError('CHANGE_REQUEST_LOCAL_CONTEXT_CHANGED')
|
||||
local[ref]={'kind':next(iter(kinds)),'source_ref':ref,'sha256':hashlib.sha256(raw).hexdigest(),'size':len(raw)}
|
||||
if parameter_grounding:result['parameter_grounding']=1
|
||||
if parameter_grounding:
|
||||
result['parameter_grounding']=parameter_grounding
|
||||
if parameter_grounding == 2:
|
||||
from viano_bug_analysis.profiles.database_bindings import fact_bindings
|
||||
result['database_fact_bindings'] = fact_bindings(result['case'])
|
||||
validate(result, "change-request-context")
|
||||
if len(json_bytes(result)) > self.limits.max_context_bytes:
|
||||
raise ChangeRequestError("CHANGE_REQUEST_CONTEXT_LIMIT")
|
||||
@@ -153,7 +157,7 @@ class ChangeRequestBuilder:
|
||||
policy = old["max_invalid_responses"]
|
||||
if self.config_explicit and policy != self.config.max_invalid_responses:
|
||||
raise ChangeRequestError("CHANGE_REQUEST_POLICY_CHANGED")
|
||||
context = self.context(reader, policy, protocol=old.get("handoff_protocol",1) if started else 2, parameter_grounding=old.get("parameter_grounding",0) if started else 1)
|
||||
context = self.context(reader, policy, protocol=old.get("handoff_protocol",1) if started else 2, parameter_grounding=old.get("parameter_grounding",0) if started else 2)
|
||||
if started and (old != context or started["details"]["context_sha256"] != digest(context)):
|
||||
raise ChangeRequestError("CHANGE_REQUEST_CONTEXT_CHANGED")
|
||||
if completed:
|
||||
|
||||
@@ -46,6 +46,11 @@ def compact_handoff_context(context):
|
||||
view['case'] = {k: context['case'][k] for k in
|
||||
('bug', 'human_observations', 'expected_behavior', 'data_quality')
|
||||
if k in context['case']}
|
||||
if context.get('parameter_grounding') == 2:
|
||||
# Show only actual bound values, not the full runtime fact inventory.
|
||||
view['database_fact_bindings'] = context['database_fact_bindings']
|
||||
ids = {b['fact_id'] for b in context['database_fact_bindings']}
|
||||
view['bound_case_facts'] = [f for f in context['case'].get('runtime_facts', {}).get('facts', []) if f.get('id') in ids]
|
||||
view['evidence'] = {eid: {'content': entry['content'],
|
||||
'content_ref': entry['record']['content_ref'],
|
||||
'content_sha256': entry['record']['content_sha256']}
|
||||
|
||||
@@ -5,13 +5,15 @@ It never substitutes values or modifies model instructions.
|
||||
"""
|
||||
import re
|
||||
from decimal import Decimal
|
||||
from math import isfinite
|
||||
|
||||
|
||||
def unsupported_assignments(payload,context):
|
||||
observed={}
|
||||
table_columns={}
|
||||
def remember(column,value):
|
||||
if (isinstance(column,str) and re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]{0,127}',column)
|
||||
and type(value) in (int,float)):
|
||||
and type(value) in (int,float) and (type(value) is int or isfinite(value))):
|
||||
observed.setdefault(column.lower(),set()).add(Decimal(str(value)))
|
||||
for entry in context.get('evidence',{}).values():
|
||||
for capture in entry['content'].get('items',[]):
|
||||
@@ -19,6 +21,21 @@ def unsupported_assignments(payload,context):
|
||||
query=capture.get('query');result=capture.get('result')
|
||||
if not isinstance(query,dict) or not isinstance(result,dict):continue
|
||||
kind=query.get('kind')
|
||||
if context.get('parameter_grounding') == 2 and kind in {'database.rows','database.schema','database.relations'}:
|
||||
for item in result.get('items', []):
|
||||
if not isinstance(item, dict): continue
|
||||
table = item.get('table') or query.get('table')
|
||||
if not isinstance(table, str): continue
|
||||
columns = table_columns.setdefault(table, set())
|
||||
for col in item.get('columns', []) or []:
|
||||
if (isinstance(col, dict) and isinstance(col.get('COLUMN_NAME'), str)
|
||||
and isinstance(col.get('COLUMN_TYPE'), str)
|
||||
and re.match(r'(?:tinyint|smallint|mediumint|int|bigint|decimal|numeric|float|double)\b', col.get('COLUMN_TYPE', ''), re.I)):
|
||||
columns.add(col['COLUMN_NAME'])
|
||||
rows = item.get('rows', []) if kind == 'database.relations' else [item.get('row', {})]
|
||||
for row in rows:
|
||||
if isinstance(row, dict):
|
||||
columns.update(k for k, v in row.items() if type(v) in (int, float))
|
||||
if kind=='database.relations':
|
||||
for node in result.get('items',[]):
|
||||
for row in node.get('rows',[]):
|
||||
@@ -44,6 +61,27 @@ def unsupported_assignments(payload,context):
|
||||
elif isinstance(v,list):
|
||||
for item in v:case_values(item)
|
||||
case_values(context.get('case',{}))
|
||||
if context.get('parameter_grounding') == 2:
|
||||
facts = context.get('case', {}).get('runtime_facts', {}).get('facts', [])
|
||||
counts = {}
|
||||
for fact in facts:
|
||||
fid = fact.get('id')
|
||||
if isinstance(fid, str): counts[fid] = counts.get(fid, 0) + 1
|
||||
unique = {f['id']: f for f in facts if counts.get(f.get('id')) == 1}
|
||||
for fact in unique.values():
|
||||
# A named canonical fact is not a free-text model claim.
|
||||
name = fact.get('name')
|
||||
if isinstance(name, str) and name.lower() in observed:
|
||||
remember(name, fact.get('value'))
|
||||
from viano_bug_analysis.profiles.database_bindings import fact_bindings
|
||||
# Re-derive adapter bindings; arbitrary descriptors cannot authorize values.
|
||||
allowed = fact_bindings(context.get('case', {}))
|
||||
for binding in context.get('database_fact_bindings', []):
|
||||
if binding not in allowed: continue
|
||||
fact = unique.get(binding['fact_id'])
|
||||
if (fact and fact.get('name') == binding['fact_name']
|
||||
and binding['column'] in table_columns.get(binding['table'], set())):
|
||||
remember(binding['column'], fact.get('value'))
|
||||
bad=[]
|
||||
for text in payload.get('implementation_guidance',[]):
|
||||
if not isinstance(text,str):continue
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
"""Explicit adapter-owned fact/column bindings; no inferred field aliases."""
|
||||
|
||||
|
||||
def fact_bindings(case):
|
||||
project = case.get('case', {}).get('project', {})
|
||||
if project.get('type') == 'wow' and project.get('profile') == 'vcore':
|
||||
from .vcore.database_bindings import reproduction_bindings_v1
|
||||
return reproduction_bindings_v1(case)
|
||||
return []
|
||||
@@ -0,0 +1,12 @@
|
||||
"""wowdiag reproduction location to Vcore creature schema, version 1."""
|
||||
|
||||
|
||||
def reproduction_bindings_v1(case):
|
||||
# Intentionally narrow: no suffix matching, coordinates, masks or entity IDs.
|
||||
return [{'fact_id': fact['id'], 'fact_name': 'map_id',
|
||||
'table': 'creature', 'column': 'map'}
|
||||
for fact in case.get('runtime_facts', {}).get('facts', [])
|
||||
if fact.get('name') == 'map_id'
|
||||
and isinstance(fact.get('id'), str)
|
||||
and fact.get('provenance', {}).get('kind') == 'reproduction_record'
|
||||
and type(fact.get('value')) is int and fact['value'] >= 0]
|
||||
@@ -77,7 +77,73 @@
|
||||
"const": 2
|
||||
},
|
||||
"parameter_grounding": {
|
||||
"const": 1
|
||||
"type": "integer",
|
||||
"enum": [
|
||||
1,
|
||||
2
|
||||
]
|
||||
},
|
||||
"database_fact_bindings": {
|
||||
"type": "array",
|
||||
"maxItems": 10000,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"fact_id",
|
||||
"fact_name",
|
||||
"table",
|
||||
"column"
|
||||
],
|
||||
"properties": {
|
||||
"fact_id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"fact_name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"table": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
},
|
||||
"column": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 200
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {
|
||||
"required": [
|
||||
"parameter_grounding"
|
||||
],
|
||||
"properties": {
|
||||
"parameter_grounding": {
|
||||
"const": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"required": [
|
||||
"database_fact_bindings"
|
||||
]
|
||||
},
|
||||
"else": {
|
||||
"not": {
|
||||
"required": [
|
||||
"database_fact_bindings"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user