Registro público
Informe de salud del softwareesquema 0.27.0 · métricas 1.13.0 · 2026-07-25 20:23 UTC

usestapel / stapel-core

Core Django utilities for the Stapel framework — JWT, permissions, error handling, bus abstractions

PythonMIT★ 1 estrella⑂ 0 forksdesde jun 2026Ver en GitHub ↗

usestapel/stapel-core tiene un índice de salud de 54 sobre 100, lo que lo sitúa en la banda Moderado. Su puntuación más alta es Engineering Quality (78/100) y la más baja, Sustainability & Governance (34/100). Se actualizó por última vez hoy. Una sola persona concentra la mayor parte del trabajo reciente.

54
global / 100
Moderado

Índice de salud del software

Las métricas se agrupan en categorías ponderadas sobre una escala de 1 a 100. El resultado global parte de su media; cuando la evidencia pública activa la Política de Jurisdicciones de Alto Riesgo, la calificación se ajusta y recibe el límite 49 (En riesgo). Preparación para IA queda fuera.

54
Excelente85-100Ejemplar; cumple prácticamente todos los criterios evaluados
Bueno70-84Saludable; carencias menores
Moderado50-69Aceptable con carencias notables; se recomienda revisión
En riesgo30-49Debilidades significativas; su adopción exige cautela
Crítico1-29Problemas graves (proyecto abandonado, un solo mantenedor, sin higiene)
VitalidadComunidad yAdopciónSostenibilidady GobernanzaCalidad deIngenieríaSeguridadPreparaciónpara IA

Perfil de puntuación

Cada eje es una categoría. La forma importa más que la media: un proyecto sano llena toda la figura, mientras que un perfil de picos y cráteres indica que la fortaleza en una dimensión enmascara el riesgo en otra.

Titularidad

usestapelOrganización
1 seguidor31 repositorios públicosdesde jun 2026

Este repositorio está respaldado por una organización: una custodia compartida y responsable que puede sobrevivir a cualquier mantenedor individual.

Ecosistemas de paquetes

RegistroPaqueteVersiónDescargas / mesVersionesÚltima publicaciónEtiquetas
PyPIstapel-core0.14.2679420hace 0 díasdjangostapelauthjwt

Métricas por categoría

Vitalidad

¿Está vivo el proyecto: se escribe código y se publican versiones?

70Bueno · 22% del índice global
Cómo se puntúa
36/36Recencia de push — último push hace 0 días
3.5/36Cadencia de commits — 5/52 semanas con commits
18/18Volumen de commits — 109 commits en el último año
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Datos de entrada utilizados
commits_last_year109
human_commit_share1
days_since_last_push0
active_weeks_last_year5
Cómo se puntúa
16.2/27Publica versiones — 24 etiquetas de versión (sin releases de GitHub)
36/36Recencia de las versiones — última versión hace 0 días
27/27Cadencia de publicación — una versión cada ~1 días
0/10OpenSSF Scorecard: Signed-Releases — sin datos
Datos de entrada utilizados
releases_count24
latest_release_tagv0.14.2
releases_from_tags
days_since_latest_release0
mean_days_between_releases1
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: Signed-Releases. Los pesos restantes se han renormalizado.

Comunidad y Adopción

¿Tiene el proyecto usuarios, descargas, atención y unas condiciones acogedoras para quienes contribuyen?

49En riesgo · 18% del índice global
Cómo se puntúa
0/60Estrellas — 1 estrellas
0/25Forks — 0 forks
0/15Observadores — 0 observadores
Datos de entrada utilizados
forks0
stars1
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Cómo se puntúa
22.5/22.5README
22.5/22.5Licencia — licencia reconocida (MIT)
18/18Guía CONTRIBUTING
13.5/13.5Código de conducta
0/7.2Plantilla de issues
6.3/6.3Plantilla de PR
Datos de entrada utilizados
has_readme
has_license
has_contributing
has_issue_templateno
has_code_of_conduct
has_pull_request_template
Cómo se puntúa
51.1/80Descargas mensuales — 6794 descargas/mes en pypi
0/20Dependientes en el registro — no lo informa este ecosistema
Datos de entrada utilizados
packagesstapel-core
dependents
ecosystemspypi
total_downloads
monthly_downloads6794
Excluidos de la puntuación (sin datos o no aplicable): Dependientes en el registro. Los pesos restantes se han renormalizado.

Sostenibilidad y Gobernanza

¿Sobrevivirá el proyecto a sus personas: factor bus, capacidad de respuesta, quién lo respalda y mantenimiento del paquete?

34En riesgo · 24% del índice global
Cómo se puntúa
9/54Factor bus — la mitad de los commits recae en 1 contribuyente(s)
0/22.5Distribución de commits — el principal contribuyente firma el 100% de los commits
1.4/13.5Amplitud de contribuyentes — 1 contribuyentes
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Datos de entrada utilizados
bus_factor1
contributors_sampled1
top_contributor_share1
Cómo se puntúa
0/46.8Resolución de issues — sin issues o sin datos
0/38.3Aceptación de PR — sin PR decididos o sin datos
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Datos de entrada utilizados
merged_prs0
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluidos de la puntuación (sin datos o no aplicable): Resolución de issues, Aceptación de PR. Los pesos restantes se han renormalizado.
Cómo se puntúa
30/30Respaldo de la propiedad — propiedad de una organización
0/20Dominio verificado
2.2/25Alcance del propietario — 1 seguidores de usestapel
11.1/25Trayectoria — 31 repos públicos, cuenta de ~0 años
Datos de entrada utilizados
followers1
owner_typeOrganization
is_verified
owner_loginusestapel
public_repos31
account_age_days31
Cómo se puntúa
25/25Publicado y resoluble — 1 paquete(s) en pypi
35/35Recencia de publicación — última publicación hace 0 días
20/20Historial de versiones — 20 versiones en el registro
20/20No obsoleto — activo, ni obsoleto ni retirado
Datos de entrada utilizados
packagesstapel-core
ecosystemspypi
any_deprecatedno
min_days_since_publish0

Calidad de Ingeniería

¿Existen unas prácticas mínimas de ingeniería y documentación?

78Bueno · 20% del índice global
Cómo se puntúa
24/24Flujos de trabajo de CI — 2 flujo(s) de trabajo
24/24Pruebas presentes
16/16Configuración de linter — ruff.toml
0/9.6Hooks de pre-commit
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — sin datos
Datos de entrada utilizados
has_ci
has_tests
has_editorconfigno
has_linter_config
has_precommit_configno
Excluidos de la puntuación (sin datos o no aplicable): OpenSSF Scorecard: CI-Tests. Los pesos restantes se han renormalizado.
Cómo se puntúa
30/30README
25/25Directorio de documentación
0/15Sitio de documentación / página del proyecto
10/10Descripción del repositorio
0/10Topics
10/10Wiki
Datos de entrada utilizados
topics
has_wiki
homepage
has_readme
has_docs_dir
has_description

Seguridad

¿Son sólidas las prácticas visibles de seguridad y de cadena de suministro, sin exposición jurisdiccional de alto riesgo sin resolver?

39En riesgo · 16% del índice global
Cómo se puntúa
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — sin datos
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Licencia — license file detected
0/7.5Maintained — project was created within the last 90 days. Please review its contents carefully
5/5Packaging — packaging workflow detected
0/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
0/5SAST — no SAST tool detected
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — sin datos
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Datos de entrada utilizados
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3,9
Excluidos de la puntuación (sin datos o no aplicable): ci_tests, signed_releases. Los pesos restantes se han renormalizado.

Preparación para IA

¿Hasta qué punto está el repositorio preparado para desarrollarse y mantenerse con agentes de codificación de IA? Es una insignia independiente y experimental — peso 0,0, de modo que se presenta por separado y no afecta a la puntuación de salud global.

59Moderado · 0% del índice global
Cómo se puntúa
0/45Instrucciones para agentes — sin CLAUDE.md / AGENTS.md / reglas de editor
0/15Documentación legible por máquinas (llms.txt)
40/40Historial de commits legible — 98 de 100 commits humanos declaran su intención (asunto estructurado o cuerpo explicativo)
Datos de entrada utilizados
has_llms_txtno
legible_history_share0,98
agent_instruction_files
agent_instruction_max_bytes
Cómo se puntúa
18/18Arranque con un solo comando — Makefile
22/22Pruebas automatizadas
11/11Configuración de lint / formato — ruff.toml
11/11Verificación estática de tipos — py.typed
0/10Entorno reproducible
10/10Práctica demostrada con agentes — 92 de los últimos 100 commits con autoría o crédito de agente
0/8Mantenimiento automatizado — no se observan actualizaciones automáticas de dependencias
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Datos de entrada utilizados
has_nixno
has_tests
lockfiles
has_dockerfileno
typed_languageno
bootstrap_filesMakefile
has_devcontainerno
has_linter_config
typecheck_configspy.typed
agent_commit_share0,92
toolchain_manifests
dependency_bot_commit_share0
Cómo se puntúa
27/45Código verificable por tipos — Python con configuración de verificación de tipos (py.typed)
55/55Tamaños de archivo manejables — 0/347 archivos fuente de más de 60 KB
Datos de entrada utilizados
primary_languagePython
largest_source_bytes35.295
source_files_sampled347
oversized_source_files0
Cómo se puntúa
0/40Esquema de API (OpenAPI/GraphQL/proto)
0/20Servidor MCP
40/40Ejemplos ejecutables — examples
Datos de entrada utilizados
example_dirsexamples
has_mcp_signalno
api_schema_files

Datos clave

1estrellas de GitHub
1contribuidores
109commits en los últimos 12 meses
0días desde el último push
24versiones publicadas
1factor bus
0issues abiertas
PyPIecosistemas de paquetes

Advertencias de recopilación de datos

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository
  • deps.dev does not index pypi:stapel-core@0.14.2; advisories assessed against the repository dependency graph instead

Más detalle

OpenSSF Scorecard 3.9 / 10
3.9agregado

Evaluación de seguridad independiente y agnóstica en cuanto a herramientas, procedente del proyecto de código abierto OpenSSF Scorecard. Cada comprobación premia una práctica de seguridad, no la herramienta de un proveedor concreto. Las comprobaciones que Scorecard no pudo determinar se marcan como n/d y se excluyen de la puntuación de seguridad (nunca se cuentan como cero).Scorecard v5.5.0 · 2026-07-25 20:23 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/dCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
0Maintainedproject was created within the last 90 days. Please review its contents carefully
10Packagingpackaging workflow detected
0Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 0
0SASTno SAST tool detected
10Security-Policysecurity policy file detected
n/dSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Dependencias directas 10
RegistroPaqueteRestricción de versiónManifiesto
PyPIDjango>=5.1pyproject.toml
PyPIdjangorestframework>=3.14pyproject.toml
PyPIdjangorestframework-dataclasses>=1.2pyproject.toml
PyPIdrf-spectacular>=0.27pyproject.toml
PyPIdjango-cors-headers>=4.3pyproject.toml
PyPIdjango-redis>=5.4pyproject.toml
PyPIPyJWT>=2.8pyproject.toml
PyPIcryptography>=41.0pyproject.toml
PyPIrequests>=2.31pyproject.toml
PyPIphonenumbers>=8.13pyproject.toml
Todas las dependencias no recopilado

No fue posible recopilar el conjunto de dependencias resuelto para este informe: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Informe JSON sin procesar legible por máquina
{
  "data": {
    "repo": {
      "topics": [],
      "is_fork": false,
      "size_kb": 2046,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "HTML": 7979,
        "Shell": 2646,
        "Python": 1938307,
        "Makefile": 320,
        "JavaScript": 40115
      },
      "pushed_at": "2026-07-25T20:17:13Z",
      "created_at": "2026-06-23T23:54:25Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-25T20:17:13Z",
      "description": "Core Django utilities for the Stapel framework — JWT, permissions, error handling, bus abstractions",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "MIT",
      "default_branch": "main",
      "license_spdx_raw": "MIT",
      "primary_language": "Python",
      "significant_languages": [
        "Python"
      ]
    },
    "owner": {
      "blog": null,
      "name": null,
      "type": "Organization",
      "login": "usestapel",
      "company": null,
      "location": null,
      "followers": 1,
      "avatar_url": "https://avatars.githubusercontent.com/u/296323797?v=4",
      "created_at": "2026-06-23T23:51:53Z",
      "is_verified": null,
      "public_repos": 31,
      "account_age_days": 31
    },
    "license": {
      "state": "standard",
      "spdx_id": "MIT",
      "raw_spdx": "MIT",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.14.2",
          "kind": "patch",
          "published_at": "2026-07-25T20:17:05Z"
        },
        {
          "tag": "v0.14.1",
          "kind": "patch",
          "published_at": "2026-07-25T18:48:00Z"
        },
        {
          "tag": "v0.14.0",
          "kind": "minor",
          "published_at": "2026-07-24T11:53:18Z"
        },
        {
          "tag": "v0.13.0",
          "kind": "minor",
          "published_at": "2026-07-21T20:13:41Z"
        },
        {
          "tag": "v0.12.4",
          "kind": "patch",
          "published_at": "2026-07-17T04:16:40Z"
        },
        {
          "tag": "v0.12.3",
          "kind": "patch",
          "published_at": "2026-07-17T03:19:45Z"
        },
        {
          "tag": "v0.12.2",
          "kind": "patch",
          "published_at": "2026-07-17T01:50:29Z"
        },
        {
          "tag": "v0.12.1",
          "kind": "patch",
          "published_at": "2026-07-17T01:18:53Z"
        },
        {
          "tag": "v0.12.0",
          "kind": "minor",
          "published_at": "2026-07-17T00:31:01Z"
        },
        {
          "tag": "v0.11.2",
          "kind": "patch",
          "published_at": "2026-07-16T22:17:24Z"
        },
        {
          "tag": "v0.11.1",
          "kind": "patch",
          "published_at": "2026-07-16T21:54:07Z"
        },
        {
          "tag": "v0.11.0",
          "kind": "minor",
          "published_at": "2026-07-16T21:40:02Z"
        },
        {
          "tag": "v0.10.4",
          "kind": "patch",
          "published_at": "2026-07-16T14:18:33Z"
        },
        {
          "tag": "v0.10.3",
          "kind": "patch",
          "published_at": "2026-07-16T12:28:07Z"
        },
        {
          "tag": "v0.10.2",
          "kind": "patch",
          "published_at": "2026-07-16T11:19:38Z"
        },
        {
          "tag": "v0.10.1",
          "kind": "patch",
          "published_at": "2026-07-14T09:31:01Z"
        },
        {
          "tag": "v0.10.0",
          "kind": "minor",
          "published_at": "2026-07-11T18:10:10Z"
        },
        {
          "tag": "v0.8.0",
          "kind": "minor",
          "published_at": "2026-07-05T23:52:24Z"
        },
        {
          "tag": "v0.3.2",
          "kind": "patch",
          "published_at": "2026-07-05T00:50:10Z"
        },
        {
          "tag": "v0.3.1",
          "kind": "patch",
          "published_at": "2026-07-04T00:41:43Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-07-02T20:01:51Z"
        },
        {
          "tag": "v0.2.2",
          "kind": "patch",
          "published_at": "2026-07-02T18:21:22Z"
        },
        {
          "tag": "v0.2.1",
          "kind": "patch",
          "published_at": "2026-07-02T14:48:05Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-07-02T14:37:10Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "0195be77e753248dbac9381fa4382cada4814459",
          "body": "…-op on an in-process bus\n\nMemoryBus.consume() drains a queue that lives in the publisher's memory.\nRun it in a SEPARATE process and it finds nothing, returns, and the\nprocess exits 0 — which under a container restart policy is an infinite\nloop that looks like activity and delivers nothing. 0.11.0 m\n[…]\n-allow-in-process keeps the legitimate single-process test path. Backends\ndeclare the property themselves via BusBackend.in_process.\n\nCo-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: stapel-core 0.14.2 — a consumer process must not silently no…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-25T20:17:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "09b1b4464cc7f3780f10aff99b8729c16086fcc3",
          "body": "…s a pre-rename table\n\n0.8.0 renamed the app label stapel_tasks → stapel_taskstore and pinned\ndb_table to the historical name, calling it \"label-only, no data\nmovement\". The DATA didn't move; the migration STATE did. Any database\nthat had migrated before 0.8.0 records those rows under the old label,\n[…]\ns cover both paths against the real executor: absent table → created,\npre-existing table → adopted with its rows intact (1899 pass).\n\nCo-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: stapel-core 0.14.1 — the taskstore's initial migration adopt…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-25T18:48:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "657c38151d6fa265ac0dcbaa366fe9283cb857eb",
          "body": "- verification: VerificationFactor.strength strong|weak (default weak —\n  email code alone is not 2FA), describe()/strong_names()/strong_factors()\n- users: password_change_required + mfa_enrollment_required flags,\n  auth_type 'login' (org-provisioned), StapelUsernameValidator — exactly\n  one '/' as \n[…]\nremote error, degrade to membership+builtin\n  role table on old workspaces)\n\n1897 tests (+41), ruff clean, migration-lint clean, wheel verified.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: stapel-core 0.14.0 — org-program Wave 0 primitives (additive)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-24T11:53:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1a74cb188727b7474cc2105428cc8ac8a340cd46",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: stapel-core 0.13.0 (StapelImage descriptor + Н4 allow_blank)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-21T20:13:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1fa327ef81d5237fa722175c691318716965aa95",
          "body": "DataclassSerializer mapped `description: str = \"\"` to a CharField that is\nrequired=False (has a default) but allow_blank=False (DRF default) — so the\nserializer REJECTED the very value it defaults to. That desynced from the\nDjango model (blank=True, default=\"\") and forced callers to omit the key\nrat\n[…]\ndeclared default is \"\", blank is a valid value.\n`allow_blank` also joins the field(metadata=...) override keys as an escape\nhatch.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(api): a str DTO field defaulting to \"\" now allows blank (libgaps Н4)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-21T20:12:16Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d1b25da252a2e38672e6cc2192c2783fdc2f2e6d",
          "body": "The single contract a renderer (@stapel/image <Image>) consumes for ANY image\n— CDN ladder, plain file, or external OAuth link — so it renders whether or not\na CDN is wired. A superset of RenderMetadata: adds a `source` tag and an\nalways-present top-level `url`, with `variants` = the ladder or [] (r\n[…]\nserializers so any\n  ref-carrying serializer denormalizes an image next to the ref and\n  drf-spectacular emits a stable component.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(media): source-agnostic StapelImage descriptor + DRF serializer",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-20T23:21:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bb9e20bcee5f82015059f773a5385dce61350274",
          "body": "stapel-auth 0.8.0 promotes an anonymous guest session via SSO JIT\nprovisioning and needs an accurate auth_type to record — reusing \"oauth\"\nor \"email\" for an SSO anchor would misreport how the account was\nactually established. Choices-only migration, no data change.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(users): add \"sso\" identity anchor to AUTH_TYPE_CHOICES (0.12.5)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-20T00:17:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "817ecabfb76be00766d9da0fba4d041d5773ce57",
          "body": "…cks (0.12.4)\n\ncdn-modularity.md §2.1/§5 (owner GO): image_type was frozen to a verbatim\ncopy of the legacy marketplace ImageType/AssetType enum, raising\nValueError at class-definition time for anything outside 6 hardcoded\nvalues — while the server (stapel-cdn 0.7.0+) already accepted any\nconfigured\n[…]\nred\n  at all — catches the miттudei-class incident at manage.py check time\n  instead of on the user's first \"Change avatar\" click in production.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(cdn): unfreeze CdnImageField image_type — open string + lazy che…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-17T04:16:40Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6751d6904878c841511ab488fdb47127525aeecd",
          "body": "…dy() (0.12.3)\n\nstapel_core.django's own eager AutoSchema import builds drf-spectacular's\nspectacular_settings singleton before a project's SPECTACULAR_SETTINGS\nassignment executes (settings.py opening with `from\nstapel_core.django.settings import *`), permanently pinning every schema\nthis process e\n[…]\no-effect when the order was\nalready correct. Same fix stapel-example-monolith previously had to carry\nlocally; it can now drop that local patch.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(openapi): patch drf-spectacular import-order bug in AppConfig.rea…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-17T03:19:45Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "72b971266a3e45825b2995a696a7bb7f71e3832a",
          "body": "…ment check (0.12.2)\n\nThe live incident: nginx reserved the bare /calendar prefix for the backend,\nsilently killing the frontend's SPA page at that path. Canon (BACKLOG §37)\nwas already clear — a backend module owns only /<mod>/api/, /<mod>/swagger/,\n/<mod>/schema.json, /<mod>/admin/ — but nothing m\n[…]\negories, gdpr, listings, notifications, profiles, workspaces,\n  mailtrap, agent, tasks, recordings, geo, video, reviews, currencies) is\n  compliant. Fixing stapel-translate is a separate repo/release.",
          "is_bot": false,
          "headline": "feat(mounts): §37 surface topology — reserved_paths() + mount-contain…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-17T01:50:29Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "d9e2c65f4da77a844420bd10b7d175444ea5524f",
          "body": "RedisStreamsBus (bus/backends/redis_streams.py) rounds out the\nkafka/nats/redis_streams standard set (владельческая директива). One\nRedis stream per topic (XADD), one consumer group per subscriber\n(XREADGROUP+XACK, group naming matches Kafka's group.id / NATS's durable\nname), XAUTOCLAIM reclaims ent\n[…]\nUTOCLAIM semantics\nwithout a live server: publish/consume round trip, ack, retry, DLQ,\npending redelivery after a simulated crashed-consumer claim, checks,\nconfig. 98% line coverage on the new module.",
          "is_bot": false,
          "headline": "feat(bus): Redis Streams backend — third standard comm-bus transport",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-17T01:18:53Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9275e4f9bc3ebabda15a02c0495911ef96aa3cf3",
          "body": "…RON_HOST, flat captcha, wire user block\n\nRemoved (breaking, minor per house law):\n- stapel_core.kafka transport (consumer/producer/config/health) + bus re-exports\n  + BaseKafkaConsumerCommand alias; package keeps only EventType/TOPIC_* constants\n- EventType.TRANSLATIONS_CHANGED / TOPIC_TRANSLATIONS\n[…]\nAC no-mandate behavior, get_config ConfigKeyUnknown path, nav prefix\nfallback — live runtime, not compat. 1775 tests green; verify errors 32→18.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(release)!: 0.12.0 — legacy sweep: kafka transport, JWT shims, I…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-17T00:31:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f5066335b7710ce80bec5bde3099000ca6c530cb",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(release): 0.11.2 - error params/language + bus singleton lifecycle",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T22:17:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bf2d1136c9fa14fab08512e9ef6e5ab03405cff3",
          "body": "…`error`\n\nLive finding: the backend sent a Russian `error` message and the frontend\nhad no way to know whether that text was safe to show verbatim or needed\ntranslating - COMMON_ERRORS templates are always English, but the DRF/\nDjango-ValidationError fallback (str(detail)) renders in whatever locale\n[…]\ns unchanged: the frontend still\ntranslates by localizable_error+params; error stays a fallback/debug string,\nnow with a known language attached.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(errors): error_language field tells the client whether to trust …",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T22:16:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c05371e589f21fae61042437eab3aad068f76cee",
          "body": "…gth, etc)\n\nDRF's ErrorDetail carries only .code (e.g. 'max_length') - the actual limit\nlives on the field that raised it (serializer.fields[field_name].max_length),\nso a catalog consumer (frontend i18n) could never render \"no more than N\ncharacters\" for a max_length/min_length/max_value/min_value/.\n[…]\nain rest_framework.serializers.Serializer that never attaches itself\ndegrades to the field-name-only params exactly as before - purely additive.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(errors): validation error params carry the field's limit (max_len…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T22:14:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f1bf497c36256ac0ce999993d0abff0fea08409c",
          "body": "get_bus() resolves the backend lazily on first call, but then holds it as a\nmodule-level singleton for the rest of the process. If something calls\nget_bus() once before a later override_settings(STAPEL_BUS_BACKEND=...) (or\nthe pytest-django settings fixture, used throughout this suite), the first\nre\n[…]\nis read once\nat boot and doesn't change without a restart - this specifically closes the\nruntime-reconfiguration gap (tests first and foremost).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(bus): auto-invalidate cached backend on settings change",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T22:11:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2b451ea314e23524b266272b1f682901dc75292a",
          "body": "…(0.11.1)\n\nFollow-up to the 0.11.0 kafka-default postmortem: get_config's Required\nwas only ever enforced the first time some code path called get_config(key)\n- possibly never, for a key nothing reads through the config seam yet.\nNew stapel_config system check (E001) walks every required key (CONFIG\n[…]\nnd of drift that left STAPEL_BUS_BACKEND's CONFIG.MD row still pointing at\nkafka after 0.11.0 moved the real default to memory (fixed here too).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(config): required keys enforced at boot + code-sourced metadata …",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T21:54:07Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "5d56e7890a62c951bb31178bec7a84a0bf982ce8",
          "body": "…av (0.11.0)\n\nBREAKING: STAPEL_BUS_BACKEND now defaults to the in-process MemoryBus\ninstead of kafka. Live run finding: request_notification (OTP emails)\nsilently never left the process because confluent-kafka wasn't installed\nand the ModuleNotFoundError was swallowed by fail-soft callers. Kafka/NAT\n[…]\nndex links each module's admin section, Swagger and schema, and a\nstaff-gated GET /nav exposes the same structure as JSON for a future\nfrontend.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(bus)+feat(nav): memory default over silent-kafka + admin module n…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T21:40:02Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bd6eb1b31f677a46ac3be46747bcc8e1f713609b",
          "body": "…-catalog, reference get_presenter() consumer (0.10.4)\n\ndeclare_swap()/declared_swaps() (swappable.py): import-time registration\nof swap points, independent of first resolution; get_model/get_presenter\nrecord their defaults lazily as a backstop; declarations survive\nclear_swap_cache().\n\nAuto-catalog\n[…]\n\n\nTests: test_presenter_catalog.py (19) — declarations, catalog entries,\nrendering, command incl. --check, profile block incl. swap. 1723 green.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(presenters): §55 slice 2 — swap declarations, PRESENTERS.MD auto…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T14:18:33Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6080e84d51d1a97ac98564b958c7ed980ce03479",
          "body": "…hs (§61, 0.10.3)\n\nimages-and-cdn.md §1/§1а/§6 п.6:\n- RenderMetadataProvider Protocol + pil (default, zero-infra ImageField\n  path) and cdn (delegates to cdn.describe comm Function) providers;\n  dotted-path escape hatch; describe(ref) facade — backend swap is\n  config (STAPEL_MEDIA / STAPEL_MEDIA_BA\n[…]\nrridable via STAPEL_CDN_MEDIA_URL\n\n1705 tests green (incl. 33 new media + field tests), ruff clean; wheel\nverified to carry stapel_core/media/*.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(media): stapel_core.media — one media interface, two storage pat…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T12:28:07Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "85523e09b71b9f50068ebf01b9e497e3cf4d1a92",
          "body": "…rojections-and-composition §1); real info.version via package= (api-versioning step 0)\n\n- Projection.live_query / force_mode; resolve_mode() by owner app label\n  (get_app_config, not is_installed — CTO-review note)\n- validate_registry(): local valid without model but requires live_query;\n  remote r\n[…]\ndes\n- get_spectacular_settings(package=...): info.version from installed\n  distribution metadata; explicit version wins\n- chore(release): 0.10.2\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(projections): local/remote colocation modes + read() accessor (p…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-16T11:19:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "dbde3a6b29b153cbfc08c4f3865ac4c94355f4d0",
          "body": "The avatar StringDataRightTruncation fix (5dc6c04) shipped after v0.10.0 was\ntagged without a version bump; cutting 0.10.1 now so the fix is installable\nfrom PyPI ahead of the stapel-auth companion release.\n\nCo-Authored-By: Claude Sonnet <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore(release): 0.10.1 — patch release for avatar URLField widening",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-14T09:31:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5dc6c0495cb9503a3b894ac3197b491aa0a2c958",
          "body": "…r avatar URLs (Google/GitHub) exceed the URLField default varchar(200), causing StringDataRightTruncation on OAuth signup\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(users): widen avatar URLField 200→500 + migration — OAuth provide…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-14T08:57:43Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7b0eb1e6c181d9a20de741b98cbf8f2cfd5dbd00",
          "body": "…s list — was missing → clean install/CI ImportError, publish blocked (packaging drift, explicit list)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(packaging): add stapel_core.django.projections subtree to package…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-11T18:10:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0c3c2e869cad3d9d115b2ade7cd5e92b94e88de1",
          "body": "… (§55 slice 1)\n\nPresenter (django/api/presenters.py) on top of the existing\ndjangorestframework-dataclasses dependency and StapelDataclassSerializer:\nmodel + as-is fields (type/description from the DAO) + custom_fields\n(PresenterField: rename/transform/nest another Presenter's dto) ->\npresent(dao) \n[…]\n end-to-end in tests/test_presenters.py — present(),\ngenerated schema, and presenter replaced by a host subclass via\nSTAPEL_SWAP config, no fork.\n\nCo-Authored-By: Claude Sonnet <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(presenters): DAO→DTO presenter primitive + swappable indirection…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-11T15:41:32Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "0cbec0b3900fd7f55d66cf936a5a4d087e4c16cc",
          "body": "get_config(key) (stapel_core.config) is a thin generalization of get_secret:\nenv-source keys read os.environ, vault-source keys delegate to the\nsecret-provider seam (get_secret). Routing table is a CONFIG.MD markdown\nregistry — {Key | Source: env|vault | Purpose | Required | Default}, grouped by\n`##\n[…]\ne tables/owners/required/default/bad-source, env|vault\nrouting, fail-closed, discovery+cache, core's own registry). Full suite green,\nruff clean.\n\nCo-Authored-By: Claude Sonnet <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(config): get_config over the secret seam + CONFIG.MD registry (§2)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-11T14:13:01Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "26607226849f5102851c391ece85961a69ae42e7",
          "body": "…10, Q2)\n\nFourth comm primitive alongside Action/Function/Task. Declare which Action\ntopic(s) feed which local read-model table and how each event upserts a row;\nthe framework gives generated idempotency + ordering (an event applies only\nif its position — sequence_field, else event timestamp — is st\n[…]\nommand. Suite 1598→1618 green.\n\nMinor bump 0.9.0 → 0.10.0; MODULE.md + CHANGELOG documented with an abstract\ncatalog-likes-from-engagement example.\n\nCo-Authored-By: Claude Opus <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "comm: Projection primitive — event-carried read-models over Action (§…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-09T23:13:55Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8480d2b627493a17c350bdcc84978c4c9e176ec0",
          "body": "…(gap G14)\n\nWebSocket/ASGI counterpart of the HTTP JWT stack. `JWTAuthMiddleware`\n(+ `JWTAuthMiddlewareStack` factory) validates the token with the SAME\n`jwt_provider` — same signing config, token/user blacklists, and\n`get_or_create_user_from_jwt` sync — then populates `scope[\"user\"]` and\n`scope[\"st\n[…]\nels\n  raises a clear ImportError.\n\nMinor bump 0.8.0 -> 0.9.0. Tests: tests/test_jwt_channels.py (28), 100% module\ncoverage; full suite 1598 passed.\n\nCo-Authored-By: Claude Opus <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(jwt): Channels JWT auth middleware over the shared jwt_provider …",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-09T21:26:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b7b64cc83362af2783d5ca07c90401dd485f47a1",
          "body": "Adds `make migration-lint` and a CI step next to Lint/emit-check/Import\ncheck. This repo's own migration finding (phone AlterField narrowing) was\ntriaged and marked in the prior commit — `make migration-lint` is green\nnow. The CI step ships commented-out with a TODO: no module repo's CI\ninstalls sta\n[…]\nthe new org, no git-install\nmechanism wired anywhere). Run `make migration-lint` locally / at\nrelease-cut time until CI can fetch stapel-tools.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: wire migration-lint gate (release-management.md §3)",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-09T11:33:45Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "47f3e2e378426af2cc3369cbf5602c0387c51660",
          "body": "…er_phone)\n\nstapel-migration-lint --strict flagged the phone AlterField narrowing\n(20 -> 18 chars) as destructive without a contract-phase marker. This\nmigration shipped in stapel-core's first commit/release (extracted from\nthe monolith); no released version of this package predates it, and\ndjango/u\n[…]\nre normalized via RunPython while the column is still varchar(20), before\nthe AlterField narrows it. Marker/comment only, no operation changes.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(migrations): mark verified expand/contract finding (0004_alter_us…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-09T11:29:17Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "068d82f22e1f8611106d139e3c4a02eb76c7b639",
          "body": null,
          "is_bot": false,
          "headline": "docs: add PyPI badge to README",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-08T14:29:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b509835954a8af9138f945b34e9e0e5bfd5137bc",
          "body": "Rewrite legacy-provenance references in docstrings, comments, and the\nchangelog for consistency.",
          "is_bot": false,
          "headline": "docs: refactor provenance wording in comments and changelog",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-07T13:23:47Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c1e12853a9575ce4418acb3ef46a95456f9e7d6a",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: CHANGELOG + MODULE.md for the flow Gherkin projection",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T16:38:38Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a06bbe399989a3aca37da7799fcaf5fa803ceaca",
          "body": "…drift gate\n\ndocs/examples/auth-flow-features/: committed snapshot (stapel-auth\nflows.json + en/ru catalogs) → generated en/ru bundles (.feature ×3 +\nplaywright-bdd step-defs + fixtures). tests/test_flow_feature_reference.py\nis the release-gate drift check (regenerate-and-diff, byte-stable;\nSTAPEL_R\n[…]\ncore because stapel-auth is read-only\nfor this change; wiring features/ + the gate into stapel-auth proper is\nthe module maintainer's follow-up.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(flows): reference Gherkin bundles for the 3 stapel-auth flows + …",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T16:33:28Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "63f693e68570654ebb16cc2e59f9c0ac7f571878",
          "body": "…e Gherkin projection\n\nload_flows_json rebuilds (FlowSpec list, endpoint index) from the exported\nmachine artifact — the Gherkin projection can now run from a committed\nflows.json (codegen output, reference snapshot) without booting the Django\ninstance that produced it; loading never touches the glo\n[…]\n by\ngenerate_flow_features and json-based generation. Parity test: live\nregistry and json-loaded flows render byte-identical .feature/step-defs.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(flows): flows.json loader + shared language-bundle writer for th…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T16:31:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fc15e62c42b8bd42d85023c55daa4125e8b4c4c8",
          "body": "…e-stable bundles\n\n15 tests: positional Given/When/Then mapping (And-folding), ru dialect\nheader + localized keywords, unknown-language fallback, single-line note\nfolding, JS-regex escaping, Django→OpenAPI path conversion, HTTP steps\ncalling the typed client, honest TODOs (parametrized paths, unrout\n[…]\nd\nviews, testid-less UI steps, comm effects), i18n-resolved regexes, and\nthe bilingual generate_flow_features bundles reproducing byte-for-byte.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(flows): Gherkin generator — dialects, step-def client calls, byt…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T16:27:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "94c0a8f3338528a9669302c44e66d5ea04ebcc5d",
          "body": "… generator\n\nflow-system.md §3 (wish #3): the flow is the source, the .feature is a\nprojection. stapel_core.flows.gherkin renders one localized Feature per\nflow (positional Given/When/Then over the resolved i18n step notes, ru\ndialect header for non-en) plus a playwright-bdd step library: HTTP steps\n[…]\nflow_features writes one\nself-consistent bundle per project language (byte-stable, drift-gate\nready — same discipline as generate_project_docs).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(flows): Gherkin projection — .feature + playwright-bdd step-defs…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T16:27:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8d032b51fe021a77c808efff2114885b5c718eec",
          "body": "…taff mandate (AS-7)\n\nNew subsection under the Staff mandate: day-1 no-op guarantees (opt-in by the\nfirst role, additive-only migration, step-up self-disabling), the five\nindependent enabling steps (backends -> roles -> declarations ->\nStapelModelAdmin -> step-up), the AS-2 sync note for local staff\n[…]\nt ordering for\nmigration scripts), the exit path from the legacy Staff group via\naccess_report, and the symptom -> system-check diagnostics map.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(MODULE): migration guide — moving an existing project onto the s…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T12:37:36Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6797548af74611325cf8ad1dfd48b47b421f5fb3",
          "body": "…te AS-6)\n\nQ8a: step-up is part of the standard preset (delete=HIGH), enforced by\ndefault — not opt-in. A HIGH-required admin mutation needs a fresh\nverification grant on top of the mandate; the mandate says a role *may*\nact, step-up says it was re-proven recently.\n\n- stapel_core.access.stepup: STAP\n[…]\n5 (enforced but degraded). No\n  migrations (policy + signals + admin behavior only).\n- 31 new tests (1550 total green); ruff + emit-check clean.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(access): step-up on HIGH admin ops + audit forwarding (admin-sui…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T11:30:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4eed59dfd371a34ffe1778884ef57e3b79dd86c0",
          "body": "…AS-4 §2 arbitration)\n\nThe AS-4 review (§2) rejected the original current_dashboard_url heuristic —\n\"first admissible dashboards/tools link under the current URL_PREFIX\" — as\ntoo implicit for a mechanism contract: a module should say it owns the\nservice dashboard, not have it inferred from URL shape\n[…]\nings-overlay flag,\n  fallback preserved with no flag set, two-flags ordering, duplicate-flag\n  warning. MODULE.md documents the selection order.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(nav): explicit service_dashboard flag for current_dashboard_url (…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T11:07:49Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c6c90f5f19bcd818200f63676b2ee025dd39060e",
          "body": "…-suite AS-4)\n\nService navigation is no longer hardcoded in the framework — the marketplace-legacy\nSTAPEL_SERVICES list in core/config.py and the Tools/Monitoring/dashboard links\nbaked into base_site.html + the Swagger inject were policy in a mechanism\n(§2.1). Both move to deploy-config.\n\n- stapel_c\n[…]\nting for\n  staff/superuser/non-staff/anonymous, empty registry, monolith vs micro,\n  malformed-config checks). MODULE.md nav section, CHANGELOG.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(nav): STAPEL_SERVICES env-JSON + NAV_LINKS merge-registry (admin…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T10:55:27Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6ef446fd681b397b270b4d367233c18dbf061d4f",
          "body": "…PEL_ADMIN, core ops admins (admin-suite AS-3)\n\nEnforcement through the AS-1 backend, not app-list cosmetics: a direct\n/admin/app/model/ URL answers 403 per (declaration x clearance). The admin\nlayer adds what has_perm cannot express — ops read-only even for superusers\n(A5 bypasses the mandate), sec\n[…]\nts: category x role x operation matrix via real admin client on direct\n  URLs, masking, SHOW_OPS_MODELS, overrides, contrib (51 new; suite 1479)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(admin): category-driven admin visibility — StapelModelAdmin, STA…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T10:02:07Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "61527add4607b338c9aa3b1936f0b40a49e76cec",
          "body": "…AS-2)\n\nConsumer half of the staff-role transport (producer: stapel-auth 6fdb6f9).\nAuth is the single source of truth for staff status (в.3):\n\n- staff_roles JSONField on AbstractStapelUser (+ users/0006 migration): the\n  shadow copy of the staff_roles JWT claim.\n- serialize_user_to_jwt_data emits th\n[…]\nmade the group-source\nfallback unreachable (field is now authoritative). 1428 core tests pass;\nstapel-auth 1194 stay green against the new core.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(jwt): staff-roles sync-down REPLACE + claim bridge (admin-suite …",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T07:08:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bfe0941833b7081fdb3e223340ce505269846b79",
          "body": "…-module targets, stop hardcoding root-relative paths\n\nRoot cause (live, sub-path-mounted project): LOGIN_URL and every\ncross-module URL target were hardcoded root-relative, so a project\nmounted whole under a prefix redirected anonymous admin visitors to\n/admin/login/ -> /auth/admin/login/ -> 404.\n\n\n[…]\n chain under an\n  include prefix (tests/test_mounts.py, 38 tests). MODULE.md section +\n  no-absolute-paths anti-pattern; CHANGELOG [Unreleased].\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(mounts): canonical URL mount registry — derive LOGIN_URL & cross…",
          "author_name": "Victor Pasenchuk",
          "author_login": "Pasenchuk",
          "committed_at": "2026-07-06T05:40:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "21a5c0607eb5539d7890b990ad1ad07bb7d18459",
          "body": "…8n-shipping wave 0)\n\nGeneralize the flow-i18n contour into stapel_core.i18n over content domains\n(flows, errors, …): per-app translations/<domain>.<lang>.json catalogs\ndiscovered over INSTALLED_APPS, merged later-wins (fork-free host override);\nSTAPEL_I18N (LOCALES single project-languages knob, EX\n[…]\ns).\n\nTests: discovery/merge, provenance roundtrip, seed/llm/approve/stale,\nparams + override gate, error-docs, command wiring. Full suite green.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(i18n): domain-agnostic content catalogs + errors ru pipeline (i1…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T04:38:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "36b82bc1dea0845bb73db95be0c226c9e5409377",
          "body": "… TTL cache)\n\narch-stapel-vault Part 1. get_secret(name, default=…) resolves a secret\nthrough a dotted-path provider seam STAPEL_SECRETS[\"PROVIDER\"] (canon like\nAUDIT_SINK/ROLE_SOURCES). Default EnvSecretProvider = os.environ — local dev\nand the minimal preset are unchanged, zero new deps. Pointing \n[…]\n, fail-closed, env transparency, bootstrap,\nprodguard compat, checks). Full suite 1356 passed, ruff clean, emit-check clean.\nVersion NOT bumped.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(secrets): SecretProvider seam in core (env default, fail-closed,…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T03:15:25Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "144a2429b104968c572f494999e1c68d2e9e62b4",
          "body": "stapel_core.access: MAC for staff/admin — has_perm is a computed function\nof (@access declaration x role clearance), no Permission-string generation.\n\n- @access decorator + presets (standard/sensitive/ops/secret); undecorated\n  model = implicit standard; admin category in the same declaration\n- Mand\n[…]\n); runtime role\n  definitions reserved behind a flag with a written mini-design (в.1)\n\n78 new tests (1336 total green); ruff + emit-check clean.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(access): staff mandate — computed admin rights (admin-suite AS-1)",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T03:01:37Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fe1712664f9b49ebebe7a82d7ccfd4ed47216785",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): editable test install + publish gated on tests (§7.5-7.6)",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T02:36:31Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "52e02b35ec4398c42224ac877dc2f99e50848817",
          "body": "Add the backend companion of schema.json/flows.json: a `generate_error_keys`\nmanagement command that emits errors.json — the byte-stable registry of every\n`error.<status>.<name>` key an instance can raise, as an array of\n`{code, status, params, remediation, en}` sorted by code. Shape matches the\nfro\n[…]\nr own remediation, and captcha en text\nis aligned to the canonical fuller copy so a re-declaring service produces an\norder-independent artifact.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(errors): errors.json codegen artifact + declarative remediation",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T02:16:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4a851b0e3fce573a23a7ca90d0daebdb3a1511ad",
          "body": "stapel_core.django.prodguard.guard_secret/guard_db_password reject known\n.env.example placeholders (change_me*, django-insecure-), too-short\nSECRET_KEY/JWT_SECRET_KEY (<50 chars), and the default/placeholder\nPOSTGRES_PASSWORD — the previous inline guard in generated prod.py only\ncaught an empty or django-insecure- prefixed secret. Wired into\nstapel-tools' prod settings templates in a companion change.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(django): add prod-guard for placeholder/weak secrets (SEC-4/B2/B6)",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-06T01:33:14Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "aa595b78f6aa18da27798022037f46ecb831dbd6",
          "body": "…6.0 (904f3a8), gateway as 0.7.0\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: restore [0.6.0] heading in CHANGELOG — eventstore shipped as 0.…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T23:52:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8972a36c17e368b7b6350cedf3d0102f7c5bb98d",
          "body": "…classifiers)\n\nstapel-core was the only workspace lib missing [project.urls] and carried\nthe old minimal classifier set. Align to docs/library-standard.md §4\nartifact checklist and the 19 sibling libs: add Homepage/Repository/\nDocumentation/Changelog/Issues and the full classifier matrix (License\nOS\n[…]\nce, Development Status 4-Beta, OS\nIndependent, Python 3::Only, 3.11-3.13 matching CI). Metadata-only; no\nversion bump (0.8.0 not yet published).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "packaging: complete PyPI metadata for 0.8.0 (project.urls + standard …",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T23:48:35Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "35ff95702ca72d34bbc9cdc9fae4d03a523e326b",
          "body": "…tore (0.8.0)\n\nThe internal comm-Task persistence app collided on the Django label\n`stapel_tasks` with the new generic stapel-tasks module (0.1.0), which owns\nthat canonical label. Two apps can't share a label in one INSTALLED_APPS, so\ncore vacates to `stapel_taskstore` (docs/tasks-module.md §2/§11)\n[…]\ntogether in one\n  INSTALLED_APPS (subprocess, since Django configures once per process).\n\nMinor bump — label is part of the public app contract.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor(taskstore): rename Django label stapel_tasks -> stapel_tasks…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T20:53:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e575a6dcc1f1c748afd0f852d99e436a95714ec9",
          "body": "…it (0.7.0)\n\nstapel_core.gateway (Studio SN-4, studio-design §2.3 / system-design §5.9):\nthe OSS mechanism of \"capability, not credentials\" — concrete verbs and\npolicies stay in the deployment (Studio, private).\n\n- Verb = name + mandatory JSON args schema + policy {tiers, rate_limit,\n  require_confi\n[…]\nn mandatory, fails closed without jsonschema\n  (extra stapel-core[gateway]).\n\n96 new tests (1231 total). Root export stapel_core.gateway (lazy).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(gateway): privilege gateway mechanism — verbs, scope tokens, aud…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T20:41:48Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "904f3a82133febd6a1bb83163680c1d1ff5a66de",
          "body": "…nd + buffer/rollup/retention (0.6.0)\n\nNew `stapel_core.eventstore` seam (Studio SN-3′, docs/data-storage-and-observability.md\n§1; studio-design §3 three storage contours): one primitive for the many\nhigh-volume append-only streams (LLM-call ledger, gateway audit, analytics,\ndelivery logs) — written\n[…]\nrtition command;\n  SQLite minimal profile degrades to one plain table (documented).\n\nBump 0.5.1 → 0.6.0. CHANGELOG, MODULE.md. Base 1101 → 1135.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(eventstore): append-only stream primitive — ABC + Postgres backe…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T20:07:55Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "22e6649ac3eb55313777a06a319357aba6dd23f3",
          "body": "…oncurrent issuance (0.5.1)\n\nReview H-3 (catalog feature editor): RevisionMixin.save bumped the in-memory\nrevision on every save but never added it to update_fields — scoped saves\nemitted a revision that was never persisted, and the number was reused by the\nnext content change, so sync clients follo\n[…]\no,\none-shot-iterable update_fields, nested-atomic regression, 8x5-thread\nuniqueness (sqlite-compat). Suite 1101 passed; ruff + emit-check clean.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(revision): phantom revision on save(update_fields=...) + locked c…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T19:50:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d6b8b02cfcb359154daf4821bd18e9059a9da1fb",
          "body": "…gual trees (flow-system.md §4)\n\nFLOW_DOC_RENDERER seam (default DefaultFlowDocRenderer): per-flow markdown\nwith a GitHub-native mermaid step diagram, numbered steps and an Endpoints\ntable carrying serializers + the step-up verification contract. Renderer\nchrome is localized (en/ru; unknown -> Engli\n[…]\n makes the regenerate + git diff --exit-code release-gate\nmeaningful. New settings FLOW_DOC_RENDERER, DOC_LANGUAGES; additive\nthroughout. 0.5.0.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(flows): SA-document renderer — mermaid + endpoint tables + bilin…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T19:35:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "eef94ba40ba8689d92eeb0c77cb02ee803b12fdb",
          "body": "….4.1)\n\nDefensive review findings (low). _breaker_* now take _provider_lock for all\nstate access — the failure counter is a shared read-modify-write, so under a\nconcurrent fail-open flood unlocked increments dropped counts and the breaker\nopened later than its threshold; the module comment claiming \n[…]\ngs reconfiguration re-warns instead of staying silent for the\nwhole process. Behaviour: exact, faster breaker trip + log hygiene; no API change.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(netintel): lock circuit-breaker state + reset warned-providers (0…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T19:03:25Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1b38fbd2d1098cdcac6067333cd146fa0520baa8",
          "body": "…m.md §2)\n\nFlow/step texts become i18n keys (implicit flow.<id>.title /\nflow.<id>.step.<order>.note, explicit *_key override); the in-code literal\nstays the canonical English source and the fallback — literal-only flows\nwork unchanged. flows.json now carries the keys alongside the literals\n(language\n[…]\n → source literal. generate_flow_docs\ngains --lang/--llm/--llm-cache; check_flows flags colliding step keys.\n\nMinor bump 0.4.0 (pre-1.0 semver).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(flows): i18n keys for flow texts + resolution engine (flow-syste…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T18:32:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c6c5b4bf01bf5283128c4d8868e5be9a4a27a9cf",
          "body": "Reliability/correctness hardening of the builtin IP-classification +\ncaptcha-tiering defense (internal review findings H1/H2/M1/M3/L1 +\nL2/L3/M2 docs). No behavior-offensive changes; all defensive.\n\nH1 load resilience: provider errors now negative-cache the unknown\nprofile for a new short NEGATIVE_C\n[…]\nmoization,\nIPv6/garbage normalization, no_env targeting; updated negative-cache and\nclient_ip-trust-model expectations. 1064 passed, ruff clean.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "netintel/captcha: harden anti-abuse seam for load & concurrency",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T17:30:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9056af8f039e5090ff611863fd801311a9ade83a",
          "body": "… emit-check gate (0.3.3)\n\nTwo module repos independently broke the outbox guarantee the same two\nways (categories C1: swallowed emit failure; listings L2: save and emit\nin separate transactions). Turn the discipline into mechanism:\n\n- comm.mutate_and_emit(): mutation + emit in ONE transaction; yiel\n[…]\n new _requeue().\n- Tests: test_emit_atomicity.py (15, incl. test_failing_emit_rolls_back\n  class) + test_emit_check.py (18). Suite: 1053 passed.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: outbox atomicity as a seam — mutate_and_emit(), runtime guards,…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T12:58:47Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "57fab122f4cebd06f757f9541bd98e52a2103cf9",
          "body": "…dditive)\n\nstapel_core.netintel (classify_ip seam) + captcha challenge-policy tiers.\nBackward-compatible (NullProvider default = unknown = today's behavior; all\n20 legacy captcha tests pass unchanged) -> patch bump. 1018 tests. Queued for\nFable re-review (docs/fable-review-queue.md).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: release 0.3.2 — netintel IP-classification + tiered captcha (a…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-05T00:50:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7cf091a07d53ae7d21c6d0974918a2ccc04f574c",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: gitignore *.err build scratch",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-04T20:03:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "104acdc2a95c151cecabe82b9ff8698468799c17",
          "body": "Implements docs/geo-network-trust.md §0 and §2:\n\n- stapel_core.netintel: classify_ip/country_of/client_ip on a dotted-path\n  provider seam (STAPEL_NETINTEL[\"PROVIDER\"], default NullProvider).\n  Built-ins: MaxMindProvider (GeoLite2/GeoIP2 mmdb, optional extra\n  netintel-maxmind) and HttpJsonProvider \n[…]\nion, level,\n  allowed). Unconfigured netintel reproduces the historical binary\n  behavior exactly; CaptchaMixin and existing backends untouched.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: netintel IP-classification seam + tiered captcha challenge policy",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-04T09:10:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "55703ca620476add8e2abdc1738c8b55ec57c274",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: release v0.3.1",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-04T00:41:43Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2b1798634ddd44b96a652dd6c99d188b3a13fb4c",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add MODULE.md — agent-facing extension-point map",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-03T16:05:40Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a6aa18d638af734c33afa7fd566c4ff8a657ddbe",
          "body": "…late topic deprecated\n\n- schemas/emits/notification.requested.json describes the real payload\n  incl. optional content_html/content_text; request_notification validates\n  shape at the edge (type-registry membership stays consumer-side + lint —\n  split documented in the schema)\n- TOPIC_TRANSLATIONS_CHANGED / EventType.TRANSLATIONS_CHANGED deprecated\n  in place (kept for pinned deployments)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: notification.requested contract + edge validation; legacy trans…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T21:17:17Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8c70942c55e686492921fe0c6bc3d601808f4b2c",
          "body": "…%, migrations/tests ignored\n\nCodecov's default patch target=auto compares every diff against the\nproject average — a bar that only drifts upward and fails legitimate\nlow-testability diffs. Policy codified in system-design §7.13.5.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: codecov policy — project ratchet (0.5% tolerance), patch floor 80…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T20:59:30Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b255da07880a0f4bc9158063e25891dd9d88b14b",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: __version__ reads package metadata instead of a stale literal",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T20:08:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0107c13e8b0a01538c40768bbcc29be6f9032cb5",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.3.0",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T20:01:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "15ed1fceb4b21255fb89495562209a4c5f151bff",
          "body": "- stapel_core.verification: attach OTP/TOTP/passkey requirements to any\n  endpoint via @requires_verification(scope, factors, max_age) — 403 with\n  a structured challenge envelope, interchangeable factors, server-side\n  grants per user+scope (or stateless X-Verification-Token), policy\n  overrides vi\n[…]\n some topics\n  to NATS, others to Kafka/memory) behind the same BusBackend facade\n- comm: task dispatch and action/config refinements with tests\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: step-up verification framework + per-topic bus routing",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T19:56:38Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ed1c544f518fddb65c0b1aa41b587be82bc5febd",
          "body": "phonenumbers (User.save phone E.164 normalization) and celery (the\noptional comm.tasks executor) are exercised by the suite but were declared\nnowhere, so 3 tests failed on the clean CI runner. phonenumbers is now a\nruntime dependency (silent degradation otherwise); celery lives in the dev\nextra and CI installs .[dev].\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: declare phonenumbers dep + celery dev-extra for CI",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T19:09:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "896860d04830e3caf350a7cc1c79aa7e25f39a5e",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.2.2",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T18:21:22Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "da96930199df300e2fbfd475694b9f187d0dfe2d",
          "body": "EndpointInfo carries the handler attribute name (action name for\nViewSets, http verb for APIViews); the postprocessing hook resolves the\nreal handler — @flow_step/@requires_verification on ViewSet actions now\nreach x-stapel-flows / x-stapel-verification.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: flows/verification OpenAPI hook resolves ViewSet action handlers",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T15:42:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a9e3be82471ddde1ed4cf5929a9165d4c93175d6",
          "body": "COMMON_INSTALLED_APPS/COMMON_MIDDLEWARE require corsheaders, but the\nwheel did not pull it in — pip-mode installs failed at django.setup().\nFound by the examples' switch to the real user install path.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: declare django-cors-headers dependency (release 0.2.1)",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T14:48:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "28fbf156f4da5ce84325b0448a527db39004b1f4",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.2.0",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T14:37:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e66b454221fbf70988ab3fb71231a8c947614227",
          "body": "…ublic API\n\nFlows (stapel_core.flows) — the self-documentation contract:\n- Flow objects with http/action/function/task/human steps; @flow_step\n  attaches an endpoint to any number of flows; autodiscovery from\n  INSTALLED_APPS flows.py\n- manage.py generate_flow_docs renders SA-grade markdown per flow\n[…]\nte docstring\n- coverage 52% -> 99% (354 -> 887 tests): jwt, infra, api, comm gaps\n- consume_actions bridge included (bus -> @on_action registry)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: flows doc engine + step-up verification + 99% coverage + real p…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T14:33:27Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "13177b23860a4e3251475f6a1409fbc62bdc24f1",
          "body": "…action delivery\n\nIn ACTION_TRANSPORT=bus deployments, emitted actions land on Kafka topics\nnamed after the action, but nothing delivered them to @on_action handlers\nin other services. consume_actions subscribes to every locally subscribed\naction name (or an explicit --topics subset) and dispatches events to the\nlocal registry; handler failures re-raise so the backend's DLQ applies.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: consume_actions — bus-to-registry bridge for cross-service @on_…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T13:53:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c0dcfaa0d3511144dac74991f6909e8980301ef1",
          "body": "…R templates, CI matrix\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: community files — contributing guide, security policy, issue/P…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T10:33:28Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f485486c632c201cb7571c3c7965dc8fe135c74a",
          "body": "…ract user, topology settings, security)\n\nTask (third comm primitive, docs §2.1): start()/status()/@task_handler —\nasync named operations with persistent state (TaskRecord), outbox-backed\ntask.requested, atomic claim (idempotent under redelivery), retries with\nattempts cap, task.completed/task.faile\n[…]\n_BLACKLIST_FAIL_OPEN opt-out)\n\nPlus CHANGELOG.md, py.typed, packaging for the new packages. 60+ new\ntests; full suite green; no migration drift.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Task primitive + fork-free core (conf namespaces, signals, abst…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T10:15:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d2fb399079e22ac954eada51acf3cae147947d31",
          "body": "- explicit ack_wait=300 on the durable consumer: handler retries sleep up\n  to 14s per message and a batch of 10 is sequential — the 30s default\n  would redeliver messages still being processed\n- deterministic Nats-Msg-Id (<event_id>.dlq) on DLQ publishes so a\n  redelivery after a failed ack does no\n[…]\ntests need the django_db mark —\n  close_old_connections() trips pytest-django's connection guard in\n  full-suite runs (order-dependent failures)\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: NATS bus review fixes on top of the JetStream backend",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T08:04:25Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "90d77d6f8109afd4086ddf9706984c18686d6661",
          "body": null,
          "is_bot": false,
          "headline": "wip nats refactoring",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T07:56:50Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9e0e2e0aaa045ba1d1476d9bad9a927838738d9e",
          "body": "…ices\n\n- comm/nats.py: client transport with a dedicated event-loop thread and\n  one multiplexed connection per process (nats-py is asyncio-only, call\n  sites are sync Django); protocol-level timeouts; NoResponders maps to\n  FunctionNotRegistered\n- manage.py serve_functions: provider-side worker sub\n[…]\nnats extra (nats-py>=2.6)\n- removes the connection-per-call failure class entirely and needs no\n  FUNCTION_ROUTES — the subject is the discovery\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: NATS request-reply as the recommended Function RPC between serv…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T07:24:03Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d595a5708a9756ec560ca7411925f8295e38c2ea",
          "body": "…hardening\n\n- stapel_core.comm: name-addressed Action (emit/on_action) and Function\n  (call/@function) primitives; in-process, bus and pluggable dotted-path\n  transports (gRPC/NATS slot); pooled HTTP session with connect-only\n  retries to survive high call rates; schema validation hooks\n- stapel_cor\n[…]\ning\n- packaging: ship comm/outbox packages, templates and static in wheels;\n  Django floor raised to 5.1 (code uses CheckConstraint(condition=))\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: Action/Function comm layer with transactional outbox; security …",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-02T07:10:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "187615bf57853fc5551d308ffbb680c8786366c2",
          "body": "- Remove IronDataclassSerializer alias (use StapelDataclassSerializer)\n- Remove iron_exception_handler alias (use stapel_exception_handler)\n- Remove IRON_SERVICES / iron_services aliases (use STAPEL_SERVICES)\n- Introduce bus/ module as transport-agnostic replacement for kafka/\n- kafka/__init__.py be\n[…]\n re-exporting from bus\n- Cookie names now configurable via STAPEL_COOKIE_* Django settings\n- django/errors.py: wildcard → explicit re-exports\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: rename Iron* aliases to Stapel* across the framework",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T20:36:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "56432281769908e71b51b11b88528c058ab20919",
          "body": "…eipt\n\nKafkaBus watchdog killed the consumer after HEARTBEAT_STALENESS_S (120s)\nof inactivity because _touch_heartbeat() was only called when a message\nwas received. On low-traffic topics this caused a crash loop every ~2 min.\n\nHeartbeat now reflects poll-loop liveness, not message throughput.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: touch heartbeat on every poll iteration, not only on message rec…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T02:01:35Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9196596168eca7053fd650d8fc483d9482814db0",
          "body": "TestGetOrCreateUserFromJwt tests use @pytest.mark.django_db — without\npytest-django installed, the mark is ignored and the in-memory SQLite\ndatabase is never initialized, causing 'no such table: users' errors.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): add pytest-django to test dependencies",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T00:49:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d175d08693b351496889f00a0c0ef48581853f87",
          "body": "…import\n\npytest tries to import the nearest parent's __init__.py as a package\ninitializer for every test in the package (importtestmodule with\nself.path / \"__init__.py\"). With flat layout, __init__.py sits at the\nrepo root and has relative imports that fail without package context.\n\nAfter pip instal\n[…]\nl source is\nno longer needed. Renaming __init__.py prevents pytest from finding it.\n\nConfirmed 296/296 tests pass locally with this approach.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): rename __init__.py before tests to stop pytest package init …",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T00:24:54Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3c50062546619d823c11b4cc50285b8d01d75f34",
          "body": "…rsal\n\nIn legacy pytest mode, when tests/ has __init__.py, pytest traverses\nparent directories through __init__.py to determine the package root.\nThe flat package layout (package-dir={\"stapel_core\":\".\"}) places the\nactual package __init__.py at the repo root, which has relative imports\nthat fail whe\n[…]\ncontext.\n\nRemoving tests/__init__.py makes pytest add tests/ to sys.path and\nimport test files as standalone modules, avoiding the traversal.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): remove tests/__init__.py to prevent parent __init__.py trave…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T00:15:32Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3561581f6f6079e9798b125bbdd0d30738e5a6e8",
          "body": "In legacy pytest mode, when tests/__init__.py exists, pytest traverses\nparent directories looking for the package root and tries to import the\nrepo root's __init__.py as a standalone module. With flat package layout\n(package-dir={\"stapel_core\": \".\"}), this __init__.py has relative\nimports that fail \n[…]\nithout a package context.\n\nimportlib mode bypasses this sys.path traversal, so pytest imports test\nfiles directly without affecting sys.path.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): add --import-mode=importlib to prevent __init__.py traversal",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T00:10:09Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e3844064e8c939cd5580e9c896c89c1a7e0ab898",
          "body": "The flat package layout (package-dir={\"stapel_core\":\".\"}) places a\ndjango/ directory at the repo root. pytest adds conftest parent dirs\nto sys.path, so `import django` resolved to the local django/ package\ninstead of the installed Django framework — causing CI failures.\n\nFix by removing the repo roo\n[…]\nat the very top of\nconftest.py, before any stapel_core.testing imports run.\n\nAlso revert the cd /tmp workaround in ci.yml (no longer needed).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: remove repo root from sys.path to prevent django/ shadowing Django",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-07-01T00:00:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "07a7da2c72b96f2e610cb1db288ea69c6d632f4c",
          "body": "…ango\n\nFlat package layout (package-dir={\"stapel_core\":\".\"}) places django/ at\nrepo root. Python's '' sys.path entry causes `import django` to resolve\nto the local package dir instead of installed Django, breaking all tests.\n\nFix: run pytest from /tmp so CWD has no django/ dir. Revert\n--import-mode=importlib (it broke relative imports in __init__.py).\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(ci): run pytest from /tmp to avoid django/ shadowing installed Dj…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T23:55:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "78b8bf3dba880dee57bd657cc3c672af5ee79de4",
          "body": "The package uses flat layout (package-dir = {\"stapel_core\" = \".\"}), so\ndjango/ lives at the repo root. pytest adds CWD to sys.path, causing\n`import django` in testing.py to pick up the local django/ package\ndirectory instead of the installed Django framework.\n\n--import-mode=importlib prevents pytest from adding CWD to sys.path.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix: add --import-mode=importlib to pytest to fix django/ shadow in CI",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T23:39:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "de10cbf6fb2c84b0f2c0845b8c776bd94ad0d740",
          "body": "…erage\n\n- Consolidate jwt_provider.py → jwt/provider.py (removed duplicate)\n- Extend IronServiceError/IronValidationError aliases\n- Improve openapi schema generation\n- Add test files: test_bus_config, test_captcha_mixin, test_notification_tokens, test_oauth\n- Various minor refactoring and cleanup\n\nAll 296 tests pass\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: JWT provider consolidation, API layer cleanup, new test cov…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T23:07:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6c8a7d6db4fa6a772957b72b69901380b5e6000c",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: add coverage reporting + Codecov badge to README",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T21:51:36Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6168aff2aba2288acf8f2d5ebd19f2c4f6242f9e",
          "body": null,
          "is_bot": false,
          "headline": "fix: ruff lint cleanup (auto-fix unused imports and style)",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T21:37:46Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c977ffb1ec68f4128359a04e347f309950597d17",
          "body": "…ntication\n\n- Add stapel_core.captcha package: CaptchaVerifier ABC, TurnstileVerifier,\n  RecaptchaVerifier, HcaptchaVerifier, NoopVerifier, build_verifier factory\n- Add stapel_core.django.captcha: CaptchaMixin DRF serializer mixin,\n  get_verifier() reads CAPTCHA_BACKEND/CAPTCHA_SECRET from Django se\n[…]\ncation.py: remove module-level User = get_user_model() (unused,\n  caused AppRegistryNotReady when DRF imported the class before Django setup)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add pluggable captcha system + fix AppRegistryNotReady in authe…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-30T20:53:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3730189028f4ed28115d8383e08664035ac16a25",
          "body": null,
          "is_bot": false,
          "headline": "кrfactor structure",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-29T23:43:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0185e1bb6ef97250a78ff65d91b876a60198520f",
          "body": "Tests: 160 cases across bus, JWT, errors, permissions (0 failures, 0 warnings).\n  - tests/conftest.py   — configure_django with SQLite+LocMemCache, reset fixtures\n  - tests/test_bus.py   — Event serialization, MemoryBus publish/subscribe/consume/clear, router\n  - tests/test_jwt.py   — JWTConfig, JWT\n[…]\n  - is_token_near_expiry: utcnow() → now(timezone.utc)\n\npyproject.toml: add [project.optional-dependencies.dev] and [tool.pytest.ini_options]\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add test suite + fix datetime.utcnow() timezone bug in jwt_handler",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-29T11:26:02Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3473071689f07945e7fb8559f6eb6cd7ce6338f4",
          "body": "…is client dependency",
          "is_bot": false,
          "headline": "refactor: TokenBlacklist uses Django cache framework, removes raw Red…",
          "author_name": "Victor Pasenchuk",
          "author_login": null,
          "committed_at": "2026-06-28T16:05:21Z",
          "body_truncated": false,
          "is_coding_agent": false
        }
      ],
      "releases_count": 24,
      "commits_last_year": 109,
      "latest_release_at": "2026-07-25T20:17:05Z",
      "latest_release_tag": "v0.14.2",
      "releases_from_tags": true,
      "days_since_last_push": 0,
      "active_weeks_last_year": 5,
      "days_since_latest_release": 0,
      "mean_days_between_releases": 1
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": true,
      "health_percentage": 100,
      "has_issue_template": false,
      "has_code_of_conduct": true,
      "has_pull_request_template": true
    },
    "ecosystem": {
      "packages": [
        {
          "name": "stapel-core",
          "exists": true,
          "license": "MIT",
          "keywords": [
            "django",
            "stapel",
            "auth",
            "jwt",
            "Development Status :: 4 - Beta",
            "Framework :: Django",
            "Framework :: Django :: 5.2",
            "Intended Audience :: Developers",
            "License :: OSI Approved :: MIT License",
            "Operating System :: OS Independent",
            "Programming Language :: Python :: 3 :: Only",
            "Programming Language :: Python :: 3.11",
            "Programming Language :: Python :: 3.12",
            "Programming Language :: Python :: 3.13",
            "Typing :: Typed"
          ],
          "ecosystem": "pypi",
          "matches_repo": true,
          "registry_url": "https://pypi.org/project/stapel-core/",
          "is_deprecated": false,
          "latest_version": "0.14.2",
          "repository_url": "https://github.com/usestapel/stapel-core",
          "versions_count": 20,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 6794,
          "first_published_at": "2026-07-04T00:44:36.034812Z",
          "latest_published_at": "2026-07-25T20:19:07.909336Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 0
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 1,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [
        "examples"
      ],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [
        "Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "py.typed"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 35295,
      "source_files_sampled": 347,
      "oversized_source_files": 0,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "pyproject.toml"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "pypi"
      ],
      "dependencies": [
        {
          "name": "Django",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=5.1"
        },
        {
          "name": "djangorestframework",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3.14"
        },
        {
          "name": "djangorestframework-dataclasses",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.2"
        },
        {
          "name": "drf-spectacular",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.27"
        },
        {
          "name": "django-cors-headers",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=4.3"
        },
        {
          "name": "django-redis",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=5.4"
        },
        {
          "name": "PyJWT",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.8"
        },
        {
          "name": "cryptography",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=41.0"
        },
        {
          "name": "requests",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.31"
        },
        {
          "name": "phonenumbers",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=8.13"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "Pasenchuk",
          "commits": 44,
          "avatar_url": "https://avatars.githubusercontent.com/u/7511219?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "publish.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [
        "ruff.toml"
      ],
      "has_editorconfig": false,
      "has_linter_config": true,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 0,
            "reason": "project was created within the last 90 days. Please review its contents carefully",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 0,
            "reason": "dependency not pinned by hash detected -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 10,
            "reason": "0 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "0195be77e753248dbac9381fa4382cada4814459",
        "ran_at": "2026-07-25T20:23:38Z",
        "aggregate_score": 3.9,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": true,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-25T20:19:11Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/usestapel/stapel-core",
    "host": "github.com",
    "name": "stapel-core",
    "owner": "usestapel"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 54,
      "inputs": {
        "security": 39,
        "vitality": 70,
        "community": 49,
        "governance": 34,
        "engineering": 78
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 70,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 58,
            "inputs": {
              "commits_last_year": 109,
              "human_commit_share": 1,
              "days_since_last_push": 0,
              "active_weeks_last_year": 5
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "5/52 weeks with commits",
                "points": 3.5,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "109 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 109
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 88,
            "inputs": {
              "releases_count": 24,
              "latest_release_tag": "v0.14.2",
              "releases_from_tags": true,
              "days_since_latest_release": 0,
              "mean_days_between_releases": 1
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "24 version tags (no GitHub releases)",
                "points": 16.2,
                "status": "partial",
                "details": [
                  {
                    "code": "version_tags_no_releases",
                    "params": {
                      "count": 24
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 0 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~1 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 1
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "unverified",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": "repository_too_young",
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": null,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "maintenance record not established from the collected data",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_unverified",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "at_risk",
        "name": "Community & Adoption",
        "value": 49,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 1,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "1 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "excellent",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 92,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": true,
              "has_issue_template": false,
              "has_code_of_conduct": true,
              "has_pull_request_template": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (MIT)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "MIT"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 18,
                "status": "met",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 13.5,
                "status": "met",
                "details": [],
                "max_points": 13.5
              },
              {
                "key": "issue_template",
                "name": "Issue template",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.2
              },
              {
                "key": "pr_template",
                "name": "PR template",
                "detail": null,
                "points": 6.3,
                "status": "met",
                "details": [],
                "max_points": 6.3
              }
            ]
          },
          {
            "key": "ecosystem_adoption",
            "band": "moderate",
            "name": "Ecosystem adoption (downloads)",
            "note": "Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "registry_dependents"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 64,
            "inputs": {
              "packages": [
                "stapel-core"
              ],
              "dependents": null,
              "ecosystems": "pypi",
              "total_downloads": null,
              "monthly_downloads": 6794
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "6,794 downloads/month across pypi",
                "points": 51.1,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 6794,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 80
              },
              {
                "key": "registry_dependents",
                "name": "Registry dependents",
                "detail": "not reported by this ecosystem",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_reported_by_this_ecosystem",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 34,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 43,
            "inputs": {
              "followers": 1,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "usestapel",
              "public_repos": 31,
              "account_age_days": 31
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "1 followers of usestapel",
                "points": 2.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 1,
                      "login": "usestapel"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "31 public repos, account ~0 yr old",
                "points": 11.1,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 31
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 0
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "stapel-core"
              ],
              "ecosystems": "pypi",
              "any_deprecated": false,
              "min_days_since_publish": 0
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on pypi",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "pypi"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 0 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 0
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "20 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 78,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "good",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 80,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "ruff.toml",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "ruff.toml"
                    }
                  }
                ],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "topics": [],
              "has_wiki": true,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": true,
              "has_description": true
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 30,
                "status": "met",
                "details": [],
                "max_points": 30
              },
              {
                "key": "documentation_directory",
                "name": "Documentation directory",
                "detail": null,
                "points": 25,
                "status": "met",
                "details": [],
                "max_points": 25
              },
              {
                "key": "documentation_homepage_site",
                "name": "Documentation / homepage site",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "repository_description",
                "name": "Repository description",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "topics",
                "name": "Topics",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 39,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 39,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.9
            },
            "components": [
              {
                "key": "binary_artifacts",
                "name": "Binary-Artifacts",
                "detail": "no binaries found in the repo",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "branch_protection",
                "name": "Branch-Protection",
                "detail": "branch protection not enabled on development/release branches",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "ci_tests",
                "name": "CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "0 existing vulnerabilities detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 59,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.98,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "98 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 98,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [],
              "has_dockerfile": false,
              "typed_language": false,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "py.typed"
              ],
              "agent_commit_share": 0.92,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": "ruff.toml",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "ruff.toml"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "py.typed",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "py.typed"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "92 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 92,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "good",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 82,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 35295,
              "source_files_sampled": 347,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (py.typed)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "py.typed",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/347 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 347,
                      "oversized": 0
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          },
          {
            "key": "ai_interfaces",
            "band": "at_risk",
            "name": "Machine-readable interfaces",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "example_dirs": [
                "examples"
              ],
              "has_mcp_signal": false,
              "api_schema_files": []
            },
            "components": [
              {
                "key": "api_schema_openapi_graphql_proto",
                "name": "API schema (OpenAPI/GraphQL/proto)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 40
              },
              {
                "key": "mcp_server",
                "name": "MCP server",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "runnable_examples",
                "name": "Runnable examples",
                "detail": "examples",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "examples"
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          }
        ],
        "description": "How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score."
      }
    ],
    "metrics_version": "1.13.0"
  },
  "warnings": [
    "Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token",
    "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
    "deps.dev does not index pypi:stapel-core@0.14.2; advisories assessed against the repository dependency graph instead"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-25T20:23:43.169712Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/u/usestapel/stapel-core.svg",
  "full_name": "usestapel/stapel-core",
  "license_state": "standard",
  "license_spdx": "MIT"
}

Las puntuaciones son señales, no garantías. Reflejan prácticas públicamente visibles en GitHub; no son una auditoría de código ni una garantía de seguridad.

Los datos ausentes se excluyen y los pesos se renormalizan; nunca se puntúan como cero. La metodología es versionada y abierta: métricas v1.13.0, esquema v0.27.0 — metodología completa · wiki de métricas.

Cómo se sitúa un resultado dentro del registro general: estadísticas agregadasPyPI.