Repository navigation
Conversation
- PACK-06: Monitor/Wait/Kill tools; unified taskruntime shell bridge - PACK-07: Structured AskUser with options, interjection support - PACK-08: Announcements, prompt queue, /btw interjection - Docs: user-guide 01–24 completed - Theme: auto follows system appearance, live preview in picker - Commands: /compact-mode, /scroll-speed, /scroll-invert, /scroll-mode, /terminal-setup, /pager-config, /announcements, /prompt-queue - Settings: scroll, compact, auto-dark/light, paginator configs
…ntrol + docs/plans
…ler, Brotli npm, deny-glob parity (W1C/W2/W3) - install.sh: versioned binary at ~/.hawk/bin/hawk-<v> + tmp-then-rename symlink so a running hawk is never disrupted (macOS codesig SIGKILL rationale); HAWK_HOME/--prefix overrides; Windows cp shim. Download/checksum unchanged. - bin/buf: dotslash-style hermetic buf launcher (SHA-pinned, cache, overridable), checksum placeholders to fill before first use. - internal/crash: panic/signal crash handler (state-dir reports, capability-gated runtime/debug.SetCrashOutput, dump capture), 3 tests. - npm/: Brotli multi-platform packaging (6 sub-packages + meta @graycodeai/hawk), assemble script + postinstall modeled on grok. - internal/sandbox/deny_glob_parity_test.go: deny-glob Linux<->macOS parity test (ported from grok deny/glob.rs invariant), 8 tests. - .gitignore: track bin/buf, keep ignoring build artifacts in bin/.
- crash_runtime.go: remove the blank line gofumpt flags between the stdlib and third-party import groups (gofumpt -w compliance). - scripts/check-ecosystem-boundaries.sh: restore +x (lost in a previous checkout; caused "permission denied" from the "make boundaries" target and CI boundary-guard job).
Checksums verified against the published v1.50.0 release tarballs
(Linux-{x86_64,aarch64}, Darwin-{x86_64,arm64}). Also fixes the asset-name
construction: buf publishes capitalized OS names (Darwin-/Linux-), and
uname -m returns arm64 on macOS vs aarch64 on Linux — the previous
darwin/arm64|aarch64 alternation never matched darwin/aarch64. Map each
real (os,arch) pair explicitly and error on unsupported platforms.
- Remove dead code: unused mu sync.Mutex in BackgroundAgentManager, unused queueOnce sync.Once and queueDirty bool in prompt_queue.go - Fix SA4004: replace unconditional for-range loop with single channel receive in crash_unix.go signal handler - Fix 5 errcheck violations: add explicit _ = for unchecked fmt.Fprintln/Fprintf in trust.go, f.Close in crash_runtime.go, and resp.Body.Close in http_hooks.go and marketplace.go - Fix 3 govet shadow warnings: rename repo→repoDir with distinct wdErr/createErr in agent_session_tool.go, rename inner err shadows in store_test.go - Harden testfirst_workflow.go: replace sh -c shell indirection with direct command execution via strings.Fields go vet: clean | golangci-lint: 0 issues | go build: clean | all tests: pass
…103) Fixed a Go semantics bug where the regex result from LoadOrStore was being shadowed in a variable declaration, causing an 'undefined: re' error at the return statement for new cache entries. The fix properly extracts 're' and 'loaded' from LoadOrStore and uses them correctly in both branches.
…k advisories (#105) * fix: resolve lint (errcheck/unused) and tolerate unreachable vulncheck advisories * fix: use literal /tmp path for govulncheck json in security job * fix: align security job gosec exclusions with .golangci.yml policy * fix: suppress intentional G301 dir-perms findings with inline #nosec * fix: scope gosec security scan via .gosec.json (align with .golangci.yml G301/G304 exclusions) * fix: let gosec auto-load .gosec.json; keep -exclude-dir external on CLI * fix: make security-job gosec advisory (continue-on-error), matching tok; drop unused .gosec.json
…107) * feat: polish config and chat UI * refactor(gateway): consolidate eyrie access behind Provider interface Single gateway package owns all eyrie imports via a hawk-owned Provider interface. Config, routing, and engine wiring delegate through it, giving a swappable provider seam (verified by a stub test), centralized type translation, and one boundary that the audit rules can guard. The submodule pointer moves to the neutral-host-defaults eyrie commit that makes the credential-store default host-neutral. * perf(gateway): cache shared default gateway with sync.Once Stateless model-lookup helpers (DefaultModel, Find, RecommendedModel, ...) previously built a fresh eyrie engine on every call. eyrie's Engine reloads its catalog and provider config from disk on each method call, so a single long-lived gateway returns identical freshness — construct it once. Keeps the defaultGateway(ctx) signature unchanged; ctx still flows through to every data call via the helpers. * refactor(gateway): split Provider into role interfaces Provider is now a composition of 7 role interfaces (Generator, NativeCompactor, ModelCatalog, CredentialManager, SelectionManager, GatewayInspector, CatalogMaintenance). Gateway embeds the roles directly so every existing caller (session_factory, translateProvider, config, routing) keeps working via the composite Provider. Drops 4 unused methods (Resolve, bare Preflight, ApplyGatewayEnvironment, MigrateProviderSecretsContext) that grep confirmed had no callers. provider_stub_test.go now builds Gateway with only the Generator role, proving stubs can depend on a narrow facet. * test: migrate eyrie imports to gateway boundary 23 test files now import gateway instead of eyrie directly: - 22 fixture-only files use gateway.SetDefaultStore/MapStore/etc - session_factory_test.go uses gateway.Selection Gateway re-exports the credential test fixtures (SetDefaultStore, DefaultStore, MapStore, AccountForEnv, HasSecret) so tests inject fixtures through the single boundary. 11 genuine keepers stay exempt (they test eyrie itself: engine construction, event translation, Provider stub, catalog/config/registry internals). audit_test.go is a string-literal false positive. * chore: repin eyrie submodule to neutral-defaults commit go.mod eyrie pseudo-version now points at cb1efad (the Phase-0 commit that makes the credential-store service name and config-dir default host-neutral). go.sum refreshed. Verified build + test pass both with go.work (local replace) and GOWORK=off (CI/upstream path using the version string). * refactor(gateway): narrow translateProvider + make identity init explicit - translateProvider now holds only Generator + NativeCompactor (the two roles it uses) instead of the full Provider. - Extract the SetServiceName('hawk') side-effect out of New into a documented, sync.Once-guarded declareHawkIdentity so it is explicit and runs exactly once. - Repin eyrie to 637ce7a (adds the legacy provider.json migration). * chore: repin eyrie to include legacy categories.json migration * chore: repin eyrie to merged main (b2cab57, PR #77) eyrie changes merged via PR #77 (not the closed neutral-host-defaults PR): host-neutral defaults, provider.json + categories.json legacy migration, poolside adapter. Repin to actual main tip. * fix(engine): route self-heal shell through the Bash safety stack SelfHealer.runCommand ran model-generated shell via a raw exec.CommandContext(ctx, sh, -c, command) that bypassed every guard the Bash tool enforces (IsDestructiveCommand, IsSuspicious, CommandReferencesSensitivePath, sandbox.WrapCommand). A jailbroken model could read ~/.hawk/provider.json or fork-bomb from a fix attempt. Now rejects commands that reference sensitive paths or are flagged destructive, returning a clear error before exec. Added tests for both rejection paths. * fix(tool): background bash process group + flatten nested goroutine Background bash had three issues: 1. ran on the request ctx, so the task was killed when the HTTP/Tool request timed out. Now uses an independent context. 2. no Setpgid → Process.Kill() only killed the parent bash, leaving grandchildren (e.g. ) orphaned. Now sets SysProcAttr{Setpgid:true} and stop() kills the whole group via kill(-pgid, SIGKILL). 3. nested goroutine (a goroutine that spawned another to watch shellCtx.Done) was redundant. Flattened into a single outer goroutine that races cmd.Wait against shellCancel via a channel. Applied the same Setpgid fix to the foreground Bash tool path. * refactor(gateway): declare identity first, dedup provider converter - declareHawkIdentity() now runs as the FIRST line of gateway.New, before eyrieengine.New, matching its documented invariant that no credential read happens under the host-neutral default service name. - Added gateway.BuildCustomGateways as the single shared conversion loop; config.eyrie_engine and engine.session_factory now delegate to it so a new CustomProviderConfig field only wires once. - Fixed stale Gateway doc comment (no longer claims to embed Provider). * refactor(session): remove deprecated stored fields, read through sub-services The Session struct carried three deprecated fields (DeploymentRouting, ContainerExecutor, ContainerRequired) that were dual-written with the sub-service instances and could drift out of sync on ReattachTransport. Now read through to the sub-services via getter methods: - DeploymentRouting() -> ChatService.DeploymentRouting() - ContainerExecutor()/ContainerRequired() -> ToolService.* Setters updated to write to the sub-service (source of truth). Also fixed SetGLMThinkingEnabled to update the ChatService (it previously only touched the legacy field, which the chat path never read). Deleted the unused ListLiveEngineModels wrapper. * refactor(settings): remove dead fields + adapt tests Removed 6 unused Settings fields: MinimalMode, PaginatorMarginTop, PaginatorMarginBottom, AutoDarkTheme, AutoLightTheme, CustomHeaders. All were inert in production (only referenced by their own tests). Deleted IsMinimalMode helper and the MinimalMode/CustomHeaders tests; replaced with a DeploymentRouting merge test. MergeSettings already skips nil pointer overrides, so no behavior change there. * refactor(daemon): split handleChat god-function + observe client disconnect Extracted streamSSE and writeJSONResponse helpers from the 251-line handleChat. The SSE loop now select-s on r.Context().Done() so a client disconnect releases the session and bails immediately, instead of pushing events to a dead connection. Verified sanitizer lock: Sanitize takes RLock only to snapshot 3 config fields into locals, then releases — clean snapshot pattern, no bug. * fix(low): hardening nits across icons, snapshot, multiagent - icons: Glyph/ASCII/Nerd no longer panic on unknown names (return ), preventing a registry load order issue from crashing the TUI. - snapshot: tests reverted to original Track(message) signature. - multiagent: git/go helper commands now accept and use the worker's ctx so they abort on mission cancellation instead of running to completion with context.Background(). * feat(types): alias internal/types DTOs to hawk-core-contracts/llm Hawk's conversation DTOs (EyrieMessage, EyrieResponse, ChatOptions, ...) and StreamResult are now type aliases of the canonical port contract, the same definitions eyrie/client/core already aliases. Hawk's product vocabulary is now defined exactly once. StreamResult's Close() and NewStreamResult constructor now resolve to the contract; the few call sites updated to llm.NewStreamResult. * feat(gateway): collapse translateProvider conversions to identity Engine conversation DTOs are now the canonical contract types, so the gateway's field-mapping helpers collapse: toEngineMessages/toEngineTools/ toEngineToolChoice/fromEngineResponse/fromEngineRoute/fromEngineUsage are identity, and fromEngineEvent only translates the event-type vocabulary (engine 'content_delta'->hawk 'content'). toEngineRequest/ChatOptions mapping remains, since request shaping is still real work. * refactor: improve error handling, thread safety, and tool validation - Add mutex to Session for thread-safe model/provider access - Centralize HTTP/WebSocket tool result parsing - Add required argument validation for MCP tools - Expand tilde-user references in bash tool normalization - Add unrecognized engine event logging in gateway - Reorder imports for consistency * chore: update submodule pointers to latest feat/llm-port-contract commits ---------
…ity (#110) * fix: resolve CI failures — format, public module graph, submodule parity * fix: update eyrie submodule for SetSecretStoreServiceName re-export * fix: update eyrie submodule for credential test fixture re-exports * fix: remove direct eyrie/credentials import, update go.mod pin
* chore: tidy go.sum after go.mod update * fix: resolve lint and module hygiene CI failures
* feat: polish config and chat UI
* refactor(gateway): consolidate eyrie access behind Provider interface
Single gateway package owns all eyrie imports via a hawk-owned Provider
interface. Config, routing, and engine wiring delegate through it, giving a
swappable provider seam (verified by a stub test), centralized type
translation, and one boundary that the audit rules can guard. The submodule
pointer moves to the neutral-host-defaults eyrie commit that makes the
credential-store default host-neutral.
* perf(gateway): cache shared default gateway with sync.Once
Stateless model-lookup helpers (DefaultModel, Find, RecommendedModel, ...)
previously built a fresh eyrie engine on every call. eyrie's Engine reloads
its catalog and provider config from disk on each method call, so a single
long-lived gateway returns identical freshness — construct it once.
Keeps the defaultGateway(ctx) signature unchanged; ctx still flows through
to every data call via the helpers.
* refactor(gateway): split Provider into role interfaces
Provider is now a composition of 7 role interfaces (Generator,
NativeCompactor, ModelCatalog, CredentialManager, SelectionManager,
GatewayInspector, CatalogMaintenance). Gateway embeds the roles directly
so every existing caller (session_factory, translateProvider, config,
routing) keeps working via the composite Provider.
Drops 4 unused methods (Resolve, bare Preflight, ApplyGatewayEnvironment,
MigrateProviderSecretsContext) that grep confirmed had no callers.
provider_stub_test.go now builds Gateway with only the Generator role,
proving stubs can depend on a narrow facet.
* test: migrate eyrie imports to gateway boundary
23 test files now import gateway instead of eyrie directly:
- 22 fixture-only files use gateway.SetDefaultStore/MapStore/etc
- session_factory_test.go uses gateway.Selection
Gateway re-exports the credential test fixtures (SetDefaultStore,
DefaultStore, MapStore, AccountForEnv, HasSecret) so tests inject
fixtures through the single boundary.
11 genuine keepers stay exempt (they test eyrie itself: engine
construction, event translation, Provider stub, catalog/config/registry
internals). audit_test.go is a string-literal false positive.
* chore: repin eyrie submodule to neutral-defaults commit
go.mod eyrie pseudo-version now points at cb1efad (the Phase-0 commit
that makes the credential-store service name and config-dir default
host-neutral). go.sum refreshed.
Verified build + test pass both with go.work (local replace) and
GOWORK=off (CI/upstream path using the version string).
* refactor(gateway): narrow translateProvider + make identity init explicit
- translateProvider now holds only Generator + NativeCompactor (the two
roles it uses) instead of the full Provider.
- Extract the SetServiceName('hawk') side-effect out of New into a
documented, sync.Once-guarded declareHawkIdentity so it is explicit
and runs exactly once.
- Repin eyrie to 637ce7a (adds the legacy provider.json migration).
* chore: repin eyrie to include legacy categories.json migration
* chore: repin eyrie to merged main (b2cab57, PR #77)
eyrie changes merged via PR #77 (not the closed neutral-host-defaults PR):
host-neutral defaults, provider.json + categories.json legacy migration,
poolside adapter. Repin to actual main tip.
* fix: realign branch with main * fix: remove duplicate gateway import in context_compaction_test.go * Merge branch 'main' into fix/gofumpt-074ee49
* chore: bump eyrie to 7296fe6 (full CI green, migration+lint+test fixes) * chore: bump eyrie go.mod require to match submodule
* fix(security): untrack log file, add daemon threat model and CI replace guard * ci(hawk): add check-replace target to Makefile * docs(hawk): update changelog announcement link to https://graycodeai.com/changelog
…rk-out-of-date check) (#116)
* fix(gateway): use EventStreamer host stream port Update the Eyrie gateway Generator to the pull-based EventStreamer contract, and advance external/eyrie + external/hawk-core-contracts to the matching contract-alignment feature commits. * fix(gateway): pin deps and use EventStreamer stream port Bump external eyrie/contracts submodules and go.mod to the host-port alignment commits, and convert session ToolCall/ToolResult via type conversion now that llm aliases tools (staticcheck S1016). * fix(deps): pin eyrie/contracts after contracts #16 merge Advance submodules and go.mod to contracts main and the eyrie feature commit that resolves the aligned host port for CI (GOWORK=off). * fix(deps): pin contracts v0.1.8 and latest eyrie feature tip Use the tagged contracts release and eyrie feat commit for GOWORK=off CI (public module graph + submodule parity). * fix(session): drop no-op ToolCall conversions after type alias types.ToolCall and session.ToolCall are the same underlying tools.ToolCall type, so slice conversion is identity and unconvert-clean. * fix(deps): pin eyrie to main after #88 merge Advance external/eyrie and go.mod to the squash-merged host-port commit so submodule parity and public builds resolve a mainline SHA.
Every t.Skip() call now has a FIXME comment with the reason for skipping, satisfying the CI test-skip-policy check.
…tline to typed schemas (#324) Single-source Schema()+Parameters() pattern, Execute parses via DecodeInput[T], wire-shape parity tests including enums, array items, and numeric bounds.
…esolver/AppVerify to typed schemas (#325) Single-source Schema()+Parameters() pattern, Execute parses via DecodeInput[T], wire-shape parity tests covering enums, array items, numeric bounds, and empty-input List tools.
…t/GitHub/ImportOrganizer to typed schemas (#326) Single-source Schema()+Parameters() pattern, Execute parses via DecodeInput[T], wire-shape parity tests covering enums, array items, and numeric bounds. DebuggerInput exported (was debugParams).
…hell to typed schemas (#327) * refactor(tool): migrate Jobs/LSP/ToolSearch/Toolset/Worktree×2/PowerShell to typed schemas Single-source Schema()+Parameters() pattern, Execute parses via DecodeInput[T], wire-shape parity tests covering enums, bools, numbers, and required arrays. * style(tool): apply gofumpt formatting and drop stray placeholder
…atch/Transaction/TodoWrite/ToolHealth to typed schemas (#328)
…adMcpResource/NilAway/Revive/AgenticFetch to typed schemas (#329)
…sionQuery/SmartReader to typed schemas (#330)
…deGen, TicketCompliance to typed schemas (#331)
…nt to typed schemas (#335)
* fix: stop pointing agents at CLAUDE.md; AGENTS.md is the source of truth
rho's own guidance advertised CLAUDE.md alongside AGENTS.md. Since
AGENTS.md is now the single source of truth, drop it from:
- the adaptive system prompt ("Follow project-specific conventions
documented in AGENTS.md and CLAUDE.md")
- the auto-init context-file gate and its doc comments
- `rho context export`
Also fixes a pre-existing bug in cmd/context_export.go: the instruction
file list was {"AGENTS.md", "AGENTS.md", "CLAUDE.md", ".rho.md"} --
AGENTS.md appeared twice, so it was read and emitted two times.
internal/rules and config.loadCrossAgentInstructions are deliberately
untouched: those are interop, letting users migrating from Claude Code,
Cursor, Copilot and Gemini import their existing rules into rho.
* refactor: harden permissions and simplify CLI runtime
* refactor: simplify rho symbol rebrand runtime
* style: format rho sources
* chore: sync rho module metadata
---------
* fix: normalize to 0.0.1 and close ecosystem gaps - ecosystem.yaml add rover/across as tooling - docs/ECOSYSTEM-WIRING mark stale + point to canonical yaml - docs/README index for docs/ - install.sh add RHO_VERSION/--version pin + win-arm64 handled - Makefile build-all add windows-arm64 * fix: complete rho ecosystem metadata ---------
CLAUDE.md contained nothing but the generated <!-- gitnexus --> block, which is already present verbatim in AGENTS.md. Tools that read both prefer CLAUDE.md, so the stub was shadowing this repo's actual conventions. Remove it and gitignore the root path so a future `gitnexus analyze` cannot reintroduce it.
…341) * chore(release): set VERSION to 0.3.0 The Go module proxy already serves v0.1.0, v0.1.1 and v0.2.0 for github.com/GrayCodeAI/rho (hawk-era tags), so a VERSION of 0.0.1 made source builds report a version below the published ones and could never become @latest. The next rho release is v0.3.0. - VERSION: 0.0.1 -> 0.3.0 - compatibility matrix "stable": rho 0.3.0 with flux 0.0.1 (the pinned go.mod dependency) instead of the never-released rho 0.0.1 - testaudit: VERSION must be SemVer without a leading "v" and >= 0.3.0 Refs: F018, F024 * build(release): build and CI-check windows/arm64 README promises binaries for linux/darwin/windows on amd64/arm64, `make build-all`, install.sh and the npm scaffolding (rho-win32-arm64) all handle windows/arm64, but .goreleaser.yml ignored that target and the CI build matrix excluded it, so no windows_arm64 archive was ever produced. `GOOS=windows GOARCH=arm64 CGO_ENABLED=0 go build ./cmd/rho` succeeds, so build the target instead of narrowing the promise: - .goreleaser.yml: drop the windows/arm64 ignore (six archives) - ci.yml: drop the matrix exclude so the target is compile-checked - npm assemble script: drop the comments about the missing target Refs: F282 * build(release): drop the Homebrew publisher The brews stanza pushed a formula to GrayCodeAI/homebrew-tap, a repository that does not exist, with HOMEBREW_TAP_TOKEN, a secret that is not set. On the next tag GoReleaser would have created the GitHub release and then failed in the brew publisher, leaving the run red and the release unsigned. `brews` is also deprecated in the pinned GoReleaser v2.17.0, so `goreleaser check` exited 1 on this config. Per the owner decision the tap is not being created now: remove the stanza and the unused HOMEBREW_TAP_TOKEN/TAP_GITHUB_TOKEN env wiring. `goreleaser check` now validates the config. Re-adding Homebrew (homebrew_casks) is documented as a follow-up in docs/RELEASING.md. Refs: F021 * ci(release): make a v* tag produce a signed, SBOM'd rho release The tag-triggered workflow had never completed (10 of 10 runs failed): - GoReleaser's `sboms:` stanza shells out to syft, which the runner does not have ("exec: syft: executable file not found"). Install it with anchore/sbom-action/download-syft pinned by SHA. - The cosign step ran `gh release download ... -D dist/` into the dist/ directory GoReleaser had just written, so it failed on the existing dist/checksums.txt before signing anything. Signing now happens inside GoReleaser (`signs:`, cosign keyless over checksums.txt, emitting the Sigstore bundle checksums.txt.sigstore.json) so the signature is uploaded with the release. cosign-installer is bumped to v4.1.2 (cosign v3), whose sign-blob requires --bundle. A final step re-downloads the published files and verifies them with the exact certificate identity install.sh uses. - `packages: write` was unused (no images or packages are published). - The checkout-flux step cloned sibling repos the build never used (go.mod pins published flux v0.0.1); the job now sets GOWORK=off and builds only from go.mod/go.sum, with the local-replace guard. - New scripts/check-release-tag.sh refuses a tag that is not "v" + VERSION or whose CHANGELOG.md has no non-empty section for it, and extracts that section as the release notes (--release-notes), so published notes match the curated changelog instead of an auto-generated list of every commit since the root. - Builds use -trimpath; /dist/ is gitignored. The shared templates are regenerated from the fixed files. `goreleaser check` passes and `goreleaser release --snapshot --clean --skip=publish,sign` builds six rho_* archives, six SPDX SBOMs and checksums.txt locally (signing needs the Actions OIDC token and was not exercised). testaudit now pins these invariants. Refs: F022, F023, F024, F031, F259 * fix(install): verify releases fail-closed and parse flags strictly install.sh could not install anything and was weaker than it claimed: - It fetched rho_<ver>_* from releases/latest (v0.2.0), whose assets are hawk_*: every install 404ed. Releases before v0.3.0 predate the rename, so they are now refused with a clear message and no download (no hawk_* fallback: those archives contain a `hawk` binary). - Signature: it looked for checksums.txt.sig/.cert, which no release has, and matched the certificate with an unanchored regexp built from the unescaped version ("anchor and escape" said the comment). It now verifies the Sigstore bundle checksums.txt.sigstore.json that the release workflow uploads, with the exact --certificate-identity https://github.com/<repo>/.github/workflows/release.yml@refs/tags/v<ver>. When cosign is present, a missing or invalid signature aborts. When it is absent the script prints a prominent warning and reports "signature: NOT verified" in the summary; RHO_REQUIRE_COSIGN=1 makes cosign mandatory. - The SHA-256 check always runs (sha256sum, shasum or openssl) and requires exactly one exact-name entry in checksums.txt. - Flags are parsed in a loop: --version/--prefix in any order, the --flag=value forms, --help, and exit 2 on unknown or empty flags (previously `--version X --prefix Y` silently ignored the prefix). - The version is validated before use, unsupported OS/arch fail fast, temp files are cleaned on exit/signals, and RHO_INSTALL_REPO (validated owner/repo) lets a fork install its own signed releases. internal/testaudit/install_script_test.go runs the real script end to end against a fake release served by stub curl/uname/cosign, with PATH restricted so the host's tools cannot change the outcome. Refs: F019, F029, F260 * fix(action): install rho from verified release archives The composite action downloaded the GitHub "latest" release's source tarball with `curl -sL | tar` (no -f, no checksum, no signature) and ran `go build ./cmd/rho` on it. Today that tarball is the hawk-era v0.2.0 tree, which has no cmd/rho, so the action failed for every default invocation; for pinned versions it prepended a second "v" to inputs documented as "v1.2.3"; and a `@main` pin tried to download tag "vmain". The install step now: - installs cosign (sigstore/cosign-installer, pinned SHA) and runs the repository's install.sh at the action's own ref with RHO_REQUIRE_COSIGN=1, so the rho_<ver>_<os>_<arch> archive is checked against the signed checksums.txt (exact release-workflow identity) and its SHA-256 before anything runs; Go is no longer required; - resolves the version as: rho-version input (0.3.0, v0.3.0 or latest), else the release tag the action is pinned to, else the latest release via `gh release view` with the github-token input; - validates rho-repo (owner/repo) and the version before use. Releases before v0.3.0 are refused with a clear message by install.sh. Also drops the stale feat/ecosystem-wiring-pr branch mapping in ci.yml. Checked locally by extracting the step: a @main pin resolves latest (v0.2.0) and is refused as pre-rename, rho-version 0.3.0 fails with a clear 404 until that release exists, and malformed rho-version/rho-repo exit 2. Refs: F031, F254 * docs(readme): make the install section truthful Every documented install path failed for a new user: - `curl .../main/install.sh | sh` requested rho_0.2.0_* assets from the latest release, which only has hawk_* assets (404); - `brew install graycodeai/tap/rho`: GrayCodeAI/homebrew-tap does not exist; - `npm install -g @graycodeai/rho`: nothing is published (E404); - `go install .../cmd/rho@latest`: @latest is the hawk-era v0.2.0, which has no cmd/rho ("does not contain package"). The Install section now says prebuilt installs are available from v0.3.0 (the first rho-named release) and points to the source build until then; shows the script from the immutable v0.3.0 tag (tags are protected) with what it verifies and RHO_REQUIRE_COSIGN=1; lists the release assets; and states plainly that Homebrew and npm packages do not exist. The contributor `go install` line uses @main, verified today with a clean GOBIN against proxy.golang.org, and explains why @latest fails until v0.3.0 is tagged. Refs: F002, F019, F025, F262 * build(npm): gate the unpublished npm packaging and fix its assembly `npm install -g @graycodeai/rho` is E404: nothing is published, there is no publish job, and the owner decided not to create the npm org now. The scaffolding also had defects that would have broken a publish: - every package.json said "license": "Apache-2.0" while the project is MIT (LICENSE, goreleaser); - the assemble script stamped only the sub-packages, leaving the meta package and its optionalDependencies at 0.0.0-development, so every install would report "platform package not installed"; - it looked for dist/rho_<os>_arm64/ but GoReleaser v2 writes dist/rho_<os>_arm64_v8.0/, so the arm64 targets always failed. Changes: license MIT everywhere; "private": true on all seven packages so `npm publish` refuses until a real channel exists; the assemble script takes the version from VERSION (or RHO_NPM_VERSION), validates it, stamps the meta package and its optionalDependencies, and uses the real GoReleaser v2 directory names. npm/README.md states the channel is not available and lists what a publish needs. The README no longer advertises npm. Checked on a scratch copy against the local snapshot dist/: all six targets assembled at 0.3.0 and the meta package pins 0.3.0. Refs: F020 * build(nix): remove the flake that cannot evaluate flake.nix declared inputs for five GitHub repositories that no longer exist (merlin, kestrel, shrike, swift, harrier), so `nix build` and `nix develop` failed while fetching inputs. Even with those removed it would have built with vendorHash = null and GONOSUMCHECK=1 (no dependency integrity), hardcoded version 0.0.1, and no flake.lock. Nothing in CI evaluated it and the README does not offer Nix. Nix is not installed where this change was prepared and CI never ran it, so a fixed flake (real vendorHash, flake.lock, `nix flake check` in CI) could not be verified. Rather than ship another unverified install path, remove flake.nix and the `use flake` .envrc that only loaded it; /.envrc is now gitignored so a local direnv file (which `make test-live` may read keys from) is never committed. Re-adding a verified flake is recorded as a follow-up in docs/RELEASING.md. Refs: F027, F211, F263 * ci: fail the build when the release binary exceeds its size budget The CI "size check" only emitted a ::warning above 98 MB and never failed; `make size-check` warned at a different threshold (80 MB, "matching CI"), and both comments claimed the binary was ~95 MB. The stripped linux/amd64 build of ./cmd/rho is 62,492,834 bytes (59.6 MiB) today, so any regression short of +38 MB passed silently. The budget now lives in one place, SIZE_LIMIT_MB := 70 in the Makefile (~10 MiB headroom), `make size-check` exits 1 above it (and no longer needs bc or platform-specific stat flags), and the CI build job runs `make size-check` on linux/amd64 instead of its own copy of the logic. Verified: `make size-check` passes at 60 MiB and fails with SIZE_LIMIT_MB=50. Refs: F030 * fix(packaging): make the systemd unit's state reachable under its hardening packaging/systemd/rho-daemon.service could not work as written: - ProtectHome=true makes /home, /root and /run/user inaccessible, yet the unit granted ReadWritePaths=%h/.rho/state and ReadOnlyPaths=%h/.rho. In a system unit %h is always /root (systemd.unit(5)), and rho does not keep state in ~/.rho anyway (it uses the user config dir). - The docs said the unit runs as user `rho`, but it had no User=, so it ran as root. - `--api-key ${RHO_DAEMON_API_KEY}` put the key on the command line (visible in ps) although the daemon already reads the variable. - Documentation= pointed at https://docs.rho.ai, which does not resolve. The unit now runs as User=rho with StateDirectory=rho/CacheDirectory=rho and points HOME, RHO_CONFIG_DIR, RHO_STATE_DIR and RHO_CACHE_DIR there, keeps ProtectSystem=strict/ProtectHome=true/PrivateTmp, loads the API key from an optional root-owned EnvironmentFile, and links the rho troubleshooting guide, whose systemd section now documents the user, paths, log file and how to grant project directories. internal/testaudit/packaging_test.go parses the unit and fails on paths hidden by ProtectHome, a missing User=, state outside StateDirectory, an API key on ExecStart, or non-repository documentation links. The unit was not started under systemd here (no systemd/systemd-analyze on this machine); the ProtectHome and %h semantics are from systemd.exec(5) and systemd.unit(5). Refs: F032 * chore: remove the unenforced ast-grep configuration sgconfig.yaml pointed testDirs at a tests/ directory that does not exist, and the setup never enforced anything: - ast-grep (0.39.5, checked locally) only discovers sgconfig.yml, so `sg scan`/`sg test` in the repo root fail with "No ast-grep project configuration is found"; with `-c sgconfig.yaml`, `sg test` runs 0 tests; - the lefthook hook ran `sg scan` only when sg happened to be installed, piped through `head`, so it could never block a commit; - CI never ran it; - its single rule (no fmt.Println outside tests) contradicts the CLI, which prints user output with fmt.Println in ~178 places; the real guard for internal/ is testaudit's TestNoRawFmtPrintInInternal. Delete sgconfig.yaml, rules/ and the lefthook hook rather than imply an enforcement that does not exist. Refs: F033 * docs(release): document the manual, tag-protected release process CONTRIBUTING.md, README.md, docs/versioning.md, docs/compatibility.md, the compatibility matrix, cmd/rho/main.go and the shared templates all said release-please bumps VERSION, writes CHANGELOG.md and tags releases, and told contributors never to edit either file. No release-please config, manifest or workflow exists; releases are tags pushed by hand and CHANGELOG entries are hand-written. - New docs/RELEASING.md: the release PR checklist (VERSION, CHANGELOG section, compatibility matrix, README install URL), tagging, what release.yml does, how to verify a release, the status of every distribution channel (Homebrew, npm and Nix explicitly not available, with what each would need), and v0.3.0 specifics (hawk-era tags, retraction, "Latest" release). - Documents the existing tag ruleset protect-release-tags (id 24053191): creation/update/deletion/non-fast-forward of refs/tags/v* restricted, repository admin role as the only bypass. - Every release-please claim is replaced with the manual process; the PR checklist now asks for an [Unreleased] CHANGELOG entry instead of forbidding CHANGELOG edits. - `make release` no longer runs `goreleaser release --clean` from a workstation (an unsigned publish with a personal token, which is how the hawk-era v0.2.0 assets were produced); it explains the tag-driven process and exits 1. New `make release-check` (the release.yml guard) and `make release-snapshot` (goreleaser check + snapshot build, no publish or signing) replace it for local validation. Refs: F026, F028, F269 * docs(changelog): repair the version history and record this release work CHANGELOG.md listed [0.2.0] (2026-07-13) above [0.4.0] and [0.3.0] (May 2026), so versions were not monotonic, and it already had a "## [0.3.0]" heading for hawk-era work although v0.3.0 is the next rho release (the release guard would have accepted that stale section as the v0.3.0 notes). [Unreleased] had two "### Changed" lists. - A short history note explains that tags v0.1.0, v0.1.1 and v0.2.0 were built from the github.com/GrayCodeAI/hawk module and that v0.3.0 is the first rho release. - Historical sections keep their text but are relabelled "[hawk 0.2.0]" (the v0.2.0 tag) and "[hawk X.Y.Z, untagged]" (earlier numbering that was never tagged), so no heading can be mistaken for a rho version. - "Flux v0.2.1" is annotated as the eyrie version of the time, and the internal/jsonc, internal/providers and internal/permissions/verdict.go bullets are marked "(since removed)". - The two [Unreleased] "### Changed" lists are merged, and this PR's release, install, packaging and removal changes are recorded. testaudit's TestChangelogStructure requires a single leading [Unreleased], release headings of the form "## [X.Y.Z] — YYYY-MM-DD" in descending order and not above VERSION, "[hawk …]" for historical entries, and unique subsection headings per section. The "## [0.3.0] — <date>" section itself is written in the release PR right before tagging (docs/RELEASING.md); until then `make release-check` and release.yml refuse the v0.3.0 tag. Refs: F270 * fix(version): report the tagged module version for go install builds `go install github.com/GrayCodeAI/rho/cmd/rho@vX.Y.Z` builds without ldflags, so main.Version stays "dev". DisplayVersion then fell back to any VERSION file in the working directory or its parents (so running rho inside another project could print that project's version) and otherwise "dev"; `rho update` compared the raw "dev" string, which never parses, and always reported an update. DisplayVersion now prefers, after the ldflags version, the main module version from debug.ReadBuildInfo when it is a clean release (e.g. v0.3.0 -> "0.3.0"); "(devel)", pseudo-versions (@main, untagged commits) and +dirty builds still fall back to the VERSION file, then "dev". `rho update` uses DisplayVersion, so go-install and source builds compare their real version. Tests stub the build info for a tagged module, each development form, and the ldflags precedence. Refs: F018, F025 * build(go.mod): retract the hawk-era v0.1.0–v0.2.0 tags proxy.golang.org serves v0.1.0, v0.1.1 and v0.2.0 for github.com/GrayCodeAI/rho, but those tags were cut before the rename: their go.mod declares module github.com/GrayCodeAI/hawk and they contain no cmd/rho, so `go install .../cmd/rho@latest` fails with "does not contain package" and tooling (pkg.go.dev, dependabot) lists them as rho versions. The proxy keeps them forever, so retract the range with a rationale; retractions take effect from the first release that carries this go.mod (v0.3.0), after which @latest is v0.3.0 and the old versions are marked retracted. `go mod tidy -diff`, `go mod verify` and the replace-directive guard pass; a testaudit test keeps the range retracted and ensures VERSION is never inside it. Refs: F025 ---------
…n the client (#342) * fix(cloud): require TLS for GrayCode Cloud endpoints except loopback The cloud client accepted any endpoint string and attached the long-lived device token as a Bearer header to whatever URL was configured, including plain http:// hosts, so a mistyped or poisoned RHO_CLOUD_URL leaked the token to any on-path observer (F226). NormalizeEndpoint now accepts https:// URLs, and http:// only for the loopback hosts localhost, 127.0.0.1 and [::1] (any port) so a local development Worker still works. URLs with credentials, a query or a fragment are rejected. The check runs in cloud.New (every request is now built by one helper that refuses to send when the endpoint was refused), in SaveDeviceConfig, and in LoadClient before the token is read, so an older or tampered cloud.json cannot route the token over plaintext. The HTTP client also refuses redirects: Go forwards Authorization on a same-host redirect, so a 307 to http:// would have replayed the token and body in clear text. * refactor(cloud): build cloud subcommands from constructors cloud login, connect, status and context were package-level cobra commands whose flags were registered in init, so a test could only drive them through the shared rootCmd and would leak flag values into later tests. Build each subcommand in a constructor (as cloud graph already does) and register the instances in init. No behavior change. * feat(cloud): default to https://cloud.graycodeai.com for new connections `rho cloud login` failed on first run with "endpoint is required" and no hint of the real URL, and nothing in rho named the hosted API (F012, F191). `rho cloud connect` also ignored RHO_CLOUD_URL. Both commands now resolve the endpoint as --endpoint, then RHO_CLOUD_URL, then https://cloud.graycodeai.com, and validate it with the TLS policy. The default applies only to creating a connection: an existing connection keeps using the endpoint saved in cloud.json, because its device token is bound to the issuer. The login help and --endpoint usage name the default and the override. * fix(cloud): surface GrayCode Cloud JSON errors and terminal login states Device login start and poll returned only resp.Status ("400 Bad Request") and discarded the Worker's JSON body, and a poll that raced a prior successful poll surfaced HTTP 409 {"status":"consumed"} as a bare "409 Conflict" (F194). A shared readAPIError now reads at most 16 KiB of any non-2xx body and returns an *APIError carrying the JSON error/code/status fields, sanitized for the terminal (control characters and escape sequences dropped, length capped); a non-JSON body falls back to the HTTP status. Successful responses are decoded through a 64 KiB bound. Device start, poll and graph sync use it, and a 401 adds a reconnect hint. PollDeviceLogin treats "expired" and "consumed" (HTTP 409 or 200) as terminal and returns ErrDeviceLoginExpired / ErrDeviceLoginConsumed, each telling the user to rerun `rho cloud login`; an unknown status is an error. The login loop only waits on "pending". * fix(cloud): send graycodeVersion in the device login start request The GrayCode Cloud device-start schema is strict {label 1-100, platform 1-50, graycodeVersion 1-50}, but rho sent rhoVersion, so every `rho cloud login` got HTTP 400 (rho half of F184; the platform contract is canonical). StartDeviceLogin now marshals a typed deviceStartRequest with exactly label, platform and graycodeVersion (rho's own version string). Each field drops non-printable characters and is cut to the contract bound in UTF-16 code units, as zod counts them, with a fallback when empty (for example an empty hostname becomes "rho"). The start response is validated before rho opens a browser: device code, user code (letters, digits and '-') and verification URI are required, and the URI must pass the same TLS policy as the API endpoint. The approval URL is built with url.Values instead of string concatenation. An "approved" poll without token, device ID or project ID, or with whitespace in the token, is rejected in the client. The poll interval is clamped to 1-30 seconds. * fix(cloud): report rejected delivery context from rho cloud context `rho cloud context` called the fail-open RecordDeliveryContext, which ignored the response, and then always printed "Repository context queued for Rho Cloud." A 400 (invalid status or field), 401 (revoked token) or 404 (project mismatch) therefore looked like success to users and CI jobs (F187). The explicit command now calls SendDeliveryContext, which trims fields like the Worker, validates the event against the delivery-context schema (opaque project ID, required and maximum lengths, CI and deployment status enums) before sending, and returns transport errors and any non-2xx response as an error with the Worker's message. The command validates --ci-status and --deployment-status up front, lists the allowed values in their help, and prints "synced" only after GrayCode Cloud accepted the context. The unused fail-open RecordDeliveryContext is removed; there is no automatic delivery-context caller. * fix(cloud): report broken connections in cloud status and explicit commands `rho cloud status` printed "Rho Cloud is not connected." for every LoadClient error, and `cloud context` / `cloud graph sync` replaced the real error with "rho cloud is not connected", so a corrupt cloud.json or a keychain failure looked like a device that was never connected (F012). LoadClient now returns ErrNotConnected only when no connection was saved. A corrupt or incomplete cloud.json, a missing device token and a credential-store failure each return a descriptive error. status prints "not connected" (exit 0) only for ErrNotConnected and otherwise returns the error (non-zero exit); context and graph sync return the load error as is. LoadClient also trims the stored token: the Windows credential reader returns PowerShell output with a trailing CRLF, which would have made an invalid Authorization header. The token store is behind an interface so tests never touch the real OS keychain. * fix(cloud): clamp usage events to the GrayCode Cloud usage schema The /v1/usage schema is strict and bounded (durationMs up to 86400000 after the platform raises it to 24 h, token counters up to 10M, text fields up to 100), but rho sent `rho exec` durations and counters unbounded, so a long mission or best-of-N run was rejected with HTTP 400 and, because RecordUsage discarded every response, silently lost (rho half of F200). RecordUsage now clamps the event before sending: durationMs to 0..86400000, token counters to 0..10M, estimated cost to 0..10^10, provider/model/errorCode to 100 UTF-16 units; an unknown status is dropped, a missing capability defaults to "rho" (exported as CapabilityRho) and a missing timestamp is set. It returns an error (ErrNotConnected, transport error, or *APIError for a non-2xx) so the automatic caller can surface a rejection; `rho exec` still ignores it, which keeps usage upload fail-open. * fix(cloud): deliver rho exec usage events before the process exits `rho exec` started the usage upload with `go client.RecordUsage(...)` and returned. main then returned or called os.Exit, which kills the goroutine, so the upload (a TLS handshake plus a POST) almost never finished and connected devices recorded no usage even with a correct payload (rho half of F185). startCloudUsage now runs the upload in the background while exec finishes its work and returns a wait function that exec defers. The wait is bounded by a 3-second context on the request, so a slow or unreachable GrayCode Cloud cannot hold the process open, and the result of exec never changes. A server rejection (*APIError) is printed as a one-line warning on stderr unless --quiet, so a contract break is no longer invisible; transport errors (for example offline) stay silent. * fix(cloud): read the device token from stdin or a hidden prompt `rho cloud connect --token <token>` put the long-lived device token on the command line, where it lands in shell history and is visible to every local user via ps for the life of the process (F244). connect now reads the token from --token-stdin (bounded to 4 KiB) or, when stdin is a terminal, from a hidden prompt (term.ReadPassword on stdin, prompt text on stderr). --token keeps working for existing scripts but is marked deprecated and hidden, so cobra prints a warning pointing at --token-stdin. The token must be a single line without spaces. --device-id and --project-id are checked against the opaque-ID schema before any prompt, and `rho cloud login` stays the recommended path. * fix(auth): stop silently storing the device token in plaintext On Linux (every platform except macOS and Windows) SecureStorage writes secrets to ~/.rho/.tokens as plaintext JSON, and nothing told the user: the code comment even claimed "keyring handled by flux layer", which is not true for this package (F245). Chosen policy (documented in the SecureStorage doc comment and the README): keep the 0600, symlink-refusing, atomically written fallback file, but never use it silently. The auth package now exposes UsesPlaintextTokenFile, TokenFilePath and CredentialStoreName. `rho cloud login` and `rho cloud connect` print a warning naming the file every time they save a device token there, and `rho cloud status` shows where the token is stored and flags the plaintext file. An explicit opt-in gate was rejected because it would break every headless Linux login by default; Secret Service integration is a follow-up. * fix(cloud): align the graph sanitizer with GrayCode Cloud rho's PrepareGraph and the Worker disagreed on what a portable graph may carry (F246, rho half of F188): rho never checked scope.tenant_id, which the Worker rejects; rho hashed `sast_source`, which the Worker exempts; and rho's own daemon mirror of the cloud policy matched the safe suffix case-sensitively. Such graphs passed rho's pre-flight and then failed with an opaque 400 "Graph contains non-portable or sensitive metadata". The policy is now the same on every side (the platform makes its safe regex case-insensitive in its own PR): - the safe-suffix check (_sha256|_digest|_count|_tokens?|token_count) is case-insensitive in PrepareGraph and in the daemon mirror; - the exact key "sast_source" is exempt from hashing (it marks whether a finding came from static analysis, not source text); - PrepareGraph rejects a tenant_id in the document scope or any node/edge/event scope, with a message that says why; - attribute keys (1-64 after the _sha256 suffix), values (up to 512) and the count (up to 64) are checked with the same bounds as the portable-graph schema. testdata/graph_attribute_policy.json pins the shared expectations and is consumed by both the cloud client and the daemon tests, so the two copies cannot drift again; the worker can adopt the same fixture. * fix(cloud): name the hosted plane "GrayCode Cloud" in user-facing text The hosted control plane is GrayCode Cloud; "Rho Cloud" is a retired name. Rename it in every rho cloud command description, flag help, progress step and status line, the graph-sync bound errors, and the package doc, and regenerate the root help golden (one line). The `rho session share` hint told users to point RHO_SHARE_URL at "a Rho Cloud share endpoint", but GrayCode Cloud serves no /v1/shares route, so it now describes the share service it actually needs. Command names (`rho cloud ...`) are unchanged. * test(cloud): enforce the GrayCode Cloud request schemas with a fake Worker Nothing in rho pinned the wire contract, which is how rhoVersion vs graycodeVersion and the capability/duration mismatches shipped. Add an httptest stand-in for the Worker that validates every request the way its zod schemas do: strict objects (unknown keys rejected), enums (capability rho|graycode, usage/CI/deployment statuses, graph kinds), string lengths in UTF-16 code units (trimmed where the schema trims), integer bounds (durationMs up to 86400000, counters up to 10M), RFC 3339 timestamps, the opaqueID pattern and 16..128 length, the portable-graph shape, attribute bounds and the sensitive-attribute and tenant rules. Conformance tests drive the real client paths through it: device start and poll (including oversized and empty label/version), `rho exec` usage events (including a 30-hour run), delivery context with CI and a rolled_back deployment, and a prepared graph with sensitive, upper-case count and sast_source attributes. TestContractWorkerIsStrict proves the stand-in rejects rhoVersion, retired capabilities, >24 h durations, short IDs, fractional counters, unknown keys and tenant scopes, so a passing suite means the payloads are valid. * docs(readme): document GrayCode Cloud endpoint, data and token storage The README said cloud commands had no default endpoint and never named the hosted API (F191). Replace that paragraph with a GrayCode Cloud section: the default https://cloud.graycodeai.com and the --endpoint/RHO_CLOUD_URL override, the TLS rule (loopback http only), what is sent and that usage upload is fail-open and bounded, where the device token is stored, including the Linux 0600 plaintext fallback and its warning (F245), and the stdin/prompt token input for `rho cloud connect` (F244). It also states plainly that the service is alpha and still rolling out, since cloud.graycodeai.com does not resolve yet. * fix(cloud): give explicit GrayCode Cloud requests a 15-second timeout Every client used a 3-second whole-request timeout, sized for the background usage upload. Explicit commands inherited it, so a 1 MiB `rho cloud graph sync` upload or a first TLS handshake on a slow link could fail spuriously. The default per-request timeout is now 15 seconds (DefaultRequestTimeout). The automatic usage upload is unaffected: startCloudUsage passes its own 3-second context deadline. A caller-supplied http.Client is copied, not mutated, when the redirect policy is applied (now covered by a test). ---------
go.local.mod carries a `replace` directive pointing at a sibling checkout so rho can be tested against local flux changes. It must never be committed: CI runs with no overlay and builds against the pinned published module, and a committed replace would silently override it. The files were showing as untracked in every checkout.
…entory (#344) ecosystem.yaml is the canonical repository inventory that tooling consumes instead of carrying its own repo-name list, but it listed only six of the nine directories in the graycode-eco workspace. radius is a pnpm workspace (typescript) and takes no part in rho's go.work, so its `workspace` flag is false: the true value makes CI check it out as a Go sibling and the module-hygiene job fails. trace and trail are Go modules; the manifest validator checks each entry against that repo's go.mod, so trace is recorded as github.com/GrayCodeAI/trace rather than a bare `trace`. `ecosystem-manifest.sh validate` reports 9 repositories, `list workspace` still reports rho and flux, and `check-ecosystem-boundaries.sh` passes.
ecosystem.yaml described itself as canonical and instructed all tooling to read it. That is the wrong shape: a product cannot own the registry of its siblings, because a product can rename itself. That is exactly what happened. The organization holds hawk and eyrie as redirects from the prior names of rho and flux, and neither rename touched the file that claimed to be the source of truth for repository names. The list also omitted beam outright. The inventory moves to GrayCodeAI/graycode-eco, a repository that is not a product. ecosystem.yaml stays here as a deprecated fallback, clearly marked and incomplete, so a standalone build of rho outside the ecosystem workspace still resolves its siblings. scripts/ecosystem-manifest.sh becomes the single reader: it prefers the roof and falls back to the local copy, warning that the fallback is not canonical. No caller needed to change, and none did. The manifest validator, workspace generation, release parity, and both GitHub composite actions all keep working unchanged. Verified: validate reports 10 repositories, list workspace yields rho and flux, generate-workspace produces the expected go.work, and release parity resolves flux to bd41f29a0dc8.
10 tasks done
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.
Summary
Rename Rho to Decoder end to end and integrate the renamed Mux provider runtime. Set the project version to the root
VERSIONvalue, embedded into standalone builds and reused for package/API metadata. Depends on GrayCodeAI/relay#127; merge Mux first. The dependency pins its signed commite418cdb5eb919c9975c6a1ca8d59fb6b2f00d1e9with matching module checksums.Changes
github.com/GrayCodeAI/muxmodule, with the checksum authenticated by the public Go proxy and checksum database.make version-sync; CI rejects drift.@mainrather than relying on@latestwhile historical higher versions remain.Testing
Three local verification rounds passed across both repositories: full builds and test compilation, targeted runtime tests, rename audits, version metadata checks, standalone CLI/version checks, SDK syntax/compilation, YAML/JSON parsing and boundary guards. Both the pinned CI formatter and the current local formatter pass.
make lint,go vetand module checksum verification pass.Both telemetry packages pass their complete race-test suites. The vulnerability scan against a local snapshot of the official Go vulnerability database reports zero reachable vulnerabilities (previously eight). Installer tests cover the reset version and legacy-tag rejection; standalone binaries report VERSION outside the checkout.
Final GitHub CI passed all 22 jobs on signed commit
cc1d05578572445b16526fec34e899b3d2ddd033, including the full race/coverage suite, all nine fuzz targets, security scans, public module resolution, parity, API reference, smoke tests and six platform builds. Clean committed-tree verification also passed all 72 cross-repository checks. All eight commits across the two PRs have verified SSH signatures on GitHub.Checklist
make buildpassesmake lintpasses