Skip to content

Fix/shenron audit fixes - #6

Merged
S1933 merged 9 commits into
mainfrom
fix/shenron-audit-fixes
Jul 12, 2026
Merged

S1933 merged 9 commits into
mainfrom
fix/shenron-audit-fixes

Conversation

@S1933

@S1933 S1933 commented Jul 12, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • OpenCode synchronization now removes previously managed agents that are no longer defined.
    • Diff and push commands provide clearer separation between results and warnings.
    • Push state is saved earlier, allowing interrupted pushes to be safely retried.
  • Bug Fixes

    • Improved manual-edit detection and package installation reliability.
    • Updated documentation to reflect current parsing and state-storage behavior.
  • Chores

    • Added automated checks for formatting, linting, validation, and tests.

@coderabbitai

coderabbitai Bot commented Jul 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds CI validation and repository hygiene updates, documents audit fixes, separates sync stdout from stderr, persists push state earlier, adds OpenCode stale managed-entry pruning, and reuses staged publication logic for local package installation.

Changes

Shenron audit fixes

Layer / File(s) Summary
Repository gates and documentation
.github/workflows/ci.yml, .gitignore, Makefile, docs/...
Adds CI checks, vet and fmt-check targets, backup-file exclusions, audit implementation documentation, and updated architecture and PRD descriptions.
Package publication and state hashing
internal/package/package.go, internal/package/package_test.go, internal/diff/state.go
Delegates local installation to publishStaged, makes lock release one-time in the test, and initializes managed-file hashes from existing files.
Sync output and persistence flow
internal/cli/sync_runtime.go, internal/cli/package_apply.go, internal/cli/sync_test.go, internal/integration_test.go, internal/diff/differ.go
Threads stdout and stderr writers through diff and push execution, saves state before native writes, and updates output-capture tests and integrations.
Managed OpenCode pruning
internal/adapter/..., internal/cli/sync.go, internal/cli/package_apply.go, internal/cli/package_test.go
Adds optional managed pruning, removes stale OpenCode leaves while preserving native entries, recomputes ownership, and tests interrupted pushes and agent removal.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RunPackagePush
  participant runPushAt
  participant Generate
  participant OpenCodeAdapter
  participant StateFile
  RunPackagePush->>runPushAt: start package push
  runPushAt->>StateFile: save managed state before native writes
  runPushAt->>Generate: generate fragments with loaded state
  Generate->>OpenCodeAdapter: prune stale managed leaves and merge fragments
  OpenCodeAdapter-->>Generate: updated opencode.json
  Generate-->>runPushAt: generated files
  runPushAt-->>RunPackagePush: push result
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.59% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main goal of implementing Shenron audit fixes and is clear enough for history scanning.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/shenron-audit-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/integration_test.go (1)

260-268: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

TestEndToEnd_TargetedPushNoClaudeOrphans no longer detects the regression it guards against.

With the new stderr routing, runPushAt emits orphan warnings via printOrphanWarnings(stderr, ...). The test captures stdout (out) and discards stderr (_), then checks out for "warning: orphaned". Since orphan warnings now go to stderr, out will never contain them — the assertion always passes regardless of whether Claude orphan warnings are produced.

🐛 Proposed fix: check stderr instead of stdout
-	out, _, err := cli.CaptureOutput(func() error {
+	_, stderr, err := cli.CaptureOutput(func() error {
 		return cli.RunPush(env.pushOpts("opencode"))
 	})
 	if err != nil {
 		t.Fatalf("targeted push: %v", err)
 	}
-	if strings.Contains(out, "warning: orphaned") && strings.Contains(out, env.claudeDir) {
-		t.Fatalf("targeted push should not warn about Claude orphans, got:\n%s", out)
+	if strings.Contains(stderr, "warning: orphaned") && strings.Contains(stderr, env.claudeDir) {
+		t.Fatalf("targeted push should not warn about Claude orphans, got:\n%s", stderr)
 	}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/integration_test.go` around lines 260 - 268, Update
TestEndToEnd_TargetedPushNoClaudeOrphans to retain and inspect the stderr value
returned by cli.CaptureOutput instead of discarding it, then perform the
orphan-warning assertion against stderr while preserving the existing
Claude-directory check.
🧹 Nitpick comments (4)
docs/plans/2026-07-11-shenron-audit-fixes.md (1)

66-66: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Specify language for fenced code blocks.

Two fenced code blocks (lines 66 and 900) lack a language specifier, triggering markdownlint MD040. The block at line 66 is a bash snippet; the one at line 900 is a plain text list item.

♻️ Proposed fix
-```
+```bash
 # Backup files
 *.bak

And:

-```
- **Markdown frontmatter** : `github.com/adrg/frontmatter`
+```text
+- **Markdown frontmatter** : `github.com/adrg/frontmatter`

Also applies to: 900-900

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/plans/2026-07-11-shenron-audit-fixes.md` at line 66, Specify explicit
languages for both unlabeled fenced code blocks in the documentation: mark the
backup-file snippet near line 66 as bash and the plain-text list item near line
900 as text, preserving their existing contents.

Source: Linters/SAST tools

.github/workflows/ci.yml (1)

16-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider using the official golangci-lint GitHub action.

Piping a remote install script to sh is a supply-chain risk. The official golangci/golangci-lint-action pins versions, caches results, and handles Go compatibility. This is optional but improves supply-chain posture.

♻️ Proposed refactor
-      - name: Install golangci-lint
-        run: |
-          curl -sSf https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.62.2
+      - uses: golangci/golangci-lint-action@v6
+        with:
+          version: v1.62.2
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 16 - 18, Replace the remote
install-script step named “Install golangci-lint” with the official
golangci/golangci-lint-action in the workflow, configuring the required linter
version and preserving the existing lint execution behavior.
internal/cli/sync_runtime.go (1)

36-63: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

CaptureOutput can deadlock on large output.

Both pipes are written to by fn but only read after fn returns. If fn writes more than the pipe buffer (~64KB on Linux) to either stdout or stderr, the write will block and the test will hang. Consider reading from both pipes concurrently via goroutines while fn runs.

♻️ Suggested fix: read pipes concurrently
 func CaptureOutput(fn func() error) (stdout, stderr string, err error) {
 	oldOut, oldErr := os.Stdout, os.Stderr
 	rOut, wOut, err := os.Pipe()
 	if err != nil {
 		return "", "", err
 	}
 	rErr, wErr, err := os.Pipe()
 	if err != nil {
 		_ = wOut.Close()
 		return "", "", err
 	}
 	os.Stdout = wOut
 	os.Stderr = wErr

+	var outData, errData []byte
+	var outErr, errErr error
+	doneOut := make(chan struct{})
+	doneErr := make(chan struct{})
+	go func() { outData, outErr = io.ReadAll(rOut); close(doneOut) }()
+	go func() { errData, errErr = io.ReadAll(rErr); close(doneErr) }()
+
 	runErr := fn()
 	_ = wOut.Close()
 	_ = wErr.Close()
 	os.Stdout = oldOut
 	os.Stderr = oldErr

-	outData, _ := io.ReadAll(rOut)
-	errData, _ := io.ReadAll(rErr)
+	<-doneOut
+	<-doneErr
+	_ = outErr
+	_ = errErr
 	return string(outData), string(errData), runErr
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/cli/sync_runtime.go` around lines 36 - 63, Update CaptureOutput so
rOut and rErr are drained concurrently while fn executes, using separate readers
or goroutines for stdout and stderr; wait for both reads to complete after fn
returns, then restore the original streams and return the captured data and
runErr.
internal/adapter/opencode/adapter_test.go (1)

231-264: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a native entry and content verification to strengthen coverage.

The test verifies stale managed entries are pruned, but doesn't confirm that (1) native (non-managed) entries survive PruneManaged and (2) the remaining agent.build has the updated description from fragments (confirming MergeFile upsert runs after pruning). Adding a native agent and checking build's description would cover both properties in one test.

🧪 Suggested test enhancement
 func TestMergeFilePrunesStaleManagedAgent(t *testing.T) {
 	a := opencode.NewAdapter()
 	existing := []byte(`{
   "agent": {
     "build": {"description": "Build"},
+    "native": {"description": "user-created, not managed"},
     "stale": {"description": "was managed, now removed from pivot"}
   },
   "command": {}
 }`)
 	fragments := map[string]any{
 		"agent.build": map[string]any{"description": "Build and deploy agent"},
 	}
 	managed := map[string][]string{
 		"agent":   {"build", "stale"},
 		"command": {},
 	}

 	pruned, err := a.PruneManaged("opencode.json", existing, managed, fragments)
 	if err != nil {
 		t.Fatal(err)
 	}

 	var root map[string]any
 	if err := json.Unmarshal(pruned, &root); err != nil {
 		t.Fatal(err)
 	}
 	agents := root["agent"].(map[string]any)
 	if _, ok := agents["stale"]; ok {
 		t.Error("stale managed agent should be pruned")
 	}
 	if _, ok := agents["build"]; !ok {
 		t.Error("build agent should remain")
 	}
+	if _, ok := agents["native"]; !ok {
+		t.Error("native (non-managed) agent should be preserved")
+	}
+	build := agents["build"].(map[string]any)
+	if desc, _ := build["description"].(string); desc != "Build and deploy agent" {
+		t.Errorf("build agent should have updated description from fragments, got %q", desc)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/adapter/opencode/adapter_test.go` around lines 231 - 264, Enhance
TestMergeFilePrunesStaleManagedAgent by adding a native, non-managed agent to
the existing configuration and asserting it survives PruneManaged. Also verify
the retained agent.build entry has the fragment’s updated description,
confirming the post-pruning merge upsert.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Line 12: Update the actions/checkout@v4 step to disable persisted GitHub
credentials by setting persist-credentials to false, while leaving the checkout
behavior otherwise unchanged.

---

Outside diff comments:
In `@internal/integration_test.go`:
- Around line 260-268: Update TestEndToEnd_TargetedPushNoClaudeOrphans to retain
and inspect the stderr value returned by cli.CaptureOutput instead of discarding
it, then perform the orphan-warning assertion against stderr while preserving
the existing Claude-directory check.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 16-18: Replace the remote install-script step named “Install
golangci-lint” with the official golangci/golangci-lint-action in the workflow,
configuring the required linter version and preserving the existing lint
execution behavior.

In `@docs/plans/2026-07-11-shenron-audit-fixes.md`:
- Line 66: Specify explicit languages for both unlabeled fenced code blocks in
the documentation: mark the backup-file snippet near line 66 as bash and the
plain-text list item near line 900 as text, preserving their existing contents.

In `@internal/adapter/opencode/adapter_test.go`:
- Around line 231-264: Enhance TestMergeFilePrunesStaleManagedAgent by adding a
native, non-managed agent to the existing configuration and asserting it
survives PruneManaged. Also verify the retained agent.build entry has the
fragment’s updated description, confirming the post-pruning merge upsert.

In `@internal/cli/sync_runtime.go`:
- Around line 36-63: Update CaptureOutput so rOut and rErr are drained
concurrently while fn executes, using separate readers or goroutines for stdout
and stderr; wait for both reads to complete after fn returns, then restore the
original streams and return the captured data and runErr.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cc0a5313-6dd6-4506-aa63-39576c19373e

📥 Commits

Reviewing files that changed from the base of the PR and between 5339d91 and 29193a7.

📒 Files selected for processing (20)
  • .github/workflows/ci.yml
  • .gitignore
  • Makefile
  • docs/ARCHITECTURE.md
  • docs/plans/2026-07-11-shenron-audit-fixes.md
  • docs/prd/shenron.md
  • internal/adapter/adapter.go
  • internal/adapter/opencode/adapter.go
  • internal/adapter/opencode/adapter_test.go
  • internal/adapter/opencode/ordered.go
  • internal/cli/package_apply.go
  • internal/cli/package_test.go
  • internal/cli/sync.go
  • internal/cli/sync_runtime.go
  • internal/cli/sync_test.go
  • internal/diff/differ.go
  • internal/diff/state.go
  • internal/integration_test.go
  • internal/package/package.go
  • internal/package/package_test.go
💤 Files with no reviewable changes (1)
  • docs/prd/shenron.md

Comment thread .github/workflows/ci.yml
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Add persist-credentials: false to the checkout step.

By default, actions/checkout@v4 persists the GITHUB_TOKEN in .git/config for subsequent steps. Since this job only needs the source tree (no git pushes), disabling credential persistence reduces the risk of token leakage if a later step is compromised.

🔒️ Proposed fix
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
       - uses: actions/setup-go@v5
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- uses: actions/checkout@v4
- uses: actions/checkout@v4
with:
persist-credentials: false
🧰 Tools
🪛 zizmor (1.26.1)

[warning] 12-12: credential persistence through GitHub Actions artifacts (artipacked): does not set persist-credentials: false

(artipacked)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml at line 12, Update the actions/checkout@v4 step to
disable persisted GitHub credentials by setting persist-credentials to false,
while leaving the checkout behavior otherwise unchanged.

Source: Linters/SAST tools

@S1933
S1933 merged commit 97825fe into main Jul 12, 2026
1 of 2 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant