Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
tag: vibe-check-command-and-reporter
author: jay-flowers
category: pattern
created_at: 2026-09-02T14:34:41Z
identity: vibe-check-command-and-reporter-20260902T143441-jay-flowers
tier: draft
---

When extending the vibe-check scaffold system to deploy multiple asset categories (agents and commands), the key architectural decision was extracting a deployCategory helper function that encapsulates the per-category deployment logic (glob, ensureDir, file iteration, prefix). The Run() function calls deployCategory for each category and merges Result slices. Category-prefixed Result paths (e.g., "agents/divisor-entropy.md", "commands/vibe-check.md") disambiguate entries from different categories. The refactoring preserved all existing security properties (symlink safety, containment checks, path validation) by reusing ensureDir and verifyContained for both deployment targets. Two separate //go:embed directives with separate embed.FS variables are required because Go's embed directive does not support multiple glob patterns in one directive for different directories.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
tag: vibe-check-command-and-reporter
author: jay-flowers
category: gotcha
created_at: 2026-09-02T14:34:45Z
identity: vibe-check-command-and-reporter-20260902T143445-jay-flowers
tier: draft
---

The AD-007 400-line file size threshold is a MUST rule that should be addressed proactively during implementation, not reactively during code review. For scaffold_test.go, the natural split is three files: scaffold_test.go for deployment lifecycle tests (deploy, skip, force, sort, mixed results), security_test.go for symlink/traversal/containment tests, and contract_test.go for embedded asset contract tests and helper functions (frontmatterDescription, bashPermissions). The test helper types (orderedGlobFS, emptyFS) belong in scaffold_test.go since they're used by the lifecycle tests. Constants shared across test files are accessible because all files are in the same package.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
tag: vibe-check-command-and-reporter
author: jay-flowers
category: pattern
created_at: 2026-09-02T14:34:57Z
identity: vibe-check-command-and-reporter-20260902T143457-jay-flowers
tier: draft
---

The spec review for the vibe-check-command-and-reporter change revealed several recurring patterns across all six divisor agents: (1) Go's //go:embed directive with a glob pattern will fail at compile time if no files match — specs should not describe zero-match scenarios as succeeding; (2) Constitution Principle IV requires a coverage strategy section in the design document — the Tester classified its absence as CRITICAL while other reviewers classified it as MEDIUM/HIGH, resolving to add a Test Strategy section with three categories (scaffold Go code, embedded asset contracts, agent behavioral validation); (3) documentation tasks (README, AGENTS.md, CHANGELOG, doc.go, init.go GoDoc) are consistently missed during initial task creation — adding a dedicated documentation section to tasks.md prevents this gap.
17 changes: 10 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ go build ./...
# Build the CLI binary
go build ./cmd/vibe-check

# Deploy embedded Review Council agent assets into .opencode/agents/ of a project
# Deploy embedded agent and command assets into .opencode/ of a project
go run ./cmd/vibe-check init . # --force to overwrite, --json for machine output

# Analyze a module and write ModuleGraph JSON to a file (default: stdout)
Expand Down Expand Up @@ -276,7 +276,7 @@ cmd/vibe-check/ # CLI entry point (Layer 3)
root.go # Cobra root command with --version flag
analyze.go # analyze subcommand with threshold flags (incl --output/-o)
diff.go # diff subcommand (base vs PR entropy delta + verdict)
init.go # init subcommand (deploys embedded agent assets)
init.go # init subcommand (deploys embedded agent and command assets)
internal/goadapter/ # Go language adapter (Layer 2)
adapter.go # Adapter struct implementing metrics.Adapter
resolve.go # Package loading via go/packages
Expand All @@ -286,13 +286,16 @@ internal/goadapter/ # Go language adapter (Layer 2)
extensions.go # go.interfaceWidth and go.interfaceProximity extensions
doc.go # Package-level GoDoc
testdata/ # Test fixtures (coupling, types, lcom, extensions, partial)
internal/scaffold/ # Embedded agent-asset deployment for `vibe-check init`
internal/scaffold/ # Embedded asset deployment for `vibe-check init`
doc.go # Package-level GoDoc
embed.go # //go:embed assets/agents/*.md (embedded source of truth)
embed.go # //go:embed assets/{agents,commands}/*.md (embedded source of truth)
scaffold.go # Symlink-safe asset writer (skip/force; 0o755 dirs, 0o644 files)
scaffold_test.go # Writer + embedded-asset contract tests
assets/agents/ # Embedded Review Council agent assets
divisor-entropy.md # Structural-entropy divisor agent (source of truth)
divisor-entropy.md # Structural-entropy divisor agent (source of truth)
vibe-check-reporter.md # Interactive metrics reporter agent (summary/detailed/trending)
assets/commands/ # Embedded slash command assets
vibe-check.md # /vibe-check command (delegates to vibe-check-reporter agent)
metrics/ # Universal coupling metrics model (Layer 1)
adapter.go # Adapter interface and Capability type
compute.go # Metric computation functions
Expand Down Expand Up @@ -341,8 +344,8 @@ The architecture follows a three-layer design per the RFC phasing:
`--max-distance`, `--max-lcom`, `--no-circular-deps`, `--timeout`,
`--output`/`-o`) and JSON output; `vibe-check diff <base.json> <pr.json>`
computing the entropy delta and verdict (with tighten-only threshold
overrides); and `vibe-check init [path]` deploying the embedded Review
Council agent assets into `.opencode/agents/`.
overrides); and `vibe-check init [path]` deploying the embedded agent assets
into `.opencode/agents/` and command assets into `.opencode/commands/`.

RFC phasing status:

Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
deployed by `vibe-check init`): measures the base↔PR design-quality
delta via `vibe-check analyze` and `vibe-check diff` inside an isolated
git worktree and reports a verdict. Runs only on trusted refs.
- `/vibe-check` slash command asset (deployed by `vibe-check init` to
`.opencode/commands/vibe-check.md`): delegates to the `vibe-check-reporter`
agent for conversational architectural analysis with three modes —
summary (traffic-light health indicator), detailed (per-package
breakdown), and trending (longitudinal metric comparison via Dewey
snapshots).
Spec: `openspec/changes/vibe-check-command-and-reporter/`
- `vibe-check-reporter` agent asset (deployed by `vibe-check init` to
`.opencode/agents/vibe-check-reporter.md`): interprets Martin coupling
metrics in natural language, runs `vibe-check analyze` to gather data,
and stores metric snapshots in Dewey for trend tracking.
Spec: `openspec/changes/vibe-check-command-and-reporter/`
- `vibe-check init` now deploys command assets to `.opencode/commands/`
alongside agent assets in `.opencode/agents/`. The scaffold system
uses a `deployCategory` helper to iterate both asset categories with
the same symlink-safe, containment-checked pattern.
Spec: `openspec/changes/vibe-check-command-and-reporter/`
- `vibe-check analyze --output <file>` (`-o`) writes the ModuleGraph JSON
to a file instead of stdout (stdout remains the default; a failed write
exits with code 2 and a stderr diagnostic without emitting partial
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,24 @@ It exits `0` whenever both inputs are valid — the verdict is data in the paylo
schema-invalid, or when a `--max-instability-delta`, `--max-distance-delta`, or
`--max-lcom-delta` override is looser than the protected default (overrides may only tighten).

## Deploying agents: `vibe-check init`
## Deploying agents and commands: `vibe-check init`

`init` deploys the embedded Review Council agent assets into a project's `.opencode/agents/`
directory:
`init` deploys the embedded agent and command assets into a project's `.opencode/` directory:

```bash
vibe-check init [path] # path defaults to "."; --force to overwrite, --json for machine output
```

It writes the bundled `divisor-entropy` agent — a structural-entropy reviewer that runs
`analyze` + `diff` across a base↔PR pair in an isolated worktree — and skips files that
already exist unless `--force` is given. It exits `0` on success (including when every asset
is skipped) and `2` on an invalid target path or I/O failure.
It writes assets to two directories:

- `.opencode/agents/` — the `divisor-entropy` agent (structural-entropy reviewer that runs
`analyze` + `diff` across a base↔PR pair) and the `vibe-check-reporter` agent (interactive
metrics interpreter with summary, detailed, and trending modes).
- `.opencode/commands/` — the `/vibe-check` slash command, which delegates to the
`vibe-check-reporter` agent for conversational architectural analysis.

Existing files are skipped unless `--force` is given. Exit code `0` on success (including
when every asset is skipped), `2` on an invalid target path or I/O failure.

## Known limitations

Expand Down
46 changes: 25 additions & 21 deletions cmd/vibe-check/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ type InitOptions struct {
Stdout io.Writer
// Stderr is the writer for diagnostics and errors. Required.
Stderr io.Writer
// Path is the target project directory into which agent assets are deployed.
// Empty defaults to the current directory (".").
// Path is the target project directory into which agent and command assets
// are deployed. Empty defaults to the current directory (".").
Path string
// Force overwrites existing agent asset files instead of skipping them.
// Force overwrites existing asset files instead of skipping them.
Force bool
// JSON selects machine-readable JSON output when true; otherwise a
// human-readable summary is written.
Expand All @@ -42,12 +42,14 @@ type InitOptions struct {
// InitResult contains the outcome of an init deployment.
// It follows the AP-001 Result struct pattern.
type InitResult struct {
// Written lists the asset filenames newly created.
// Written lists the category-prefixed asset paths newly created (e.g.
// "agents/divisor-entropy.md", "commands/vibe-check.md").
Written []string
// Skipped lists the asset filenames left untouched because they already
// existed and Force was not set.
// Skipped lists the category-prefixed asset paths left untouched because
// they already existed and Force was not set.
Skipped []string
// Forced lists the asset filenames overwritten because Force was set.
// Forced lists the category-prefixed asset paths overwritten because Force
// was set.
Forced []string
// ExitCode is the process exit code: 0 on success (including an all-skipped
// run), 2 on an invalid target path or an I/O failure.
Expand All @@ -64,10 +66,10 @@ type initJSON struct {
Forced []string `json:"forced"`
}

// RunInit deploys the embedded Review Council agent assets into the target
// project's .opencode/agents/ directory and writes a summary to opts.Stdout. It
// is the testable entry point per AP-002/AP-003: all business logic lives here,
// not in the cobra command layer.
// RunInit deploys the embedded agent and command assets into the target
// project's .opencode/agents/ and .opencode/commands/ directories and writes a
// summary to opts.Stdout. It is the testable entry point per AP-002/AP-003: all
// business logic lives here, not in the cobra command layer.
//
// Exit code semantics (also mirrored in the returned InitResult.ExitCode):
// - 0: assets were deployed (or all skipped) and the summary was written.
Expand Down Expand Up @@ -147,12 +149,13 @@ func writeInitJSON(w io.Writer, res *scaffold.Result) error {
}

// writeInitSummary renders the deployment result as a human-readable summary to
// w. It names the target agents directory, then lists the written, skipped, and
// forced assets. The lists are consumed in the stable, sorted order scaffold.Run
// guarantees, so output is byte-stable across runs.
// w. It names the target .opencode directory, then lists the written, skipped,
// and forced assets. The lists contain category-prefixed paths (e.g.
// "agents/divisor-entropy.md", "commands/vibe-check.md") in stable sorted
// order.
func writeInitSummary(w io.Writer, targetDir string, res *scaffold.Result) {
agentsDir := filepath.Join(targetDir, ".opencode", "agents")
_, _ = fmt.Fprintf(w, "Deployed agent assets under %s:\n", agentsDir)
openCodeDir := filepath.Join(targetDir, ".opencode")
_, _ = fmt.Fprintf(w, "Deployed agent and command assets under %s:\n", openCodeDir)
writeListSection(w, "Written:", res.Written)
writeListSection(w, "Skipped:", res.Skipped)
writeListSection(w, "Forced:", res.Forced)
Expand All @@ -178,10 +181,11 @@ func initCmd() *cobra.Command {

cmd := &cobra.Command{
Use: "init [path]",
Short: "Deploy vibe-check Review Council agent assets into a project",
Long: `Init deploys the embedded vibe-check Review Council agent assets (such as the
divisor-entropy structural-entropy reviewer) into a target project's
.opencode/agents/ directory.
Short: "Deploy vibe-check agent and command assets into a project",
Long: `Init deploys the embedded vibe-check agent assets (such as the
divisor-entropy structural-entropy reviewer and the vibe-check-reporter) and
command assets (such as the /vibe-check slash command) into a target project's
.opencode/agents/ and .opencode/commands/ directories.

Existing files are skipped by default; use --force to overwrite them. The target
path defaults to the current directory. Output is a human-readable summary by
Expand Down Expand Up @@ -237,7 +241,7 @@ path is invalid or an asset cannot be written.`,
},
}

cmd.Flags().BoolVar(&force, "force", false, "Overwrite existing agent asset files instead of skipping them")
cmd.Flags().BoolVar(&force, "force", false, "Overwrite existing asset files instead of skipping them")
cmd.Flags().BoolVar(&jsonOut, "json", false, "Emit a machine-readable JSON payload instead of a summary")

return cmd
Expand Down
Loading
Loading