Skip to content

chore: close the repository-hygiene gaps before the first release - #65

Merged
Blind-Striker merged 8 commits into
masterfrom
chore/repo-hygiene
Sep 8, 2026
Merged

Blind-Striker merged 8 commits into
masterfrom
chore/repo-hygiene

Conversation

@Blind-Striker

Copy link
Copy Markdown
Collaborator

Stacked on #64 — merge that first, and this reduces to its own commit.

A four-dimension pre-release audit found the code in better shape than the repository around it. These are the repository-side gaps: what a scanner reads first, and what a first-time visitor meets before any of the code.

Workflow hardening

  • File-level permissions: contents: read in both workflows. Both jobs already set their own and the repository default is read, but neither is a property of the file — a job added later inherits silently, and Scorecard reads the file, not the setting.
  • A concurrency group on CI that cancels superseded pull-request runs and never a master run.
  • The three third-party actions move from movable tags to commit SHAs. Two are the exposure that matters: dorny/test-reporter runs on pull requests holding checks: write, and NuGet/login mints the publishing key. The repository already pinned the badge action it runs with a secret; this applies the same pattern everywhere it belongs.
  • Both publish steps pass their key through env: instead of interpolating it onto the command line — the pattern the workflow already used for its non-secret input.

Supply chain

  • dependabot.yml for nuget (Central Package Management, so one file) and github-actions (which keeps the new SHA pins from freezing).
  • dependency-review.yml on pull requests. Build-time NuGetAudit only sees an advisory that already exists when the build runs; this reads the dependency diff.

Issues and contributors

  • The bug template becomes a YAML form, so version / framework / OS / server version are answered rather than deleted. A second form covers the report this project actually attracts — a missing or mis-shaped generated operation — and asks for the document fragment that turns it into a fix.
  • config.yml routes server bugs upstream, points vulnerabilities at the private advisory flow, and usage questions at the guide.
  • CODEOWNERS.

Cleanup

  • triage-labels.md documented an agent harness rather than the labels, mapping a third party's vocabulary onto identical strings. It now says what the labels mean.
  • ADR-0006 loses a judgement about someone else's project and keeps the mechanism.
  • The opencode-mcp submodule is removed: nothing referenced it, and every recursive clone fetched it for one ADR sentence.

Not covered here — repository settings, which need the maintainer: Dependabot alerts and security updates, CodeQL default setup, and a ruleset on master (which Trusted Publishing now treats as the supply-chain boundary).

Gates: analyzers, Release build with zero warnings, both format passes, 5,138/5,138 tests.

Three consumer-facing documents had drifted from the tree they describe. The
test count in the README and the changelog predates the terminal work by 124
tests, and the package table still rendered dynamic NuGet badges for packages
that are not published, so both resolved to an error rather than a status. The
project status also claimed the first published package was the only thing
missing, which its own next bullet and the known issues already contradict.

The terminals guide never received the lifetime contract that shipped with the
terminal work. A caller reading it could not learn that canceling a read leaves
the connection usable, that undelivered frames queue and the queue is not
history, that termination delivers what it already holds before reporting its
outcome, that disposal abandons unread frames, or that a send carries a
thirty-second budget it can configure. That is now one shared section for both
families, relaying the exact contract to the runtime architecture that owns it,
and the changelog records the same addition with its SendTimeout option.

Every sample now leads with OPENCODE_PASSWORD. Upstream reads that name first
and calls OPENCODE_SERVER_PASSWORD its legacy name, still honored, so the
fallback survives as a single sentence on the page that owns the explanation
rather than as the name every example teaches.

The launcher has landed, so the packaging canon states where it ships instead
of where it will ship, and every guide page now carries the Date line the
documentation rules require.
The first NuGet.org release will be a 0.8.0-preview.N prerelease rather than
0.1.0, iterating the preview and patch numbers toward 1.0.0. Nothing is
published yet, so nothing migrates: the single-sourced VersionPrefix moves and
every place that quoted the old number follows it.

Prose that named an exact release number now names the release instead, because
that number changes with each preview while the version prefix and the nightly
format do not. The roadmap's open question narrows accordingly: the
prerelease-versus-stable decision is made, and only the release-notes flow
stays open.
nuget.org reserves the OpenCode. prefix for an unrelated owner, which is why
every push of OpenCode.Sdk was refused with a 409 the publish lane reported as
success. An upload probe confirmed the reservation is dot-terminated, so
OpenCodeAI.Sdk is accepted, and the first release ships under that identity
while the dispute runs its course.

Only the package id moves. Neither project declared a PackageId before, so both
inherited their assembly name; each now sets its own and nothing else changes.
The shipped assemblies stay OpenCode.Sdk.dll and OpenCode.Sdk.Extensions.dll,
every public namespace stays OpenCode.Sdk, the public API baseline is untouched,
and consumer source needs no edit at all: a project reference packs as a
dependency on OpenCodeAI.Sdk, so the two artifacts still resolve each other.

The install commands, package table, badge targets, and GitHub Packages links
follow the ids, the publish workflow's header names the ids the Trusted
Publishing policy has to cover, and the packaging canon records that the ids and
the assembly names are deliberately different things.
The fixture contract guard landed with the snapshot refresh and runs on every
target framework, so the suite is four tests larger than the number the readme
and the changelog carry.
The standalone door defaulted to `opencode serve`, described in its own summary as upstream's
default command shape. Upstream has no such default: its standalone mode re-invokes the executable
it is already running, because the CLI is opencode. An SDK cannot do that, so it has to name a
binary, and the one it should name is the one the line this SDK speaks to installs. That CLI
package declares exactly one executable and calls it `opencode2`.

Nothing exercised the old value. Every test supplies its own command, pointing at the pinned
submodule through bun, so the default was never spawned by anything and the mismatch survived: the
readme tells a reader to install `opencode2`, and the quick start then called `StartAsync()` with a
default that looked for `opencode`. That is the first sample on the package page, sold as needing
nothing running in advance.

A caller who wants a different binary still passes `Command`; a caller whose executable is missing
gets a spawn failure naming it.
The root readme is the package page, and it announced that the package does not exist: "Nothing is
on NuGet.org yet", an install command that "will not resolve", and three grey coming-soon badges.
That text is embedded at pack time and a published package's readme cannot be edited afterwards, so
it had to go before the first push rather than after it. The wording is now evergreen — the
commands say what to run, and a live version badge says whether a version exists — so it is correct
both before and after a publish, and does not need editing again at the next one.

Twenty-three relative links are now absolute. NuGet.org renders the embedded readme with a
restricted subset that blanks any non-absolute link, so the whole documentation table, the licence,
the changelog and every guide link resolved to nothing on the package page. They still work on
GitHub.

The package id differs from the assembly name, and nothing a consumer reads said so. Installing
`OpenCodeAI.Sdk` and writing `using OpenCode.Sdk;` now has an explanation where it is needed: a
note under both install blocks, the known-issue entry, and the package description itself, which
also gives the search index the name people will look for.

Corrections found alongside it: the readme stated a pin two refreshes stale, so it now relays to
the file that owns the pin instead of repeating a value that rots; the changelog gained the
`f9bc223` entry it never received and lost the migration notes written for nightly consumers of an
API no one has published; the guide counted 27 client families where the baseline has 28, missing
`Rpc`; the guide's install section installed nothing; the roadmap and the security policy still
named the old package ids; and a workflow rule pointed at a path that moved in the public/private
split.

Upstream's MIT notice now travels with the snapshot derived from it, in THIRD-PARTY-NOTICES.md,
packed beside the licence.
The audit that preceded this found the code in better shape than the repository around it. These
are the repository-side gaps that a scanner reads first and that a first-time visitor meets before
any of the code.

Workflows gain a file-level `permissions: contents: read`. Both jobs already set their own, and the
repository default is read, but neither is a property of the file: a job added later inherits
silently, and Scorecard reads the file rather than the setting. Continuous integration also gains a
concurrency group that cancels superseded pull-request runs and never a master run, whose badges
and nightly publish are the point of it.

The three third-party actions move from movable tags to commit SHAs. Two of them are the exposure
that matters: the test reporter runs on pull requests holding `checks: write`, and the NuGet login
action is what mints the publishing key, so a tag repointed under either reaches the project's
release identity. The repository already used this pattern for the badge action it runs with a
secret; it now uses it everywhere it applies, and Dependabot's actions ecosystem keeps the pins from
freezing. Both publish steps stop interpolating their key onto the command line and pass it through
the environment instead, which is what the workflow already did for its non-secret input.

Dependabot covers NuGet and actions. Dependency review joins the pull-request gate, where a newly
introduced package is judged in the diff rather than the next time someone builds — the build-time
audit only sees an advisory that already exists.

The bug template becomes a form, so the version, framework, operating system and server version it
asks for are answered rather than deleted; a second form covers the report this project actually
attracts, an operation that is missing or mis-shaped, and asks for the document fragment that turns
it into a fix. Issue routing gains the link that matters most: server bugs belong upstream.

`triage-labels.md` documented an agent harness rather than the labels, mapping a third party's
vocabulary onto identical strings. It now says what the labels mean to whoever reads them. An
architecture record loses a judgement about someone else's project and keeps the mechanism it was
there to explain, and the `opencode-mcp` submodule goes: nothing referenced it, and every recursive
clone was fetching it for that one sentence.
Code scanning is on and reports nothing, but it extracts C# without a build, which leaves the
database just under CodeQL's own confidence thresholds. That is a coverage limit rather than a
finding, and it has a known price to close, so it belongs with the other measured gaps instead of
being carried as a warning nobody wrote down.
@Blind-Striker
Blind-Striker merged commit 62c7511 into master Sep 8, 2026
9 checks passed
@Blind-Striker
Blind-Striker deleted the chore/repo-hygiene branch September 14, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant