Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d75e737f07 | |||
| 1e3d86b604 | |||
| cc053cfede |
+26
-26
@@ -1,6 +1,6 @@
|
|||||||
# Dev Loop Health Check — 2026-05-15 01:33 UTC
|
# Dev Loop Health Check — 2026-05-15 03:33 UTC
|
||||||
|
|
||||||
## Status: ✅ OPTIMAL
|
## Status: ✅ ACTIVE WORK COMPLETED
|
||||||
|
|
||||||
### Test Results
|
### Test Results
|
||||||
- All packages: **PASS** ✅ (6/6, fresh -count=1 run)
|
- All packages: **PASS** ✅ (6/6, fresh -count=1 run)
|
||||||
@@ -18,33 +18,33 @@
|
|||||||
| llm | 81.3% |
|
| llm | 81.3% |
|
||||||
| review | 92.0% |
|
| review | 92.0% |
|
||||||
|
|
||||||
### Recent Activity (since last check 01:28 UTC)
|
### PR #138 Status
|
||||||
- Pulled `d0b0b0b` (dev-loop health update from 01:28 cycle)
|
|
||||||
- No new commits from dev work
|
|
||||||
- No open issues or PRs
|
|
||||||
- Working tree: clean, up to date with origin/main
|
|
||||||
|
|
||||||
### Notes on Coverage
|
- **Branch:** issue-137
|
||||||
- `cmd/review-bot` at 46.1% — main() itself at 26.5%; lowest coverage package
|
- **Feature:** feat(#137): add doc-map input for path-scoped doc injection
|
||||||
- Potential: integration test harness (issue #TBD)
|
- **Review status:** ✅ All 3 bots approved (sonnet, gpt, security)
|
||||||
- `vcs.go` adapter wrappers intentionally 0% — thin delegation, real logic tested in gitea/github packages
|
- **Review findings addressed:**
|
||||||
|
- Fixed package comment collision in `review/docmap.go` (sonnet #1)
|
||||||
|
- Added `truncateUTF8` duplication note (sonnet #2)
|
||||||
|
- Added debug log for directory expansion fallback (sonnet #3)
|
||||||
|
- Added `validateDocPath` — rejects absolute/`..` paths (security #3)
|
||||||
|
- Added prompt injection guardrail for DesignDocs (security #2)
|
||||||
|
- Fixed trim order comment in `budget/budget.go` (gpt #1)
|
||||||
|
- Fixed `globMatch` comment to say `filepath.Match` (gpt nit #3)
|
||||||
|
- Added `doc-map` and `doc-map-max-bytes` to README inputs table (gpt #2)
|
||||||
|
- Added tests for `validateDocPath` and path traversal rejection
|
||||||
|
- Updated CHANGELOG with security fixes
|
||||||
|
- **Labels:** ready, self-reviewed
|
||||||
|
- **Assignee:** aweiker
|
||||||
|
- **Mergeable:** ✅ yes
|
||||||
|
|
||||||
### Next Phase Priorities
|
### Next Priority
|
||||||
1. **PR Submission (#132+)** — Enable review-bot to create PRs
|
|
||||||
2. **`github.Client.DismissReview`** — method referenced in orphaned files, not in client.go; file issue
|
|
||||||
3. **GitHub Enterprise Support** — Enterprise URL patterns, token scopes
|
|
||||||
4. **Increase cmd/review-bot coverage** — integration test harness for main()
|
|
||||||
5. **Performance & Observability** — Metrics, load testing, audit logging
|
|
||||||
|
|
||||||
### System Health
|
- Await merge of PR #138
|
||||||
- ✅ All tests passing
|
- After merge: increase cmd/review-bot coverage (46.1% → target 60%+)
|
||||||
- ✅ No warnings or lint issues
|
- Issue #132+: PR Submission feature
|
||||||
- ✅ Code clean, working tree clean
|
- `github.Client.DismissReview` method referenced but missing — file issue
|
||||||
- ✅ No open issues or PRs on Gitea
|
|
||||||
- ✅ Ready for next development cycle
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Previous check:** 2026-05-15 01:28 UTC
|
_Dev-loop cycle complete at 03:33 UTC._
|
||||||
**This check:** 2026-05-15 01:33 UTC
|
|
||||||
**Action:** NONE — healthy, no work to do
|
|
||||||
|
|||||||
@@ -1383,3 +1383,126 @@ func TestFetchPatterns_MultipleRepos(t *testing.T) {
|
|||||||
t.Errorf("expected Elixir pipes content, got: %q", got)
|
t.Errorf("expected Elixir pipes content, got: %q", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestMainSubprocess_MissingLLMBaseURL confirms that --llm-base-url is required
|
||||||
|
// when provider=openai (the default).
|
||||||
|
func TestMainSubprocess_MissingLLMBaseURL(t *testing.T) {
|
||||||
|
if os.Getenv("TEST_SUBPROCESS_MAIN") == "1" {
|
||||||
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
os.Args = []string{"review-bot",
|
||||||
|
"--vcs-url", "https://gitea.example.com",
|
||||||
|
"--repo", "owner/repo",
|
||||||
|
"--pr", "1",
|
||||||
|
"--reviewer-token", "tok",
|
||||||
|
"--llm-model", "gpt-4",
|
||||||
|
// --llm-base-url and --llm-api-key intentionally omitted
|
||||||
|
}
|
||||||
|
main()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_MissingLLMBaseURL")
|
||||||
|
cmd.Env = append(cleanEnv(), "TEST_SUBPROCESS_MAIN=1")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected non-zero exit when llm-base-url is missing")
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(out), "llm-base-url") {
|
||||||
|
t.Errorf("expected error mentioning llm-base-url, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMainSubprocess_MissingAICoreCredentials confirms that aicore-specific credentials
|
||||||
|
// are required when provider=aicore.
|
||||||
|
func TestMainSubprocess_MissingAICoreCredentials(t *testing.T) {
|
||||||
|
if os.Getenv("TEST_SUBPROCESS_MAIN") == "1" {
|
||||||
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
os.Args = []string{"review-bot",
|
||||||
|
"--vcs-url", "https://gitea.example.com",
|
||||||
|
"--repo", "owner/repo",
|
||||||
|
"--pr", "1",
|
||||||
|
"--reviewer-token", "tok",
|
||||||
|
"--llm-model", "gpt-4",
|
||||||
|
"--llm-provider", "aicore",
|
||||||
|
// aicore-client-id, aicore-client-secret, aicore-auth-url, aicore-api-url omitted
|
||||||
|
}
|
||||||
|
main()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_MissingAICoreCredentials")
|
||||||
|
cmd.Env = append(cleanEnv(), "TEST_SUBPROCESS_MAIN=1")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected non-zero exit when aicore credentials are missing")
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(out), "AI Core credentials") {
|
||||||
|
t.Errorf("expected error about AI Core credentials, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMainSubprocess_ConflictingPersonaFlags confirms that --persona and --persona-file
|
||||||
|
// cannot be used together.
|
||||||
|
func TestMainSubprocess_ConflictingPersonaFlags(t *testing.T) {
|
||||||
|
if os.Getenv("TEST_SUBPROCESS_MAIN") == "1" {
|
||||||
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
os.Args = []string{"review-bot",
|
||||||
|
"--vcs-url", "https://gitea.example.com",
|
||||||
|
"--repo", "owner/repo",
|
||||||
|
"--pr", "1",
|
||||||
|
"--reviewer-token", "tok",
|
||||||
|
"--llm-base-url", "https://api.example.com",
|
||||||
|
"--llm-api-key", "key",
|
||||||
|
"--llm-model", "gpt-4",
|
||||||
|
"--persona", "security",
|
||||||
|
"--persona-file", "custom.json",
|
||||||
|
}
|
||||||
|
main()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_ConflictingPersonaFlags")
|
||||||
|
cmd.Env = append(cleanEnv(), "TEST_SUBPROCESS_MAIN=1")
|
||||||
|
out, err := cmd.CombinedOutput()
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected non-zero exit with both --persona and --persona-file set")
|
||||||
|
}
|
||||||
|
if !strings.Contains(string(out), "mutually exclusive") {
|
||||||
|
t.Errorf("expected error about mutually exclusive flags, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMainSubprocess_DeprecatedGiteaURLEnv confirms that GITEA_URL env var still works
|
||||||
|
// as a deprecated fallback for VCS_URL.
|
||||||
|
func TestMainSubprocess_DeprecatedGiteaURLEnv(t *testing.T) {
|
||||||
|
if os.Getenv("TEST_SUBPROCESS_MAIN") == "1" {
|
||||||
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
// Set required flags but omit --vcs-url; GITEA_URL should be picked up.
|
||||||
|
// The test will exit with an error after VCS init (no PR to fetch), but
|
||||||
|
// the deprecation warning must appear.
|
||||||
|
os.Args = []string{"review-bot",
|
||||||
|
// No --vcs-url: should fall back to GITEA_URL env var
|
||||||
|
"--repo", "owner/repo",
|
||||||
|
"--pr", "1",
|
||||||
|
"--reviewer-token", "tok",
|
||||||
|
"--llm-base-url", "https://api.example.com",
|
||||||
|
"--llm-api-key", "key",
|
||||||
|
"--llm-model", "gpt-4",
|
||||||
|
}
|
||||||
|
main()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_DeprecatedGiteaURLEnv")
|
||||||
|
// Inject GITEA_URL but NOT VCS_URL.
|
||||||
|
env := append(cleanEnv(),
|
||||||
|
"TEST_SUBPROCESS_MAIN=1",
|
||||||
|
"GITEA_URL=https://gitea.example.com",
|
||||||
|
)
|
||||||
|
cmd.Env = env
|
||||||
|
out, _ := cmd.CombinedOutput()
|
||||||
|
// The process will fail (no real server), but the deprecation warning must appear.
|
||||||
|
if !strings.Contains(string(out), "deprecated") {
|
||||||
|
t.Errorf("expected deprecation warning for GITEA_URL, got: %s", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -125,3 +125,60 @@ func TestRunValidateURL_WithCapture(t *testing.T) {
|
|||||||
t.Errorf("expected error about https in stderr, got %q", errBuf.String())
|
t.Errorf("expected error about https in stderr, got %q", errBuf.String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestIsValidateError_Nil confirms that isValidateError returns false for a nil error.
|
||||||
|
func TestIsValidateError_Nil(t *testing.T) {
|
||||||
|
var ve *validateError
|
||||||
|
if isValidateError(nil, &ve) {
|
||||||
|
t.Error("isValidateError(nil, ...) should return false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidateURL_EmptyHost confirms that a URL with no hostname returns a code-2 error.
|
||||||
|
func TestValidateURL_EmptyHost(t *testing.T) {
|
||||||
|
// "https://" parses fine but has no hostname.
|
||||||
|
err := validateURL("https://")
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error for URL with no host, got nil")
|
||||||
|
}
|
||||||
|
var ve *validateError
|
||||||
|
if !isValidateError(err, &ve) {
|
||||||
|
t.Fatalf("expected *validateError, got %T: %v", err, err)
|
||||||
|
}
|
||||||
|
if ve.code != 2 {
|
||||||
|
t.Errorf("expected code 2, got %d (msg=%s)", ve.code, ve.message)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ve.message, "no host") {
|
||||||
|
t.Errorf("expected 'no host' in error message, got %q", ve.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRunValidateURL_Success confirms that a resolvable public URL prints "OK" and returns 0.
|
||||||
|
// This test requires external DNS; it is skipped in environments without network access.
|
||||||
|
func TestRunValidateURL_Success(t *testing.T) {
|
||||||
|
// Pre-check: validate that DNS is available before exercising the success path.
|
||||||
|
err := validateURL("https://example.com/")
|
||||||
|
if err != nil {
|
||||||
|
t.Skipf("skipping success-path test: DNS unavailable or example.com blocked (%v)", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var outBuf, errBuf bytes.Buffer
|
||||||
|
origOut, origErr := outWriter, errWriter
|
||||||
|
outWriter = &outBuf
|
||||||
|
errWriter = &errBuf
|
||||||
|
defer func() {
|
||||||
|
outWriter = origOut
|
||||||
|
errWriter = origErr
|
||||||
|
}()
|
||||||
|
|
||||||
|
code := runValidateURL([]string{"https://example.com/"})
|
||||||
|
if code != 0 {
|
||||||
|
t.Errorf("expected exit code 0 for safe URL, got %d (stderr: %s)", code, errBuf.String())
|
||||||
|
}
|
||||||
|
if !strings.Contains(outBuf.String(), "OK:") {
|
||||||
|
t.Errorf("expected 'OK:' in stdout, got %q", outBuf.String())
|
||||||
|
}
|
||||||
|
if errBuf.Len() != 0 {
|
||||||
|
t.Errorf("expected no stderr for safe URL, got %q", errBuf.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user