code-atlas-forge 1.1.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.
- code_atlas/__init__.py +6 -0
- code_atlas/__main__.py +6 -0
- code_atlas/api/__init__.py +5 -0
- code_atlas/api/handlers.py +1514 -0
- code_atlas/api/server.py +394 -0
- code_atlas/cli.py +2084 -0
- code_atlas/config.py +149 -0
- code_atlas/context/__init__.py +66 -0
- code_atlas/context/contracts.py +277 -0
- code_atlas/context/domain.py +760 -0
- code_atlas/context/engine.py +442 -0
- code_atlas/context/expand.py +133 -0
- code_atlas/context/renderers.py +71 -0
- code_atlas/context/tokenizer.py +37 -0
- code_atlas/contracts/__init__.py +38 -0
- code_atlas/contracts/enums.py +67 -0
- code_atlas/contracts/models.py +175 -0
- code_atlas/contracts/ports.py +70 -0
- code_atlas/discovery/__init__.py +25 -0
- code_atlas/discovery/gitignore.py +119 -0
- code_atlas/discovery/identity.py +52 -0
- code_atlas/discovery/models.py +204 -0
- code_atlas/discovery/scanner.py +495 -0
- code_atlas/domains/__init__.py +107 -0
- code_atlas/domains/api/__init__.py +28 -0
- code_atlas/domains/api/adapter.py +405 -0
- code_atlas/domains/api/common.py +308 -0
- code_atlas/domains/api/consumers.py +224 -0
- code_atlas/domains/api/emit.py +231 -0
- code_atlas/domains/api/openapi.py +398 -0
- code_atlas/domains/api/routes.py +582 -0
- code_atlas/domains/api/yaml_subset.py +683 -0
- code_atlas/domains/aws/__init__.py +28 -0
- code_atlas/domains/aws/adapter.py +382 -0
- code_atlas/domains/aws/common.py +261 -0
- code_atlas/domains/aws/configs.py +1164 -0
- code_atlas/domains/aws/emit.py +411 -0
- code_atlas/domains/aws/pyfiles.py +2220 -0
- code_atlas/domains/aws/sqlbridge.py +448 -0
- code_atlas/domains/cicd/__init__.py +50 -0
- code_atlas/domains/cicd/adapter.py +762 -0
- code_atlas/domains/cicd/classify.py +136 -0
- code_atlas/domains/cicd/workflow.py +504 -0
- code_atlas/domains/cicd/yamlmini.py +946 -0
- code_atlas/domains/cli_surface.py +739 -0
- code_atlas/domains/contracts.py +274 -0
- code_atlas/domains/crossresolve.py +1208 -0
- code_atlas/domains/evidence.py +57 -0
- code_atlas/domains/identity.py +149 -0
- code_atlas/domains/model.py +202 -0
- code_atlas/domains/registry.py +250 -0
- code_atlas/domains/resolution.py +65 -0
- code_atlas/domains/spark/__init__.py +31 -0
- code_atlas/domains/spark/adapter.py +146 -0
- code_atlas/domains/spark/emit.py +344 -0
- code_atlas/domains/spark/extract.py +108 -0
- code_atlas/domains/spark/sqllit.py +192 -0
- code_atlas/domains/spark/walk.py +1943 -0
- code_atlas/domains/sql/__init__.py +30 -0
- code_atlas/domains/sql/adapter.py +382 -0
- code_atlas/domains/sql/extract.py +1271 -0
- code_atlas/domains/sql/nodes.py +264 -0
- code_atlas/domains/sql/parser.py +1510 -0
- code_atlas/domains/sql/tokens.py +318 -0
- code_atlas/domains/summarize.py +126 -0
- code_atlas/domains/terraform/__init__.py +20 -0
- code_atlas/domains/terraform/adapter.py +127 -0
- code_atlas/domains/terraform/catalog.py +43 -0
- code_atlas/domains/terraform/extract.py +1277 -0
- code_atlas/domains/terraform/hcl.py +538 -0
- code_atlas/domains/terraform/refs.py +181 -0
- code_atlas/domains/terraform/tfjson.py +163 -0
- code_atlas/domains/terraform/tfvars.py +98 -0
- code_atlas/domains/validation.py +120 -0
- code_atlas/env_override.py +27 -0
- code_atlas/eval/__init__.py +26 -0
- code_atlas/eval/domain_eval.py +681 -0
- code_atlas/eval/ground_truth.py +688 -0
- code_atlas/eval/impact_truth.py +758 -0
- code_atlas/eval/metrics.py +178 -0
- code_atlas/eval/pack_value.py +610 -0
- code_atlas/federation/__init__.py +72 -0
- code_atlas/federation/federation.py +676 -0
- code_atlas/federation/model.py +355 -0
- code_atlas/federation/store.py +194 -0
- code_atlas/gitbin.py +48 -0
- code_atlas/graph/__init__.py +20 -0
- code_atlas/graph/builder.py +378 -0
- code_atlas/graph/identity.py +54 -0
- code_atlas/graph/queries.py +47 -0
- code_atlas/graph/validation.py +92 -0
- code_atlas/impact/__init__.py +37 -0
- code_atlas/impact/blast_radius.py +171 -0
- code_atlas/impact/contract_analyzer.py +104 -0
- code_atlas/impact/contracts.py +315 -0
- code_atlas/impact/diff_reader.py +142 -0
- code_atlas/impact/domain_traversal.py +768 -0
- code_atlas/impact/engine.py +392 -0
- code_atlas/impact/packs.py +128 -0
- code_atlas/impact/report.py +136 -0
- code_atlas/impact/revision_binding.py +220 -0
- code_atlas/impact/risk_classifier.py +83 -0
- code_atlas/impact/snapshot_resolver.py +136 -0
- code_atlas/impact/symbol_diff.py +210 -0
- code_atlas/impact/test_selector.py +166 -0
- code_atlas/indexing/__init__.py +5 -0
- code_atlas/indexing/cache.py +396 -0
- code_atlas/indexing/change_detector.py +278 -0
- code_atlas/indexing/coordinator.py +1032 -0
- code_atlas/indexing/dependency_planner.py +268 -0
- code_atlas/indexing/persist.py +436 -0
- code_atlas/indexing/reuse.py +85 -0
- code_atlas/install/__init__.py +7 -0
- code_atlas/install/cli.py +168 -0
- code_atlas/install/lifecycle.py +286 -0
- code_atlas/mcp.py +674 -0
- code_atlas/observability/__init__.py +19 -0
- code_atlas/observability/logging.py +199 -0
- code_atlas/parsers/__init__.py +43 -0
- code_atlas/parsers/contracts.py +209 -0
- code_atlas/parsers/engine.py +125 -0
- code_atlas/parsers/languages/__init__.py +1 -0
- code_atlas/parsers/languages/python.py +391 -0
- code_atlas/parsers/languages/tsx.py +28 -0
- code_atlas/parsers/languages/typescript.py +909 -0
- code_atlas/parsers/registry.py +51 -0
- code_atlas/parsers/tree_sitter_runtime.py +118 -0
- code_atlas/resolvers/__init__.py +33 -0
- code_atlas/resolvers/bootstrap.py +246 -0
- code_atlas/resolvers/capabilities.py +33 -0
- code_atlas/resolvers/contracts.py +133 -0
- code_atlas/resolvers/coordinator.py +96 -0
- code_atlas/resolvers/enrichment.py +318 -0
- code_atlas/resolvers/lsp/__init__.py +26 -0
- code_atlas/resolvers/lsp/adapter.py +256 -0
- code_atlas/resolvers/lsp/lifecycle.py +251 -0
- code_atlas/resolvers/lsp/spawn.py +92 -0
- code_atlas/resolvers/lsp/transport.py +240 -0
- code_atlas/resolvers/registry.py +33 -0
- code_atlas/resolvers/scip/__init__.py +49 -0
- code_atlas/resolvers/scip/decoder.py +216 -0
- code_atlas/resolvers/scip/importer.py +248 -0
- code_atlas/resolvers/scip/loader.py +90 -0
- code_atlas/resolvers/scip/mapper.py +271 -0
- code_atlas/resolvers/scip/provenance.py +211 -0
- code_atlas/resolvers/scip/resolver.py +289 -0
- code_atlas/resolvers/scip/validation.py +101 -0
- code_atlas/resolvers/syntactic/__init__.py +8 -0
- code_atlas/resolvers/syntactic/resolver.py +254 -0
- code_atlas/search/__init__.py +32 -0
- code_atlas/search/contracts.py +176 -0
- code_atlas/search/domain.py +561 -0
- code_atlas/search/engine.py +530 -0
- code_atlas/search/federated.py +276 -0
- code_atlas/search/graph.py +282 -0
- code_atlas/search/hybrid.py +151 -0
- code_atlas/search/intent.py +48 -0
- code_atlas/search/lexical.py +176 -0
- code_atlas/search/refs.py +359 -0
- code_atlas/search/structural.py +505 -0
- code_atlas/search/tokenizer.py +43 -0
- code_atlas/search/vector/__init__.py +35 -0
- code_atlas/search/vector/contracts.py +76 -0
- code_atlas/search/vector/embeddings.py +96 -0
- code_atlas/search/vector/index.py +169 -0
- code_atlas/search/vector/provider.py +50 -0
- code_atlas/storage/__init__.py +14 -0
- code_atlas/storage/db.py +47 -0
- code_atlas/storage/migrations.py +552 -0
- code_atlas/storage/store.py +1045 -0
- code_atlas/ui/__init__.py +29 -0
- code_atlas/ui/i18n.py +143 -0
- code_atlas/ui/kit.py +689 -0
- code_atlas/ui/wizard.py +317 -0
- code_atlas_forge-1.1.0.dist-info/METADATA +246 -0
- code_atlas_forge-1.1.0.dist-info/RECORD +181 -0
- code_atlas_forge-1.1.0.dist-info/WHEEL +5 -0
- code_atlas_forge-1.1.0.dist-info/entry_points.txt +2 -0
- code_atlas_forge-1.1.0.dist-info/licenses/LICENSE +202 -0
- code_atlas_forge-1.1.0.dist-info/licenses/NOTICE +17 -0
- code_atlas_forge-1.1.0.dist-info/top_level.txt +1 -0
code_atlas/config.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Resolução de configuração do Code Atlas.
|
|
2
|
+
|
|
3
|
+
Ordem de precedência (Blueprint ADR-013):
|
|
4
|
+
|
|
5
|
+
1. Argumento explícito (``--root``).
|
|
6
|
+
2. Variáveis de ambiente ``CODE_ATLAS_*``.
|
|
7
|
+
3. ``XDG_CONFIG_HOME``/``XDG_DATA_HOME`` (Linux) ou ``%APPDATA%``/``%LOCALAPPDATA%`` (Windows).
|
|
8
|
+
4. Defaults por plataforma.
|
|
9
|
+
|
|
10
|
+
Nada é escrito dentro do repositório indexado por padrão.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
import tomllib
|
|
18
|
+
from collections.abc import Mapping
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
22
|
+
ENV_PREFIX = "CODE_ATLAS_"
|
|
23
|
+
ENV_ROOT = "CODE_ATLAS_ROOT"
|
|
24
|
+
ENV_CONFIG_DIR = "CODE_ATLAS_CONFIG_DIR"
|
|
25
|
+
ENV_DATA_DIR = "CODE_ATLAS_DATA_DIR"
|
|
26
|
+
|
|
27
|
+
APP_DIRNAME = "code-atlas"
|
|
28
|
+
CONFIG_FILENAME = "config.toml"
|
|
29
|
+
CONFIG_SCHEMA_VERSION = 1
|
|
30
|
+
|
|
31
|
+
_Env = Mapping[str, str]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _is_windows() -> bool:
|
|
35
|
+
return sys.platform == "win32"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def config_dir(env: _Env | None = None) -> Path:
|
|
39
|
+
"""Diretório de configuração (arquivo ``config.toml``)."""
|
|
40
|
+
e = os.environ if env is None else env
|
|
41
|
+
if override := e.get(ENV_CONFIG_DIR):
|
|
42
|
+
return Path(override).expanduser()
|
|
43
|
+
if _is_windows():
|
|
44
|
+
base = e.get("APPDATA") or e.get("LOCALAPPDATA")
|
|
45
|
+
if base:
|
|
46
|
+
return Path(base) / APP_DIRNAME
|
|
47
|
+
return Path.home() / "AppData" / "Roaming" / APP_DIRNAME
|
|
48
|
+
base = e.get("XDG_CONFIG_HOME")
|
|
49
|
+
return (Path(base) if base else Path.home() / ".config") / APP_DIRNAME
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def data_dir(env: _Env | None = None) -> Path:
|
|
53
|
+
"""Diretório de dados (bancos SQLite, manifests)."""
|
|
54
|
+
e = os.environ if env is None else env
|
|
55
|
+
if override := e.get(ENV_DATA_DIR):
|
|
56
|
+
return Path(override).expanduser()
|
|
57
|
+
if _is_windows():
|
|
58
|
+
base = e.get("LOCALAPPDATA") or e.get("APPDATA")
|
|
59
|
+
if base:
|
|
60
|
+
return Path(base) / APP_DIRNAME
|
|
61
|
+
return Path.home() / "AppData" / "Local" / APP_DIRNAME
|
|
62
|
+
base = e.get("XDG_DATA_HOME")
|
|
63
|
+
return (Path(base) if base else Path.home() / ".local" / "share") / APP_DIRNAME
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def config_file(env: _Env | None = None) -> Path:
|
|
67
|
+
"""Caminho completo do ``config.toml``."""
|
|
68
|
+
return config_dir(env) / CONFIG_FILENAME
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def resolve_root(arg: str | None = None, env: _Env | None = None) -> Path:
|
|
72
|
+
"""Resolve a raiz do workspace: argumento > ``CODE_ATLAS_ROOT`` > cwd."""
|
|
73
|
+
e = os.environ if env is None else env
|
|
74
|
+
candidate = arg or e.get(ENV_ROOT)
|
|
75
|
+
if candidate:
|
|
76
|
+
return Path(candidate).expanduser().resolve()
|
|
77
|
+
return Path.cwd().resolve()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True)
|
|
81
|
+
class WorkspaceConfig:
|
|
82
|
+
"""Configuração persistida do workspace (``config.toml``)."""
|
|
83
|
+
|
|
84
|
+
root: Path
|
|
85
|
+
schema_version: int = CONFIG_SCHEMA_VERSION
|
|
86
|
+
# Tabela [resolvers] crua — parsing tipado em resolvers/bootstrap
|
|
87
|
+
# (lf-0027); ausente = sem resolvers opt-in.
|
|
88
|
+
resolvers: Mapping[str, object] | None = None
|
|
89
|
+
# [indexing] retain_snapshots — máximo de snapshots STALE mantidos
|
|
90
|
+
# por repositório (lf-0031). 0/ausente = retenção ilimitada (default:
|
|
91
|
+
# histórico completo preservado para auditoria).
|
|
92
|
+
retain_snapshots: int = 0
|
|
93
|
+
|
|
94
|
+
def to_toml(self) -> str:
|
|
95
|
+
return (
|
|
96
|
+
"# Code Atlas workspace config\n"
|
|
97
|
+
f"schema_version = {self.schema_version}\n"
|
|
98
|
+
"\n"
|
|
99
|
+
"[workspace]\n"
|
|
100
|
+
f'root = "{self.root.as_posix()}"\n'
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def load(path: Path) -> WorkspaceConfig:
|
|
105
|
+
"""Carrega o ``config.toml``. Levanta ``ConfigError`` se inválido."""
|
|
106
|
+
try:
|
|
107
|
+
raw = tomllib.loads(path.read_text(encoding="utf-8"))
|
|
108
|
+
except (OSError, tomllib.TOMLDecodeError) as exc:
|
|
109
|
+
raise ConfigError(f"cannot read config {path}: {exc}") from exc
|
|
110
|
+
try:
|
|
111
|
+
root = Path(raw["workspace"]["root"])
|
|
112
|
+
except (KeyError, TypeError) as exc:
|
|
113
|
+
raise ConfigError(f"config {path} missing [workspace].root") from exc
|
|
114
|
+
res = raw.get("resolvers")
|
|
115
|
+
if res is not None and not isinstance(res, dict):
|
|
116
|
+
raise ConfigError(f"config {path}: [resolvers] must be a table")
|
|
117
|
+
idx = raw.get("indexing", {})
|
|
118
|
+
if not isinstance(idx, dict):
|
|
119
|
+
raise ConfigError(f"config {path}: [indexing] must be a table")
|
|
120
|
+
retain = idx.get("retain_snapshots", 0)
|
|
121
|
+
if not isinstance(retain, int) or isinstance(retain, bool) or retain < 0:
|
|
122
|
+
raise ConfigError(
|
|
123
|
+
f"config {path}: [indexing].retain_snapshots must be an "
|
|
124
|
+
"integer >= 0"
|
|
125
|
+
)
|
|
126
|
+
return WorkspaceConfig(
|
|
127
|
+
root=root,
|
|
128
|
+
schema_version=int(raw.get("schema_version", 0)),
|
|
129
|
+
resolvers=res,
|
|
130
|
+
retain_snapshots=retain,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ConfigError(Exception):
|
|
135
|
+
"""Erro de configuração do workspace."""
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def write_config(root: Path, env: _Env | None = None, *, force: bool = False) -> Path:
|
|
139
|
+
"""Escreve ``config.toml`` e garante o diretório de dados.
|
|
140
|
+
|
|
141
|
+
Levanta ``ConfigError`` se o arquivo já existir e ``force`` for falso.
|
|
142
|
+
"""
|
|
143
|
+
cfg_path = config_file(env)
|
|
144
|
+
if cfg_path.exists() and not force:
|
|
145
|
+
raise ConfigError(f"config already exists at {cfg_path} (use --force to overwrite)")
|
|
146
|
+
cfg_path.parent.mkdir(parents=True, exist_ok=True)
|
|
147
|
+
cfg_path.write_text(WorkspaceConfig(root=root).to_toml(), encoding="utf-8")
|
|
148
|
+
data_dir(env).mkdir(parents=True, exist_ok=True)
|
|
149
|
+
return cfg_path
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Context pack engine (lf-0040 / Cycle 4 Ph6+7)."""
|
|
2
|
+
|
|
3
|
+
from code_atlas.context.contracts import (
|
|
4
|
+
DOMAIN_PACK_SCHEMA_VERSION,
|
|
5
|
+
PACK_SCHEMA_VERSION,
|
|
6
|
+
Budget,
|
|
7
|
+
ContextItem,
|
|
8
|
+
ContextPack,
|
|
9
|
+
ContextRequest,
|
|
10
|
+
DomainEntityCard,
|
|
11
|
+
DomainNeighborRef,
|
|
12
|
+
DomainPackRequest,
|
|
13
|
+
DomainPackSection,
|
|
14
|
+
ExpansionRef,
|
|
15
|
+
PackDiagnostics,
|
|
16
|
+
PackEvidence,
|
|
17
|
+
PackManifest,
|
|
18
|
+
)
|
|
19
|
+
from code_atlas.context.domain import (
|
|
20
|
+
DOMAIN_REQUERY_CONTRACT,
|
|
21
|
+
DomainContextEngine,
|
|
22
|
+
resolve_domain_ref,
|
|
23
|
+
)
|
|
24
|
+
from code_atlas.context.engine import ContextEngine
|
|
25
|
+
from code_atlas.context.expand import (
|
|
26
|
+
ContextExpander,
|
|
27
|
+
ExpansionResult,
|
|
28
|
+
expansion_to_json,
|
|
29
|
+
)
|
|
30
|
+
from code_atlas.context.renderers import (
|
|
31
|
+
RENDERER_VERSION_JSON,
|
|
32
|
+
RENDERER_VERSION_MD,
|
|
33
|
+
render_json,
|
|
34
|
+
render_markdown,
|
|
35
|
+
)
|
|
36
|
+
from code_atlas.context.tokenizer import HeuristicTokenizer, TokenCounter
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"DOMAIN_PACK_SCHEMA_VERSION",
|
|
40
|
+
"DOMAIN_REQUERY_CONTRACT",
|
|
41
|
+
"PACK_SCHEMA_VERSION",
|
|
42
|
+
"RENDERER_VERSION_JSON",
|
|
43
|
+
"RENDERER_VERSION_MD",
|
|
44
|
+
"Budget",
|
|
45
|
+
"ContextEngine",
|
|
46
|
+
"ContextExpander",
|
|
47
|
+
"ContextItem",
|
|
48
|
+
"ContextPack",
|
|
49
|
+
"ContextRequest",
|
|
50
|
+
"DomainContextEngine",
|
|
51
|
+
"DomainEntityCard",
|
|
52
|
+
"DomainNeighborRef",
|
|
53
|
+
"DomainPackRequest",
|
|
54
|
+
"DomainPackSection",
|
|
55
|
+
"ExpansionRef",
|
|
56
|
+
"ExpansionResult",
|
|
57
|
+
"HeuristicTokenizer",
|
|
58
|
+
"PackDiagnostics",
|
|
59
|
+
"PackEvidence",
|
|
60
|
+
"PackManifest",
|
|
61
|
+
"TokenCounter",
|
|
62
|
+
"expansion_to_json",
|
|
63
|
+
"render_json",
|
|
64
|
+
"render_markdown",
|
|
65
|
+
"resolve_domain_ref",
|
|
66
|
+
]
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
"""Contratos do context pack engine (lf-0040 / Cycle 4 Ph6+7).
|
|
2
|
+
|
|
3
|
+
Determinístico e utilizável sem LLM: seleção por prioridade sobre
|
|
4
|
+
evidências persistidas, nunca geração. Níveis: ``orientation``,
|
|
5
|
+
``focus``, ``deep`` — outro nível sem reconstruir consulta (pack
|
|
6
|
+
carrega ``expansion_refs``).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import dataclasses
|
|
12
|
+
from collections.abc import Callable, Mapping
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
from typing import Literal
|
|
15
|
+
|
|
16
|
+
PACK_SCHEMA_VERSION = 2
|
|
17
|
+
|
|
18
|
+
# lf-0121 (Cycle 7 Ph13): a seção ``domain`` é campo ADITIVO de
|
|
19
|
+
# ``ContextPack`` — o envelope continua schema v2 (consumidores v2
|
|
20
|
+
# ignoram a chave nova); a seção carrega a própria versão.
|
|
21
|
+
DOMAIN_PACK_SCHEMA_VERSION = 1
|
|
22
|
+
|
|
23
|
+
Level = Literal["orientation", "focus", "deep"]
|
|
24
|
+
|
|
25
|
+
# Prioridade de seleção (spec lf-0040): menor = mais prioritário.
|
|
26
|
+
PRIORITY_ESSENTIAL = 0 # evidências essenciais
|
|
27
|
+
PRIORITY_DIRECT = 1 # símbolos diretos (match da task)
|
|
28
|
+
PRIORITY_CONTRACT = 2 # contratos (defs com docstring/signature)
|
|
29
|
+
PRIORITY_DEP = 3 # dependências comprovadas (arestas)
|
|
30
|
+
PRIORITY_SNIPPET = 4 # snippets de código
|
|
31
|
+
PRIORITY_COMPLEMENT = 5 # complementar (vizinhos transitivos)
|
|
32
|
+
PRIORITY_TRANSITIVE = 6 # transitivos menos relevantes
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass(frozen=True)
|
|
36
|
+
class ContextRequest:
|
|
37
|
+
"""Pedido de pack — snapshot resolvido para o READY atual."""
|
|
38
|
+
|
|
39
|
+
task: str # query livre da tarefa
|
|
40
|
+
repository_root: str = ""
|
|
41
|
+
snapshot_id: int | None = None
|
|
42
|
+
level: Level = "focus"
|
|
43
|
+
budget_tokens: int = 4000
|
|
44
|
+
max_symbols: int = 50
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class DomainPackRequest:
|
|
49
|
+
"""Pedido de pack semeado por entidade de domínio (lf-0121).
|
|
50
|
+
|
|
51
|
+
``seed`` — formas aceitas, nesta ordem:
|
|
52
|
+
|
|
53
|
+
- ``dom:<identity_key>`` — chave lógica canônica da entidade
|
|
54
|
+
(``DomainIdentity.logical_key``), escopada ao repositório;
|
|
55
|
+
- ``entity:<row_id>`` (ou dígitos puros) — row id de
|
|
56
|
+
``domain_entities`` **daquele** repositório;
|
|
57
|
+
- ``<qualified_name>`` — match exato em ``domain_entities`` do repo;
|
|
58
|
+
múltiplos matches viram ``seed-ambiguous`` honesto com as
|
|
59
|
+
``identity_key``s candidatas — nunca escolha por sorte.
|
|
60
|
+
|
|
61
|
+
``max_neighbors`` limita os vizinhos de ``domain_relations``
|
|
62
|
+
(top-N por confiança); os excedentes viram ``expansion_refs``
|
|
63
|
+
re-consultáveis — nunca silenciados.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
seed: str
|
|
67
|
+
repository_root: str = ""
|
|
68
|
+
snapshot_id: int | None = None
|
|
69
|
+
level: Level = "focus"
|
|
70
|
+
budget_tokens: int = 4000
|
|
71
|
+
max_neighbors: int = 20
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
|
+
class PackEvidence:
|
|
76
|
+
"""Referência a evidência persistida — nunca inventada."""
|
|
77
|
+
|
|
78
|
+
kind: str # "occurrence"|"relation"|"diagnostic"
|
|
79
|
+
ref: str # id/canônico
|
|
80
|
+
detail: str = ""
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class ContextItem:
|
|
85
|
+
"""Unidade de contexto com custo estimado e prioridade."""
|
|
86
|
+
|
|
87
|
+
item_id: str # estável p/ expansão/dedup
|
|
88
|
+
kind: str # "symbol"|"file"|"dependency"|"evidence"|"snippet"
|
|
89
|
+
priority: int # PRIORITY_*
|
|
90
|
+
title: str
|
|
91
|
+
body: str # texto indexável/contábil
|
|
92
|
+
est_tokens: int
|
|
93
|
+
symbol_key: str = ""
|
|
94
|
+
path: str = ""
|
|
95
|
+
evidence: tuple[PackEvidence, ...] = ()
|
|
96
|
+
expandable: bool = False # tem refs p/ nível mais profundo
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class ExpansionRef:
|
|
101
|
+
"""Referência para disclosure progressivo (outro nível)."""
|
|
102
|
+
|
|
103
|
+
ref_id: str
|
|
104
|
+
kind: str # "symbol"|"file"|"dependency"
|
|
105
|
+
reason: str # por que está disponível p/ expandir
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@dataclass(frozen=True)
|
|
109
|
+
class Budget:
|
|
110
|
+
"""Orçamento declarado + reservas do envelope."""
|
|
111
|
+
|
|
112
|
+
limit: int
|
|
113
|
+
reserved_meta: int
|
|
114
|
+
reserved_evidence: int
|
|
115
|
+
reserved_diagnostics: int
|
|
116
|
+
available_items: int # limit - reservas
|
|
117
|
+
estimator: str # id do estimador (heurístico)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class PackDiagnostics:
|
|
122
|
+
"""Diagnósticos tipados do build."""
|
|
123
|
+
|
|
124
|
+
codes: tuple[str, ...] = ()
|
|
125
|
+
messages: tuple[str, ...] = ()
|
|
126
|
+
insufficient_budget: bool = False
|
|
127
|
+
mandatory_context_omitted: bool = False
|
|
128
|
+
suggested_minimum_budget: int = 0
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@dataclass(frozen=True)
|
|
132
|
+
class PackManifest:
|
|
133
|
+
"""Metadados do pack — auditoria do build."""
|
|
134
|
+
|
|
135
|
+
schema_version: int
|
|
136
|
+
repository: str
|
|
137
|
+
snapshot_id: int
|
|
138
|
+
level: Level
|
|
139
|
+
estimator: str
|
|
140
|
+
tokens_estimated: bool # True — nunca contagem exata
|
|
141
|
+
selected: int
|
|
142
|
+
omitted: int
|
|
143
|
+
truncated: bool
|
|
144
|
+
freshness: str # id do snapshot + estado
|
|
145
|
+
ranking_explanation: str
|
|
146
|
+
tokens_payload: int = 0 # estimativa do payload serializado
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------
|
|
150
|
+
# lf-0121 — seção de domínio (aditiva; envelope segue schema v2)
|
|
151
|
+
# ---------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@dataclass(frozen=True)
|
|
155
|
+
class DomainEntityCard:
|
|
156
|
+
"""Cartão da entidade-semente — identidade + ancoragem no snapshot.
|
|
157
|
+
|
|
158
|
+
``identity_key`` é o ref re-consultável (ver ``requery`` da seção).
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
entity_id: int # row id em domain_entities
|
|
162
|
+
occurrence_id: int # 1ª ocorrência da entidade no snapshot
|
|
163
|
+
occurrence_count: int
|
|
164
|
+
domain: str
|
|
165
|
+
kind: str # DomainEntityKind (data_table, …)
|
|
166
|
+
name: str
|
|
167
|
+
qualified_name: str
|
|
168
|
+
identity_key: str # dom:… lógica — ref estável
|
|
169
|
+
repository_id: int
|
|
170
|
+
snapshot_id: int
|
|
171
|
+
catalog: str = ""
|
|
172
|
+
namespace: str = ""
|
|
173
|
+
environment: str = ""
|
|
174
|
+
source_path: str = ""
|
|
175
|
+
extraction_status: str = ""
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
@dataclass(frozen=True)
|
|
179
|
+
class DomainNeighborRef:
|
|
180
|
+
"""Vizinho de domínio incluído no pack — espelho estruturado do item.
|
|
181
|
+
|
|
182
|
+
``ref`` é o identificador re-consultável: ``dom:<identity_key>``
|
|
183
|
+
para entidades (mesmo valor do ``ref_id`` de ``expansion_refs``) ou
|
|
184
|
+
``sym:<qualified_name>`` para símbolo de código (ponte
|
|
185
|
+
domínio↔código via ``*_symbol_occ_id``).
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
item_id: str # == ref — item carrega o ref como id
|
|
189
|
+
ref: str # dom:… | sym:<qualified_name>
|
|
190
|
+
kind: str # "entity" | "symbol"
|
|
191
|
+
entity_kind: str # data_table… | kind do símbolo
|
|
192
|
+
display: str # qualified_name legível
|
|
193
|
+
direction: str # incoming | outgoing | both
|
|
194
|
+
relation_types: tuple[str, ...]
|
|
195
|
+
resolution_status: str # status da aresta de maior confiança
|
|
196
|
+
confidence_class: str # classe de confiança mais forte
|
|
197
|
+
edge_count: int
|
|
198
|
+
evidence_ids: tuple[int, ...] = ()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@dataclass(frozen=True)
|
|
202
|
+
class DomainPackSection:
|
|
203
|
+
"""Seção aditiva de domínio no ``ContextPack`` (schema próprio v1).
|
|
204
|
+
|
|
205
|
+
``status`` é a palavra honesta do build:
|
|
206
|
+
|
|
207
|
+
- ``ok`` — seed resolvida, seção completa;
|
|
208
|
+
- ``absent`` — snapshot sem ocorrências de domínio (nenhuma entidade
|
|
209
|
+
observada — repo indexado sem adapters de domínio ou snapshot
|
|
210
|
+
anterior à v9);
|
|
211
|
+
- ``seed-not-found`` — seed não casa nenhuma entidade do repo;
|
|
212
|
+
- ``seed-ambiguous`` — seed por ``qualified_name`` casou >1
|
|
213
|
+
entidade; ``candidates`` lista as ``identity_key``s;
|
|
214
|
+
- ``seed-not-in-snapshot`` — entidade existe no repo mas sem
|
|
215
|
+
ocorrência neste snapshot.
|
|
216
|
+
|
|
217
|
+
``counts`` agrega TODAS as arestas do snapshot que tocam a seed
|
|
218
|
+
(não só as incluídas): ``incoming``/``outgoing`` por direção,
|
|
219
|
+
``resolved``/``ambiguous``/``external`` por status,
|
|
220
|
+
``unresolved`` exclui alvos com marcador de honestidade
|
|
221
|
+
(``dynamic:``, ``${dynamic}``, ``environment:``) — esses contam em
|
|
222
|
+
``dynamic`` à parte.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
schema_version: int
|
|
226
|
+
status: str
|
|
227
|
+
entity: DomainEntityCard | None
|
|
228
|
+
neighbors: tuple[DomainNeighborRef, ...] # incluídos (top-N ∩ budget)
|
|
229
|
+
neighbors_total: int # candidatos dedupados
|
|
230
|
+
neighbors_omitted: int # total - incluídos
|
|
231
|
+
counts: Mapping[str, int]
|
|
232
|
+
unresolved_targets: tuple[str, ...] # amostra de alvos pendentes
|
|
233
|
+
ambiguous_targets: tuple[str, ...] # amostra de alvos ambíguos
|
|
234
|
+
summary: str # domains.summarize — fatos
|
|
235
|
+
requery: str # contrato dos expansion refs
|
|
236
|
+
candidates: tuple[str, ...] = () # seeds ambíguas
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass(frozen=True)
|
|
240
|
+
class DomainPackPart:
|
|
241
|
+
"""Saída do ``domain_factory`` — construída DENTRO do loop de
|
|
242
|
+
enforcement de budget, logo tudo aqui entra na estimativa do
|
|
243
|
+
payload serializado."""
|
|
244
|
+
|
|
245
|
+
section: DomainPackSection
|
|
246
|
+
extra_expansion_refs: tuple[ExpansionRef, ...] = () # vizinhos além do top-N
|
|
247
|
+
extra_unresolved: tuple[str, ...] = () # → unresolved_refs
|
|
248
|
+
ranking_explanation: str = "" # override do manifest
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
DomainSectionFactory = Callable[[tuple[ContextItem, ...]], "DomainPackPart | None"]
|
|
252
|
+
"""Monta a seção a partir dos itens SELECIONADOS de uma iteração do
|
|
253
|
+
enforcement — garante que ``section.neighbors`` espelha exatamente o
|
|
254
|
+
que o pack entrega (nunca anuncia vizinho despejado por budget)."""
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@dataclass(frozen=True)
|
|
258
|
+
class ContextPack:
|
|
259
|
+
"""Envelop versionado — o artefato final do engine.
|
|
260
|
+
|
|
261
|
+
``domain`` é aditivo (lf-0121): ``None`` para packs de símbolo;
|
|
262
|
+
``DomainPackSection`` para packs semeados por entidade de domínio.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
manifest: PackManifest
|
|
266
|
+
budget: Budget
|
|
267
|
+
items: tuple[ContextItem, ...]
|
|
268
|
+
selected_symbols: tuple[str, ...]
|
|
269
|
+
relevant_files: tuple[str, ...]
|
|
270
|
+
dependencies: tuple[PackEvidence, ...]
|
|
271
|
+
unresolved_refs: tuple[str, ...]
|
|
272
|
+
expansion_refs: tuple[ExpansionRef, ...]
|
|
273
|
+
diagnostics: PackDiagnostics
|
|
274
|
+
domain: DomainPackSection | None = None
|
|
275
|
+
|
|
276
|
+
def to_dict(self) -> dict[str, object]:
|
|
277
|
+
return dataclasses.asdict(self)
|