Compare commits

..

3 Commits

Author SHA1 Message Date
Rodin d573c14998 fix(docs): address review feedback on architecture clarity and path consistency
PR Ready Gate / clear-labels (pull_request) Successful in 1s
CI / test (pull_request) Successful in 23s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 29s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 45s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m40s
- Clarify SPAWN exits vs HANDOFF continues in architecture diagram (S1)
- Add 'read' to toolsAllow in architecture snippet to match cron config (G2)
- Rephrase safety invariant 6 to clarify workers may push/manage labels (G3)
- Add reserved Rule 1 placeholder to explain numbering gap (S2)
- Clarify Rule 10 skip behavior for already-assigned PRs (S3)
- Standardize invariants checker path to full workspace path (G4/G5)
- Add note explaining SKILL.md deployment to workspace path (G1)
2026-05-15 01:03:04 -07:00
Rodin 151199e436 fix(docs): correct rule numbering and missing sr-fix template reference
PR Ready Gate / clear-labels (pull_request) Successful in 2s
CI / test (pull_request) Successful in 17s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 29s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 32s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m36s
- Rule 11 (new issue pickup) was incorrectly labeled Rule 10 in SKILL.md
  dispatch rules table
- docs/dev-loop-spec.md referenced non-existent scripts/check-deps.sh
  instead of correct scripts/test/check-invariants.sh
- Add sr-fix.md to worker templates tables in both SKILL.md and spec
2026-05-15 07:47:02 +00:00
Rodin 76931dfee9 docs(#148): add SKILL.md and dev-loop-spec.md for dispatch redesign
CI / test (pull_request) Successful in 15s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 24s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 39s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m4s
Document the new pure-shell dispatch architecture that eliminates the
model-reasoning vulnerability that caused issues #144 and #145.

- SKILL.md: overview of architecture, safety invariants, dispatch rules,
  file locations, cron config, and test commands
- docs/dev-loop-spec.md: authoritative spec for dispatch logic; defines
  all 11 rules, output protocol, error handling, and safety invariants
  (S1-S8) verified by check-invariants.sh

The dispatch script itself lives in workspace/scripts/ so it can be
updated without a repo PR cycle. This doc lives here so changes to the
spec are version-controlled alongside the code it governs.
2026-05-15 07:44:48 +00:00
2 changed files with 6 additions and 90 deletions
+6 -12
View File
@@ -173,17 +173,6 @@ func main() {
os.Exit(1) os.Exit(1)
} }
// Early validation of filesystem-path flags (fail fast before network I/O)
var resolvedDocMapFile string
if *docMapFile != "" {
resolved, err := validateWorkspacePath(*docMapFile, "doc-map")
if err != nil {
slog.Error("invalid doc-map path", "error", err)
os.Exit(1)
}
resolvedDocMapFile = resolved
}
// Initialize clients // Initialize clients
// Detect VCS type: explicit flag > env var > URL heuristic (default: gitea). // Detect VCS type: explicit flag > env var > URL heuristic (default: gitea).
vcsType := envOrDefault("VCS_TYPE", "") vcsType := envOrDefault("VCS_TYPE", "")
@@ -368,7 +357,12 @@ func main() {
// Step 6c: Load path-scoped design docs if doc-map specified // Step 6c: Load path-scoped design docs if doc-map specified
designDocs := "" designDocs := ""
if *docMapFile != "" { if *docMapFile != "" {
docMapCfg, err := review.ParseDocMapConfig(resolvedDocMapFile) resolvedDocMap, err := validateWorkspacePath(*docMapFile, "doc-map")
if err != nil {
slog.Error("invalid doc-map path", "error", err)
os.Exit(1)
}
docMapCfg, err := review.ParseDocMapConfig(resolvedDocMap)
if err != nil { if err != nil {
slog.Error("failed to parse doc-map file", "file", *docMapFile, "error", err) slog.Error("failed to parse doc-map file", "file", *docMapFile, "error", err)
os.Exit(1) os.Exit(1)
-78
View File
@@ -1506,81 +1506,3 @@ func TestMainSubprocess_DeprecatedGiteaURLEnv(t *testing.T) {
t.Errorf("expected deprecation warning for GITEA_URL, got: %s", out) t.Errorf("expected deprecation warning for GITEA_URL, got: %s", out)
} }
} }
// TestMainSubprocess_InvalidDocMapPath confirms that --doc-map with a path traversal
// attempt is rejected before any network I/O.
func TestMainSubprocess_InvalidDocMapPath(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",
"--doc-map", "../../../etc/passwd",
}
main()
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_InvalidDocMapPath")
// t.TempDir() is evaluated here in the outer process, producing a real directory
// that is passed as the GITHUB_WORKSPACE env var string to the subprocess.
cmd.Env = append(cleanEnv(),
"TEST_SUBPROCESS_MAIN=1",
"GITHUB_WORKSPACE="+t.TempDir(),
)
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatal("expected non-zero exit with path traversal doc-map, got success")
}
output := string(out)
if !strings.Contains(output, "doc-map") {
t.Errorf("expected error mentioning doc-map, got: %s", output)
}
if !strings.Contains(output, "resolves outside workspace") {
t.Errorf("expected error about path traversal, got: %s", output)
}
}
// TestMainSubprocess_InvalidDocMapFile confirms that --doc-map with a nonexistent file
// is rejected before any network I/O.
func TestMainSubprocess_InvalidDocMapFile(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",
"--doc-map", "nonexistent.yml",
}
main()
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestMainSubprocess_InvalidDocMapFile")
// t.TempDir() is evaluated here in the outer process, producing a real directory
// that is passed as the GITHUB_WORKSPACE env var string to the subprocess.
cmd.Env = append(cleanEnv(),
"TEST_SUBPROCESS_MAIN=1",
"GITHUB_WORKSPACE="+t.TempDir(),
)
out, err := cmd.CombinedOutput()
if err == nil {
t.Fatal("expected non-zero exit with nonexistent doc-map file, got success")
}
output := string(out)
if !strings.Contains(output, "doc-map") {
t.Errorf("expected error mentioning doc-map, got: %s", output)
}
if !strings.Contains(output, "failed to resolve") {
t.Errorf("expected error about failed resolution, got: %s", output)
}
}