refactor(evidence): unify formulas with typed evidence
This commit is contained in:
@@ -348,6 +348,14 @@ Prerequisite: Phase 3 closed.
|
||||
"<concept>"` (or derive it from the evidence/context), present it, and let the
|
||||
reviewer approve/reject (`concept_formula_approved`/`concept_formula_rejected`).
|
||||
Reflect the approved formula in `schema_linking.json` (`concept_formulas`).
|
||||
4. **Formula proposals.** A `kind=formula` result from Evidence search is Published
|
||||
Evidence and can be cited with its provenance. If no published formula is suitable and
|
||||
you synthesize one for this question, present it to the reviewer and, after their F4
|
||||
decision, include `{concept, columns, sql, sources}` in `concept_formulas`. This creates
|
||||
a schema-versioned, **session-only Formula proposal**: it helps this session but is not
|
||||
Published Evidence, has no `evidence:` ID, is not returned by runtime search, and never
|
||||
writes to the workspace repository. A curator must separately import, review, and
|
||||
publish it before another session can treat it as Evidence.
|
||||
5. Persist the **joins** (and any `concept_formulas`/`open_questions`) with the gate's
|
||||
`write_schema_linking` tool — it validates the object against the `SchemaLinking`
|
||||
model and writes the file deterministically (never hand-write it, never edit it
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
4. **Formula proposals.** A `kind=formula` result from Evidence search is Published
|
||||
Evidence and can be cited with its provenance. If no published formula is suitable and
|
||||
you synthesize one for this question, present it to the reviewer and, after their F4
|
||||
decision, include `{concept, columns, sql, sources}` in `concept_formulas`. This creates
|
||||
a schema-versioned, **session-only Formula proposal**: it helps this session but is not
|
||||
Published Evidence, has no `evidence:` ID, is not returned by runtime search, and never
|
||||
writes to the workspace repository. A curator must separately import, review, and
|
||||
publish it before another session can treat it as Evidence.
|
||||
@@ -216,6 +216,7 @@ Prerequisite: Phase 3 closed.
|
||||
those to joins you derive yourself, and flag to the reviewer any join you need
|
||||
that is NOT in the list.
|
||||
{{DISAMBIGUATION_SCHEMA_GROUNDING}}
|
||||
{{EVIDENCE_FORMULA_PROPOSALS}}
|
||||
5. Persist the **joins** (and any `concept_formulas`/`open_questions`) with the gate's
|
||||
`write_schema_linking` tool — it validates the object against the `SchemaLinking`
|
||||
model and writes the file deterministically (never hand-write it, never edit it
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
"""Migration boundary: legacy formulas become curated evidence or session proposals."""
|
||||
|
||||
from tht.evidence import formula_store
|
||||
from tht.evidence.formula_store import ConceptFormula
|
||||
|
||||
|
||||
def test_reviewed_formula_migration_has_deterministic_provenance_hash():
|
||||
formula = ConceptFormula(
|
||||
concept="fascia pediatrica",
|
||||
columns=["clinical.patient.birth_date"],
|
||||
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
|
||||
status="reviewed",
|
||||
sources=["Regola clinica approvata dal gruppo pediatrico."],
|
||||
)
|
||||
|
||||
first = formula_store.legacy_formula_to_curated(
|
||||
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
|
||||
)
|
||||
second = formula_store.legacy_formula_to_curated(
|
||||
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
|
||||
)
|
||||
|
||||
assert first is not None
|
||||
assert second is not None
|
||||
assert first.provenance.source_sha256 == second.provenance.source_sha256
|
||||
assert first.provenance.source_sha256.startswith("sha256:")
|
||||
@@ -8,7 +8,13 @@ part of the schema-linking artifact. The store is frontmatter-YAML + SQL body.
|
||||
"""
|
||||
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from tht.decisions import DecisionRecord
|
||||
from tht.evidence import formula_store
|
||||
from tht.evidence.formula_store import ConceptFormula, retrieve_formula, save_formula
|
||||
from tht.evidence.session import project_session
|
||||
from tht.session.models import SchemaLinking
|
||||
|
||||
|
||||
def test_formula_retrieval_by_concept(tmp_path):
|
||||
@@ -82,3 +88,63 @@ def test_concept_formula_default_status(tmp_path):
|
||||
f = ConceptFormula(concept="x", columns=["c"], sql="SELECT 1")
|
||||
assert f.status == "draft" # not yet reviewed
|
||||
assert f.sources == []
|
||||
|
||||
|
||||
def test_reviewed_legacy_formula_becomes_curated_formula_with_stable_provenance():
|
||||
formula = ConceptFormula(
|
||||
concept="fascia pediatrica",
|
||||
columns=["clinical.patient.birth_date"],
|
||||
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
|
||||
status="reviewed",
|
||||
sources=["Regola clinica approvata dal gruppo pediatrico."],
|
||||
)
|
||||
|
||||
migrated = formula_store.legacy_formula_to_curated(
|
||||
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
|
||||
)
|
||||
|
||||
assert migrated is not None
|
||||
assert migrated.id == "evidence:fascia-pediatrica"
|
||||
assert migrated.title == "Fascia pediatrica"
|
||||
assert migrated.kind == "formula"
|
||||
assert migrated.payload.concept == formula.concept
|
||||
assert migrated.payload.columns == ("clinical.patient.birth_date",)
|
||||
assert migrated.payload.sql == formula.sql
|
||||
assert migrated.provenance.source_file == "source/formulas/fascia-pediatrica-1.sql.md"
|
||||
assert migrated.provenance.supporting_excerpts == tuple(formula.sources)
|
||||
assert migrated.review_items == ()
|
||||
assert formula_store.legacy_formula_to_curated(
|
||||
formula, legacy_path="formulas/fascia-pediatrica-1.sql.md",
|
||||
).id == migrated.id
|
||||
|
||||
|
||||
def test_session_formula_proposal_is_versioned_and_is_not_published_evidence(tmp_path):
|
||||
linking = SchemaLinking(
|
||||
question="Conta i pazienti pediatrici",
|
||||
concept_formulas=[{
|
||||
"concept": "fascia pediatrica",
|
||||
"columns": ["clinical.patient.birth_date"],
|
||||
"sql": "CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
|
||||
"sources": ["Sintetizzata nella sessione"],
|
||||
}],
|
||||
)
|
||||
decisions = [DecisionRecord(
|
||||
seq=9,
|
||||
ts=datetime(2026, 8, 25, tzinfo=UTC),
|
||||
type="concept_formula_approved",
|
||||
subject="phase:4",
|
||||
detail="fascia pediatrica",
|
||||
)]
|
||||
|
||||
projected = project_session(decisions, linking, tmp_path / "evidence")
|
||||
|
||||
assert projected == [{
|
||||
"schema_version": 1,
|
||||
"kind": "formula_proposal",
|
||||
"publication": "session_only",
|
||||
"concept": "fascia pediatrica",
|
||||
"columns": ["clinical.patient.birth_date"],
|
||||
"sql": "CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
|
||||
"sources": ["Sintetizzata nella sessione"],
|
||||
"decision_seq": 9,
|
||||
}]
|
||||
|
||||
@@ -5,8 +5,16 @@ Completes the formula layer beyond the store: the `auto` status the spec require
|
||||
and the guarantee that load_evidence_dir does NOT choke on *.sql.md formula files when
|
||||
they live under the evidence root.
|
||||
"""
|
||||
import json
|
||||
from types import SimpleNamespace
|
||||
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from tht.cli import app, search_cmd
|
||||
from tht.evidence import formula_store
|
||||
from tht.evidence.formula_store import ConceptFormula, save_formula, search_formulas
|
||||
from tht.evidence.model import EvidenceDoc, load_evidence_dir
|
||||
from tht.evidence.search import EvidenceSearchOutcome
|
||||
|
||||
|
||||
def test_status_auto_is_valid():
|
||||
@@ -35,3 +43,77 @@ def test_load_evidence_dir_skips_formula_files(tmp_path):
|
||||
ids = [d.id for d in docs]
|
||||
assert ids == ["ev1"] # the .sql.md formula file is skipped, no crash
|
||||
assert all(isinstance(d, EvidenceDoc) for d in docs)
|
||||
|
||||
|
||||
def test_unreviewed_legacy_formulas_cannot_become_curated_evidence():
|
||||
for status in ("auto", "draft"):
|
||||
formula = ConceptFormula(
|
||||
concept="fascia pediatrica",
|
||||
columns=["clinical.patient.birth_date"],
|
||||
sql="CASE WHEN age < 18 THEN 'pediatrica' ELSE 'adulta' END",
|
||||
status=status,
|
||||
)
|
||||
assert formula_store.legacy_formula_to_curated(
|
||||
formula, legacy_path="formulas/pediatric-1.sql.md",
|
||||
) is None
|
||||
|
||||
|
||||
def test_formula_search_uses_typed_evidence_with_a_formula_constraint():
|
||||
class Searcher:
|
||||
vector_generation = "gen:" + "a" * 32
|
||||
|
||||
def __init__(self):
|
||||
self.calls = []
|
||||
|
||||
def search(self, embedding, **kwargs):
|
||||
self.calls.append((embedding, kwargs))
|
||||
return [SimpleNamespace(
|
||||
id="fragment:formula", similarity=0.9, content="formula excerpt",
|
||||
title="Fascia pediatrica", metadata={
|
||||
"evidence_id": "evidence:fascia-pediatrica",
|
||||
"evidence_kind": "formula",
|
||||
"document_id": "doc:formula",
|
||||
"ordinal": 0,
|
||||
"source_uri": "file:///curated/formula/fascia-pediatrica.md",
|
||||
"provenance": {},
|
||||
},
|
||||
)]
|
||||
|
||||
class Embedder:
|
||||
def embed_query(self, query):
|
||||
return [0.25]
|
||||
|
||||
searcher = Searcher()
|
||||
outcome = search_cmd.search_formula_evidence(
|
||||
"fascia pediatrica", searcher=searcher, embedder=Embedder(), top=3,
|
||||
)
|
||||
|
||||
assert outcome.status == "available"
|
||||
assert [result.evidence_id for result in outcome.results] == ["evidence:fascia-pediatrica"]
|
||||
assert searcher.calls[0][1]["metadata_filter"]["required_kinds"] == ["formula"]
|
||||
|
||||
|
||||
def test_formula_search_json_is_pristine_while_human_output_warns_about_legacy_store(monkeypatch):
|
||||
monkeypatch.setattr(search_cmd, "_load_config_or_exit", lambda _path: SimpleNamespace(embeddings=object()))
|
||||
monkeypatch.setattr(search_cmd, "workspace_id_for_config", lambda _cfg, _path: "workspace-a")
|
||||
monkeypatch.setattr(search_cmd, "search_formula_evidence", lambda *args, **kwargs: EvidenceSearchOutcome(
|
||||
"available", "gen:" + "a" * 32,
|
||||
))
|
||||
monkeypatch.setattr("tht.cli.vector_cmd.require_vector_cfg", lambda _cfg: None)
|
||||
monkeypatch.setattr("tht.cli.vector_cmd.open_searcher", lambda _cfg: object())
|
||||
monkeypatch.setattr("tht.cli.vector_cmd.make_embedder", lambda _cfg: object())
|
||||
monkeypatch.setattr("tht.evidence.active_searcher", lambda *args, **kwargs: object())
|
||||
monkeypatch.setattr("tht.evidence.validate_corpus_workspace", lambda _cfg, _workspace: None)
|
||||
|
||||
json_result = CliRunner().invoke(
|
||||
app, ["search", "find", "fascia pediatrica", "--kind", "formula", "--top", "1", "--json"],
|
||||
)
|
||||
human_result = CliRunner().invoke(
|
||||
app, ["search", "find", "fascia pediatrica", "--kind", "formula", "--top", "1"],
|
||||
)
|
||||
|
||||
assert json_result.exit_code == 0
|
||||
assert json.loads(json_result.stdout) == []
|
||||
assert "ATTENZIONE" not in json_result.stdout
|
||||
assert human_result.exit_code == 0
|
||||
assert "ATTENZIONE" in human_result.output
|
||||
|
||||
@@ -9,7 +9,7 @@ from tht.pi_skill_projection import (
|
||||
render_projection,
|
||||
)
|
||||
|
||||
BASELINE_SHA256 = "bb6daa6fe83d22f5e701025c5334d72ec9eb35349e90d24cb9d7f6290d0fecfe"
|
||||
BASELINE_SHA256 = "62bfa0dbc1179b43b2d80dc48155a6119421488a1ffc160e9c664f1fe280ce52"
|
||||
|
||||
|
||||
def test_modular_pi_skill_renders_the_byte_identical_approved_projection():
|
||||
@@ -23,6 +23,7 @@ def test_modular_pi_skill_renders_the_byte_identical_approved_projection():
|
||||
("{{DISAMBIGUATION_REWRITING_INSTRUCTIONS}}", "disambiguation/phase-3.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F4}}", "memory/solved-search-f4.md"),
|
||||
("{{DISAMBIGUATION_SCHEMA_GROUNDING}}", "disambiguation/schema-grounding.md"),
|
||||
("{{EVIDENCE_FORMULA_PROPOSALS}}", "evidence/formula-proposals.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F6}}", "memory/solved-search-f6.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F7}}", "memory/solved-search-f7.md"),
|
||||
("{{MEMORY_PROMOTION_F8}}", "memory/phase-8-promotion.md"),
|
||||
|
||||
@@ -11,7 +11,7 @@ KIND_MAP = {
|
||||
"evidence": ["evidence"],
|
||||
"schema": ["schema_table", "schema_column"],
|
||||
"values": [], # solo LSH
|
||||
"formula": [], # solo formula store (D14b), niente LSH/vector
|
||||
"formula": ["evidence"],
|
||||
}
|
||||
|
||||
_STAGE_PURPOSES = {
|
||||
@@ -30,6 +30,20 @@ DEFAULT_TOP_FALLBACK = 10
|
||||
search_app = typer.Typer(help="Ricerca semantica (evidence/schema/values) nel vectorstore")
|
||||
|
||||
|
||||
def search_formula_evidence(keyword: str, *, searcher, embedder, top: int):
|
||||
"""Search only published Formula Evidence through the shared typed facade."""
|
||||
from tht.evidence import EvidenceSearchContext, search_evidence
|
||||
|
||||
return search_evidence(
|
||||
keyword,
|
||||
"sql_generation",
|
||||
EvidenceSearchContext(required_kinds=("formula",)),
|
||||
searcher=searcher,
|
||||
embedder=embedder,
|
||||
top_n=top,
|
||||
)
|
||||
|
||||
|
||||
def _leased_dwh_snapshot(cfg, context: typer.Context):
|
||||
from tht.jobs.dwh_pipeline import lease_dwh_snapshot
|
||||
|
||||
@@ -143,12 +157,6 @@ def search_cmd(
|
||||
|
||||
workspace_id = workspace_id_for_config(cfg, config)
|
||||
validate_corpus_workspace(cfg, workspace_id)
|
||||
dwh_snapshot = _leased_dwh_snapshot(cfg, ctx)
|
||||
require_vector_cfg(cfg)
|
||||
runtime_searcher = active_searcher(
|
||||
cfg, open_searcher(cfg),
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
if kind is not None and kind not in KIND_MAP:
|
||||
typer.secho(
|
||||
f"ERRORE: --kind sconosciuto: {kind} (validi: {', '.join(KIND_MAP)})",
|
||||
@@ -160,29 +168,60 @@ def search_cmd(
|
||||
top = cfg.search.top_schema_tables if kind == "schema" else DEFAULT_TOP_FALLBACK
|
||||
|
||||
if kind == "formula":
|
||||
# D14b: recupero formule di concetto dallo store locale (niente LSH/vector).
|
||||
from tht.cli.evidence_cmd import evidence_root
|
||||
from tht.evidence.formula_store import search_formulas
|
||||
|
||||
formulas = search_formulas(evidence_root(cfg), keyword)[:top]
|
||||
require_vector_cfg(cfg)
|
||||
outcome = search_formula_evidence(
|
||||
keyword,
|
||||
searcher=active_searcher(cfg, open_searcher(cfg), workspace_id=workspace_id),
|
||||
embedder=make_embedder(cfg.embeddings),
|
||||
top=top,
|
||||
)
|
||||
if outcome.status == "unavailable":
|
||||
payload = {"status": outcome.status, "code": outcome.code, "message": outcome.message}
|
||||
if json_out:
|
||||
typer.echo(json.dumps(payload, ensure_ascii=False))
|
||||
else:
|
||||
typer.secho(f"ERRORE: {outcome.message}", fg=typer.colors.RED, err=True)
|
||||
raise typer.Exit(1)
|
||||
if json_out:
|
||||
typer.echo(json.dumps(
|
||||
[f.model_dump(mode="json") for f in formulas], ensure_ascii=False, indent=2))
|
||||
typer.echo(json.dumps([
|
||||
{
|
||||
"evidence_id": result.evidence_id,
|
||||
"title": result.title,
|
||||
"kind": result.kind,
|
||||
"excerpts": list(result.excerpts),
|
||||
"provenance": result.provenance,
|
||||
"citation": result.citation,
|
||||
"document_id": result.document_id,
|
||||
}
|
||||
for result in outcome.results
|
||||
], ensure_ascii=False, indent=2))
|
||||
return
|
||||
if not formulas:
|
||||
typer.secho(
|
||||
"ATTENZIONE: lo store formule legacy non viene più consultato; "
|
||||
"sono disponibili solo Formula Evidence pubblicate.",
|
||||
fg=typer.colors.YELLOW,
|
||||
err=True,
|
||||
)
|
||||
if not outcome.results:
|
||||
typer.secho(f"Nessuna formula per '{keyword}'.", fg=typer.colors.YELLOW)
|
||||
return
|
||||
table = Table(title=f"Formule per '{keyword}'")
|
||||
table.add_column("Concetto")
|
||||
table.add_column("Status")
|
||||
table.add_column("Colonne")
|
||||
table.add_column("SQL")
|
||||
for f in formulas:
|
||||
sql_preview = (f.sql[:80] + "…") if len(f.sql) > 80 else f.sql
|
||||
table.add_row(f.concept, f.status, ", ".join(f.columns), sql_preview)
|
||||
table = Table(title=f"Formula Evidence per '{keyword}'")
|
||||
table.add_column("Formula")
|
||||
table.add_column("Provenienza")
|
||||
table.add_column("Estratto")
|
||||
for result in outcome.results:
|
||||
excerpt = result.excerpts[0] if result.excerpts else ""
|
||||
table.add_row(result.title, result.citation, excerpt[:120])
|
||||
Console().print(table)
|
||||
return
|
||||
|
||||
dwh_snapshot = _leased_dwh_snapshot(cfg, ctx)
|
||||
require_vector_cfg(cfg)
|
||||
runtime_searcher = active_searcher(
|
||||
cfg, open_searcher(cfg),
|
||||
workspace_id=workspace_id,
|
||||
)
|
||||
|
||||
lsh_hits = None
|
||||
try:
|
||||
lsh, minhashes, meta = load_index(
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
"""SQL concept->formula evidence store (spec D14b, §4.7).
|
||||
"""Legacy ConceptFormula reader and one-way migration into Curated Evidence.
|
||||
|
||||
A concept (e.g. 'fascia pediatrica', 'ablazione') maps to a reusable SQL formula
|
||||
(a CASE WHEN ...) that derives it from physical columns. These are reviewable
|
||||
units: the gate surfaces a candidate formula, the reviewer approves or rejects it
|
||||
(decision types concept_formula_approved / concept_formula_rejected), and approved
|
||||
formulas travel with the schema-linking artifact.
|
||||
|
||||
Storage: one file per formula, frontmatter YAML + SQL body (same shape as
|
||||
EvidenceDoc.parse). Directory layout: <root>/formulas/<slug>-<n>.sql.md.
|
||||
Retrieve is by concept (may return several, e.g. competing drafts vs reviewed).
|
||||
The ``formulas/*.sql.md`` store is retained only for the migration window. Runtime
|
||||
lookup uses typed, published ``kind=formula`` Evidence instead. A session reviewer
|
||||
may still approve a formula locally; that is a proposal, not publication.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
@@ -19,6 +14,8 @@ from typing import Literal
|
||||
import yaml
|
||||
from pydantic import BaseModel
|
||||
|
||||
from tht.evidence.canonical import CuratedEvidence
|
||||
|
||||
FORMULAS_SUBDIR = "formulas"
|
||||
_SUFFIX_RE = re.compile(r"^(.*?)-(\d+)\.sql\.md$")
|
||||
|
||||
@@ -104,8 +101,45 @@ def retrieve_formula(root: Path | str, concept: str) -> list[ConceptFormula]:
|
||||
|
||||
|
||||
def search_formulas(root: Path | str, query: str) -> list[ConceptFormula]:
|
||||
"""Formulas whose concept contains `query` (case-insensitive). Used by
|
||||
`tht search find --kind formula` (D14b retrieval, §4.7.2): the reviewer searches a
|
||||
concept term and gets the candidate formulas to approve before they reach the CTE."""
|
||||
"""Read legacy formulas for migration tooling only (case-insensitive concept match)."""
|
||||
q = query.strip().lower()
|
||||
return [f for f in _load_all(Path(root)) if q in f.concept.lower()]
|
||||
|
||||
|
||||
def legacy_formula_to_curated(
|
||||
formula: ConceptFormula,
|
||||
*,
|
||||
legacy_path: str,
|
||||
) -> CuratedEvidence | None:
|
||||
"""Convert one reviewed legacy formula into its deterministic curated counterpart.
|
||||
|
||||
Drafts and model-generated formulas have no global publication status. Their
|
||||
caller must project them as session-local Formula proposals instead.
|
||||
"""
|
||||
if formula.status != "reviewed":
|
||||
return None
|
||||
source_notes = tuple(formula.sources) or (
|
||||
"Legacy formula migrated without a recorded provenance note.",
|
||||
)
|
||||
source_sha256 = hashlib.sha256(formula.dump().encode("utf-8")).hexdigest()
|
||||
source_file = legacy_path if legacy_path.startswith("source/") else f"source/{legacy_path}"
|
||||
return CuratedEvidence.model_validate({
|
||||
"schema_version": 1,
|
||||
"id": f"evidence:{formula._slug}",
|
||||
"title": formula.concept[:1].upper() + formula.concept[1:],
|
||||
"kind": "formula",
|
||||
"purposes": ["schema_linking", "sql_generation"],
|
||||
"applies_to": {"concepts": [formula.concept], "columns": formula.columns},
|
||||
"language": "it",
|
||||
"provenance": {
|
||||
"source_file": source_file,
|
||||
"source_sha256": f"sha256:{source_sha256}",
|
||||
"supporting_excerpts": source_notes,
|
||||
},
|
||||
"review_items": [],
|
||||
"payload": {
|
||||
"concept": formula.concept,
|
||||
"columns": formula.columns,
|
||||
"sql": formula.sql,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -55,7 +55,45 @@ def project_session(
|
||||
"esito": "accettata" if decision.type == "evidence_accepted" else "scartata",
|
||||
"decision_seq": decision.seq,
|
||||
}
|
||||
return list(entries.values())
|
||||
return list(entries.values()) + _formula_proposals(decisions, linking)
|
||||
|
||||
|
||||
def _formula_proposals(decisions: list["DecisionRecord"], linking: "SchemaLinking") -> list[dict]:
|
||||
"""Project locally approved F4 formulas without representing them as Evidence.
|
||||
|
||||
A proposal remains in the persisted session artifact until a separate curator
|
||||
imports, reviews, and publishes it in the workspace repository.
|
||||
"""
|
||||
approved = {
|
||||
decision.detail: decision.seq
|
||||
for decision in decisions
|
||||
if decision.type == "concept_formula_approved" and decision.detail
|
||||
}
|
||||
proposals = []
|
||||
for formula in linking.concept_formulas:
|
||||
if not isinstance(formula, dict):
|
||||
continue
|
||||
concept = formula.get("concept")
|
||||
sql = formula.get("sql")
|
||||
columns = formula.get("columns")
|
||||
if not isinstance(concept, str) or not isinstance(sql, str) or not isinstance(columns, list):
|
||||
continue
|
||||
# A referenced published Formula Evidence is already represented by its
|
||||
# Evidence receipt/citation, so it must not be reintroduced as a proposal.
|
||||
evidence_id = formula.get("evidence_id", formula.get("id", ""))
|
||||
if isinstance(evidence_id, str) and evidence_id.startswith("evidence:"):
|
||||
continue
|
||||
proposals.append({
|
||||
"schema_version": 1,
|
||||
"kind": "formula_proposal",
|
||||
"publication": "session_only",
|
||||
"concept": concept,
|
||||
"columns": columns,
|
||||
"sql": sql,
|
||||
"sources": formula.get("sources", []),
|
||||
"decision_seq": approved.get(concept),
|
||||
})
|
||||
return proposals
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -19,6 +19,7 @@ FRAGMENT_ORDER = (
|
||||
("{{DISAMBIGUATION_REWRITING_INSTRUCTIONS}}", "disambiguation/phase-3.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F4}}", "memory/solved-search-f4.md"),
|
||||
("{{DISAMBIGUATION_SCHEMA_GROUNDING}}", "disambiguation/schema-grounding.md"),
|
||||
("{{EVIDENCE_FORMULA_PROPOSALS}}", "evidence/formula-proposals.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F6}}", "memory/solved-search-f6.md"),
|
||||
("{{MEMORY_SOLVED_SEARCH_F7}}", "memory/solved-search-f7.md"),
|
||||
("{{MEMORY_PROMOTION_F8}}", "memory/phase-8-promotion.md"),
|
||||
|
||||
Reference in New Issue
Block a user