graphitect 0.2.0__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.
- graphify/__init__.py +30 -0
- graphify/__main__.py +757 -0
- graphify/_minhash.py +107 -0
- graphify/affected.py +318 -0
- graphify/always_on/agents-md.md +12 -0
- graphify/always_on/antigravity-rules.md +14 -0
- graphify/always_on/claude-md.md +9 -0
- graphify/always_on/gemini-md.md +9 -0
- graphify/always_on/kiro-steering.md +5 -0
- graphify/always_on/vscode-instructions.md +17 -0
- graphify/analyze.py +769 -0
- graphify/benchmark.py +152 -0
- graphify/build.py +2300 -0
- graphify/cache.py +1746 -0
- graphify/callflow_html.py +2051 -0
- graphify/cargo_introspect.py +109 -0
- graphify/cli.py +4745 -0
- graphify/cluster.py +409 -0
- graphify/command-kilo.md +15 -0
- graphify/cross_repo_calls.py +216 -0
- graphify/cross_repo_types.py +75 -0
- graphify/csharp_dispatch.py +154 -0
- graphify/dedup.py +1213 -0
- graphify/detect.py +2566 -0
- graphify/diagnostics.py +406 -0
- graphify/export.py +1349 -0
- graphify/exporters/__init__.py +1 -0
- graphify/exporters/base.py +14 -0
- graphify/exporters/graphdb.py +173 -0
- graphify/exporters/html.py +637 -0
- graphify/extract.py +7856 -0
- graphify/extractors/MIGRATION.md +107 -0
- graphify/extractors/__init__.py +66 -0
- graphify/extractors/apex.py +215 -0
- graphify/extractors/base.py +85 -0
- graphify/extractors/bash.py +579 -0
- graphify/extractors/blade.py +53 -0
- graphify/extractors/commonlisp.py +540 -0
- graphify/extractors/csharp.py +448 -0
- graphify/extractors/dart.py +564 -0
- graphify/extractors/dm.py +494 -0
- graphify/extractors/elixir.py +241 -0
- graphify/extractors/engine.py +6509 -0
- graphify/extractors/fortran.py +311 -0
- graphify/extractors/go.py +527 -0
- graphify/extractors/json_config.py +240 -0
- graphify/extractors/julia.py +289 -0
- graphify/extractors/markdown.py +408 -0
- graphify/extractors/models.py +131 -0
- graphify/extractors/objc.py +566 -0
- graphify/extractors/ocaml.py +289 -0
- graphify/extractors/pascal.py +688 -0
- graphify/extractors/pascal_forms.py +196 -0
- graphify/extractors/powershell.py +522 -0
- graphify/extractors/razor.py +192 -0
- graphify/extractors/resolution.py +3584 -0
- graphify/extractors/robot.py +296 -0
- graphify/extractors/rust.py +470 -0
- graphify/extractors/sln.py +92 -0
- graphify/extractors/sql.py +720 -0
- graphify/extractors/terraform.py +181 -0
- graphify/extractors/verilog.py +329 -0
- graphify/extractors/zig.py +181 -0
- graphify/file_slice.py +246 -0
- graphify/global_graph.py +194 -0
- graphify/google_workspace.py +237 -0
- graphify/hooks.py +933 -0
- graphify/ids.py +93 -0
- graphify/ingest.py +358 -0
- graphify/install.py +2366 -0
- graphify/llm.py +3544 -0
- graphify/manifest.py +4 -0
- graphify/manifest_ingest.py +311 -0
- graphify/mcp_ingest.py +386 -0
- graphify/multigraph_compat.py +212 -0
- graphify/pascal_resolution.py +129 -0
- graphify/paths.py +436 -0
- graphify/pg_introspect.py +165 -0
- graphify/prs.py +770 -0
- graphify/querylog.py +80 -0
- graphify/reflect.py +882 -0
- graphify/report.py +346 -0
- graphify/resolver_registry.py +85 -0
- graphify/ruby_resolution.py +242 -0
- graphify/scip_ingest.py +363 -0
- graphify/security.py +460 -0
- graphify/semantic_cleanup.py +336 -0
- graphify/serve.py +2608 -0
- graphify/skill-agents.md +710 -0
- graphify/skill-aider.md +1283 -0
- graphify/skill-amp.md +710 -0
- graphify/skill-claw.md +713 -0
- graphify/skill-codex.md +710 -0
- graphify/skill-copilot.md +713 -0
- graphify/skill-devin.md +1410 -0
- graphify/skill-droid.md +710 -0
- graphify/skill-kilo.md +722 -0
- graphify/skill-kiro.md +713 -0
- graphify/skill-opencode.md +705 -0
- graphify/skill-pi.md +713 -0
- graphify/skill-trae.md +711 -0
- graphify/skill-vscode.md +709 -0
- graphify/skill-windows.md +755 -0
- graphify/skill.md +713 -0
- graphify/skills/agents/references/add-watch.md +56 -0
- graphify/skills/agents/references/exports.md +87 -0
- graphify/skills/agents/references/extraction-spec.md +70 -0
- graphify/skills/agents/references/github-and-merge.md +46 -0
- graphify/skills/agents/references/hooks.md +33 -0
- graphify/skills/agents/references/query.md +311 -0
- graphify/skills/agents/references/transcribe.md +52 -0
- graphify/skills/agents/references/update.md +210 -0
- graphify/skills/amp/references/add-watch.md +56 -0
- graphify/skills/amp/references/exports.md +87 -0
- graphify/skills/amp/references/extraction-spec.md +70 -0
- graphify/skills/amp/references/github-and-merge.md +46 -0
- graphify/skills/amp/references/hooks.md +33 -0
- graphify/skills/amp/references/query.md +311 -0
- graphify/skills/amp/references/transcribe.md +52 -0
- graphify/skills/amp/references/update.md +210 -0
- graphify/skills/claude/references/add-watch.md +56 -0
- graphify/skills/claude/references/exports.md +87 -0
- graphify/skills/claude/references/extraction-spec.md +70 -0
- graphify/skills/claude/references/github-and-merge.md +46 -0
- graphify/skills/claude/references/hooks.md +33 -0
- graphify/skills/claude/references/query.md +311 -0
- graphify/skills/claude/references/transcribe.md +52 -0
- graphify/skills/claude/references/update.md +210 -0
- graphify/skills/claw/references/add-watch.md +56 -0
- graphify/skills/claw/references/exports.md +87 -0
- graphify/skills/claw/references/extraction-spec.md +31 -0
- graphify/skills/claw/references/github-and-merge.md +46 -0
- graphify/skills/claw/references/hooks.md +33 -0
- graphify/skills/claw/references/query.md +311 -0
- graphify/skills/claw/references/transcribe.md +52 -0
- graphify/skills/claw/references/update.md +210 -0
- graphify/skills/codex/references/add-watch.md +56 -0
- graphify/skills/codex/references/exports.md +87 -0
- graphify/skills/codex/references/extraction-spec.md +31 -0
- graphify/skills/codex/references/github-and-merge.md +46 -0
- graphify/skills/codex/references/hooks.md +33 -0
- graphify/skills/codex/references/query.md +311 -0
- graphify/skills/codex/references/transcribe.md +52 -0
- graphify/skills/codex/references/update.md +210 -0
- graphify/skills/copilot/references/add-watch.md +56 -0
- graphify/skills/copilot/references/exports.md +87 -0
- graphify/skills/copilot/references/extraction-spec.md +70 -0
- graphify/skills/copilot/references/github-and-merge.md +46 -0
- graphify/skills/copilot/references/hooks.md +33 -0
- graphify/skills/copilot/references/query.md +311 -0
- graphify/skills/copilot/references/transcribe.md +52 -0
- graphify/skills/copilot/references/update.md +210 -0
- graphify/skills/droid/references/add-watch.md +56 -0
- graphify/skills/droid/references/exports.md +87 -0
- graphify/skills/droid/references/extraction-spec.md +70 -0
- graphify/skills/droid/references/github-and-merge.md +46 -0
- graphify/skills/droid/references/hooks.md +33 -0
- graphify/skills/droid/references/query.md +311 -0
- graphify/skills/droid/references/transcribe.md +52 -0
- graphify/skills/droid/references/update.md +210 -0
- graphify/skills/kilo/references/add-watch.md +56 -0
- graphify/skills/kilo/references/exports.md +87 -0
- graphify/skills/kilo/references/extraction-spec.md +70 -0
- graphify/skills/kilo/references/github-and-merge.md +46 -0
- graphify/skills/kilo/references/hooks.md +33 -0
- graphify/skills/kilo/references/query.md +311 -0
- graphify/skills/kilo/references/transcribe.md +52 -0
- graphify/skills/kilo/references/update.md +210 -0
- graphify/skills/kiro/references/add-watch.md +56 -0
- graphify/skills/kiro/references/exports.md +87 -0
- graphify/skills/kiro/references/extraction-spec.md +31 -0
- graphify/skills/kiro/references/github-and-merge.md +46 -0
- graphify/skills/kiro/references/hooks.md +33 -0
- graphify/skills/kiro/references/query.md +311 -0
- graphify/skills/kiro/references/transcribe.md +52 -0
- graphify/skills/kiro/references/update.md +210 -0
- graphify/skills/opencode/references/add-watch.md +56 -0
- graphify/skills/opencode/references/exports.md +87 -0
- graphify/skills/opencode/references/extraction-spec.md +70 -0
- graphify/skills/opencode/references/github-and-merge.md +46 -0
- graphify/skills/opencode/references/hooks.md +33 -0
- graphify/skills/opencode/references/query.md +311 -0
- graphify/skills/opencode/references/transcribe.md +52 -0
- graphify/skills/opencode/references/update.md +210 -0
- graphify/skills/pi/references/add-watch.md +56 -0
- graphify/skills/pi/references/exports.md +87 -0
- graphify/skills/pi/references/extraction-spec.md +31 -0
- graphify/skills/pi/references/github-and-merge.md +46 -0
- graphify/skills/pi/references/hooks.md +33 -0
- graphify/skills/pi/references/query.md +311 -0
- graphify/skills/pi/references/transcribe.md +52 -0
- graphify/skills/pi/references/update.md +210 -0
- graphify/skills/trae/references/add-watch.md +56 -0
- graphify/skills/trae/references/exports.md +87 -0
- graphify/skills/trae/references/extraction-spec.md +70 -0
- graphify/skills/trae/references/github-and-merge.md +46 -0
- graphify/skills/trae/references/hooks.md +35 -0
- graphify/skills/trae/references/query.md +311 -0
- graphify/skills/trae/references/transcribe.md +52 -0
- graphify/skills/trae/references/update.md +210 -0
- graphify/skills/vscode/references/add-watch.md +56 -0
- graphify/skills/vscode/references/exports.md +87 -0
- graphify/skills/vscode/references/extraction-spec.md +70 -0
- graphify/skills/vscode/references/github-and-merge.md +46 -0
- graphify/skills/vscode/references/hooks.md +33 -0
- graphify/skills/vscode/references/query.md +311 -0
- graphify/skills/vscode/references/transcribe.md +52 -0
- graphify/skills/vscode/references/update.md +210 -0
- graphify/skills/windows/references/add-watch.md +56 -0
- graphify/skills/windows/references/exports.md +87 -0
- graphify/skills/windows/references/extraction-spec.md +70 -0
- graphify/skills/windows/references/github-and-merge.md +46 -0
- graphify/skills/windows/references/hooks.md +33 -0
- graphify/skills/windows/references/query.md +311 -0
- graphify/skills/windows/references/transcribe.md +52 -0
- graphify/skills/windows/references/update.md +210 -0
- graphify/symbol_resolution.py +556 -0
- graphify/transcribe.py +186 -0
- graphify/tree_html.py +603 -0
- graphify/validate.py +95 -0
- graphify/watch.py +2280 -0
- graphify/wiki.py +405 -0
- graphitect/__init__.py +28 -0
- graphitect/__main__.py +4 -0
- graphitect/_vendor/__init__.py +2 -0
- graphitect/_vendor/archify/LICENSE +22 -0
- graphitect/_vendor/archify/SKILL.md +137 -0
- graphitect/_vendor/archify/THIRD_PARTY_NOTICES.md +69 -0
- graphitect/_vendor/archify/assets/JetBrainsMono-OFL.txt +93 -0
- graphitect/_vendor/archify/assets/template.html +14935 -0
- graphitect/_vendor/archify/bin/archify.mjs +2091 -0
- graphitect/_vendor/archify/bin/open-artifact.mjs +86 -0
- graphitect/_vendor/archify/bin/preview.mjs +653 -0
- graphitect/_vendor/archify/bin/visual-check.mjs +829 -0
- graphitect/_vendor/archify/brand-marks/README.md +31 -0
- graphitect/_vendor/archify/brand-marks/catalog.json +131 -0
- graphitect/_vendor/archify/delta/architecture-delta.mjs +1221 -0
- graphitect/_vendor/archify/examples/agent-run.lifecycle.json +60 -0
- graphitect/_vendor/archify/examples/agent-tool-call.workflow.json +94 -0
- graphitect/_vendor/archify/examples/async-job-roundtrip.sequence.json +61 -0
- graphitect/_vendor/archify/examples/brand-aware-delivery.architecture.json +47 -0
- graphitect/_vendor/archify/examples/cache-miss-request.sequence.json +82 -0
- graphitect/_vendor/archify/examples/checkout-platform.base.architecture.json +31 -0
- graphitect/_vendor/archify/examples/checkout-platform.head.architecture.json +31 -0
- graphitect/_vendor/archify/examples/dataflow-product-analytics.html +15045 -0
- graphitect/_vendor/archify/examples/deployment-release.lifecycle.json +49 -0
- graphitect/_vendor/archify/examples/event-stream.dataflow.json +57 -0
- graphitect/_vendor/archify/examples/incident-response.workflow.json +64 -0
- graphitect/_vendor/archify/examples/lifecycle-agent-run.html +14980 -0
- graphitect/_vendor/archify/examples/product-analytics.dataflow.json +76 -0
- graphitect/_vendor/archify/examples/production-deployment.architecture.json +71 -0
- graphitect/_vendor/archify/examples/release-delivery.workflow.json +62 -0
- graphitect/_vendor/archify/examples/sequence-cache-miss-request.html +15060 -0
- graphitect/_vendor/archify/examples/web-app-rendered.html +15009 -0
- graphitect/_vendor/archify/examples/web-app.architecture.json +46 -0
- graphitect/_vendor/archify/examples/workflow-agent-tool-call-rendered.html +15051 -0
- graphitect/_vendor/archify/migrations/workflow-v2.mjs +279 -0
- graphitect/_vendor/archify/package-lock.json +149 -0
- graphitect/_vendor/archify/package.json +39 -0
- graphitect/_vendor/archify/recipes/scenarios.mjs +391 -0
- graphitect/_vendor/archify/references/authoring-contract.md +243 -0
- graphitect/_vendor/archify/references/brand-marks.md +65 -0
- graphitect/_vendor/archify/references/delivery-contract.md +120 -0
- graphitect/_vendor/archify/references/viewer-runtime.md +45 -0
- graphitect/_vendor/archify/renderers/architecture/grid.mjs +62 -0
- graphitect/_vendor/archify/renderers/architecture/render-architecture.mjs +1078 -0
- graphitect/_vendor/archify/renderers/dataflow/README.md +104 -0
- graphitect/_vendor/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- graphitect/_vendor/archify/renderers/lifecycle/README.md +115 -0
- graphitect/_vendor/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- graphitect/_vendor/archify/renderers/sequence/README.md +114 -0
- graphitect/_vendor/archify/renderers/sequence/render-sequence.mjs +464 -0
- graphitect/_vendor/archify/renderers/shared/brand-marks.mjs +563 -0
- graphitect/_vendor/archify/renderers/shared/cli.mjs +218 -0
- graphitect/_vendor/archify/renderers/shared/desktop-readability.mjs +26 -0
- graphitect/_vendor/archify/renderers/shared/diagnostics.mjs +127 -0
- graphitect/_vendor/archify/renderers/shared/engineering-profiles.mjs +157 -0
- graphitect/_vendor/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- graphitect/_vendor/archify/renderers/shared/generated-validators.mjs +13 -0
- graphitect/_vendor/archify/renderers/shared/geometry.mjs +1423 -0
- graphitect/_vendor/archify/renderers/shared/i18n.mjs +595 -0
- graphitect/_vendor/archify/renderers/shared/layout-report.mjs +40 -0
- graphitect/_vendor/archify/renderers/shared/legend.mjs +217 -0
- graphitect/_vendor/archify/renderers/shared/output-path.mjs +340 -0
- graphitect/_vendor/archify/renderers/shared/repository-evidence.mjs +238 -0
- graphitect/_vendor/archify/renderers/shared/repository-location.mjs +58 -0
- graphitect/_vendor/archify/renderers/shared/text-fit.mjs +49 -0
- graphitect/_vendor/archify/renderers/shared/utils.mjs +232 -0
- graphitect/_vendor/archify/renderers/shared/validator.mjs +86 -0
- graphitect/_vendor/archify/renderers/workflow/README.md +223 -0
- graphitect/_vendor/archify/renderers/workflow/render-workflow.mjs +35 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- graphitect/_vendor/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- graphitect/_vendor/archify/schemas/README.md +211 -0
- graphitect/_vendor/archify/schemas/architecture.schema.json +178 -0
- graphitect/_vendor/archify/schemas/common.schema.json +115 -0
- graphitect/_vendor/archify/schemas/dataflow.schema.json +243 -0
- graphitect/_vendor/archify/schemas/lifecycle.schema.json +266 -0
- graphitect/_vendor/archify/schemas/sequence.schema.json +223 -0
- graphitect/_vendor/archify/schemas/workflow.schema.json +428 -0
- graphitect/_vendor/archify/scripts/check-render-output.mjs +836 -0
- graphitect/_vendor/archify/scripts/check-update.mjs +1667 -0
- graphitect/_vendor/archify/scripts/generate-brand-marks.mjs +141 -0
- graphitect/_vendor/archify/scripts/generate-validators.mjs +66 -0
- graphitect/_vendor/archify/scripts/render-examples.mjs +26 -0
- graphitect/_vendor/archify/scripts/update-contract.mjs +182 -0
- graphitect/_vendor/archify/skill-release.json +10 -0
- graphitect/cli.py +981 -0
- graphitect/deliver/__init__.py +5 -0
- graphitect/deliver/archify_adapter.py +1877 -0
- graphitect/deliver/archify_ir.py +160 -0
- graphitect/deliver/archify_repair.py +135 -0
- graphitect/deliver/doc_compiler.py +916 -0
- graphitect/ground/__init__.py +5 -0
- graphitect/ground/describe_source.py +27 -0
- graphitect/ground/fullread_source.py +56 -0
- graphitect/ground/graphify_source.py +107 -0
- graphitect/models.py +118 -0
- graphitect/skill/SKILL.md +80 -0
- graphitect/skill/agents/openai.yaml +4 -0
- graphitect/synthesize/__init__.py +5 -0
- graphitect/synthesize/engine.py +281 -0
- graphitect/synthesize/llm_backend.py +331 -0
- graphitect/synthesize/questions.py +139 -0
- graphitect/synthesize/rubric.py +104 -0
- graphitect-0.2.0.dist-info/METADATA +284 -0
- graphitect-0.2.0.dist-info/RECORD +336 -0
- graphitect-0.2.0.dist-info/WHEEL +5 -0
- graphitect-0.2.0.dist-info/entry_points.txt +2 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE +21 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-ARCHIFY-MIT +22 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-APACHE-2.0 +202 -0
- graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-MIT +21 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-ARCHIFY-THIRD-PARTY.md +69 -0
- graphitect-0.2.0.dist-info/licenses/NOTICE-GRAPHIFY +8 -0
- graphitect-0.2.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""archify's real architecture-mode IR - checked directly against
|
|
2
|
+
archify/references/authoring-contract.md and archify/renderers/architecture/grid.mjs
|
|
3
|
+
(not inferred from its README). See plan.md section 04's "Correction" note.
|
|
4
|
+
|
|
5
|
+
Deliberately narrower than archify's full schema: this covers what
|
|
6
|
+
graphitect.deliver.archify_adapter needs to emit (grid-mode architecture
|
|
7
|
+
diagrams), not every mode/field archify's authoring contract supports.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
from pydantic import BaseModel, Field
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _drop_empty_lists(value):
|
|
18
|
+
if isinstance(value, dict):
|
|
19
|
+
return {k: _drop_empty_lists(v) for k, v in value.items() if not (isinstance(v, list) and not v)}
|
|
20
|
+
if isinstance(value, list):
|
|
21
|
+
return [_drop_empty_lists(v) for v in value]
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
ComponentType = Literal[
|
|
25
|
+
"frontend", "backend", "database", "cloud", "security", "messagebus", "external"
|
|
26
|
+
]
|
|
27
|
+
ConnectionVariant = Literal["default", "emphasis", "security", "dashed"]
|
|
28
|
+
ConnectionSide = Literal["left", "right", "top", "bottom"]
|
|
29
|
+
ConnectionRoute = Literal["auto", "straight", "orthogonal-h", "orthogonal-v"]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class GridLayout(BaseModel):
|
|
33
|
+
mode: Literal["grid"] = "grid"
|
|
34
|
+
cols: int = 4
|
|
35
|
+
# archify's own DEFAULT_GRID (renderers/architecture/grid.mjs) is
|
|
36
|
+
# {cellW: 130, gapX: 30, gapY: 40} - fine for short labels, but confirmed
|
|
37
|
+
# live to produce "label wider than component" and label/connection
|
|
38
|
+
# overlap failures on a real graph with longer labels ("Schema
|
|
39
|
+
# Discoverer Agent") and vertically-adjacent connected components.
|
|
40
|
+
# graphitect widens these based on actual label length instead of
|
|
41
|
+
# leaving archify's short-label default in place - see
|
|
42
|
+
# archify_adapter.to_architecture_ir.
|
|
43
|
+
cell_w: int | None = Field(default=None, alias="cellW")
|
|
44
|
+
cell_h: int | None = Field(default=None, alias="cellH")
|
|
45
|
+
gap_x: int | None = Field(default=None, alias="gapX")
|
|
46
|
+
gap_y: int | None = Field(default=None, alias="gapY")
|
|
47
|
+
|
|
48
|
+
model_config = {"populate_by_name": True}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Component(BaseModel):
|
|
52
|
+
id: str
|
|
53
|
+
type: ComponentType
|
|
54
|
+
label: str
|
|
55
|
+
sublabel: str | None = None
|
|
56
|
+
row: int | None = None
|
|
57
|
+
col: int | None = None
|
|
58
|
+
pos: tuple[int, int] | None = None # explicit pixel pos wins over row/col
|
|
59
|
+
size: tuple[int, int] | None = None
|
|
60
|
+
tag: str | None = None
|
|
61
|
+
sources: list[dict] = Field(default_factory=list) # repo-evidence links, see authoring-contract.md
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class Boundary(BaseModel):
|
|
65
|
+
kind: str # e.g. "region", "security-group"
|
|
66
|
+
label: str
|
|
67
|
+
wraps: list[str] # component ids
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class Connection(BaseModel):
|
|
71
|
+
id: str
|
|
72
|
+
from_: str = Field(alias="from")
|
|
73
|
+
to: str
|
|
74
|
+
label: str | None = None
|
|
75
|
+
variant: ConnectionVariant = "default"
|
|
76
|
+
# Routing/label-position overrides - unused by archify_adapter's own
|
|
77
|
+
# deterministic layout (it never sets these), but archify_repair's LLM
|
|
78
|
+
# loop writes them to resolve real validator diagnostics like
|
|
79
|
+
# clean-flow/edge-through-node and layout/constraint (label overlaps
|
|
80
|
+
# component) - see archify/references/authoring-contract.md and
|
|
81
|
+
# archify/schemas/architecture.schema.json's Connection definition,
|
|
82
|
+
# confirmed against the real schema rather than guessed.
|
|
83
|
+
from_side: ConnectionSide | None = Field(default=None, alias="fromSide")
|
|
84
|
+
to_side: ConnectionSide | None = Field(default=None, alias="toSide")
|
|
85
|
+
route: ConnectionRoute | None = None
|
|
86
|
+
via: list[tuple[float, float]] | None = None
|
|
87
|
+
label_at: tuple[float, float] | None = Field(default=None, alias="labelAt")
|
|
88
|
+
label_dx: float | None = Field(default=None, alias="labelDx")
|
|
89
|
+
label_dy: float | None = Field(default=None, alias="labelDy")
|
|
90
|
+
label_segment: int | None = Field(default=None, alias="labelSegment")
|
|
91
|
+
|
|
92
|
+
model_config = {"populate_by_name": True}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Card(BaseModel):
|
|
96
|
+
dot: str
|
|
97
|
+
title: str
|
|
98
|
+
items: list[str]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class GuidedView(BaseModel):
|
|
102
|
+
"""One evidence-grounded chapter in Archify's guided-story rail."""
|
|
103
|
+
|
|
104
|
+
id: str
|
|
105
|
+
label: str
|
|
106
|
+
focus: list[str]
|
|
107
|
+
note: str | None = None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Meta(BaseModel):
|
|
111
|
+
title: str
|
|
112
|
+
# Architecture's schema supports an explicit [width, height] canvas.
|
|
113
|
+
# Graphitect uses it when a safe detour lane extends past the rightmost
|
|
114
|
+
# component; otherwise Archify's automatic component-only bounds clip
|
|
115
|
+
# the valid connection geometry.
|
|
116
|
+
view_box: tuple[int, int] | None = Field(default=None, alias="viewBox")
|
|
117
|
+
# "draft" was never a real archify value - confirmed against its own
|
|
118
|
+
# --help output (`--quality standard|showcase`) - and defaulting to
|
|
119
|
+
# "showcase" made every real multi-node graph fail archify's strict
|
|
120
|
+
# layout validator (labels wider than boxes, overlapping connections,
|
|
121
|
+
# crossing corridors) since graphitect's own grid layout is a simple
|
|
122
|
+
# heuristic, not hand-tuned for showcase-grade spacing rules. "standard"
|
|
123
|
+
# is the honest default until the layout heuristic itself improves
|
|
124
|
+
# (wider boxes for long labels, real connection routing) rather than
|
|
125
|
+
# just asking archify to be less strict about a still-rough auto-layout.
|
|
126
|
+
quality_profile: Literal["standard", "showcase"] = "standard"
|
|
127
|
+
# Story uses Archify's own focused chapter rail and optional trace
|
|
128
|
+
# motion. Overview and Full rollup stay still until the reader asks.
|
|
129
|
+
animation: Literal["none", "trace"] = "none"
|
|
130
|
+
visual_preset: Literal["classic", "signal-flow", "blueprint", "editorial"] | None = None
|
|
131
|
+
views: list[GuidedView] = Field(default_factory=list)
|
|
132
|
+
|
|
133
|
+
model_config = {"populate_by_name": True}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class ArchitectureIR(BaseModel):
|
|
137
|
+
"""A complete document for `archify validate` / `archify deliver`."""
|
|
138
|
+
|
|
139
|
+
schema_version: int = 1
|
|
140
|
+
diagram_type: Literal["architecture"] = "architecture"
|
|
141
|
+
meta: Meta
|
|
142
|
+
layout: GridLayout = Field(default_factory=GridLayout)
|
|
143
|
+
components: list[Component]
|
|
144
|
+
boundaries: list[Boundary] = Field(default_factory=list)
|
|
145
|
+
connections: list[Connection]
|
|
146
|
+
cards: list[Card] = Field(default_factory=list)
|
|
147
|
+
|
|
148
|
+
def model_dump_archify(self) -> dict:
|
|
149
|
+
"""Serialize with `from`/`to` un-aliased back to archify's own field
|
|
150
|
+
names, and with empty-list fields dropped entirely rather than
|
|
151
|
+
emitted as `[]`.
|
|
152
|
+
|
|
153
|
+
Confirmed live (12 Sep 2026) against the real `archify validate`:
|
|
154
|
+
archify's schema requires `minItems: 1` on array fields like a
|
|
155
|
+
component's `sources` whenever the key is present at all - pydantic's
|
|
156
|
+
own `default_factory=list` means every Component always carries
|
|
157
|
+
`sources: []` unless explicitly stripped, which fails validation with
|
|
158
|
+
exactly the field populated by default and never actually used.
|
|
159
|
+
"""
|
|
160
|
+
return _drop_empty_lists(self.model_dump(by_alias=True, exclude_none=True))
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Agent-driven layout repair for archify diagrams.
|
|
2
|
+
|
|
3
|
+
archify_adapter's grid layout is a simple topological-depth heuristic with
|
|
4
|
+
no real connection routing - fine for small graphs, but archify's own
|
|
5
|
+
strict layout validator rejects dense real-world graphs (edges crossing
|
|
6
|
+
unrelated components, labels overlapping boxes - confirmed live against
|
|
7
|
+
FundersAI's real 7,885-node graph). Rather than hand-writing a real graph
|
|
8
|
+
layout/routing algorithm, this asks an LLM to patch the IR's own
|
|
9
|
+
routing/label-position fields (fromSide/toSide/route/via/labelAt/labelDx/
|
|
10
|
+
labelDy/labelSegment) against archify's exact structured diagnostics
|
|
11
|
+
(`archify deliver --json`'s `diagnostics[]`: code, offending connection,
|
|
12
|
+
obstacle, and often a concrete suggested fix), then re-validates - the
|
|
13
|
+
"agent iterates against archify validate feedback" approach flagged as
|
|
14
|
+
future work in plan.md, built as an LLM-backend loop (reusing the same
|
|
15
|
+
Ollama/Anthropic/Gemini backends as Synthesize) so it works from the
|
|
16
|
+
standalone CLI too, not only inside an agent host.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import json
|
|
22
|
+
import subprocess
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
from ..synthesize.llm_backend import LLMBackend
|
|
26
|
+
from .archify_ir import ArchitectureIR
|
|
27
|
+
|
|
28
|
+
_REPAIR_TOOL_NAME = "emit_repaired_architecture_ir"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RepairUnavailableError(RuntimeError):
|
|
32
|
+
"""The optional LLM layout-repair step could not produce a valid IR."""
|
|
33
|
+
|
|
34
|
+
_REPAIR_SYSTEM_PROMPT = """\
|
|
35
|
+
You are repairing an archify architecture diagram IR that failed archify's
|
|
36
|
+
own layout validator. You will be given the current IR (JSON) and the exact
|
|
37
|
+
list of validator diagnostics (each with a code, the offending connection or
|
|
38
|
+
label, the obstacle it collides with, and often a suggested fix).
|
|
39
|
+
|
|
40
|
+
Fix ONLY layout/routing/label-position fields on the existing connections:
|
|
41
|
+
fromSide, toSide, route ("auto"|"straight"|"orthogonal-h"|"orthogonal-v"),
|
|
42
|
+
via (a list of [x, y] waypoints), labelAt ([x, y]), labelDx, labelDy,
|
|
43
|
+
labelSegment. You may also adjust a component's row/col/pos/size if a
|
|
44
|
+
diagnostic specifically calls for moving it. Do NOT change any id, label,
|
|
45
|
+
from/to endpoint, or add/remove any component or connection - every
|
|
46
|
+
diagnostic must be resolved by adjusting geometry, not by altering what the
|
|
47
|
+
diagram says. When a diagnostic's message includes a "Suggested fix" with
|
|
48
|
+
concrete coordinates, prefer using those exact numbers over estimating your
|
|
49
|
+
own.
|
|
50
|
+
|
|
51
|
+
Return the complete corrected IR as a single JSON object matching the given
|
|
52
|
+
schema exactly - every field from the input IR must still be present unless
|
|
53
|
+
you are deliberately changing it.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def run_deliver_json(
|
|
58
|
+
bin_cmd: list[str], ir_path: Path, out_path: Path, *, diagram_type: str = "architecture"
|
|
59
|
+
) -> dict:
|
|
60
|
+
"""Runs `archify deliver ... --json` and returns its parsed JSON report
|
|
61
|
+
regardless of exit code. Confirmed live: archify's own --json output
|
|
62
|
+
carries structured `diagnostics` (code/subject/evidence/supportedFixes)
|
|
63
|
+
on failure - the identical shape `archify validate --json` gives - so a
|
|
64
|
+
single deliver attempt is enough to both try rendering and get repair
|
|
65
|
+
feedback if it fails, with no separate validate call needed.
|
|
66
|
+
|
|
67
|
+
Not underscore-prefixed: also used by archify_adapter.render()'s
|
|
68
|
+
no-LLM-backend mechanical label-fix retry (_apply_suggested_label_fixes)
|
|
69
|
+
- both need the exact same structured diagnostics, LLM-driven repair or
|
|
70
|
+
not.
|
|
71
|
+
"""
|
|
72
|
+
result = subprocess.run(
|
|
73
|
+
[*bin_cmd, "deliver", diagram_type, str(ir_path), str(out_path), "--json"],
|
|
74
|
+
capture_output=True,
|
|
75
|
+
text=True,
|
|
76
|
+
encoding="utf-8",
|
|
77
|
+
errors="replace",
|
|
78
|
+
check=False,
|
|
79
|
+
)
|
|
80
|
+
try:
|
|
81
|
+
return json.loads(result.stdout)
|
|
82
|
+
except json.JSONDecodeError as exc:
|
|
83
|
+
raise RuntimeError(
|
|
84
|
+
f"archify deliver --json produced non-JSON output (exit {result.returncode}): "
|
|
85
|
+
f"stdout={result.stdout[:500]!r} stderr={result.stderr[:500]!r}"
|
|
86
|
+
) from exc
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def repair_and_deliver(
|
|
90
|
+
ir: ArchitectureIR,
|
|
91
|
+
bin_cmd: list[str],
|
|
92
|
+
ir_path: Path,
|
|
93
|
+
out_path: Path,
|
|
94
|
+
backend: LLMBackend,
|
|
95
|
+
*,
|
|
96
|
+
max_iterations: int = 3,
|
|
97
|
+
) -> dict:
|
|
98
|
+
"""Writes `ir`, attempts `archify deliver`, and if archify's validator
|
|
99
|
+
rejects it, asks `backend` to patch the IR against the real diagnostics
|
|
100
|
+
and retries - up to `max_iterations` total delivery attempts. Returns
|
|
101
|
+
the final JSON report (whatever the last attempt produced, success or
|
|
102
|
+
failure) - the caller decides what to do with a still-failing report.
|
|
103
|
+
"""
|
|
104
|
+
if max_iterations < 1:
|
|
105
|
+
raise ValueError("max_iterations must be >= 1")
|
|
106
|
+
|
|
107
|
+
schema = ArchitectureIR.model_json_schema()
|
|
108
|
+
|
|
109
|
+
for attempt in range(1, max_iterations + 1):
|
|
110
|
+
ir_path.write_text(json.dumps(ir.model_dump_archify(), indent=2), encoding="utf-8")
|
|
111
|
+
report = run_deliver_json(bin_cmd, ir_path, out_path)
|
|
112
|
+
if report.get("ok"):
|
|
113
|
+
return report
|
|
114
|
+
|
|
115
|
+
diagnostics = report.get("diagnostics") or []
|
|
116
|
+
if not diagnostics or attempt == max_iterations:
|
|
117
|
+
return report
|
|
118
|
+
|
|
119
|
+
user_prompt = (
|
|
120
|
+
f"Current IR:\n{json.dumps(ir.model_dump_archify(), indent=2)}\n\n"
|
|
121
|
+
f"Validator diagnostics ({len(diagnostics)} issue(s)):\n"
|
|
122
|
+
f"{json.dumps(diagnostics, indent=2)}"
|
|
123
|
+
)
|
|
124
|
+
try:
|
|
125
|
+
patched = backend.complete_json(
|
|
126
|
+
_REPAIR_SYSTEM_PROMPT, user_prompt, schema, tool_name=_REPAIR_TOOL_NAME
|
|
127
|
+
)
|
|
128
|
+
ir = ArchitectureIR.model_validate(patched)
|
|
129
|
+
except Exception as exc:
|
|
130
|
+
# Layout repair is opportunistic. A provider quota/outage or an
|
|
131
|
+
# invalid structured response must leave render() free to use
|
|
132
|
+
# its deterministic, no-LLM fallback instead of crashing build.
|
|
133
|
+
raise RepairUnavailableError("optional LLM layout repair is unavailable") from exc
|
|
134
|
+
|
|
135
|
+
raise AssertionError("unreachable - loop always returns") # pragma: no cover
|