release: dev → main (v0.10.0)#200
Merged
Merged
Conversation
… reusable
El job `publish` corre como reusable workflow invocado desde release-please.yml.
La attestation (PEP 740) registra en su certificado el workflow top-level
(release-please.yml), pero el Trusted Publisher de PyPI espera publish-pypi.yml,
así que PyPI rechaza el upload con 400 ("Build Config URI does not match expected
Trusted Publisher"). PyPA no soporta reusable workflows con Trusted Publishing.
El OIDC del token sí matchea (job_workflow_ref = publish-pypi.yml); solo falla la
verificación de la attestation. Con `attestations: false` el publish automático
post-release funciona sin perder el trusted publishing por OIDC.
Detectado en el release v0.9.0, cuyo publish automático falló por esta causa
(se publicó a mano vía workflow_dispatch, que no sufre el problema).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ci(release): deshabilitar attestations en publish-pypi (camino reusable)
chore: back-merge main → dev (sincronizar release 0.9.0)
…apean el ciclo) (#150) * docs(adr): 0037 superficie CLI 0.10.0 — 10 verbos agents-first que mapean el ciclo Gradúa la decisión del PO (Discussion #127) de consolidar la superficie CLI a 10 verbos que mapean el ciclo de investigación (INIT→SEED→CHAIN→CURATE→BUILD→ READ→EXPORT/SNAPSHOT, STATUS transversal como mapa tipo `git status`). Registra las 4 sub-decisiones (a)-(d): set de 10, curate/read noun-verb, monitor→chain --since, aliases de retrocompat. Enmienda 0021 (12→10) sin tocar el contrato de salida: envelope schema="1", exit codes y FSM se preservan; readiness/next_best_action/diagnóstico red-vacía entran como campos aditivos de status (mismo patrón que la enmienda R3). Diferidos a seguimiento: preview por-red (e.5) y maturity-stamp del one-shot (e.6). Actualiza el índice: fila 0037, enmienda anotada en 0021, y rellena el hueco preexistente de la fila 0036. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(adr): 0037 incorpora (e) preview por-red en status y (f) maturity-stamp del one-shot El PO decide no diferir las dos afinadas: entran a 0037. (e) mueve el diagnóstico de red-vacía de build-time a status-time (modelo git status staged/unstaged): status dice, por red, si build saldría vacío y por qué, ANTES de actuar — cura plena de la trampa de la Nota 20. (f) los artefactos one-shot llevan un bloque `maturity` aditivo que los autodeclara borrador sin pulir (sin curar, scope, saturación, redes vacías). Ambas son campos aditivos que preservan schema="1"; mismo género que el resto del ADR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…or-red (ADR 0037 §e) (#153) Fase 1A del hito 0.10.0. Agrega tres campos ADITIVOS al data de `b2g status --json`, preservando schema="1" (mismo patrón que la enmienda R3): - next_best_action: próximo comando recomendado, determinista desde el FSM. - readiness {ready,reason}: si el próximo paso DARÁ FRUTO (no solo si la transición está permitida). Para build, ready=False si todas las redes saldrían vacías; para chain, ready=False si ningún seed tiene source_id. - build_preview: por cada red, would_be_empty + reason + fix_command, ANTES de build. Cura de raíz la trampa de "redes vacías con cara de éxito". El preview usa los MISMOS predicados que los proyectores, con un test de no-divergencia que construye las redes reales y cruza would_be_empty contra el build (cubre datos disjuntos). +53 tests. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…os verificable) (#154) El 0037 declaró "10 verbos" pero no nombró 5 subcomandos que el CLI tenía por acreción; mientras vivieran como verbos sueltos el conteo era ficción contable. El 0038 los despacha: gui se mantiene fuera de los 10 (0027/0028, gate #34), enrich→absorbido en chain/build (0025), restore→snapshot restore (0030), thesaurus→retirado (contexto #149 invalid), resolve→retirado (ruta única seed --resolve, 0035). Fija además los cabos abiertos del 0037: ventana de deprecación cierra en 0.11.0 (P1), build --scope default = all (P2), forma del bloque maturity como apéndice en docs/API.md (P3). Preserva envelope schema="1", exit codes y FSM. Enmienda a 0037; revisa el thesaurus del 0031. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…o enforced (#151) (#168) Sub-issue fundacional del epic #167 (superficie CLI 0.10.0, ADR 0037/0038). - Reemplaza los 19 bloques duplicados de @click.option("--json") por un único decorador compartido @json_option (cli/_options.py); --json sigue por-comando y post-verbo (b2g <cmd> --json), decisión del PO (no global pre-verbo). - Nuevo B2G_JSON: env var truthy (1/true/yes) activa el modo JSON en todos los comandos sin flag; precedencia --json explícito > env; sin --no-json. Caso agents-first: un agente hace `export B2G_JSON=1` una vez. - json_mode(local_flag) centraliza la resolución flag-vs-env; handle_errors respeta B2G_JSON también en el camino de error. - stdout puro enforced: en modo JSON stdout emite exactamente una línea-envelope schema="1" (incl. error ok=false); gui.py manda los echo de arranque a stderr. - Tests de contrato (tests/unit/test_cli_json_option.py): stdout puro por comando, --json y B2G_JSON, camino de error, precedencia. - Docs en lockstep: API.md (B2G_JSON + stdout puro), enmienda aditiva al ADR 0021 (append-only), índice y AGENTS.md. Invariante intacto: envelope schema="1", exit codes y FSM sin cambios. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…agnóstico red-vacía (#159) (#169) Sub-issue del epic #167 (superficie CLI 0.10.0, ADR 0037 a/e + 0038). - build absorbe la capacidad de networks vía `build --spec YAML` (modo declarativo) sin perder el one-shot (sin --spec = Networks.quick). Helper compartido _build_from_spec_file = fuente única para build --spec y networks. - build --spec transiciona el FSM a BUILT y sella .corpus_hash (paso BUILD pleno, decisión PO D1); networks sigue ortogonal (frontera con #165). - Renombre --corpus-scope→--scope all|accepted|seeds (default all, ADR 0038 P2); --corpus-scope queda como alias deprecado oculto (avisa a stderr; cierra 0.11.0). - --min-weight N filtra aristas; solo en modo quick. Con --spec se ignora con aviso a stderr (el umbral vive en el YAML por-red). - Diagnóstico de red-vacía en build-time reusando predict_build_preview (no-divergencia con status-time, ADR 0037 §e, por-corpus). Warning a stderr en humano; en --json va en data.empty_networks (separado de data.warnings). En modo spec el reason culpa al min_weight del YAML, no al --min-weight CLI. - data.scope expone el token CLI (seeds); data.corpus_scope conserva el vocab interno (seeds_only) para backward compat y como gancho estable de #160. - Networks.quick extendido con min_weight. - Docs en lockstep: API.md (build/networks/scope/empty_networks), AGENTS.md, enmienda append-only D1 al ADR 0037. Invariante intacto: envelope schema="1", exit codes y FSM (apply_transition) sin cambios. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#156) (#170) Sub-issue del epic #167 (superficie CLI 0.10.0, ADR 0037 b + 0036). - Primer grupo noun-verb del repo: `read {list,stats,show}` (Click group con invoke_without_command=True → exit 0 + ayuda en `read` sin subcomando, workaround a la regresión de Click 8.4). - read list: --query (solo título, substring CI) + --status/--seeds/--candidates/ --year; envelope data={papers:[{id,title,year,curation_status,is_seed}], count}. - read stats --group-by {status,year,is_seed} (default status); data={group_by,total,groups:[{key,count}]}. - read show --id: delega en get_paper, devuelve la fila completa (~14 campos). - get_paper(ws, ident) resuelve por id|doi|source_id (prioridad id>doi>source_id, ADR 0036); DataError si no matchea. Caller api/routers/reads.py sin ruptura. - Lógica nueva en service/reads.py (list_papers, corpus_stats, get_paper extendido), exportada en service/__init__; la capa de servicios sigue dueña (ADR 0028/0032). - inspect queda INTACTO (su deprecación es #165). command del envelope = ruta completa ("read list"/"read stats"/"read show"). Gancho abierto para read top (#157). - Docs en lockstep: API.md (get_paper id|doi|source_id, grupo read), ARCHITECTURE.md (patrón noun-verb), AGENTS.md. Invariante intacto: envelope schema="1", exit codes (DataError→2, uso→1), stdout puro. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…investigación) (#157) (#171) Sub-issue del epic #167 (superficie CLI 0.10.0, ADR 0037 b). Cierra el grupo read. - read top: dos bloques en un envelope. central = nodos de la red --kind ordenados por degree_centrality desc (top N); cocitation = pares de la red cocitation por weight desc, con ambos títulos resueltos. - Flags: --top/-n (default 10), --kind (Choice sobre los 5 NetworkKind, default bibliographic_coupling), --json. El bloque co-citación es SIEMPRE cocitation. - Default bibliographic_coupling para "centrales": robusto en el one-shot frío (se calcula desde las referencias propias, no necesita enrich/forward), así la salida estrella entrega valor temprano. Co-citación necesita datos forward. - Honest-empty: co-citación vacía → exit 0 + bloque [] + reason/fix_command (predict_build_preview), NO DataError. No requiere build previo (recomputa vía get_network). Título completo (join id→title); entidad → label. - service/reads.py:get_top (capa de servicios dueña), exportado en service/__init__. - Gancho del grupo read (#156) cerrado: read {list,stats,show,top}. - Docs en lockstep: API.md (read top, get_top, honest-empty; + drift de #156), AGENTS.md, ARCHITECTURE.md. Invariante intacto: envelope schema="1", exit codes (0 éxito incl. vacío, 1 uso, 2 datos), stdout puro (#151). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…filter} (#155) (#172) Sub-issue del epic #167 (superficie CLI 0.10.0, ADR 0037 b). Segundo grupo noun-verb (tras read). - curate pasa de comando plano a grupo: curate {dump,apply,accept,reject,filter}. curate sin subcomando → help + exit 0 (workaround Click 8.4). - BREAKING (autorizado por ADR 0037 b): la forma-flag curate --dump/--from-csv se elimina sin alias; --dump→curate dump, --from-csv→curate apply. --all eliminado (usar --scope all). - curate filter transiciona el FSM a FILTERED; dump/apply/accept/reject son transversales (el VERBO define la transición, precedente D1 de #159). - Lógica extraída a service/curate.py (fuente única para los verbos sueltos que #165 retirará): run_curate_dump, run_curate_from_csv, filter_corpus (decided_at INYECTADO por el caller → neutralidad de servicio restaurada, ADR 0017). - Verbos sueltos accept/reject/filter quedan intactos (sin warning); comparten la lógica de servicio. Su deprecación/retiro es #165. - Docs en lockstep: API.md (grupo curate, filter_corpus, transiciones), feature files, AGENTS.md, ARCHITECTURE.md; notas append-only D2 al ADR 0037 (transición por verbo) y corrección del gap P1 al ADR 0038 (filter entra a la ventana 0.11.0). Invariante intacto: envelope schema="1", exit codes, FSM (apply_transition), stdout puro. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… top) (#160) (#173) Sub-issue del epic #167 (superficie CLI 0.10.0, ADR 0037 f + 0038 P3). - Bloque aditivo maturity en el --json de build, snapshot y read top: {curated, scope, saturated, empty_networks}. schema="1" intacto. - curated = ≥1 paper accepted/rejected (sobre corpus_full, pre-scope, para que un scope que deja 0 papers no falsee curated). scope = token CLI. saturated = false constante (one-shot nunca sobre-afirma; gancho referenced_refs_count documentado). empty_networks = kinds vacíos (build: de data.empty_networks; read top: ["cocitation"] si vacía; snapshot: []). reason/fix_command no se duplican. - Helper puro service/maturity.py:compute_maturity (fuente única), exportado. - Presente siempre en build/snapshot/read top; ausente en read list/stats/show. - Docs en lockstep: API.md (apéndice maturity + secciones build/snapshot/read top), AGENTS.md. Sin ADR nuevo (0038 P3 delegó la forma a API.md). Invariante intacto: envelope schema="1", exit codes, FSM. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
) (#161) (#174) Sub-issue del epic #167 (ADR 0038). resolve y seed --from-bib --resolve ya comparten la fuente única service/resolve.py::_resolve_dois_on_store (desde ADR 0035); este test de paridad certifica que ambos caminos producen el mismo mapa doi→source_id, blindando el retiro del verbo resolve en #165. resolve queda intacto (su retiro/aviso es #165). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…l) (#158) (#176) Sub-issue del epic #167 (ADR 0037 c). - chain --since (forrajeo incremental): acepta fecha ISO YYYY-MM-DD y atajos relativos (90d/6m/1y); resuelve la ventana contra now inyectado en la frontera (R2/ADR 0017). --since fuerza forward (backward+since → UsageError; both+since → forward). chain --since transiciona a MONITORED; chain normal → FORAGED (no existe CHAINED). Vía apply_transition (fuente única, sin estados nuevos). - Plumbing nuevo de la ventana: Forager.chain(since=) → _fetch_forward → fetch_citing_batch(since=) agrega from_publication_date: al filtro cites:; sin since el comportamiento es idéntico al previo (**_since_kw no rompe sources mock). - new_candidates aditivo (siempre). Guarda de corpus vacío portada de monitor. - monitor suelto INTACTO pero delega en run_chain(_fsm_action="monitor") → fuente única. CAMBIO DE COMPORTAMIENTO: monitor ahora deduplica post-merge (persist_replace + normalize_and_dedup), antes no; más consistente con el pipeline. Su retiro/aviso es #165. Docs de contrato (chain --since) se consolidan en #166. Invariante: schema="1", exit codes, FSM (apply_transition) intactos; campos aditivos. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#177) Sub-issue del epic #167 (ADR 0038/0030). Tercer grupo noun-verb (tras read, curate). - snapshot pasa de comando plano a grupo {create, restore}; el snapshot plano actual → snapshot create (BREAKING, como curate). snapshot sin subcomando → help + exit 0. - restore → snapshot restore (noun-verb); mergea+dedup, transiciona a FILTERED. - restore suelto intacto (shim que delega; su retiro es #165). command del envelope = ruta completa ("snapshot create"/"snapshot restore"); el shim mantiene "restore". - Fuente única: run_snapshot/run_restore extraídos a service/snapshot.py; create, restore-sub y restore-shim delegan ahí. Reloj inyectado (decided_at) desde la frontera CLI (ADR 0017); servicio neutral. Invariante intacto: envelope schema="1", exit codes, FSM (apply_transition fuente única; create NO transiciona, restore→FILTERED). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sub-issue del epic #167 (ADR 0038/0025). - El Enricher se absorbe en el ciclo: pasada refs→DOI en chain (automática, tras normalize_and_dedup), pasada cited_by en build (automática cuando hay seeds aceptadas; no-op graceful si no → 4 redes vs 5). - Helper compartido cli/_enrich.py:enrich_corpus(corpus, source, *, max_citing, pass_name) opera sobre el corpus en memoria dentro de la transacción existente (sin doble-abrir el store). pass_name ∈ {refs_doi, cited_by, both}; métricas sparse. - enrichers/openalex.py expone enrich_references_doi/enrich_cited_by (Protocol preservado). enrich suelto INTACTO pero delega en enrich_corpus (fuente única; su retiro es #165). Sin flag --enrich. - Métricas como bloque aditivo data["enrichment"]. build suma opciones email/--max-citing. - NO toca el FSM (estado ENRICHED no existe). BUILD-PURO: build ahora hace requests de red cuando hay aceptadas (rompe el invariante "build puro" del ADR 0025; enmienda al 0025 se consolida en #166). Docs de contrato (enrich absorbido + enmienda 0025) se consolidan en #166. Invariante: envelope schema="1", exit codes intactos; campos aditivos. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d --thesaurus (#164) (#179) Sub-issue del epic #167 (ADR 0038/0011). - Retira el VERBO thesaurus (borra cli/commands/thesaurus.py, des-registra en cli/__init__.py). La capacidad (consolidación cross-lingüe, ADR 0011) se preserva como flag build --thesaurus <archivo>: carga el mapa y aplica apply_thesaurus + persist_replace sobre el corpus completo ANTES del scoping/proyección. - preprocessors/thesaurus.py y Preprocessor.apply_thesaurus INTACTOS (ADR 0011). - facade.py: fix_command de red keyword-vacía → "b2g build --thesaurus <archivo>". - data["thesaurus"] aditivo (stats); sin --thesaurus, build idéntico. Docs de contrato (retiro thesaurus + flag + nota append-only 0031) se consolidan en #166. Invariante: envelope schema="1", exit codes intactos. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…int bib2graph (#165) (#180) Sub-issue del epic #167 (ADR 0037 d + 0038 P1). Absorbe #152. - Helper cli/_deprecation.py:emit_deprecation(old, new, *, removed_in="0.11.0"): imprime a stderr SIEMPRE (nunca stdout, #151) y devuelve el mensaje. - 9 verbos sueltos avisan deprecación y siguen delegando (lógica intacta): accept/reject/filter a curate, networks a build --spec, inspect a read show|status, monitor a chain --since, resolve a seed --resolve, restore a snapshot restore, enrich a chain. El aviso va a stderr + al warnings[] top-level del envelope en --json. - Entry-point bib2graph a b2g (#152): main_bib2graph_alias avisa y delega en main(); script en pyproject. --corpus-scope refactorizado al helper. - Ventana cierra en 0.11.0. NO incluye thesaurus (ya eliminado en #164). Docs (seccion deprecacion + enmienda 0038 P1 sumando enrich) se consolidan en #166. Invariante: envelope schema="1", exit codes, stdout puro intactos; logica sin cambios. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/0038 (#166) (#181) Sub-issue del epic #167. Cierra el drift de contrato deferido y registra las enmiendas ADR pendientes de la superficie 0.10.0. API.md / AGENTS.md: - chain --since (#158): ISO + relativos, fuerza forward, →MONITORED (chain→FORAGED), new_candidates/loop_state/round; corrige drift "CHAINED" y "monitor verbo vivo". - enrich absorbido (#162): alias; refs→DOI en chain, cited_by en build; data.enrichment; build suma --email/--max-citing. - thesaurus retirado (#164): capacidad como flag build --thesaurus; data.thesaurus. - Sección "Avisos de deprecación" (#165): formato, stderr + warnings[], 9 aliases + entry-point bib2graph + --corpus-scope. - Header "10 verbos + 3 grupos noun-verb + gui + 9 aliases" verificable vs cli/__init__.py. Enmiendas ADR (append-only): - 0025: enrich deja de ser verbo; build deja de ser puro (requests cited_by con aceptadas; no-op sin aceptadas). - 0031: el 18° paso explícito thesaurus se retira como verbo; capacidad → build --thesaurus. - 0038 P1: suma enrich a la ventana de deprecación; consolida los 9 aliases. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…adiness (#76) (#182) Sub-issue del epic #167 (capstone de aceptación). Cierra la superficie 0.10.0. - E2E PURO-CLI: encadena init/seed/chain/build/read por CliRunner sobre un workspace tmp, leyendo status --json en cada paso. Monkeypatch del símbolo de módulo OpenAlexSource con httpx.MockTransport (el CLI no inyecta transport) → corre offline. - Demuestra el flujo agents-first guiado por status: next_best_action apunta al verbo correcto en cada paso; la TRAMPA de la Nota 20 (seed --from-bib sin --resolve → readiness.ready=false con reason que apunta a seed --resolve) y su remedio; el build_preview diagnostica red-vacía antes de build; build emite maturity {curated:false, scope:"all", saturated:false}; read top da la salida de investigación. - Aserciones de valor (no triviales): init/seed/chain (candidates_found>0)/build/read, stdout JSON puro (1 línea) en cada invocación. Marker integration (corre en el gate). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lo (#183) Retroalimentación del giro de superficie 0.10.0 (#167) incorporada al proceso de agentes (medición forense: ~50% del tiempo de cada coder se fue esperando el suite completo de tests): - .claude/agents/coder.md: testing por capas — iterar con tests pertinentes (pytest test_X::test_Y, 7-60s), auto-formatear antes de gatear, dejar el suite completo al verifier+CI; timeout alto en corridas largas. - .claude/agents/verifier.md: el verifier es dueño del gate COMPLETO (red de seguridad mientras el coder itera con subconjuntos). - AGENTS.md §"Ejecución concurrente y testing": paralelizar solo ramas con archivos disjuntos (serializar las que comparten build.py/cli/__init__.py); los Edit/Write de un subagente se aíslan al worktree de la sesión (no a la ruta del prompt); batchear decisiones del PO; evitar rutas con acentos en Git Bash. - .claude/commands/retro-ciclo.md: nuevo comando /retro-ciclo — retrospectiva metacognitiva de fin de ciclo que mide con datos, reflexiona y BAKEA las lecciones donde viven (agentes/AGENTS.md/hooks). Cierra el lazo de aprendizaje. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…184) (#185) Análisis único/redundante/sinérgico/tensión de los ~1238 tests (68 archivos) para encuadrar la poda del epic #184. Incluye el criterio del PO (defensa en profundidad selectiva) y el DoD por candidato (cobertura no baja + gate verde). Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…dos) (#186) * test(service): consolidar neutralidad-AST en un test parametrizado (#184) Las 5 copias del test de neutralidad de transporte (ADR 0028) que vivían en test_service, test_service_reads, test_api, test_cli_read y test_cli_read_top se unifican en un único test parametrizado en test_service.py que auto-descubre TODOS los módulos de service/ (8) vía pkgutil — fuente única del contrato y menos frágil (cubre módulos futuros sin editar el test). Sub-tarea 1 del epic #184 (criterio: conservar consolidados). Antes: 5 funciones guardando 4 módulos. Ahora: 1 función guardando 8 (curate/maturity/resolve/ snapshot quedaban sin cubrir). Gate verde, cobertura no baja. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(cli): colapsar "stdout 1 línea JSON" duplicado al guard/helper canónico (#184) Sub-tarea 2 del epic #184. El invariante #151 (stdout = 1 línea en modo --json) ya está garantizado por el helper _assert_one_json_line en cada test *_forma y por el guard parametrizado de test_cli_json_option (camino de error). Se retiran los tests dedicados redundantes: - test_cli_read: list/show/stats stdout_una_linea (3) → helper en *_envelope_forma. - test_cli_read_top: stdout_una_linea + su gemelo _cocitacion_vacia (escenario idéntico) → helper en test_cli_read_top_json_envelope_forma. - test_build_absorber_networks: test_json_stdout_una_sola_linea → lo subsume test_json_warnings_en_envelope_no_en_stdout (asserta len==1 + warnings). Funciones de test: -6. Cobertura del invariante intacta. Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(maturity): semántica solo en helper puro, 1 presencia/forma por superficie (#184) Sub-tarea 3 del epic #184. La semántica curated/saturated vive en TestComputeMaturity; se retiran las re-pruebas por superficie en build/snapshot/ read top (curated_false/true, saturated_false, -9). next_best_action: la semántica del FSM vive en TestNextBestAction; en TestRunStatusCamposAditivos se deja 1 smoke de cableado y se retiran los otros 3. -12 funciones. Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(enrich): concentrar co-citación end-to-end, recortar enrichers_8b (#184) Sub-tarea 4 del epic #184. Idempotencia, tope max_citing, no-op-sin-seeds, conteo de redes 4-vs-5, contrato de claves y "no pierde papers" ya están cubiertos en test_enrich_cocitacion_integrado (store) y test_enrich_absorb (chain/build). Se retiran sus copias en memoria de test_enrichers_8b (-7). Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(cli): retirar re-pruebas de aliases deprecados, conservar solo el aviso (#184) Sub-tarea 5 del epic #184. La migración noun-verb (ADR 0038) cerró: se retiran los corre_y_delega (la delegación la cubre el verbo canónico), los json_stdout_puro_una_linea y los schema_1_intacto (ya garantizados por el helper _assert_one_json_stdout). Se conserva el aviso de deprecación (stderr + warnings en envelope) por alias. -16 funciones. Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(sources): adelgazar mocks de exclusión + contrato BibtexSource.load duplicado (#184) Sub-tareas 6 y 7 del epic #184. - 6: los tests seed+mock de forma-de-query con exclusiones solo re-verificaban la salida de _translate; la forma "campo no repetido" vive en los tests PUROS de _translate (gate) y el comportamiento real en el test @network de integración (-3). - 7: el contrato de campos de BibtexSource.load (is_seed/curation_status/campos faltantes/sin KeyError) vive en test_bibtex_source_contrato y se ejercita en test_seed_from_bib; se retira el subconjunto en test_sources (-4). "seed sin modo" idéntico entre seed_from_bib y equation_spec: se conserva en seed_from_bib (-1). Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo * test(robustez): refactor de fragilidad — constantes, hard-fail, patch targets (#184) Sub-tarea 8 del epic #184 (refactor, no poda): - Contradicción resuelta: test_idempotencia_pipeline_bitabit ahora FALLA (no skipea) si falta el parquet commiteado, coherente con test_example_r2_gate (la reproducibilidad es sagrada; un skip ocultaría la regresión). - fix_command: literales repetidos en ~10 asserts de test_status_10 → 3 constantes de módulo (FIX_RESOLVE/FIX_ENRICH/FIX_THESAURUS); el copy se edita en un solo lugar. - patch targets en test_r3_commands_domain centralizados en constantes documentadas (se parchea el método en su clase de definición — robusto al import-path del SUT). Gate verde. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…(~-24%) (#189) El test más lento de la suite, test_comunidades_identicas_entre_dos_runs_pipeline, reconstruía las 5 redes (run_build) DOS veces aunque la aserción solo compara las comunidades de Networks.quick. Se conserva run_build en el run A (mantiene la cobertura del camino de escritura de artefactos) y se elimina el duplicado del run B. Medido aislado (--no-cov): 29.6s → 22.5s (−24%); en el gate con coverage el ahorro es ~3x mayor. Cobertura sin cambios (484 missed) y suite verde (1187 passed). Surgió al evaluar el impacto de la poda del epic #184: la palanca de tiempo de la suite NO es la cantidad de tests sino un puñado de tests pesados de reproducibilidad (este solo era ~17% del wall-clock del gate). Optimización sin perder calidad: la idempotencia de comunidades se sigue verificando íntegra. Claude-Session: https://claude.ai/code/session_01T8tphqfD3Gq9aX3vWXyyuo Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…add` (#188) (#192) * feat(skill): distribuir skill de Claude Code end-user vía `b2g skill add` (#188) Agrega la skill end-user de bib2graph, vendoreada en el wheel e instalable con un comando meta nuevo, para que "la mejor forma de usar bib2graph sea pedirle a Claude que lo use". - src/bib2graph/skill/ (SKILL.md + reference/ciclo.md): entrevista híbrida, eje one-shot↔profundizar mapeado al ciclo de la Nota 05, elaboración de la ecuación, y feedback fácil (bloque + link a issue nuevo). - `b2g skill add [--user|--project] [--force]`: copia la skill vendida; comando meta global (sin workspace), envelope --json schema="1" sin FSM, idempotente (no-op si ya está la versión vendida; --force pisa si difiere). - La skill viaja por `packages` (fuente commiteada), NO force-include: el wheel ya la incluye → version-lock skill==cli (verificado con `uv build`). - ADR 0039: `skill` como 2.ª excepción meta (junto a `gui`); conteo "10 + gui + skill". Docs: API.md, AGENTS.md, README.md, índice de decisiones. - Tests: registro, scopes, --force, idempotencia, sin-workspace, envelope, version-lock (importlib.resources) y anti-drift (verbos del SKILL.md ⊆ CLI). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(adr): 0039 — registrar atado a Claude Code como limitación deliberada (#193) La distribución agnóstica al proveedor (Claude Code, OpenCode, …) es prioridad de 0.11.0; este ADR lo deja explícito como limitación conocida, no olvido. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…era + leelo) (#194) Para que un agente de CUALQUIER proveedor (no solo Claude Code) pueda operar bib2graph, la salida es explícita sobre el artículo a leer, sin depender del descubrimiento de skills del cliente. - Salida humana y envelope dicen DÓNDE quedó el SKILL.md, explican a grandes rasgos cómo opera bib2graph (entrevista + ciclo seed→chain→build→read), y piden leer ese archivo. - `data` del envelope gana campos aditivos `skill_md`, `reference_dir`, `how_to` (schema="1" intacto) → un agente se auto-onboardea desde el --json. - Primer puente hacia la distribución agnóstica al proveedor de 0.11.0 (#193). - Docs: API.md + ADR 0039. Tests: data agéntico + salida humana explícita. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e la librería (#197) La GUI local (epic #34: API FastAPI + SPA React `frontend/` + subcomando `b2g gui`) deja de ser parte de la librería: el core es CLI/agente-native sobre la biblioteca viva. Se borra el código y se limpian los docs activos; la historia (ADR 0027/0028, Notas de diseño) queda intacta como archivo. - Borra: `cli/commands/gui.py`, `src/bib2graph/api/`, `src/bib2graph/gui/`, `frontend/`, tests `test_api.py`/`test_packaging_config.py`. - pyproject: sin extra `[gui]`, sin `force-include` del wheel, sin per-file-ignores de `api/`, sin override mypy `uvicorn.*`. Wheel Python puro. - CI: `ci.yml` sin job `frontend`; `publish-pypi.yml`/`publish-testpypi.yml` sin build de frontend (Node/pnpm). - `service/reads.py` se CONSERVA: lo usa el grupo CLI `read` (corrige el inventario del issue; borrarlo rompía `b2g read`). - ADR 0040 (supersede 0027/0028, enmienda 0038); banners de retiro en docs activos (la limpieza profunda de marcadores AS-BUILT es #191). Closes #190. Refs #117 (vista de paper rota: moot al retirar la GUI). BREAKING CHANGE: se elimina el subcomando b2g gui y el extra [gui]; el wheel ya no vendorea frontend. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#198) `manifest.enrichers` era efímero: `DuckDBStore.load()` arma un Manifest fresco y `persist()` solo escribía la tabla Arrow, no los EnricherRef. Tras recargar, la procedencia de enriquecimiento se perdía (mismo bug que #126 con los filtros). Fix = mismo patrón que #126/#140 para los filtros: tabla hermana `enricher_log` + reconstrucción en `load()`. - `DuckDBBackend`: DDL `enricher_log`, `persist_enricher_refs(refs, replace=True)` (DELETE+INSERT, idempotente por nombre — ADR 0025) y `load_enricher_refs()`. - `DuckDBStore.load()`: reconstruye `manifest.enrichers` desde el log. - Llamado tras persistir en `enrich`/`chain`/`build`. Timestamp `recorded_at` inyectado en la frontera (DuckDB now()), núcleo puro intacto (sin duckdb). - Legacy graceful: `CREATE TABLE IF NOT EXISTS` → stores viejos devuelven []. Closes #141. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Un doc vivo describe lo que ES, no el camino. Con la superficie de 10 verbos (ADR 0037/0038) y la GUI retirada (ADR 0040), los docs vivos se reescriben al presente y el debate queda en los ADR. - ARCHITECTURE.md: 788→408 líneas; 0 marcadores AS-BUILT/TARGET/SUPERADO/ HISTÓRICO en el cuerpo; §6.3 a los 10 verbos + destino de los huérfanos; GUI como ausencia (ADR 0040). - API.md: 2638→1632; §0.2 (API local FastAPI) retirada; convenciones CLI a 10 verbos; 0 refs "Nº subcomando"; firmas verificadas contra src/bib2graph/cli/; nota de enricher_log (manifest.enrichers persiste, #141). - PRD.md: 481→330; target user no-técnico-vía-IA primero; §10 reescrito (el ciclo aplicado a la teoría que mejora bib2graph); IED como validación histórica. - AGENTS.md: reglas docs-vivos + prosa + MILESTONES (milestone = versión que un issue libera; release = cerrar su milestone). Superficie a 10 verbos + skill. - CONTRIBUTING.md: check en template de PR (ADR firmado → reescribir docs vivos). - pyproject: comentario de cabecera al presente (publish a PyPI conectado desde 0.10.0 vía Trusted Publishing). Closes #191. Closes #134. Refs #132, #34. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v0.10.0 — agents-first, primer publish a PyPI
Libera a
maintodo lo acumulado endevdesde 0.9.0. release-please calculará el bump (BREAKING en 0.x → minor, 0.9.0 → 0.10.0) y cortará el tag/CHANGELOG/Release.BREAKING
b2g gui, el extra[gui]y el vendoreo del frontend. Wheel Python puro.enrich→chain/build(feat(cli): absorber enrich en chain/build (#162) #178),monitor→chain --since(feat(chain): absorber monitor como chain --since (#158) #176), verbothesaurusretirado →build --thesaurus(refactor(cli): retirar verbo thesaurus, preservar capacidad como build --thesaurus (#164) #179), aliases de retrocompat con aviso de deprecación hasta 0.11.0 (feat(cli): aliases de retrocompat + ventana de deprecación (cierre 0.11.0) #165/feat(cli): aliases de retrocompat + ventana de deprecación 0.11.0 (#165) #180).Features (superficie 10 verbos agents-first, ADR 0037/0038)
statuscomo mapa:next_best_action+readiness+ preview por-red (feat(cli): status como mapa — next_best_action + readiness + preview por-red (ADR 0037 §e, Fase 1A) #153).--jsonunificado (decorador +B2G_JSON+ stdout puro) (feat(cli): --json unificado por decorador + B2G_JSON env + stdout puro enforced (#151) #168).curate/read/snapshot;read top(salida de investigación) (feat(cli): grupo noun-verb read {list,stats,show} absorbiendo inspect (#156) #170/feat(read): read top — centrales + co-citación con título (#157) #171/refactor(cli): curate como grupo noun-verb {dump,apply,accept,reject,filter} (#155) #172/refactor(cli): snapshot como grupo noun-verb {create, restore} (#163) #177).buildabsorbenetworks --spec+--scope+ diagnóstico red-vacía (feat(build): absorber networks via --spec + --scope/--min-weight + diagnóstico red-vacía (#159) #169).b2g skill add: distribuye la skill de Claude Code vendeada en el wheel (epic(skill): distribuir skill de Claude Code vendeada en el wheel +b2g skill add#188/feat(skill): distribuir skill de Claude Code end-user víab2g skill add(#188) #192/feat(skill): salida agéntica explícita enskill add(ruta + cómo opera + leelo) #194).Fixes
manifest.enricherspersiste víaenricher_log(fix(provenance): enrich tampoco persiste manifest.enrichers (mismo bug que #126) #141).Docs
Nota: al mergear este PR (merge commit), release-please abrirá el PR de release 0.10.0. El publish a PyPI (Trusted Publishing) se dispara al mergear ESE PR de release, y requiere el pending publisher configurado en pypi.org.
🤖 Generated with Claude Code