From 214afde650ffa41e09a382f320801d5e97de96b0 Mon Sep 17 00:00:00 2001 From: Sahil Ahuja Date: Wed, 3 Jun 2026 20:46:44 +0530 Subject: [PATCH 1/3] =?UTF-8?q?ci:=20Teardown=20shll.ai=20Help-Tree=20Publ?= =?UTF-8?q?ish=20(push=20=E2=86=92=20pull)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the final 'Publish help tree to shll.ai' step from .github/workflows/release.yml, per shll.ai's help-dump-contract Teardown Directive (push model → pull model). The single self-contained step carried all four push components — producer CI (rk help-dump + jq validate), PR-opening logic (clone/branch/commit/push + gh pr create), auto-merge wiring (gh pr merge --auto), and SHLLAI_TOKEN usage — all removed together (95 deletions). Preserve 'rk help-dump' unchanged: it is now the single contract surface shll.ai pulls from on a schedule. Reconcile docs/memory architecture (help-dump row, Release Flow section, changelog). The removed step was the last and best-effort, so the GitHub Release and Homebrew tap path is unaffected. --- .github/workflows/release.yml | 95 -------- docs/memory/run-kit/architecture.md | 9 +- .../.history.jsonl | 12 + .../.status.yaml | 49 ++++ .../intake.md | 212 ++++++++++++++++++ .../plan.md | 117 ++++++++++ 6 files changed, 395 insertions(+), 99 deletions(-) create mode 100644 fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl create mode 100644 fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml create mode 100644 fab/changes/260603-iak3-teardown-shllai-publish-ci/intake.md create mode 100644 fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3f6a267..573d2eff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -156,98 +156,3 @@ jobs: git add Formula/rk.rb git commit -m "rk ${version}" git push - - # Placed LAST in the job — after the GitHub Release and Homebrew tap are - # already published — so the documented "best-effort, can't break the - # release" guarantee actually holds: any fatal line here runs only once - # the user-visible release artifacts exist. - - name: Publish help tree to shll.ai - env: - SHLLAI_TOKEN: ${{ secrets.SHLLAI_TOKEN }} - run: | - version="${{ steps.version.outputs.version }}" - - # Emit the CLI help tree from the versioned linux/amd64 artifact so - # the dumped version is the real release version (not "dev"). A failure - # here (binary missing, dump errors, invalid JSON) is a real defect in - # THIS repo's build, so it correctly fails the job under `bash -e`. - # Because this step is last, that failure does NOT preempt the already- - # published GitHub Release / Homebrew tap above. - mkdir -p help - dist/rk-linux-amd64/rk help-dump help/run-kit.json - jq empty help/run-kit.json - - # The cross-repo publish is best-effort: the GitHub Release and Homebrew - # tap are already produced by the time we get here, so a hiccup that is - # external to this repo (shll.ai unreachable, token scope, repo-level - # auto-merge disabled, a same-version re-run whose branch already - # exists, or an unchanged help tree) MUST NOT fail an otherwise- - # successful release. We surface the outcome in the log and leave any - # opened PR for a manual merge if auto-merge is unavailable. - publish_to_shllai() { - # Open a PR (not a direct push) into sahil87/shll.ai with the - # refreshed help tree, mirroring the Homebrew-tap token-clone pattern. - # The clone is EXTERNAL (an unreachable shll.ai must not fail the - # release), so it stays best-effort with `|| return 1`. - git clone "https://x-access-token:${SHLLAI_TOKEN}@github.com/sahil87/shll.ai.git" /tmp/shll-ai || return 1 - - branch="rk-help-dump-${version}" - - # Copying the produced help tree into the clone is INTERNAL: a failure - # here means our source is wrong or the destination is unwritable — a - # real defect in THIS repo's publish, so it MUST fail the job loudly - # rather than be swallowed by the `if publish_to_shllai` wrapper (which - # neutralizes `set -e`). The explicit `exit 1` terminates the whole - # `run:` shell regardless of the `if`, so a bad copy fails the Release - # job and can never reach the "unchanged — nothing to publish" guard - # below. `mkdir -p` guarantees the destination dir exists in a fresh - # clone whose tree has no `help/` at that commit (the v2.1.8 blocker). - src="${PWD}/help/run-kit.json" - dest="/tmp/shll-ai/help/run-kit.json" - echo "Publishing help tree: src=${src} dest=${dest}" - mkdir -p /tmp/shll-ai/help - cp "$src" "$dest" || { echo "::error::Failed to copy ${src} to ${dest}"; exit 1; } - ls -l "$dest" - - cd /tmp/shll-ai - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - if [ -z "$(git status --porcelain help/run-kit.json)" ]; then - echo "help/run-kit.json unchanged since last release — nothing to publish." - return 0 - fi - - # --force-with-lease tolerates a re-dispatched release at the same - # version (branch may already exist from a prior attempt). - git checkout -B "$branch" || return 1 - git add help/run-kit.json - git commit -m "rk help tree ${version}" || return 1 - git push --force-with-lease --set-upstream origin "$branch" || return 1 - - # gh authenticates via GH_TOKEN; reuse the shll.ai token (contents + - # pull-request write). Explicit arguments only — no untrusted - # shell-string interpolation. If a PR for this head already exists, - # `gh pr create` is a no-op error we tolerate before attempting merge. - GH_TOKEN="$SHLLAI_TOKEN" gh pr create \ - --repo sahil87/shll.ai \ - --base main \ - --head "$branch" \ - --title "rk help tree ${version}" \ - --body "Automated help-tree update for rk ${version}." \ - || echo "PR for ${branch} may already exist; continuing to merge." - - # --auto requires repo-level "Allow auto-merge" on shll.ai. If that - # is off, leave the PR open for a manual merge rather than failing. - GH_TOKEN="$SHLLAI_TOKEN" gh pr merge "$branch" \ - --repo sahil87/shll.ai \ - --auto \ - --squash \ - || echo "Auto-merge unavailable for ${branch}; PR left open for manual merge." - } - - if publish_to_shllai; then - echo "shll.ai help-tree publish step completed." - else - echo "::warning::shll.ai help-tree publish failed (external to this repo); release is unaffected. Any PR opened was left for manual merge." - fi diff --git a/docs/memory/run-kit/architecture.md b/docs/memory/run-kit/architecture.md index 6b517713..86e0285a 100644 --- a/docs/memory/run-kit/architecture.md +++ b/docs/memory/run-kit/architecture.md @@ -442,7 +442,7 @@ Single-view model: there are no page transitions or per-page chrome injection. T | `context` | `context.go` | Agent-optimized environment info — prints Environment (dynamic tmux pane context), Capabilities (terminal windows, iframe windows, proxy, categorized CLI commands), and Conventions (tmux user options, window lifecycle, SSE reactivity) sections as plain text. Uses `exec.CommandContext` (5s timeout) for tmux queries targeting the pane's own server via `tmux.OriginalTMUX` (restores `$TMUX` in child env since `internal/tmux` init strips it). Server URL via `config.Load()`. Gracefully degrades outside tmux. CLI Commands section lists `rk riff` under **Workflow** | | `init-conf` | `initconf.go` | Scaffold default tmux.conf to `~/.rk/tmux.conf` from embedded config and create `~/.rk/tmux.d/` drop-in directory. `--force` to overwrite config (tmux.d contents untouched) | | `riff` | `riff.go` | Create a worktree, open a new tmux window in it, and launch a Claude Code session. Repeatable pane flags: `--skill ` (claude pane), `--cmd ` (shell pane) — argv order sets pane order; bare forms allowed (blank claude / bare shell). Layout flags: `--layout ` accepts 6 canonical tmux layouts + 6 shortforms (`a`/`t`/`h`/`v`/`deck-h`/`deck-v`); default `auto` picks by pane count. Preset flags: positional `rk riff ` or `--preset ` resolve config-defined presets (`riff.presets.` in `fab/project/config.yaml`); `--list-presets` prints them and exits. Fan-out: `--fan-out N` spawns N identical windows in parallel, each with its own `wt` worktree. `-- ` passthrough to `wt create`. Preconditions: `$TMUX` set + `wt` on PATH (fast-fail, exit 2). Launcher resolved via `internal/fabconfig.ReadSpawnCommand` (falls back to `claude --dangerously-skip-permissions`). All subprocess calls via `exec.CommandContext` (wt: 30s, tmux: 10s). `tmux.OriginalTMUX` restored in child env so tmux targets the user's current server. Local exit-code wrapper returns 2 (precondition) / 3 (subprocess) without modifying `main.execute()`. See `rk-riff.md` for full reference | -| `help-dump` | `help_dump.go` | **Hidden build tooling** (`Hidden: true`) — not in user-facing help, self-excludes from its own dump. Emits the CLI help tree as JSON to stdout, or to a file when an optional `[output-path]` arg is given (`cobra.MaximumNArgs(1)`, stdlib `os.WriteFile` 0o644, no subprocess — pure in-process Cobra introspection per §I). `captureNode` walks `rootCmd.Commands()` recursively to full depth, dropping `completion`, `help`, and any `Hidden` node at every level (a hidden parent's subtree is dropped entirely) via `includeInDump`; leaves serialize `"commands":[]` (non-nil slice), never `null`. Per-node fields `name`/`path`/`short`/`usage`/`text` come from `cmd.Name()`/`CommandPath()`/`Short`/`UseLine()`/`UsageString()` (raw, newlines preserved). The **frozen JSON contract** is `{tool:"rk", version, captured_at (RFC3339 UTC), schema_version:1 (const), root:node}`, mirroring `sahil87/shll.ai` `help/wt.json`. `version` comes from `displayVersion()` (ldflags `-X main.version`, never hardcoded); `captured_at` via the injectable `nowUTC` package var + the pure `buildDump(root, version, now)` builder (test seam — no global time freeze). Consumed by the release-pipeline shll.ai publish step (see `## Release Flow & CI/CD`) | +| `help-dump` | `help_dump.go` | **Hidden build tooling** (`Hidden: true`) — not in user-facing help, self-excludes from its own dump. Emits the CLI help tree as JSON to stdout, or to a file when an optional `[output-path]` arg is given (`cobra.MaximumNArgs(1)`, stdlib `os.WriteFile` 0o644, no subprocess — pure in-process Cobra introspection per §I). `captureNode` walks `rootCmd.Commands()` recursively to full depth, dropping `completion`, `help`, and any `Hidden` node at every level (a hidden parent's subtree is dropped entirely) via `includeInDump`; leaves serialize `"commands":[]` (non-nil slice), never `null`. Per-node fields `name`/`path`/`short`/`usage`/`text` come from `cmd.Name()`/`CommandPath()`/`Short`/`UseLine()`/`UsageString()` (raw, newlines preserved). The **frozen JSON contract** is `{tool:"rk", version, captured_at (RFC3339 UTC), schema_version:1 (const), root:node}`, mirroring `sahil87/shll.ai` `help/wt.json`. `version` comes from `displayVersion()` (ldflags `-X main.version`, never hardcoded); `captured_at` via the injectable `nowUTC` package var + the pure `buildDump(root, version, now)` builder (test seam — no global time freeze). shll.ai **pulls** this dump by running `rk help-dump` itself on a schedule and capturing the output — the in-repo release-pipeline publish step that used to push it was retired (`260603-iak3`, see `## Release Flow & CI/CD`); `help-dump` remains the single contract surface shll.ai depends on | ## Embedded Frontend Assets @@ -494,13 +494,13 @@ Updates are silent (`registerType: "autoUpdate"`) — the service worker detects **Release script** (`scripts/release.sh`): accepts bump level (`patch`/`minor`/`major`), increments `VERSION` file semver, commits with message `v{version}`, creates git tag `v{version}`, pushes commit + tag. Tag push triggers CI. -**GitHub Actions** (`.github/workflows/release.yml`): triggers on `v*` tag push. Steps: checkout → setup Go (from `go.mod`) → setup Node 20 + pnpm → install frontend deps → build frontend → copy dist to backend → cross-compile 4 targets → create GitHub Release with tarballs → update Homebrew tap → publish help tree to shll.ai. +**GitHub Actions** (`.github/workflows/release.yml`): triggers on `v*` tag push. Steps: checkout → setup Go (from `go.mod`) → setup Node 20 + pnpm → install frontend deps → build frontend → copy dist to backend → cross-compile 4 targets → create GitHub Release with tarballs → update Homebrew tap. Cross-compile targets: `darwin/arm64`, `darwin/amd64`, `linux/arm64`, `linux/amd64`. Each target built with `CGO_ENABLED=0` and ldflags. Output: `run-kit-{os}-{arch}.tar.gz` tarballs uploaded to GitHub Release via `softprops/action-gh-release`. -**Help-tree publish to shll.ai** (the **final** CI step — placed after the GitHub Release and Homebrew tap deliberately): runs `dist/rk-linux-amd64/rk help-dump help/run-kit.json` against the versioned linux/amd64 artifact (so the emitted `version` is the real release), then validates the output with `jq empty`. **The entire in-repo produce+copy class — `rk help-dump`, `jq empty`, the `mkdir -p /tmp/shll-ai/help`, and the `cp help/run-kit.json` into the clone — is fatal and fails the job loudly (`::error::` + non-zero) on any defect** (missing binary, dump error, invalid JSON, missing source/dest); the `cp` is explicitly NOT part of the best-effort wrapper and is guarded so a failed copy can never reach the "unchanged → nothing to publish" no-diff `return 0` that previously silenced it. Because the step runs last, that failure does not preempt the already-published GitHub Release / Homebrew tap. The cross-repo publish itself is **best-effort / non-fatal**: it opens an auto-merge PR (not a direct push) into `sahil87/shll.ai` via the `SHLLAI_TOKEN` secret, mirroring the Homebrew-tap token-clone pattern (`git clone https://x-access-token:${SHLLAI_TOKEN}@github.com/sahil87/shll.ai.git`, fresh `rk-help-dump-` branch off `main`, `gh pr create` + `gh pr merge --auto` with `GH_TOKEN=$SHLLAI_TOKEN`). Since it runs *after* the release artifacts / GitHub Release / Homebrew tap are produced, it skips with a log line when `help/run-kit.json` is unchanged, and logs a `::warning::` (leaving any PR open for manual merge) rather than failing the release if shll.ai (clone/PR/merge) is unreachable or repo-level auto-merge is disabled. `help/run-kit.json` is rk's slice of a multi-tool (7-tool) shll.ai "Command reference" rollout; the shll.ai site-side Astro loader / reference UI lives in a separate repo (out of scope). The producer is the hidden `rk help-dump` subcommand (see `## CLI Subcommands`). (`260602-a36m-help-dump-shll-ai`) +**Help-tree publish to shll.ai — RETIRED (push → pull, `260603-iak3-teardown-shllai-publish-ci`):** the release job no longer publishes the help tree. The former **final** CI step (`Publish help tree to shll.ai`, deliberately placed after the GitHub Release and Homebrew tap) ran `rk help-dump help/run-kit.json` then opened a best-effort auto-merge PR into `sahil87/shll.ai` via the `SHLLAI_TOKEN` cross-repo write secret — that entire step was removed. shll.ai inverted its integration model from **push** (each toolkit CLI produced its help JSON in CI and PR'd it into shll.ai) to **pull** (shll.ai now runs `rk help-dump` itself on a schedule and captures the output), making the push path redundant work and a redundant attack surface (dual writers to `help/run-kit.json` could race). The `SHLLAI_TOKEN` reference was removed from CI along with the step; the GitHub repo secret itself is an operator follow-up to delete (least-privilege, no remaining consumer). The hidden `rk help-dump` subcommand is **preserved unchanged** as the single contract surface shll.ai pulls from (see `## CLI Subcommands`). After removal, the `release` job's final step is **Update Homebrew tap**. (Historical: the push step was added in `260602-a36m-help-dump-shll-ai` and hardened in `260602-2dt9-fix-shllai-help-publish`.) -**Homebrew tap update** (runs before the final help-tree publish step): computes SHA256 for all 4 tarballs, clones `sahil87/homebrew-tap` via `BUILD_TOKEN` secret, generates `Formula/rk.rb` from `.github/formula-template.rb` (placeholder substitution via `sed`), commits and pushes. +**Homebrew tap update** (now the **final** step in the `release` job): computes SHA256 for all 4 tarballs, clones `sahil87/homebrew-tap` via `BUILD_TOKEN` secret, generates `Formula/rk.rb` from `.github/formula-template.rb` (placeholder substitution via `sed`), commits and pushes. ## Homebrew Distribution @@ -705,3 +705,4 @@ E2E test coverage: create/kill session via UI, SSE stream delivers real data, si | 2026-06-02 | **`exit-empty` server-death prevention (Constitution VI).** The `_rk-ctl` control-mode anchor is now created **unconditionally** on every server the tmuxctl Supervisor observes — a permanent session floor that keeps the count above zero so tmux's default `exit-empty on` can't reap the server. Previously `resolveBootstrap` created it only on servers empty at first connect, so servers with real sessions got no floor and collapsed once their last real session closed (relays-only → next disconnect → zero → whole-server death; killed live agent sessions, ≥3×). The floor is decoupled from the attach target (still prefers a real session; `firstSessionName` skips the anchor). `tmux.SetExitEmptyOff` sets `exit-empty off` server-globally, imperatively in `productionDial` before the anchor on every dial/reconnect (ordering closes the restart-window sliver); embedded conf sets it too. **Corrects the prior §VI compliance claim in this file**: tmuxctl now *does* create a session on servers with live user sessions (incl. `default`) — but it remains harmless (filtered from UIs via `ControlAnchorSessionName`, ungrouped, read-only `-r` attach), and `exit-empty off` is the deliberate §VI *prevention*, not a violation. Lifetime is explicit-kill-only (server dies only via `kill-server`/`rk reaper`; anchor-only servers persist by design; no cross-process state, §II). Test/e2e sockets still excluded by `isTmuxSocketCandidate`→`IsTestServerName` (no resurrection). | `260602-a1wo-prevent-exit-empty-server-death` | | 2026-06-02 | **Build-time help-dump — emit rk CLI help tree as `help/run-kit.json`** — New hidden Cobra subcommand `help-dump` in `cmd/rk/help_dump.go` (`Hidden: true`, registered in `root.go` `init()`). `captureNode` walks `rootCmd.Commands()` recursively to full depth, dropping `completion`, `help`, and any `Hidden` node at every level via `includeInDump` (self-excludes `help-dump`); leaves serialize `"commands":[]` (non-nil slice, never `null`). Per-node `name`/`path`/`short`/`usage`/`text` from `cmd.Name()`/`CommandPath()`/`Short`/`UseLine()`/`UsageString()` (raw `text`, newlines preserved). Frozen top-level contract `{tool:"rk", version, captured_at (RFC3339 UTC), schema_version:1, root:node}`, mirroring `sahil87/shll.ai` `help/wt.json`. `version` from `displayVersion()` (ldflags, never hardcoded); `captured_at` via injectable package-level `nowUTC` var + pure `buildDump(root, version, now)` builder (test seam, no global time freeze). Writes to `[output-path]` arg via `os.WriteFile` (0o644) else stdout — pure in-process introspection, no subprocess/shell-string (§I Security First). `release.yml` gains a **Publish help tree to shll.ai** step placed LAST (after the GitHub Release + Homebrew tap, so a fatal in-repo failure can't preempt the published release): `dist/rk-linux-amd64/rk help-dump help/run-kit.json` (versioned artifact) + `jq empty` validation (in-repo, fails the job on bad JSON), then a best-effort auto-merge PR (not a direct push) into `sahil87/shll.ai` via `SHLLAI_TOKEN` mirroring the Homebrew-tap token-clone pattern — skips when unchanged, and logs `::warning::` (PR left open) rather than failing the release when shll.ai is unreachable / auto-merge is disabled. rk's slice of a 7-tool "Command reference" rollout; the shll.ai site-side consumer is a separate repo (out of scope). Tests: `cmd/rk/help_dump_test.go` (shape, filtering via synthetic + real tree, self-exclusion, full-depth recursion, leaf `[]` not `null`, version-from-`displayVersion()`, injected/parseable `captured_at`). | `260602-a36m-help-dump-shll-ai` | | 2026-06-02 | **Move-based server-scoped boards (pin sessions); relay ephemerals removed.** Boards switched from per-WebSocket `rk-relay-*` ephemeral isolation + `@rk_board` server-option encoding to a move-based pin-session model. A pinned window is `move-window`'d into its own single-window session `_rk-pin-` (helpers `PinSessionName`/`WindowIDFromPinSession` in `tmux.go`); a board = the set of pin-sessions sharing an `@rk_board` SESSION var (server-scoped), with `@rk_home` (restore) + `@rk_board_order` (fractional `ComputeOrderKey`). `board.go` rewritten: pin-session-derived `ListBoardEntries`/`ListBoards`/`GetBoard` (no write-back), move-based `Pin`/`Unpin`/`Reorder` (Pin stamps 3 vars with `context.Background()`-rooted rollback + double-fault guard; idempotent incl. wrong-board re-stamp; Unpin restores to `@rk_home` or recreates a dead home via `rename-session`, no placeholder); kept `ComputeOrderKey`/`nextAppendKey`/validators/`BoardOption` (now the session-var key); deleted `parseBoardValue`/`serializeBoardValue`/`setBoardValue`/`ListAllBoardEntries`/`RemoveAllByWindowID`. **Relay** (`api/relay.go`) attaches the PTY directly to `ResolveWindowSession`'s result (home or `_rk-pin-*`) — removed the ephemeral allocation, `@rk_owner_pid` stamp, ephemeral select/kill; `ResolveWindowSession` no longer filters `rk-relay-*`. **Deleted**: `cmd/rk/serve_sweep.go` + its `serve.go` wiring; `RelaySessionPrefix`/`OwnerPIDOption`/`NewGroupedSession`/`SetSessionOwnerPID`/`GetSessionOwnerPID`/`ListRawSessionNames` from `tmux.go`; `TmuxOps.NewGroupedSession`/`SetSessionOwnerPID`. `parseSessions` skips `_rk-pin-*`+`_rk-ctl`, NO LONGER `rk-relay-*`. **SSE** (`api/sse.go`): dropped `detectKilledWindowIDs`/`previousWindowIDs`/window-kill `board-changed {cleanup}` diff + `RemoveAllByWindowID` dep, `broadcastBoardBootstrap`/`previousBoardJSON`/first-poll bootstrap/cached-board send; only pin/unpin/reorder `board-changed` events remain (`BoardEntriesFetcher` slimmed to one method). Accepted tradeoffs: multi-client active-window collisions on a shared home session; a pinned window disappears from its home session's sidebar until unpinned. Pins persist across rk restarts → no restore-sweep. `_rk-ctl` anchor + `exit-empty off` (`260602-a1wo`) unchanged — pin-session persistence relies on `exit-empty off`; `tmuxctl/` active-window derivation left untouched (investigate-only). Frontend `boards.ts`/`use-boards.ts` doc/contract updated to server-scoped pin-session derivation; `BoardEntry` shape + `board-pane.tsx`/`board-page.tsx` structure unchanged. | `260602-qn62-move-based-board-pin-sessions` | +| 2026-06-03 | **Teardown shll.ai help-tree publish CI (push → pull migration).** Removed the `Publish help tree to shll.ai` step from `.github/workflows/release.yml` (the deliberately-last step, ~95 lines incl. its leading "Placed LAST…" comment): producer CI (`rk help-dump help/run-kit.json` + `jq empty`), PR-opening logic (`publish_to_shllai()` token-clone + `gh pr create`), auto-merge wiring (`gh pr merge --auto --squash`), and all `SHLLAI_TOKEN` usage (`env:` + every `$SHLLAI_TOKEN`) deleted in one cut. shll.ai moved from a **push** model (each toolkit CLI PR'd its help JSON into shll.ai) to a **pull** model (shll.ai now runs `rk help-dump` itself on a schedule and captures the output), so the push transport was redundant and a redundant cross-repo write attack surface (dual writers to `help/run-kit.json` could race). The hidden `rk help-dump` subcommand (`cmd/rk/help_dump.go`) and its tests are **preserved unchanged** — it is now the single contract surface shll.ai pulls from. The `release` job's final step is now **Update Homebrew tap**; the GitHub release/tap path is unaffected (the removed step was last + best-effort). No code references to `secrets.SHLLAI_TOKEN` remain; deleting the orphaned GitHub repo secret is an out-of-tree operator follow-up (least-privilege). Reduces attack surface per Constitution §I. | `260603-iak3-teardown-shllai-publish-ci` | diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl new file mode 100644 index 00000000..39010ca9 --- /dev/null +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl @@ -0,0 +1,12 @@ +{"action":"enter","driver":"fab-new","event":"stage-transition","stage":"intake","ts":"2026-06-03T07:50:30Z"} +{"args":"There's an update in the way we integrate with shll.ai (help-dump-contract teardown directive): shll.ai moves from a push model (tool repos PR help JSON into shll.ai) to a pull model (shll.ai runs \u003ctool\u003e help-dump and captures output). Implement the teardown — remove producer CI, PR-opening logic, auto-merge wiring, and the SHLLAI_TOKEN secret usage from release.yml, while preserving the rk help-dump subcommand as the single contract surface.","cmd":"fab-new","event":"command","ts":"2026-06-03T07:50:30Z"} +{"delta":"+3.8","event":"confidence","score":3.8,"trigger":"calc-score","ts":"2026-06-03T07:51:52Z"} +{"delta":"+0.0","event":"confidence","score":3.8,"trigger":"calc-score","ts":"2026-06-03T07:52:00Z"} +{"cmd":"fab-fff","event":"command","ts":"2026-06-03T15:05:47Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"apply","ts":"2026-06-03T15:05:47Z"} +{"cmd":"fab-continue","event":"command","ts":"2026-06-03T15:06:50Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"review","ts":"2026-06-03T15:10:12Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"hydrate","ts":"2026-06-03T15:12:53Z"} +{"event":"review","result":"passed","ts":"2026-06-03T15:12:53Z"} +{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"ship","ts":"2026-06-03T15:15:22Z"} +{"cmd":"git-pr","event":"command","ts":"2026-06-03T15:16:04Z"} diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml new file mode 100644 index 00000000..43045176 --- /dev/null +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml @@ -0,0 +1,49 @@ +id: iak3 +name: 260603-iak3-teardown-shllai-publish-ci +created: 2026-06-03T07:50:30Z +created_by: sahil-noon +change_type: ci +issues: [] +progress: + intake: done + apply: done + review: done + hydrate: done + ship: active + review-pr: pending +plan: + generated: true + task_count: 4 + acceptance_count: 8 + acceptance_completed: 8 +confidence: + certain: 4 + confident: 4 + tentative: 0 + unresolved: 0 + score: 3.8 + fuzzy: true + dimensions: + signal: 88.9 + reversibility: 81.0 + competence: 88.8 + disambiguation: 88.9 +stage_metrics: + intake: {started_at: "2026-06-03T07:50:30Z", driver: fab-new, iterations: 1, completed_at: "2026-06-03T15:05:47Z"} + apply: {started_at: "2026-06-03T15:05:47Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:10:12Z"} + review: {started_at: "2026-06-03T15:10:12Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:12:53Z"} + hydrate: {started_at: "2026-06-03T15:12:53Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:15:22Z"} + ship: {started_at: "2026-06-03T15:15:22Z", driver: fab-fff, iterations: 1} +prs: [] +true_impact: + added: 0 + deleted: 0 + net: 0 + excluding: + added: 0 + deleted: 0 + net: 0 + computed_at: "2026-06-03T15:15:22Z" + computed_at_stage: hydrate +# true_impact: lazily created on first apply-finish (no placeholder here). +last_updated: 2026-06-03T15:15:22Z diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/intake.md b/fab/changes/260603-iak3-teardown-shllai-publish-ci/intake.md new file mode 100644 index 00000000..436d16c4 --- /dev/null +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/intake.md @@ -0,0 +1,212 @@ +# Intake: Teardown shll.ai Help-Tree Publish CI (Push → Pull Migration) + +**Change**: 260603-iak3-teardown-shllai-publish-ci +**Created**: 2026-06-03 +**Status**: Draft + +## Origin + +This change was initiated one-shot via `/fab-new` with a directive pointing at the +shll.ai help-dump contract spec: + +> There's an update in the way we integrate with shll.ai. To understand it read +> https://github.com/sahil87/shll.ai/blob/main/docs/specs/help-dump-contract.md#teardown-directive-paste-to-a-tool-repo-agent . +> Implement the change. + +The referenced **Teardown Directive (paste-to-a-tool-repo-agent)** section of the +help-dump contract describes shll.ai inverting its integration model: + +- **Before (push model)**: each of the 7 toolkit CLIs (idea, hop, fab-kit, wt, run-kit, + tu, shll) produced its help JSON in its own release CI and opened an auto-merge PR + into `sahil87/shll.ai` to publish it. run-kit's slice was added in change + `260602-a36m-help-dump-shll-ai` and hardened in `260602-2dt9-fix-shllai-help-publish`. +- **After (pull model)**: shll.ai runs a scheduled job that invokes ` help-dump` + itself and captures the output. The tool repos no longer push. + +The directive instructs each tool repo to remove the now-redundant producer/push transport +in a single PR, while **preserving the `help-dump` subcommand** — it is now the single +contract surface shll.ai depends on (shll.ai pulls from it). This intake captures run-kit's +execution of that directive. + +No prior `/fab-discuss` or exploration preceded this change; the spec and the current +`release.yml` are the sole inputs. The directive's precondition ("apply only after shll.ai's +pull workflow is live and proven") is taken as satisfied by the user issuing this directive +now (recorded as an Open Question / assumption below). + +## Why + +1. **Problem it solves** — shll.ai has moved to a pull model. run-kit's release pipeline + still runs the obsolete push path: it produces `help/run-kit.json` and opens an + auto-merge PR into `sahil87/shll.ai` via the `SHLLAI_TOKEN` cross-repo write secret + (`.github/workflows/release.yml`, the final "Publish help tree to shll.ai" step, lines + ~160–254). Under the pull model this push is redundant work and a redundant attack + surface — every release still clones an external repo with a write-scoped token and + opens a PR that shll.ai no longer needs. +2. **Consequence of not fixing** — the release job keeps holding a cross-repo write secret + (`SHLLAI_TOKEN`) and performing cross-repo writes that duplicate (and can race) what + shll.ai's scheduled pull now does. Two writers to the same `help/run-kit.json` is exactly + the multi-repo write race the original push design tried to avoid; once shll.ai pulls, + the push side is dead weight that only adds risk. Leaving a write-scoped token in the + repo with no remaining consumer violates least-privilege. +3. **Why this approach** — the directive is prescriptive: remove the four push components + (producer CI, PR-opening logic, auto-merge wiring, `SHLLAI_TOKEN` usage) in one PR and + keep `help-dump`. The alternative (keep both push and pull) was rejected by the contract + author precisely because dual writers race. The transport is what's obsolete; the + command is the contract surface and stays. This is a pure deletion of one CI step plus a + secret reference — low-risk, since the step was already explicitly designed to be the + LAST step and best-effort, so removing it cannot affect the GitHub Release or Homebrew tap. + +## What Changes + +### 1. Remove the "Publish help tree to shll.ai" step from `release.yml` + +`.github/workflows/release.yml` currently ends with a single step (the **final** step in the +`release` job) that does all four push components the directive names. The entire step is +removed — name, `env: SHLLAI_TOKEN`, and the full `run:` block (the `rk help-dump` produce + +`jq empty` validate + `publish_to_shllai()` clone/branch/commit/push + `gh pr create` + +`gh pr merge --auto`). + +The step to delete (verbatim, current `release.yml`): + +```yaml + # Placed LAST in the job — after the GitHub Release and Homebrew tap are + # already published — so the documented "best-effort, can't break the + # release" guarantee actually holds: any fatal line here runs only once + # the user-visible release artifacts exist. + - name: Publish help tree to shll.ai + env: + SHLLAI_TOKEN: ${{ secrets.SHLLAI_TOKEN }} + run: | + version="${{ steps.version.outputs.version }}" + mkdir -p help + dist/rk-linux-amd64/rk help-dump help/run-kit.json + jq empty help/run-kit.json + publish_to_shllai() { + git clone "https://x-access-token:${SHLLAI_TOKEN}@github.com/sahil87/shll.ai.git" /tmp/shll-ai || return 1 + branch="rk-help-dump-${version}" + ... + GH_TOKEN="$SHLLAI_TOKEN" gh pr create --repo sahil87/shll.ai ... + GH_TOKEN="$SHLLAI_TOKEN" gh pr merge "$branch" --repo sahil87/shll.ai --auto --squash ... + } + if publish_to_shllai; then + echo "shll.ai help-tree publish step completed." + else + echo "::warning::shll.ai help-tree publish failed ..." + fi +``` + +This single deletion covers all four directive components at once, because run-kit's push +path was implemented as one self-contained step: + +| Directive component | Where it lives in run-kit | Action | +|---------------------|---------------------------|--------| +| 1. Producer CI (walk tree → write JSON in CI) | `rk help-dump help/run-kit.json` + `jq empty` lines in the step | Removed with the step | +| 2. PR-opening logic | `publish_to_shllai()` clone/branch/commit/push + `gh pr create` | Removed with the step | +| 3. Auto-merge wiring | `gh pr merge "$branch" --auto --squash` | Removed with the step | +| 4. `SHLLAI_TOKEN` removal | `env: SHLLAI_TOKEN: ${{ secrets.SHLLAI_TOKEN }}` + its uses | Removed with the step | + +After removal, the `release` job's final remaining step is **"Update Homebrew tap"**. + +### 2. Preserve `rk help-dump` unchanged — it is now the contract surface + +The hidden Cobra subcommand `help-dump` (`app/backend/cmd/rk/help_dump.go`) and its tests +(`app/backend/cmd/rk/help_dump_test.go`) are **NOT** touched. The directive's critical +preservation rule: ` help-dump` is the single contract surface shll.ai depends on +(shll.ai now pulls from it). Post-change, `rk help-dump` MUST still: + +- Exit 0 with empty stderr +- Emit valid JSON to stdout (the `captured_at` field is stamped by run-kit but shll.ai + re-stamps on capture per the contract — its value is not depended upon by shll.ai) +- Keep `schema_version: 1` (the frozen contract `const schemaVersion = 1`) +- Report the built binary's actual version via `displayVersion()` (ldflags `-X main.version`) + +No code change is required to preserve these — leaving the file untouched preserves them by +definition. Verification (not modification) confirms it. + +### 3. `SHLLAI_TOKEN` secret — confirm no other usage, then it becomes orphaned + +Grep across the repo confirms `SHLLAI_TOKEN` is referenced in active code/config **only** +within the one `release.yml` step being removed (other matches are historical: `fab/backlog.md` +and the two archived/in-flight `fab/changes/**` intake & plan artifacts, plus the +`docs/memory` records — none are executable workflow references). After this change there are +zero active references to `secrets.SHLLAI_TOKEN`. + +The actual GitHub repository secret named `SHLLAI_TOKEN` lives in repo settings, not in the +git tree — removing it is a GitHub-settings action outside this repo's code. This change +removes all *code* references and documents that the operator SHOULD delete the repo secret +(least-privilege) once the pull model is confirmed live. The code change does not depend on +the secret being deleted; deleting it is a follow-up operational step noted in the PR body. + +### 4. Update `docs/memory/run-kit/architecture.md` + +Three locations describe the now-removed publish step and must be reconciled during hydrate +(this is recorded as Affected Memory; the actual edit happens at hydrate, not apply): + +- The `help-dump` row in the **CLI Subcommands** table (line ~445) ends with "Consumed by the + release-pipeline shll.ai publish step (see `## Release Flow & CI/CD`)" — update to reflect + that shll.ai now **pulls** via a scheduled `rk help-dump` invocation (no in-repo publish). +- The **Release Flow & CI/CD** section (lines ~497, ~501) lists "publish help tree to shll.ai" + as the final CI step and describes it in detail — remove/rewrite to state the push step was + retired in favor of shll.ai's pull model; the final release step is now "Update Homebrew tap". +- The changelog row (line ~706) for `260602-a36m-help-dump-shll-ai` is historical and stays; + a new changelog row for this teardown is added at hydrate. + +### Out of scope + +- shll.ai's site-side pull job (Astro loader / scheduled `rk help-dump` capture) — lives in + the separate `sahil87/shll.ai` repo, not here. +- Any change to the `help-dump` JSON shape, schema version, or the command's behavior. +- Other toolkit repos' teardowns (idea, hop, fab-kit, wt, tu, shll) — each is its own PR. +- Deleting the `SHLLAI_TOKEN` GitHub repo secret (operational follow-up in repo settings; + noted in the PR body, not a code change). + +## Affected Memory + +- `run-kit/architecture`: (modify) Update the `help-dump` CLI Subcommands row (drop "Consumed + by the release-pipeline shll.ai publish step", note shll.ai now pulls via scheduled + `rk help-dump`); remove/rewrite the **Release Flow & CI/CD** "Publish help tree to shll.ai" + final-step description (push retired → pull model; final step is now Homebrew tap); add a + changelog row for this teardown. The historical `260602-a36m-help-dump-shll-ai` changelog + row is preserved. + +## Impact + +- **`.github/workflows/release.yml`** — delete the final "Publish help tree to shll.ai" step + (~95 lines). No other step references `help/`, `SHLLAI_TOKEN`, or shll.ai. The job's last + step becomes "Update Homebrew tap". `permissions: contents: write` and `concurrency` are + unaffected (they were never specific to the publish step). +- **`app/backend/cmd/rk/help_dump.go`** — unchanged (verify-only; it is the preserved contract + surface). +- **`app/backend/cmd/rk/help_dump_test.go`** — unchanged (verify-only; tests must still pass). +- **`docs/memory/run-kit/architecture.md`** — reconciled at hydrate (see Affected Memory). +- **GitHub repo secret `SHLLAI_TOKEN`** — orphaned by this change (no code references remain); + operator deletes it in repo settings as a follow-up (out of tree). +- **No dependency changes**, no Go module changes, no frontend changes. +- **CI safety**: `ci.yml` does not reference shll.ai/`help-dump`/`SHLLAI_TOKEN` — unaffected. + Removing the LAST step of the `release` job cannot affect the GitHub Release or Homebrew tap + (both produced earlier in the job), so the release path is preserved by construction. + +## Open Questions + +- Has shll.ai's pull workflow been confirmed live and proven? The directive states the + teardown applies "only after shll.ai's pull workflow is live and proven, preventing a + stale-help gap if executed prematurely." Issuing this directive is taken as the user's + confirmation. (Recoverable: if premature, revert is a one-step `git revert`.) +- Should the `SHLLAI_TOKEN` GitHub repo secret deletion be performed as part of this PR's + merge, or tracked separately? (Defaulting to: note it in the PR body as an operator + follow-up — secret deletion is a repo-settings action, not a code change.) + +## Assumptions + +| # | Grade | Decision | Rationale | Scores | +|---|-------|----------|-----------|--------| +| 1 | Certain | Implement the spec's Teardown Directive: remove producer CI, PR-opening logic, auto-merge wiring, and `SHLLAI_TOKEN` usage from `release.yml`; preserve `rk help-dump` | Directive is prescriptive and names exactly these four components + the preservation rule | S:98 R:80 A:95 D:96 | +| 2 | Certain | All four directive components are deletable as one self-contained `release.yml` step (run-kit implemented the whole push path as the single final "Publish help tree to shll.ai" step) | Verified by reading current `release.yml` lines ~160–254 — produce, PR, auto-merge, and `SHLLAI_TOKEN` env all live in that one step | S:95 R:80 A:95 D:95 | +| 3 | Certain | `rk help-dump` (`help_dump.go`) and its tests are NOT modified — preserved as the single contract surface shll.ai pulls from | Directive's explicit "Critical Preservation Rule"; removing only the transport, never the command | S:98 R:85 A:95 D:97 | +| 4 | Certain | `SHLLAI_TOKEN` has no other active usage in the repo — grep confirms only the one workflow step plus historical fab/docs records | Repo-wide grep shows no second executable reference; directive requires confirming this before removal | S:95 R:75 A:92 D:90 | +| 5 | Confident | Deleting the LAST step of the `release` job cannot affect the GitHub Release or Homebrew tap; release path is preserved | The step was deliberately placed last and documented best-effort precisely so it couldn't preempt the release; both artifacts are produced earlier in the job | S:88 R:80 A:90 D:88 | +| 6 | Confident | Deleting the `SHLLAI_TOKEN` GitHub repo secret is an out-of-tree operator follow-up, noted in the PR body, not a code change in this PR | The secret lives in repo settings, not the git tree; the directive's component 4 is satisfied in-tree by removing all code references | S:80 R:75 A:85 D:82 | +| 7 | Confident | `docs/memory/run-kit/architecture.md` is reconciled at hydrate (not apply): the `help-dump` row, the Release Flow CI step description, and a new changelog row | Memory hydration is a hydrate-stage activity per the fab pipeline; apply touches code/CI only | S:85 R:88 A:88 D:85 | +| 8 | Confident | shll.ai's pull workflow is live/proven (the directive's precondition) — taken as satisfied by the user issuing the directive now | Directive gates on this; user invoking the teardown is the signal. Fully reversible via `git revert` if premature | S:72 R:85 A:70 D:78 | + +8 assumptions (4 certain, 4 confident, 0 tentative, 0 unresolved). diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md b/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md new file mode 100644 index 00000000..478c5d86 --- /dev/null +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md @@ -0,0 +1,117 @@ +# Plan: Teardown shll.ai Help-Tree Publish CI (Push → Pull Migration) + +**Change**: 260603-iak3-teardown-shllai-publish-ci +**Status**: In Progress +**Intake**: `intake.md` + +## Requirements + + + +### CI: Release Pipeline Push Transport Removal + +#### R1: Remove the shll.ai push transport from the release workflow +The `release` job in `.github/workflows/release.yml` SHALL NOT contain the `Publish help tree to shll.ai` step. Removing this single self-contained step eliminates all four directive components at once: the producer CI (`rk help-dump help/run-kit.json` + `jq empty`), the PR-opening logic (`publish_to_shllai()` clone/branch/commit/push + `gh pr create`), the auto-merge wiring (`gh pr merge --auto --squash`), and the `SHLLAI_TOKEN` usage (`env: SHLLAI_TOKEN` + every `$SHLLAI_TOKEN` reference). The step's leading explanatory comment block (the "Placed LAST in the job…" comment) SHALL be removed with it, since it only describes that step. + +- **GIVEN** the current `release.yml` whose final step is `Publish help tree to shll.ai` +- **WHEN** the change is applied +- **THEN** that step and its leading comment block are gone +- **AND** `grep` over `.github/workflows/` finds zero references to `SHLLAI_TOKEN`, `shll.ai`, or `help/run-kit.json` + +#### R2: The release workflow remains a valid, intact workflow +After removing the step, `.github/workflows/release.yml` SHALL remain valid, parseable YAML, and the `release` job's final remaining step SHALL be `Update Homebrew tap`. All steps prior to the removed one (checkout, tag, setup-go/node/pnpm, build, cross-compile, GitHub Release, Homebrew tap) SHALL be unchanged, and the job's `permissions: contents: write` and `concurrency` blocks SHALL be untouched (they were never specific to the publish step). + +- **GIVEN** the edited `release.yml` +- **WHEN** parsed by a YAML parser +- **THEN** it parses without error +- **AND** the last step's `name` is `Update Homebrew tap` +- **AND** no earlier step or top-level key is altered + +#### R3: Preserve `rk help-dump` as the single contract surface (verify-only) +The hidden Cobra subcommand `help-dump` (`app/backend/cmd/rk/help_dump.go`) and its tests (`app/backend/cmd/rk/help_dump_test.go`) MUST NOT be modified. After the change, `rk help-dump` MUST still exit 0, emit valid JSON to stdout, report `schema_version: 1`, and report `tool: rk`. No code change is required to preserve this — leaving the files untouched preserves it by definition; this requirement is satisfied by verification, not modification. + +- **GIVEN** the unmodified `help_dump.go` / `help_dump_test.go` +- **WHEN** the `rk` binary is built and `rk help-dump` is run +- **THEN** it exits 0 with valid JSON on stdout where `schema_version == 1` and `tool == "rk"` +- **AND** `go vet ./...` passes and the help-dump tests pass unchanged + +### Non-Goals + +- shll.ai's site-side pull job (scheduled `rk help-dump` capture / Astro loader) — lives in the separate `sahil87/shll.ai` repo, not here. +- Any change to the `help-dump` JSON shape, schema version, or command behavior. +- Other toolkit repos' teardowns (idea, hop, fab-kit, wt, tu, shll) — each is its own PR. +- Deleting the `SHLLAI_TOKEN` GitHub repo secret — a repo-settings (out-of-tree) operator follow-up noted in the PR body, not a code change here. +- Reconciling `docs/memory/run-kit/architecture.md` — a hydrate-stage activity, not apply. + +### Design Decisions + +1. **Delete the whole step rather than trim individual lines**: run-kit implemented the entire push path as one self-contained final step, so a single deletion cleanly satisfies all four directive components. — *Why*: minimizes blast radius and leaves no orphaned shell fragments. — *Rejected*: surgically editing lines within the step (more error-prone, leaves dead scaffolding). +2. **Preserve `rk help-dump` untouched**: the directive's Critical Preservation Rule names it as the single contract surface shll.ai pulls from. — *Why*: removing the transport, never the command. — *Rejected*: refactoring/relocating the command (out of scope, would risk the contract surface). +3. **Security posture (Constitution §I — Security First)**: this change strictly *reduces* attack surface. It removes a cross-repo write secret (`SHLLAI_TOKEN`) clone + `gh` invocation that wrote into an external repo on every release. With shll.ai pulling, the push side is dead weight that holds a least-privilege-violating write-scoped token with no remaining consumer. Removal aligns with §I and least-privilege. + +### Deprecated Requirements + +#### shll.ai help-tree push publish (from `260602-a36m-help-dump-shll-ai`, hardened in `260602-2dt9-fix-shllai-help-publish`) +**Reason**: shll.ai moved to a pull model (it now invokes `rk help-dump` on a schedule). The push transport is redundant and a redundant attack surface; dual writers to `help/run-kit.json` race. +**Migration**: shll.ai pulls via its own scheduled `rk help-dump` capture in the `sahil87/shll.ai` repo. run-kit's contract obligation is now solely to keep `rk help-dump` working (R3). + +## Tasks + +### Phase 2: Core Implementation + +- [x] T001 Delete the `Publish help tree to shll.ai` step (and its leading "Placed LAST in the job…" comment block) from `.github/workflows/release.yml` so the job's final step becomes `Update Homebrew tap`; match surrounding YAML style and leave all prior steps and top-level keys untouched. + +### Phase 3: Integration & Edge Cases + +- [x] T002 Validate `.github/workflows/release.yml` still parses as YAML (using `yq`, since pyyaml is unavailable) and confirm the last step is `Update Homebrew tap`. +- [x] T003 Grep `.github/workflows/` to confirm zero remaining references to `SHLLAI_TOKEN`, `shll.ai`, or `help/run-kit.json`. +- [x] T004 Verify the preserved contract surface unchanged: build `rk` and run `rk help-dump`, asserting exit 0, valid JSON on stdout, `schema_version == 1`, `tool == "rk"`; run `go vet ./...` and the help-dump tests (the `cmd/rk` package tests cover all `TestBuildDump*`/`TestCaptureNode*` help-dump tests). Do NOT modify `help_dump.go` / `help_dump_test.go`. + +## Acceptance + +### Functional Completeness + +- [x] A-001 R1: The `Publish help tree to shll.ai` step and its leading comment block are removed from `.github/workflows/release.yml`; all four directive components (producer CI, PR-opening logic, auto-merge wiring, `SHLLAI_TOKEN` usage) are gone. +- [x] A-002 R2: `.github/workflows/release.yml` parses as valid YAML and the `release` job's final step is `Update Homebrew tap`, with all prior steps and top-level keys unchanged. +- [x] A-003 R3: `rk help-dump` (built binary) exits 0, emits valid JSON to stdout with `schema_version == 1` and `tool == "rk"`; `help_dump.go` and `help_dump_test.go` are unmodified. + +### Removal Verification + +- [x] A-004 R1: A grep over `.github/workflows/` returns zero matches for `SHLLAI_TOKEN`, `shll.ai`, and `help/run-kit.json` (historical matches under `fab/` and `docs/memory/` are expected and untouched). + +### Scenario Coverage + +- [x] A-005 R3: `go vet ./...` passes and the help-dump tests pass unchanged. + +### Security + +- [x] A-006 R1: No remaining workflow code references the `SHLLAI_TOKEN` cross-repo write secret; the change strictly reduces attack surface per Constitution §I (Security First). Deleting the GitHub repo secret itself is an out-of-tree operator follow-up noted in the PR body. + +### Code Quality + +- [x] A-007 Pattern consistency: The edited `release.yml` matches surrounding YAML style (indentation, step structure); no orphaned shell fragments or dangling comments remain. +- [x] A-008 No unnecessary duplication: No new utilities introduced; this is a pure deletion. + +## Notes + +- Check items as you review: `- [x]` +- All acceptance items must pass before `/fab-continue` (hydrate) +- If an item is not applicable, mark checked and prefix with **N/A**: `- [x] A-NNN **N/A**: {reason}` + +## Assumptions + +| # | Grade | Decision | Rationale | Scores | +|---|-------|----------|-----------|--------| +| 1 | Certain | Remove the entire `Publish help tree to shll.ai` step as one deletion to satisfy all four directive components | Directive is prescriptive; run-kit implemented the whole push path as one self-contained final step (verified in `release.yml` lines ~160–254) | S:98 R:80 A:95 D:96 | +| 2 | Certain | Remove the step's leading "Placed LAST in the job…" comment block along with the step | The comment exclusively documents the removed step; leaving it would be a dangling, now-false comment | S:95 R:85 A:95 D:95 | +| 3 | Certain | Do NOT modify `help_dump.go` / `help_dump_test.go` — preserved as the contract surface; satisfied by verify-only | Directive's explicit Critical Preservation Rule; removing only the transport, never the command | S:98 R:85 A:95 D:97 | +| 4 | Certain | Use `yq` for YAML validation (pyyaml is not installed in this environment) | Environment probe: `python3 -c 'import yaml'` fails (ModuleNotFoundError); `yq v4.53.2` is available | S:95 R:90 A:95 D:92 | +| 5 | Confident | Deleting the LAST step of the `release` job cannot affect the GitHub Release or Homebrew tap; release path preserved | The step was deliberately placed last and documented best-effort; both artifacts are produced earlier in the job | S:88 R:80 A:90 D:88 | +| 6 | Confident | `docs/memory/run-kit/architecture.md` reconciliation is deferred to hydrate, not done at apply | Memory hydration is a hydrate-stage activity per the fab pipeline; apply touches code/CI only (per intake Affected Memory + task directive) | S:85 R:88 A:88 D:85 | + +6 assumptions (4 certain, 2 confident, 0 tentative). + + From f2d6512faee2a6aeb1344039003829dda25e458c Mon Sep 17 00:00:00 2001 From: Sahil Ahuja Date: Wed, 3 Jun 2026 20:47:52 +0530 Subject: [PATCH 2/3] Update ship status and record PR URL --- .../.history.jsonl | 1 + .../.status.yaml | 12 +++++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl index 39010ca9..b2b7d59d 100644 --- a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl @@ -10,3 +10,4 @@ {"event":"review","result":"passed","ts":"2026-06-03T15:12:53Z"} {"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"ship","ts":"2026-06-03T15:15:22Z"} {"cmd":"git-pr","event":"command","ts":"2026-06-03T15:16:04Z"} +{"action":"enter","driver":"git-pr","event":"stage-transition","stage":"review-pr","ts":"2026-06-03T15:17:48Z"} diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml index 43045176..1a3d6624 100644 --- a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml @@ -9,8 +9,8 @@ progress: apply: done review: done hydrate: done - ship: active - review-pr: pending + ship: done + review-pr: active plan: generated: true task_count: 4 @@ -33,8 +33,10 @@ stage_metrics: apply: {started_at: "2026-06-03T15:05:47Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:10:12Z"} review: {started_at: "2026-06-03T15:10:12Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:12:53Z"} hydrate: {started_at: "2026-06-03T15:12:53Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:15:22Z"} - ship: {started_at: "2026-06-03T15:15:22Z", driver: fab-fff, iterations: 1} -prs: [] + ship: {started_at: "2026-06-03T15:15:22Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:17:48Z"} + review-pr: {started_at: "2026-06-03T15:17:48Z", driver: git-pr, iterations: 1} +prs: + - https://github.com/sahil87/run-kit/pull/236 true_impact: added: 0 deleted: 0 @@ -46,4 +48,4 @@ true_impact: computed_at: "2026-06-03T15:15:22Z" computed_at_stage: hydrate # true_impact: lazily created on first apply-finish (no placeholder here). -last_updated: 2026-06-03T15:15:22Z +last_updated: 2026-06-03T15:17:48Z From 1ed967171a983caae20d74c44b24641fa74916a9 Mon Sep 17 00:00:00 2001 From: Sahil Ahuja Date: Thu, 4 Jun 2026 15:35:55 +0530 Subject: [PATCH 3/3] fix: address review feedback from @Copilot --- docs/memory/run-kit/architecture.md | 6 +++--- .../260603-iak3-teardown-shllai-publish-ci/.history.jsonl | 2 ++ .../260603-iak3-teardown-shllai-publish-ci/.status.yaml | 6 +++--- fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md | 2 -- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/memory/run-kit/architecture.md b/docs/memory/run-kit/architecture.md index 86e0285a..b9925dfe 100644 --- a/docs/memory/run-kit/architecture.md +++ b/docs/memory/run-kit/architecture.md @@ -496,11 +496,11 @@ Updates are silent (`registerType: "autoUpdate"`) — the service worker detects **GitHub Actions** (`.github/workflows/release.yml`): triggers on `v*` tag push. Steps: checkout → setup Go (from `go.mod`) → setup Node 20 + pnpm → install frontend deps → build frontend → copy dist to backend → cross-compile 4 targets → create GitHub Release with tarballs → update Homebrew tap. -Cross-compile targets: `darwin/arm64`, `darwin/amd64`, `linux/arm64`, `linux/amd64`. Each target built with `CGO_ENABLED=0` and ldflags. Output: `run-kit-{os}-{arch}.tar.gz` tarballs uploaded to GitHub Release via `softprops/action-gh-release`. +Cross-compile targets: `darwin/arm64`, `darwin/amd64`, `linux/arm64`, `linux/amd64`. Each target built with `CGO_ENABLED=0` and ldflags. Output: `rk-{os}-{arch}.tar.gz` tarballs uploaded to GitHub Release via `softprops/action-gh-release`. -**Help-tree publish to shll.ai — RETIRED (push → pull, `260603-iak3-teardown-shllai-publish-ci`):** the release job no longer publishes the help tree. The former **final** CI step (`Publish help tree to shll.ai`, deliberately placed after the GitHub Release and Homebrew tap) ran `rk help-dump help/run-kit.json` then opened a best-effort auto-merge PR into `sahil87/shll.ai` via the `SHLLAI_TOKEN` cross-repo write secret — that entire step was removed. shll.ai inverted its integration model from **push** (each toolkit CLI produced its help JSON in CI and PR'd it into shll.ai) to **pull** (shll.ai now runs `rk help-dump` itself on a schedule and captures the output), making the push path redundant work and a redundant attack surface (dual writers to `help/run-kit.json` could race). The `SHLLAI_TOKEN` reference was removed from CI along with the step; the GitHub repo secret itself is an operator follow-up to delete (least-privilege, no remaining consumer). The hidden `rk help-dump` subcommand is **preserved unchanged** as the single contract surface shll.ai pulls from (see `## CLI Subcommands`). After removal, the `release` job's final step is **Update Homebrew tap**. (Historical: the push step was added in `260602-a36m-help-dump-shll-ai` and hardened in `260602-2dt9-fix-shllai-help-publish`.) +**Help-tree publish to shll.ai — RETIRED (push → pull, `260603-iak3-teardown-shllai-publish-ci`):** the release job no longer publishes the help tree. The former **final** CI step (`Publish help tree to shll.ai`, deliberately placed after the GitHub Release and Homebrew tap) ran `dist/rk-linux-amd64/rk help-dump help/run-kit.json` (the versioned linux/amd64 artifact, so the dumped `version` matched the release) then opened a best-effort auto-merge PR into `sahil87/shll.ai` via the `SHLLAI_TOKEN` cross-repo write secret — that entire step was removed. shll.ai inverted its integration model from **push** (each toolkit CLI produced its help JSON in CI and PR'd it into shll.ai) to **pull** (shll.ai now runs `rk help-dump` itself on a schedule and captures the output), making the push path redundant work and a redundant attack surface (dual writers to `help/run-kit.json` could race). The `SHLLAI_TOKEN` reference was removed from CI along with the step; the GitHub repo secret itself is an operator follow-up to delete (least-privilege, no remaining consumer). The hidden `rk help-dump` subcommand is **preserved unchanged** as the single contract surface shll.ai pulls from (see `## CLI Subcommands`). After removal, the `release` job's final step is **Update Homebrew tap**. (Historical: the push step was added in `260602-a36m-help-dump-shll-ai` and hardened in `260602-2dt9-fix-shllai-help-publish`.) -**Homebrew tap update** (now the **final** step in the `release` job): computes SHA256 for all 4 tarballs, clones `sahil87/homebrew-tap` via `BUILD_TOKEN` secret, generates `Formula/rk.rb` from `.github/formula-template.rb` (placeholder substitution via `sed`), commits and pushes. +**Homebrew tap update** (now the **final** step in the `release` job): computes SHA256 for all 4 tarballs, clones `sahil87/homebrew-tap` via the `HOMEBREW_TAP_TOKEN` secret (exported as `TAP_TOKEN`), generates `Formula/rk.rb` from `.github/formula-template.rb` (placeholder substitution via `sed`), commits and pushes. ## Homebrew Distribution diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl index b2b7d59d..b86b6cdd 100644 --- a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.history.jsonl @@ -11,3 +11,5 @@ {"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"ship","ts":"2026-06-03T15:15:22Z"} {"cmd":"git-pr","event":"command","ts":"2026-06-03T15:16:04Z"} {"action":"enter","driver":"git-pr","event":"stage-transition","stage":"review-pr","ts":"2026-06-03T15:17:48Z"} +{"cmd":"git-pr-review","event":"command","ts":"2026-06-03T15:18:52Z"} +{"event":"review","result":"passed","ts":"2026-06-03T15:19:28Z"} diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml index 1a3d6624..0736dd36 100644 --- a/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/.status.yaml @@ -10,7 +10,7 @@ progress: review: done hydrate: done ship: done - review-pr: active + review-pr: done plan: generated: true task_count: 4 @@ -34,7 +34,7 @@ stage_metrics: review: {started_at: "2026-06-03T15:10:12Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:12:53Z"} hydrate: {started_at: "2026-06-03T15:12:53Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:15:22Z"} ship: {started_at: "2026-06-03T15:15:22Z", driver: fab-fff, iterations: 1, completed_at: "2026-06-03T15:17:48Z"} - review-pr: {started_at: "2026-06-03T15:17:48Z", driver: git-pr, iterations: 1} + review-pr: {started_at: "2026-06-03T15:17:48Z", driver: git-pr, iterations: 1, completed_at: "2026-06-03T15:19:28Z"} prs: - https://github.com/sahil87/run-kit/pull/236 true_impact: @@ -48,4 +48,4 @@ true_impact: computed_at: "2026-06-03T15:15:22Z" computed_at_stage: hydrate # true_impact: lazily created on first apply-finish (no placeholder here). -last_updated: 2026-06-03T15:17:48Z +last_updated: 2026-06-04T10:05:20Z diff --git a/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md b/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md index 478c5d86..54d292f0 100644 --- a/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md +++ b/fab/changes/260603-iak3-teardown-shllai-publish-ci/plan.md @@ -113,5 +113,3 @@ The hidden Cobra subcommand `help-dump` (`app/backend/cmd/rk/help_dump.go`) and | 6 | Confident | `docs/memory/run-kit/architecture.md` reconciliation is deferred to hydrate, not done at apply | Memory hydration is a hydrate-stage activity per the fab pipeline; apply touches code/CI only (per intake Affected Memory + task directive) | S:85 R:88 A:88 D:85 | 6 assumptions (4 certain, 2 confident, 0 tentative). - -