Repository navigation
chore: close the repository-hygiene gaps before the first release - #65
Merged
Merged
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
permissions: contents: readin 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.dorny/test-reporterruns on pull requests holdingchecks: write, andNuGet/loginmints the publishing key. The repository already pinned the badge action it runs with a secret; this applies the same pattern everywhere it belongs.env:instead of interpolating it onto the command line — the pattern the workflow already used for its non-secret input.Supply chain
dependabot.ymlfornuget(Central Package Management, so one file) andgithub-actions(which keeps the new SHA pins from freezing).dependency-review.ymlon pull requests. Build-timeNuGetAuditonly sees an advisory that already exists when the build runs; this reads the dependency diff.Issues and contributors
config.ymlroutes server bugs upstream, points vulnerabilities at the private advisory flow, and usage questions at the guide.CODEOWNERS.Cleanup
triage-labels.mddocumented an agent harness rather than the labels, mapping a third party's vocabulary onto identical strings. It now says what the labels mean.opencode-mcpsubmodule 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.