Spaces:
Paused
Paused
Upload 9 files
Browse files- app.py +48 -0
- phases/phase1_ingest.py +13 -0
- phases/phase2_rules.py +10 -0
- phases/phase3_static.py +27 -0
- phases/phase4_runtime.py +9 -0
- phases/phase5_ai_review.py +2 -0
- prompts/fix_planner.txt +14 -0
- prompts/gap_detector.txt +19 -0
- prompts/rule_normalizer.txt +18 -0
app.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import gradio as gr
|
| 2 |
+
from phases.phase1_ingest import ingest
|
| 3 |
+
from phases.phase2_rules import load_rules
|
| 4 |
+
from phases.phase3_static import static_scan
|
| 5 |
+
from phases.phase4_runtime import runtime_risks
|
| 6 |
+
|
| 7 |
+
with gr.Blocks(title="SpecGuard AI Lab", theme=gr.themes.Soft()) as app:
|
| 8 |
+
|
| 9 |
+
gr.Markdown("## 🧠 SpecGuard AI Lab")
|
| 10 |
+
gr.Markdown("Rulebook ↔ Code Deep Research & Verification System")
|
| 11 |
+
|
| 12 |
+
with gr.Sidebar():
|
| 13 |
+
rulebook_input = gr.Textbox(
|
| 14 |
+
label="📘 Rulebook (paste)",
|
| 15 |
+
lines=12,
|
| 16 |
+
placeholder="Paste specification or rulebook text here"
|
| 17 |
+
)
|
| 18 |
+
code_zip = gr.File(label="📦 Source Code (ZIP)")
|
| 19 |
+
run_btn = gr.Button("🚀 Start Deep Research", variant="primary")
|
| 20 |
+
|
| 21 |
+
status = gr.Markdown()
|
| 22 |
+
output = gr.Markdown()
|
| 23 |
+
|
| 24 |
+
def run_all(rulebook, zipfile):
|
| 25 |
+
s1 = ingest(rulebook, zipfile)
|
| 26 |
+
rules = load_rules()
|
| 27 |
+
findings = static_scan()
|
| 28 |
+
risks = runtime_risks()
|
| 29 |
+
|
| 30 |
+
report = f"""
|
| 31 |
+
### ✅ Research Complete
|
| 32 |
+
|
| 33 |
+
**Rules Loaded:** {len(rules.get("rules", []))}
|
| 34 |
+
**Static Findings:** {len(findings)}
|
| 35 |
+
**Runtime Risks Identified:** {len(risks)}
|
| 36 |
+
|
| 37 |
+
Artifacts saved in `/artifacts/`
|
| 38 |
+
"""
|
| 39 |
+
return "Done", report
|
| 40 |
+
|
| 41 |
+
run_btn.click(
|
| 42 |
+
run_all,
|
| 43 |
+
inputs=[rulebook_input, code_zip],
|
| 44 |
+
outputs=[status, output]
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
if __name__ == "__main__":
|
| 48 |
+
app.launch()
|
phases/phase1_ingest.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, zipfile, json
|
| 2 |
+
|
| 3 |
+
def ingest(rulebook_text, code_zip):
|
| 4 |
+
os.makedirs("artifacts", exist_ok=True)
|
| 5 |
+
|
| 6 |
+
with open("artifacts/rulebook_raw.txt", "w", encoding="utf-8") as f:
|
| 7 |
+
f.write(rulebook_text)
|
| 8 |
+
|
| 9 |
+
if code_zip:
|
| 10 |
+
with zipfile.ZipFile(code_zip, "r") as z:
|
| 11 |
+
z.extractall("artifacts/code")
|
| 12 |
+
|
| 13 |
+
return "Ingestion complete"
|
phases/phase2_rules.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import yaml, json
|
| 2 |
+
|
| 3 |
+
def load_rules(path="RULEBOOK.yaml"):
|
| 4 |
+
with open(path, "r", encoding="utf-8") as f:
|
| 5 |
+
rules = yaml.safe_load(f)
|
| 6 |
+
|
| 7 |
+
with open("artifacts/normalized_rules.json", "w") as out:
|
| 8 |
+
json.dump(rules, out, indent=2)
|
| 9 |
+
|
| 10 |
+
return rules
|
phases/phase3_static.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import ast, os, json
|
| 2 |
+
|
| 3 |
+
def static_scan(root="artifacts/code"):
|
| 4 |
+
findings = []
|
| 5 |
+
|
| 6 |
+
for dirpath, _, files in os.walk(root):
|
| 7 |
+
for f in files:
|
| 8 |
+
if f.endswith(".py"):
|
| 9 |
+
path = os.path.join(dirpath, f)
|
| 10 |
+
try:
|
| 11 |
+
tree = ast.parse(open(path, "r", encoding="utf-8").read())
|
| 12 |
+
for node in ast.walk(tree):
|
| 13 |
+
if isinstance(node, ast.Assign):
|
| 14 |
+
src = ast.unparse(node)
|
| 15 |
+
if "socket_timeout" in src:
|
| 16 |
+
findings.append({
|
| 17 |
+
"file": path,
|
| 18 |
+
"issue": "socket_timeout_detected",
|
| 19 |
+
"line": node.lineno
|
| 20 |
+
})
|
| 21 |
+
except Exception as e:
|
| 22 |
+
findings.append({"file": path, "error": str(e)})
|
| 23 |
+
|
| 24 |
+
with open("artifacts/static_findings.json", "w") as f:
|
| 25 |
+
json.dump(findings, f, indent=2)
|
| 26 |
+
|
| 27 |
+
return findings
|
phases/phase4_runtime.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def runtime_risks():
|
| 2 |
+
risks = [
|
| 3 |
+
"Python threads cannot be force-killed safely",
|
| 4 |
+
"yt-dlp may block despite socket_timeout",
|
| 5 |
+
"HF Spaces may restart and kill background tasks"
|
| 6 |
+
]
|
| 7 |
+
with open("artifacts/environment_risks.md", "w") as f:
|
| 8 |
+
f.write("\n".join(f"- {r}" for r in risks))
|
| 9 |
+
return risks
|
phases/phase5_ai_review.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
def summarize():
|
| 2 |
+
return "Artifacts ready for AI reasoning (external or local LLM)"
|
prompts/fix_planner.txt
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
You are a senior systems engineer.
|
| 2 |
+
|
| 3 |
+
INPUT:
|
| 4 |
+
- Rule–Code gap report
|
| 5 |
+
|
| 6 |
+
TASK:
|
| 7 |
+
- Generate minimal patches (not rewrites)
|
| 8 |
+
- Suggest rulebook corrections if rule is unrealistic
|
| 9 |
+
- Maintain architectural philosophy
|
| 10 |
+
|
| 11 |
+
CONSTRAINTS:
|
| 12 |
+
- No new features
|
| 13 |
+
- No scope expansion
|
| 14 |
+
- No speculative optimizations
|
prompts/gap_detector.txt
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
You are a rule–code auditor.
|
| 2 |
+
|
| 3 |
+
INPUTS:
|
| 4 |
+
- RULEBOOK.yaml
|
| 5 |
+
- Static code findings
|
| 6 |
+
- Environment constraints
|
| 7 |
+
- Source code excerpts
|
| 8 |
+
|
| 9 |
+
TASK:
|
| 10 |
+
For EACH rule ID:
|
| 11 |
+
- Classify as COMPLIANT, VIOLATED, PARTIAL, or UNKNOWN
|
| 12 |
+
- Cite exact file and line numbers
|
| 13 |
+
- Explain mismatch logically
|
| 14 |
+
- Do NOT rewrite code
|
| 15 |
+
- Propose minimal fixes only
|
| 16 |
+
|
| 17 |
+
IMPORTANT:
|
| 18 |
+
- Never guess intent
|
| 19 |
+
- If Python or runtime prevents enforcement, say so clearly
|
prompts/rule_normalizer.txt
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
You are a specification engineer.
|
| 2 |
+
|
| 3 |
+
INPUT:
|
| 4 |
+
- Human-written rulebook text
|
| 5 |
+
|
| 6 |
+
TASK:
|
| 7 |
+
1. Extract atomic rules.
|
| 8 |
+
2. Assign unique rule IDs.
|
| 9 |
+
3. Convert vague intent into verifiable constraints.
|
| 10 |
+
4. If a rule cannot be enforced by code, mark it as "environment-limited".
|
| 11 |
+
|
| 12 |
+
STRICT RULES:
|
| 13 |
+
- Do not invent rules.
|
| 14 |
+
- Do not assume behavior.
|
| 15 |
+
- If evidence is missing, mark as UNKNOWN.
|
| 16 |
+
|
| 17 |
+
OUTPUT:
|
| 18 |
+
Structured JSON only.
|