Skip to main content

Gate determinista de complejidad y task-contracts (CCDD), con servidor MCP. Sin LLM en el veredicto.

Project description

ccdd-gate

tests License: MIT

Gates deterministas que mantienen honesto al código escrito por IA. Un modelo pequeño implementa; un árbitro que no se puede sobornar decide si pasó.

No es otro loop "dale al modelo grande hasta que diga que está listo". La condición de parada no es el juicio del modelo —es un veredicto determinista: complejidad ≤ budget firmado + property-tests congelados que el implementador no puede ablandar. Mismo input, mismo veredicto, corrida a corrida.

El mismo principio cubre dos pilares:

  • Código (gate de complejidad/tests): verifica que la función implementada respeta el budget y pasa los property-tests congelados. 100% determinista.
  • Comportamiento de agentes (pilar de evals): para la salida NO determinista de un agente (texto/JSON), un dataset congelado + checks deterministas (schema, contención, citas/groundedness anti-alucinación, PII, trayectoria) deciden PASS/FAIL sin LLM (Tier 1); un juez LLM acotado y auditado contra un golden set es Tier 2 opt-in. Ver Pilar de evals.

Construido sobre CCDD (incluido aquí como ccdd.py, MIT). El sustrato determinista no llama a ningún LLM: el cerebro es el agente anfitrión (Claude Code, Cursor, etc.) que invoca estas herramientas.


La idea en una frase

El modelo grande planifica y audita; el modelo pequeño (local/barato) implementa; un gate determinista decide. El grande solo entra a autorar la tarea, auditar los tests y rescatar cuando el chico se atasca. La inteligencia difusa va en el LLM; el control y el veredicto van en código que no se puede engañar.

Qué hay acá

Pieza Qué hace LLM
runners/metrics_backends.py Capa neutral compartida (umbrales + severity + lint_results) y registro get_backend(language|extension) para backends por lenguaje no
runners/metrics.py Backend Python: métricas de complejidad por AST (ciclomática, anidamiento, params, longitud) no
runners/metrics_treesitter.py Backend universal vía tree-sitter (TS/TSX/JS) — dep opcional; sin ella, solo Python no
runners/pre_complexity_runner.py Orquestador L3 para el contrato pre-complexity-agent no
runners/pre_complexity_helpers.py Data-helpers para inyectar contexto de diseño y negocio no
runners/complexity_runner.py Orquestador L3 para el contrato complexity-agent no
runners/complexity_gate.py Gate determinista; CLI o hook PostToolUse de Claude Code no
runners/tc_lint.py Linter del task-contract (anti-desvarío del autor) no
runners/task_gate.py Veredicto unificado: tc_lint + tests congelados + complejidad≤budget + anotaciones + firma implementada (gate-signature) + deps opt-in (gate-deps) + aprobación; kind:group compone hijas + test de integración no
runners/deps_check.py unauthorized_imports: imports top-level de terceros NO permitidos (núcleo del enforcement de deps_allowed / anti-slopsquatting). AST puro no
runners/sig_check.py signature_mismatch: la firma IMPLEMENTADA vs la del contrato (nombre + nombres de params en orden); caza el drift de firma. AST puro no
runners/purity_check.py impure_operations: operaciones impuras en el cuerpo (print/open/eval/exec/import/input, global/nonlocal, import interno). AST puro no
runners/mutdef_check.py mutable_defaults: params con default mutable ([]/{}/set()/list()/dict()). AST puro no
runners/bareexcept_check.py bare_except_lines: líneas de except: desnudo (sin tipo). AST puro no
runners/assert_check.py assert_lines: líneas de assert (desaparecen con python -O). AST puro no
runners/nonecmp_check.py none_eq_lines: comparaciones con None por ==/!= (en vez de is/is not). AST puro no
runners/coverage_check.py function_lines: líneas del cuerpo que la ejecución debería cubrir (primitivo, sin etapa). AST puro no
runners/rules_gate.py Aplica los checks deterministas project-wide por glob desde un rules.yaml (idea declarativa estilo autorules, árbitro AST sin LLM) no
runners/audit_composition.py Auditor project-wide: composición sin gatear (función importa a otra sin kind:group); distingue deuda de FORMA vs de COMPORTAMIENTO no
runners/audit_orphan_targets.py Auditor project-wide: .py de implementación que no son target de ningún contrato (código fuera del flujo gate); exime datos puros no
runners/audit_annotations.py Auditor project-wide: nombres en anotaciones sin importar/definir; caza bugs de portabilidad que lazy annotations (PEP 649) enmascara no
runners/mutation_audit.py Mide la fuerza del oráculo vía mutation testing determinista (mutaciones fijas → corre los tests congelados por mutante); superviviente = test débil no
runners/approve_tests.py Firma humana de los tests (tests_sha256), a prueba de manipulación no
runners/orchestrator.py Loop stateless: pequeño implementa → tests fallan/complejidad falla → reintenta (sin memoria) → escala al grande sí (worker)
runners/test_audit.py Auditoría advisory de los tests contra el contrato sí (advisory)
runners/measure.py Harness de medición: tokens/intentos/escalados, costo vs loop grande no
runners/complexity_mcp.py Servidor MCP (stdio JSON-RPC) que expone el sustrato no
runners/mcp_smoke.py Smoke test del MCP no
runners/guardrails_lang.yaml Guardrails específicos por lenguaje no
runners/eval_gate.py Pilar de evals — Tier 1: veredicto determinista sobre output NO determinista (agentes). Dataset congelado + checks (schema, contención, citas/groundedness, PII, trayectoria) no
runners/eval_checks.py Checkers deterministas Tier 1 (una función por check; anti-alucinación de fuentes y evaluación de trayectoria) no
runners/approve_eval_cases.py Firma humana del dataset de evals (cases_sha256), a prueba de manipulación no
runners/eval_judge.py Tier 2 (opt-in): juez LLM acotado (modelo pinneado, temp 0) — el único módulo del pilar que llama a un LLM sí (opt-in)
runners/judge_audit.py Fuerza/deriva del juez: acuerdo vs golden set humano (análogo a mutation_audit); si baja del umbral, falla el JUEZ sí (opt-in)
contracts/ Rubrics firmados: pre-complexity-agent, complexity-agent, task-author-agent, eval-agent (juez Tier 2)

Quickstart

pip install -r requirements.txt

# 1) Gate determinista sobre un task-contract de ejemplo (PASS)
python runners/task_gate.py examples/sandbox/task.md

# 2) Tests congelados (deterministas, sin LLM): gate + gobernanza L2 (Ed25519) + reglas tc_lint
python -m unittest tests.test_gates tests.test_l2_governance tests.test_tc_lint_rules -v

# 3) Linter de un task-contract
python runners/tc_lint.py examples/sandbox/task.md

Como hook de auto-validación en Claude Code

settings.jsonhooks.PostToolUse con matcher Write|Edit:

{ "command": "python runners/complexity_gate.py" }

Cada archivo que el agente escribe se mide con el backend de su lenguaje (por extensión, o --language en CLI); si una métrica entra en CRÍTICA (umbral firmado), el hook bloquea y pide refactor. Determinista, sin tokens. Una extensión sin backend registrado es un no-op anunciado (aviso por stderr, exit 0), nunca un fallo silencioso. Hoy el único backend es Python.

Instalación como paquete (console scripts)

Para usar ccdd-gate sin clonar ni rutas absolutas, instálalo como distribución (deja los scripts ccdd-mcp, ccdd-lint, ccdd-gate, ccdd-measure en el PATH):

pipx install ccdd-gate        # o: uvx ccdd-gate · o: pip install ccdd-gate

El modo desde el repo (python runners/<x>.py) sigue funcionando para desarrollo. Nota: los datos de gobernanza (rubric/attestations bajo contracts/, schema en la raíz) se resuelven en el layout del repo / instalación editable (pip install -e .); el núcleo determinista (medir/lintar/gate/MCP) funciona en cualquier modo.

Como MCP

Con el paquete instalado, el .mcp.json no necesita clonar el repo ni cwd:

{ "mcpServers": { "ccdd": { "command": "ccdd-mcp" } } }

Desde el repo, copiá .mcp.json.example a .mcp.json. Tools (sin LLM):

  • measure_complexity(code) — métricas AST reales por función.
  • complexity_rubric(agent) — el criterio gobernado (system/policies/thresholds) firmado.
  • scan_guardrails(code, agent?, language?, filename?) — guardrails deterministas: secretos (texto-puro, igual en todo lenguaje), anidamiento (estructural, vía el backend del lenguaje), dsv_check (anti-alucinación por "drift", exige coincidencia exacta con HEAD local) y específicos por lenguaje opt-in (runners/guardrails_lang.yaml, p. ej. no-eval). agent evalúa contra ese contrato. Sin language, Python.
  • lint_task_contract(contract_text, test_code?) - valida un task-contract (anti-desvarío del modelo grande).
  • scan_dependencies(code, deps_allowed?) - imports top-level de terceros NO permitidos (enforcement de deps_allowed / anti-slopsquatting). Determinista, sin LLM.
  • check_signature(source, fn_name, expected_signature) - "" si la firma implementada coincide con la esperada (nombre + nombres de params en orden), o el desajuste. Determinista, sin LLM.
  • check_purity(source, fn_name, target_line?) - operaciones impuras del cuerpo (gate-purity). Sin LLM.
  • check_mutable_defaults(source, fn_name, target_line?) - params con default mutable (gate-mutdef). Sin LLM.
  • check_bare_except(source, fn_name, target_line?) - líneas de except: desnudo (gate-bareexcept). Sin LLM.
  • check_asserts(source, fn_name, target_line?) - líneas de assert (gate-assert). Sin LLM.
  • check_none_cmp(source, fn_name, target_line?) - comparaciones con None por ==/!= (gate-nonecmp). Sin LLM.
  • run_rules_gate(rules_path?, root?) - aplica los checks deterministas project-wide por glob desde un rules.yaml. Sin LLM.
  • run_integration_gate(task_path) - veredicto PASS/FAIL unificado de un contrato YA EN DISCO (lint + aprobación de tests + tests congelados + complejidad ≤ budget), idéntico a la CLI task_gate.py. Para kind:group compone las hijas + el test de integración sobre los archivos reales (sin sandbox). El agente NO implementa: delega a run_ephemeral_agent.
  • run_ephemeral_agent(task_path) - delega la implementación al modelo pequeño local y la valida contra el gate. El servidor fija modelo y endpoint; el LLM anfitrión solo pasa task_path (no elige el modelo). El operador puede elegir el modelo por entorno (CCDD_EXECUTOR_MODEL, CCDD_EXECUTOR_API) sin tocar la fuente; el LLM no. Default: qwen3-coder:480b-cloud vía Ollama (http://localhost:11434/v1).
  • audit_composition(root?) - composición sin gatear project-wide; separa deuda de FORMA (composición ejercitada por el test del composer) de deuda de COMPORTAMIENTO (mock o test ausente). ok = sin deuda de comportamiento.
  • audit_orphan_targets(root?) - .py de implementación que no son target de ningún contrato (exime tests/__init__/datos puros). Para proyectos 100% CCDD.
  • audit_annotations(root?) - nombres usados en anotaciones sin importar/definir, sobre todos los targets; caza bugs de portabilidad que Python 3.14 (lazy annotations) enmascara en runtime.
  • mutation_audit(task_path) - fuerza del oráculo vía mutation testing determinista; un mutante sobreviviente delata un test débil. Opt-in (corre los tests por mutante).
  • request_human_attestation(code, reason) - permite al agente pedir una excepción firmada cuando no puede reducir la complejidad por reglas de negocio.
  • run_eval_gate(eval_path) - pilar de evals (Tier 1): veredicto determinista sobre el output NO determinista de un agente (dataset congelado + checks). Sin LLM.
  • eval_rubric() - rúbrica firmada del juez Tier 2 (contrato eval-agent).
  • judge_audit(eval_path, provider?) - acuerdo del juez Tier 2 vs golden set humano; si baja del umbral, el juez no es de fiar. Provider stub (determinista) por defecto.

Checklist de cierre (antes de dar una tarea por terminada): corré las cuatro auditorías —audit_composition, audit_orphan_targets, audit_annotations y mutation_audit— hasta ok:true. El gate de función no cubre composición, código huérfano, anotaciones ni la fuerza del oráculo; quedarse con la auditoría que da verde y declarar "todo en verde" es el modo de falla que la checklist existe para cerrar (y que el CI hace no-opcional).

Pilar de evals: gatear output NO determinista (Tier 1 + Tier 2 opt-in)

El gate de complejidad/tests verifica código (funciones con oráculo independiente). Pero un agente de producción (un bot de soporte, un asistente de research) produce texto/JSON no determinista que ese gate no cubre. El pilar de evals lo cierra sin renunciar al determinismo donde se puede, en dos niveles:

  • Tier 1 — checks deterministas, sin LLM (eval_gate.py + eval_checks.py). Sobre cada caso de un dataset CONGELADO y firmado (cases_sha256, igual que tests_sha256): schema del output, contención/ausencia de términos, must_cite + groundedness (toda cita apunta a una fuente existente → anti-alucinación), PII, y evaluación de trayectoria (tools requeridas/prohibidas, max_steps). Veredicto = función del budget: pass_rate ≥ pass_rate_min y violaciones duras ≤ forbidden_violations_max. Mismo input → mismo veredicto. Muchos agentes (extracción, clasificación, routing) se gatean 100% aquí.

  • Tier 2 — juez LLM ACOTADO, opt-in (eval_judge.py + judge_audit.py). Solo para lo genuinamente subjetivo (coherencia, utilidad). El modelo se pinnea (temperature 0) y su veredicto no cuenta hasta pasar judge_audit: el juez se calibra contra un golden set atestado por humano y debe alcanzar agreement ≥ agreement_min. Es a la calidad lo que mutation_audit es al oráculo: no confía en el juez, lo mide. La única relajación de determinismo (el score por-corrida del LLM) queda acotada y auditada; cualquier deriva del modelo pinneado la caza judge_audit en CI.

# Firmar el dataset (OK humano, congela los casos)
python runners/approve_eval_cases.py examples/eval/support-bot-refunds/eval.md

# Veredicto Tier 1 (sin LLM, reproducible)
python runners/eval_gate.py examples/eval/support-bot-refunds/eval.md

# Calibrar el juez Tier 2 contra el golden set (offline con provider stub)
python runners/judge_audit.py examples/eval/support-bot-refunds/eval.md

El eval-contract (front-matter YAML + cuerpo, espeja al task-contract) declara target, agent_entry, dataset, budget, deterministic_checks y el bloque judge (opt-in). Ver examples/eval/support-bot-refunds/ para el ejemplo end-to-end (agente determinista de juguete, dataset firmado, schema y rúbrica).

El loop grande/pequeño (Stateless Feedback y Evolución CEFL)

El loop es stateless (sin estado) pero incorpora mecánicas evolutivas inspiradas en CEFL (Candidate Expansion and Freezing):

  1. Expansión Paralela: El orquestador no pide 1 intento; pide N candidatos paralelos (--candidates 3).
  2. Torneo de Complejidad (Freezing): Cada candidato se aísla y se valida contra los tests y el budget de complejidad. Si varios pasan, el Gate elige automáticamente el que tenga la menor puntuación matemática de complejidad, congelándolo como la respuesta definitiva.
  3. Feedback Masivo (Partial Success): Si todos los candidatos fallan, el sistema no reenvía código roto uno por uno. Agrupa las N soluciones fallidas junto a sus errores (stack traces) en un JSON combinado masivo. Esto permite al modelo en la siguiente iteración aprender cruzando los fracasos de todas sus rutas exploratorias, resolviendo problemas que un solo reintento jamás lograría.
# OFFLINE (sin modelo): stub que entrega 1 impl rota y 1 buena -> intento 1 FAIL, intento 2 PASS
python runners/orchestrator.py examples/sandbox/loop_demo/task.md \
  --provider stub \
  --stub examples/sandbox/loop_demo/_stub_bad.py \
  --stub examples/sandbox/loop_demo/_stub_good.py --max-attempts 3

# CON MODELOS: el pequeño genera 3 vías paralelas; si todas fallan, escala al grande
python runners/orchestrator.py examples/sandbox/loop_demo/task.md \
  --provider openai --model <modelo-chico> --candidates 3 \
  --escalate-provider ollama --escalate-model <modelo-grande> --escalate-attempts 2

Providers: ollama, openai (LM Studio/vLLM, urllib stdlib), anthropic (SDK), y stub (secuencia offline para probar la mecánica sin modelo).

task-contract (formato)

Front-matter YAML (machine-checkable) + cuerpo Markdown (prescriptivo). Ver examples/sandbox/task.md y el rubric de autoría en contracts/task-author-agent/. Regla central: especifica el contrato y los property-tests con oráculo independiente, NO el algoritmo. Los tests se congelan y firman antes de que el implementador toque la tarea.

Campo target_line (opcional, desambiguación). El gate de complejidad resuelve la función objetivo por nombre. Si el target tiene varias funciones/métodos homónimos (p. ej. set en varias clases), declara target_line: N (la línea de la def correcta) para que mida esa y no otra. Sin desambiguador y con >1 def del nombre, el gate devuelve INVALID (ambiguo) en vez de medir la última en silencio (issue #41). Con un solo match el campo es innecesario (comportamiento idéntico).

Campo enforce_deps (opcional, anti-slopsquatting). Si enforce_deps: true, el gate corre la etapa gate-deps: flaggea los imports top-level del target que no estén en deps_allowed (ni en la stdlib) y falla con stage: gate-deps. Opt-in (default off): los contratos que no lo declaran no cambian. Limitación actual: trata como "tercero" cualquier import no-stdlib que no esté en deps_allowed, así que con enforce_deps los módulos locales del propio proyecto deben listarse en deps_allowed (la exención automática de módulos locales queda como mejora futura). Solo Python por ahora.

Conformidad de firma (etapa gate-signature, default-on). El gate compara la firma IMPLEMENTADA con la signature del contrato (nombre + nombres de parámetros en orden; ignora anotaciones, defaults y retorno) y falla con stage: gate-signature si difieren — caza el drift de firma que rompe a los callers. Es default-on: no requiere campo. Solo Python (compara vía AST).

Gates de antipatrones opt-in (default-off, Python, vía AST). Cada uno corre solo si el contrato declara su campo; sin él, el contrato no cambia. Todos fallan con su propio stage:

Campo Etapa Falla si la función…
pure: true gate-purity tiene operaciones impuras (print/open/eval/exec/__import__/input, global/nonlocal, import interno)
forbid_mutable_defaults: true gate-mutdef tiene params con default mutable ([]/{}/set()/list()/dict())
forbid_bare_except: true gate-bareexcept tiene un except: desnudo (sin tipo)
forbid_assert: true gate-assert usa assert (desaparece con python -O)
forbid_none_eq: true gate-nonecmp compara con None usando ==/!= en vez de is/is not

Cada uno honra target_line para funciones homónimas y tiene su tool MCP (check_purity, check_mutable_defaults, check_bare_except, check_asserts, check_none_cmp).

Reglas project-wide por glob (rules_gate). Los gates de antipatrón de arriba se disparan por contrato. Para aplicarlos a TODO el repo sin contratos, runners/rules_gate.py lee un rules.yaml declarativo (lista de {check, files}) y corre el check determinista sobre cada función de los archivos que matchean el glob — política de repo, no solo por función gateada. La idea del formato declarativo + glob está tomada de autorules, pero el árbitro es AST determinista, no un LLM (autorules usa un juez LLM; ccdd-gate no). Ver examples/rules.yaml.example y la evaluación completa en docs/evaluations/autorules.md.

python runners/rules_gate.py rules.yaml [root]   # exit 0 ok · 1 violaciones · 2 config inválida

Campo language (opcional, multi-lenguaje). Por defecto python. Con language: python la firma se valida con el AST nativo (preciso). Para otros lenguajes (typescript, javascript, go, …) tc_lint valida la firma por aridad genérica (cuenta de parámetros top-level y extracción de nombre, respetando ()[]{}<> y comillas) y emite el warning tc-signature-generic para señalar que no hay parser nativo. params_max y el resto de reglas se aplican igual. Sin el campo, el comportamiento es idéntico al actual (Python).

Conformancia multi-lenguaje

fixtures/conformance/ define un oráculo congelado de las 4 métricas (fixtures equivalentes por lenguaje + valores esperados). Todo backend debe reproducirlo: Python es el baseline y el backend TypeScript/JS (tree-sitter) pasa la suite con métricas estructurales idénticas (cyclomatic/nesting_depth/parameter_count); solo function_length diverge por formato y se fija por-lenguaje. Un backend nuevo no se acepta hasta pasar tests/test_conformance.py. Ver fixtures/conformance/README.md.

Multi-lenguaje hoy: Python nativo (sin deps) + TS/TSX/JS vía tree-sitter (dep opcional: pip install tree_sitter tree_sitter_typescript). El gate, el hook y measure_complexity miden .ts/.js igual que .py cuando la dep está instalada; si no, esos archivos son no-op anunciado.

Integración GitHub (opcional, integrations/github/)

Capa adaptadora opcional: el sustrato determinista no depende de GitHub. Sin ella, todo funciona en local. Usa gh CLI; tokens por entorno, nunca en el repo.

  • reporter.py — toma el JSON de task_gate/complexity_gate y genera un comentario Markdown determinista (PASS/FAIL, métricas vs budget, motivo). Idempotente: actualiza un comentario "marcado" en vez de spamear. Offline imprime el Markdown; --post lo publica vía gh.
  • ci_gate.py + .github/workflows/ccdd-gate.ymlGitHub Action: en cada PR descubre los task-contracts afectados (el .md o su target), corre tc_lint + task_gate y bloquea el merge (exit 1) si el veredicto no pasa; publica el resumen como comentario idempotente vía el Reporter. Además corre no-opcionalmente audit_composition y audit_annotations project-wide y mutation_audit sobre los contratos afectados: composición sin gatear, anotaciones sin importar o mutantes sobrevivientes también ponen el check en rojo (lo que el autor tiende a saltar cuando es opt-in, acá no se puede saltar). Sin LLM, sin secretos (usa el GH_TOKEN del runner). Copiable a un repo consumidor: copia .github/workflows/ccdd-gate.yml e integrations/github/ (o vendoriza/instala ccdd-gate) y activa branch protection sobre el check ccdd-gate.
  • scaffold.py — genera el esqueleto de un task-contract desde un issue (--issue owner/repo#N o --from-json offline). Captura la intención (título/cuerpo/labels) con placeholders TODO; el resultado es incompleto a propósito (tc_lint lo marca, no falsamente verde).
  • lifecycle.py — conecta el orquestador con el ciclo de vida del issue: un issue ccdd:ready con gate verde abre un PR enlazado (Closes #N) y pasa a ccdd:in-review; si escala → ccdd:escalated; si ni el grande pasa → ccdd:needs-split. El loop del orquestador no sabe de GitHub (recibe un callback opcional on_result); sin él, corre igual en local. Transiciones de label deterministas y reversibles.
  • decompose.py — materializa task-contracts atómicos como sub-issues de un issue padre (epic/feature), con vínculo bidireccional y marker idempotente (re-ejecutar no duplica). No decide la descomposición (la decide el autor): solo la materializa.
  • link.py — vínculo bidireccional contrato↔issue: status --contract (estado + labels) o status --issue owner/repo#N (contratos que lo referencian), y sync-labels (refleja el estado como labels ccdd:*, idempotente, sin pisar labels ajenas). El campo issue del front-matter es opcional; tc_lint valida su formato (regla tc-issue-ref) sin romper contratos sin él.

Benchmarks

Ver BENCHMARKS.md. En resumen: el gate determinista cuesta 0 tokens y su lógica es sub-milisegundo (python benchmarks/bench_gate.py). La economía grande/pequeño está medida y honestamente etiquetada como ilustrativa — gana por reuso/volumen, no en el one-shot trivial (ahí es más caro).

Honestidades (léelas antes de creerle a nadie)

  • El ahorro de tokens es condicional, no universal. En una tarea trivial de un tiro, este flujo puede salir más caro que solo llamar al modelo grande. Gana por volumen, reuso y dificultad. Medilo vos con runners/measure.py; no te fíes del titular.
  • El gate es tan fuerte como sus property-tests. Tests laxos → el modelo pequeño pasa basura. La auditoría del modelo grande (test_audit.py) y el oráculo independiente son lo que hace que el veredicto signifique algo.
  • task_gate ejecuta los tests. En local con tus modelos es seguro; para correr código ajeno usá un sandbox aislado (contenedor), no el host.
  • Auditar tests requiere un modelo grande. Un modelo de ~12B como auditor tiende a aprobar tests rotos. Implementar lo hace bien; auditar, no.

Licencia

MIT © 2026 Mauricio Perera. Incluye ccdd.py y ccdd_context.schema.json del proyecto CCDD (mismo autor, MIT), con una única adaptación para uso standalone: ccdd.py resuelve su schema junto a sí mismo (upstream lo busca en el directorio padre). Publicado as-is, sin garantía ni soporte.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ccdd_gate-0.1.0.tar.gz (126.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ccdd_gate-0.1.0-py3-none-any.whl (108.5 kB view details)

Uploaded Python 3

File details

Details for the file ccdd_gate-0.1.0.tar.gz.

File metadata

  • Download URL: ccdd_gate-0.1.0.tar.gz
  • Upload date:
  • Size: 126.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for ccdd_gate-0.1.0.tar.gz
Algorithm Hash digest
SHA256 34c8b4040c3d0023527505a9b1bd2a580d91e2b56c81288085f2117d54897bbc
MD5 8ba75d1f64a7b7fda7b5637f17307f4d
BLAKE2b-256 11a3d76ba45aecd0b423f021829c5ed629a1ad50c312738765eb0e0f20dd01fe

See more details on using hashes here.

File details

Details for the file ccdd_gate-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ccdd_gate-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 108.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for ccdd_gate-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9f3356fb78d0f587b33cb9b1693cdb47886e6cebbe732974f12a3056a30f7415
MD5 d6778a62af279574b479617328f07fdd
BLAKE2b-256 2525228c29f767f85f2855bc479ad48efa8e4a586013141a4da5bfad3953e354

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page