Public record
Software health reportschema 0.27.0 · metrics 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 star⑂ 0 forkssince Jun 2026View on GitHub ↗

usestapel/stapel-core holds a health index of 54 out of 100, placing it in the Moderate band. It scores highest on Engineering Quality (78/100) and lowest on Sustainability & Governance (34/100). It was last updated today. A single contributor accounts for most of its recent work.

54
overall / 100
Moderate

Software health index

Metrics are grouped into weighted categories on one standardized 1–100 scale. Overall starts as their weighted mean; when public evidence triggers the High-Risk Jurisdiction Policy, the rating is adjusted and receives an At risk ceiling of 49. AI Readiness sits outside the overall score.

54
Excellent85-100Exemplary; meets essentially all checked criteria
Good70-84Healthy; minor gaps
Moderate50-69Acceptable with notable gaps; review recommended
At risk30-49Significant weaknesses; adoption warrants caution
Critical1-29Severe problems (abandoned, single-maintainer, no hygiene)
VitalityCommunity &AdoptionSustainability &GovernanceEngineeringQualitySecurityAI Readiness

Score profile

Each axis is a category. The shape matters more than the average — a healthy subject fills the whole shape, while a spike-and-crater profile means strength in one dimension is masking risk in another.

Ownership

usestapelOrganization
1 follower31 public repossince Jun 2026

This repository is backed by an organization — shared, accountable stewardship that can outlive any single maintainer.

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publishTags
PyPIstapel-core0.14.26,794200 days agodjangostapelauthjwt

Metrics by category

Vitality

Is the project alive — is code being written and are releases shipping?

70Good · 22% of overall
How it's scored
36/36Push recency — last push 0 days ago
3.5/36Commit cadence — 5/52 weeks with commits
18/18Commit volume — 109 commits in the last year
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Inputs used
commits_last_year109
human_commit_share1
days_since_last_push0
active_weeks_last_year5
How it's scored
16.2/27Ships releases — 24 version tags (no GitHub releases)
36/36Release recency — latest release 0 days ago
27/27Release cadence — a release every ~1 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count24
latest_release_tagv0.14.2
releases_from_tagsyes
days_since_latest_release0
mean_days_between_releases1
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.

Community & Adoption

Does the project have users, downloads, attention, and a welcoming setup for contributors?

49At risk · 18% of overall
How it's scored
0/60Stars — 1 stars
0/25Forks — 0 forks
0/15Watchers — 0 watchers
Inputs used
forks0
stars1
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history

Community health

92Excellent
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (MIT)
18/18CONTRIBUTING guide
13.5/13.5Code of conduct
0/7.2Issue template
6.3/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingyes
has_issue_templateno
has_code_of_conductyes
has_pull_request_templateyes
How it's scored
51.1/80Monthly downloads — 6,794 downloads/month across pypi
0/20Registry dependents — not reported by this ecosystem
Inputs used
packagesstapel-core
dependents
ecosystemspypi
total_downloads
monthly_downloads6,794
Excluded from scoring (no data or not applicable): Registry dependents. Remaining weights renormalized.

Sustainability & Governance

Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?

34At risk · 24% of overall
How it's scored
9/54Bus factor — 1 contributor(s) cover half of all commits
0/22.5Commit distribution — top contributor authored 100% of commits
1.4/13.5Contributor breadth — 1 contributors
0/10OpenSSF Scorecard: Contributors — project has 0 contributing companies or organizations -- score normalized to 0
Inputs used
bus_factor1
contributors_sampled1
top_contributor_share1
How it's scored
0/46.8Issue resolution — no issues or no data
0/38.3PR acceptance — no decided pull requests or no data
0/15OpenSSF Scorecard: Code-Review — Found 0/30 approved changesets -- score normalized to 0
Inputs used
merged_prs0
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs0
Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.
How it's scored
30/30Ownership backing — organization-owned
0/20Verified domain
2.2/25Owner reach — 1 followers of usestapel
11.1/25Track record — 31 public repos, account ~0 yr old
Inputs used
followers1
owner_typeOrganization
is_verified
owner_loginusestapel
public_repos31
account_age_days31
How it's scored
25/25Published & resolvable — 1 package(s) on pypi
35/35Publish recency — latest publish 0 days ago
20/20Version history — 20 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packagesstapel-core
ecosystemspypi
any_deprecatedno
min_days_since_publish0

Engineering Quality

Are baseline engineering and documentation practices in place?

78Good · 20% of overall
How it's scored
24/24CI workflows — 2 workflow(s)
24/24Tests present
16/16Linter config — ruff.toml
0/9.6Pre-commit hooks
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — no data
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configyes
has_precommit_configno
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.
How it's scored
30/30README
25/25Documentation directory
0/15Documentation / homepage site
10/10Repository description
0/10Topics
10/10Wiki
Inputs used
topics
has_wikiyes
homepage
has_readmeyes
has_docs_diryes
has_descriptionyes

Security

Are visible security and supply-chain practices strong, without unresolved high-risk jurisdiction exposure?

39At risk · 16% of overall
How it's scored
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 — no data
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.5License — 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 — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
7.5/7.5Vulnerabilities — 0 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3.9
Excluded from scoring (no data or not applicable): ci_tests, signed_releases. Remaining weights renormalized.

AI Readiness

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.

59Moderate · 0% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 98 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.98
agent_instruction_files
agent_instruction_max_bytes
How it's scored
18/18One-command bootstrap — Makefile
22/22Automated tests
11/11Lint / format config — ruff.toml
11/11Static type checking — py.typed
0/10Reproducible environment
10/10Demonstrated agent practice — 92 of the last 100 commits agent-authored or agent-credited
0/8Automated maintenance — no automated dependency updates observed
0/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 0
Inputs used
has_nixno
has_testsyes
lockfiles
has_dockerfileno
typed_languageno
bootstrap_filesMakefile
has_devcontainerno
has_linter_configyes
typecheck_configspy.typed
agent_commit_share0.92
toolchain_manifests
dependency_bot_commit_share0
How it's scored
27/45Type-checkable code — Python with type-check config (py.typed)
55/55Manageable file sizes — 0/347 source files over 60KB
Inputs used
primary_languagePython
largest_source_bytes35,295
source_files_sampled347
oversized_source_files0
How it's scored
0/40API schema (OpenAPI/GraphQL/proto)
0/20MCP server
40/40Runnable examples — examples
Inputs used
example_dirsexamples
has_mcp_signalno
api_schema_files

Key facts

1GitHub stars
1contributors
109commits, last 12 months
0days since last push
24releases
1bus factor
0open issues
PyPIpackage ecosystems

Data collection 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

More detail

OpenSSF Scorecard 3.9 / 10
3.9aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).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/aCI-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/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
10Vulnerabilities0 existing vulnerabilities detected
Direct dependencies 10
RegistryPackageVersion constraintManifest
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
All dependencies not collected

The resolved dependency set could not be collected for this report: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Raw JSON report machine-readable
{
  "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"
}

Scores are signals, not warranties. They reflect publicly visible practices on GitHub — not a code audit, and not a security guarantee.

Missing data is excluded and weights renormalized, never scored as zero. Methodology is versioned and open: metrics v1.13.0, schema v0.27.0 — full methodology · metrics wiki.

How one result sits in the wider record: aggregate statisticsPyPI.