Public record
Software health reportschema 0.26.0 · metrics 1.13.0 · 2026-07-22 15:11 UTC

lambdadevelopment / lambda-erp

Open-source AI-native ERP. Accounting, inventory, analytics — all through chat.

Python · TypeScriptApache-2.0★ 20 stars⑂ 5 forkssince Apr 2026View on GitHub ↗

lambdadevelopment/lambda-erp holds a health index of 53 out of 100, placing it in the Moderate band. It scores highest on Vitality (83/100) and lowest on Sustainability & Governance (34/100). It was last updated 2 days ago. A single contributor accounts for most of its recent work.

53
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.

53
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

1 follower3 public repossince Jul 2024

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

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publish
PyPIlambda-erp0.3.44,886635 days ago
npm@lambda-development/erp-core0.3.46,160615 days ago

Metrics by category

Vitality

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

83Good · 22% of overall
How it's scored
36/36Push recency — last push 2 days ago
7.6/36Commit cadence — 11/52 weeks with commits
18/18Commit volume — 148 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year148
human_commit_share1
days_since_last_push2
active_weeks_last_year11

Release discipline

100Excellent
How it's scored
27/27Ships releases — 60 releases published
36/36Release recency — latest release 5 days ago
27/27Release cadence — a release every ~0.2 days
0/10OpenSSF Scorecard: Signed-Releases — no data
Inputs used
releases_count60
latest_release_tagv0.3.4
releases_from_tagsno
days_since_latest_release5
mean_days_between_releases0.2
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?

45At risk · 18% of overall
How it's scored
20.7/60Stars — 20 stars
5/25Forks — 5 forks
0/15Watchers — 0 watchers
Inputs used
forks5
stars20
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
22.5/22.5License — recognized license (Apache-2.0)
0/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
0/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno
How it's scored
53.9/80Monthly downloads — 11,046 downloads/month across npm, pypi
0/20Registry dependents — not reported by this ecosystem
Inputs used
packageslambda-erp, @lambda-development/erp-core
dependents
ecosystemsnpm, pypi
total_downloads
monthly_downloads11,046
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
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
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 lambdadevelopment
8.4/25Track record — 3 public repos, account ~2 yr old
Inputs used
followers1
owner_typeOrganization
is_verified
owner_loginlambdadevelopment
public_repos3
account_age_days742
How it's scored
25/25Published & resolvable — 2 package(s) on npm, pypi
35/35Publish recency — latest publish 5 days ago
20/20Version history — 63 published versions
20/20Not deprecated — active, not deprecated or yanked
Inputs used
packageslambda-erp, @lambda-development/erp-core
ecosystemsnpm, pypi
any_deprecatedno
min_days_since_publish5

Engineering Quality

Are baseline engineering and documentation practices in place?

66Moderate · 20% of overall
How it's scored
24/24CI workflows — 4 workflow(s)
24/24Tests present
0/16Linter config
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_configno
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?

36At 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.8/2.5Contributors — project has 1 contributing companies or organizations -- score normalized to 3
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
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
5/5Packaging — packaging workflow detected
0.5/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 1
0/5SAST — no SAST tool detected
0/5Security-Policy — security policy file not detected
0/7.5Signed-Releases — no data
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
0/7.5Vulnerabilities — 11 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate3.6
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.

70Good · 0% of overall
How it's scored
45/45Agent instructions — CLAUDE.md
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 92 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.92
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes1,598
How it's scored
0/18One-command bootstrap
22/22Automated tests
0/11Lint / format config
11/11Static type checking — frontend/tsconfig.json
10/10Reproducible environment — Dockerfile, lockfile
10/10Demonstrated agent practice — 78 of the last 100 commits agent-authored or agent-credited
0/8Automated maintenance — no automated dependency updates observed
1/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 1
Inputs used
has_nixno
has_testsyes
lockfilespackage-lock.json
has_dockerfileyes
typed_languageno
bootstrap_files
has_devcontainerno
has_linter_configno
typecheck_configsfrontend/tsconfig.json
agent_commit_share0.78
toolchain_manifests
dependency_bot_commit_share0
How it's scored
27/45Type-checkable code — Python with type-check config (frontend/tsconfig.json)
53.9/55Manageable file sizes — 3/145 source files over 60KB
Inputs used
primary_languagePython
largest_source_bytes181,453
source_files_sampled145
oversized_source_files3

Key facts

20GitHub stars
1contributors
148commits, last 12 months
2days since last push
60releases
1bus factor
0open issues
npm, 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:lambda-erp@0.3.4; advisories assessed against the repository dependency graph instead

More detail

Star and fork history 0 ★ / 5 ⇿
0Stars
5Forks
37Releases

When each star and fork was added, collected from GitHub and bucketed by day. Cumulative growth sits directly above the daily additions it is made of, so the two read against each other: steady organic accretion looks nothing like an abrupt, short-lived burst. Where that difference is measurable, it is reported as growth authenticity.

12345522026-052026-062026-07
Major 0Minor 0Patch 37
OpenSSF Scorecard 3.6 / 10
3.6aggregate

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-22 15:11 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
3Contributorsproject has 1 contributing companies or organizations -- score normalized to 3
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
10Packagingpackaging workflow detected
1Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 1
0SASTno SAST tool detected
0Security-Policysecurity policy file not detected
n/aSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
0Vulnerabilities11 existing vulnerabilities detected
Direct dependencies 16
RegistryPackageVersion constraintManifest
npm@fontsource/inter^5.2.8frontend/package.json
PyPIfastapi>=0.115pyproject.toml
PyPIuvicorn>=0.30pyproject.toml
PyPIpython-multipart>=0.0.9pyproject.toml
PyPIpydantic>=2.0pyproject.toml
PyPIanthropic>=0.40pyproject.toml
PyPIopenai>=1.0pyproject.toml
PyPIhttpx>=0.27pyproject.toml
PyPIpython-dotenv>=1.0pyproject.toml
PyPIpython-dateutil>=2.8pyproject.toml
PyPIpython-jose>=3.3pyproject.toml
PyPIbcrypt>=5pyproject.toml
PyPIJinja2>=3.1pyproject.toml
PyPIweasyprint>=62.0pyproject.toml
PyPIpypdf>=4.0pyproject.toml
PyPIholidays>=0.40pyproject.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": 1886,
      "has_wiki": true,
      "homepage": null,
      "languages": {
        "CSS": 4229,
        "HCL": 22484,
        "HTML": 14447,
        "Python": 1085771,
        "Dockerfile": 2566,
        "JavaScript": 81,
        "TypeScript": 507245
      },
      "pushed_at": "2026-07-20T11:07:41Z",
      "created_at": "2026-04-23T13:19:56Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-21T15:23:27Z",
      "description": "Open-source AI-native ERP. Accounting, inventory, analytics — all through chat.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "Apache-2.0",
      "default_branch": "master",
      "license_spdx_raw": "Apache-2.0",
      "primary_language": "Python",
      "significant_languages": [
        "Python",
        "TypeScript"
      ]
    },
    "owner": {
      "blog": "https://lambda.dev",
      "name": "Lambda Automation Development",
      "type": "Organization",
      "login": "lambdadevelopment",
      "company": null,
      "location": null,
      "followers": 1,
      "avatar_url": "https://avatars.githubusercontent.com/u/175140732?v=4",
      "created_at": "2024-07-09T15:52:35Z",
      "is_verified": null,
      "public_repos": 3,
      "account_age_days": 742
    },
    "license": {
      "state": "standard",
      "spdx_id": "Apache-2.0",
      "raw_spdx": "Apache-2.0",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.3.4",
          "kind": "patch",
          "published_at": "2026-07-17T13:24:50Z"
        },
        {
          "tag": "v0.3.3",
          "kind": "patch",
          "published_at": "2026-07-17T12:15:01Z"
        },
        {
          "tag": "v0.3.2",
          "kind": "patch",
          "published_at": "2026-07-17T11:58:37Z"
        },
        {
          "tag": "v0.3.1",
          "kind": "patch",
          "published_at": "2026-07-17T11:03:16Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-07-16T10:28:05Z"
        },
        {
          "tag": "v0.2.6",
          "kind": "patch",
          "published_at": "2026-07-16T09:14:02Z"
        },
        {
          "tag": "v0.2.5",
          "kind": "patch",
          "published_at": "2026-07-15T20:47:57Z"
        },
        {
          "tag": "v0.2.4",
          "kind": "patch",
          "published_at": "2026-07-15T20:32:15Z"
        },
        {
          "tag": "v0.2.3",
          "kind": "patch",
          "published_at": "2026-07-15T18:05:05Z"
        },
        {
          "tag": "v0.2.2",
          "kind": "patch",
          "published_at": "2026-07-15T17:09:27Z"
        },
        {
          "tag": "v0.2.1",
          "kind": "patch",
          "published_at": "2026-07-15T16:35:53Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-07-15T16:11:56Z"
        },
        {
          "tag": "v0.1.49",
          "kind": "patch",
          "published_at": "2026-07-14T17:32:00Z"
        },
        {
          "tag": "v0.1.48",
          "kind": "patch",
          "published_at": "2026-07-14T17:29:29Z"
        },
        {
          "tag": "v0.1.47",
          "kind": "patch",
          "published_at": "2026-07-14T15:42:57Z"
        },
        {
          "tag": "v0.1.46",
          "kind": "patch",
          "published_at": "2026-07-14T15:37:20Z"
        },
        {
          "tag": "v0.1.45",
          "kind": "patch",
          "published_at": "2026-07-14T14:30:59Z"
        },
        {
          "tag": "v0.1.44",
          "kind": "patch",
          "published_at": "2026-07-14T13:41:19Z"
        },
        {
          "tag": "v0.1.43",
          "kind": "patch",
          "published_at": "2026-07-14T10:23:05Z"
        },
        {
          "tag": "v0.1.42",
          "kind": "patch",
          "published_at": "2026-07-14T10:00:23Z"
        },
        {
          "tag": "v0.1.41",
          "kind": "patch",
          "published_at": "2026-07-14T09:56:52Z"
        },
        {
          "tag": "v0.1.40",
          "kind": "patch",
          "published_at": "2026-07-14T09:53:40Z"
        },
        {
          "tag": "v0.1.39",
          "kind": "patch",
          "published_at": "2026-07-14T09:32:02Z"
        },
        {
          "tag": "v0.1.38",
          "kind": "patch",
          "published_at": "2026-07-13T09:20:19Z"
        },
        {
          "tag": "v0.1.37",
          "kind": "patch",
          "published_at": "2026-07-10T20:45:43Z"
        },
        {
          "tag": "v0.1.36",
          "kind": "patch",
          "published_at": "2026-07-10T17:14:53Z"
        },
        {
          "tag": "v0.1.35",
          "kind": "patch",
          "published_at": "2026-07-01T17:58:22Z"
        },
        {
          "tag": "v0.1.34",
          "kind": "patch",
          "published_at": "2026-07-01T16:48:45Z"
        },
        {
          "tag": "v0.1.33",
          "kind": "patch",
          "published_at": "2026-07-01T16:21:11Z"
        },
        {
          "tag": "v0.1.32",
          "kind": "patch",
          "published_at": "2026-07-01T16:10:51Z"
        },
        {
          "tag": "v0.1.31",
          "kind": "patch",
          "published_at": "2026-06-25T13:37:33Z"
        },
        {
          "tag": "v0.1.30",
          "kind": "patch",
          "published_at": "2026-06-24T15:36:45Z"
        },
        {
          "tag": "v0.1.29",
          "kind": "patch",
          "published_at": "2026-06-24T15:13:16Z"
        },
        {
          "tag": "v0.1.28",
          "kind": "patch",
          "published_at": "2026-06-24T14:15:22Z"
        },
        {
          "tag": "v0.1.27",
          "kind": "patch",
          "published_at": "2026-06-24T09:34:51Z"
        },
        {
          "tag": "v0.1.26",
          "kind": "patch",
          "published_at": "2026-06-24T08:58:01Z"
        },
        {
          "tag": "v0.1.25",
          "kind": "patch",
          "published_at": "2026-06-23T21:31:19Z"
        },
        {
          "tag": "v0.1.24",
          "kind": "patch",
          "published_at": "2026-06-22T07:47:31Z"
        },
        {
          "tag": "v0.1.23",
          "kind": "patch",
          "published_at": "2026-06-20T17:25:02Z"
        },
        {
          "tag": "v0.1.22",
          "kind": "patch",
          "published_at": "2026-06-20T17:12:51Z"
        },
        {
          "tag": "v0.1.21",
          "kind": "patch",
          "published_at": "2026-06-20T16:19:16Z"
        },
        {
          "tag": "v0.1.20",
          "kind": "patch",
          "published_at": "2026-06-19T11:49:03Z"
        },
        {
          "tag": "v0.1.19",
          "kind": "patch",
          "published_at": "2026-06-17T17:55:00Z"
        },
        {
          "tag": "v0.1.18",
          "kind": "patch",
          "published_at": "2026-06-05T11:10:33Z"
        },
        {
          "tag": "v0.1.17",
          "kind": "patch",
          "published_at": "2026-06-05T08:51:23Z"
        },
        {
          "tag": "v0.1.16",
          "kind": "patch",
          "published_at": "2026-06-05T08:33:38Z"
        },
        {
          "tag": "v0.1.15",
          "kind": "patch",
          "published_at": "2026-06-04T22:41:22Z"
        },
        {
          "tag": "v0.1.14",
          "kind": "patch",
          "published_at": "2026-06-04T18:09:14Z"
        },
        {
          "tag": "v0.1.13",
          "kind": "patch",
          "published_at": "2026-06-04T17:02:12Z"
        },
        {
          "tag": "v0.1.12",
          "kind": "patch",
          "published_at": "2026-06-04T13:13:10Z"
        },
        {
          "tag": "v0.1.11",
          "kind": "patch",
          "published_at": "2026-06-04T12:34:40Z"
        },
        {
          "tag": "v0.1.10",
          "kind": "patch",
          "published_at": "2026-06-04T09:45:15Z"
        },
        {
          "tag": "v0.1.9",
          "kind": "patch",
          "published_at": "2026-06-04T07:57:20Z"
        },
        {
          "tag": "v0.1.8",
          "kind": "patch",
          "published_at": "2026-06-03T22:31:14Z"
        },
        {
          "tag": "v0.1.7",
          "kind": "patch",
          "published_at": "2026-06-03T17:51:03Z"
        },
        {
          "tag": "v0.1.6",
          "kind": "patch",
          "published_at": "2026-06-03T17:33:47Z"
        },
        {
          "tag": "v0.1.5",
          "kind": "patch",
          "published_at": "2026-06-03T16:33:13Z"
        },
        {
          "tag": "v0.1.4",
          "kind": "patch",
          "published_at": "2026-06-03T15:22:18Z"
        },
        {
          "tag": "v0.1.3",
          "kind": "patch",
          "published_at": "2026-06-03T14:47:29Z"
        },
        {
          "tag": "v0.1.2",
          "kind": "patch",
          "published_at": "2026-05-26T21:48:10Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "39068a6e52f556509734450d296da9a3e65cc4a2",
          "body": null,
          "is_bot": false,
          "headline": "Update LICENSE",
          "author_name": "Jonathan C. Frei",
          "author_login": "jcfrei",
          "committed_at": "2026-07-20T11:07:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "b7b8c193fac0c45a886a7de62d17ed76ed49fbe3",
          "body": "Deactivating the chat API in Settings now shows a confirmation dialog (enabling\nstays instant) that makes clear the API keys are preserved — the flag only gates\nthe API surface; keys keep working when it's re-enabled. Frontend-only\n(settings.tsx + en/de/fr).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.3.4: confirm dialog before deactivating the chat API",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-17T13:23:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "350101b033e3bf95085fa5678149521ebd1a7d38",
          "body": "…ds from chat\n\nsearch_masters hides disabled records by default (good — don't book to retired\naccounts), but they were unreachable: no way to find a disabled account to\nre-enable it (catch-22) or answer \"what's disabled\". Adds an optional\ninclude_disabled flag (default false) applied to the no-query\n[…]\nuzzy paths, plus a prompt nudge to retry with it when an expected record isn't\nfound. Mirrors the existing list_masters include_disabled param.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.3.3: search_masters include_disabled — reach disabled recor…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-17T12:14:04Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8d90394b3bbb0ca018468bb43fe35c8a8b043796",
          "body": "…ol flag updates; anti-fabrication prompt\n\n- JournalEntry._normalize_amounts back-fills base debit/credit <-> *_in_account_currency\n  so a JE created with amounts only in the account-currency fields (the chat path)\n  actually posts; on_submit rejects a JE that would post no non-zero GL lines.\n- _nor\n[…]\nmpt: confirm actions from the tool result before claiming success;\n  no master rename exists; verify a field exists before saying it's missing.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.3.2: journal entries post from account-currency amounts; bo…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-17T11:57:30Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "407f4b89aaccdb0640b9f76aa561e8a308ab6353",
          "body": "search_masters no longer hard-caps at 20 results: the chat can pass an optional\n`limit` (omit for no cap), so listing accounts returns the whole chart of\naccounts instead of a silently-truncated 20. Applies to the no-query listing,\nsubstring match, and fuzzy fallback. Backend-only; frontend version bumped to\nkeep the shared version in lockstep. Rebased onto 0.3.0 (Apache relicense).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.3.1: uncap master search (LLM-set limit; omit = all)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-17T11:02:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "aec917afd71ed995eed7ac639d673eaa0fc25dde",
          "body": "First Apache-2.0-licensed release of both packages (lambda-erp on PyPI,\n@lambda-development/erp-core on npm). Adds frontend/LICENSE so the npm\ntarball ships a license file. Releases through 0.2.6 remain MIT.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.3.0: relicense from MIT to Apache License 2.0",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-07-16T10:27:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "20aee90659269f6e3f589775ed0256f68e589c97",
          "body": "index.html was returned with no Cache-Control, so browsers heuristically\ncached the shell and a normal reload could load an old shell pointing at\nprevious-build asset URLs — requiring a hard-refresh. Serve it no-cache so\nthe shell always revalidates (304 when unchanged). Also serve the\ncontent-hashe\n[…]\n a 1y max-age so reloads skip asset\nrevalidation entirely — net fewer requests, not more. Bump 0.2.5 -> 0.2.6\n(both packages version together).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(web): stop serving a stale SPA shell on soft refresh",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-16T09:12:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "10fc84adf7c38a0b9aa1d0c2273d06768de804af",
          "body": "0.2.4 fixed /account/tree, but the CoA page the GUI renders is the\n/reports/chart-of-accounts report, which still listed disabled accounts\n(shown as \"disabled\"). It now hides them by default too, with the same\nprune rule (balances still roll up over disabled descendants; a disabled\ngroup with an enabled descendant is kept). include_disabled=true shows them.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.5: hide disabled accounts in the Chart of Accounts report",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T20:47:04Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3a316b6fa666f69a2a74aaf524d001cf0a9643c6",
          "body": "/account/tree now omits disabled accounts by default (they already drop out\nof account pickers), so a retired account leaves the CoA view instead of\nlingering as clutter. A disabled group is still shown when it has an enabled\ndescendant, so an active account is never hidden behind a disabled parent.\ninclude_disabled=true shows them; each node now carries a disabled flag.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.4: hide disabled accounts in Chart of Accounts tree",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T20:31:10Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cb50615c1a9ca61f992fff94091cd1c1b453cb66",
          "body": "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.3: German DATEV SKR03/SKR04 localization packs",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T18:04:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9f39496b14ff5061e23b03607f2766792cfb6577",
          "body": "First use of the country[.variant] pack axis: two Germany charts registered\nunder country=\"de\" — de.skr03 (process-ordered, the most common German SME\nchart, and the default for a bare \"de\") and de.skr04 (balance-sheet-ordered,\nDATEV's recommendation for new companies). Each is a curated ~70-account\n[…]\nUpdates the chat tool descriptions, CHANGELOG [Unreleased], README, and the\ncontributor doc (de_skr03/04 now the multi-variant worked example).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(setup): add German DATEV SKR03/SKR04 localization packs",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T17:47:11Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "90687f45c891e197efa1233f1f431d3aa8230f6f",
          "body": "…at bold-heading fix\n\nCompany setup now converges toward the desired chart instead of refusing when\nthe company already exists:\n- Accounts: create only what's missing (skip existing by name, no PK failure);\n  parents-then-children so a partial chart completes.\n- Defaults: fill only empty company def\n[…]\ntead of being mangled into a `*` bullet.\n\nCloses the guided-setup dead-end seen when a company pre-existed with stray\naccounts (chat 2527ebc9).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.2: idempotent company setup (converge, don't refuse) + ch…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T17:08:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2ae9ecd2c1f6d50879c18fc92b50a894e52087cd",
          "body": "The markdown list detector matched any line starting with `*`, so a bold\nheading like `**Grundlage**` had its first star stripped and emitted as a `•`\nbullet, leaving `*Grundlage**` unrendered. Require whitespace after the list\nmarker (`- `, `* `, `• `) so `**bold**` (star-star, no space) falls through to\nnormal inline rendering. Real bullets are unaffected.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(chat): render bold lines instead of mis-parsing them as bullets",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T16:45:26Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "63cf4bb753f1315b06b483f43c48a3e153dc55cf",
          "body": "… line breaks\n\n- Sector-profile overlay accounts are created in the localization pack's language\n  (LocalizationPack.language). On the Swiss (German) chart they now read in German\n  (Beratungserlöse, Aufwand für Fremdleistungen, …) via per-account i18n names;\n  generic stays English. Engine remaps p\n[…]\n line breaks in the user's own message bubble (whitespace-pre-wrap).\n- Tests cover German overlay names + the services default resolving on CH.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.1: localize sector accounts + fix services default + chat…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T16:31:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1ee06eb0870848895ebd5b84edd19dc193a0044f",
          "body": "Add lambda_erp/accounting/setup/ — a three-layer chart-of-accounts builder:\na universal account_type spine, pluggable localization packs keyed\ncountry[.variant] (registry + permanent generic fallback), and seven\njurisdiction-independent sector profiles that attach accounts to pack anchors,\nnever to \n[…]\nt_company_setup.py\n  (self-contained) + docs/agents/company_setup.md.\n- Bundles the held API-keys grouped-by-owner UI change into this release.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.2.0: guided sector-aware company setup + Swiss pack",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T16:09:00Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4d8baa56d05530b5d07c598351195fba9c2cf8fc",
          "body": "Make the admin \"sees everyone's keys\" behavior legible on the API keys page\ninstead of a flat, ambiguous list:\n\n- Backend: list_api_keys LEFT JOINs \"User\" so each key carries owner_full_name\n  + owner_email, plus an is_mine flag. Additive only — no fields removed, safe\n  for a mixed-version window. \n[…]\nm (Badge, semantic tokens, ring-bordered lists,\n  Button variants) instead of ad-hoc gray utility classes.\n\nNo version bump yet (release held).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(api-keys): group keys by owner + confirm dialog for others' keys",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-15T13:39:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "36c87d753606316273351b7848cf6febb0e77c56",
          "body": "0.1.48's per-user keys named the column `user`, which is a reserved word\nin PostgreSQL — schema setup died with 'syntax error at or near \"user\"'\n(SQLite happily accepted it; the dual-backend CI suite caught it before\nany deploy). Renamed to `owner` in the DDL, migration 19, and every\nquery; the seri\n[…]\nield stays `user` so the frontend and API\nconsumers are unchanged. 0.1.48 was published but never deployable on\nPostgres — no data implications.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.49: rename Api Key column user -> owner (PG reserved word)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T17:30:59Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b43929a6293a4eb936a5ed33d086801cff8fb471",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.1.48",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T17:28:28Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7a2c43c56a2ef74e982636c27cdca472acc35f74",
          "body": "v1 keys were org-global objects minted by admins with a free-standing\nrole and a synthetic identity (session_owner apikey:<id>) — whoever held\nthe token WAS the key, with no user attribution and, via a shared org\ntoken, interleaved chat sessions across people.\n\nv2 (clean break, decided 2026-07-14 — \n[…]\nd: same-user keys SHARE sessions (semantic flip), plus\n  cross-user isolation, cap enforcement, and the delete flow via a\n  second (viewer) user\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "auth: per-user API keys (self-service, live role cap, two-step delete)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T17:28:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "793d99792b285a2a766d3893d72917f782312f46",
          "body": "The 0.1.46 Master Data Deletion prompt section contained a literal\n{\"disabled\": 1} inside build_system_prompt's f-string — syntactically\nvalid (py_compile passes: \"disabled\" parses as an expression with \" 1\"\nas its format spec) but raising \"Space not allowed in string format\nspecifier\" on EVERY prom\n[…]\npush. Escaped to {{\"disabled\": 1}};\nverified by evaluating the extracted f-string (also proves no other\nunescaped braces exist in the template).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.47: fix chat prompt f-string (0.1.46 broke every chat turn)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T15:41:58Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "1655bfac3861bcf244462f3c7a1265ee8dc5affc",
          "body": "Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.1.46",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T15:36:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "780b58357ecf71304e728f2893b1b0be37eba95f",
          "body": "The master form pre-filled default_currency with a hardcoded \"USD\"\n(company form) or left it blank (customer/supplier) — so a hasty save\ngave a CHF shop USD customers. A dedicated effect now fills an untouched\ncurrency field with the company base currency once it loads, without\nwiping other fields t\n[…]\ner already typed. The backend create path\n(chat/API) already inherited the company currency for customer/supplier\n— this aligns the GUI with it.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(gui): new-master currency defaults to the company base currency",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T15:11:02Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fc3cb8352092a18fef026284eea7a5b8bbc87033",
          "body": "… USD)\n\nDoctypes without a per-document currency column (Stock Entry, ...) store\ntheir values in the company BASE currency, but three render paths fell\nback to a hardcoded \"USD\" when row/form data had no currency field —\nstamping $ on CHF numbers (display-only; stored values were correct):\n\n- docume\n[…]\n KPIs, table cells, and chart tooltips\n\nStandard report pages (trial balance, GL, aging, ...) already bound\nuseBaseCurrency and were unaffected.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(gui): stop stamping $ on base-currency values (Stock Entry showed…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T15:07:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fb33cf6fb4261311bff2243ad838c7360e6a91b7",
          "body": "The agent had no way to delete master records and, worse, justified the\ngap with an invented policy (\"records are retained for audit integrity\")\ngeneralized from the documents-lifecycle prompt rule — while the GUI's\nadmin delete route worked fine (live repro: CUST-007 refusal, manual GUI\ndeletion ne\n[…]\n now scopes \"no delete /\n  audit integrity\" explicitly to DOCUMENTS with a new Master Data\n  Deletion section describing the protected behavior.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chat: delete_master tool + scope the no-delete rule to documents",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T14:59:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ce9fa8a260e9cf4d70cbaf7b150c0135078f036f",
          "body": "The Account table always had full tree machinery (parent_account,\nis_group, root_type) but the GUI never exposed it — accounts were only\nvisible through Trial Balance / GL reports and the chat agent.\n\n- GET /reports/chart-of-accounts: every account (groups + zero-balance\n  included) with period bala\n[…]\nral Ledger pre-filtered to\n  account + period (the GL page is URL-backed, so drill-down is free).\n- i18n en/de/fr (Kontenplan / Plan comptable).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.45: Chart of Accounts page",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T14:29:51Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f9cc2e23ebfc077cbe9bb416c48d91218f98d0b4",
          "body": "…OM guidance\n\nStock UOM (and every other master-form select) silently rendered the\n\"Select...\" placeholder when the stored value wasn't in the hardcoded\noption list — which is every chat-created item (\"Stück\", \"Bottle\",\n\"Unit\"): the backend value existed but the GUI looked unset, inviting\nusers to o\n[…]\ns as users add units), nudging the agent\nto reuse exact spellings; create_master guidance points at it. A proper\nUOM master remains future work.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.44: show out-of-list select values in the GUI + dynamic U…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T13:40:16Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "4aa7aea161f5e9101f860b0848a775c69fab76dc",
          "body": "… versioning)\n\n- System prompt: sales-doc VAT contract — one \"On Net Total\" taxes[] row;\n  account_head OPTIONAL on drafts. The agent previously burned its step\n  budget hunting for a Tax Payable account that rate-only lines don't need\n  (live repro: 7/8 iterations, then refused; QTN-0004's working \n[…]\nw carries `modified` so callers can tell a\n  stale PDF from the current one (timestamped attachment names).\n- Hoist ORCHESTRATOR_MODEL constant.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.43: chat agent robustness (VAT guidance, cap wrap-up, doc…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T10:22:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f91139e53f300b99467c0e867e9bb5f3e2c94a4d",
          "body": "Complete the terra pricing entry with the 5.6 family's cache-write rates\n(3.125 / 6.25 per 1M, short/long context). gpt-5.4 verified against the\nofficial tiering table (272K threshold).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.42: gpt-5.6-terra cache-write pricing",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T09:59:32Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "81969b40db46ed77b3d61ca5b978fbd8d26895d7",
          "body": "…nput)\n\nOfficial model page: requests exceeding 272K input tokens incur 2x input\nand 1.5x output charges — same structure as gpt-5.4. 0.1.39's flat entry\nunder-counted large-context calls.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.41: gpt-5.6-terra pricing is tiered (2x/1.5x above 272K i…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T09:55:56Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d8fdc0e360afa630bef36f263c955a1c62253eca",
          "body": "0.1.39's terra switch broke every chat turn: gpt-5.6-terra 400s on\n/v1/chat/completions when function tools are present unless\nreasoning_effort=\"none\" — and the loop swallows LLM errors into an\non_event, so it surfaced as silent empty replies. Set the param;\nResponses-API migration (reasoning + tools) is follow-up.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release 0.1.40: fix gpt-5.6-terra tool calls (reasoning_effort=none)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T09:52:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "001ba4f4ab0aea786e3e2c9c87b833ff9c874833",
          "body": "Carries the chat orchestrator switch gpt-5.4 -> gpt-5.6-terra (5b66e5b).\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "release: 0.1.39",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T09:30:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5b66e5b21df4f9e94f20b3d27602204f4cc76db4",
          "body": "Register gpt-5.6-terra in OPENAI_PRICING (flat $2.50/$0.25/$15, no 272K tier\nstep-up) so cost tracking is accurate rather than falling back to gpt-5.4's\ntiered rate. Same price at normal context, stronger agentic tool-use.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chat: switch orchestrator model gpt-5.4 -> gpt-5.6-terra",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-14T09:03:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cae9f1addcabdcca099375bbefe790aab9948aa2",
          "body": "Point readers to lambda-erp-example next to the PyPI/npm badges — a minimal\ndeployment that consumes both packages via version pins, a template to start from.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(readme): link the reference deployment (lambda-erp-example)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-13T12:49:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5c2eee9869117ac1ece1f9f3eebec8d7131fe7c9",
          "body": "Remove the \"not yet production-ready … run your payroll\" disclaimer and soften\nthe remaining prototype language (tagline, storage row, Status section) to a\nconfident-but-honest tone. No functional change.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(readme): drop the not-production-ready framing",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-13T10:14:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "1f757918b713186752a9377c4cd480df85e8ebfb",
          "body": null,
          "is_bot": false,
          "headline": "Release v0.1.38",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-13T09:17:05Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "cb069b2d0a8dc63907807ff39ee4ae70c0c5570c",
          "body": "The chat agent writes for its own web UI, so API-channel replies contained\nERP-relative links (/app, /masters, /api/documents) that are dead outside the\nERP. Fix in two layers:\n\n- run_session_turn takes a `channel` (\"web\" | \"api\"); the API surface passes\n  \"api\", switching the system prompt's link g\n[…]\ne-parsing prose.\n\nTests assert the api-channel prompt is applied and the documents array is\npopulated from a referenced PDF link. Docs updated.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(chat-api): channel-aware replies + structured document refs",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-13T09:13:34Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "fcc138ad69fed038e95602c37d943b56e4ed0f74",
          "body": "Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Document read endpoints on the chat API + self-host Inter — 0.1.37",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-10T20:44:38Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cca56dd40e64d921ed04a210ddc79d151cc57cc7",
          "body": "Chat replies link to /api/documents/{slug}/{name}/pdf, but that route is\ncookie-gated (require_role) and unreachable by a Bearer API caller — so PDFs a\nreply references can't be opened downstream (the iOS app / websocket).\n\nMirror them on the programmatic surface:\n- GET /api/v1/documents/{doctype_sl\n[…]\nerits the chat_api_enabled flag + key role). Missing\ndoc -> 404, unknown doctype -> 422 via the existing global handlers. Tests +\ndocs updated.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(chat-api): Bearer-gated document PDF + JSON read endpoints",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-10T20:40:41Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "b1332d09be4c4dd53177de1d7b56b79c2e6911ac",
          "body": "The package's Tailwind preset sets fontFamily.sans to [\"Inter\", ...], but Inter\nwas only loaded by the demo's Google Fonts <link> in index.html — which is not\npart of the published @lambda-development/erp-core package. Consumers (e.g.\nlambda-erp-internal) therefore asked for \"Inter\" but loaded nothi\n[…]\ntop of src/index.css, which ships via exports[\"./styles.css\"]. Every\nconsumer of the stylesheet now gets Inter self-hosted, no <link> required.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "fix(frontend): self-host Inter so consumers inherit the font",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-10T18:04:02Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "410c35c2f36abc6492b011977af2ecb2022c58f8",
          "body": "npm@latest shipped 12.0.0, whose global self-upgrade left 'sigstore'\nunresolved and crashed provenance generation, failing the 0.1.36 npm\npublish (PyPI succeeded). Pin npm@11 and make the PyPI step skip-existing\nso re-pointing the v0.1.36 tag completes the release without choking on\nthe already-published PyPI version.",
          "is_bot": false,
          "headline": "ci(release): pin npm to 11.x + skip-existing on PyPI",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-10T17:11:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "84ac63b8199c78f06956d4559eaf4f842b9051a5",
          "body": "Add a synchronous REST surface over the chat agent so an external application can\nconverse with an ERP instance (the basis for driving it from Lambda's own infra\nand, later, a mobile app). Off by default via the `chat_api_enabled` Settings\nflag; an admin enables it and issues Bearer API keys.\n\n- New\n[…]\ni18n en/de/fr.\n- Tests: tests/test_chat_api.py (gating/auth/keys/sessions/stateless-vs-replay),\n  wired into CI on SQLite + Postgres.\n- Docs: docs/chat-api.md (usage) + docs/chat-api-plan.md (design).",
          "is_bot": false,
          "headline": "Programmatic chat API — opt-in Bearer-key chat endpoint — 0.1.36",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-07-10T17:03:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "66ad0bc1cb905c022693166292ff54c69ddbe3fe",
          "body": "A user created via Google/Apple has no password. Settings now shows a \"Set a\nPassword\" card (no current-password field) so they can add an email+password\nfallback if they lose provider access.\n\n- New POST /auth/set-password (authenticated; refuses if a real password already\n  exists — use change-pas\n[…]\nnge Password so password users are\n  unaffected. Auth context gains refreshUser() to flip the card after setting.\n- i18n en/de/fr.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Set a password for social-login-only accounts — 0.1.35",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-07-01T17:57:25Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "00b7f18d621e3f71de5c7745cc908939320c5c5f",
          "body": "When registration is closed (not first-run, public signup off), the login page\nno longer shows the \"Register\" link that led to a dead-end signup form. Invite-\nonly instances now show sign-in only (password + configured social providers);\ninvited users still register via their invite link, completable with a password\nor by continuing with Google / Apple.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "No signup screen on invite-only instances — 0.1.34",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-07-01T16:47:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2f0ee9266b1a4c839a4d960e6dfa4fc0e946f9c4",
          "body": "The \"User OAuth Identity\" table added in 0.1.32 had a column named `user`,\nwhich is a reserved word in PostgreSQL, so CREATE TABLE failed at startup on\nPostgres deployments (SQLite accepts it, so it passed local testing). Rename\nthe column to `user_name` and update all references. Verified against the\nPostgreSQL validation suite (books balance) and the OAuth flow on Postgres.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Fix social-login table crash on PostgreSQL (reserved word) — 0.1.33",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-07-01T16:20:03Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "dcc163d4ef703207d9ef98d3b71ef17e8a46b702",
          "body": "Add OAuth/OIDC social login layered on the existing password auth: once a\nprovider proves identity, the same lambda_erp_token JWT cookie is minted, so\neverything downstream is unchanged and password login keeps working.\n\n- api/oauth.py: /auth/{provider}/login + /auth/{provider}/callback for Google\n \n[…]\n (buttons hidden).\n- Also: read-only Notes / Terms now preserve line breaks (whitespace-pre-line).\n\nSee docs/social-login-plan.md.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Social login (Google & Apple) + read-only notes line-break fix — 0.1.32",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-07-01T16:09:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6bbbf3ccc306c1c769a53cbdb093e6d85543923c",
          "body": "A submitted (read-only) document showed the Notes / Terms text in a <p>, which\ncollapses newlines. Render textarea fields with whitespace-pre-line in the\nread-only path so multi-line notes keep their formatting, matching the editable\ntextarea and the PDF.\n\nNo version bump — rides the next release.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Preserve line breaks in read-only Notes / Terms",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-29T16:59:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d85d86be7c40da69e2b61b7b034650d31be5d5c1",
          "body": "… 0.1.31\n\n- `---` (3+ dashes) in the Notes / Terms markup renders a full-width thin\n  divider. Core emits a semantic `<div class=\"rm-hr\">`; templates style it\n  (light grey default; a branded template can match its own separators).\n  Documented in the chat system prompt and the formatting-help toolt\n[…]\nription -> posted invoices (Phase 0/1/2, scheduler via Container Apps\n  Job, manual + chat conversion). No code from the plan yet.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Notes markup: horizontal rule (---), + recurring-subscriptions plan —…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-25T13:36:33Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a98e5cec19c6037480563af18c9e84a84e22f29a",
          "body": "….1.30\n\n- Fix: the line-item search picker was clipped by the table's overflow-x-auto\n  scroller. It now renders in a body portal with fixed positioning and follows\n  the input on scroll/resize.\n- Docs: the chat system prompt now covers the quotation line `frequency`\n  field (and when to use it vs the cosmetic `>>` note), and states the\n  `>> left | amount` pipe rule explicitly.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Portal the link-field dropdown + document frequency/notes in chat — 0…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-24T15:35:48Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e0d7e9eb0ecdd9b28a6db58f6fbe4068a3be31d7",
          "body": "A quotation line carries a frequency: One-time (default) or a recurring\ncadence (Monthly/Quarterly/Half-Yearly/Yearly, matching the Subscription\nintervals). When an offer mixes one-time and recurring lines, the headline\ntotals AND tax rows reflect the one-time part only, and each recurring\ncadence i\n[…]\ncker on\nquotation line items, and a split_by_frequency helper that runs the shared\ntax engine per group (engine itself unchanged).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Billing frequency on quotation lines (recurring offers) — 0.1.29",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-24T15:12:18Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "542d8d1ec0996391bab6dfd0ac2c0fb9a2d82e19",
          "body": "The line-item table gains a Description column, so a quotation/order/invoice\nline can carry its own blurb (like a Proposal position). It defaults from\nthe Item master on save (blank falls back to the master, never wiping it);\na typed value overrides it for that document only. Backend already stored\nand defaulted the field — this exposes it in the form.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Editable per-line description on document line items — 0.1.28",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-24T14:14:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4f7151b1bbfc966eac66f8548eaf9bb1fb19ce71",
          "body": "The remarks field moves onto its own full-width row below the other\ndocument fields and is taller, with an info icon beside the label that\nexplains the supported markup (# heading, *italic*/**bold**, and the\nright-aligned `>> Period | Amount` price line) on hover or click.\nTranslated for en/de/fr.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Make the Notes / Terms field prominent + markup help — 0.1.27",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-24T09:33:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "124879fc8262c719331aee4fc04ad274956da91f",
          "body": "The remarks field now renders on the PDF from a small markup subset\n(# headings, *italic*/**bold**, and a right-aligned `>> Period | Amount`\nprice line that sits beside the description above it), so a closing block\nreads like a real offer. generate_pdf exposes it as safe `remarks_html`;\ntemplates re\n[…]\nses, so a\nbranded template can restyle the same markup. The chat assistant knows\nthe syntax and uses it for customer-facing notes.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Lightweight markup for document Notes / Terms — 0.1.26",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-24T08:57:06Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "da8b2a9c5cfeb9ce9b8eb8e0dc9aa09d31a76fc5",
          "body": "When unsure where a value belongs on a master, the assistant now consults\nget_master_fields before create_master/update_master (tool + system-prompt\nguidance; the ignored-fields warning points there too). create_master also\nspells out the customer contact-person mapping with an example. Fixes contact\npersons being dropped / contact phones misfiled into the company phone.\n\nNo version bump — rides the next release.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Chat: discover master fields instead of guessing",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-23T22:06:58Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7f1638f84338bd44a640271681cbf0993041f795",
          "body": "Master search: case-insensitive matching (fixes a Postgres-only bug where\nsearch returned nothing unless the exact stored casing was typed), searches all\ntext columns incl. address/city, fuzzy-matches misspellings, with an optional\n`fields` arg to target columns and a new get_master_fields tool.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Bump to lambda-erp 0.1.25",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-23T21:30:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5979ac8c1d16608eb7e5d88d993819ef90ac542b",
          "body": "…ix, addressing your three follow-ups.\n\n  1. Per-column targeting (fields)\n\n  search_masters now accepts an optional fields list. When provided it searches only those columns (validated against the\n  real schema; unknown names → clear error). It uses lower(CAST(col AS TEXT)) so even non-text columns\n[…]\nty + ERP validation suites pass locally on SQLite; the Postgres leg (where the original case bug lived) runs in\n  CI.\n\n  Still unpushed — say the word and I'll branch and open a PR against lambda-erp.",
          "is_bot": false,
          "headline": "All green on both suites. Here's what I added on top of the earlier f…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-23T21:10:20Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "eb3ad3a69f69f02cbfcbc7fb20ccd7ad7bd4819e",
          "body": "The custom Proposal page had no way to remove a proposal. Add a Discard button\n(existing proposals only, with a confirm) that soft-deletes via the standard\ndiscard_document path (status Discarded, hidden from the list).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add Discard action to the Proposal builder — 0.1.24",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-22T07:46:26Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bd232036f78cb87fc24822a0c78db3b609bab6cb",
          "body": "…— 0.1.23\n\nThe 0.1.22 Proposal guidance embedded a `{...}` JSON example in build_system_prompt's\nf-string, so the braces were treated as format fields and every chat message\nraised \"Invalid format specifier\". Rephrase the example as brace-free prose and\nadd the German doctype names (Sammelofferte/Sammelofferten) so the assistant\nmaps them to proposals.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Fix chat crash: literal braces in the Proposal system-prompt section …",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-20T17:24:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "42126b26cfe61943bf4c9900fd88a7c06bef461a",
          "body": "Add `proposal` to the LLM document tools (create/read/update/list) and\ndocument its shape in the system prompt: the quotations[] child table\nreferences existing quotations (not items), and it is print-only. Guard the\nProposal class against submit so an accidental submit raises a clear error.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Expose Proposal (Sammelofferte) to the chat assistant — 0.1.22",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-20T17:11:54Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f85da195b332033875029c5b0d851659f594d660",
          "body": "A print-only document that assembles several independent quotations into one\nbranded PDF: each rendered as a lettered position (A, B, C…) with an optional\nrecommendation badge, a cover letter pre-filled from a per-company template,\nand an optional uploaded appendix PDF stapled on the end (pypdf). No\n[…]\nthe nav. Tables Proposal / Proposal Item / Proposal Appendix\n(appendix as a blob); Company.proposal_cover_template (migration 17).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add Proposal (Sammelofferte) — combine quotations into one PDF — 0.1.21",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-20T16:18:15Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "3ff8f3fc0b041a435cc91c31007b4515291917bf",
          "body": "The Customer master gains contact_person / contact_email / contact_phone\n(a named contact at the customer, distinct from the company-level\nemail/phone). Surfaced in the master form with en/de/fr labels and in the\nchat create_master/update_master tools. Additive migration 16; existing\ncustomers keep the fields empty until edited.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add contact person (Ansprechperson) to Customer master — 0.1.20",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-19T11:47:55Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7950677ceba3084adf6d04d60ec906bc4efc5033",
          "body": "A microphone button in the chat composer records a spoken message,\ntranscribes it with OpenAI gpt-4o-transcribe, and drops the text into the\ninput for review before sending (it does not auto-send). Click to start,\nclick to stop; a too-short accidental tap is ignored without an API call.\n\n- api/chat.\n[…]\n_of_transcription().\n- frontend: mic button + MediaRecorder capture in the composer, transcribeAudio\n  on the chat context, and de/en/fr labels.\n\nCo-Authored-By: Claude Fable 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.19: voice-to-text in chat",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-17T17:52:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6c63ecd736e773a5c725c73286958203abeba48f",
          "body": "Added — Discard draft:\n- Document.discard() soft-deletes an unwanted DRAFT (discarded=1, status\n  'Discarded'): keeps the row for the audit trail, hides it from default lists,\n  no GL/stock impact. Drafts only; submitted docs must be cancelled. submit()\n  refuses a discarded draft (no \"posted but hi\n[…]\ninline confirmation\n(confirm button placed apart from the original) so a double-click can't fire an\nirreversible posting reversal.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.18: discard draft (void) + two-step cancel confirmation",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-05T11:09:24Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "a423566e54ba2c4a000b6cf6640174e6ad8c24d9",
          "body": "…tings UI\n\nFixed:\n- Change Password form now works with password managers: autocomplete\n  current-password / new-password (on both new + confirm so managers fill the\n  generated password into both), unique id/name per field, and a hidden\n  username anchor (autocomplete=username, display:none) so 1Pa\n[…]\nnding-invite \"Copy link\" / \"Revoke\" are now proper Buttons\n  (secondary / danger) instead of text links, matching \"Create Invite\".\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.17: password-manager-friendly change-password form + set…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-05T08:50:28Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "47e1be2c6ae2fa7922b8ff053599e4e7cd45f693",
          "body": "Signed-in users can change their own password from General Settings (a card\nreachable by every role; hidden for the shared public_manager demo account).\n\nNew endpoint POST /auth/change-password verifies the current password, requires\nthe new one to be >= 6 chars, and rejects public_manager. Wrong current -> 403,\ntoo short -> 422. Auth-flow regression (both backends) extended to cover it.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.16: change password",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-05T08:32:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "613ae2885586aa916ec5f2972b38a6e2b0029a13",
          "body": ".lambda-logo-icon masked its gradient with url('/logo_lad_erp.png') — an\nabsolute path to an asset only in the core's own public/ and not shipped in the\nnpm package, so every downstream deployment (e.g. acme-erp, lambda-erp-internal)\nrendered a broken/empty logo. Move the mark to src/logo_lad_erp.pn\n[…]\ne files. The Paint.NET source moves to frontend/design/ so it stops\nshipping in builds. Frontend only; backend bumps for lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.15 (fix sidebar logo for consumer deployments)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T22:40:31Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "8d7535085e74fdec503105f17a1afde10096a7b7",
          "body": "… fix\n\nAdded:\n- Opt-in public signup. New admin setting `allow_public_signup` (default off)\n  under General Settings; when on, anyone may self-register as a viewer. First\n  user still bootstraps as admin; toggle off keeps registration invite-only.\n  setup-status now returns first_run / public_signup\n[…]\n admin-only, leads with total LLM spend off-demo.\n- Double-quoted-literal CI guard now scans continuation lines of multi-line SQL.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.14: opt-in public signup, invite management, Token Spend…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T18:08:05Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "218a0a89d2a359635864246279f0223d2d220925",
          "body": "Two generic mechanisms a deployment plugin needs to render a payment slip\n(e.g. a Swiss QR-bill) without forking the core:\n\n- register_pdf_context(fn): providers fn(doctype, name, context) return extra\n  keys merged into the PDF render context just before rendering. Exceptions in\n  a provider are sw\n[…]\nn field (schema + migration _m014) and an IBAN input on the\n  company master form. generate_pdf now includes iban in company_info.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.13: register_pdf_context seam + Company.iban",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T17:00:39Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "12afba01448497e515ea90213cbd85260f266150",
          "body": "…url)\n\ngenerate_pdf sets WeasyPrint base_url to the rendered template's directory, so\na register_pdf_template_dir override can `<img src=\"logo.png\">` a logo (or load\nfonts/CSS) sitting next to its document.html.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.12 (PDF templates can reference sibling assets via base_…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T13:12:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9d80c0f4c5cf72186a8fbb05caf7bf5d4e4e63cd",
          "body": "- register_pdf_template_dir: deployments can override the invoice/document PDF\n  template with their own styling.\n- New customers/suppliers inherit the company's base currency instead of\n  defaulting to USD (fixes non-USD companies getting USD-defaulted documents);\n  Currency field exposed on the master forms.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.11 (PDF template seam + party currency inheritance)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T12:33:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7d48aee25f7e4ac0f46bb0d406f3f51d443509ee",
          "body": "Deployment plugins can register a template directory whose document.html (and\nother templates) override the built-in invoice/document PDF, searched before\nthe core's via a Jinja ChoiceLoader. Custom templates get the same render\ncontext generate_pdf() builds, so they only restyle the same data. No change\nwhen nothing is registered.\n\nNo release cut (per request); lands on master in [Unreleased].\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Add PDF template override seam (register_pdf_template_dir)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T12:28:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "16a00976167701278c4d1c4c0f15ada3f206c53c",
          "body": "New customers/suppliers defaulted to USD (the default_currency column default),\nso a non-USD company (e.g. CHF) got USD customers that forced sales/purchase\ndocuments to USD and failed with no USD->base rate. create_master_record now\nfills default_currency from the company when not provided (explici\n[…]\n it\nwas only settable via the API/chat, hence invisible in the UI.\n\nNo release cut (per request); lands on master in [Unreleased].\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Party masters inherit company currency; expose Currency on the form",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T12:18:29Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "0d238d71cfb10a5d552480784ea85b4527491db7",
          "body": "The v0.1.10 release failed because pypa/gh-action-pypi-publish is a Docker\naction whose image is pulled from ghcr.io, and the anonymous pull returned\n\"docker: ... unauthorized\" (ghcr anonymous-pull rate limit / flake) — the\npublish never reached PyPI. Log in with GITHUB_TOKEN first so it's an\nauthenticated pull. Adds packages: read.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci(release): authenticate to ghcr.io before the PyPI publish step",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T09:42:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "502d41f375b9642e44d721bb01556c69f6616909",
          "body": "…fill)\n\n/setup/company used to fill missing contact fields with a deterministic\npseudo-random US address (for complete-looking demo PDFs) — so every real\ncompany got a bogus address on its invoices. The setup form now collects\naddress/city/zip/country/tax-id/email/phone (optional), and the backend only\nauto-fills when the caller passes autofill_address (the demo seeding does).\nReal setups keep unprovided fields blank.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.10 (Company Setup: collect real address, stop fake auto-…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T08:44:29Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f0501f137840b7798dbb2a37eeec23b36b28bb16",
          "body": "A NavLink to a path that's a prefix of a sibling's (/setup \"Company Setup\" vs\n/setup/opening-balances \"Opening Balances\") matched active on the longer path's\npage, so both rows showed the active grey background on the Opening Balances\npage. Items nested under a sibling now use exact (end) matching. Frontend-only;\n0.1.9 for lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.9 (sidebar: stop double-highlighting nested nav paths)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-04T07:56:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "703018c0363f7bf89b48c2a265dfb8716627cbea",
          "body": "…mplete)\n\nFrontend-only changes; backend bumps for version lockstep.\n\n- Setup page gains a \"Create empty company, no demo data\" seed mode that creates\n  only the company + chart of accounts and seeds nothing.\n- Sidebar hides the \"Company Setup\" (/setup) link once a company exists.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.8 (Company Setup: no-data option + hide nav link once co…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T22:30:17Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e50b3695b25a5494d045a8a5dce9ce593231eb84",
          "body": "Two frontend-only changes to the Company Setup flow, both for all deployments:\n\n- setup page gains a third seed mode, \"Create empty company, no demo data\",\n  that creates only the company + chart of accounts and seeds nothing (for real\n  deployments starting from scratch). Backend already exposed co\n[…]\nxisting setup-status\n  query invalidation makes the link vanish reactively. Getting Started and\n  Opening Balances are unaffected.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Setup: add no-data option + hide Company Setup nav link once complete",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T22:29:16Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "9bceb63a7dfd63d9680672e9fdbeb3bb40780ec8",
          "body": "db.sql() always called fetchall(); after INSERT/UPDATE/DELETE psycopg raises\n\"the last operation didn't produce records\" (SQLite returns []). This broke the\nchat/WebSocket path (constant disconnect/reconnect) and other db.sql write call\nsites. Now fetches only when cursor.description is not None. test_db_portability\nextended to assert write-via-db.sql() returns [] on both backends. Frontend\nunchanged; 0.1.7 for lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.7 (db.sql() no longer raises on Postgres for writes)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T17:50:07Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d71064d07e881c4816d1ac7200958e2c59d656d3",
          "body": "…esilience)\n\nDouble-quoted string literals in SQL (WHERE x = \"value\", IN (\"a\",\"b\")) broke on\nPostgres — it reads \"...\" as an identifier, not a string — 500ing auth/chat/pdf\n/master paths. Converted all to single-quoted values. Also: a failed statement\nunder autocommit=False left the thread-local con\n[…]\ny (static double-quote scan + functional auth\nsmoke test on both backends), wired into CI. Frontend unchanged; 0.1.6 for lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.6 (fix double-quoted SQL literals; Postgres connection r…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T17:32:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "24ef54e0572c541f11fe621927a7e8b83d00fd1a",
          "body": "gotchas.md gains the dual-backend portability rules (portable SQL, the\ntransaction-abort and quoted-identifier differences); decisions.md updates the\nSQLite-vs-Postgres entry to reflect optional Postgres (added because Azure SMB\ncan't host SQLite's file locks).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(agents): note dual SQLite/Postgres backend",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T16:54:41Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4e3895c8b0ab2c31617a670c0461f649588f58d9",
          "body": "Add a Postgres backend to lambda_erp/database.py alongside SQLite (still the\ndefault). Select it at runtime via LAMBDA_ERP_DB=postgresql://...; install the\ndriver with the new lambda-erp[postgres] extra. A _PgConn wrapper (?->%s) and\n_PgRow (mirrors sqlite3.Row) keep the ~35 internal + external db.c\n[…]\nn SQLite AND a Postgres service container;\nboth produce identical balanced books. Frontend unchanged; ships at 0.1.5 for\nlockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.5 (optional PostgreSQL backend)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T16:32:10Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "ed2e388ef47b30eac5c213f1bb44823f0795c1d4",
          "body": "…yments)\n\nWAL needs a memory-mapped -shm file, so `PRAGMA journal_mode=WAL` fails with\n\"database is locked\" when the SQLite DB lives on an SMB/NFS share (e.g. Azure\nFiles). Add LAMBDA_ERP_SQLITE_JOURNAL_MODE (default WAL, allowlist-validated);\na network-FS deployment sets it to DELETE. Frontend unchanged; ships at 0.1.4\nto keep the packages in lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.4 (configurable SQLite journal mode for network-FS deplo…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T15:21:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "806e1abe145b2acdbc93632960cde5d73fa33d26",
          "body": "Add version badges for lambda-erp (PyPI) and @lambda-development/erp-core\n(npm) to the top badge cluster — standard for published packages and the\nlinks were missing.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: link PyPI + npm package pages from README",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T14:54:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6c258904bac97f68544444e83b9d42fa1110ba12",
          "body": "…buildable)\n\nerp-core@0.1.2 emitted an absolute /assets/report-runtime.worker-*.js URL\nfrom `new Worker(new URL(...))`, which a downstream Vite build resolved\nagainst its own public/ dir and failed on (\"Could not resolve entry\nmodule\"). Import the worker with ?worker&inline so it's embedded as a blo\n[…]\nindex.js — nothing external for consumers to resolve. Backend\nunchanged; ships at 0.1.3 only to keep the two packages in lockstep.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "Release v0.1.3 (fix: inline analytics worker so erp-core is consumer-…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-03T14:45:57Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "d3acdc5e375360ef86b55001c9921c4beb319999",
          "body": null,
          "is_bot": false,
          "headline": "some updates to readmes",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-06-02T14:00:35Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7ee15a557616e0633273a2f090f86a33cbcb1138",
          "body": "  - Backend lambda-erp and frontend @lambda-development/erp-core are both published and live on PyPI + npm at 0.1.2, via a keyless OIDC tag-triggered pipeline (release.yml) that also auto-creates the GitHub Release from the changelog.\n  - The doc now records Phase C as DONE with the three hard-won l\n[…]\nt README-only (it doesn't prove the packages compose), and a separate project (rather than an examples/ dir, which tends to drift to workspace deps and clutter the monorepo) — which is what you chose.",
          "is_bot": false,
          "headline": "What's done (Phases A → C)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T22:22:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dc7cda947f1031085fd1179c181831f46b77a9d5",
          "body": "  - actions/checkout@v4 → @v5 (×4)\n  - actions/setup-python@v5 → @v6\n  - actions/setup-node@v4 → @v6\n\n  (pypa/gh-action-pypi-publish@release/v1 isn't in the list because it's a Docker-based action, not a Node one — it doesn't trigger this warning.)\n\n  These take effect on the next release run, since\n[…]\n bump it to checkout@v5 too if you want to clear that warning as well. Want me to?\n\n  Sources:\n  - Deprecation of Node 20 on GitHub Actions runners\n  - actions/setup-node (v6)\n  - actions/setup-python",
          "is_bot": false,
          "headline": "Done — release.yml updated and YAML re-validated:",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T21:51:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9283e56386bb7120a27b83b61446d3b1bf8b5856",
          "body": null,
          "is_bot": false,
          "headline": "Release v0.1.2 (fix npm provenance: add repository to package.json)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T21:47:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "60a3c6c0e6b3887c246ab2f82b9af3ffc677b286",
          "body": null,
          "is_bot": false,
          "headline": "Release v0.1.1",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T21:41:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "098d9051d8cebbafc040d85e2657c02ad5fb13e7",
          "body": "…e packaging plan.",
          "is_bot": false,
          "headline": "Done — docs/releasing.md is the runbook, with a pointer added from th…",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T21:31:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1468730436fa4242ad6b9770b3e5458e64ee1ac6",
          "body": "  1. CHANGELOG.md (root) — Keep-a-Changelog + SemVer, one unified log for both packages, seeded with the 0.1.0 entry (core engine, multi-currency, stock analytics, i18n, open-core packaging) and an empty [Unreleased] to accrue into. Notes that the\n  extension seams are the semver-governed surface.\n \n[…]\nrs the Azure deploy (code changes), so tell me whether to commit + push or just commit and hold —\n  and then the first keyless release is: bump both to 0.1.1, git tag v0.1.1 && git push origin v0.1.1.",
          "is_bot": false,
          "headline": "  What I added",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T21:25:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "19fb1af25272357e9a5d2cbf809343a23d9a3ff9",
          "body": "  What I just did\n\n  .github/workflows/release.yml (new) — tag-v*-triggered (+ manual dispatch), fully keyless OIDC:\n  - verify-version — fails the release unless tag (minus v) == pyproject version == frontend/package.json version. Dry-ran the extraction: both read 0.1.0. ✓\n  - publish-pypi — python\n[…]\necisions (OIDC, release env, private flipped) and the remaining manual checklist.\n\n  Validated: release.yml parses as YAML, package.json is valid JSON with private gone, version-lockstep check passes.",
          "is_bot": false,
          "headline": " Phase C's code is in place and validated.",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T20:50:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0a111ae896f08ce1e6492722202238f49be5d999",
          "body": "… re-verified.\n\n  What I changed and confirmed:\n  - Renamed @lambda/erp-core → @lambda-development/erp-core across frontend/package.json, src/index.ts, vite.lib.config.ts, tailwind.preset.ts, the README customer-deployment section, and both planning docs. No\n  stale references remain.\n  - Regenerate\n[…]\n and the trusted-publisher settings to paste into each\n  registry.\n  - Sequencing footer marks 1–3 done, Phase C next. Also synced docs/core-extension-architecture.md and the README to the final name.",
          "is_bot": false,
          "headline": "Done — the package is now @lambda-development/erp-core everywhere and…",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T18:12:51Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b52908c28e90248d0f4f7be0c1a8f0a98e1e4a6f",
          "body": "  Frontend override seams (additive — demo app unchanged, builds green):\n  - Doctype registry — registerDoctype on the existing lib/doctypes.ts (pages already read it live).\n  - Route registry — routes.tsx now exposes registerRoute + buildRoutes() with merge-by-path, so a registered route overrides \n[…]\nk → a scratch consumer app installed the tarball + peers and tsc --noEmit passed while\n  exercising every seam. Docker's npm ci stays valid (lockfile regenerated). README + both planning docs updated.",
          "is_bot": false,
          "headline": "Phase B is done and verified. Here's what landed.",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T17:46:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e00e5cdabc43d676a10be799d7678053b8f3108d",
          "body": "  pyproject.toml\n  - Build backend flit → hatchling. flit packages only the one module matching the project name, so it was silently dropping api — the wheel would've installed without register_doctype / load_plugins / the\n  document classes. Now: packages = [\"lambda_erp\", \"api\"].\n  - Added readme, \n[…]\np venv (kept dist/).\n\n  One nice side effect: a future pip install -e . now makes api importable via the editable install itself, instead of only working because you launch uvicorn from the repo root.",
          "is_bot": false,
          "headline": "Phase A is done. Here's what changed and how it was verified.",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T17:16:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "0caf64e1d8751e68be5a2da434d1992ff8869975",
          "body": "  Added \"Building a customer deployment on top of the core\" to README.md (after \"Repository layout\"). It tells a future dev/LLM how to: stand up a separate private repo that depends on the core, override logic\n  (subclass + register_doctype), add behavior (register_hook, with the before-=in-txn / af\n[…]\nis a no-op.\n  Env + injected modules + hooks are restored in finally.\n\n  So the whole extension seam — registry, hooks, converters, and the loader — is now regression-protected in the canonical suite.",
          "is_bot": false,
          "headline": "README",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T16:29:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "862dd8e0a8cf336e82ef49ca59ba6626aed4772a",
          "body": "  - register_converter(source, target, fn) — lets a plugin replace conversion logic for a (source, target) pair.\n  - convert_document now honors class overrides generically: after the core converter builds the (base-class) target doc, if a subclass is registered for that doctype, it upgrades the ins\n[…]\n hook lifecycle events, swap converter logic, and is loaded via LAMBDA_ERP_PLUGINS — all without editing\n  core files, all additive (zero impact when no plugin is set), and all exercised by the suite.",
          "is_bot": false,
          "headline": "What landed (api/services.py)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T16:20:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "eda874862a40703089da8aa10ed0422e27795416",
          "body": "  api/main.py — new load_plugins(), called in the lifespan right after setup() (so overrides/hooks are registered before anything — including demo seeding — creates documents):\n  - Reads LAMBDA_ERP_PLUGINS (comma-separated module names), imports each, and calls its register().\n  - Fails fast if a na\n[…]\n(registered a hook + doctype path).\n  - A module without register() raises a clear RuntimeError.\n  - Empty LAMBDA_ERP_PLUGINS is a clean no-op.\n  - api.main imports fine; full validation suite passes.",
          "is_bot": false,
          "headline": "  What landed",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T16:15:59Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bc27cb37315d6433117804c9283ee6dc6b69d2d1",
          "body": "  Phase 1 — registry seam (api/services.py)\n  - Added register_doctype(doctype, cls, slug=None) over the existing DOCUMENT_CLASSES dict. Since get_document_class already resolves live, all of create/load/update/submit/cancel_document honor an override\n  automatically — no other change needed.\n\n  Pha\n[…]\nnd zero-impact by default: with no plugins registered, run_hooks is an empty dict lookup and the registry holds the core classes, so existing behavior is byte-identical (the full suite\n  confirms it).",
          "is_bot": false,
          "headline": " What landed",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T16:02:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1de26ea5ac8cbdd01f48ebddaa7939ed88208033",
          "body": "  - 167 label keys (fields.*, conversions.*, tables.*, tutorial.chips.*, tutorial.links.*) — the key suffix is the English label (and matched the inline defaultValue), so derived directly.\n  - 40 step strings (tutorial.steps.N.{title,description,tip}) — recovered from the STEPS array that's still in\n[…]\ns at all), I can remove them as a follow-up; otherwise they're a safe belt-and-suspenders.\n\n  en.json, de.json, and fr.json are now structurally identical, so future missing-key drift is easy to spot.",
          "is_bot": false,
          "headline": " What I moved into en.json (244 keys)",
          "author_name": "Jonathan",
          "author_login": "jcfrei",
          "committed_at": "2026-05-26T10:51:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "15b3f81cf134424982caa684b9994f70670ea15c",
          "body": "  Dashboard — the four metric cards (Total Revenue, Outstanding Receivable/Payable, Total Stock Value), the \"Recent Documents\" heading, the empty state, and each recent-doc's doctype label.\n\n  Chat — empty-state title + subtitle, the four suggestion chips, \"Load older messages\", attachment statuses \n[…]\ner your call. Everything's been verified green — want me to commit the whole i18n effort now (engine + reports + forms + lists + dashboard + chat + tutorial)? If so I'll\n  group it into clean commits.",
          "is_bot": false,
          "headline": "What's now translated (en/de/fr)",
          "author_name": "Jon",
          "author_login": "jcfrei",
          "committed_at": "2026-05-25T22:15:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 60,
      "commits_last_year": 148,
      "latest_release_at": "2026-07-17T13:24:50Z",
      "latest_release_tag": "v0.3.4",
      "releases_from_tags": false,
      "days_since_last_push": 2,
      "active_weeks_last_year": 11,
      "days_since_latest_release": 5,
      "mean_days_between_releases": 0.2
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 50,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "lambda-erp",
          "exists": true,
          "license": "Apache-2.0",
          "keywords": [],
          "ecosystem": "pypi",
          "matches_repo": null,
          "registry_url": "https://pypi.org/project/lambda-erp/",
          "is_deprecated": false,
          "latest_version": "0.3.4",
          "repository_url": null,
          "versions_count": 63,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": 4886,
          "first_published_at": "2026-05-26T21:37:29.460244Z",
          "latest_published_at": "2026-07-17T13:24:31.340288Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 5
        },
        {
          "name": "@lambda-development/erp-core",
          "exists": true,
          "license": "Apache-2.0",
          "keywords": [],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/@lambda-development/erp-core",
          "is_deprecated": false,
          "latest_version": "0.3.4",
          "repository_url": "https://github.com/lambdadevelopment/lambda-erp",
          "versions_count": 61,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 6160,
          "first_published_at": "2026-05-26T21:12:41.847000Z",
          "latest_published_at": "2026-07-17T13:24:40.890000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 5
        }
      ]
    },
    "popularity": {
      "forks": 5,
      "stars": 20,
      "watchers": 0,
      "fork_history": {
        "days": [
          {
            "date": "2026-05-26",
            "count": 1
          },
          {
            "date": "2026-06-17",
            "count": 2
          },
          {
            "date": "2026-06-28",
            "count": 1
          },
          {
            "date": "2026-07-14",
            "count": 1
          }
        ],
        "complete": true,
        "collected": 5,
        "total_forks": 5
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": true,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "frontend/tsconfig.json"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 181453,
      "source_files_sampled": 145,
      "oversized_source_files": 3,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 1598
    },
    "dependencies": {
      "manifests": [
        "frontend/package.json",
        "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": [
        "npm",
        "pypi"
      ],
      "dependencies": [
        {
          "name": "@fontsource/inter",
          "manifest": "frontend/package.json",
          "ecosystem": "npm",
          "version_constraint": "^5.2.8"
        },
        {
          "name": "fastapi",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.115"
        },
        {
          "name": "uvicorn",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.30"
        },
        {
          "name": "python-multipart",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.0.9"
        },
        {
          "name": "pydantic",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.0"
        },
        {
          "name": "anthropic",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.40"
        },
        {
          "name": "openai",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.0"
        },
        {
          "name": "httpx",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.27"
        },
        {
          "name": "python-dotenv",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=1.0"
        },
        {
          "name": "python-dateutil",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=2.8"
        },
        {
          "name": "python-jose",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3.3"
        },
        {
          "name": "bcrypt",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=5"
        },
        {
          "name": "Jinja2",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=3.1"
        },
        {
          "name": "weasyprint",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=62.0"
        },
        {
          "name": "pypdf",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=4.0"
        },
        {
          "name": "holidays",
          "manifest": "pyproject.toml",
          "ecosystem": "pypi",
          "version_constraint": ">=0.40"
        }
      ],
      "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": "jcfrei",
          "commits": 148,
          "avatar_url": "https://avatars.githubusercontent.com/u/523261?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "deploy.yml",
        "release.yml",
        "terraform-apply.yml"
      ],
      "has_docs_dir": true,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "package-lock.json"
      ],
      "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": 3,
            "reason": "project has 1 contributing companies or organizations -- score normalized to 3",
            "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": 10,
            "reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "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": 1,
            "reason": "dependency not pinned by hash detected -- score normalized to 1",
            "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": 0,
            "reason": "security policy file not 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": 0,
            "reason": "11 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "39068a6e52f556509734450d296da9a3e65cc4a2",
        "ran_at": "2026-07-22T15:11:10Z",
        "aggregate_score": 3.6,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": false,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": null,
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": null,
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/lambdadevelopment/lambda-erp",
    "host": "github.com",
    "name": "lambda-erp",
    "owner": "lambdadevelopment"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 53,
      "inputs": {
        "security": 36,
        "vitality": 83,
        "community": 45,
        "governance": 34,
        "engineering": 66
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 83,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "good",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 72,
            "inputs": {
              "commits_last_year": 148,
              "human_commit_share": 1,
              "days_since_last_push": 2,
              "active_weeks_last_year": 11
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 2 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 2
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "11/52 weeks with commits",
                "points": 7.6,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "148 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 148
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "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": 100,
            "inputs": {
              "releases_count": 60,
              "latest_release_tag": "v0.3.4",
              "releases_from_tags": false,
              "days_since_latest_release": 5,
              "mean_days_between_releases": 0.2
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "60 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 60
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 5 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~0.2 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 0.2
                    }
                  }
                ],
                "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": 45,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 26,
            "inputs": {
              "forks": 5,
              "stars": 20,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "20 stars",
                "points": 20.7,
                "status": "partial",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 20
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "5 forks",
                "points": 5,
                "status": "partial",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "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": "moderate",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "has_readme": true,
              "has_license": true,
              "has_contributing": false,
              "has_issue_template": false,
              "has_code_of_conduct": false,
              "has_pull_request_template": false
            },
            "components": [
              {
                "key": "readme",
                "name": "README",
                "detail": null,
                "points": 22.5,
                "status": "met",
                "details": [],
                "max_points": 22.5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "recognized license (Apache-2.0)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "Apache-2.0"
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributing_guide",
                "name": "CONTRIBUTING guide",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 18
              },
              {
                "key": "code_of_conduct",
                "name": "Code of conduct",
                "detail": null,
                "points": 0,
                "status": "missed",
                "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": 0,
                "status": "missed",
                "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": 67,
            "inputs": {
              "packages": [
                "lambda-erp",
                "@lambda-development/erp-core"
              ],
              "dependents": null,
              "ecosystems": "npm, pypi",
              "total_downloads": null,
              "monthly_downloads": 11046
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "11,046 downloads/month across npm, pypi",
                "points": 53.9,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 11046,
                      "ecosystems": "npm, 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": 13,
            "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 1 contributing companies or organizations -- score normalized to 3",
                "points": 3,
                "status": "partial",
                "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": 41,
            "inputs": {
              "followers": 1,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "lambdadevelopment",
              "public_repos": 3,
              "account_age_days": 742
            },
            "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 lambdadevelopment",
                "points": 2.2,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 1,
                      "login": "lambdadevelopment"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "3 public repos, account ~2 yr old",
                "points": 8.4,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 3
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 2
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "lambda-erp",
                "@lambda-development/erp-core"
              ],
              "ecosystems": "npm, pypi",
              "any_deprecated": false,
              "min_days_since_publish": 5
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "2 package(s) on npm, pypi",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 2,
                      "ecosystems": "npm, pypi"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 5 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 5
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "63 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 63
                    }
                  }
                ],
                "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": "moderate",
        "name": "Engineering Quality",
        "value": 66,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "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": 60,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "4 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": 36,
        "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": 36,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 3.6
            },
            "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 1 contributing companies or organizations -- score normalized to 3",
                "points": 0.8,
                "status": "partial",
                "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": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "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 1",
                "points": 0.5,
                "status": "partial",
                "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 not detected",
                "points": 0,
                "status": "missed",
                "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": "11 existing vulnerabilities detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 1
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "good",
        "name": "AI Readiness",
        "value": 70,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "excellent",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 85,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.92,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 1598
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "CLAUDE.md",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "CLAUDE.md"
                    }
                  }
                ],
                "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": "92 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 92,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "moderate",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 54,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "package-lock.json"
              ],
              "has_dockerfile": true,
              "typed_language": false,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [
                "frontend/tsconfig.json"
              ],
              "agent_commit_share": 0.78,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "frontend/tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "frontend/tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "Dockerfile, lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Dockerfile, lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "78 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 78,
                      "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 1",
                "points": 1,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "good",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 81,
            "inputs": {
              "primary_language": "Python",
              "largest_source_bytes": 181453,
              "source_files_sampled": 145,
              "oversized_source_files": 3
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Python with type-check config (frontend/tsconfig.json)",
                "points": 27,
                "status": "partial",
                "details": [
                  {
                    "code": "typecheck_config_language",
                    "params": {
                      "files": "frontend/tsconfig.json",
                      "language": "Python"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "3/145 source files over 60KB",
                "points": 53.9,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 145,
                      "oversized": 3
                    }
                  }
                ],
                "max_points": 55
              }
            ]
          }
        ],
        "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:lambda-erp@0.3.4; advisories assessed against the repository dependency graph instead"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-22T15:11:17.963013Z",
  "schema_version": "0.26.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/l/lambdadevelopment/lambda-erp.svg",
  "full_name": "lambdadevelopment/lambda-erp",
  "license_state": "standard",
  "license_spdx": "Apache-2.0"
}

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.26.0 — full methodology · metrics wiki.

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