Публічний реєстр
Звіт про здоров'я програмного забезпеченнясхема 0.27.0 · метрики 1.13.0 · 2026-07-24 12:30 UTC

zaclummys / sf-plugin-permission-sets

Declarative, GitOps-style management of permission set assignments for Salesforce orgs.

TypeScript · JavaScriptBSD-3-Clause★ 1 зірка⑂ 0 форківз черв. 2026 р.Переглянути на GitHub ↗

zaclummys/sf-plugin-permission-sets має індекс здоров’я 57 зі 100, що відповідає смузі «Помірний». Найвищий показник — AI Readiness (82/100), найнижчий — Community & Adoption (34/100). Останнє оновлення було 5 днів тому. Більшість нещодавньої роботи виконує один учасник.

57
загалом / 100
Помірний

Індекс здоров'я програмного забезпечення

Метрики згруповано у зважені категорії на шкалі 1–100. Загальна оцінка починається як їхнє середнє; коли публічні дані активують Політику юрисдикцій високого ризику, рейтинг коригується й отримує верхню межу 49 («Під ризиком»). Готовність до ШІ не входить до індексу.

57
Відмінний85-100Зразковий; відповідає практично всім перевіреним критеріям
Добрий70-84Здоровий; незначні прогалини
Помірний50-69Прийнятний, але з помітними прогалинами; рекомендовано перевірку
У зоні ризику30-49Суттєві слабкі місця; впровадження потребує обережності
Критичний1-29Серйозні проблеми (покинутий, єдиний мейнтейнер, без базової гігієни)
ЖиттєздатністьСпільнота тавпровадженняСталість таврядуванняІнженернаякістьБезпекаГотовність доШІ

Профіль оцінок

Кожна вісь — окрема категорія. Форма важить більше, ніж середнє: здоровий об'єкт заповнює всю фігуру, тоді як профіль із піками та провалами означає, що сила в одному вимірі маскує ризик в іншому.

Власність

Isaac FerreiraОсобистий обліковий запис
20 підписників28 публічних репозиторіївз бер. 2016 р.@gympass

Цей репозиторій належить особистому обліковому запису. Проєкт з єдиним власником несе більший ризик безперервності, ніж підтримуваний організацією.

Пакетні екосистеми

РеєстрПакетВерсіяЗавантажень / місВерсіїОстання публікаціяТеги
npmsf-plugin-permission-sets0.4.08 679575 днів томуsalesforcesf-pluginsfdx-pluginpermission-setspermission-set-assignmentpermission-set-groupaccess-managementuser-provisioninggitopsdeclarative

Метрики за категоріями

Життєздатність

Чи живий проєкт — чи пишеться код і чи виходять релізи?

74Добрий · 22% загального індексу
Як обчислюється оцінка
36/36Свіжість push — останній push 5 дн. тому
2.8/36Ритм комітів — 4/52 тижнів із комітами
18/18Обсяг комітів — 123 комітів за останній рік
0/10OpenSSF Scorecard: Maintained — project was created within the last 90 days. Please review its contents carefully
Використані вхідні дані
commits_last_year123
human_commit_share0,98
days_since_last_push5
active_weeks_last_year4
Як обчислюється оцінка
27/27Випускає релізи — опубліковано 5 релізів
36/36Свіжість релізів — останній реліз 5 дн. тому
27/27Ритм релізів — реліз кожні ~5 дн.
0/10OpenSSF Scorecard: Signed-Releases — немає даних
Використані вхідні дані
releases_count5
latest_release_tagv0.4.0
releases_from_tagsні
days_since_latest_release5
mean_days_between_releases5
Виключено з оцінювання (немає даних або не застосовно): OpenSSF Scorecard: Signed-Releases. Залишкові ваги перенормовано.

Спільнота та впровадження

Чи має проєкт користувачів, завантаження, увагу та влаштовані умови для контриб’юторів?

34У зоні ризику · 18% загального індексу
Як обчислюється оцінка
0/60Зірки — 1 зірок
0/25Форки — 0 форків
0/15Спостерігачі — 0 спостерігачів
Використані вхідні дані
forks0
stars1
watchers0
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
Як обчислюється оцінка
22.5/22.5README
22.5/22.5Ліцензія — визнана ліцензія (BSD-3-Clause)
0/18Настанови CONTRIBUTING
0/13.5Кодекс поведінки
0/7.2Шаблон issue
0/6.3Шаблон PR
Використані вхідні дані
has_readmeтак
has_licenseтак
has_contributingні
has_issue_templateні
has_code_of_conductні
has_pull_request_templateні
Як обчислюється оцінка
52.5/80Щомісячні завантаження — 8 679 завантажень/місяць у npm
0/20Залежні пакети в реєстрі — ця екосистема цього не повідомляє
Використані вхідні дані
packagessf-plugin-permission-sets
dependents
ecosystemsnpm
total_downloads
monthly_downloads8 679
Виключено з оцінювання (немає даних або не застосовно): Залежні пакети в реєстрі. Залишкові ваги перенормовано.

Сталість та врядування

Чи переживе проєкт своїх людей — бас-фактор, реактивність, хто за ним стоїть і як супроводжуються пакети?

42У зоні ризику · 24% загального індексу
Як обчислюється оцінка
9/54Бас-фактор — на 1 контриб’ютор(ів) припадає половина всіх комітів
0/22.5Розподіл комітів — головний контриб’ютор — автор 100% комітів
1.4/13.5Широта контриб’юторів — 1 контриб’юторів
3/10OpenSSF Scorecard: Contributors — project has 1 contributing companies or organizations -- score normalized to 3
Використані вхідні дані
bus_factor1
contributors_sampled1
top_contributor_share1
Як обчислюється оцінка
0/46.8Вирішення issue — немає issue або даних
9.6/38.3Прийняття PR — злито 3/12 вирішених PR
0/15OpenSSF Scorecard: Code-Review — Found 0/27 approved changesets -- score normalized to 0
Використані вхідні дані
merged_prs3
open_issues0
closed_issues0
issue_closed_ratio
closed_unmerged_prs9
Виключено з оцінювання (немає даних або не застосовно): Вирішення issue. Залишкові ваги перенормовано.
Як обчислюється оцінка
10/30Підтримка власника — особистий (користувацький) обліковий запис
0/20Верифікований домен — не застосовно до користувацьких облікових записів
9.5/25Охоплення власника — 20 підписників у zaclummys
22.6/25Послужний список — 28 публічних репозиторіїв, вік облікового запису ~10 р.
Використані вхідні дані
followers20
owner_typeUser
is_verified
owner_loginzaclummys
public_repos28
account_age_days3 781
Виключено з оцінювання (немає даних або не застосовно): Верифікований домен. Залишкові ваги перенормовано.

Супровід пакетів

100Відмінний
Як обчислюється оцінка
25/25Опубліковано й доступно — 1 пакет(ів) у npm
35/35Свіжість публікацій — остання публікація 5 дн. тому
20/20Історія версій — 57 опублікованих версій
20/20Не застарілий — активний, не deprecated і не yanked
Використані вхідні дані
packagessf-plugin-permission-sets
ecosystemsnpm
any_deprecatedні
min_days_since_publish5

Інженерна якість

Чи наявні базові інженерні практики та документація?

74Добрий · 20% загального індексу
Як обчислюється оцінка
24/24Процеси CI — 2 процес(ів) CI
24/24Наявні тести
16/16Конфігурація лінтера — eslint.config.js
0/9.6Pre-commit-хуки
6.4/6.4.editorconfig
20/20OpenSSF Scorecard: CI-Tests — 1 out of 1 merged PRs checked by a CI test -- score normalized to 10
Використані вхідні дані
has_ciтак
has_testsтак
has_editorconfigтак
has_linter_configтак
has_precommit_configні

Документація

50Помірний
Як обчислюється оцінка
30/30README
0/25Каталог документації
0/15Сайт документації / домашня сторінка
10/10Опис репозиторію
10/10Теми — 5 тем
0/10Wiki
Використані вхідні дані
topicsaccess-management, gitops, permission-sets, salesforce, sfdx-plugin
has_wikiні
homepage
has_readmeтак
has_docs_dirні
has_descriptionтак

Безпека

Чи міцні видимі практики безпеки й ланцюга постачання, без непослабленої пов’язаності з юрисдикціями високого ризику?

60Помірний · 16% загального індексу

Стан безпеки

50Помірний
Як обчислюється оцінка
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
2.5/2.5CI-Tests — 1 out of 1 merged PRs checked by a CI test -- score normalized to 10
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/27 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
7.5/7.5Dependency-Update-Tool — update tool detected
0/5Fuzzing — project is not fuzzed
2.5/2.5Ліцензія — license file detected
0/7.5Maintained — project was created within the last 90 days. Please review its contents carefully
5/5Packaging — packaging workflow detected
1/5Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 2
0/5SAST — SAST tool is not run on all commits -- score normalized to 0
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — немає даних
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6.8/7.5Vulnerabilities — 1 existing vulnerabilities detected
Використані вхідні дані
sourceopenssf_scorecard
checks_evaluated17
scorecard_versionv5.5.0
checks_inconclusive1
scorecard_aggregate5
Виключено з оцінювання (немає даних або не застосовно): signed_releases. Залишкові ваги перенормовано.
Як обчислюється оцінка
35/35Прямі залежності без відомих сповіщень — жодна пряма залежність не має відомих сповіщень
25/25Непрямі залежності без відомих сповіщень — жодна непряма залежність не має відомих сповіщень
0/40Немає задавнених сповіщень — жодне сповіщення не має дати публікації
Використані вхідні дані
sourceosv
advisories0
affected_packages0
assessed_packages288
unassessed_packages0
affected_by_severitynone
direct_affected_packages0
Виключено з оцінювання (немає даних або не застосовно): Немає задавнених сповіщень. Залишкові ваги перенормовано. Звірено з runtime-замиканням залежностей npm:sf-plugin-permission-sets@0.4.0 — тим, що тягне за собою встановлення опублікованого пакета, — 288 пакетів. Досяжність не аналізується.

Готовність до ШІ

Наскільки репозиторій оснащений для розробки та супроводу за участі ШІ-агентів? Незалежний, експериментальний бейдж — вага 0.0, тож він подається окремо і не впливає на загальний індекс здоров'я.

82Добрий · 0% загального індексу
Як обчислюється оцінка
45/45Інструкції для агентів — CLAUDE.md
0/15Машиночитана документація (llms.txt)
40/40Читабельна історія комітів — намір зазначено у 87 з 98 людських комітів (структурований заголовок або пояснювальний текст)
Використані вхідні дані
has_llms_txtні
legible_history_share0,888
agent_instruction_filesCLAUDE.md
agent_instruction_max_bytes6 263
Як обчислюється оцінка
0/18Розгортання однією командою
22/22Автоматизовані тести
11/11Конфігурація лінтера / форматера — eslint.config.js
11/11Статична перевірка типів — tsconfig.json
10/10Відтворюване середовище — lockfile
10/10Підтверджена практика роботи з агентами — 84 з останніх 100 комітів створено агентом або з його зазначенням
8/8Автоматизоване супроводження — 2 з останніх 100 комітів — автоматичні оновлення залежностей
2/10OpenSSF Scorecard: Pinned-Dependencies — dependency not pinned by hash detected -- score normalized to 2
Використані вхідні дані
has_nixні
has_testsтак
lockfilespackage-lock.json
has_dockerfileні
typed_languageтак
bootstrap_files
has_devcontainerні
has_linter_configтак
typecheck_configstsconfig.json
agent_commit_share0,84
toolchain_manifests
dependency_bot_commit_share0,02
Як обчислюється оцінка
45/45Типізований код — TypeScript (статично типізована)
55/55Керовані розміри файлів — 0/41 файлів вихідного коду понад 60 КБ
Використані вхідні дані
primary_languageTypeScript
largest_source_bytes17 998
source_files_sampled41
oversized_source_files0

Ключові факти

1зірок GitHub
1контриб'юторів
123комітів за останні 12 місяців
5днів від останнього пушу
5релізів
1бас-фактор
0відкритих issue
npmпакетних екосистем

Попередження щодо збору даних

  • 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

Докладніше

OpenSSF Scorecard 5.0 / 10
5.0сукупно

Незалежна, не прив'язана до інструментів оцінка безпеки від відкритого проєкту OpenSSF Scorecard. Кожна перевірка винагороджує практику безпеки, а не інструмент конкретного постачальника. Перевірки, які Scorecard не зміг визначити, позначено н/д і виключено з оцінки безпеки (вони ніколи не зараховуються як нуль).Scorecard v5.5.0 · 2026-07-24 12:30 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
10CI-Tests1 out of 1 merged PRs checked by a CI test -- score normalized to 10
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/27 approved changesets -- score normalized to 0
3Contributorsproject has 1 contributing companies or organizations -- score normalized to 3
10Dangerous-Workflowno dangerous workflow patterns detected
10Dependency-Update-Toolupdate tool detected
0Fuzzingproject is not fuzzed
10Licenselicense file detected
0Maintainedproject was created within the last 90 days. Please review its contents carefully
10Packagingpackaging workflow detected
2Pinned-Dependenciesdependency not pinned by hash detected -- score normalized to 2
0SASTSAST tool is not run on all commits -- score normalized to 0
10Security-Policysecurity policy file detected
н/дSigned-Releasesno releases found
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
9Vulnerabilities1 existing vulnerabilities detected
Прямі залежності 6
РеєстрПакетОбмеження версіїМаніфест
npm@oclif/core^4package.json
npm@salesforce/core^8package.json
npm@salesforce/sf-plugins-core^12package.json
npmglobby^16package.json
npmyaml^2package.json
npmzod^4package.json
Усі залежності не зібрано

Не вдалося зібрати розв'язаний набір залежностей для цього звіту: GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

Сповіщення про залежності 0

Встановлення npm:sf-plugin-permission-sets@0.4.0 тягне 288 пакетів, прямих і транзитивних: 0 мають відомі сповіщення, з них 0 — прямі залежності.

Жодне відоме сповіщення не стосується оцінених залежностей.

Сповіщення означає, що версія, записана в графі залежностей, потрапляє в уражений діапазон. Досяжність не аналізується, а граф містить піниї розробки й тестування — знахідка може стосуватися інструментів, а не поставленого коду.

Звіт у форматі JSON машиночитний
{
  "data": {
    "repo": {
      "topics": [
        "access-management",
        "gitops",
        "permission-sets",
        "salesforce",
        "sfdx-plugin"
      ],
      "is_fork": false,
      "size_kb": 330,
      "has_wiki": false,
      "homepage": null,
      "languages": {
        "Batchfile": 117,
        "JavaScript": 31044,
        "TypeScript": 92386
      },
      "pushed_at": "2026-07-18T23:31:44Z",
      "created_at": "2026-06-28T17:53:02Z",
      "owner_type": "User",
      "updated_at": "2026-07-18T23:31:48Z",
      "description": "Declarative, GitOps-style management of permission set assignments for Salesforce orgs.",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": "BSD-3-Clause",
      "default_branch": "main",
      "license_spdx_raw": "BSD-3-Clause",
      "primary_language": "TypeScript",
      "significant_languages": [
        "TypeScript",
        "JavaScript"
      ]
    },
    "owner": {
      "blog": null,
      "name": "Isaac Ferreira",
      "type": "User",
      "login": "zaclummys",
      "company": "@gympass ",
      "location": "Niterói, RJ",
      "followers": 20,
      "avatar_url": "https://avatars.githubusercontent.com/u/17896485?v=4",
      "created_at": "2016-03-17T05:59:52Z",
      "is_verified": null,
      "public_repos": 28,
      "account_age_days": 3781
    },
    "license": {
      "state": "standard",
      "spdx_id": "BSD-3-Clause",
      "raw_spdx": "BSD-3-Clause",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v0.4.0",
          "kind": "minor",
          "published_at": "2026-07-18T19:13:11Z"
        },
        {
          "tag": "v0.3.1",
          "kind": "patch",
          "published_at": "2026-07-17T19:49:33Z"
        },
        {
          "tag": "v0.3.0",
          "kind": "minor",
          "published_at": "2026-07-04T21:30:28Z"
        },
        {
          "tag": "v0.2.0",
          "kind": "minor",
          "published_at": "2026-06-29T19:55:18Z"
        },
        {
          "tag": "v0.1.0",
          "kind": "minor",
          "published_at": "2026-06-28T21:10:29Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "41e66be1eed3fad7344af0c8333e8b58cb822361",
          "body": null,
          "is_bot": false,
          "headline": "Simplify methods",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T23:31:42Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "35c1908b65f14e1dea2c5dc7aea9d41fd2d73fba",
          "body": "…ames\n\nMove every inline SELECT into a dedicated, self-contained query builder\n(buildMembershipQuery, buildCurrentMembershipQuery, buildLicenseQuery,\nbuildCurrentLicenseQuery, buildUserQuery, buildTargetQuery), each owning\nits own WHERE construction so the adapter methods pass domain inputs, not\nSOQ\n[…]\nnments -> listCurrentAssignments (and its private membership\nand license readers), plus soqlLiteral, inList, outcomeOf, and the batch\nbuilders.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: extract SOQL builders and give methods and functions verb n…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T23:08:35Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "2eead214bd66c194345cae4e47c0aec8752a0258",
          "body": "… sites\n\nRename membershipKindTarget to classifyMembership and narrow its return\nto the two kinds it can produce (never permissionSetLicense). Destructure\n{ kind, target } in each mapper and list every field explicitly, instead\nof spreading a fresh call result into the middle of the object literal.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: rename membership classifier and destructure it at the call…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:51:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ec098fd604a725cec635ac7ee57f00e619d2757e",
          "body": null,
          "is_bot": false,
          "headline": "Add concurrency control",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:47:39Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "65dde384cfde11e953fca65d6130ba043c7aaac4",
          "body": "Collapse the duplicated object-shaped ternaries in the membership\nmappers into a shared membershipKindTarget helper, and pull the nested\nternary in formatFindings into a locationPrefix helper.\n\nRepresent \"no expiration\" uniformly as null instead of an absent/optional\nkey: expiration is now a require\n[…]\nough diff, report,\nserialize, and normalize. This removes the null-vs-undefined mismatch\nthat could have surfaced a value as a spurious update.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: make assignment expiration an always-present string | null",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:45:42Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "532913921e7bf80d38a3718a773bc0d2242ef878",
          "body": null,
          "is_bot": false,
          "headline": "Increase vitest timeout",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:32:45Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bfcd8d1d7bb2bfc017dbea906880c8373cd85e57",
          "body": null,
          "is_bot": false,
          "headline": "Fix ternary connection org client",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:32:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "306f7899a43daeef122d6416110631b457ac486f",
          "body": null,
          "is_bot": false,
          "headline": "Fix ternary in finding",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:32:44Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7afda633da7aae61ddfaa5ba95659efc9a876908",
          "body": "Remove the saved-plan artifact (plan --out, apply --plan)",
          "is_bot": false,
          "headline": "Merge pull request #12 from zaclummys/remove-saved-plan-feature",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:17:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "001a573d3d55941c4f2b468b2441754820e6ff14",
          "body": "Without the saved-plan injection there is no deterministic, org-agnostic\nway to force an in-scope removal, so this case cannot run reliably as a\nblack-box real-org test. Remove it along with its now-unused imports.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: drop the --json deletion-refusal case",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:15:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5690ddb073fe2f247708563c162b02463698ef03",
          "body": "Dropping a whole user left zero removals: the apply diff only fetches\nassignments for targets referenced in the file, so a dropped user's\ntargets fall out of the managed set and are never seen as removable.\n\nRelocate one target to another user instead. The target stays\nreferenced (still managed), so the original membership becomes a removal\nsync acts on, reaching the confirmation gate the test asserts on.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: force an in-scope removal for the --json refusal case",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:12:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9b981495ee3a39dce1ed1f84a01a7d3582e2c6f8",
          "body": "The saved-plan round-trip was the sole cause of apply's two-source\nbifurcation, and the sf ecosystem rarely wires up the CI artifact\nhand-off it needs (the README's own recommended workflow never used\nit). Dropping it collapses apply to a single --file source.\n\nRemove:\n- plan --out and the core/plan\n[…]\nrvices barrel exports\n\napply now recomputes from the files every run; run plan shortly before\napply so the preview reflects what apply will do.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: remove the saved-plan artifact (plan --out, apply --plan)",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T22:05:36Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "f45d882bd1ea8c242b5840d71bcdfd34c2d0402b",
          "body": "A --json run that hits the delete-confirmation gate throws error.promptInJson,\nwhich SfCommand serializes into the JSON envelope on stdout. stderr only carries\nsf's linked-ESM-module notice, so the message assertion belonged on stdout.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: assert apply's --json refusal on stdout, not stderr",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T21:32:40Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d1a3ab443f3ab1b08f684f727b87dbc2f2eacf74",
          "body": "…id-input handling\n\nAdds a non-dry-run apply round trip, the JSON-mode delete-confirmation\nrefusal, schema/malformed fixture coverage for plan/apply/validate\n(previously only exercised by check), and export's --help, invalid\n--kind, and positive --user match cases.\n\nCo-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: cover apply's real-mutation and confirm-prompt paths, and inval…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T21:19:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "67cc13a11b8cffd093a70fd560e9375746e73cc8",
          "body": "Match the versions the README now recommends. Node pin (24) and the rest\nof the pipeline are unchanged.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: bump actions/checkout and actions/setup-node to v7",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:15:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7a3ce9da57a0c37131dd9aa5e6e71df122a245ec",
          "body": "Space out the workflow YAML, drop the paths filter and the inline\ncomment, and rename the auth secret to SFDX_AUTH_URL consistently in\nboth the workflow and the prose.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: tidy GitHub Actions example (spacing, secret name, triggers)",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:12:56Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "57e75e8deb158e89293898d83fde9a6e18b65fbd",
          "body": "Pin actions/checkout and actions/setup-node to v7 (current latest).\nAuthenticate by piping the secret into `sf org login sfdx-url\n--sfdx-url-stdin`, which drops the separate auth-file write step and\navoids writing the secret to disk.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: bump GitHub Actions to v7 and auth via --sfdx-url-stdin",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:09:39Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "12bd49a183b4ae0bf17817d983e03716db0a00d0",
          "body": "Give each run step a descriptive name so the workflow logs read clearly.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: name the run steps in the GitHub Actions examples",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:08:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c3928d7923c35ce54b332db3aa3e5767a67a617b",
          "body": "Relocate the section (and its TOC entry) below Commands, so readers meet\nthe commands first. Scope the check workflow to pull requests targeting\nmain.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: move GitHub Actions after Commands, scope check to PRs into main",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:07:19Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c01d2dacdb40a18372c0149f34cc247bf6e412b6",
          "body": "Two copy-paste workflows: check every pull request with no org, and\napply on merge to main using a stored SF_AUTH_URL secret. Notes how to\nget the auth URL and how to add a plan step for PR diffs.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add a dead-simple GitHub Actions section",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:05:01Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b6e72085d52d17f09e180c0796bd83e534a32895",
          "body": "We only ship `latest` (tagged releases) and `dev` (main snapshots), so\nthe `next` prerelease channel was never used. Remove its row and note\nfrom the README and the hyphenated-tag case from the publish workflow;\ntagged releases now always publish to `latest`.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build: drop the unused `next` dist-tag",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:03:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cca341ff1e07d0f5e9d5748ee8e0d0741d380ac8",
          "body": "- Node.js floor is 20+, not 18+ (matches package.json engines).\n- --file describes check/validate/plan/apply; note export's -f is output.\n- Add the mode and plan-file core modules to the architecture table.\n- Note plan --out / apply --plan in the pipeline description.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: fix README staleness after recent features",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T19:01:27Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d6f77d2baa0fe75a321f7aee4ecc194079faf5d6",
          "body": "generatedAt was informational only: nothing in apply used it, and we do\nno staleness checks (the org-id and version guards cover safety). It also\nmade plan --out non-deterministic, at odds with the rest of the plugin.\nRemove the field, the timestamp in the apply header, and the schema entry.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor(plan): drop generatedAt from the saved plan",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:58:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5333897894eb97f7ea7a8ff89126f29f631f6068",
          "body": "The saved-plan suite spanned two commands. Split it so test/plugin has\none file per command: the plan --out coverage moves to plan.test.js, the\napply --plan round-trip and guard cases move to apply.test.js, and\nsaved-plan.test.js is removed.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: one file per command, fold saved-plan into plan and apply",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:54:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6b56cc1e2510247e50d44b3f95b99ba9b4141706",
          "body": "The file split `sf ps validate` into two identically-titled describe\nblocks along the old online/offline line. With that distinction gone,\nfold them into a single describe; the round-trip comment now annotates\nthe first real-org test instead of the second block.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(validate): merge the two validate describe blocks into one",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:51:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bfe83be7032bf1612dedb81f8fed04add0e55357",
          "body": "There is no \"online\" vs \"offline\" concept in this plugin. Replace every\nmention with concrete phrasing (needs an org vs does not) across service\nand core doc comments, the README, command message descriptions, and test\ndescribe titles. Rename the `online` local in ValidateService to\n`resolved`. No behavior change.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: drop the online/offline distinction",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:49:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ab1b60c739e87ae5db3fb6a49bcd4e0154b84673",
          "body": "`plan --out <file>` freezes the resolved, mode-scoped change set to a\nplan file; `apply --plan <file>` runs it verbatim, with no re-read,\nre-resolve, or re-diff. What you review is exactly what applies.\n\ncore: new plan-file module (SavedPlan type, zod schema, serialize/parse,\nversion) and mode modul\n[…]\nt), which\nthe README documents. README updated first per repo convention. Adds\nblack-box tests for the round-trip and every guard.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(plan,apply): saved-plan artifact (plan --out, apply --plan)",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:44:44Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "c5e6a8c202160f73f86aa95b6f51a815ba021f7e",
          "body": "Adds offline checks (unresolvable org, --help) and online round-trip\ntests against PS_TARGET_ORG: export the org, validate the snapshot\nback against it, and assert no findings plus a valid --json envelope.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test(validate): cover ps validate",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:32:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5000ad672c921dc444d449f036129f835026f9f7",
          "body": "globby@16 requires Node >=20, so the previous >=18.0.0 floor was\nnever actually runnable. Node 18 is EOL as of April 2025.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build: raise engines floor to Node 20 to match globby",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:31:59Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "6209a484eebb49d2202d3bd684ebd5692588d8cc",
          "body": "Add BSD-3-Clause LICENSE.md matching the declared license field, replace\nthe Salesforce-internal vuln boilerplate in SECURITY.md with a real policy\nrouted to this repo, and add homepage and bugs URLs to package.json.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: add license file, real security policy, and npm metadata",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:19:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "05a1e3aec14a899ef496e0ff13c4656e95225d2c",
          "body": "The summary prints to stdout in file mode and is absent in stdout mode,\nnot \"on stderr\" as previously written. Clarify that under --json the\ncontent field appears only when --output-file is omitted.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs(export): correct stdout/stderr description for stdout mode",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:17:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "b3e8254822378312e5e15fa73246296e695cee51",
          "body": "Make --output-file optional. Without it, export writes the YAML document\nto stdout, byte-for-byte identical to the file it would have written, so\nit pipes and diffs cleanly (sf ps export -o prod | diff - snapshot.yml).\nWarnings and the summary stay on stderr, keeping stdout pure YAML. Under\n--json t\n[…]\nME updated first per repo convention. Adds black-box tests for the\nstdout write, file/stdout parity, and the --json content field.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(export): write to stdout when --output-file is omitted",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:16:12Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "bc6e769f0d61b951b5bad7f28c2433383c1b2769",
          "body": "Every other required flag already had a short alias (-f for --file, -o\nfor --target-org). --output-file on export was the only required flag\nwithout one. Use -f, consistent with the plugin's \"f = file\" convention\n(export has no input --file flag to collide with).\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(export): add -f short alias for --output-file",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:09:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3a12bdb7186ccbcc4b7c0a6a616fe46609bc9a19",
          "body": "Add optional --user (repeatable, exact username) and --kind (repeatable,\nYAML scope keys) flags to `sf ps export`. Values within a flag union,\n--user and --kind intersect. A requested --user with no in-scope\nassignments warns and continues instead of writing a silent empty file.\n\nThe filter is a pur\n[…]\ny scoping next.\n\nREADME updated first per repo convention. Adds black-box tests for\n--kind scoping and the unmatched-user warning.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat(export): scope exports with --user and --kind",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:01:42Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "09e0bd4a41a1499707b5006f35a1c20a0b15ed25",
          "body": "Treat the plugin as a product: weigh developer and user experience in\nevery change, design for real usage (scoping, safe destructive actions,\nclear output), and call out UX and DX gaps proactively.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add product mindset (DX & UX) convention to CLAUDE.md",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T18:01:33Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "8a32e0f64c9a7571f05492cc42949eae6ca6b6a1",
          "body": null,
          "is_bot": false,
          "headline": "Break line",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:42:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1a97f5cca7df3da1bf434b7f68323b30aa6f5c71",
          "body": null,
          "is_bot": false,
          "headline": "Update global",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:42:25Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0cf8cda1773c757ba93bdfa346979da7c40ac438",
          "body": null,
          "is_bot": false,
          "headline": "Update packages",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:42:18Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "9d8d4227dbbd8bdcc120957fe165aa58a3954a78",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "style: reindent eslint config to 4 spaces, break long lines",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:35:43Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0465bce3855993280072eeca9db4596cbca1b13d",
          "body": "Convert the remaining .then callbacks in the org client and the\nresolution/validate services to async/await, keeping parallelism by\npushing promises from async helpers and async map callbacks into the\nPromise.all arrays.\n\nEnforce the conventions in eslint.config.js: a no-restricted-syntax\nselector b\n[…]\nnested-callbacks, max-statements, and max-lines-per-function caps\nset just above today's largest functions to block future growth.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: convert .then to await, add ESLint style guards",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:34:50Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6ec4e832fd3212f9575a7a5efc6949b0c305f25a",
          "body": "getMessage tokens accept number and every placeholder is %s, so String()\nwrapping was a no-op. Pass numbers directly and drop the now-redundant\naffected alias in countsLine.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: drop redundant String() around getMessage tokens",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:24:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7332b5fab0535d67c3d23dc99b46e1500d45dd6e",
          "body": "Extend the named-message-method convention to this.error and this.confirm:\nerrorInvalid, errorMaxDeletes, errorFailed, and confirmDelete. jsonEnabled\nguards stay at the call site; messages.createError stays inline.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: wrap command errors and confirms in named methods",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:21:49Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "05303d9965dbf665e6440937d805c10376a39747",
          "body": "Every this.log(messages.getMessage(...)) call across the commands now goes\nthrough a named private method (logHeaderTitle, logHeaderOrg, logSummaryCounts,\n...) that takes raw token values. this.error/confirm paths are unchanged. The\nexport 'success' message is logExportSuccess to avoid SfCommand.logSuccess.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: wrap command messages in named log methods",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:19:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0c5afb3eea49c2f9450e08f4e0ead9d9d7bbac99",
          "body": "Enable the complexity rule (max 10) and split the four functions that\nexceeded it into cohesive helpers:\n- report.formatDiff -> collectBuckets + renderBucket\n- apply service run -> planForMode\n- plan command run -> logPlan\n- apply command run -> reportOutcome (ApplyResult now via the barrel)\n\nBehavior is unchanged. tsc and lint pass clean.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: cap cyclomatic complexity at 10 via eslint",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:11:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c97cbea66bfda6c73b38111953c4f85f85fe7e0e",
          "body": "Replace the sequential await-in-loop read with a parallel Promise.all over\nthe files, so the eslint-disable directive is no longer needed. Order and\ndedup semantics are unchanged.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: read files in parallel, drop no-await-in-loop disable",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:02:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a532175965195ec11a9bcffd6a2b9cd5b7ecde51",
          "body": "…lint\n\nAdd no-restricted-imports per-directory overrides (core purity, layer\ndirection, barrel-only cross-dir imports), id-length, and a\nno-restricted-syntax ban on === undefined. Existing code passes clean.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: enforce layering, barrels, id-length, and no-=== undefined via es…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T17:00:22Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ef17588d0c9650576d429c64c532e69bf2242dfb",
          "body": "Array literals built from more than one element (values or spreads) now go\nmultiline with a trailing comma. Enum-style literal lists and lookup-table\ntuple rows stay inline as single logical units.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "style: put multi-element array literals one per line",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:56:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "cb13e02f89518c7554222034d22bcfcc82a60d3f",
          "body": "PlanInput only wrapped a single mode field, so run() now takes mode directly.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: pass plan mode directly, drop PlanInput wrapper",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:51:44Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2fe9296bc78a33811c6569bf278c51041fba6914",
          "body": "Avoid call + immediate property access (countFindings(x).errors); bind the\ncounts to a variable first. Broaden the CLAUDE.md rule to cover plain calls.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: bind countFindings result before member access",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:49:20Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "68ab3f56e25959c7827d7ceda21c1ed10aaf7cca",
          "body": "fileSchema, userEntrySchema, ReportOptions, FindingLevel, and FindingCode\nare used only within their own file, so they no longer carry export per the\nno-internal-exports convention.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: drop export on internal-only core helpers",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:47:56Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "09f24d9a8cdd8f2b92dac88803be8e88dd49fd90",
          "body": "Capture the layering, barrel, service DI, code-style, testing, and\nworkflow guidelines established so far.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: add CLAUDE.md with project conventions",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:45:23Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a2d1b33c009bed92c1ccab11b8482e21f7f52fdc",
          "body": "Give the apply deletion-approval callback a named type alias so the\nport reads like the other injected dependencies at both ends.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: name the ConfirmDeletions port",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:41:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "239bfc95eaadd7e5c301fd6259455ec51aa81982",
          "body": "Service constructors now take only injected collaborators; per-invocation\ninputs (files, mode/config, assignments) move to run() parameters.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: inject only dependencies into service constructors",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:38:18Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5f177acb7ff6f93ba0919f2103850ec428bf9d2a",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "style: group external and local imports in ps commands",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:34:05Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4a0803289c9eec92434bd87a9922a47dea46a22f",
          "body": "Route external importers of src/adapters/ and src/services/adapters/\nthrough index.js barrels that re-export only the used symbol.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: add adapters barrels for imports",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:33:22Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0c94d145a36a17580c798aeb3bbdfc36216a03d1",
          "body": "Route external importers through index.js barrels that re-export only\nthe symbols used outside each layer, consolidating multiple per-file\nimports into one.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: add core/ and services/ barrels for imports",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-18T16:31:27Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "14d10acf782d9dae7e2b8ec5b1bdd1fa3472e94d",
          "body": null,
          "is_bot": false,
          "headline": "Improve plan output",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-15T20:53:04Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c8e676856a12f3eea209f8933d9a2dcfc4f17fcc",
          "body": "The login output is not consumed, so the human-readable form is fine.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: drop --json from the sfdx-url login",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T21:27:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "35e5680dfdaa92c8c5941c5c3fb7c6ec74fd8101",
          "body": "The publish workflow calls ci.yml via workflow_call as its release gate, but reusable workflows do not inherit the caller's secrets, so the test job's auth step got an empty SFDX_AUTH_URL and would fail on push-to-main and release. ci.yml now declares the secret under on.workflow_call.secrets and publish.yml passes it through explicitly.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: pass SFDX_AUTH_URL to the reusable ci workflow",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T21:25:30Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "10fab41ba7edfc10d547c834be70b5631c1a08f5",
          "body": "The test:only/test split existed to keep lint out of the test run for CI job parallelism. Renaming test:only to test (with no lint dependency) keeps that benefit with one fewer script: `npm test` compiles and runs vitest, while lint stays in the build job. CI's test job now runs `npm test`; the parallel build/test jobs are unchanged.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: rename test:only to test, drop its lint dependency",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T21:22:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "7279f03280810ed43a720f36914c3ef74b48d7f4",
          "body": "vitest now runs every test concurrently (sequence.concurrent), bounded by maxWorkers and maxConcurrency so the many `sf` subprocesses do not thrash the machine and trip the 30s timeout. The online export test writes a unique OS temp file per case (the OS reclaims it, no cleanup), so the concurrent cases never collide. projectRoot is demoted to a local const since nothing imports it anymore.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: run the whole suite concurrently with per-test temp files",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T21:15:18Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "2a6c5de3cadc70a61263ccafef9520a710a8e068",
          "body": "globalSetup now runs sf with cwd at the repo root and links/unlinks the plugin with `.` instead of an absolute path and plugin name. It always links (dropped the already-linked short-circuit) and unlinks on teardown. --no-install stays: without it `sf plugins link` runs a production install that prunes devDependencies and breaks the test run.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: link plugin from repo root via `sf plugins link .`",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T20:36:53Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c827ae69f52d574998d5f353455274471a6633dd",
          "body": "A black-box suite drives `sf ps export` against a real org and re-checks its output offline (export then check round-trip). The harness receives the org via PS_TARGET_ORG instead of authenticating: the caller (a local logged-in org, or the CI login step) provides it. global-setup.js loads a gitignored .env so PS_TARGET_ORG can live there locally, and CI authenticates from the SFDX_AUTH_URL secret and hands the alias to the tests.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: add real-org export test targeting PS_TARGET_ORG",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T20:27:08Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "653b467ac5b8e53777e61e40bc6195ded46ff1cc",
          "body": "The plugin only touches stable standard objects (PermissionSetAssignment,\nPermissionSetLicenseAssign, PermissionSet, PermissionSetGroup, User), whose\nshape and Collections-API behavior do not vary meaningfully across API\nversions, so pinning one bought no real reproducibility. Drop the flag from\napp\n[…]\n connection now uses the org's default\nAPI version. Removing it while on 0.x is the low-regret direction: adding it\nback later is non-breaking.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat!: remove --api-version flag from org commands",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T20:21:51Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "35ee1857ee1c2991319a1dee498e9bce6ee97652",
          "body": "The --watch/-w flag was documented for check, validate, and plan but no\nsuch flag exists in any command. Remove the USAGE tokens, FLAGS entries,\nand the explanatory paragraph so the docs match the implemented commands.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: remove undocumented --watch flag from README",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T20:08:11Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0e20da54e3363bd311c4d60f964e835e7f30151d",
          "body": null,
          "is_bot": false,
          "headline": "Ignore env files",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:58:01Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "42fd0ca7731e314c041d4088f2adbf1daabdc71f",
          "body": null,
          "is_bot": false,
          "headline": "Update readme",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:57:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "eece7aa44f12ccbb252385d6f808ce83b029f0d9",
          "body": "Replace org-required.test.js (which spanned plan, apply, and topic help) with\nper-command files, matching the one-file-per-command layout check.test.js\nalready used.\n\n- apply.test.js: invalid --mode, negative --max-deletes, --help.\n- plan.test.js: clean failure when the org can't be resolved, --help\n[…]\n gains its own --help test (asserts --file).\n- Drop the redundant `ps --help` topic test; each command's --help now\n  proves the command loads.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: split plugin tests one file per command",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:51:44Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "e3ef14d5020e74305c342899a81719b68a58b197",
          "body": "Remove the --strict assertion from the check suite temporarily; the sibling\nnon-strict test keeps warning coverage.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: drop the ps check --strict case for now",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:46:52Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "c59eed4298d7a34d5897d153c8b4d8c29ca7b972",
          "body": "Introduce a vitest suite that exercises the plugin the way a user runs it:\nspawn `sf ps ...` via execa and assert on stdout, stderr, and exit codes.\nOffline only, against local YAML fixtures, so it needs no org or secrets.\n\n- test/plugin/check.test.js: the fully-offline `ps check` (valid, warnings,\n\n[…]\nion so sf loads compiled lib.\n- Add execa; vitest config moved to JS; test:only now compiles first (wireit).\n- CI test job installs the sf CLI.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "test: add offline black-box plugin tests driving sf ps",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:43:35Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "afa3b6de13f0922354811cc47c79101f990b60e9",
          "body": "…ce-core-8.31.5\n\nfix(deps): Bump @salesforce/core from 8.6.4 to 8.31.5",
          "is_bot": false,
          "headline": "Merge pull request #5 from zaclummys/dependabot-npm_and_yarn-salesfor…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:41:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bc5891aa9b0640af1163472aacc5f51fb66be60c",
          "body": "…re-4.11.14\n\nfix(deps): Bump @oclif/core from 4.0.31 to 4.11.14",
          "is_bot": false,
          "headline": "Merge pull request #6 from zaclummys/dependabot-npm_and_yarn-oclif-co…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-04T19:41:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "58fe8f68bdc04b6b32a26ba020f6311615094e66",
          "body": "Bumps [@oclif/core](https://github.com/oclif/core) from 4.0.31 to 4.11.14.\n- [Release notes](https://github.com/oclif/core/releases)\n- [Changelog](https://github.com/oclif/core/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/oclif/core/compare/4.0.31...4.11.14)\n\n---\nupdated-dependencies:\n- dependency-name: \"@oclif/core\"\n  dependency-version: 4.11.14\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "fix(deps): Bump @oclif/core from 4.0.31 to 4.11.14",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-07-04T18:43:23Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8fb0c99194b6286a62a85cb8688606a3b408a28b",
          "body": "Bumps [@salesforce/core](https://github.com/forcedotcom/sfdx-core) from 8.6.4 to 8.31.5.\n- [Release notes](https://github.com/forcedotcom/sfdx-core/releases)\n- [Changelog](https://github.com/forcedotcom/sfdx-core/blob/main/CHANGELOG.md)\n- [Commits](https://github.com/forcedotcom/sfdx-core/compare/8.\n[…]\nependency-name: \"@salesforce/core\"\n  dependency-version: 8.31.5\n  dependency-type: direct:production\n  update-type: version-update:semver-minor\n...\n\nSigned-off-by: dependabot[bot] <support@github.com>",
          "is_bot": true,
          "headline": "fix(deps): Bump @salesforce/core from 8.6.4 to 8.31.5",
          "author_name": "dependabot[bot]",
          "author_login": "dependabot[bot]",
          "committed_at": "2026-07-04T18:43:01Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e9fbc1af19cc50922d8c4523f3a51e633ea1fd17",
          "body": "v4 targets the deprecated Node 20 runtime, which GitHub was forcing\nonto Node 24 with a warning annotation. v5 targets Node 24 natively.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: bump checkout and setup-node to v5",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T18:20:21Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "537ee8bc5c7f8a97f6b9e6495b16e2115f363abf",
          "body": "CI build broke because ESLint 10 needs jiti to load the TypeScript\neslint.config.ts, and jiti was never in the lockfile. Rename the\nconfig to plain-JS eslint.config.js (no jiti needed) and add the\nglobals and typescript-eslint packages it imports as devDependencies.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "build: convert eslint config to js and declare its deps",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T18:17:13Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "e7a4673489b6e425274bcb39c96e0bf5ede8094a",
          "body": "The repo switched to npm (package-lock.json), so update the CI and\npublish workflows to match: npm cache, npm ci, and npm run scripts.\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "ci: use npm instead of yarn in workflows",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T18:12:22Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "0570a864ed703e39d05878f079608c624eb1efa8",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: switch to npm, add wireit, inline tsconfig",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T18:08:45Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "ec5d55690bf77a695f549a6aa3a59c46c5bc018c",
          "body": "…onfig\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: remove husky, dev-scripts, and oclif; migrate to flat ESLint c…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T18:05:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "54723d44facade4444a0540a983356e03352b8c0",
          "body": "…ogic\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: extract ResolutionService to eliminate duplicated resolve l…",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T17:44:02Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "fcb0c9275f91a009cf481f09d829701f14637d18",
          "body": "Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: prefer two loops over one across core and services",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-07-01T17:35:45Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "58d496cd53f35dae9a87e8be95bd8888490b0782",
          "body": "A run now performs three operations, not two. Add an \"Updates\nexpirations\" column to the mode table and note that updates ride with\nthe additive half, never revoking access.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: reflect expiration updates in the Modes section",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T20:01:00Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "85dc7bd8557427170f970ca43fc5cf82815d2d8c",
          "body": "A real export (permissions.yml with live org usernames and ids) is easy\nto stage by accident. Ignore /permissions.yml and /permissions/ so local\nscratch never lands in the repo.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: gitignore local permission files",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:53:06Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "a14940d23cb9ec2d0403b969060ae1299c069b0c",
          "body": "Use truthiness checks instead of explicit undefined comparisons:\n!list in normalize and !left || !right in diff, where the values are\nnever other-falsy. parse keeps the null-or-undefined distinction with\n== null so a scalar document still reaches schema validation.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: drop === undefined in favor of !x / == null",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:52:34Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d89e5cad45e6d3e04f60697ce298603909a48e0c",
          "body": "Give each finding code a named constructor in finding.ts (yamlError,\nschemaError, dupTargetWarning, userNotFoundError, and so on) that owns\nits code and wording, and make the generic error() and warning()\nprivate to the module. Call sites in parse, schema, normalize, resolve,\nand load now build findings by name instead of by code and string.\nnoFilesError moves here too, alongside the rest.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: name the finding constructors, hide error/warning",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:47:12Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4d636d5f46dd07319e5a30133f06b69f356cb91f",
          "body": "Permission set and permission set group entries accept an optional\nexpiration (an ISO 8601 datetime) in a new object form alongside the\nexisting string form. Permission set licenses cannot expire, so the\nobject form is rejected there.\n\nThe diff gains a toUpdate bucket. A changed expiration on an\nalr\n[…]\nxpiration in the org.\n\nAlso remove the ps info command and extract the NO_FILES finding into\na noFilesError(patterns) constructor.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: support permission set assignment expiration",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:41:19Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "0bc0bcb65ea9f288dbe34601090af3a57c0fc624",
          "body": "Read-only preview of the apply reconcile: load, resolve, fetch current\nstate, and diff, stopping before any DML. Shows the full change set\n(adds and would-be removes) regardless of mode, surfacing what the\nchosen mode won't act on as drift.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add ps plan command",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:10:37Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "bc7dbcd16c1f9303358d1fb268103bfdd276aa2e",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: move OrgClient port to services/adapters",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T19:10:25Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9d0435c705062b2e74eb6d60a66e670aff373f1e",
          "body": "Align the npm description with the GitHub About blurb and replace the\ngeneric scaffold keywords with topical, search-friendly terms\n(permission sets, access management, user provisioning, GitOps).\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: sharpen npm description and keywords for discoverability",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T18:05:14Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "f9de23244de1afb68c2191b943e22099c21b9129",
          "body": "Reconcile a target org to the YAML: load, resolve every reference to an\norg id, diff against current state, then add and/or remove per --mode\n(additive, destructive, sync). Deletions are capped by --max-deletes and\ngated by a confirmation; --dry-run previews the plan. DML runs through\nthe sObject Co\n[…]\nthe format/count helpers\npulled out of report and model. Findings are now built through the\nfactories rather than inline literals.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add ps apply command",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T16:53:22Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "85b2b14fdb8ca699834767f5c08d6e956927982f",
          "body": "Snapshot an org's current permission set, group, and license\nassignments to a single user-keyed YAML file that round-trips back\nthrough check, validate, plan, and apply.\n\nAdds a serialize core module (the inverse of normalize), a\nlistAssignments port method implemented with SOQL in the adapter, and\nthe ExportService that fetches, serializes, and writes the file.\nProfile-owned permission sets and inactive users are skipped.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "feat: add ps export command",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T16:23:47Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "3504b623f4ebccea3e70570ac081bbf96195cbdb",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: place Validations after Modes in README",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T15:16:57Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "31e7757e7175d1806798c125883bb968e40b4101",
          "body": "Add a stability badge, an under-development callout, and an explicit\n0.x policy note in the Versioning section, and fill the empty npm\ndescription, so consumers know to expect breaking changes until v1.0.0.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: flag 0.x stability and breaking-change policy",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-29T13:14:15Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "29d9e79935cb73494362927c5e9f09d4de31cded",
          "body": "Rename the SCREAMING_SNAKE module-level constants to camelCase (kindLabels,\nkinds, targetObjects, kindKeys). In countFindings, bind the filtered arrays\nto locals instead of reading .length directly off the filter call.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: rename module constants to camelCase",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:57:24Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "5d7a05743ae9781a8e7550652034aa0e560535e1",
          "body": "The OrgClient port is declared in services (not adapters), and resolve no\nlonger plans SOQL: the adapter owns it. Update the architecture section and\nthe resolve module row to match.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: correct architecture for services-owned port and SOQL location",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:32:56Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "4e4f5f61acb1f8c28771f0b082219f71306f7b9c",
          "body": "A port is owned by its consumer, not by the adapter that implements it\n(dependency inversion). OrgClient now lives in services/org-client.ts;\nConnectionOrgClient (renamed from adapters/org-client.ts) implements it.\nServices no longer import adapters; the adapter depends inward on the\nservices-owned port.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: move OrgClient port from adapters to services",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:32:51Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "9e5c85133ac02a81946ba416c43fbf4209c1d2c2",
          "body": "Introduce an adapters layer so core stays persistence-ignorant. The\nOrgClient port exposes domain lookups (findUsers, findTargets) and\nConnectionOrgClient owns all Salesforce detail: the SOQL, the\nkind-to-SObject/field map, autoFetchQuery, and mapping records to domain\nshapes. core/resolve now holds\n[…]\n with no SOQL or SObject/field names. The service depends on\nthe port, not @salesforce/core, so it unit-tests with a trivial fake.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "refactor: move org I/O behind an OrgClient adapter port",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:20:20Z",
          "body_truncated": true,
          "is_coding_agent": true
        },
        {
          "oid": "6d7ef166a72339e78f00e69ffc4f8a1f791fc5ef",
          "body": "Use interface for class-implemented contracts (e.g. the OrgClient port) and\ntype for data shapes, so neither is forced. The salesforce config defaults\nthe rule to \"type\"; turn it off and let the author choose by context.\n\nCo-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "chore: disable consistent-type-definitions lint rule",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:20:16Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "d853d640db0c9bc0242cd8a283ddb2f83ddf4dc4",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: document adapters layer and resolve module in architecture",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:17:48Z",
          "body_truncated": false,
          "is_coding_agent": true
        },
        {
          "oid": "48dea5a3e9dde7fb066bf05382652afa1b3af803",
          "body": "Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>",
          "is_bot": false,
          "headline": "docs: drop inline example from duplicate-target validation row",
          "author_name": "Isaac Ferreira",
          "author_login": "zaclummys",
          "committed_at": "2026-06-28T22:03:36Z",
          "body_truncated": false,
          "is_coding_agent": true
        }
      ],
      "releases_count": 5,
      "commits_last_year": 123,
      "latest_release_at": "2026-07-18T19:13:11Z",
      "latest_release_tag": "v0.4.0",
      "releases_from_tags": false,
      "days_since_last_push": 5,
      "active_weeks_last_year": 4,
      "days_since_latest_release": 5,
      "mean_days_between_releases": 5
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 57,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "sf-plugin-permission-sets",
          "exists": true,
          "license": "BSD-3-Clause",
          "keywords": [
            "salesforce",
            "sf-plugin",
            "sfdx-plugin",
            "permission-sets",
            "permission-set-assignment",
            "permission-set-group",
            "access-management",
            "user-provisioning",
            "gitops",
            "declarative"
          ],
          "ecosystem": "npm",
          "matches_repo": true,
          "registry_url": "https://www.npmjs.com/package/sf-plugin-permission-sets",
          "is_deprecated": false,
          "latest_version": "0.4.0",
          "repository_url": "https://github.com/zaclummys/sf-plugin-permission-sets",
          "versions_count": 57,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": 1,
          "monthly_downloads": 8679,
          "first_published_at": "2026-06-28T18:37:56.916000Z",
          "latest_published_at": "2026-07-18T19:15:07.281000Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 5
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 1,
      "watchers": 0,
      "fork_history": {
        "days": [],
        "complete": true,
        "collected": 0,
        "total_forks": 0
      },
      "star_history": null,
      "open_issues_and_prs": 0
    },
    "ai_readiness": {
      "has_nix": false,
      "example_dirs": [],
      "has_llms_txt": false,
      "has_dockerfile": false,
      "has_mcp_signal": false,
      "bootstrap_files": [],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [
        "tsconfig.json"
      ],
      "toolchain_manifests": [],
      "largest_source_bytes": 17998,
      "source_files_sampled": 41,
      "oversized_source_files": 0,
      "agent_instruction_files": [
        "CLAUDE.md"
      ],
      "agent_instruction_max_bytes": 6263
    },
    "dependencies": {
      "manifests": [
        "package.json"
      ],
      "advisories": {
        "error": null,
        "scope": "published_package",
        "source": "osv",
        "findings": [],
        "collected": true,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 288,
        "malicious_count": 0,
        "assessed_package": "npm:sf-plugin-permission-sets@0.4.0",
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "npm"
      ],
      "dependencies": [
        {
          "name": "@oclif/core",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^4"
        },
        {
          "name": "@salesforce/core",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^8"
        },
        {
          "name": "@salesforce/sf-plugins-core",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^12"
        },
        {
          "name": "globby",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^16"
        },
        {
          "name": "yaml",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^2"
        },
        {
          "name": "zod",
          "manifest": "package.json",
          "ecosystem": "npm",
          "version_constraint": "^4"
        }
      ],
      "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": 3,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 9
      },
      "bus_factor": 1,
      "bot_contributors": 1,
      "top_contributors": [
        {
          "type": "User",
          "login": "zaclummys",
          "commits": 121,
          "avatar_url": "https://avatars.githubusercontent.com/u/17896485?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "publish.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [
        "eslint.config.js"
      ],
      "has_editorconfig": true,
      "has_linter_config": true,
      "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": 10,
            "reason": "1 out of 1 merged PRs checked by a CI test -- score normalized to 10",
            "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/27 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": 10,
            "reason": "update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 10,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 0,
            "reason": "project was created within the last 90 days. Please review its contents carefully",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": 10,
            "reason": "packaging workflow detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 2,
            "reason": "dependency not pinned by hash detected -- score normalized to 2",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "SAST tool is not run on all commits -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": null,
            "reason": "no releases found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#signed-releases"
          },
          {
            "name": "Token-Permissions",
            "score": 0,
            "reason": "detected GitHub workflow tokens with excessive permissions",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#token-permissions"
          },
          {
            "name": "Vulnerabilities",
            "score": 9,
            "reason": "1 existing vulnerabilities detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#vulnerabilities"
          }
        ],
        "commit": "41e66be1eed3fad7344af0c8333e8b58cb822361",
        "ran_at": "2026-07-24T12:30:07Z",
        "aggregate_score": 5,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": true,
      "has_dependabot_config": true
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-18T23:34:33Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": "2026-07-18T22:17:03Z",
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/zaclummys/sf-plugin-permission-sets",
    "host": "github.com",
    "name": "sf-plugin-permission-sets",
    "owner": "zaclummys"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "moderate",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 57,
      "inputs": {
        "security": 60,
        "vitality": 74,
        "community": 34,
        "governance": 42,
        "engineering": 74
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 74,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 57,
            "inputs": {
              "commits_last_year": 123,
              "human_commit_share": 0.98,
              "days_since_last_push": 5,
              "active_weeks_last_year": 4
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 5 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 5
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "4/52 weeks with commits",
                "points": 2.8,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 4
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "123 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 123
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "releases_count": 5,
              "latest_release_tag": "v0.4.0",
              "releases_from_tags": false,
              "days_since_latest_release": 5,
              "mean_days_between_releases": 5
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "5 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "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 ~5 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 5
                    }
                  }
                ],
                "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": 34,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 1,
              "watchers": 0,
              "growth_state": "unverified",
              "growth_factor_pct": 100,
              "growth_unverified_reason": "no_history"
            },
            "components": [
              {
                "key": "stars",
                "name": "Stars",
                "detail": "1 stars",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "stars",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 60
              },
              {
                "key": "forks",
                "name": "Forks",
                "detail": "0 forks",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "forks",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "watchers",
                "name": "Watchers",
                "detail": "0 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 0
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "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 (BSD-3-Clause)",
                "points": 22.5,
                "status": "met",
                "details": [
                  {
                    "code": "license_standard",
                    "params": {}
                  },
                  {
                    "code": "license_spdx",
                    "params": {
                      "spdx": "BSD-3-Clause"
                    }
                  }
                ],
                "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": 66,
            "inputs": {
              "packages": [
                "sf-plugin-permission-sets"
              ],
              "dependents": null,
              "ecosystems": "npm",
              "total_downloads": null,
              "monthly_downloads": 8679
            },
            "components": [
              {
                "key": "monthly_downloads",
                "name": "Monthly downloads",
                "detail": "8,679 downloads/month across npm",
                "points": 52.5,
                "status": "partial",
                "details": [
                  {
                    "code": "downloads_monthly",
                    "params": {
                      "count": 8679,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "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": 42,
        "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. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 18,
            "inputs": {
              "merged_prs": 3,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 9
            },
            "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": "3/12 decided PRs merged",
                "points": 9.6,
                "status": "partial",
                "details": [
                  {
                    "code": "decided_prs_merged",
                    "params": {
                      "merged": 3,
                      "decided": 12
                    }
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/27 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "moderate",
            "name": "Ownership & stewardship",
            "note": "Excluded from scoring (no data or not applicable): Verified domain. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "verified_domain"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 53,
            "inputs": {
              "followers": 20,
              "owner_type": "User",
              "is_verified": null,
              "owner_login": "zaclummys",
              "public_repos": 28,
              "account_age_days": 3781
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "personal (user) account",
                "points": 10,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_personal",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": "not applicable to user accounts",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "not_applicable_to_user_accounts",
                    "params": {}
                  }
                ],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "20 followers of zaclummys",
                "points": 9.5,
                "status": "partial",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 20,
                      "login": "zaclummys"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "28 public repos, account ~10 yr old",
                "points": 22.6,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 28
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 10
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "sf-plugin-permission-sets"
              ],
              "ecosystems": "npm",
              "any_deprecated": false,
              "min_days_since_publish": 5
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on npm",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "npm"
                    }
                  }
                ],
                "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": "57 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 57
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "good",
        "name": "Engineering Quality",
        "value": 74,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "excellent",
            "name": "Engineering practices",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": true,
              "has_linter_config": true,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "2 workflow(s)",
                "points": 24,
                "status": "met",
                "details": [
                  {
                    "code": "ci_workflows",
                    "params": {
                      "count": 2
                    }
                  }
                ],
                "max_points": 24
              },
              {
                "key": "tests_present",
                "name": "Tests present",
                "detail": null,
                "points": 24,
                "status": "met",
                "details": [],
                "max_points": 24
              },
              {
                "key": "linter_config",
                "name": "Linter config",
                "detail": "eslint.config.js",
                "points": 16,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "eslint.config.js"
                    }
                  }
                ],
                "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": 6.4,
                "status": "met",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "1 out of 1 merged PRs checked by a CI test -- score normalized to 10",
                "points": 20,
                "status": "met",
                "details": [],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "moderate",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 50,
            "inputs": {
              "topics": [
                "access-management",
                "gitops",
                "permission-sets",
                "salesforce",
                "sfdx-plugin"
              ],
              "has_wiki": false,
              "homepage": null,
              "has_readme": true,
              "has_docs_dir": false,
              "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": 0,
                "status": "missed",
                "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": "5 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 5
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "moderate",
        "name": "Security",
        "value": 60,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "moderate",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): Signed-Releases. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "signed_releases"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 50,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 17,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 1,
              "scorecard_aggregate": 5
            },
            "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": "1 out of 1 merged PRs checked by a CI test -- score normalized to 10",
                "points": 2.5,
                "status": "met",
                "details": [],
                "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/27 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": "update tool detected",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.5,
                "status": "met",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "project was created within the last 90 days. Please review its contents carefully",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 2",
                "points": 1,
                "status": "partial",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "SAST tool is not run on all commits -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "no releases found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 7.5
              },
              {
                "key": "token_permissions",
                "name": "Token-Permissions",
                "detail": "detected GitHub workflow tokens with excessive permissions",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "vulnerabilities",
                "name": "Vulnerabilities",
                "detail": "1 existing vulnerabilities detected",
                "points": 6.8,
                "status": "partial",
                "details": [],
                "max_points": 7.5
              }
            ]
          },
          {
            "key": "dependency_advisories",
            "band": "excellent",
            "name": "Dependency advisories",
            "note": "Excluded from scoring (no data or not applicable): No advisories left outstanding. Remaining weights renormalized. Matched the npm:sf-plugin-permission-sets@0.4.0 runtime dependency closure — what installing the published package pulls in — 288 packages. Reachability is not analyzed.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "no_advisories_left_outstanding"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              },
              {
                "code": "advisories_scope_published",
                "params": {
                  "package": "npm:sf-plugin-permission-sets@0.4.0",
                  "assessed": 288
                }
              },
              {
                "code": "advisories_reachability",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "source": "osv",
              "advisories": 0,
              "affected_packages": 0,
              "assessed_packages": 288,
              "unassessed_packages": 0,
              "affected_by_severity": "none",
              "direct_affected_packages": 0
            },
            "components": [
              {
                "key": "direct_dependencies_free_of_known_advisories",
                "name": "Direct dependencies free of known advisories",
                "detail": "no direct dependency carries a known advisory",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "no_direct_advisories",
                    "params": {}
                  }
                ],
                "max_points": 35
              },
              {
                "key": "indirect_dependencies_free_of_known_advisories",
                "name": "Indirect dependencies free of known advisories",
                "detail": "no indirect dependency carries a known advisory",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "no_indirect_advisories",
                    "params": {}
                  }
                ],
                "max_points": 25
              },
              {
                "key": "no_advisories_left_outstanding",
                "name": "No advisories left outstanding",
                "detail": "no advisory carries a publication date",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "advisories_no_publication_date",
                    "params": {}
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "malicious_dependencies",
            "band": "excellent",
            "name": "Malicious dependencies",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "source": "osv",
              "meaning": "reported as a malicious package by the OpenSSF corpus; the remedy is removal or moving off the compromised name, never an upgrade of the same artifact. Versions the registry has since pulled are listed but not scored",
              "packages": [],
              "red_flag": false,
              "assessed_packages": 288,
              "malicious_packages": 0,
              "direct_malicious_packages": 0,
              "withdrawn_malicious_packages": 0,
              "installable_malicious_packages": 0
            },
            "components": [
              {
                "key": "no_dependency_reported_as_a_malicious_package",
                "name": "No dependency reported as a malicious package",
                "detail": "no dependency is reported as a malicious package",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "no_malicious_dependencies",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          },
          {
            "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": 2
            },
            "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": 82,
        "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.888,
              "agent_instruction_files": [
                "CLAUDE.md"
              ],
              "agent_instruction_max_bytes": 6263
            },
            "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": "87 of 98 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 87,
                      "sampled": 98
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 74,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "package-lock.json"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [],
              "has_devcontainer": false,
              "has_linter_config": true,
              "typecheck_configs": [
                "tsconfig.json"
              ],
              "agent_commit_share": 0.84,
              "toolchain_manifests": [],
              "dependency_bot_commit_share": 0.02
            },
            "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": "eslint.config.js",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "eslint.config.js"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "tsconfig.json",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "tsconfig.json"
                    }
                  }
                ],
                "max_points": 11
              },
              {
                "key": "reproducible_environment",
                "name": "Reproducible environment",
                "detail": "lockfile",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "lockfile"
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "demonstrated_agent_practice",
                "name": "Demonstrated agent practice",
                "detail": "84 of the last 100 commits agent-authored or agent-credited",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "agent_authored_commits",
                    "params": {
                      "count": 84,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "2 of the last 100 commits are automated dependency updates",
                "points": 8,
                "status": "met",
                "details": [
                  {
                    "code": "dependency_bot_commits",
                    "params": {
                      "count": 2,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "dependency not pinned by hash detected -- score normalized to 2",
                "points": 2,
                "status": "partial",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "primary_language": "TypeScript",
              "largest_source_bytes": 17998,
              "source_files_sampled": 41,
              "oversized_source_files": 0
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "TypeScript (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "TypeScript"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "0/41 source files over 60KB",
                "points": 55,
                "status": "met",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 41,
                      "oversized": 0
                    }
                  }
                ],
                "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"
  ],
  "report_type": "repository",
  "generated_at": "2026-07-24T12:30:14.842345Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/z/zaclummys/sf-plugin-permission-sets.svg",
  "full_name": "zaclummys/sf-plugin-permission-sets",
  "license_state": "standard",
  "license_spdx": "BSD-3-Clause"
}

Оцінки — це сигнали, а не гарантії. Вони відображають публічно видимі практики на GitHub — це не аудит коду й не гарантія безпеки.

Відсутні дані виключаються, а ваги перенормовуються — нуль за відсутність ніколи не ставиться. Методологія версіонована й відкрита: метрики v1.13.0, схема v0.27.0 — повна методологія · вікі метрик.

Як окремий результат виглядає на тлі всього реєстру: сукупна статистикаnpm.