lovarch-cli 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. lovarch_cli/__init__.py +16 -0
  2. lovarch_cli/__main__.py +10 -0
  3. lovarch_cli/ai/__init__.py +21 -0
  4. lovarch_cli/ai/gateway.py +240 -0
  5. lovarch_cli/api.py +111 -0
  6. lovarch_cli/auth/__init__.py +32 -0
  7. lovarch_cli/auth/keyring_store.py +214 -0
  8. lovarch_cli/auth/local_server.py +165 -0
  9. lovarch_cli/auth/pkce.py +57 -0
  10. lovarch_cli/auth/session.py +189 -0
  11. lovarch_cli/cli.py +262 -0
  12. lovarch_cli/clients/__init__.py +33 -0
  13. lovarch_cli/clients/factory.py +54 -0
  14. lovarch_cli/clients/local_client.py +432 -0
  15. lovarch_cli/clients/lovarch_storage.py +174 -0
  16. lovarch_cli/clients/lovarch_supabase.py +295 -0
  17. lovarch_cli/clients/persistence.py +166 -0
  18. lovarch_cli/clients/storage.py +66 -0
  19. lovarch_cli/commands/__init__.py +10 -0
  20. lovarch_cli/commands/account.py +172 -0
  21. lovarch_cli/commands/audit.py +394 -0
  22. lovarch_cli/commands/config_cmd.py +80 -0
  23. lovarch_cli/commands/consolidate.py +217 -0
  24. lovarch_cli/commands/context_cmd.py +73 -0
  25. lovarch_cli/commands/dev.py +287 -0
  26. lovarch_cli/commands/do_cmd.py +120 -0
  27. lovarch_cli/commands/init.py +218 -0
  28. lovarch_cli/commands/jobs_cmd.py +95 -0
  29. lovarch_cli/commands/login.py +202 -0
  30. lovarch_cli/commands/mcp_cmd.py +26 -0
  31. lovarch_cli/commands/run.py +375 -0
  32. lovarch_cli/commands/signup.py +185 -0
  33. lovarch_cli/commands/status.py +243 -0
  34. lovarch_cli/commands/upgrade.py +108 -0
  35. lovarch_cli/commands/verifica_cmd.py +174 -0
  36. lovarch_cli/config.py +101 -0
  37. lovarch_cli/config_store.py +111 -0
  38. lovarch_cli/credits/__init__.py +35 -0
  39. lovarch_cli/credits/base.py +84 -0
  40. lovarch_cli/credits/factory.py +36 -0
  41. lovarch_cli/credits/local.py +34 -0
  42. lovarch_cli/credits/lovarch.py +56 -0
  43. lovarch_cli/i18n/__init__.py +27 -0
  44. lovarch_cli/i18n/loader.py +121 -0
  45. lovarch_cli/i18n/translations/en.json +168 -0
  46. lovarch_cli/i18n/translations/es.json +168 -0
  47. lovarch_cli/i18n/translations/it.json +168 -0
  48. lovarch_cli/i18n/translations/pt.json +168 -0
  49. lovarch_cli/mcp/__init__.py +9 -0
  50. lovarch_cli/mcp/server.py +199 -0
  51. lovarch_cli/mcp/tools.py +372 -0
  52. lovarch_cli/sample_downloader.py +255 -0
  53. lovarch_cli/squad/README.md +206 -0
  54. lovarch_cli/squad/agents/auditor-input.md +353 -0
  55. lovarch_cli/squad/agents/bim-engineer.md +404 -0
  56. lovarch_cli/squad/agents/briefing-architect.md +249 -0
  57. lovarch_cli/squad/agents/cad-engineer.md +278 -0
  58. lovarch_cli/squad/agents/capitolato-writer.md +256 -0
  59. lovarch_cli/squad/agents/computo-engineer.md +258 -0
  60. lovarch_cli/squad/agents/concept-designer.md +399 -0
  61. lovarch_cli/squad/agents/contratto-architect.md +243 -0
  62. lovarch_cli/squad/agents/deliverable-builder.md +253 -0
  63. lovarch_cli/squad/agents/energy-prelim.md +388 -0
  64. lovarch_cli/squad/agents/pratiche-it.md +251 -0
  65. lovarch_cli/squad/agents/progetto-chief.md +768 -0
  66. lovarch_cli/squad/agents/quality-dati.md +409 -0
  67. lovarch_cli/squad/agents/quality-misure.md +418 -0
  68. lovarch_cli/squad/agents/quality-normativa.md +417 -0
  69. lovarch_cli/squad/agents/quality-output.md +436 -0
  70. lovarch_cli/squad/agents/regolatorio-it.md +278 -0
  71. lovarch_cli/squad/checklists/handoff-quality-gate.md +232 -0
  72. lovarch_cli/squad/checklists/quality-dati-checklist.md +134 -0
  73. lovarch_cli/squad/checklists/quality-misure-checklist.md +139 -0
  74. lovarch_cli/squad/checklists/quality-normativa-checklist.md +121 -0
  75. lovarch_cli/squad/checklists/quality-output-checklist.md +116 -0
  76. lovarch_cli/squad/config.yaml +408 -0
  77. lovarch_cli/squad/data/CHANGELOG.md +272 -0
  78. lovarch_cli/squad/data/agents-prd.md +428 -0
  79. lovarch_cli/squad/data/architettura-progetto-rules.md +328 -0
  80. lovarch_cli/squad/data/handoff-card-template.md +231 -0
  81. lovarch_cli/squad/data/mocks/catasto-visura.json +72 -0
  82. lovarch_cli/squad/data/mocks/firma-envelope.json +43 -0
  83. lovarch_cli/squad/data/prezzario-lombardia-sample.json +312 -0
  84. lovarch_cli/squad/scripts/api_clients.py +206 -0
  85. lovarch_cli/squad/scripts/architect_profile.py +276 -0
  86. lovarch_cli/squad/scripts/deliverable_generators.py +844 -0
  87. lovarch_cli/squad/scripts/generate_attico_brera_dwg.py +369 -0
  88. lovarch_cli/squad/scripts/generate_chianti_dxf.py +368 -0
  89. lovarch_cli/squad/scripts/generate_chianti_images.py +223 -0
  90. lovarch_cli/squad/scripts/generate_real_sample_images.py +189 -0
  91. lovarch_cli/squad/scripts/generate_sample_assets.py +382 -0
  92. lovarch_cli/squad/scripts/lovarch_client.py +1046 -0
  93. lovarch_cli/squad/scripts/pipeline_runner.py +2095 -0
  94. lovarch_cli/squad/scripts/render_dxf_to_png.py +57 -0
  95. lovarch_cli/squad/scripts/run_palestra_demo.sh +277 -0
  96. lovarch_cli/squad/scripts/simulate_squad_execution.py +515 -0
  97. lovarch_cli/squad/scripts/validate-squad.py +383 -0
  98. lovarch_cli/squad/tasks/audit-input.md +146 -0
  99. lovarch_cli/squad/tasks/compute-metric.md +105 -0
  100. lovarch_cli/squad/tasks/consolidate-dossier.md +187 -0
  101. lovarch_cli/squad/tasks/generate-cad-plan.md +120 -0
  102. lovarch_cli/squad/tasks/generate-ifc-model.md +108 -0
  103. lovarch_cli/squad/tasks/write-capitolato.md +100 -0
  104. lovarch_cli/squad/templates/asseverazione-tecnica.md +126 -0
  105. lovarch_cli/squad/templates/capitolato-uni-11337.md +235 -0
  106. lovarch_cli/squad/templates/cila-comune-milano.md +177 -0
  107. lovarch_cli/squad/templates/contratto-cnappc.md +220 -0
  108. lovarch_cli/squad/workflows/dal-brief-al-cantiere.yaml +218 -0
  109. lovarch_cli/squad_loader.py +114 -0
  110. lovarch_cli/verify/__init__.py +15 -0
  111. lovarch_cli/verify/contratto.py +110 -0
  112. lovarch_cli/verify/dossier.py +97 -0
  113. lovarch_cli/verify/misure.py +83 -0
  114. lovarch_cli/verify/normativa.py +178 -0
  115. lovarch_cli/version.py +13 -0
  116. lovarch_cli/workflows/__init__.py +9 -0
  117. lovarch_cli/workflows/platform.py +212 -0
  118. lovarch_cli-0.2.1.dist-info/METADATA +232 -0
  119. lovarch_cli-0.2.1.dist-info/RECORD +122 -0
  120. lovarch_cli-0.2.1.dist-info/WHEEL +4 -0
  121. lovarch_cli-0.2.1.dist-info/entry_points.txt +3 -0
  122. lovarch_cli-0.2.1.dist-info/licenses/LICENSE +38 -0
@@ -0,0 +1,383 @@
1
+ """
2
+ validate-squad.py · AIOS Quality Gate Validator
3
+
4
+ Runs squad-checklist v3.1 (Tier 1-4) against squad architettura-progetto
5
+ and outputs a quality score (target: 10/10).
6
+
7
+ Usage:
8
+ python3 validate-squad.py [--verbose]
9
+
10
+ Exit codes:
11
+ 0 = PASS (score ≥7.0)
12
+ 1 = FAIL (score <7.0)
13
+ 2 = ERROR (validation crashed)
14
+ """
15
+ import json
16
+ import re
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import Dict, List, Tuple
20
+
21
+
22
+ SQUAD_DIR = Path(__file__).parent.parent
23
+ VERBOSE = "--verbose" in sys.argv
24
+
25
+
26
+ # ============================================================================
27
+ # TIER 1 · STRUCTURE (BLOCKING)
28
+ # ============================================================================
29
+
30
+ def check_tier_1() -> Tuple[float, List[Dict]]:
31
+ """Returns (score 0-10, list of check results)"""
32
+ checks = []
33
+
34
+ # 1.1 config.yaml exists + parseable
35
+ config_path = SQUAD_DIR / "config.yaml"
36
+ config_exists = config_path.exists()
37
+ checks.append({"id": "1.1", "name": "config.yaml exists", "pass": config_exists, "severity": "BLOCKING"})
38
+
39
+ config_text = config_path.read_text() if config_exists else ""
40
+
41
+ # 1.1 Required fields
42
+ required_fields = ["name:", "version:", "description:", "entry_agent:"]
43
+ for field in required_fields:
44
+ present = field in config_text
45
+ checks.append({
46
+ "id": f"1.1.{field}",
47
+ "name": f"config field {field}",
48
+ "pass": present,
49
+ "severity": "BLOCKING",
50
+ })
51
+
52
+ # 1.1 author
53
+ author_present = "author:" in config_text
54
+ checks.append({"id": "1.1.author", "name": "config has author", "pass": author_present, "severity": "BLOCKING"})
55
+
56
+ # 1.1 slashPrefix
57
+ slash_present = "slashPrefix:" in config_text
58
+ checks.append({"id": "1.1.slashPrefix", "name": "config has slashPrefix", "pass": slash_present, "severity": "BLOCKING"})
59
+
60
+ # 1.4 Directory structure
61
+ dirs = ["agents", "tasks", "checklists", "data", "templates", "workflows", "scripts"]
62
+ for d in dirs:
63
+ d_path = SQUAD_DIR / d
64
+ exists = d_path.exists() and d_path.is_dir()
65
+ checks.append({"id": f"1.4.{d}", "name": f"dir {d}/", "pass": exists, "severity": "BLOCKING"})
66
+
67
+ # 1.4 Min agents count (>=1)
68
+ agents_dir = SQUAD_DIR / "agents"
69
+ agents_count = len(list(agents_dir.glob("*.md"))) if agents_dir.exists() else 0
70
+ checks.append({
71
+ "id": "1.4.agents_count",
72
+ "name": f"agents count >=1 (actual: {agents_count})",
73
+ "pass": agents_count >= 1,
74
+ "severity": "BLOCKING",
75
+ })
76
+
77
+ # 1.4 Min tasks count (>=1 per AIOS standard)
78
+ tasks_dir = SQUAD_DIR / "tasks"
79
+ tasks_count = len(list(tasks_dir.glob("*.md"))) if tasks_dir.exists() else 0
80
+ checks.append({
81
+ "id": "1.4.tasks_count",
82
+ "name": f"tasks count >=1 (actual: {tasks_count})",
83
+ "pass": tasks_count >= 1,
84
+ "severity": "BLOCKING",
85
+ })
86
+
87
+ # 1.5 Cross-references
88
+ central_doc = SQUAD_DIR / "data" / "architettura-progetto-rules.md"
89
+ central_exists = central_doc.exists()
90
+ checks.append({"id": "1.5.central_doc", "name": "central document exists", "pass": central_exists, "severity": "BLOCKING"})
91
+
92
+ changelog = SQUAD_DIR / "data" / "CHANGELOG.md"
93
+ changelog_exists = changelog.exists()
94
+ checks.append({"id": "1.5.changelog", "name": "CHANGELOG.md exists", "pass": changelog_exists, "severity": "BLOCKING"})
95
+
96
+ handoff_card = SQUAD_DIR / "data" / "handoff-card-template.md"
97
+ handoff_card_exists = handoff_card.exists()
98
+ checks.append({"id": "1.5.handoff_card", "name": "handoff-card-template exists", "pass": handoff_card_exists, "severity": "BLOCKING"})
99
+
100
+ handoff_qa = SQUAD_DIR / "checklists" / "handoff-quality-gate.md"
101
+ handoff_qa_exists = handoff_qa.exists()
102
+ checks.append({"id": "1.5.handoff_qa", "name": "handoff-quality-gate exists", "pass": handoff_qa_exists, "severity": "BLOCKING"})
103
+
104
+ # 1.6 Security scan (no hardcoded secrets in YAML)
105
+ secrets_pattern = re.compile(r"(api[_-]?key|secret|password)\s*[:=]\s*['\"][^'\"]{20,}", re.IGNORECASE)
106
+ has_secrets = bool(secrets_pattern.search(config_text))
107
+ checks.append({"id": "1.6.no_secrets", "name": "no hardcoded secrets", "pass": not has_secrets, "severity": "BLOCKING"})
108
+
109
+ # Score
110
+ total = len(checks)
111
+ passed = sum(1 for c in checks if c["pass"])
112
+ score = (passed / total) * 10
113
+ return score, checks
114
+
115
+
116
+ # ============================================================================
117
+ # TIER 2 · COVERAGE (BLOCKING)
118
+ # ============================================================================
119
+
120
+ def check_tier_2() -> Tuple[float, List[Dict]]:
121
+ checks = []
122
+
123
+ # Tier 0 orchestrator exists
124
+ chief_exists = (SQUAD_DIR / "agents" / "progetto-chief.md").exists()
125
+ checks.append({"id": "2.1.chief", "name": "Tier 0 orchestrator exists", "pass": chief_exists, "severity": "BLOCKING"})
126
+
127
+ # Auditor input
128
+ auditor_exists = (SQUAD_DIR / "agents" / "auditor-input.md").exists()
129
+ checks.append({"id": "2.2.auditor", "name": "auditor-input exists", "pass": auditor_exists, "severity": "BLOCKING"})
130
+
131
+ # Tier 1 specialists ≥5
132
+ tier1_agents = [
133
+ "briefing-architect", "regolatorio-it", "concept-designer", "cad-engineer",
134
+ "bim-engineer", "computo-engineer", "capitolato-writer", "pratiche-it",
135
+ "contratto-architect", "energy-prelim", "deliverable-builder",
136
+ ]
137
+ tier1_present = sum(1 for a in tier1_agents if (SQUAD_DIR / "agents" / f"{a}.md").exists())
138
+ checks.append({
139
+ "id": "2.3.tier1_count",
140
+ "name": f"Tier 1 specialists ≥5 (actual: {tier1_present})",
141
+ "pass": tier1_present >= 5,
142
+ "severity": "BLOCKING",
143
+ })
144
+
145
+ # Tier 2 QA agents
146
+ qa_agents = ["quality-misure", "quality-normativa", "quality-dati", "quality-output"]
147
+ qa_present = sum(1 for a in qa_agents if (SQUAD_DIR / "agents" / f"{a}.md").exists())
148
+ checks.append({
149
+ "id": "2.4.qa_count",
150
+ "name": f"Tier 2 QA agents ≥4 (actual: {qa_present})",
151
+ "pass": qa_present >= 4,
152
+ "severity": "BLOCKING",
153
+ })
154
+
155
+ # Tasks ≥3 (atomic)
156
+ tasks = list((SQUAD_DIR / "tasks").glob("*.md")) if (SQUAD_DIR / "tasks").exists() else []
157
+ checks.append({
158
+ "id": "2.5.tasks_count",
159
+ "name": f"Tasks ≥3 (actual: {len(tasks)})",
160
+ "pass": len(tasks) >= 3,
161
+ "severity": "BLOCKING",
162
+ })
163
+
164
+ # Workflows ≥1
165
+ workflows = list((SQUAD_DIR / "workflows").glob("*.yaml")) if (SQUAD_DIR / "workflows").exists() else []
166
+ checks.append({
167
+ "id": "2.6.workflows_count",
168
+ "name": f"Workflows ≥1 (actual: {len(workflows)})",
169
+ "pass": len(workflows) >= 1,
170
+ "severity": "BLOCKING",
171
+ })
172
+
173
+ # Templates ≥3
174
+ templates = list((SQUAD_DIR / "templates").glob("*.md")) if (SQUAD_DIR / "templates").exists() else []
175
+ checks.append({
176
+ "id": "2.7.templates_count",
177
+ "name": f"Templates ≥3 (actual: {len(templates)})",
178
+ "pass": len(templates) >= 3,
179
+ "severity": "BLOCKING",
180
+ })
181
+
182
+ # Checklists ≥4 (1 handoff + 4 QA)
183
+ checklists = list((SQUAD_DIR / "checklists").glob("*.md")) if (SQUAD_DIR / "checklists").exists() else []
184
+ checks.append({
185
+ "id": "2.8.checklists_count",
186
+ "name": f"Checklists ≥5 (actual: {len(checklists)})",
187
+ "pass": len(checklists) >= 5,
188
+ "severity": "BLOCKING",
189
+ })
190
+
191
+ total = len(checks)
192
+ passed = sum(1 for c in checks if c["pass"])
193
+ score = (passed / total) * 10
194
+ return score, checks
195
+
196
+
197
+ # ============================================================================
198
+ # TIER 3 · QUALITY (THRESHOLD 7.0)
199
+ # ============================================================================
200
+
201
+ REQUIRED_AGENT_SECTIONS = [
202
+ "ACTIVATION-NOTICE",
203
+ "IDE-FILE-RESOLUTION",
204
+ "REQUEST-RESOLUTION",
205
+ "activation-instructions",
206
+ "command_loader",
207
+ "agent:",
208
+ "persona:",
209
+ "core_principles",
210
+ "voice_dna:",
211
+ "thinking_dna:",
212
+ "handoff_to:",
213
+ "output_examples:",
214
+ "anti_patterns:",
215
+ "completion_criteria:",
216
+ "smoke_tests:",
217
+ "integration:",
218
+ "greeting:",
219
+ ]
220
+
221
+
222
+ def check_agent_quality(agent_path: Path) -> Tuple[float, List[Dict]]:
223
+ """Per-agent AIOS 6-level structure check."""
224
+ if not agent_path.exists():
225
+ return 0, [{"id": "missing", "name": f"{agent_path.name} missing", "pass": False, "severity": "BLOCKING"}]
226
+
227
+ content = agent_path.read_text()
228
+ checks = []
229
+
230
+ for section in REQUIRED_AGENT_SECTIONS:
231
+ present = section in content
232
+ checks.append({
233
+ "id": f"agent.{section}",
234
+ "name": f"{agent_path.name} has {section}",
235
+ "pass": present,
236
+ "severity": "QUALITY",
237
+ })
238
+
239
+ # Additional quality checks
240
+ has_smoke_3 = content.count("test_") >= 3
241
+ checks.append({"id": "agent.smoke_tests_3", "name": f"{agent_path.name} 3 smoke tests", "pass": has_smoke_3, "severity": "QUALITY"})
242
+
243
+ has_examples_3 = len(re.findall(r"- input:", content)) >= 3
244
+ checks.append({"id": "agent.examples_3", "name": f"{agent_path.name} 3 output examples", "pass": has_examples_3, "severity": "QUALITY"})
245
+
246
+ has_source_traces = "[SOURCE:" in content or "[Deming" in content or "[Juran" in content or "[English" in content or "[Dodds" in content or "[Schumacher" in content or "[Baldwin" in content or "[Mazria" in content or "signature]" in content
247
+ checks.append({"id": "agent.source_traces", "name": f"{agent_path.name} has [SOURCE:] traces", "pass": has_source_traces, "severity": "QUALITY"})
248
+
249
+ total = len(checks)
250
+ passed = sum(1 for c in checks if c["pass"])
251
+ score = (passed / total) * 10
252
+ return score, checks
253
+
254
+
255
+ def check_tier_3() -> Tuple[float, List[Dict]]:
256
+ agents = list((SQUAD_DIR / "agents").glob("*.md"))
257
+ all_checks = []
258
+ scores = []
259
+
260
+ for agent_path in agents:
261
+ score, checks = check_agent_quality(agent_path)
262
+ scores.append(score)
263
+ all_checks.extend(checks)
264
+
265
+ avg_score = sum(scores) / len(scores) if scores else 0
266
+ return avg_score, all_checks
267
+
268
+
269
+ # ============================================================================
270
+ # TIER 4 · CONTEXTUAL (squad type)
271
+ # ============================================================================
272
+
273
+ def check_tier_4() -> Tuple[float, List[Dict]]:
274
+ """Detect squad type · architettura-progetto = Hybrid (process + heuristics)"""
275
+ checks = []
276
+ config_text = (SQUAD_DIR / "config.yaml").read_text() if (SQUAD_DIR / "config.yaml").exists() else ""
277
+
278
+ has_hub_spoke = "hub_and_spoke" in config_text
279
+ checks.append({"id": "4.1.hub_spoke", "name": "Hub-and-spoke topology", "pass": has_hub_spoke, "severity": "QUALITY"})
280
+
281
+ has_executor_types = "executor_types:" in config_text
282
+ checks.append({"id": "4.2.executor_types", "name": "Executor types defined", "pass": has_executor_types, "severity": "QUALITY"})
283
+
284
+ has_pattern_lib = "pattern_library:" in config_text
285
+ checks.append({"id": "4.3.pattern_library", "name": "Pattern library defined", "pass": has_pattern_lib, "severity": "QUALITY"})
286
+
287
+ has_quality_gates = "quality_gates:" in config_text
288
+ checks.append({"id": "4.4.quality_gates", "name": "Quality gates referenced", "pass": has_quality_gates, "severity": "QUALITY"})
289
+
290
+ # Mind clones traceable
291
+ mind_clones = ["quality-misure", "quality-normativa", "quality-dati", "quality-output", "concept-designer", "bim-engineer", "energy-prelim"]
292
+ clones_with_source = 0
293
+ for c in mind_clones:
294
+ cpath = SQUAD_DIR / "agents" / f"{c}.md"
295
+ if cpath.exists() and "based_on:" in cpath.read_text():
296
+ clones_with_source += 1
297
+ checks.append({
298
+ "id": "4.5.mind_clones",
299
+ "name": f"Mind clones documented ({clones_with_source}/7)",
300
+ "pass": clones_with_source >= 7,
301
+ "severity": "QUALITY",
302
+ })
303
+
304
+ total = len(checks)
305
+ passed = sum(1 for c in checks if c["pass"])
306
+ score = (passed / total) * 10
307
+ return score, checks
308
+
309
+
310
+ # ============================================================================
311
+ # RUNNER
312
+ # ============================================================================
313
+
314
+ def main():
315
+ print("\n" + "=" * 70)
316
+ print(" Squad Validation · architettura-progetto")
317
+ print(" Applying squad-checklist v3.1 · AIOS Standard")
318
+ print("=" * 70 + "\n")
319
+
320
+ t1_score, t1_checks = check_tier_1()
321
+ t2_score, t2_checks = check_tier_2()
322
+ t3_score, t3_checks = check_tier_3()
323
+ t4_score, t4_checks = check_tier_4()
324
+
325
+ # Final score (weighted: T3 × 0.5 + T1 × 0.2 + T2 × 0.2 + T4 × 0.1)
326
+ final = (t3_score * 0.5) + (t1_score * 0.2) + (t2_score * 0.2) + (t4_score * 0.1)
327
+
328
+ print(f"Tier 1 · Structure: {t1_score:5.2f}/10 ({sum(1 for c in t1_checks if c['pass'])}/{len(t1_checks)} checks)")
329
+ print(f"Tier 2 · Coverage: {t2_score:5.2f}/10 ({sum(1 for c in t2_checks if c['pass'])}/{len(t2_checks)} checks)")
330
+ print(f"Tier 3 · Quality: {t3_score:5.2f}/10 (per-agent average · {len(list((SQUAD_DIR/'agents').glob('*.md')))} agents)")
331
+ print(f"Tier 4 · Contextual: {t4_score:5.2f}/10 ({sum(1 for c in t4_checks if c['pass'])}/{len(t4_checks)} checks)")
332
+ print(f"")
333
+ print(f" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
334
+ print(f" FINAL SCORE: {final:5.2f}/10")
335
+ print(f" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
336
+ print(f"")
337
+
338
+ if final >= 9.0:
339
+ print(" Verdict: EXCELLENCE 🏆")
340
+ elif final >= 7.0:
341
+ print(" Verdict: PASS ✓")
342
+ else:
343
+ print(" Verdict: FAIL ✗")
344
+ print("")
345
+
346
+ if VERBOSE:
347
+ print("\n--- Tier 1 · Structure (BLOCKING) ---")
348
+ for c in t1_checks:
349
+ mark = "✓" if c["pass"] else "✗"
350
+ print(f" {mark} [{c['id']}] {c['name']}")
351
+
352
+ print("\n--- Tier 2 · Coverage (BLOCKING) ---")
353
+ for c in t2_checks:
354
+ mark = "✓" if c["pass"] else "✗"
355
+ print(f" {mark} [{c['id']}] {c['name']}")
356
+
357
+ # Tier 3 · group by agent
358
+ print("\n--- Tier 3 · Quality (per agent) ---")
359
+ agents = list((SQUAD_DIR / "agents").glob("*.md"))
360
+ for agent_path in agents:
361
+ score, checks = check_agent_quality(agent_path)
362
+ print(f" {agent_path.stem}: {score:.1f}/10 ({sum(1 for c in checks if c['pass'])}/{len(checks)})")
363
+
364
+ print("\n--- Tier 4 · Contextual ---")
365
+ for c in t4_checks:
366
+ mark = "✓" if c["pass"] else "✗"
367
+ print(f" {mark} [{c['id']}] {c['name']}")
368
+
369
+ # Failed only
370
+ all_failed = [c for c in (t1_checks + t2_checks + t4_checks) if not c["pass"]]
371
+ if all_failed:
372
+ print(f"\n--- {len(all_failed)} FAILED CHECKS ---")
373
+ for c in all_failed:
374
+ print(f" ✗ [{c['id']}] {c['name']}")
375
+
376
+ if final >= 7.0:
377
+ sys.exit(0)
378
+ else:
379
+ sys.exit(1)
380
+
381
+
382
+ if __name__ == "__main__":
383
+ main()
@@ -0,0 +1,146 @@
1
+ # Task: audit-input
2
+
3
+ > **Pattern:** AP-TP-001 (Atomic Task Anatomy · 8 mandatory fields)
4
+ > **Executor:** @auditor-input (functional · Tier 0)
5
+ > **Squad:** architettura-progetto
6
+
7
+ ---
8
+
9
+ ## task_name
10
+ Audit input completeness · 18-item checklist gate
11
+
12
+ ## status
13
+ ACTIVE · stable · v1.0
14
+
15
+ ## responsible_executor
16
+ - **agent**: @auditor-input
17
+ - **executor_type**: AP-EP-002 (Agent Executor)
18
+ - **fallback**: @progetto-chief escalation if agent timeout
19
+
20
+ ## execution_type
21
+ **Synchronous · Pre-flight gate** · Blocks workflow if FAIL
22
+
23
+ ## input
24
+ ```yaml
25
+ required:
26
+ brief_path: "Path to briefing-cliente.md"
27
+ dwg_path: "Path to stato-attuale.dxf"
28
+ photos_dir: "Directory with stato attuale photos"
29
+ cliente_data:
30
+ nome: string
31
+ cognome: string
32
+ codice_fiscale: string # 16-char Italian CF
33
+ email: string
34
+ telefono: string
35
+ studio_data:
36
+ architetto: string
37
+ n_ordine: string
38
+ piva: string
39
+ pec: string
40
+ valore_opera: number # EUR
41
+ optional:
42
+ visura_path: "Catastal record PDF"
43
+ ```
44
+
45
+ ## output
46
+ ```yaml
47
+ file: "input_validation.json"
48
+ schema:
49
+ validation_id: uuid
50
+ status: "PASS | FAIL"
51
+ missing: array[string] # item ids that failed
52
+ warnings: array[string]
53
+ extracted_data:
54
+ client_name: string
55
+ client_cf_primary: string
56
+ address: string
57
+ geocoded:
58
+ lat: number
59
+ lon: number
60
+ comune: string
61
+ postcode: string
62
+ project_value: number
63
+ studio: object
64
+ ```
65
+
66
+ ## action_items
67
+ 1. **A1** Read briefing-cliente.md · verify char count ≥500
68
+ 2. **A2** Verify briefing has ≥3 of 12 sezioni UNI 11337
69
+ 3. **A3** Extract budget numerico (regex /€\s?[\d,.]+/)
70
+ 4. **A4** Extract timeline indicators
71
+ 5. **A5** Verify briefing has cliente expectations section
72
+ 6. **B1** Run `ezdxf.readfile(dwg)` · entities count > 0
73
+ 7. **B2** Iterate photos · verify ≥3 JPG/PNG · resolution ≥800x600
74
+ 8. **B3** Check visura PDF if present
75
+ 9. **C1** Verify cliente nome+cognome non-empty
76
+ 10. **C2-C3** Verify CF format /^[A-Z0-9]{16}$/ · Italian checksum algorithm
77
+ 11. **C4** Verify email regex
78
+ 12. **C5** Mapbox API call · geocode address · features.length > 0
79
+ 13. **D1** Verify architetto nome
80
+ 14. **D2** Verify n. Ordine (digit string)
81
+ 15. **D3** Verify P.IVA studio (11-digit + checksum)
82
+ 16. **D4** Verify PEC format
83
+ 17. **E1** Verify valore_opera numeric > 0
84
+ 18. **Final** Build extracted_data + verdict
85
+
86
+ ## acceptance_criteria
87
+ - [ ] All 18 items checked (no skipping)
88
+ - [ ] If 7/7 critici (A1, B1, B2, C2-C3, C5, D3, E1) PASS → status=PASS
89
+ - [ ] If ANY critico FAIL → status=FAIL · workflow halts
90
+ - [ ] JSON output valid against schema
91
+ - [ ] If PASS · extracted_data populated for downstream
92
+ - [ ] If FAIL · missing[] lists exact item_ids
93
+ - [ ] Verdict announcement returned to @progetto-chief
94
+
95
+ ## dependencies
96
+ - **APIs:**
97
+ - Mapbox Geocoding API (item C5 critical)
98
+ - Italian CF checksum library (item C2-C3)
99
+ - **Files:**
100
+ - All input files (brief, dwg, photos, etc.)
101
+ - **Agents:**
102
+ - @progetto-chief (invoker · returns to)
103
+
104
+ ## templates
105
+ - `~/projects/{slug}/01-input/` (expected input directory structure)
106
+ - input_validation.json schema (above)
107
+
108
+ ## quality_gate
109
+ - **Gate:** QG-AP-1.1 (Input Validation Gate)
110
+ - **Threshold:** 7/7 CRITICI must PASS (A1, B1, B2, C2-C3, C5, D3, E1)
111
+ - **Reviewer:** @progetto-chief on receipt
112
+
113
+ ## handoff
114
+ - **From:** @progetto-chief (outbound card)
115
+ - **To:** @progetto-chief (inbound card with validation_id + verdict)
116
+ - **Required announcement:** "Ritorno al @progetto-chief. Audit completato — verdict: {PASS|FAIL}."
117
+
118
+ ## veto_conditions
119
+ - Briefing < 500 chars → FAIL (A1)
120
+ - DWG corrupted (ezdxf raises) → FAIL (B1)
121
+ - Photos < 3 → FAIL (B2)
122
+ - Address not geocodable → FAIL (C5)
123
+ - Invalid CF → FAIL (C2-C3)
124
+ - Invalid P.IVA → FAIL (D3)
125
+ - Valore opera ≤0 → FAIL (E1)
126
+
127
+ ## estimated_time
128
+ **60-90 seconds** (Mapbox API call dominant)
129
+
130
+ ## output_example
131
+ ```json
132
+ {
133
+ "validation_id": "v_a7f4b2",
134
+ "status": "PASS",
135
+ "missing": [],
136
+ "warnings": [],
137
+ "extracted_data": {
138
+ "client_name": "Marco Rossini & Giulia Bianchi",
139
+ "client_cf_primary": "RSSMRC83A15F205X",
140
+ "address": "Via Fiori Chiari 17, 20121 Milano",
141
+ "geocoded": {"lat": 45.471823, "lon": 9.184828, "comune": "Milano", "postcode": "20121"},
142
+ "project_value": 180000,
143
+ "studio": {"nome": "Pablo Ruan", "ordine_n": "XXXX", "piva": "01234567890"}
144
+ }
145
+ }
146
+ ```
@@ -0,0 +1,105 @@
1
+ # Task: compute-metric
2
+
3
+ > **Pattern:** AP-TP-001
4
+ > **Executor:** @computo-engineer (functional · Tier 1 · critical: dati_zero_tolerance)
5
+ > **Squad:** architettura-progetto
6
+
7
+ ---
8
+
9
+ ## task_name
10
+ Compute metric estimative · Prezzario Lombardia 2025 · IVA 10%
11
+
12
+ ## status
13
+ ACTIVE · stable · v1.0
14
+
15
+ ## responsible_executor
16
+ - **agent**: @computo-engineer
17
+ - **executor_type**: AP-EP-002 (Agent + Python worker)
18
+ - **worker**: xlsxwriter, openpyxl, pdfplumber
19
+
20
+ ## execution_type
21
+ **Synchronous after BIM** · Output feeds @capitolato-writer + @quality-dati
22
+
23
+ ## input
24
+ ```yaml
25
+ required:
26
+ quantitativi_json: "From @bim-engineer"
27
+ prezzario_path: "data/prezzario-lombardia-sample.json"
28
+ optional:
29
+ dei_plus_subscription: bool # fallback per voci mancanti
30
+ ec3_for_epd: bool # for EPD tracking
31
+ ```
32
+
33
+ ## output
34
+ ```yaml
35
+ files:
36
+ - "05-impresa/computo-metrico.xlsx"
37
+ - "05-impresa/computo-metrico.pdf"
38
+ - "05-impresa/quadro-economico.pdf"
39
+ - "05-impresa/lista-materiali-EPDs.xlsx"
40
+ metrics:
41
+ voci_count: number
42
+ totale_lavori_eur: number
43
+ iva_eur: number
44
+ totale_iva_inclusa: number
45
+ cam_compliance_percent: number
46
+ ```
47
+
48
+ ## action_items
49
+ 1. Read quantitativi.json (volumi muri, aree pavimenti, count, etc.)
50
+ 2. Match each quantity to Prezzario Lombardia voce (semantic + code)
51
+ 3. For unmatched: query DEI fallback OR mark [VERIFY-CUSTOM]
52
+ 4. Compute Q × prezzo_unitario per voce
53
+ 5. Aggregate per categoria DEI (demolizioni, murature, impianti, etc.)
54
+ 6. Apply IVA 10% (ristrutturazione · DPR 633/72)
55
+ 7. Build quadro economico (lavori + onorari + oneri + IVA + imprevisti)
56
+ 8. Track CAM 2025 compliance per voce (target ≥80%)
57
+ 9. Cross-reference EC3 for EPDs in materials list
58
+ 10. Generate xlsx with formulas (SUM, IVA calc)
59
+ 11. Generate PDF version
60
+ 12. Self-verify: sum quantitativi IFC = sum computo (diff <2%)
61
+
62
+ ## acceptance_criteria
63
+ - [ ] ≥100 voci with codice Prezzario or [VERIFY-CUSTOM]
64
+ - [ ] Aggregazione per categoria DEI completa
65
+ - [ ] IVA 10% applied (NOT 22%)
66
+ - [ ] Quadro economico generato
67
+ - [ ] Cross-check IFC quantitativi · diff <2%
68
+ - [ ] CAM 2025 tracking ≥80% target
69
+ - [ ] xlsx with working formulas
70
+ - [ ] PDF readable
71
+
72
+ ## dependencies
73
+ - **Libraries:**
74
+ - xlsxwriter (xlsx generation)
75
+ - openpyxl (template editing)
76
+ - pdfplumber (parse Prezzario PDF if needed)
77
+ - **APIs:**
78
+ - DEI PLUS (optional · fallback)
79
+ - EC3 Building Transparency (EPDs)
80
+ - Gemini 3.1 Pro (gemini-3.1-pro-preview) (semantic mapping descrizione → voce)
81
+ - **Data:**
82
+ - Prezzario Regione Lombardia 2025 cached JSON
83
+ - quantitativi.json (@bim-engineer)
84
+
85
+ ## quality_gate
86
+ - **Gate:** QG-AP-1.4 (Cross-Doc Data Gate)
87
+ - **Reviewer:** @quality-dati
88
+ - **Threshold:** Cross-check IFC = computo · diff <2%
89
+
90
+ ## handoff
91
+ - **From:** @progetto-chief
92
+ - **To:** @progetto-chief → @capitolato-writer (uses computo) + @quality-dati (verifies)
93
+ - **Required announcement:** "Ritorno al @progetto-chief. Computo · {n} voci · totale € {X}."
94
+
95
+ ## veto_conditions
96
+ - Totale computo ≠ sum voci > 0.5% → halt
97
+ - IVA 22% applied → halt (errata · ristrutturazione = 10%)
98
+ - Voci senza prezzo → halt
99
+ - Diff vs IFC quantitativi >2% → halt + flag @quality-dati REJECT predicted
100
+
101
+ ## estimated_time
102
+ **45-60 seconds**
103
+
104
+ ## output_example
105
+ See `@computo-engineer.md` output_examples · 124 voci · €162,327 · CAM 87% · cross-check pass.