ambitgraph 0.8.1__tar.gz → 0.8.2__tar.gz
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.
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/MANIFEST.in +5 -0
- {ambitgraph-0.8.1/ambitgraph.egg-info → ambitgraph-0.8.2}/PKG-INFO +1 -1
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/daemon.py +16 -4
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/contract.py +11 -16
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/families.py +142 -15
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/langmirror.py +26 -49
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/load.py +1 -3
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/model.py +13 -15
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/orchestrate.py +0 -139
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/registry.py +92 -1093
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/serve.py +104 -630
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/cli.py +29 -1
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/cli.py +23 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/edges.py +79 -74
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/jscalls.py +14 -3
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/langs.py +12 -4
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/pipeline.py +28 -3
- ambitgraph-0.8.2/ambitgraph/responsibility.py +140 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/symbols.py +316 -6
- {ambitgraph-0.8.1 → ambitgraph-0.8.2/ambitgraph.egg-info}/PKG-INFO +1 -1
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph.egg-info/SOURCES.txt +1 -13
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/pyproject.toml +1 -1
- ambitgraph-0.8.1/ambit_map/export.py +0 -656
- ambitgraph-0.8.1/ambit_map/graph.py +0 -3091
- ambitgraph-0.8.1/ambit_map/island_layout.py +0 -462
- ambitgraph-0.8.1/ambit_map/islands.py +0 -536
- ambitgraph-0.8.1/ambit_map/judged.py +0 -279
- ambitgraph-0.8.1/ambit_map/kitdemo.py +0 -274
- ambitgraph-0.8.1/ambit_map/library.py +0 -2038
- ambitgraph-0.8.1/ambit_map/matched.py +0 -705
- ambitgraph-0.8.1/ambit_map/objects.py +0 -1046
- ambitgraph-0.8.1/ambit_map/page.py +0 -213
- ambitgraph-0.8.1/ambit_map/paths.py +0 -1434
- ambitgraph-0.8.1/ambit_map/theme.py +0 -568
- ambitgraph-0.8.1/ambit_map/unitpage.py +0 -1086
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/LICENSE +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/README.md +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/edge.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/envelope.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/fields.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/grant.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_contracts/household.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/__main__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/ambit-map-enrich.md +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/ambit-mapllm-contract.md +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/cli.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/express_pack.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/fastapi_pack.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/flask_pack.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/nextjs_pack.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/dialects/supabase_pack.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/explain.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_map/mapllm.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/export.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/ledger.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/responsibility.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambit_telegraph/store.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/WALKTHROUGH.md +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/__main__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/_delimited.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/cockpit.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/codelib.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/config.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/console.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/demo.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/design_tokens.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/docs_discovery.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/doctor.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/duplication.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/emit.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/engine_identity.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/estimate.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/eval.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/feeds.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/gate.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/gate_check.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/gate_server.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/harvest.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/inventory.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/mds.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/merge.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/merge_jsp.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/__init__.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/_init_js.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/filterpanel.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/graph.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/library.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/next_embed.css +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/search.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/siderail.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/standalone.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/omniscient_next/wholemap.js +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/parameterize.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/portal.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/prompts.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/pycalls.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/ratify.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/report.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/reports.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/sample.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/secretary.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/shell.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/stages.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/tables.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/tasks.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph/urcode.py +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph.egg-info/dependency_links.txt +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph.egg-info/entry_points.txt +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph.egg-info/requires.txt +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/ambitgraph.egg-info/top_level.txt +0 -0
- {ambitgraph-0.8.1 → ambitgraph-0.8.2}/setup.cfg +0 -0
|
@@ -12,6 +12,11 @@ prune dist
|
|
|
12
12
|
prune evidence-*
|
|
13
13
|
prune evidence
|
|
14
14
|
|
|
15
|
+
# The repository's own responsibility record (.ambit/responsibility.jsonl):
|
|
16
|
+
# it describes THIS repository's people and travels with the source tree,
|
|
17
|
+
# never with the installed package.
|
|
18
|
+
prune .ambit
|
|
19
|
+
|
|
15
20
|
# Internal process documents.
|
|
16
21
|
exclude AGENTS.md
|
|
17
22
|
exclude CHANGELOG.md
|
|
@@ -156,13 +156,12 @@ class DaemonTimeout(Exception):
|
|
|
156
156
|
|
|
157
157
|
|
|
158
158
|
# ---------------------------------------------------------------------------
|
|
159
|
-
# 2. reading the serve file -- fail-closed
|
|
159
|
+
# 2. reading the serve file -- fail-closed
|
|
160
160
|
# ---------------------------------------------------------------------------
|
|
161
161
|
|
|
162
162
|
|
|
163
163
|
def read_serve_file(out_dir):
|
|
164
|
-
"""Read <out_dir>/serve.json -> dict. FAIL-CLOSED
|
|
165
|
-
orchestrate.read_paths_index already takes: absent / unreadable /
|
|
164
|
+
"""Read <out_dir>/serve.json -> dict. FAIL-CLOSED: absent / unreadable /
|
|
166
165
|
non-JSON / not-a-dict / wrong schema version `v` -> None. Likewise,
|
|
167
166
|
"pid"/"port" that are present but not ints (null, a
|
|
168
167
|
string, missing) are the SAME fail-closed None -- every caller of this
|
|
@@ -1031,6 +1030,19 @@ def launch(args, out_dir, repo, idle_minutes, budget_s=None,
|
|
|
1031
1030
|
lines.append(reg.DAEMON_DAMAGED.format(reason=health["damaged"]))
|
|
1032
1031
|
if cmd is None:
|
|
1033
1032
|
cmd = invocation_form() + " " + cmd_name
|
|
1033
|
+
# The stop instruction echoes the --out the caller typed, exactly as
|
|
1034
|
+
# {repo} echoes the typed repo: the state file lives under that
|
|
1035
|
+
# directory, so the printed command must carry it to find the map.
|
|
1036
|
+
# A typed value holding a space is double-quoted so the line survives
|
|
1037
|
+
# a shell; without --out the segment is empty and the line is
|
|
1038
|
+
# unchanged.
|
|
1039
|
+
typed_out = getattr(args, "out", None)
|
|
1040
|
+
if typed_out:
|
|
1041
|
+
typed_out = str(typed_out)
|
|
1042
|
+
out_segment = (' --out "{0}"'.format(typed_out) if " " in typed_out
|
|
1043
|
+
else " --out " + typed_out)
|
|
1044
|
+
else:
|
|
1045
|
+
out_segment = ""
|
|
1034
1046
|
lines.append(reg.DAEMON_OPEN.format(url=url, repo=args.repo, cmd=cmd,
|
|
1035
|
-
minutes=idle_minutes))
|
|
1047
|
+
minutes=idle_minutes, out=out_segment))
|
|
1036
1048
|
return 0, lines, url
|
|
@@ -592,9 +592,9 @@ def attribute_head(text):
|
|
|
592
592
|
# module) rather than duplicated per Python pack; never resolved to a
|
|
593
593
|
# real uid here (that join belongs to the renderer -- see
|
|
594
594
|
# registry.resolve_py_module_spec, a BY-VALUE mirror of
|
|
595
|
-
# ambitgraph/edges.py's own `_resolve_python_import`, kept
|
|
596
|
-
#
|
|
597
|
-
#
|
|
595
|
+
# ambitgraph/edges.py's own `_resolve_python_import`, kept independently
|
|
596
|
+
# in registry.py since `ambit_map` imports no `ambitgraph`). Never file
|
|
597
|
+
# I/O; every function here is pure over text
|
|
598
598
|
# already handed to it, matching every other helper in this module.
|
|
599
599
|
# ---------------------------------------------------------------------------
|
|
600
600
|
|
|
@@ -770,14 +770,11 @@ def attribute_head_module_spec(head, module_imports, name_imports):
|
|
|
770
770
|
|
|
771
771
|
|
|
772
772
|
# resolve_py_module_spec (the module_spec -> real-uid resolution half)
|
|
773
|
-
# lives in registry.py, NOT here --
|
|
774
|
-
# (test_library_and_page_import_only_stdlib_and_ambit_map) restricts it to
|
|
775
|
-
# {registry, objects, families}, and registry.py already sits BELOW this
|
|
773
|
+
# lives in registry.py, NOT here -- registry.py already sits BELOW this
|
|
776
774
|
# module in the dependency order (contract.py imports registry, never the
|
|
777
|
-
# reverse)
|
|
778
|
-
#
|
|
779
|
-
#
|
|
780
|
-
# `_resolve_python_import` this module's docstring above describes.
|
|
775
|
+
# reverse). See registry.py's own import-resolution section for the
|
|
776
|
+
# BY-VALUE mirror of ambitgraph/edges.py's `_resolve_python_import` this
|
|
777
|
+
# module's docstring above describes.
|
|
781
778
|
|
|
782
779
|
|
|
783
780
|
def kwarg_text(parts, name):
|
|
@@ -1153,12 +1150,10 @@ def probe_notes(probe):
|
|
|
1153
1150
|
|
|
1154
1151
|
|
|
1155
1152
|
# ---------------------------------------------------------------------------
|
|
1156
|
-
# The entry-point glob language + resolver. Lives
|
|
1157
|
-
#
|
|
1158
|
-
#
|
|
1159
|
-
#
|
|
1160
|
-
# carries REAL PATHS, so graph.py never pattern-matches anything, only
|
|
1161
|
-
# checks set membership against `getattr(dialects, "entry_points", ())`.
|
|
1153
|
+
# The entry-point glob language + resolver. Lives here, where the pattern
|
|
1154
|
+
# language is defined and evaluated, so resolving in one place yields a
|
|
1155
|
+
# report field that carries REAL PATHS rather than raw patterns a
|
|
1156
|
+
# renderer would have to re-match itself.
|
|
1162
1157
|
# ---------------------------------------------------------------------------
|
|
1163
1158
|
|
|
1164
1159
|
ENTRY_GLOB_DOC = """A pattern is matched against the WHOLE repo-relative posix uid,
|
|
@@ -12,22 +12,46 @@ value already built from a sorted walk).
|
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
14
|
# ---------------------------------------------------------------------------
|
|
15
|
-
# 1.1 The
|
|
15
|
+
# 1.1 The nine families -- FIXED order, key . name . letter . light . dark
|
|
16
16
|
# ---------------------------------------------------------------------------
|
|
17
|
-
|
|
17
|
+
# TWO PIGMENT COLUMNS, ONE PER THEME, the same shape the folder palette carries.
|
|
18
|
+
# A family keeps ONE identity and wears the column its surface calls for; the
|
|
19
|
+
# hue is held across the pair so the green family is green in both.
|
|
20
|
+
#
|
|
21
|
+
# THE LIGHT COLUMN IS THE SHIPPED ONE, unchanged for the eight that existed.
|
|
22
|
+
# Measured rather than assumed: all eight clear the 3:1 non-text floor on the
|
|
23
|
+
# light grounds and only four clear it on the dark ones, which is what says
|
|
24
|
+
# these were authored for a light surface. The dark column is derived from it,
|
|
25
|
+
# hue held and lightness raised until each clears both dark grounds -- four
|
|
26
|
+
# needed no change at all.
|
|
27
|
+
#
|
|
28
|
+
# `modules` KEEPS ITS KEY AND CHANGES ITS NAME to Logic. The key is what every
|
|
29
|
+
# pin, artifact and stored ruling refers to, so renaming the display string
|
|
30
|
+
# moves no file and breaks no reference; renaming the key would do both for no
|
|
31
|
+
# reader-visible gain.
|
|
32
|
+
#
|
|
33
|
+
# `tests` is the one new family. Its hue sits in the widest unused gap -- 89
|
|
34
|
+
# degrees between olive and teal, centred at 131 -- and its separation from
|
|
35
|
+
# every other family was measured, not eyeballed: dE76 27.2 in the light column
|
|
36
|
+
# and 23.1 in the dark, against the tightest pair already shipped at 20.9 and
|
|
37
|
+
# 17.6. It is deliberately dark and unsaturated: it will be the second-largest
|
|
38
|
+
# family on a typical map, and a fifth of every picture wearing a shout is a
|
|
39
|
+
# picture about its tests.
|
|
18
40
|
FAMILIES = (
|
|
19
|
-
("routes", "Routes", "R", "#0e7490"),
|
|
20
|
-
("pages", "Pages & Components", "P", "#6d28d9"),
|
|
21
|
-
("modules", "
|
|
22
|
-
("helpers", "Helpers", "H", "#b45309"),
|
|
23
|
-
("tables", "Models & Tables", "T", "#26418f"),
|
|
24
|
-
("scripts", "Scripts", "S", "#a8324f"),
|
|
25
|
-
("config", "Config", "C", "#475569"),
|
|
26
|
-
("styles", "Styles", "Y", "#0f766e"),
|
|
41
|
+
("routes", "Routes", "R", "#0e7490", "#0e7490"),
|
|
42
|
+
("pages", "Pages & Components", "P", "#6d28d9", "#8246de"),
|
|
43
|
+
("modules", "Logic", "L", "#4d7c0f", "#4d7c0f"),
|
|
44
|
+
("helpers", "Helpers", "H", "#b45309", "#b45309"),
|
|
45
|
+
("tables", "Models & Tables", "T", "#26418f", "#3e62cc"),
|
|
46
|
+
("scripts", "Scripts", "S", "#a8324f", "#bc3858"),
|
|
47
|
+
("config", "Config", "C", "#475569", "#5a6b84"),
|
|
48
|
+
("styles", "Styles", "Y", "#0f766e", "#0f766e"),
|
|
49
|
+
("tests", "Tests", "E", "#1e4825", "#31773d"),
|
|
27
50
|
)
|
|
28
51
|
|
|
29
52
|
KEYS = tuple(k for k, *_rest in FAMILIES)
|
|
30
|
-
_BY_KEY = {k: (name, letter,
|
|
53
|
+
_BY_KEY = {k: (name, letter, light, dark)
|
|
54
|
+
for k, name, letter, light, dark in FAMILIES}
|
|
31
55
|
_FALLBACK_PIG = "#475569"
|
|
32
56
|
|
|
33
57
|
# ---------------------------------------------------------------------------
|
|
@@ -78,6 +102,8 @@ _PAGE_ANCHOR_EXTS = frozenset(("tsx", "jsx", "vue", "svelte", "astro", "mdx", "h
|
|
|
78
102
|
_HELPER_SEGMENTS = frozenset(("utils", "util", "lib", "libs", "helpers", "hooks",
|
|
79
103
|
"includes", "shared", "common"))
|
|
80
104
|
_HELPER_STEMS = frozenset(("utils", "util", "helpers", "helper"))
|
|
105
|
+
_TEST_SEGMENTS = frozenset(("tests", "test", "__tests__", "spec", "specs"))
|
|
106
|
+
_TEST_BASES = frozenset(("conftest.py",))
|
|
81
107
|
|
|
82
108
|
|
|
83
109
|
def family_of(unit, routes_by_uid=None):
|
|
@@ -100,6 +126,25 @@ def family_of(unit, routes_by_uid=None):
|
|
|
100
126
|
if routes_by_uid and uid in routes_by_uid:
|
|
101
127
|
return "routes"
|
|
102
128
|
|
|
129
|
+
# 1b -- a file that exists to CHECK other files is a test, whatever its
|
|
130
|
+
# extension or its directory. This sits second on purpose. A test's
|
|
131
|
+
# identity as a test dominates both: components/Button.test.tsx is a test,
|
|
132
|
+
# not a page. Placed lower it would catch almost nothing, because the page
|
|
133
|
+
# rule takes .tsx, the helper rule takes anything under lib/, and the table
|
|
134
|
+
# rule takes anything under db/ -- each would claim the file first.
|
|
135
|
+
#
|
|
136
|
+
# It sits AFTER the route fact because that fact is a real mount read from
|
|
137
|
+
# a dialect pack, and a test file cannot hold one.
|
|
138
|
+
#
|
|
139
|
+
# The prefix is `test_` WITH its underscore rather than a bare startswith,
|
|
140
|
+
# so contest.py and latest.py are not swept in. `scripts/run-tests.sh` is
|
|
141
|
+
# likewise untouched: it RUNS tests and is not one.
|
|
142
|
+
if (any(s in _TEST_SEGMENTS for s in segs)
|
|
143
|
+
or base in _TEST_BASES
|
|
144
|
+
or stem.startswith("test_") or stem.endswith("_test")
|
|
145
|
+
or stem.endswith(".test") or stem.endswith(".spec")):
|
|
146
|
+
return "tests"
|
|
147
|
+
|
|
103
148
|
# 1b -- filename convention, evaluated whether or not any pack fired.
|
|
104
149
|
if stem in _ROUTE_STEMS:
|
|
105
150
|
return "routes"
|
|
@@ -170,7 +215,7 @@ def families_in_use(units, routes_by_uid=None):
|
|
|
170
215
|
key = family_of(u, routes_by_uid)
|
|
171
216
|
buckets.setdefault(key, []).append(u)
|
|
172
217
|
out = []
|
|
173
|
-
for key, _name, _letter,
|
|
218
|
+
for key, _name, _letter, _light, _dark in FAMILIES:
|
|
174
219
|
if key in buckets:
|
|
175
220
|
out.append((key, tuple(sorted(buckets[key], key=lambda u: _uid_of(u)))))
|
|
176
221
|
return tuple(out)
|
|
@@ -180,7 +225,19 @@ def families_in_use(units, routes_by_uid=None):
|
|
|
180
225
|
# 1.5 wired -- the truth table
|
|
181
226
|
# ---------------------------------------------------------------------------
|
|
182
227
|
|
|
183
|
-
|
|
228
|
+
# The families nothing needs to point AT. A unit here with no inbound edge is
|
|
229
|
+
# not a loose end -- it is how that kind of file is supposed to look, so it
|
|
230
|
+
# reads "live" rather than joining the unlinked review queue.
|
|
231
|
+
#
|
|
232
|
+
# ⚠ TESTS BELONG HERE AND THE OMISSION IS EXPENSIVE. A test runner DISCOVERS
|
|
233
|
+
# test files; production code never imports them, so every test in a repository
|
|
234
|
+
# has zero inbound edges by design. This list is a POSITIVE one -- anything
|
|
235
|
+
# absent from it falls through to "coming" -- so leaving tests out would have
|
|
236
|
+
# flagged every test file on the map as a possible loose end. On the reference
|
|
237
|
+
# repository that is 55 files, a fifth of the picture, every one of them
|
|
238
|
+
# accused of being unreachable code while being exactly what it should be.
|
|
239
|
+
ENTRY_FAMILIES = frozenset(("routes", "pages", "scripts", "config", "styles",
|
|
240
|
+
"tables", "tests"))
|
|
184
241
|
|
|
185
242
|
|
|
186
243
|
def _inbound(model, uid):
|
|
@@ -223,9 +280,24 @@ def letter(key):
|
|
|
223
280
|
return v[1] if v else "?"
|
|
224
281
|
|
|
225
282
|
|
|
226
|
-
def pigment(key):
|
|
283
|
+
def pigment(key, theme="light"):
|
|
284
|
+
"""-> the family's fill for one theme, defaulting to the light column.
|
|
285
|
+
|
|
286
|
+
Two steps: look the family up, then take the column the theme asks for.
|
|
287
|
+
An unknown theme falls back to light rather than raising, because a
|
|
288
|
+
renderer asking for a theme this table has not heard of should still draw
|
|
289
|
+
something.
|
|
290
|
+
|
|
291
|
+
THE DEFAULT IS LIGHT, AND THAT IS NOT A PREFERENCE. Every existing caller
|
|
292
|
+
was written against a single-column table whose colours were authored for
|
|
293
|
+
a light surface, so defaulting to light leaves all of them correct and
|
|
294
|
+
unchanged. A default of dark would have silently repainted every current
|
|
295
|
+
page while every call site still read the same.
|
|
296
|
+
"""
|
|
227
297
|
v = _BY_KEY.get(key)
|
|
228
|
-
|
|
298
|
+
if not v:
|
|
299
|
+
return _FALLBACK_PIG
|
|
300
|
+
return v[3] if theme == "dark" else v[2]
|
|
229
301
|
|
|
230
302
|
|
|
231
303
|
def name(key):
|
|
@@ -233,6 +305,61 @@ def name(key):
|
|
|
233
305
|
return v[0] if v else key
|
|
234
306
|
|
|
235
307
|
|
|
308
|
+
# ---------------------------------------------------------------------------
|
|
309
|
+
# 1.7 DEFINITIONS -- one sentence per family, for the reader on its board
|
|
310
|
+
# ---------------------------------------------------------------------------
|
|
311
|
+
|
|
312
|
+
# A SEPARATE TABLE RATHER THAN A SIXTH COLUMN ON `FAMILIES`. Widening that
|
|
313
|
+
# tuple would re-arity two unpackers here and two in the suite -- a
|
|
314
|
+
# restructure riding inside a behaviour change. The completeness this costs
|
|
315
|
+
# (a row physically cannot omit a sixth column) is bought back by the pin
|
|
316
|
+
# that checks these keys against KEYS in BOTH directions.
|
|
317
|
+
#
|
|
318
|
+
# EVERY SENTENCE IS TRUE ONLY IN CASCADE ORDER, because `family_of` is a
|
|
319
|
+
# cascade: the first rule to match wins and later rules never see the unit.
|
|
320
|
+
# So a sentence must never claim a signal an EARLIER rule already took --
|
|
321
|
+
# "pages are .tsx files" is false the moment a .tsx sits under a tests
|
|
322
|
+
# directory. Where that carve-out is the thing a reader would otherwise get
|
|
323
|
+
# wrong, the sentence names it rather than leaving a quiet exception.
|
|
324
|
+
#
|
|
325
|
+
# `modules` IS THE FALLBACK, and its sentence says so instead of naming a
|
|
326
|
+
# purpose. Its members share nothing but having matched no earlier rule: a
|
|
327
|
+
# changelog, an entry point and a data-access module land there together, so
|
|
328
|
+
# any sentence claiming a subject would be false on the first board opened.
|
|
329
|
+
DEFINITIONS = {
|
|
330
|
+
"routes": ("Files whose name or folder marks them as a way in — a router, "
|
|
331
|
+
"a URL table, middleware, or anything under a routes folder."),
|
|
332
|
+
"pages": ("What the app draws — components, templates and views, by file "
|
|
333
|
+
"type or by the folder holding them."),
|
|
334
|
+
"modules": ("Everything no other rule claimed. These files share only that "
|
|
335
|
+
"nothing above matched them — a remainder, not a subject."),
|
|
336
|
+
"helpers": ("Shared odds and ends, by a utils, lib or shared folder or by "
|
|
337
|
+
"name. Data models under those same folders are in Models & "
|
|
338
|
+
"Tables."),
|
|
339
|
+
"tables": ("Where the app's data shape lives — migrations, schemas, and "
|
|
340
|
+
"files named or foldered as models."),
|
|
341
|
+
"scripts": ("Things you run rather than import — shell and batch files, and "
|
|
342
|
+
"whatever sits in a scripts, bin or tools folder."),
|
|
343
|
+
"config": ("Settings and manifests rather than code — declaration files, by "
|
|
344
|
+
"extension or by well-known name."),
|
|
345
|
+
"styles": "How the app looks — stylesheets, by file type.",
|
|
346
|
+
"tests": ("Files that exist to check other files. A test's identity beats "
|
|
347
|
+
"its file type, so a test component sits here rather than in "
|
|
348
|
+
"Pages."),
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def definition(key):
|
|
353
|
+
"""-> the family's one-sentence definition, or "" for a key this table
|
|
354
|
+
has not heard of.
|
|
355
|
+
|
|
356
|
+
The empty string rather than a raise, and rather than a placeholder: a
|
|
357
|
+
board for an unknown family should render its counts and say nothing,
|
|
358
|
+
not interrupt the page or print a sentence nobody wrote. The same
|
|
359
|
+
fall-back-quietly posture `letter`, `pigment` and `name` already take.
|
|
360
|
+
"""
|
|
361
|
+
return DEFINITIONS.get(key, "")
|
|
362
|
+
|
|
236
363
|
_QUOTE_SAFE = frozenset(
|
|
237
364
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.-~")
|
|
238
365
|
|
|
@@ -32,11 +32,9 @@ Unlike ambitgraph/langs.py (which imports `re` and `dataclasses` for its
|
|
|
32
32
|
own LangSpec declarations), this module is a pure, dependency-free LEAF:
|
|
33
33
|
no imports at all, not even stdlib. Every value below is a literal, and
|
|
34
34
|
the single function here (`blind_langs`) reads only the table declared
|
|
35
|
-
directly above it, so it needs no import either. Every
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
of registry.py/paths.py/library.py/orchestrate.py risks a cycle by their
|
|
39
|
-
now also depending, transitively, on this module.
|
|
35
|
+
directly above it, so it needs no import either. Every consumer inside
|
|
36
|
+
this package imports IT; it imports NONE of them, so no consumer risks
|
|
37
|
+
a cycle by depending, transitively, on this module.
|
|
40
38
|
|
|
41
39
|
NO MEMBER DELETION -- the dead-looking members below are kept deliberately,
|
|
42
40
|
not by oversight, and deleting one is a real regression:
|
|
@@ -180,9 +178,7 @@ GO_LANGS = ("go",)
|
|
|
180
178
|
# declaration ("class"/"interface"/"enum"/"record") -- "constructor" is
|
|
181
179
|
# DELIBERATELY not a member, the same exclusion codelib.py's own set
|
|
182
180
|
# states (a constructor is a member OF a type, never itself a type
|
|
183
|
-
# declaration).
|
|
184
|
-
# bucket so a Java interface/enum/record renders in the class(es) count
|
|
185
|
-
# on the unit story, not silently in "other named symbol(s)". Diffed
|
|
181
|
+
# declaration). Diffed
|
|
186
182
|
# against ambitgraph.codelib._TYPE_SYMBOL_KINDS by
|
|
187
183
|
# tests/test_langs_mirror_diff.py (DIFFED_FACT_NAMES entry
|
|
188
184
|
# "type_symbol_kinds") -- one origin (symbols.py's own kind literals), two
|
|
@@ -194,11 +190,9 @@ TYPE_SYMBOL_KINDS = ("class", "interface", "enum", "record")
|
|
|
194
190
|
# strings symbols.py's extract_c_symbols emits for a C type declaration --
|
|
195
191
|
# a tagged bodied "struct"/"union"/"enum", and "typedef". "function" is
|
|
196
192
|
# deliberately not a member (it is the callable half, the same boundary
|
|
197
|
-
# TYPE_SYMBOL_KINDS draws against "constructor").
|
|
198
|
-
#
|
|
199
|
-
#
|
|
200
|
-
# language gates are disjoint, so a TypeScript enum still renders as
|
|
201
|
-
# "other named symbol(s)" while a C enum counts as a type. Diffed against
|
|
193
|
+
# TYPE_SYMBOL_KINDS draws against "constructor"). "enum" is a member of
|
|
194
|
+
# BOTH this tuple and TYPE_SYMBOL_KINDS, and the two language gates are
|
|
195
|
+
# disjoint, under the C_LANGS gate above. Diffed against
|
|
202
196
|
# ambitgraph.codelib._C_TYPE_SYMBOL_KINDS by tests/test_langs_mirror_diff.py
|
|
203
197
|
# (DIFFED_FACT_NAMES entry "c_type_symbol_kinds") -- one origin
|
|
204
198
|
# (symbols.py's own kind literals), two independently-declared copies,
|
|
@@ -213,13 +207,7 @@ C_TYPE_SYMBOL_KINDS = ("struct", "union", "enum", "typedef")
|
|
|
213
207
|
# "class" itself (Ruby genuinely has classes), so it feeds BOTH halves of
|
|
214
208
|
# codelib.py's carve identically -- the same shape TYPE_SYMBOL_KINDS takes
|
|
215
209
|
# for Java, never C's shape (which unions "class" in separately because C
|
|
216
|
-
# never emits that literal kind at all).
|
|
217
|
-
# ambit_map/unitpage.py's class-count bucket under the RB_LANGS gate
|
|
218
|
-
# above, WIDENING THE COUNT ONLY -- the word stays reg.UNIT_STORY_CLASSES
|
|
219
|
-
# for Ruby (unitpage.py's own class_word ternary is not gated on RB_LANGS
|
|
220
|
-
# at all): Ruby genuinely has classes, so mirroring C's word-swap here
|
|
221
|
-
# would render "N type(s)" about a language whose classes are exactly what
|
|
222
|
-
# the count is naming. Diffed against ambitgraph.codelib._RB_TYPE_SYMBOL_KINDS
|
|
210
|
+
# never emits that literal kind at all). Diffed against ambitgraph.codelib._RB_TYPE_SYMBOL_KINDS
|
|
223
211
|
# by tests/test_langs_mirror_diff.py (DIFFED_FACT_NAMES entry
|
|
224
212
|
# "rb_type_symbol_kinds") -- one origin (symbols.py's own kind literals),
|
|
225
213
|
# two independently-declared copies, never a cross-wall import.
|
|
@@ -228,12 +216,11 @@ RB_TYPE_SYMBOL_KINDS = ("class", "module")
|
|
|
228
216
|
# NAMESPACE_LANGS (the wall-legal PRODUCT-side mirror of
|
|
229
217
|
# ambitgraph/langs.py's own NAMESPACE_LANGS): the `lang` tags whose units
|
|
230
218
|
# get the java-namespace treatment (the languages whose LangSpec declares
|
|
231
|
-
# namespace=True)
|
|
232
|
-
#
|
|
233
|
-
#
|
|
234
|
-
#
|
|
235
|
-
#
|
|
236
|
-
# check at export. Diffed against ambitgraph.langs.NAMESPACE_LANGS by
|
|
219
|
+
# namespace=True) -- symbols.py emits the SAME literal kinds
|
|
220
|
+
# ("interface"/"enum") for TypeScript declarations, which must keep
|
|
221
|
+
# rendering as "other named symbol(s)", exactly the boundary
|
|
222
|
+
# ambitgraph/codelib.py draws with its own is_java_namespace check at
|
|
223
|
+
# export. Diffed against ambitgraph.langs.NAMESPACE_LANGS by
|
|
237
224
|
# tests/test_langs_mirror_diff.py (DIFFED_FACT_NAMES entry
|
|
238
225
|
# "namespace_langs") -- one origin (the LangSpec namespace flags), two
|
|
239
226
|
# independently-declared copies, never a cross-wall import.
|
|
@@ -246,32 +233,22 @@ NAMESPACE_LANGS = frozenset({"java", "jsp"})
|
|
|
246
233
|
# can never appear as a real `lang` value today (ambitgraph/symbols.py's
|
|
247
234
|
# build_file_plane folds .jspf into the tag "jsp", never into "jspf"
|
|
248
235
|
# itself) but stays for the identical producer-portability reason.
|
|
249
|
-
#
|
|
250
|
-
#
|
|
251
|
-
#
|
|
252
|
-
#
|
|
253
|
-
# langm.ROUTEDNESS_LANGS`), kept alive for backward compatibility and
|
|
254
|
-
# because tests/test_langs_value_pins.py's own locked oracle pin
|
|
255
|
-
# still asserts `reg.ROUTEDNESS_LANGS` by value.
|
|
236
|
+
# ambit_map/registry.py re-exports the name (`ROUTEDNESS_LANGS =
|
|
237
|
+
# langm.ROUTEDNESS_LANGS`), kept alive because
|
|
238
|
+
# tests/test_langs_value_pins.py's own locked oracle pin still asserts
|
|
239
|
+
# `reg.ROUTEDNESS_LANGS` by value; no product call site reads it today.
|
|
256
240
|
# ===========================================================================
|
|
257
241
|
ROUTEDNESS_LANGS = ("jsp", "jspf", "java")
|
|
258
242
|
|
|
259
243
|
|
|
260
244
|
# ===========================================================================
|
|
261
|
-
#
|
|
262
|
-
#
|
|
263
|
-
#
|
|
264
|
-
#
|
|
265
|
-
# every pre-existing row's value is UNCHANGED. Values UNCHANGED otherwise;
|
|
266
|
-
# the "why" narrative for each individual row (which real reader function
|
|
267
|
-
# backs it, what its own language gate looks like) stays in
|
|
268
|
-
# ambit_map/paths.py's own module comment beside its KIND_READER_LANGS
|
|
269
|
-
# alias -- this module only carries the literal.
|
|
245
|
+
# KIND_READER_LANGS: the full reader-coverage matrix, 19 ALLOWED_KINDS
|
|
246
|
+
# rows -> the Unit.lang frozenset each kind's reader covers. The 19th
|
|
247
|
+
# row, include/import.java, is additive -- every pre-existing row's
|
|
248
|
+
# value is UNCHANGED.
|
|
270
249
|
#
|
|
271
|
-
#
|
|
272
|
-
#
|
|
273
|
-
# own values, so the overriding logic and its own inputs (TERMINAL_KINDS,
|
|
274
|
-
# MOUNT_CAPABLE_PACKS) stay declared in paths.py, not here):
|
|
250
|
+
# Three named divergences apply this table with an override, rather than
|
|
251
|
+
# a bare language-overlap lookup:
|
|
275
252
|
# 1. TERMINAL_KINDS (table/read|write|ddl) -- LANGUAGE-AGNOSTIC by the
|
|
276
253
|
# real reader's own construction (ambitgraph/tables.py's build_tables
|
|
277
254
|
# carries no language gate at all): applicable whenever the model
|
|
@@ -280,8 +257,8 @@ ROUTEDNESS_LANGS = ("jsp", "jspf", "java")
|
|
|
280
257
|
# 2. "reference/script" -- unconditionally False: CONFIG-gated on
|
|
281
258
|
# ScanConfig.asset_roots, never language-gated, and this product's own
|
|
282
259
|
# scan config never sets asset_roots -- so this row's frozenset below
|
|
283
|
-
# is never actually consulted either; kept only so this dict's key
|
|
284
|
-
# still equals
|
|
260
|
+
# is never actually consulted either; kept only so this dict's key
|
|
261
|
+
# set still equals the full 19-kind domain.
|
|
285
262
|
# 3. "route/mount" -- language overlap against this row IS consulted, but
|
|
286
263
|
# is NECESSARY, not SUFFICIENT: applicability additionally requires at
|
|
287
264
|
# least one of MOUNT_CAPABLE_PACKS (express_pack/fastapi_pack/
|
|
@@ -395,7 +372,7 @@ _GRAMMAR_EXTS = frozenset({
|
|
|
395
372
|
# The disclosure-gate vocabulary -- THE PER-SENTENCE LANGUAGE
|
|
396
373
|
# TAG, product side. Formerly ambit_map/library.py's own
|
|
397
374
|
# _BLIND_SENTENCE_LANGS/_blind_langs pair; it lives here, with every other
|
|
398
|
-
# cross-wall copy
|
|
375
|
+
# cross-wall copy.
|
|
399
376
|
#
|
|
400
377
|
# The engine declares each blind sentence's own languages AT the sentence
|
|
401
378
|
# (ambitgraph.symbols.BlindSentence's `langs`, empty meaning the sentence
|
|
@@ -318,9 +318,7 @@ def load_run(run_dir):
|
|
|
318
318
|
# is tried against either resolution tier at
|
|
319
319
|
# all). registry.py's unresolved_reason_sentence
|
|
320
320
|
# is the dispatch that maps these three literals
|
|
321
|
-
# to their sentences
|
|
322
|
-
# (graph.py's _quarantine_note, unitpage.py's
|
|
323
|
-
# connections block).
|
|
321
|
+
# to their sentences.
|
|
324
322
|
reason = row.get("unresolved_reason")
|
|
325
323
|
unresolved_acc[src].append(
|
|
326
324
|
model.UnresolvedRef(
|
|
@@ -51,11 +51,10 @@ class UnresolvedRef:
|
|
|
51
51
|
# ladder for Python -- neither '' nor the importer's own derived
|
|
52
52
|
# root resolved it, but some OTHER one did; carried on the edges row
|
|
53
53
|
# as "unresolved_reason"), distinct from an ordinary external/
|
|
54
|
-
# never-read import -- ambit_map/load.py sets it, and
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
# one for a target the scan genuinely read). This token is NOT
|
|
54
|
+
# never-read import -- ambit_map/load.py sets it, and
|
|
55
|
+
# registry.py's unresolved_reason_sentence picks the correct
|
|
56
|
+
# sentence for it (never the false "this scan never read" one for a
|
|
57
|
+
# target the scan genuinely read). This token is NOT
|
|
59
58
|
# Python-only -- the Java ladder's cross-root tier REUSES this exact
|
|
60
59
|
# same token, byte-identically, for a java-sourced cross-root hold,
|
|
61
60
|
# rather than minting a java-named twin (the sentence text itself
|
|
@@ -64,8 +63,8 @@ class UnresolvedRef:
|
|
|
64
63
|
# before the ladder change renamed it; see edges.py's own docstring
|
|
65
64
|
# for why the old name no longer names a distinct state.) A machine
|
|
66
65
|
# token is an interface, so this one keeps the same posture as
|
|
67
|
-
# cross_root_import_held -- each ships WITH its own
|
|
68
|
-
#
|
|
66
|
+
# cross_root_import_held -- each ships WITH its own registry.py
|
|
67
|
+
# sentence, never a free-text reason (a free-text
|
|
69
68
|
# reason falls through to the generic "never read" sentence, which is
|
|
70
69
|
# false for a target this scan genuinely did read):
|
|
71
70
|
# "wildcard_import" marks a Java wildcard import (`import com.foo.*;`)
|
|
@@ -124,8 +123,8 @@ class CallEdge:
|
|
|
124
123
|
class MemberResolvedEdge:
|
|
125
124
|
"""A DATA-PLANE row only -- emitted as data, never painted onto the
|
|
126
125
|
graph: never an IncludeEdge, never folded into `includes`/
|
|
127
|
-
`included_by
|
|
128
|
-
|
|
126
|
+
`included_by` (the quarantine precedent: present in data, never
|
|
127
|
+
painted, on purpose). `src` is the
|
|
129
128
|
importing unit; `name` is the bound name it imported; `through` is the
|
|
130
129
|
directly-imported module (already has its own ordinary IncludeEdge);
|
|
131
130
|
`target` is the module that first DECLARES `name`, reached by tracing
|
|
@@ -160,8 +159,8 @@ class PhantomEdge:
|
|
|
160
159
|
# ui/component-use). These mirror MemberResolvedEdge's own posture exactly:
|
|
161
160
|
# DATA-PLANE-shaped value objects, never resolved or joined here -- the
|
|
162
161
|
# join (composed-route table, event pairing, component-use resolution)
|
|
163
|
-
# is
|
|
164
|
-
#
|
|
162
|
+
# is a separate step, over these raw facts plus the already-loaded
|
|
163
|
+
# model. `src` is the file the site was read in, matching
|
|
165
164
|
# every other per-unit dict's own key shape (`includes`, `member_resolved`,
|
|
166
165
|
# ...).
|
|
167
166
|
# ---------------------------------------------------------------------------
|
|
@@ -221,8 +220,8 @@ class ImportBindingTarget:
|
|
|
221
220
|
|
|
222
221
|
|
|
223
222
|
# ---------------------------------------------------------------------------
|
|
224
|
-
# The PRODUCT-side output of
|
|
225
|
-
#
|
|
223
|
+
# The PRODUCT-side output of a matched-edge join -- pure value objects,
|
|
224
|
+
# never constructed by load.py (unlike every dataclass
|
|
226
225
|
# above this point, which mirrors an artifact row 1:1). `kind` is always an
|
|
227
226
|
# ambit_map.registry.EDGE_KINDS key. `src`/`dst` are always real unit uids
|
|
228
227
|
# (a matched edge with an unresolved endpoint is never constructed -- it is
|
|
@@ -405,8 +404,7 @@ class MapModel:
|
|
|
405
404
|
# Additive: raw per-unit SITE FACTS, the SAME
|
|
406
405
|
# uid -> tuple[...] shape `member_resolved` already uses. Defaulted so
|
|
407
406
|
# every existing MapModel(...) call site (every hand-built test fixture)
|
|
408
|
-
# stays byte-unaffected. Never joined/resolved here
|
|
409
|
-
# ambit_map/matched.py.
|
|
407
|
+
# stays byte-unaffected. Never joined/resolved here.
|
|
410
408
|
request_sites: dict = field(default_factory=dict)
|
|
411
409
|
event_sites: dict = field(default_factory=dict)
|
|
412
410
|
component_use_sites: dict = field(default_factory=dict)
|