feat: online verification, node removals, and knowledge browser
CI / quality (push) Canceled after 0s
CI / quality (push) Canceled after 0s
Adds claim-bound online evidence records and citation verification scripts, node removal transport/adapter, retrieval benchmark and owner-review tooling, GraphCanvas/SourceDetail knowledge browser UI, and the ingest handoff helper. Bumps the agents/gewu pack to the online-verification branch.
This commit is contained in:
1 parent
a3156f4e08
commit
78033bb830
83 files changed
+13476
-1051
No files matched your search
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Render held ingestion drafts and correction proposals as an owner decision sheet."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
def render(items: list[dict[str, Any]]) -> str:
|
||||
sections = [
|
||||
"# GEWU owner review sheet",
|
||||
"",
|
||||
(
|
||||
"Mark one decision for each item and add a short rationale. This sheet records review "
|
||||
"intent only; it does not alter GEWU state or grant acceptance authority."
|
||||
),
|
||||
"",
|
||||
(
|
||||
"For held drafts: choose keep held, request revision, or initiate the authorized owner "
|
||||
"challenge workflow. For corrections: choose approve for fresh owner workflow, request "
|
||||
"revision, or reject. Proposal base commits may be stale; every later action must "
|
||||
"context and re-evaluate the proposal."
|
||||
),
|
||||
"",
|
||||
]
|
||||
for index, item in enumerate(items, start=1):
|
||||
proposal = item.get("proposal", {})
|
||||
operations = proposal.get("operations", [])
|
||||
nodes = [operation["node"] for operation in operations if operation.get("node")]
|
||||
edges = [operation["edge"] for operation in operations if operation.get("edge")]
|
||||
node = nodes[0] if nodes else {}
|
||||
is_correction = item.get("source_stage") == "supersession"
|
||||
kind = "Correction proposal" if is_correction else "Policy-held draft"
|
||||
proposal_id = proposal.get("proposal_id", "unknown")
|
||||
revision = proposal.get("revision", "?")
|
||||
node_id = node.get("node_id", "not available")
|
||||
node_type = node.get("node_type", "unknown")
|
||||
sections.extend(
|
||||
[
|
||||
f"## {index}. {kind}: {node.get('title', proposal.get('proposal_id', 'Untitled'))}",
|
||||
"",
|
||||
f"- Proposal: `{proposal_id}` revision {revision}",
|
||||
f"- Current state: {item.get('storage_state', 'evaluated; needs-review')}",
|
||||
f"- Proposal base commit: `{proposal.get('base_commit', 'unknown')}`",
|
||||
f"- Node: `{node_id}` ({node_type})",
|
||||
f"- Summary: {node.get('summary', 'No summary recorded.')}",
|
||||
"",
|
||||
"### Claims and attached evidence",
|
||||
"",
|
||||
]
|
||||
)
|
||||
claim_groups = [
|
||||
("Claim", node.get("claims", [])),
|
||||
("Assumption", node.get("assumptions", [])),
|
||||
("Limitation", node.get("limitations", [])),
|
||||
]
|
||||
if not any(claims for _, claims in claim_groups):
|
||||
sections.append("No node claim payload was included in this review item.")
|
||||
sections.append("")
|
||||
for group_label, claims in claim_groups:
|
||||
for claim in claims:
|
||||
sections.extend(
|
||||
[
|
||||
f"- **{group_label} {claim.get('claim_id', 'claim')}:** "
|
||||
f"{claim.get('text', '')}"
|
||||
]
|
||||
)
|
||||
for evidence in claim.get("evidence", []):
|
||||
locator = evidence.get("locator", {})
|
||||
location = ", ".join(
|
||||
f"{key}={locator[key]}"
|
||||
for key in ("page", "block_id", "start", "end")
|
||||
if key in locator
|
||||
)
|
||||
quote = evidence.get("quote", "").splitlines()
|
||||
sections.append(
|
||||
f" - Evidence `{evidence.get('source_revision_id', '?')}` ({location}):"
|
||||
)
|
||||
sections.extend(f" > {line}" for line in quote)
|
||||
for edge in edges:
|
||||
sections.append(
|
||||
f"- Supersession edge: `{edge.get('source', {}).get('id', '?')}` → `"
|
||||
f"{edge.get('target', {}).get('id', '?')}` "
|
||||
f"({edge.get('edge_type', 'relationship')})"
|
||||
)
|
||||
sections.extend(["", "### Owner decision", ""])
|
||||
if is_correction:
|
||||
sections.extend(
|
||||
[
|
||||
"- [ ] Approve this replacement for the owner workflow "
|
||||
"(fresh evaluation still required)",
|
||||
"- [ ] Request revision",
|
||||
"- [ ] Reject this correction",
|
||||
]
|
||||
)
|
||||
else:
|
||||
sections.extend(
|
||||
[
|
||||
"- [ ] Keep held; do not create an exception",
|
||||
"- [ ] Request revision",
|
||||
"- [ ] Initiate the authorized owner challenge workflow",
|
||||
]
|
||||
)
|
||||
sections.extend(["- Rationale: ", "", "---", ""])
|
||||
return "\n".join(sections)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--input", type=Path, required=True)
|
||||
parser.add_argument("--output", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
items = json.loads(args.input.read_text(encoding="utf-8"))
|
||||
if not isinstance(items, list) or len(items) != 19:
|
||||
parser.error("Expected the frozen 19-item held-draft and correction review packet")
|
||||
output = render(items)
|
||||
with args.output.open("x", encoding="utf-8") as stream:
|
||||
stream.write(output)
|
||||
print(json.dumps({"status": "created", "items": len(items), "output": str(args.output)}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user