Public record
Software health reportschema 0.27.0 · metrics 1.13.0 · 2026-07-28 06:42 UTC

runos-official / cli

RunOS command-line interface: manage clusters, deploy apps, and run services on your RunOS platform. Source-available (Elastic License 2.0).

GoCustom license★ 1 star⑂ 0 forkssince Dec 2025View on GitHub ↗

runos-official/cli holds a health index of 49 out of 100, placing it in the At risk band. It scores highest on Vitality (76/100) and lowest on Community & Adoption (21/100). It was last updated 11 days ago. A single contributor accounts for most of its recent work.

49
overall / 100
At risk

Software health index

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

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

Score profile

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

Ownership

RunOSOrganization
0 followers9 public repossince Jun 2025

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

Package ecosystems

RegistryPackageVersionDownloads / moVersionsLast publish
Gogithub.com/runos-official/cliv1.12.2-4811 days ago

Metrics by category

Vitality

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

76Good · 22% of overall
How it's scored
28.8/36Push recency — last push 11 days ago
9.7/36Commit cadence — 14/52 weeks with commits
18/18Commit volume — 154 commits in the last year
10/10OpenSSF Scorecard: Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
Inputs used
commits_last_year154
human_commit_share1
days_since_last_push11
active_weeks_last_year14
How it's scored
27/27Ships releases — 31 releases published
36/36Release recency — latest release 11 days ago
27/27Release cadence — a release every ~1.8 days
0/10OpenSSF Scorecard: Signed-Releases — Project has not signed or included provenance with any releases.
Inputs used
releases_count31
latest_release_tagv1.12.2
releases_from_tagsno
days_since_latest_release11
mean_days_between_releases1.8

Community & Adoption

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

21Critical · 18% of overall
How it's scored
0/60Stars — 1 stars
0/25Forks — 0 forks
0/15Watchers — 1 watchers
Inputs used
forks0
stars1
watchers1
growth_stateunverified
growth_factor_pct100
growth_unverified_reasonno_history
How it's scored
22.5/22.5README
16.9/22.5License — license file present, not a recognized license
0/18CONTRIBUTING guide
0/13.5Code of conduct
0/7.2Issue template
0/6.3PR template
Inputs used
has_readmeyes
has_licenseyes
has_contributingno
has_issue_templateno
has_code_of_conductno
has_pull_request_templateno

Sustainability & Governance

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

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

Engineering Quality

Are baseline engineering and documentation practices in place?

66Moderate · 20% of overall
How it's scored
24/24CI workflows — 2 workflow(s)
24/24Tests present
0/16Linter config
0/9.6Pre-commit hooks
0/6.4.editorconfig
0/20OpenSSF Scorecard: CI-Tests — no data
Inputs used
has_ciyes
has_testsyes
has_editorconfigno
has_linter_configno
has_precommit_configno
Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.
How it's scored
30/30README
0/25Documentation directory
15/15Documentation / homepage site — https://runos.com
10/10Repository description
10/10Topics — 4 topics
10/10Wiki
Inputs used
topicscli, kubernetes, runos, source-available
has_wikiyes
homepagehttps://runos.com
has_readmeyes
has_docs_dirno
has_descriptionyes

Security

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

45At risk · 16% of overall
How it's scored
7.5/7.5Binary-Artifacts — no binaries found in the repo
0/7.5Branch-Protection — branch protection not enabled on development/release branches
0/2.5CI-Tests — no data
0/2.5CII-Best-Practices — no effort to earn an OpenSSF best practices badge detected
0/7.5Code-Review — Found 0/30 approved changesets -- score normalized to 0
0/2.5Contributors — project has 0 contributing companies or organizations -- score normalized to 0
10/10Dangerous-Workflow — no dangerous workflow patterns detected
0/7.5Dependency-Update-Tool — no update tool detected
0/5Fuzzing — project is not fuzzed
2.2/2.5License — license file detected
7.5/7.5Maintained — 30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
0/5Packaging — no data
5/5Pinned-Dependencies — all dependencies are pinned
0/5SAST — no SAST tool detected
5/5Security-Policy — security policy file detected
0/7.5Signed-Releases — Project has not signed or included provenance with any releases.
0/7.5Token-Permissions — detected GitHub workflow tokens with excessive permissions
6.8/7.5Vulnerabilities — 1 existing vulnerabilities detected
Inputs used
sourceopenssf_scorecard
checks_evaluated16
scorecard_versionv5.5.0
checks_inconclusive2
scorecard_aggregate4.5
Excluded from scoring (no data or not applicable): ci_tests, packaging. Remaining weights renormalized.

AI Readiness

How well is the repo equipped to be developed and maintained with AI coding agents? An independent, experimental badge — weight 0.0, so it is surfaced on its own and does not affect the overall health score.

64Moderate · 0% of overall
How it's scored
0/45Agent instructions — no CLAUDE.md / AGENTS.md / editor rules
0/15Machine-readable docs (llms.txt)
40/40Legible commit history — 98 of 100 human commits state their intent (structured subject or explanatory body)
Inputs used
has_llms_txtno
legible_history_share0.98
agent_instruction_files
agent_instruction_max_bytes
How it's scored
18/18One-command bootstrap — Makefile
22/22Automated tests
0/11Lint / format config
11/11Static type checking — Go (statically typed)
10/10Reproducible environment — lockfile
0/10Demonstrated agent practice — no agent-authored commits among the last 100
0/8Automated maintenance — no automated dependency updates observed
10/10OpenSSF Scorecard: Pinned-Dependencies — all dependencies are pinned
Inputs used
has_nixno
has_testsyes
lockfilesgo.sum
has_dockerfileno
typed_languageyes
bootstrap_filesMakefile
has_devcontainerno
has_linter_configno
typecheck_configs
agent_commit_share0
toolchain_manifestsgo.mod
dependency_bot_commit_share0
How it's scored
45/45Type-checkable code — Go (statically typed)
51.3/55Manageable file sizes — 9/133 source files over 60KB
Inputs used
primary_languageGo
largest_source_bytes95,672
source_files_sampled133
oversized_source_files9

Key facts

1GitHub stars
1contributors
154commits, last 12 months
11days since last push
31releases
1bus factor
0open issues
Gopackage ecosystems

Data collection warnings

  • Star history unavailable: GitHub GraphQL error: Resource not accessible by personal access token
  • GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository

More detail

OpenSSF Scorecard 4.5 / 10
4.5aggregate

Independent, tool-agnostic security assessment from the open-source OpenSSF Scorecard. Each check rewards a security practice, not a specific vendor's tool. Checks Scorecard could not determine are marked n/a and excluded from the security score (never counted as zero).Scorecard v5.5.0 · 2026-07-28 06:42 UTC

10Binary-Artifactsno binaries found in the repo
0Branch-Protectionbranch protection not enabled on development/release branches
n/aCI-Testsno pull request found
0CII-Best-Practicesno effort to earn an OpenSSF best practices badge detected
0Code-ReviewFound 0/30 approved changesets -- score normalized to 0
0Contributorsproject has 0 contributing companies or organizations -- score normalized to 0
10Dangerous-Workflowno dangerous workflow patterns detected
0Dependency-Update-Toolno update tool detected
0Fuzzingproject is not fuzzed
9Licenselicense file detected
10Maintained30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
n/aPackagingpackaging workflow not detected
10Pinned-Dependenciesall dependencies are pinned
0SASTno SAST tool detected
10Security-Policysecurity policy file detected
0Signed-ReleasesProject has not signed or included provenance with any releases.
0Token-Permissionsdetected GitHub workflow tokens with excessive permissions
9Vulnerabilities1 existing vulnerabilities detected
Direct dependencies 5
RegistryPackageVersion constraintManifest
Gogithub.com/pmezard/go-difflibv1.0.0go.mod
Gogithub.com/spf13/cobrav1.10.1go.mod
Gogithub.com/spf13/pflagv1.0.9go.mod
Gogolang.org/x/termv0.41.0go.mod
Gogopkg.in/yaml.v3v3.0.1go.mod
All dependencies not collected

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

Raw JSON report machine-readable
{
  "data": {
    "repo": {
      "topics": [
        "cli",
        "kubernetes",
        "runos",
        "source-available"
      ],
      "is_fork": false,
      "size_kb": 4613,
      "has_wiki": true,
      "homepage": "https://runos.com",
      "languages": {
        "Go": 2000340,
        "Shell": 13194,
        "Makefile": 2705
      },
      "pushed_at": "2026-07-16T19:08:17Z",
      "created_at": "2025-12-23T12:50:01Z",
      "owner_type": "Organization",
      "updated_at": "2026-07-16T19:08:25Z",
      "description": "RunOS command-line interface: manage clusters, deploy apps, and run services on your RunOS platform. Source-available (Elastic License 2.0).",
      "is_archived": false,
      "is_disabled": false,
      "license_spdx": null,
      "default_branch": "main",
      "license_spdx_raw": "NOASSERTION",
      "primary_language": "Go",
      "significant_languages": [
        "Go"
      ]
    },
    "owner": {
      "blog": "https://runos.com",
      "name": "RunOS",
      "type": "Organization",
      "login": "runos-official",
      "company": null,
      "location": "United States of America",
      "followers": 0,
      "avatar_url": "https://avatars.githubusercontent.com/u/214831878?v=4",
      "created_at": "2025-06-04T12:44:53Z",
      "is_verified": null,
      "public_repos": 9,
      "account_age_days": 418
    },
    "license": {
      "state": "custom",
      "spdx_id": null,
      "raw_spdx": "NOASSERTION",
      "file_present": true,
      "scorecard_found": true,
      "profile_has_license": true
    },
    "activity": {
      "releases": [
        {
          "tag": "v1.12.2",
          "kind": "patch",
          "published_at": "2026-07-16T18:50:56Z"
        },
        {
          "tag": "v1.12.2-rc.1",
          "kind": "prerelease",
          "published_at": "2026-07-16T18:43:32Z"
        },
        {
          "tag": "v1.12.1",
          "kind": "patch",
          "published_at": "2026-07-10T08:22:44Z"
        },
        {
          "tag": "v1.12.1-rc.1",
          "kind": "prerelease",
          "published_at": "2026-07-10T08:18:57Z"
        },
        {
          "tag": "v1.12.0",
          "kind": "minor",
          "published_at": "2026-07-08T19:47:03Z"
        },
        {
          "tag": "v1.12.0-rc.1",
          "kind": "prerelease",
          "published_at": "2026-07-08T07:59:31Z"
        },
        {
          "tag": "v1.11.3",
          "kind": "patch",
          "published_at": "2026-07-06T10:34:50Z"
        },
        {
          "tag": "v1.11.3-rc.1",
          "kind": "prerelease",
          "published_at": "2026-07-06T10:31:18Z"
        },
        {
          "tag": "v1.11.2",
          "kind": "patch",
          "published_at": "2026-06-30T12:02:17Z"
        },
        {
          "tag": "v1.11.2-rc.1",
          "kind": "prerelease",
          "published_at": "2026-06-30T11:47:47Z"
        },
        {
          "tag": "v1.11.1",
          "kind": "patch",
          "published_at": "2026-06-29T18:43:03Z"
        },
        {
          "tag": "v1.11.0",
          "kind": "minor",
          "published_at": "2026-06-18T07:57:24Z"
        },
        {
          "tag": "v1.10.1",
          "kind": "patch",
          "published_at": "2026-06-17T12:54:23Z"
        },
        {
          "tag": "v1.10.0",
          "kind": "minor",
          "published_at": "2026-06-11T10:30:13Z"
        },
        {
          "tag": "v1.9.0",
          "kind": "minor",
          "published_at": "2026-06-09T18:19:11Z"
        },
        {
          "tag": "v1.8.0",
          "kind": "minor",
          "published_at": "2026-06-08T14:40:53Z"
        },
        {
          "tag": "v1.7.5",
          "kind": "patch",
          "published_at": "2026-06-08T11:00:11Z"
        },
        {
          "tag": "v1.7.4",
          "kind": "patch",
          "published_at": "2026-06-08T07:20:46Z"
        },
        {
          "tag": "v1.7.3",
          "kind": "patch",
          "published_at": "2026-06-07T16:30:31Z"
        },
        {
          "tag": "v1.7.2",
          "kind": "patch",
          "published_at": "2026-06-06T16:33:39Z"
        },
        {
          "tag": "v1.7.1",
          "kind": "patch",
          "published_at": "2026-06-06T13:43:29Z"
        },
        {
          "tag": "v1.7.0",
          "kind": "minor",
          "published_at": "2026-06-06T11:14:07Z"
        },
        {
          "tag": "v1.6.0",
          "kind": "minor",
          "published_at": "2026-06-05T10:05:30Z"
        },
        {
          "tag": "v1.5.0",
          "kind": "minor",
          "published_at": "2026-06-04T17:23:30Z"
        },
        {
          "tag": "v1.4.2",
          "kind": "patch",
          "published_at": "2026-06-03T16:38:30Z"
        },
        {
          "tag": "v1.4.1",
          "kind": "patch",
          "published_at": "2026-06-03T09:17:13Z"
        },
        {
          "tag": "v1.4.0",
          "kind": "minor",
          "published_at": "2026-06-03T08:18:14Z"
        },
        {
          "tag": "v1.3.0",
          "kind": "minor",
          "published_at": "2026-06-03T08:18:23Z"
        },
        {
          "tag": "v1.2.0",
          "kind": "minor",
          "published_at": "2026-06-03T08:18:10Z"
        },
        {
          "tag": "v1.1.0",
          "kind": "minor",
          "published_at": "2026-06-01T15:38:38Z"
        },
        {
          "tag": "v1.0.0",
          "kind": "major",
          "published_at": "2026-05-31T17:11:23Z"
        }
      ],
      "recent_commits": [
        {
          "oid": "8481c009241283058aa3a22aa1da703995385ce2",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.12.2 (apps_sync allowDrop)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-07-16T18:40:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ac56a34dc1fc9d26459f0b0876fead8c7d1d5624",
          "body": "apps_sync is a declarative full replacement computed from local files, so keys\nthe server has but the local set lacks are intentional deletions (already in the\nsync plan's Remove list). Conductor's partial-drop guard (now live on prod as\n1.9.0) would otherwise 400 those legitimate removals; the guard targets an\naccidental partial `set`, not this path. Sets allowDrop on ReplaceSecretEnvVars\nand UpdateSecrets.",
          "is_bot": false,
          "headline": "fix(apps): send allowDrop on apps_sync secret/env full-replace",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-07-16T18:40:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a9f94b1b960fd3029383ea28d65c431ec9c7fc9d",
          "body": "runos status now enriches its output when signed in: company name and\nwebsite from GET /:aid/account/profile, and the default cluster's\ndisplay name resolved via GET /:aid/clusters (rendered as 'name (cid)',\nor '(not found on this account)' for a stale default cid). The\nEnvironment line is removed from the text output; --json keeps\nenvironment and adds companyName, website, defaultClusterName and\ndefaultClusterMissing. Enrichment is best-effort (10s timeout) so\nstatus still works offline.",
          "is_bot": false,
          "headline": "feat(status): show company profile and cluster name, drop env line",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-07-10T08:15:56Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "308b31177cbe6c8c2526ee79ea6366a5a42d5e44",
          "body": "platform-overview + one task topic is enough context before other\ntools unlock; the bootstrap topic router pulls further reads in via\nsee-also links. Cuts per-session token + round-trip overhead.",
          "is_bot": false,
          "headline": "feat(mcp): lower read-server topic gate from 3 to 2 topics",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-07-08T07:56:33Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0424ac1aee0a059f4e85e410a43f82c6ab93a3fd",
          "body": "…r field)\n\nA server port mapping without standardHttps (legacy docs) zero-valued to\nfalse on pull, so a pull -> deploy round-trip persisted standardHttps:\nfalse and silently moved the app off standard-HTTPS routing (test session\n2026-07-06, app dx8jw). Port.StandardHttps is now *bool with the platform\ndefault (true) resolved at every read via StandardHTTPSValue(); drift\ncompare and the sync wire payload resolve the same default on both sides.",
          "is_bot": false,
          "headline": "fix(apps): default missing standardHttps to true in pull/sync (pointe…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-07-06T10:28:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "2137bbb7c8d0db0773bf9ba23e4c4e91362a35d4",
          "body": "…script + CI notes)",
          "is_bot": false,
          "headline": "fix(release): map -rc.N tags to their base vX.Y.Z CHANGELOG section (…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-30T11:45:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f023ad65fdee2e8044e33c264c2b057045a556a7",
          "body": "…efault env files",
          "is_bot": false,
          "headline": "docs(changelog): v1.11.2 fix second-deploy failure on round-tripped d…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-30T11:43:36Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "8ee23902fb7a2299bd603c194eda1ba4c6ec007e",
          "body": "…-overrides-convergence-)",
          "is_bot": false,
          "headline": "merge test_session 67 (fm/ts-67-verify-migration-ux-report-deploy-env…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-30T08:43:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f11d0b72a895aadcaf74637a5687b6eab76c1113",
          "body": "…t deploy\n\nFirst deploy auto-derives and persists secretEnv: .runos.<cid>.<id>.env into\nrunos.yaml. On the second deploy ResolveEnvFiles read that persisted field\nback as explicit, so VerifyExplicitEnvFiles (aef934f) hard-failed on a\ngitignored secret file the CLI itself referenced but never created\n[…]\nore the check whenever the server actually has env vars.\n\nRegression: TestDeployTwiceRoundTrip_DefaultSecretEnvExempt + ResolveEnvFiles\nsubtests (fail pre-fix with the exact bug error, pass post-fix).",
          "is_bot": false,
          "headline": "fix(deploy): persisted default secretEnv/env no longer breaks the nex…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-30T08:24:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c917df85a8daf4ae44df86e47b1d682dd8a5313f",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.11.1",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-29T18:39:53Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "3f8f9411944dcc62e01384b24593cac57c4e28eb",
          "body": "…-allowlist-silently-dis)",
          "is_bot": false,
          "headline": "merge test_session 64 (fm/ts-64-app-deploy-vcs-env-file-resolution-ip…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-25T18:54:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "aef934f2fb51a4fe1dcd936a4efe64cc4e0a81cd",
          "body": "…missing\n\nBug 102 (ts-64), CLI slice (defect b). An explicit `env:`/`secretEnv:`\nreference naming a non-existent file silently deployed EMPTY env — e.g.\nwiping ALLOWED_CIDRS and disabling an app's in-app source-IP allowlist\nwith no error. `deploy.LoadEnvFile` and `apps.LoadLocalEnv` both treated\nos.\n[…]\n both paths.\n\nNot in scope: defect (a), VCS env path resolution against clone-root, is\nserver-side (conductor + cluster agent); the CLI VCS path sends only\n{sha, configPath} and never reads env files.",
          "is_bot": false,
          "headline": "fix(deploy/sync): fail loud when an explicit env:/secretEnv: file is …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-25T15:35:14Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "c12698787ba8013e754e7da99e8c4b10b16b2ebf",
          "body": "…ous delete\n\nnodes/delete is conditionally async: it returns a jobId only with\n--delete-cloud-instance (the removeServer job). The plain delete completes\nsynchronously and returns {success, nid} with no job, but --follow blindly\ncalled followJob and errored 'response does not contain jobId' on a delete that\nactually succeeded. Now --follow only engages when the response carries a real\njobId; otherwise it renders the synchronous result normally. Regression test on\nresponseHasJobID.",
          "is_bot": false,
          "headline": "fix(nodes delete): --follow no longer errors on a successful synchron…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-21T08:37:26Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "c7ac5572d0d81832c0bdcdb6e80795a2ac832b5d",
          "body": "…t repos)\n\n- Add LICENSE (Elastic License 2.0, source-available) + NOTICE, and a License\n  section in the README. RunOS is proprietary; the source is published for\n  transparency, not as open source.\n- Stop tracking .claude/ and CLAUDE.md (local AI-assistant state that also\n  documented internal release/process); gitignore them, matching the cluster\n  and node agent repos.",
          "is_bot": false,
          "headline": "Add Elastic License 2.0 + drop internal agent files (parity with agen…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-20T11:06:58Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7f687f6ce80f7bb2d60342be14611a1ea4260281",
          "body": "…new capability",
          "is_bot": false,
          "headline": "docs(release-cli): default version bumps to patch; reserve minor for …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-18T07:59:24Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "ee4b744414a959584454f982888dd4e21e24a3bd",
          "body": "…; warm manifest after login\n\nFresh install no longer prints 'Unable to load manifest' / 'failed to fetch\nCLI manifest on first run' before login. Distinguishes not-signed-in from\nbroken-setup via an auth.ErrNotAuthenticated sentinel + network-free\nauth.HasCredentials. Bare 'runos' shows a welcome; signed-out commands get a\nlogin nudge; manifest is warmed on login success.",
          "is_bot": false,
          "headline": "feat(first-run): friendly welcome + suppress pre-login manifest noise…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-18T07:54:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "134247e1806382dfb363674b4ade59568cd6d3ef",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.10.1 restore self-update for pre-1.7.0 binaries",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-17T12:50:55Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "92dcd1d764ec7038b25f87939eca541ffc1ce3bf",
          "body": "…7.0 updaters\n\nBinaries v1.0.0-v1.6.0 hardcode a runos-latest-{os}-{arch} asset name in\ntheir self-updater download + checksum-lookup paths. Commit 5ec7510\n(shipped v1.7.0) renamed the canonical asset to runos-{os}-{arch}, so those\nold binaries have 404'd on every `runos update` since. Publish a\nbyte-identical copy under the legacy name (created before the checksum and\nattestation steps so both cover it) to let stuck old clients self-update to\ncurrent. Drop once no pre-1.7.0 clients remain.",
          "is_bot": false,
          "headline": "ci(release): publish runos-latest-{os}-{arch} alias assets for pre-1.…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-17T12:20:48Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "64c055087bbe6f04585b0cfcda0fb079be177d2c",
          "body": "…deploymentStrategy sync clobber; reject inline envVars in runos.yaml; print deploy advisory warnings",
          "is_bot": false,
          "headline": "feat(apps): nodeAffinityTags round-trip (pull/diff/sync/deploy); fix …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-11T10:27:00Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7d8de3713808dbb320a48e883d4d6b74ace553e3",
          "body": null,
          "is_bot": false,
          "headline": "docs(release-cli): document foreman advertise_version step",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-09T18:04:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "14c446c9b943050512abbf47f7556299cd889fe4",
          "body": "…manifest pins",
          "is_bot": false,
          "headline": "docs(changelog): v1.9.0 postgres drop-* destructive guard + buildkit …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-09T18:04:31Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "1b3c8a02d00111b07de64fdda54f9538f1e077de",
          "body": "Objective 60 story 110: verify the BuildKit set-advanced-configs surface\nis fully manifest-driven end-to-end. No production code change needed;\nthe generic dynacmd + MCP path handles the 17 scalar knobs as shipped in\nconductor manifest 28.24.0 (story 108).\n\n- internal/dynacmd/buildkit_advanced_test.\n[…]\nnt, id required, integer->number, and\n  structured enum surfaced on snapshotter/logLevel/logFormat. Defaults\n  are prose per the house pattern (no sibling set-advanced-configs\n  structures a default).",
          "is_bot": false,
          "headline": "test(buildkit): pin set-advanced-configs manifest surface (CLI + MCP)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-09T16:32:07Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "65cf519ca8b003f42acfb7d2b8a1d688b38dee11",
          "body": "…e guard\n\nAdd 'drop-' to destructiveVerbPrefixes so the new sync-PATCH teardown\nverbs (services/postgresql/{id}/drop-user, /drop-database) inherit the\nuniform destructive guard: --yes/-y registration + y/N prompt on a TTY,\nnon-TTY refusal without --yes. Prefix match is method-agnostic and\nfuture-pro\n[…]\nthat\nformatDependentsError already renders verbatim. MCP exposure is the\nmanifest's automatic registration (no skip-set entry).\n\nRegression tests pin both verb paths as destructive (obj-58 Story 103).",
          "is_bot": false,
          "headline": "feat(dynacmd): gate postgres drop-user/drop-database under destructiv…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T17:45:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e7fdbf72b07d9aa02ae88e7650f09e24867d30ff",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.8.0 services postgresql clone-database command",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T14:33:29Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "6aa1366b5a2252d2c3ffd8c45ed7feb59ef8c932",
          "body": "…comment\n\nKeep flagNameFor's acronym-rule doc comment directly attached to the\nfunction. The override var + its rationale now sit above, so godoc\nattributes each comment to the right symbol. No behavior change.",
          "is_bot": false,
          "headline": "refactor(dynacmd): place flagSpellingOverrides above flagNameFor doc …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T14:33:12Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "4131ed0b3b5e8b4b00456c351ecf122275798f2c",
          "body": "…ssion tests\n\nservices/postgresql/clone-database is manifest-driven (no static cobra\ncode). Conductor fixed the wire body keys (sourcePgOsid / sourceDatabase /\ntargetDatabase) and delegated flag presentation to the CLI: a naive kebab\nof those names gives --source-pg-osid / --source-database / --targ\n[…]\ned from the body while the five discrete fields stay\n  top-level, and sourceCid is NOT mistaken for the ambient :cid slot.\n- TestCloneDatabaseAutoFollow: pin that the jobId output auto-wires --follow.",
          "is_bot": false,
          "headline": "feat(dynacmd): clone-database flag-spelling overrides + mapping regre…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T14:04:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9b2006c11f71ad4d869b2f26922862a80aa9041c",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.7.5 PAT / RUNOS_API_KEY auth fixes (bugs 86, 87, 88)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T10:57:03Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "bdf4f8ae69d1ec5b8814a9651c42a60bfda930d6",
          "body": "…llback (bug 88)\n\nresolveLoginAccountID fell back to the account_id already in config when\n--account-id was omitted under --api-key. A PAT is account-scoped, so\npairing a new PAT with a stale account id stored it against the wrong\ntenant: auth succeeds by token but every /:aid/... request targets th\n[…]\ntate it's required. CI env path\n(RUNOS_API_KEY + RUNOS_ACCOUNT_ID) pairs them explicitly and is untouched.\n\nRegression test asserts an absent flag errors instead of silently\ninheriting a config value.",
          "is_bot": false,
          "headline": "fix(login): require --account-id with --api-key, drop stale-config fa…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T10:51:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3dea938697a44298d322d5ac0831b5536dc60864",
          "body": "…ug 87)\n\nrunos status gated its auth check on cfg.RefreshToken != \"\" && cfg.Firebase\n!= nil, so a PAT-only config printed 'Not logged in' even though the PAT\nwas valid and every command worked. Extract resolveAuthMethod mirroring\nauth.ResolveToken's RUNOS_API_KEY -> stored PAT -> Firebase priority:\n\n[…]\nsh validation. Surface the method on the\nLogged-in line and an authMethod field in --json.\n\nTable-tested resolveAuthMethod across env/stored/firebase/none and the\nwhitespace-trim and nil-config edges.",
          "is_bot": false,
          "headline": "fix(status): recognise stored PAT / RUNOS_API_KEY as authenticated (b…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T10:50:18Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "647ec26239f8e04ef4d94429ee964dfa1c38f373",
          "body": "getAuthToken hard-required cfg.Firebase and called GetIDToken directly,\nso a PAT-only config (api_key, no refresh token) failed every MCP tool\nvia the mcp_bootstrap gate, even though the same PAT worked for ordinary\nCLI commands. Route through auth.ResolveToken (RUNOS_API_KEY -> stored\nPAT -> Firebase priority), matching the dynacmd executor and the earlier\nupdater.go / jobs/service.go migrations off the same Firebase straggler.\n\nRegression test pins the PAT and no-credential resolution paths.",
          "is_bot": false,
          "headline": "fix(mcp): accept stored PAT / RUNOS_API_KEY in MCP auth gate (bug 86)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T10:48:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "61e8769b4cb3c229dc63c5dffafc27f1a69dac7c",
          "body": null,
          "is_bot": false,
          "headline": "test(config): shorten bug-85 PAT fixture below secret-scan threshold",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T07:18:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "f8d8399ff62091d08da12ae168c5fb64263ed15c",
          "body": "…ug 85)",
          "is_bot": false,
          "headline": "docs(changelog): v1.7.4 fix stored-PAT shadowing interactive login (b…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T07:16:52Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "e67b0d0cf3732b4dceabfc6bb2790883a79b1433",
          "body": "Interactive browser login and device preauth wrote a fresh\nrefresh_token but never cleared a stored api_key from a prior\n'runos login --api-key'. ResolveToken ranks a stored PAT above the\nFirebase refresh session, so the stale PAT shadowed the fresh login\nand every call 401'd with \"Invalid token\" de\n[…]\nng RefreshToken in reverse. One credential is\nlive post-login either way.\n\nRegression test pins the api_key clear; existing\nTestResolveToken_StoredAPIKeyWinsOverFirebase pins the precedence\nmechanism.",
          "is_bot": false,
          "headline": "fix(login): clear stored api_key on non-PAT login (bug 85)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-08T07:02:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "00695d82b15ca9f0b6844b011194e4636c1ebc01",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.7.3 phantom patch for obj-55 installer verify",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-07T16:27:14Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "7d744ec079c1ec6c8c6021c5ade0269465950fa9",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.7.2 supervised re-rollout target (obj 52)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T16:27:30Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "96fd103670f1a5f483b7f7cd9ee85ae35dea0175",
          "body": "…n-controlled\n\nRework the deploy runbook so tooling never touches main. A deploy now tags the\ndev commit, pushes the tag + dev, and on success fast-forwards a new 'deployed'\nbranch to the shipped commit. main is left for the human to merge after personal\nverification. The sensitivity scan and payloa\n[…]\nepo structure, collapsed to a single 'deployed' branch\nsince the CLI has no dev/beta/prod environments. Updates scripts/release.sh,\nthe release-cli skill, and the CLAUDE.md Releasing section to match.",
          "is_bot": false,
          "headline": "chore(release): deploy branch model (dev/deployed/main); main is huma…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T16:02:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "4b1fdee7e5a83ea80bcf1cbc7cb059884473d3b5",
          "body": "…ine release",
          "is_bot": false,
          "headline": "docs(changelog): v1.7.1 release runbook + first live validation-pipel…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T13:41:16Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "573c05814b0caf228560f563e566798e5a0f75fb",
          "body": "Add scripts/release.sh, the single deterministic path for cutting a CLI\nrelease: gates (go build/vet/test, make local), fast-forward main to dev,\ntag, push, watch CI, and verify the build-provenance attestation. Includes\na fail-closed sensitivity scan over the release payload (public repo).\n\nAdd the\n[…]\nravels with the repo while\nlocal Claude state stays ignored.\n\nWire make release VERSION=vX.Y.Z (CHECK=1 for a no-side-effect dry run) and\nrepoint the CLAUDE.md Releasing section at the skill + script.",
          "is_bot": false,
          "headline": "chore(release): deterministic release runbook (script + skill + gates)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T12:53:52Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "51a2b11b916245417bb4bce802c66a889cc20461",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.7.0 build-provenance attestation + asset rename",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T11:12:08Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a657e2edad6137cfcad5ba62ea462ac02c5ab332",
          "body": "…man S88)\n\nDocument the build-provenance trust chain (keyless attestation -> Templates\nvalidator + R2 digest registry -> fail-closed installers) and the accepted\nlimitation that attestation does not defend against a compromised build.\n\nEnumerate the admin-only GitHub-settings controls that are the o\n[…]\nion (branch protection, required review, restricted workflow\nedits, restricted tag/release publishing), and note that every uses: in\nrelease.yml and ci.yml is already SHA-pinned with version comments.",
          "is_bot": false,
          "headline": "docs(security): release trust model + admin hardening checklist (fore…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T08:25:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5ec751056f2e876b37a9437b9ef81f35e33488e7",
          "body": "…{os}-{arch}\n\nAdd keyless (Sigstore/OIDC) actions/attest-build-provenance step to the\nrelease workflow, pinned to v4.1.0 SHA, attesting every released archive\nto the workflow OIDC identity. Add id-token:write + attestations:write\npermissions.\n\nDrop the literal 'latest' from archive filenames (runos-\n[…]\nleases/latest/download/runos-latest-* URLs stop\nresolving for new releases; consumers must move to exact-tag URLs. Rolls\nout together with the Templates validator + new installer (foreman obj-50 S84).",
          "is_bot": false,
          "headline": "feat(release): build-provenance attestation + rename assets to runos-…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-06T07:53:54Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "145e5f9cac587dc3c8a5f6f858a4ea33f99dfe08",
          "body": "…; add login --api-key\n\nReplace every real RunOS identifier and customer name in fixtures, help\ntext, comments and docs with obvious placeholders (myacct, mycluster,\nmyapp/appidN, myosid, mysvc, acme). No real aid/cid/app-id/service-id/\nosid or customer/org name remains in the tree. Pure rename; ful\n[…]\nAT > Firebase precedence); logout clears it. CHANGELOG v1.6.0.\n- delete TESTING.md and its CLAUDE.md reference; fold the testing\n  conventions into CLAUDE.md with a hard rule never to commit real ids.",
          "is_bot": false,
          "headline": "chore(security): purge real account/cluster/app/service ids from repo…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-05T09:45:35Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "5053da85b0445082e856fbd12b7868b47e5b4f13",
          "body": "…7 S80)\n\nBuild an auxiliary (non-app) container image from a local build context\nand push it to the cluster's system Harbor under the fixed runos-apps\nproject as runos-apps/<repo>:<tag>. Decoupled from apps/deploy/VCS: no\napp id, no commit SHA, no robot/RBAC/GitHub cred, no phantom build-only\napp. S\n[…]\n membership (both option sets), upload-URL hardening, MCP\narg translation + required-arg errors, JSON envelope + summary phrasing.\nDocs: CHANGELOG v1.5.0, CLAUDE.md section, TESTING.md regression row.",
          "is_bot": false,
          "headline": "feat(cli): services harbor build-image verb + MCP shim (foreman obj-4…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-04T16:07:21Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "52b389f47df8c9571f5520f68d1310f271a1fe73",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.4.2 --app in --help + --command literal pass-through",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T16:36:38Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0737fbcdae8e6060294e84109ea8e7acc9e25b0f",
          "body": "…rals (foreman #82 + #83)\n\n#82: bug 80 added --app as a normalizer-redirect alias for --id on\napps-scoped manifest commands (apps/run, apps/show, apps/logs, ...).\nThat redirect was invisible in --help, so users had no way to discover\nthe alias.\n\nFix: register --app as a real cobra flag in dynacmd's \n[…]\neywords, numerics, JSON fragments, mixed argv all\n  survive as []string\n- TestCoerceArrayFlagValue_ObjectItemTypeStillJSONCoerces (#83):\n  object/array itemType still JSON-decodes\n\nTESTING.md updated.",
          "is_bot": false,
          "headline": "fix(cli): make --app visible in --help + stop --command coercing lite…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T16:29:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6b8fe79496a2c2dd247b8faa703666357cdf2dad",
          "body": "…ommands",
          "is_bot": false,
          "headline": "docs(changelog): v1.4.1 --app alias for --id on app-scoped manifest c…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T09:15:13Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "0d5db91cab661109aca6444999072fed75828de4",
          "body": "… (foreman #80)\n\n`runos deploy` (hand-coded) and the new `runos apps build` (hand-coded\nin objective 43 / story 74) both accept --app <id>. Manifest-driven app\ncommands (`apps run`, `apps show`, `apps logs`, ...) only registered\n--id from the manifest's `id` positional, so `runos apps run --app\n<id>\n[…]\npid8` accepts the flag\n(falls through to \"missing sha+command\" instead of \"unknown flag\");\n`runos services postgresql show --app appid8` still errors with\n\"unknown flag: --app\".\n\nTESTING.md row added.",
          "is_bot": false,
          "headline": "fix(cli): accept --app alias for --id on app-scoped manifest commands…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T09:10:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "afb9852baa2b86eeef5e8752c6284e05dcaf9aef",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.4.0 runos apps build verb + mcp empty-body fix",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T08:16:09Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "31b4dfd3587265bc71b1f9e71817fc060be75214",
          "body": "…t-in gate) (story 74)\n\nAdds the CLI side of objective 43: a standalone `runos apps build` verb\nthat drives the conductor's new POST /apps/:id/build endpoint to build +\npush a VCS app's SHA-keyed image to Harbor and stop, with no rollout\nand no command run. Optional, opt-in for clean CI logs; build-\n[…]\ntempty contract; *bool keeps\n  explicit `false` distinguishable from absent)\n- TestJobStatus_BuildResult (typed result decoding from raw JSON)\n\nTESTING.md updated with the objective-43 / story-74 row.",
          "is_bot": false,
          "headline": "feat(cli): runos apps build verb (standalone image build, --follow op…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T07:40:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "090db67db7b4eb0980443e867775c1ce5d7e5b8c",
          "body": "…(foreman #78)\n\nContentBlock.Text had `json:\"text,omitempty\"`, so any empty-text success\nframe marshalled to `{\"type\":\"text\"}` with no `text` field. MCP rejects\nthat as an invalid_union; conformant clients see a Zod validation dump\neven though the underlying call (e.g. account/api-keys/revoke, a 200\n[…]\nreturned \"\" to the wrap layer.\n\nRegression tests pin both: TestContentBlockMarshalsTextEvenWhenEmpty\n(struct tag invariant), TestIsEmptyBody + TestEmptyBodySuccessMessage\n(executor empty-body branch).",
          "is_bot": false,
          "headline": "fix(mcp): emit valid text content block for empty-body 2xx responses …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-03T07:06:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "21281a24625d03a604f4eee922acc10542d33ccf",
          "body": "…tory 70)\n\nScope change from objective 42's round-2 verification: the original\n\"leave requires.env naming as-is\" decision was reversed. requires.env\nnow accepts BOTH `username` (preferred) and `user` (back-compat alias).\nThe CLI iterates the requires.env map field-key-agnostically already,\nso this i\n[…]\nThe collision-detection / drift-gate\nhelpers iterate the map agnostically, so `username` and `user` already\nboth flow through; the pin guards against a future refactor accidentally\ndropping the alias.",
          "is_bot": false,
          "headline": "docs(cli): prefer `username` in requires.env, keep `user` as alias (s…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-02T14:25:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "43be2514847b5cbd115cc1405a25e1ca61ae0858",
          "body": "The verb itself is fully manifest-driven (manifest 28.15.0, conductor\ncommit 7bac12a), so this commit is the CLI repo's slice of objective\n42: documentation and a regression test pinning the discrete-field\nrequires.env iteration.\n\n- CLAUDE.md: new \"Postgres adopt-user and discrete-field requires.env\n[…]\nalse-positive drift gate for the non-url keys on an\n  adopted-user app).\n\nNo static cobra code added, no MCP shim added: verb is auto-rendered\nby the dynacmd builder once the manifest entry is pulled.",
          "is_bot": false,
          "headline": "docs(cli): adopt-user verb + discrete-field requires.env (story 68)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-02T14:04:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8cb1b51489228c3a866c3294be72254eae8acf12",
          "body": "Adds the user-facing entry point for objective 41. Top-level `runos\nrun` sibling to `runos deploy`: deploy is build then rollout, run is\nbuild then execute. Targets pre-rollout work (DB migrations, seeds,\nbackfills) that today still requires kubectl exec and cluster admin in\nCI, blocking retirement \n[…]\ners: --timeout parsing, the VCS-only\npreflight, exit-code extraction off JobStatus.RawResult, the\nexitCodeError ExitCode() interface contract, the MCP arg builder, and\nthe static-tool category gating.",
          "is_bot": false,
          "headline": "feat(cli): runos run verb (CI + laptop shapes, VCS-only, exit-code prop)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-02T10:06:16Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "caea43e517380aa076ceb0aea97cbc8c31500ae3",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.1.0 docker build args on runos deploy",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-01T15:31:32Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "862744217690e3ea11ccfc2b4c4f98aa2d411231",
          "body": "Implements story 60 of objective 40 on the CLI side. Adds Docker build\nargs support to `runos deploy` for both CLI-deploy and VCS-deploy paths,\nso apps needing build-time configuration (Next.js bundles baking\nNEXT_PUBLIC_* etc.) no longer need bespoke GitHub Actions workarounds.\n\n- DeployConfig gain\n[…]\nnos subprocess.\n\nPure-helper tests for the parser, validator, dup-key detector, yaml\nround-trip, wire-shape on both deploy paths, and the MCP translation.\nTESTING.md updated with the regression entry.",
          "is_bot": false,
          "headline": "feat(cli): docker build args on runos deploy (yaml + --build-arg)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-06-01T14:48:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "750e95558716cdd90a5856a63e6e3b477ce1158d",
          "body": null,
          "is_bot": false,
          "headline": "docs(changelog): v1.0.0 GA notes covering rc.8 -> 1.0.0 deltas",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-31T17:09:28Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "5f4996ee32b464808547b9969908e637a41489d7",
          "body": "… deploymentStrategy round-trip\n\napps pull now writes a thin yaml on a named RRC (just resourceRequirementClassId, no replicas/cpu/memory) since the class bakes those in. Custom rrcId still emits every dimension. apps sync detects when a local override on a named RRC conflicts with the class default\n[…]\nround-trips through apps pull and reaches the deploy wire body, so a yaml setting `deploymentStrategy: recreate` (single GPU, RWO PVC) no longer silently falls back to the conductor's rolling default.",
          "is_bot": false,
          "headline": "feat(cli): thin-yaml RRC pulls, custom flip on conflicting overrides,…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-31T17:07:47Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "3892179b9c9ad2a9792398dfbbf67e47bc64fa77",
          "body": "The generic key:value formatter rendered the {kubeconfig: \"...\"}\nresponse as `kubeconfig: <multiline YAML>`, prefixing the YAML with a\nstray top-level key. That broke the documented use case of piping the\noutput straight to kubectl (\"yaml: mapping values are not allowed in\nthis context\"). The only w\n[…]\nng one entry instead of duplicating the carve-out.\n\nRegression test on the pure helper (positive on clusters/kubeconfig,\nnegative on missing/empty/wrong-typed field, invalid JSON, and other\ncommands).",
          "is_bot": false,
          "headline": "fix(cli): emit clusters/kubeconfig raw YAML in text mode (foreman #48)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-31T09:28:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "af620290812c0df552d943c9bcb2fbbf95753b48",
          "body": "…y/N] (foreman #37)\n\nConductor manifest 28.6.0 surfaces the maintenance-scripts run path\nunder `mcp:[\"write\"]` so dynacmd auto-generates the command and MCP\ntool. The new node-apt-upgrade-reboot script reboots the node, but\nthe matcher's last-segment test (`run`) is too generic to add as a\nblanket d\n[…]\nntenance-scripts trigger; unrelated `…/run` paths stay non-gated.\n\nRegression rows in TestIsDestructiveCommand cover the positive match,\na hypothetical future script, and a negative (`apps/{id}/run`).",
          "is_bot": false,
          "headline": "feat(cli): gate maintenance-scripts run triggers behind destructive […",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-30T09:28:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "249bec58c4d2cac8133754b0aef62a8df2924234",
          "body": "The set-*-config family declares its fields as type: string to match\nthe Record<string,string> wire contract Bug #36 enforced, but the -f\nYAML loader in collectInput copied parsed values to the body verbatim.\nA `queue_size: 128` therefore reached Conductor as a JSON number and\nwas rejected (\"expecte\n[…]\n\nstrings. Unconvertible values pass through so the server can return\nits own typed error. Generic and manifest-driven, not endpoint-specific.\n\nRegression tests on the pure helpers in executor_test.go.",
          "is_bot": false,
          "headline": "fix(cli): coerce -f YAML values per manifest field type (foreman #40)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-30T08:17:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "67defb8b449c37d5b9519c5fddc232407cb09cd1",
          "body": "The server returns 200 OK with `{cancelled:false,message:\"...\"}` when\nthe target job is already terminal. The structured body was rendered\ncorrectly but the process exit code stayed 0, so CI / LLM gates keyed\non $? misread the noop as success. Add jobsCancelExitGate (mirroring\ndomainCheckExitGate) and wire it next to the existing exit-gate\ndispatch. The body still renders; only the return value flips.",
          "is_bot": false,
          "headline": "fix(cli): jobs cancel exit code reflects cancelled:false (foreman #147)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-18T07:58:07Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "45525f15e7a1cba2a13974a4d44c4c7c03d4c65e",
          "body": "…#145)\n\nformatAuthError rewrote every 401 with a generic RUNOS_API_KEY\nremediation, including the upstream-provider 401s that flow through\nintegrations/add/<provider>. Operators chasing a bad Hetzner /\nDigitalOcean token were pointed at runos account api-keys add when\nthe real fix is to rotate the p\n[…]\nh-based\nclassifier (is401UpstreamProxyCommand) and skip the formatAuthError\nrewrite for integrations/add/*; the conductor's APIError body\n(which already carries the provider message) renders verbatim.",
          "is_bot": false,
          "headline": "fix(cli): skip auth-refused rewrite for upstream-proxy 401s (foreman …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-18T07:50:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "8f14d487a4814e51b2d20f115a330f6173a5fac5",
          "body": "exec-sql is the only verb in destructiveVerbSuffixes whose actual\ndestructiveness depends on a runtime flag. Pre-fix, every `services\npostgresql exec-sql ... --query \"SELECT 1\"` required --yes, training\noperators to bypass the prompt for routine diagnostic reads. Introduce\ndestructivePromptApplies t\n[…]\n the prompt. Other destructive\nverbs (delete / drain / reset / clear-cache / ...) stay gated\nunconditionally; --yes flag registration is unchanged so users can\nstill opt out on read-write invocations.",
          "is_bot": false,
          "headline": "fix(cli): skip destructive-confirm on read-only exec-sql (foreman #140)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-18T06:44:06Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b8aa63911d5731cf16ee505d1d67feeda161efc3",
          "body": "A bare RFC 3339 timestamp like \"2026-08-01T00:00:00\" reached the\nconductor and was interpreted as local time, producing PATs that\nexpired up to 14 hours off the operator's intent on non-UTC machines.\nPre-flight the value with time.Parse(time.RFC3339, ...) inside the\nexisting applyCLIDefaults carve-out and refuse with a message naming\nboth accepted shapes (Z suffix or numeric offset).",
          "is_bot": false,
          "headline": "fix(cli): refuse tz-less --expires-at on api-keys add (foreman #132)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-18T05:33:41Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "202aa26a1a52ac37f25d6c3c71365cfe5df1c747",
          "body": "…131)\n\ndestructiveSummary only consulted the positional args slice, so endpoints\nthat accept the URL-bound id via --cid (clusters/{cid}/reset) leaked the\nmanifest description in the confirmation message. Read the matching\n--<flagNameFor(field.Name)> value when the positional slot is empty, so\nthe target line consistently reads cid=mycluster3 (matching the apps delete\nid=c479n shape).",
          "is_bot": false,
          "headline": "fix(cli): destructive-confirm target surfaces --flag value (foreman #…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-18T05:27:10Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "a08b5b3e7a7119339aac7b1f1180e3fc0b80cd59",
          "body": "Follow-up to #86/#93/#94. Two bare stdout writes in cmd/deploy.go's\n--follow success branch still landed on stdout under --json:\n  - \"Deployment completed successfully!\" (line 670)\n  - \"App available at: <url>\" (line 694)\n\nBoth now route through humanOut, the io.Writer already plumbed by the\n#94 com\n[…]\nly thing on stdout under --json, restoring\n`python json.load(out)` / `jq` parsing on completed deploys.\n\ndeploy_vcs.go's analogous block already uses the `human` writer\ncorrectly via vcsDeployStreams.",
          "is_bot": false,
          "headline": "fix(cli): deploy --follow --json success stdout leaks (foreman #126)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T20:47:57Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e69ab0fc3464f8d27292907d925d84da50235ff9",
          "body": "`runos apps prepare-cli-pull c479n bogus --cid mycluster3` returned a 400\n\"cliUploadId is required\" because the second positional was silently\ndropped. Only `--cli-upload-id <id>` or `-f body.yaml` bound the value.\nThe help text correctly showed `<id> <cliUploadId>` so the contract\nwas right; only t\n[…]\ning behaviour is preserved.\n\nLive verified: `apps prepare-cli-pull c479n bogus --cid mycluster3` now\ncorrectly sends cliUploadId=bogus to conductor (404 Archive not\nfound, as expected for a bogus id).",
          "is_bot": false,
          "headline": "fix(cli): bind body-positional args into request body (foreman #122)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T16:52:33Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "dd45dc46d0e0f43e586d5736116ebb515a0bf8c7",
          "body": "…an #117)\n\nFollow-up to #104. The strict-yaml decoder error for known apps-add\nbody fields (`envVars`, `secretEnvVars`) used to bottom out at \"field\nenvVars not found in runos.yaml\" with no path forward — users had\ncopy-pasted from the apps/add manifest help into their runos.yaml.\n\nAdded envFieldHin\n[…]\ner behaviour). Non-strict-decoder yaml errors are untouched.\n\nRegression tests cover both hints, an unknown-field no-hint case, the\nnon-strict-decoder pass-through, and an end-to-end LoadConfig check.",
          "is_bot": false,
          "headline": "fix(cli): did-you-mean for envVars/secretEnvVars in runos.yaml (forem…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T15:52:22Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bb9d1f4b5aa74b0f9d1a80a22e546c090b77f080",
          "body": "A PAT pasted from Slack / docs / web UIs often carries a trailing\nnewline (Cmd-A Cmd-C from a chat client) or leading space. Pre-fix:\n  - trailing newline: leaked \"request failed: Get '...':\n    net/http: invalid header field value for Authorization\" because\n    net/http refuses CR/LF in header valu\n[…]\ning \"set but empty\" refusal instead of reaching the net/http\nlayer.\n\nRegression tests cover the trailing-newline / CRLF / leading-space /\nsurrounding-whitespace cases plus the whitespace-only refusal.",
          "is_bot": false,
          "headline": "fix(cli): trim whitespace on RUNOS_API_KEY (foreman #110)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T15:01:27Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "18be81d753e0dd45e309df2de5c039b9676c7188",
          "body": "Follow-up to #102 (the 40-hex shape gate). git treats commit SHAs as\ncase-insensitive — `git rev-parse 61D950...` works — but\nvalidateCommitSHA stayed strict on lowercase. Users pasting a SHA from\nGitHub / GitLab web UIs hit \"--sha contains non-hex character 'D'\".\n\nFix: strings.ToLower the resolved \n[…]\nalidateCommitSHA itself stays strict so the\ncontract is enforced at one place.\n\nRegression test pins the round-trip: validator alone refuses\nuppercase, normalise-then-validate accepts both case forms.",
          "is_bot": false,
          "headline": "fix(cli): normalise --sha to lowercase before validating (foreman #109)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:58:45Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e480c78d4317e88ccaa121e4d08e2dd8073ee83d",
          "body": "`runos apps sync < /dev/null` (no TTY, no --yes) silently applied the\npatch and exited 0. CI / cron jobs running sync without an explicit\nopt-in got unconfirmed mutations. apps delete already refuses in this\nshape via the destructive-confirm gate; apps sync now matches.\n\nThe pre-fix logic (I25-AE) a\n[…]\n> prompt as before.\n\nScope limited to apps sync (the filed issue). deploy / services_sync\nstay on the auto-skip pattern; if the same footgun surfaces there,\nfile a separate issue to extend the policy.",
          "is_bot": false,
          "headline": "fix(cli): apps sync refuses non-TTY without --yes (foreman #107)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:34:43Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "adc623f493b95ae4a9f7a9458e6070e4f14ecf87",
          "body": "… #106)\n\n`runos apps network-access <id>` truncates the LINK column to 40 chars\nregardless of terminal width:\n`https://app-c479n-3000.testing.mercat...`. Users came for the URL\nspecifically; the command is unusable in text mode without --json.\n\ntruncateCell now bypasses the maxTextCellWidth cap when\n[…]\nlues (250-char names, descriptions, uids) still\ntruncate to keep tables readable in narrow terminals.\n\nRegression tests cover http/https URL bypass plus a substring-only\ncase that must still truncate.",
          "is_bot": false,
          "headline": "fix(cli): exempt URL-shaped cells from text-table truncation (foreman…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:31:49Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "08190b308fc67347b3bac580f007943a5707c56c",
          "body": "MCP exposed `jobs_follow` as a tool, but the CLI had no\n`runos jobs follow` subcommand. Users moving between MCP-driven\nflows and the bare CLI hit a surprise parity gap; `runos jobs show\n--follow` errored \"unknown flag: --follow\" too.\n\nAdded cmd/jobs.go with a static `jobsCmd` parent and a\n`jobsFoll\n[…]\nnder the same parent — `runos jobs --help` now lists all six.\n\nTop-level `runos follow <jobId>` stays as the alias the MCP tool\nshells to; both surfaces now expose the verb under the `jobs`\nnamespace.",
          "is_bot": false,
          "headline": "fix(cli): add `runos jobs follow` static subcommand (foreman #105)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:29:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9b42ff7e111986f1fdc841060a250f3098e47ba0",
          "body": "Two CLI-side error-message inconsistencies fixed:\n\n1. Validate's \"app name is required in runos.yaml\" misled users to try\n   `name:` (the conductor manifest's body-field name on apps add).\n   Renamed to \"`app:` field is required in runos.yaml\" so the user\n   follows the actual yaml key.\n\n2. yaml.v3'\n[…]\n, not\nthe yaml schema. The two are distinct surfaces and the manifest\nwording is correct.\n\nRegression tests cover the sanitizer (rewrite, idempotence,\npass-through) and an end-to-end LoadConfig check.",
          "is_bot": false,
          "headline": "fix(cli): runos.yaml error wording (foreman #104)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:26:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9109ddca446137b65f6f7b55160c5b2f86f66067",
          "body": "…c (foreman #103)\n\nEight apps subcommands (show, status, logs, builds, restart, delete,\nenv-vars, update) take a 5-char app id as positional. Three (pull,\ndiff, sync) take a yaml file path. Users who learned the id-positional\nshape ran `runos apps pull c479n` and hit \"yaml file\n'<cwd>/c479n' not fou\n[…]\nps pull / diff / sync.\n\nKeeps the documented yaml-file positional semantic; just makes the\nfailure mode actionable. Regression tests cover the helper (10\ninputs) and the error-wording shape (3 cases).",
          "is_bot": false,
          "headline": "fix(cli): hint --app-id on id-shaped positional in apps pull/diff/syn…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T14:22:05Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "bfa611e341b1fbdc049c94f8534cc751ce06030a",
          "body": "`runos deploy --app X --sha <short>` (e.g. the 7-char form `git log\n--oneline` emits) returned a jobId and only async-failed at step 1\n\"Fetch source: fatal: couldn't find remote ref <short>\". Git requires\nthe full ref on the server side; only 40-char SHAs work.\n\nAdded validateCommitSHA pure helper t\n[…]\ns 40-hex.\n\nError names the offending value + length and points at\n`git rev-parse <ref>` as the way to expand a short ref. Regression\ntest covers 40-hex pass, short, empty, uppercase, non-hex, 41-char.",
          "is_bot": false,
          "headline": "fix(cli): refuse short --sha pre-network on VCS deploy (foreman #102)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T13:37:12Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "055b4ef8c91fe51d7d09ff196d670952395985ae",
          "body": "`runos follow <jobId>` is the canonical \"block until a job ends\" verb\n(suggested by apps-sync's \"Follow rollout: runos follow <id>\" hint),\nbut it had no --json flag. CI / LLM consumers that wanted machine-\nreadable progress were stuck with human text or had to poll\n`jobs show` in a loop.\n\nAdded -j/-\n[…]\nstderr.\n\nFailure case preserved: if the job's terminal status is `failed`,\nFollowJob* returns non-nil; the JSON envelope still emits to stdout\nand the error propagates so the exit code stays non-zero.",
          "is_bot": false,
          "headline": "fix(cli): runos follow -j/--json mode (foreman #99)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T09:28:34Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2ca55f678d1628fab656ed09d2ec17ae94027744",
          "body": "After #86 (drift refusal) and #93 (--follow), the deploy success path\nstill wrote four human-text lines to stdout regardless of --json:\n  - \"Wrote .dockerignore: ...\" (writeDeployIaCArtifacts)\n  - \"Wrote env file: ...\"        (writeDeployIaCArtifacts)\n  - \"Wrote service yaml: ...\"    (writeProvision\n[…]\nPrintf inside the helpers becomes fmt.Fprintf(humanOut, ...);\nthe JSON envelope at line 590 still emits on real stdout.\n\nMirrors the I10-L `progress` helper that was already in place for the\npreamble.",
          "is_bot": false,
          "headline": "fix(cli): deploy --json success-path stdout pollution (foreman #94)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T07:22:15Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "6eb72e664165f276d773d6a0ddc9ffd8a84a5c5e",
          "body": "…eman #93)\n\nAfter the #86 drift fix, `runos deploy --follow --json` still wrote the\nbuildctl progress lines (`#5 1.623 (1/39) Installing ncurses-...`) to\nstdout before the terminal JSON envelope, breaking jq parsers.\n\nThe CLI-deploy follow call used jobs.FollowJob which hard-codes stdout\nas the writ\n[…]\n to jobs.FollowJobToWriter and pass\nos.Stderr when jsonOutput is set. Also moves the \"Following job\nprogress...\" preamble through the existing `progress` helper so it lands\non stderr under --json too.",
          "is_bot": false,
          "headline": "fix(cli): deploy --follow --json routes build progress to stderr (for…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-17T07:21:39Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9e996d909c2c530601d297bf0b45b7367241d345",
          "body": "A runos.yaml with `cpuRequestMc: 0.5` (or any fractional integer field)\npassed silently because yaml.v3 truncates a float literal to int(0)\nwhen the target struct field is int-typed. k8s reads limit=0 as\nUNLIMITED, so the user thinks they capped resources while the pod\nactually runs uncapped.\n\nAdded\n[…]\nfore LoadConfig's typed decode so the rounding never\nhappens.\n\nRegression tests cover the issue 91 repro, memory + replicas variants,\nthe integer-passes happy path, and an end-to-end LoadConfig check.",
          "is_bot": false,
          "headline": "fix(cli): refuse fractional cpu/mem yaml literals (foreman #91)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T21:09:32Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b67d89232d798c89c60d9bc9aa7db728c2825fbc",
          "body": "A runos.yaml with a block-scalar healthCheckPath, e.g.\n\n    healthCheckPath: |\n      /healthz\n      /readiness\n\ndeployed cleanly and persisted '/healthz\\n/readiness\\n' on the\nAppDocument. k8s probe semantics don't define behaviour for paths with\nembedded newlines, so the probe silently misbehaves un\n[…]\n\n\nRegression tests cover the issue 88 repro, trailing newline, tab,\nmissing leading /, embedded whitespace, plus an end-to-end LoadConfig\ntest that confirms the block-scalar input is rejected on load.",
          "is_bot": false,
          "headline": "fix(cli): validate healthCheckPath / metricsPath shape (foreman #88)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T20:59:41Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "06ec9365e9382c3efe582173d1f0703731cb6bd7",
          "body": "…#87)\n\nA .secrets.env with bytes like \\x01..\\xff..\\xfd passed CLI + conductor\nintake, exit 0 with a jobId, then failed at step 4/10 with kubectl\nemitting \"yaml: control character\". Without --follow the user saw\nsuccess while no env was actually applied.\n\nAdded envfile.Validate that walks each value \n[…]\nsync paths refuse before the request is\nqueued. Error message names the offending key + byte offset.\n\n\\n / \\r / \\t still pass for multi-line content (PEM blocks, JSON\npayloads with embedded newlines).",
          "is_bot": false,
          "headline": "fix(cli): env-value control-byte / invalid-UTF-8 pre-flight (foreman …",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T20:57:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "1c98b9e1d90cf0644215811d1ae41e9141cdbde3",
          "body": "`runos deploy --json` on drift wrote the human-readable drift report\n(boxes, --- local/+++ server, reconcile hints) to stdout before the\n{\"error\":...} JSON envelope, so `python3 json.load(out.txt)` raised\nJSONDecodeError. Pre-existing --force path already routed its warnings\nto stderr; the refusal p\n[…]\ne JSON error envelope still emits to stdout via\nthe runDeploy defer'd emitJSONError path.\n\nRegression test pipes os.Stdout and asserts the gate writes zero bytes\nto it under --json on a drift refusal.",
          "is_bot": false,
          "headline": "fix(cli): deploy --json drift refusal stdout pollution (foreman #86)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T20:53:55Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "7d9b2143a27eaffcf9341574d4899b3191d94416",
          "body": "Accumulated work from earlier sessions that landed in the working tree\nbefore the foreman-driven per-issue commit workflow. Bundled here so\nthe per-issue fixes above don't carry ride-along noise. Touches:\n\n  - cmd/parents.go + parents_test.go (new): top-level `runos parents`\n    helper for the apps/\n[…]\nructive.go + destructive_test.go (new):\n    destructive-op confirmation helper\n  - internal/update/updater.go, updater_test.go: update endpoint\n    follow-ups\n\nNo new test failures; full suite passes.",
          "is_bot": false,
          "headline": "chore: carry-forward CLI changes from prior session",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:33:50Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "542ea39cb6737eeb504724574a57bc7ac758d609",
          "body": "`runos apps pull <yaml>` and `runos services pull <yaml>` refused with\n\"cluster mismatch: yaml is for X but default is Y\" when the user's\nconfigured default cluster differed from the yaml's cid — even though\nboth --help texts promise the cid is read from the file. Broke the\ndocumented round-trip for\n[…]\ng help with exit 0\n  - cmd/apps_pull.go / cmd/services_pull.go SilenceUsage + ENOENT\n    wording polish\n\nRegression tests cover both reconciliation branches and the existing\nexplicit-mismatch refusal.",
          "is_bot": false,
          "headline": "fix(cli): pull honors yaml cid over default (foreman #83)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:33:31Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "a28693f9a70b8f0d93e33606bb35e59e0de676df",
          "body": "Every `runos deploy` printed \"Warning: domain-removal gate skipped\n(fetch failed: ...)\" because GetAccountDomains hard-coded the legacy\nbare-array shape, but conductor's `/:aid/domains` migrated to the\nenvelope `{domains:[...]}` in the iter-27 envelope sweep. The\ndomain-removal confirmation was sile\n[…]\nts both shapes\n(bare array AND single-key envelope) so the gate fires through the\nconductor migration window and beyond.\n\nRegression test covers envelope, bare-array, empty, and malformed-input\ncases.",
          "is_bot": false,
          "headline": "fix(cli): deploy domain-removal envelope (foreman #70)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:33:19Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "83dac664502cf86e6e38512b29298776865c230e",
          "body": "Two related fixes that share internal/deploy/config.go; bundling so the\nshared file changes stay coherent.\n\n#50 LoadConfig: switched from yaml.Unmarshal to yaml.NewDecoder +\nKnownFields(true). Typo'd top-level runos.yaml keys (replica vs\nreplicas, healtCheck, envVars, ...) now fail with a typed erro\n[…]\ness codec.\n\nRegression tests cover typo refusal, pulled-yaml round-trip,\nPEM-block / JSON / unicode values, both quote styles, leading/trailing\nwhitespace, empty values, and the on-disk format change.",
          "is_bot": false,
          "headline": "fix(cli): deploy yaml strict + lossless env-file (foreman #50, #73)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:33:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "b72198f772017bd89f6c4fabccbdff307816b964",
          "body": "Five related pre-network gates in the dynacmd executor that turn\nconductor 5xx / silent-drop classes into clean local refusals. Deeply\ninterleaved in the same file, bundled here.\n\n#47 validateClusterIDShape: refuses `--cid` with slash, control char,\nor runaway length (>64 chars) before the request h\n[…]\nlicit empty values pass through to the\nmissing-required gate.\n\nRegression tests for each helper cover positional + flag/body slots,\nboundary conditions, and non-PATCH skip / non-enum field skip paths.",
          "is_bot": false,
          "headline": "fix(cli): dynacmd pre-flight defenses (foreman #47, #48, #53, #60, #69)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:32:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "9f1e067c9f44325f58d258b74f2fad35c36ba9f6",
          "body": "`runos manifest list <no-match> --json` emitted `null` because the\nfiltered slice was declared as a nil `var paths []string`. jq's `.[]`\nand `.length` error on null but work on [], so downstream CI/LLM\nconsumers broke on the empty-match shape mismatch.\n\nExtracted filterManifestCommandPaths which seeds a non-nil `[]string{}`\nso the --json branch always returns an array regardless of how many\ncommands match.\n\nRegression test covers the helper and the JSON marshal contract.",
          "is_bot": false,
          "headline": "fix(cli): manifest list --json returns [] not null (foreman #39)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:32:43Z",
          "body_truncated": false,
          "is_coding_agent": false
        },
        {
          "oid": "124c09f7a334cce54ad318672eeb7ec6c2fbe8d4",
          "body": "`runos services postgresql users <id>` printed minified raw JSON to the\nterminal instead of the tabular USERNAME/DATABASES table its peers\nrender. The response is a two-key envelope `{users:[...],\norphanSecretsDetected:[]}`; unwrapArrayEnvelope only handles single-key\nenvelopes, so formatArray's []m\n[…]\nared. --json mode is untouched.\n\nRegression test covers the postgresql users payload, multi-key\ndisambiguation, empty arrays, missing-fields-hint, bare-array pass-\nthrough, and dotted manifest fields.",
          "is_bot": false,
          "headline": "fix(cli): output multi-key envelope unwrap (foreman #38)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:32:36Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "789243caf88823c0af119af2b405b87472f22378",
          "body": "`runos clusters default <cid>` stored any string verbatim — no whitespace\ntrim, no charset check, no existence check vs the account's clusters list.\nA typo'd or whitespace-padded id silently broke every subsequent command.\n\nNow: trim whitespace, run apps.ValidateIdentifier on the trimmed value,\nand \n[…]\nard-reject unknown ids.\n\nRegression tests cover trim, shape refusal (incl. path-traversal), the\ntwo clusters-list payload shapes (bare array + single-key envelope), and\nthe cid/id field-name fallback.",
          "is_bot": false,
          "headline": "fix(cli): clusters default trim+validate (foreman #36)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-16T19:32:26Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "2da55f7f4d7bd1f2efdaf44dbed1ae5bec9fd603",
          "body": "`deploy.GetAppDependencies` decoded /apps/:id/dependencies directly\ninto []deploy.AppDependency. Conductor 17.7.0's envelope-everywhere\nmigration (sibling of I27-AA / I27-T sweep) wrapped the endpoint in\n{dependencies: [...]}, causing `cannot unmarshal object into Go value\nof type []deploy.AppDepend\n[…]\nn test TestGetAppDependencies_AcceptsEnvelope covers four\ncases: envelope-with-rows / legacy-bare-with-row / envelope-empty /\nlegacy-bare-empty.\n\nSame fix pattern as I26-O (envVars envelope handling).",
          "is_bot": false,
          "headline": "rc.8: I27-AG (CLI dependencies-parse envelope)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T17:33:58Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "45983f502b6db445eac5c576ded5d72de8a9c209",
          "body": "Adds internal/deploy/wire_shape_i27y_test.go which captures the exact\nbytes the CLI POSTs to /prepare-cli-deployment for a monorepo-shaped\nDeployConfig (sourceDir=../../.., dockerfile=apps/api/Dockerfile)\nand asserts:\n- `dockerfile` lands on the wire as the FULL path \"apps/api/Dockerfile\"\n  (not bas\n[…]\nion wrapper). No\nfurther CLI work would change what the build server sees.\n\nThe CLI's available I27-Y surface (ResolveDockerfilePath pre-flight gate\n+ NginxDockerfileHint advisory) is already shipped.",
          "is_bot": false,
          "headline": "rc.8: I27-Y wire-shape proof + closure note",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T15:51:04Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "df2b31f7293aabc0928f983b3d5684ccd2ccd019",
          "body": "- I27-M / I27-N: configPath round-trips in pulled VCS yaml.\n  internal/apps/pull.go: new PulledApp.ConfigPath field with\n  yaml `omitempty`; BuildPulledApp reads raw[configPath]. With\n  sourceDir + dockerfile already surfacing via the conductor 17.7.0\n  AppDocument reconciliation, the third build-me\n[…]\nxDockerfileHint\n  helper. Non-blocking. Regression test covers bare-nginx,\n  unprivileged-drop-in (negative), multi-stage (intermediate\n  triggers), and token-boundary (`mynginxfork` doesn't trigger).",
          "is_bot": false,
          "headline": "rc.8: iter-27 R6 carry-forward CLI fixes (M/N + AE residual + G)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T14:09:03Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "e89749085c45f38bea0b976e39f5e2ceb55e6b28",
          "body": "Direct probe of the archive walker against iter27-sandbox proved\nsub-bug (a) is not a CLI bug — the walker traverses sourceDir\ncorrectly and includes every source file. The 4KB archive size was\njust the actual compressed size of the small test monorepo.\n\nSub-bug (b) — buildctl --opt filename=Dockerf\n[…]\n UploadTarball burn the round-trip.\n\nRegression test TestResolveDockerfilePath covers default +\nmonorepo apps/api/Dockerfile + missing + absolute + escapes +\nDockerfile.prod + directory-shaped target.",
          "is_bot": false,
          "headline": "rc.8: I27-Y re-triage + dockerfile path pre-flight",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T13:28:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "309b4fadf7acb85a2b7f7981269d79304b8e9a3a",
          "body": "The bare-500 the test bench attributed to conductor on `apps env-vars\nset -f /dev/stdin` was actually a CLI stdin-double-drain. The\nmissing-required pre-flight calls bodyFileProvidesField (which calls\nloadYAMLFile internally) before collectInput does; the stdin cache\nwas keyed on the literal `-` sen\n[…]\ne route returns a structured 400 + Content-Type\nhint instead of falling over.\n\nRegression tests:\n- stdin alias /dev/stdin caches like dash sentinel (I27-S/X)\n- isStdinPath enumerates every stdin alias",
          "is_bot": false,
          "headline": "rc.8: iter-27-R3 fold-in (I27-S/X stdin-cache root cause)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T10:14:11Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "01b8238d6440fe5f81a117f4ce362624b522e8a8",
          "body": "… hint)\n\n- I27-Z internal/deploy/service.go: DeployVCS returns &APIError on 4xx\n  (was plain fmt.Errorf), so cmd/apps_pull.go:emitJSONError's existing\n  errors.As fallback flattens the conductor body into the outer envelope\n  instead of double-encoding it as a quoted string.\n- I27-AB internal/dynacm\n[…]\nthe equals-form.\n- I27-AA verification: new sub-test pins dependents / dependencies\n  envelope keys through the shape-keyed unwrapArrayEnvelope helper.\n\nRegression tests for all three CLI fixes added.",
          "is_bot": false,
          "headline": "rc.8: iter-27-R2 fold-in (deploy --json purity + --tail 0 + bool flag…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-14T09:14:30Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "ce9a74bf311ed4a946d979a25aeb85df26582929",
          "body": "…urity + docs)\n\n- I27-H cmd/deploy_vcs.go: replace alarmist `configPath: <not sent> — using\n  whatever the AppDocument has stored` fallback with calm\n  `configPath: (using AppDocument default)`. Test updated.\n- I27-L internal/mcp/services.go: buildServicesPullArgs maps schema\n  `service_id` to CLI `\n[…]\not committed):\n  \"Pulled yaml is NOT a full round-trip\" + \"Static-site Dockerfile\n  templates (non-root constraint)\" subsections.\n\nI27-Q (apps --help duplication) not reproduced against current build.",
          "is_bot": false,
          "headline": "rc.8: iter-27 fold-in (deploy phrasing + MCP services_pull + --json p…",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-13T20:32:24Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "f5a7417533afa196ae77370402c461100a01215a",
          "body": "- I26-N: MCP tool schemas for map-of-object fields like `requires`\n  used to project `additionalProperties: {type: \"string\"}`, forcing\n  LLM clients to stringify their payload and then hit conductor's\n  `requires.X must be an object` refusal. Manifest.Field extended\n  with ValueType + ValueFields (p\n[…]\nst description appends \"This field has no `--requires`\n  flag; pass via `-f body.yaml`\". Both visible in a single --help.\n  Same closure pattern as I26-S.\n\nCHANGELOG + TEST_LOG.md stamped per finding.",
          "is_bot": false,
          "headline": "rc.8: iter-26-R4 fold-in (I26-N MCP requires schema + I26-P closure)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-13T19:03:42Z",
          "body_truncated": true,
          "is_coding_agent": false
        },
        {
          "oid": "542614c80b43ca72418f669ee3ef0e16a854698c",
          "body": "iter-26-R1 retest exposed two remaining CLI/conductor mismatches\nunder conductor's envelope-everywhere migration:\n\n- I26-O extended: my earlier env-vars fix only covered\n  apps_secret-env-vars + apps_env-vars. R1 found apps_list,\n  jobs_list, users, overrides, logs, network-access also moved to\n  en\n[…]\nss.\n\n5 new tests:\n  TestUnmarshalListResponse, TestUnwrapArrayEnvelope (output),\n  TestService_ListApps_AcceptsEnvelope, TestRefuseAmbiguousKeyValueArray.\n\nCHANGELOG + TEST_LOG.md stamped per finding.",
          "is_bot": false,
          "headline": "rc.8: iter-26-R1 fold-in (envelope-everywhere + k=v→JSON pointer)",
          "author_name": "Didier Breedt",
          "author_login": "didierbreedt-ros",
          "committed_at": "2026-05-13T17:13:10Z",
          "body_truncated": true,
          "is_coding_agent": false
        }
      ],
      "releases_count": 31,
      "commits_last_year": 154,
      "latest_release_at": "2026-07-16T18:50:56Z",
      "latest_release_tag": "v1.12.2",
      "releases_from_tags": false,
      "days_since_last_push": 11,
      "active_weeks_last_year": 14,
      "days_since_latest_release": 11,
      "mean_days_between_releases": 1.8
    },
    "community": {
      "has_readme": true,
      "has_license": true,
      "has_description": true,
      "has_contributing": false,
      "health_percentage": 50,
      "has_issue_template": false,
      "has_code_of_conduct": false,
      "has_pull_request_template": false
    },
    "ecosystem": {
      "packages": [
        {
          "name": "github.com/runos-official/cli",
          "exists": true,
          "license": null,
          "keywords": [],
          "ecosystem": "go",
          "matches_repo": true,
          "registry_url": "https://pkg.go.dev/github.com/runos-official/cli",
          "is_deprecated": false,
          "latest_version": "v1.12.2",
          "repository_url": "https://github.com/runos-official/cli",
          "versions_count": 48,
          "total_downloads": null,
          "dependents_count": null,
          "deprecation_note": null,
          "maintainers_count": null,
          "monthly_downloads": null,
          "first_published_at": null,
          "latest_published_at": "2026-07-16T18:40:48Z",
          "latest_version_yanked": null,
          "days_since_latest_publish": 11
        }
      ]
    },
    "popularity": {
      "forks": 0,
      "stars": 1,
      "watchers": 1,
      "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": [
        "Makefile"
      ],
      "api_schema_files": [],
      "has_devcontainer": false,
      "typecheck_configs": [],
      "toolchain_manifests": [
        "go.mod"
      ],
      "largest_source_bytes": 95672,
      "source_files_sampled": 133,
      "oversized_source_files": 9,
      "agent_instruction_files": [],
      "agent_instruction_max_bytes": null
    },
    "dependencies": {
      "manifests": [
        "go.mod"
      ],
      "advisories": {
        "error": null,
        "scope": null,
        "source": null,
        "findings": [],
        "collected": false,
        "malicious": [],
        "truncated": false,
        "by_severity": {},
        "advisory_count": 0,
        "affected_count": 0,
        "assessed_count": 0,
        "malicious_count": 0,
        "assessed_package": null,
        "unassessed_count": 0,
        "direct_affected_count": 0
      },
      "ecosystems": [
        "go"
      ],
      "dependencies": [
        {
          "name": "github.com/pmezard/go-difflib",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.0.0"
        },
        {
          "name": "github.com/spf13/cobra",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.10.1"
        },
        {
          "name": "github.com/spf13/pflag",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v1.0.9"
        },
        {
          "name": "golang.org/x/term",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v0.41.0"
        },
        {
          "name": "gopkg.in/yaml.v3",
          "manifest": "go.mod",
          "ecosystem": "go",
          "version_constraint": "v3.0.1"
        }
      ],
      "all_dependencies": {
        "error": "GitHub dependency-graph SBOM unavailable (404); the dependency graph may be disabled for this repository",
        "source": null,
        "packages": [],
        "collected": false,
        "truncated": false,
        "total_count": null,
        "direct_count": null,
        "indirect_count": null
      }
    },
    "maintainership": {
      "issues": {
        "open_prs": 0,
        "merged_prs": 0,
        "open_issues": 0,
        "closed_ratio": null,
        "closed_issues": 0,
        "closed_unmerged_prs": 0
      },
      "bus_factor": 1,
      "bot_contributors": 0,
      "top_contributors": [
        {
          "type": "User",
          "login": "didierbreedt-ros",
          "commits": 154,
          "avatar_url": "https://avatars.githubusercontent.com/u/243085795?v=4"
        }
      ],
      "contributors_sampled": 1,
      "top_contributor_share": 1
    },
    "quality_signals": {
      "has_ci": true,
      "has_tests": true,
      "ci_workflows": [
        "ci.yml",
        "release.yml"
      ],
      "has_docs_dir": false,
      "linter_configs": [],
      "has_editorconfig": false,
      "has_linter_config": false,
      "has_precommit_config": false
    },
    "security_signals": {
      "lockfiles": [
        "go.sum"
      ],
      "scorecard": {
        "checks": [
          {
            "name": "Binary-Artifacts",
            "score": 10,
            "reason": "no binaries found in the repo",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#binary-artifacts"
          },
          {
            "name": "Branch-Protection",
            "score": 0,
            "reason": "branch protection not enabled on development/release branches",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#branch-protection"
          },
          {
            "name": "CI-Tests",
            "score": null,
            "reason": "no pull request found",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#ci-tests"
          },
          {
            "name": "CII-Best-Practices",
            "score": 0,
            "reason": "no effort to earn an OpenSSF best practices badge detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#cii-best-practices"
          },
          {
            "name": "Code-Review",
            "score": 0,
            "reason": "Found 0/30 approved changesets -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#code-review"
          },
          {
            "name": "Contributors",
            "score": 0,
            "reason": "project has 0 contributing companies or organizations -- score normalized to 0",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#contributors"
          },
          {
            "name": "Dangerous-Workflow",
            "score": 10,
            "reason": "no dangerous workflow patterns detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dangerous-workflow"
          },
          {
            "name": "Dependency-Update-Tool",
            "score": 0,
            "reason": "no update tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#dependency-update-tool"
          },
          {
            "name": "Fuzzing",
            "score": 0,
            "reason": "project is not fuzzed",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#fuzzing"
          },
          {
            "name": "License",
            "score": 9,
            "reason": "license file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#license"
          },
          {
            "name": "Maintained",
            "score": 10,
            "reason": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#maintained"
          },
          {
            "name": "Packaging",
            "score": null,
            "reason": "packaging workflow not detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#packaging"
          },
          {
            "name": "Pinned-Dependencies",
            "score": 10,
            "reason": "all dependencies are pinned",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#pinned-dependencies"
          },
          {
            "name": "SAST",
            "score": 0,
            "reason": "no SAST tool detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#sast"
          },
          {
            "name": "Security-Policy",
            "score": 10,
            "reason": "security policy file detected",
            "documentation_url": "https://github.com/ossf/scorecard/blob/c395761df6afe1a69e476bc60a013a94bcbc153f/docs/checks.md#security-policy"
          },
          {
            "name": "Signed-Releases",
            "score": 0,
            "reason": "Project has not signed or included provenance with any releases.",
            "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": "8481c009241283058aa3a22aa1da703995385ce2",
        "ran_at": "2026-07-28T06:42:27Z",
        "aggregate_score": 4.5,
        "scorecard_version": "v5.5.0"
      },
      "has_codeql_workflow": false,
      "has_security_policy": true,
      "has_dependabot_config": false
    },
    "contribution_flow": {
      "collected": true,
      "ci_last_run_at": "2026-07-16T19:09:31Z",
      "oldest_open_prs": [],
      "last_merged_pr_at": null,
      "ci_last_conclusion": "SUCCESS",
      "oldest_open_issues": []
    }
  },
  "config": {
    "disabled_metrics": [],
    "disabled_categories": [],
    "disabled_components": {}
  },
  "source": {
    "url": "https://github.com/runos-official/cli",
    "host": "github.com",
    "name": "cli",
    "owner": "runos-official"
  },
  "metrics": {
    "overall": {
      "key": "overall",
      "band": "at_risk",
      "name": "Overall health",
      "note": null,
      "notes": [],
      "value": 49,
      "inputs": {
        "security": 45,
        "vitality": 76,
        "community": 21,
        "governance": 33,
        "engineering": 66
      },
      "components": []
    },
    "categories": [
      {
        "key": "vitality",
        "band": "good",
        "name": "Vitality",
        "value": 76,
        "weight": 0.22,
        "metrics": [
          {
            "key": "development_activity",
            "band": "moderate",
            "name": "Development activity",
            "note": null,
            "notes": [],
            "value": 66,
            "inputs": {
              "commits_last_year": 154,
              "human_commit_share": 1,
              "days_since_last_push": 11,
              "active_weeks_last_year": 14
            },
            "components": [
              {
                "key": "push_recency",
                "name": "Push recency",
                "detail": "last push 11 days ago",
                "points": 28.8,
                "status": "partial",
                "details": [
                  {
                    "code": "push_recency",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_cadence",
                "name": "Commit cadence",
                "detail": "14/52 weeks with commits",
                "points": 9.7,
                "status": "partial",
                "details": [
                  {
                    "code": "commit_cadence_weeks",
                    "params": {
                      "weeks": 14
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "commit_volume",
                "name": "Commit volume",
                "detail": "154 commits in the last year",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "commits_last_year",
                    "params": {
                      "count": 154
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "openssf_scorecard_maintained",
                "name": "OpenSSF Scorecard: Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "release_discipline",
            "band": "excellent",
            "name": "Release discipline",
            "note": null,
            "notes": [],
            "value": 90,
            "inputs": {
              "releases_count": 31,
              "latest_release_tag": "v1.12.2",
              "releases_from_tags": false,
              "days_since_latest_release": 11,
              "mean_days_between_releases": 1.8
            },
            "components": [
              {
                "key": "ships_releases",
                "name": "Ships releases",
                "detail": "31 releases published",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "releases_published",
                    "params": {
                      "count": 31
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "release_recency",
                "name": "Release recency",
                "detail": "latest release 11 days ago",
                "points": 36,
                "status": "met",
                "details": [
                  {
                    "code": "release_recency",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 36
              },
              {
                "key": "release_cadence",
                "name": "Release cadence",
                "detail": "a release every ~1.8 days",
                "points": 27,
                "status": "met",
                "details": [
                  {
                    "code": "release_cadence",
                    "params": {
                      "gap": 1.8
                    }
                  }
                ],
                "max_points": 27
              },
              {
                "key": "openssf_scorecard_signed_releases",
                "name": "OpenSSF Scorecard: Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "abandonment",
            "band": "excellent",
            "name": "Abandonment",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "cap": null,
              "state": "maintained",
              "guards": [],
              "signals": [],
              "red_flag": false,
              "multiplier_pct": 100,
              "declared_reason": null,
              "unverified_reason": null,
              "unanswered_open_prs": null,
              "unanswered_open_issues": null,
              "days_since_last_merged_pr": null,
              "days_since_last_human_commit": 11,
              "days_since_last_human_commit_is_floor": false
            },
            "components": [
              {
                "key": "project_is_still_maintained",
                "name": "Project is still maintained",
                "detail": "last human commit 11 days ago",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "abandonment_maintained",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Is the project alive — is code being written and are releases shipping?"
      },
      {
        "key": "community",
        "band": "critical",
        "name": "Community & Adoption",
        "value": 21,
        "weight": 0.18,
        "metrics": [
          {
            "key": "popularity",
            "band": "critical",
            "name": "Popularity & adoption",
            "note": null,
            "notes": [],
            "value": 1,
            "inputs": {
              "forks": 0,
              "stars": 1,
              "watchers": 1,
              "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": "1 watchers",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "watchers",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 15
              }
            ]
          },
          {
            "key": "community_health",
            "band": "at_risk",
            "name": "Community health",
            "note": null,
            "notes": [],
            "value": 44,
            "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": "license file present, not a recognized license",
                "points": 16.9,
                "status": "partial",
                "details": [
                  {
                    "code": "license_custom",
                    "params": {}
                  }
                ],
                "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
              }
            ]
          }
        ],
        "description": "Does the project have users, downloads, attention, and a welcoming setup for contributors?"
      },
      {
        "key": "governance",
        "band": "at_risk",
        "name": "Sustainability & Governance",
        "value": 33,
        "weight": 0.24,
        "metrics": [
          {
            "key": "maintainer_resilience",
            "band": "critical",
            "name": "Maintainer resilience (bus factor)",
            "note": null,
            "notes": [],
            "value": 10,
            "inputs": {
              "bus_factor": 1,
              "contributors_sampled": 1,
              "top_contributor_share": 1
            },
            "components": [
              {
                "key": "bus_factor",
                "name": "Bus factor",
                "detail": "1 contributor(s) cover half of all commits",
                "points": 9,
                "status": "partial",
                "details": [
                  {
                    "code": "bus_factor",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 54
              },
              {
                "key": "commit_distribution",
                "name": "Commit distribution",
                "detail": "top contributor authored 100% of commits",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "top_contributor_share",
                    "params": {
                      "share": 100
                    }
                  }
                ],
                "max_points": 22.5
              },
              {
                "key": "contributor_breadth",
                "name": "Contributor breadth",
                "detail": "1 contributors",
                "points": 1.4,
                "status": "partial",
                "details": [
                  {
                    "code": "contributors_sampled",
                    "params": {
                      "count": 1
                    }
                  }
                ],
                "max_points": 13.5
              },
              {
                "key": "openssf_scorecard_contributors",
                "name": "OpenSSF Scorecard: Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "responsiveness",
            "band": "critical",
            "name": "Issue & PR responsiveness",
            "note": "Excluded from scoring (no data or not applicable): Issue resolution, PR acceptance. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "issue_resolution",
                    "pr_acceptance"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 1,
            "inputs": {
              "merged_prs": 0,
              "open_issues": 0,
              "closed_issues": 0,
              "issue_closed_ratio": null,
              "closed_unmerged_prs": 0
            },
            "components": [
              {
                "key": "issue_resolution",
                "name": "Issue resolution",
                "detail": "no issues or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_issues_or_data",
                    "params": {}
                  }
                ],
                "max_points": 46.75
              },
              {
                "key": "pr_acceptance",
                "name": "PR acceptance",
                "detail": "no decided pull requests or no data",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_decided_prs_or_data",
                    "params": {}
                  }
                ],
                "max_points": 38.25
              },
              {
                "key": "openssf_scorecard_code_review",
                "name": "OpenSSF Scorecard: Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              }
            ]
          },
          {
            "key": "stewardship",
            "band": "at_risk",
            "name": "Ownership & stewardship",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "followers": 0,
              "owner_type": "Organization",
              "is_verified": null,
              "owner_login": "runos-official",
              "public_repos": 9,
              "account_age_days": 418
            },
            "components": [
              {
                "key": "ownership_backing",
                "name": "Ownership backing",
                "detail": "organization-owned",
                "points": 30,
                "status": "met",
                "details": [
                  {
                    "code": "owner_organization",
                    "params": {}
                  }
                ],
                "max_points": 30
              },
              {
                "key": "verified_domain",
                "name": "Verified domain",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 20
              },
              {
                "key": "owner_reach",
                "name": "Owner reach",
                "detail": "0 followers of runos-official",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "owner_followers",
                    "params": {
                      "count": 0,
                      "login": "runos-official"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "track_record",
                "name": "Track record",
                "detail": "9 public repos, account ~1 yr old",
                "points": 9.6,
                "status": "partial",
                "details": [
                  {
                    "code": "public_repos",
                    "params": {
                      "count": 9
                    }
                  },
                  {
                    "code": "account_age_years",
                    "params": {
                      "years": 1
                    }
                  }
                ],
                "max_points": 25
              }
            ]
          },
          {
            "key": "package_maintenance",
            "band": "excellent",
            "name": "Package maintenance",
            "note": null,
            "notes": [],
            "value": 100,
            "inputs": {
              "packages": [
                "github.com/runos-official/cli"
              ],
              "ecosystems": "go",
              "any_deprecated": false,
              "min_days_since_publish": 11
            },
            "components": [
              {
                "key": "published_resolvable",
                "name": "Published & resolvable",
                "detail": "1 package(s) on go",
                "points": 25,
                "status": "met",
                "details": [
                  {
                    "code": "packages_published",
                    "params": {
                      "count": 1,
                      "ecosystems": "go"
                    }
                  }
                ],
                "max_points": 25
              },
              {
                "key": "publish_recency",
                "name": "Publish recency",
                "detail": "latest publish 11 days ago",
                "points": 35,
                "status": "met",
                "details": [
                  {
                    "code": "publish_recency",
                    "params": {
                      "days": 11
                    }
                  }
                ],
                "max_points": 35
              },
              {
                "key": "version_history",
                "name": "Version history",
                "detail": "48 published versions",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "published_versions",
                    "params": {
                      "count": 48
                    }
                  }
                ],
                "max_points": 20
              },
              {
                "key": "not_deprecated",
                "name": "Not deprecated",
                "detail": "active, not deprecated or yanked",
                "points": 20,
                "status": "met",
                "details": [
                  {
                    "code": "package_not_deprecated",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          }
        ],
        "description": "Will the project survive its people — bus factor, responsiveness, who backs it, and package upkeep?"
      },
      {
        "key": "engineering",
        "band": "moderate",
        "name": "Engineering Quality",
        "value": 66,
        "weight": 0.2,
        "metrics": [
          {
            "key": "engineering_practices",
            "band": "moderate",
            "name": "Engineering practices",
            "note": "Excluded from scoring (no data or not applicable): OpenSSF Scorecard: CI-Tests. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "openssf_scorecard_ci_tests"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 60,
            "inputs": {
              "has_ci": true,
              "has_tests": true,
              "has_editorconfig": false,
              "has_linter_config": false,
              "has_precommit_config": false
            },
            "components": [
              {
                "key": "ci_workflows",
                "name": "CI workflows",
                "detail": "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": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 16
              },
              {
                "key": "pre_commit_hooks",
                "name": "Pre-commit hooks",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 9.6
              },
              {
                "key": "editorconfig",
                "name": ".editorconfig",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 6.4
              },
              {
                "key": "openssf_scorecard_ci_tests",
                "name": "OpenSSF Scorecard: CI-Tests",
                "detail": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 20
              }
            ]
          },
          {
            "key": "documentation",
            "band": "good",
            "name": "Documentation",
            "note": null,
            "notes": [],
            "value": 75,
            "inputs": {
              "topics": [
                "cli",
                "kubernetes",
                "runos",
                "source-available"
              ],
              "has_wiki": true,
              "homepage": "https://runos.com",
              "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": "https://runos.com",
                "points": 15,
                "status": "met",
                "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": "4 topics",
                "points": 10,
                "status": "met",
                "details": [
                  {
                    "code": "topics_count",
                    "params": {
                      "count": 4
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "wiki",
                "name": "Wiki",
                "detail": null,
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          }
        ],
        "description": "Are baseline engineering and documentation practices in place?"
      },
      {
        "key": "security",
        "band": "at_risk",
        "name": "Security",
        "value": 45,
        "weight": 0.16,
        "metrics": [
          {
            "key": "security_posture",
            "band": "at_risk",
            "name": "Security posture",
            "note": "Excluded from scoring (no data or not applicable): CI-Tests, Packaging. Remaining weights renormalized.",
            "notes": [
              {
                "code": "excluded_no_data",
                "params": {
                  "components": [
                    "ci_tests",
                    "packaging"
                  ]
                }
              },
              {
                "code": "weights_renormalized",
                "params": {}
              }
            ],
            "value": 45,
            "inputs": {
              "source": "openssf_scorecard",
              "checks_evaluated": 16,
              "scorecard_version": "v5.5.0",
              "checks_inconclusive": 2,
              "scorecard_aggregate": 4.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": "no pull request found",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 2.5
              },
              {
                "key": "cii_best_practices",
                "name": "CII-Best-Practices",
                "detail": "no effort to earn an OpenSSF best practices badge detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "code_review",
                "name": "Code-Review",
                "detail": "Found 0/30 approved changesets -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "contributors",
                "name": "Contributors",
                "detail": "project has 0 contributing companies or organizations -- score normalized to 0",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "dangerous_workflow",
                "name": "Dangerous-Workflow",
                "detail": "no dangerous workflow patterns detected",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              },
              {
                "key": "dependency_update_tool",
                "name": "Dependency-Update-Tool",
                "detail": "no update tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "fuzzing",
                "name": "Fuzzing",
                "detail": "project is not fuzzed",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "license",
                "name": "License",
                "detail": "license file detected",
                "points": 2.2,
                "status": "partial",
                "details": [],
                "max_points": 2.5
              },
              {
                "key": "maintained",
                "name": "Maintained",
                "detail": "30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10",
                "points": 7.5,
                "status": "met",
                "details": [],
                "max_points": 7.5
              },
              {
                "key": "packaging",
                "name": "Packaging",
                "detail": "packaging workflow not detected",
                "points": 0,
                "status": "excluded",
                "details": [
                  {
                    "code": "no_data",
                    "params": {}
                  }
                ],
                "max_points": 5
              },
              {
                "key": "pinned_dependencies",
                "name": "Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "sast",
                "name": "SAST",
                "detail": "no SAST tool detected",
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 5
              },
              {
                "key": "security_policy",
                "name": "Security-Policy",
                "detail": "security policy file detected",
                "points": 5,
                "status": "met",
                "details": [],
                "max_points": 5
              },
              {
                "key": "signed_releases",
                "name": "Signed-Releases",
                "detail": "Project has not signed or included provenance with any releases.",
                "points": 0,
                "status": "missed",
                "details": [],
                "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": "high_risk_jurisdiction_exposure",
            "band": "excellent",
            "name": "High-Risk Jurisdiction Exposure",
            "note": "Only high-confidence self-published location evidence affects this multiplier. Ambiguous matches are review-only; country evidence is not proof of nationality, citizenship, legal registration, malicious intent, or sanctions status.",
            "notes": [
              {
                "code": "jurisdiction_evidence_limits",
                "params": {}
              }
            ],
            "value": 100,
            "inputs": {
              "meaning": "self-published location evidence; not nationality or citizenship",
              "red_flag": false,
              "exposures": [],
              "policy_countries": [
                "Russia",
                "Iran",
                "North Korea"
              ],
              "review_only_matches": 0,
              "assessed_self_published_locations": 1
            },
            "components": [
              {
                "key": "policy_exposure_multiplier",
                "name": "Policy exposure multiplier",
                "detail": "no confirmed policy-scope location match",
                "points": 100,
                "status": "met",
                "details": [
                  {
                    "code": "jurisdiction_no_match",
                    "params": {}
                  }
                ],
                "max_points": 100
              }
            ]
          }
        ],
        "description": "Are visible security and supply-chain practices strong, with no malicious dependency and no unresolved high-risk jurisdiction exposure?"
      },
      {
        "key": "ai_readiness",
        "band": "moderate",
        "name": "AI Readiness",
        "value": 64,
        "weight": 0,
        "metrics": [
          {
            "key": "ai_agent_context",
            "band": "at_risk",
            "name": "Agent context & guidance",
            "note": null,
            "notes": [],
            "value": 40,
            "inputs": {
              "has_llms_txt": false,
              "legible_history_share": 0.98,
              "agent_instruction_files": [],
              "agent_instruction_max_bytes": null
            },
            "components": [
              {
                "key": "agent_instructions",
                "name": "Agent instructions",
                "detail": "no CLAUDE.md / AGENTS.md / editor rules",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_instructions",
                    "params": {}
                  }
                ],
                "max_points": 45
              },
              {
                "key": "machine_readable_docs_llms_txt",
                "name": "Machine-readable docs (llms.txt)",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 15
              },
              {
                "key": "legible_commit_history",
                "name": "Legible commit history",
                "detail": "98 of 100 human commits state their intent (structured subject or explanatory body)",
                "points": 40,
                "status": "met",
                "details": [
                  {
                    "code": "legible_history",
                    "params": {
                      "legible": 98,
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 40
              }
            ]
          },
          {
            "key": "ai_verify_loop",
            "band": "good",
            "name": "Verify loop (build / test / typecheck)",
            "note": null,
            "notes": [],
            "value": 71,
            "inputs": {
              "has_nix": false,
              "has_tests": true,
              "lockfiles": [
                "go.sum"
              ],
              "has_dockerfile": false,
              "typed_language": true,
              "bootstrap_files": [
                "Makefile"
              ],
              "has_devcontainer": false,
              "has_linter_config": false,
              "typecheck_configs": [],
              "agent_commit_share": 0,
              "toolchain_manifests": [
                "go.mod"
              ],
              "dependency_bot_commit_share": 0
            },
            "components": [
              {
                "key": "one_command_bootstrap",
                "name": "One-command bootstrap",
                "detail": "Makefile",
                "points": 18,
                "status": "met",
                "details": [
                  {
                    "code": "file_list",
                    "params": {
                      "files": "Makefile"
                    }
                  }
                ],
                "max_points": 18
              },
              {
                "key": "automated_tests",
                "name": "Automated tests",
                "detail": null,
                "points": 22,
                "status": "met",
                "details": [],
                "max_points": 22
              },
              {
                "key": "lint_format_config",
                "name": "Lint / format config",
                "detail": null,
                "points": 0,
                "status": "missed",
                "details": [],
                "max_points": 11
              },
              {
                "key": "static_type_checking",
                "name": "Static type checking",
                "detail": "Go (statically typed)",
                "points": 11,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "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": "no agent-authored commits among the last 100",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_agent_authored_commits",
                    "params": {
                      "sampled": 100
                    }
                  }
                ],
                "max_points": 10
              },
              {
                "key": "automated_maintenance",
                "name": "Automated maintenance",
                "detail": "no automated dependency updates observed",
                "points": 0,
                "status": "missed",
                "details": [
                  {
                    "code": "no_dependency_automation",
                    "params": {}
                  }
                ],
                "max_points": 8
              },
              {
                "key": "openssf_scorecard_pinned_dependencies",
                "name": "OpenSSF Scorecard: Pinned-Dependencies",
                "detail": "all dependencies are pinned",
                "points": 10,
                "status": "met",
                "details": [],
                "max_points": 10
              }
            ]
          },
          {
            "key": "ai_code_legibility",
            "band": "excellent",
            "name": "Code legibility for models",
            "note": null,
            "notes": [],
            "value": 96,
            "inputs": {
              "primary_language": "Go",
              "largest_source_bytes": 95672,
              "source_files_sampled": 133,
              "oversized_source_files": 9
            },
            "components": [
              {
                "key": "type_checkable_code",
                "name": "Type-checkable code",
                "detail": "Go (statically typed)",
                "points": 45,
                "status": "met",
                "details": [
                  {
                    "code": "statically_typed_language",
                    "params": {
                      "language": "Go"
                    }
                  }
                ],
                "max_points": 45
              },
              {
                "key": "manageable_file_sizes",
                "name": "Manageable file sizes",
                "detail": "9/133 source files over 60KB",
                "points": 51.3,
                "status": "partial",
                "details": [
                  {
                    "code": "oversized_source_files",
                    "params": {
                      "kb": 60,
                      "sampled": 133,
                      "oversized": 9
                    }
                  }
                ],
                "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-28T06:42:31.984813Z",
  "schema_version": "0.27.0",
  "badge_url": "https://raw.githubusercontent.com/inspect-software/badges/main/v1/r/runos-official/cli.svg",
  "full_name": "runos-official/cli",
  "license_state": "custom",
  "license_spdx": null
}

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

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

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