Compare commits

...

2 Commits

Author SHA1 Message Date
Rodin 83a1835474 chore(#141): remove TODO.md — dev-loop artifact, not project documentation
PR Ready Gate / clear-labels (pull_request) Successful in 1s
CI / test (pull_request) Successful in 16s
CI / review (anthropic--claude-4.6-sonnet, sonnet, SONNET_REVIEW_TOKEN) (pull_request) Successful in 38s
CI / review (gpt-5, gpt, GPT_REVIEW_TOKEN) (pull_request) Successful in 1m22s
CI / review (gpt-5, security, ., rodin/security-patterns, SECURITY_REVIEW.md, SECURITY_REVIEW_TOKEN) (pull_request) Successful in 1m43s
2026-05-15 00:24:32 -07:00
Rodin 5c6758e990 fix(#141): address review feedback — tighten escape check, improve error messages, add comments 2026-05-15 00:24:28 -07:00
4 changed files with 24 additions and 54 deletions
-47
View File
@@ -1,47 +0,0 @@
## Dev Loop Status: 2026-05-15 07:02 UTC
**Issue:** #141 — validate-docmap subcommand
**Branch:** issue-141
**Status:** ✅ READY FOR PR SUBMISSION
### Implementation Complete
-`cmd/review-bot/validatedocmap.go` — main subcommand implementation (139 lines)
-`cmd/review-bot/validatedocmap_test.go` — 9 test cases (all passing)
-`review/docmap.go` — exported `FileCoveredByDocMap` helper
-`cmd/review-bot/main.go` — integrated `validate-docmap` subcommand
-`README.md` — documented subcommand with examples and flags
### Test Results
```
TestRunValidateDocmap_Clean ✅
TestRunValidateDocmap_MissingDocmapFlag ✅
TestRunValidateDocmap_BadYAML ✅
TestRunValidateDocmap_StaleDocs ✅
TestRunValidateDocmap_UncoveredFile ✅
TestRunValidateDocmap_BothFailures ✅
TestRunValidateDocmap_EmptyStdin ✅
TestRunValidateDocmap_BlankLinesSkipped ✅
TestRunValidateDocmap_DuplicateDocsDeduped ✅
```
### Pre-Push Checks
- ✅ All packages pass: `go test ./...` (6/6)
-`go vet ./...` — clean
-`go build ./cmd/review-bot` — clean
-`scripts/check-deps.sh` — pass
### Commits
1. feat(#141): add validate-docmap subcommand
2. docs(#141): add validate-docmap subcommand to README
3. fix(#141): use stdinValidateDocmap in Clean test — avoid real os.Stdin dependency
### Next Steps
- [ ] Create PR via gitea-rodin token (title: "feat(#141): add validate-docmap subcommand")
- [ ] Labels: none yet (AI reviewers will assign)
- [ ] Link issue #141
+12 -1
View File
@@ -68,8 +68,15 @@ func runValidateDocmap(args []string) int {
failed := false
// --- Check 1: Coverage ---
// Note: an empty docmap (no mappings) means every changed file is
// uncovered — there are no patterns to match against. This is intentional:
// if you declare a doc-map, every changed file must be accounted for.
// On empty stdin the check is vacuously true (no files to cover).
var uncovered []string
for _, f := range changedFiles {
// Normalize Windows-style backslashes to forward slashes so that
// changed-file paths from git on Windows match doc-map globs.
f = strings.ReplaceAll(f, "\\", "/")
if !review.FileCoveredByDocMap(cfg, f) {
uncovered = append(uncovered, f)
}
@@ -93,7 +100,11 @@ func runValidateDocmap(args []string) int {
}
resolvedRoot, err := filepath.EvalSymlinks(absRoot)
if err != nil {
if os.IsNotExist(err) {
fmt.Fprintf(errWriter, "Error: --repo-root %q does not exist\n", *repoRootFlag)
} else {
fmt.Fprintf(errWriter, "Error: failed to resolve --repo-root %q: %v\n", *repoRootFlag, err)
}
return 2
}
// checkStaleDocs validates each path before touching the filesystem; see
@@ -151,7 +162,7 @@ func checkStaleDocs(cfg *review.DocMapConfig, repoRoot string) []string {
// filepath.Rel check confirms the path is still under repoRoot.
fullPath := filepath.Clean(filepath.Join(repoRoot, filepath.FromSlash(docPath)))
rel, err := filepath.Rel(repoRoot, fullPath)
if err != nil || strings.HasPrefix(rel, "..") {
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
stale = append(stale, docPath)
continue
}
+6
View File
@@ -131,6 +131,12 @@ mappings:
}
// stdinValidateDocmap runs runValidateDocmap with a synthetic stdin.
//
// Implementation note: we write stdinContent to a temp file and point
// os.Stdin at it. The defer f.Close() fires after stdinValidateDocmap
// returns, which is after runValidateDocmap has finished reading stdin
// synchronously — so the file is not closed while still in use.
// Tests must not call t.Parallel() while sharing the global os.Stdin.
func stdinValidateDocmap(t *testing.T, stdinContent string, args []string) (code int, stdout, stderr string) {
t.Helper()
// Write stdin content to a temp file and redirect os.Stdin.
+5 -5
View File
@@ -310,11 +310,11 @@ func readFileBytes(path string) ([]byte, error) {
return os.ReadFile(path)
}
// ValidateDocPath rejects doc paths that could cause path traversal via the
// VCS API (absolute paths, any ".." segment, backslashes). Defense-in-depth:
// the VCS API should already scope paths to the repo, but we validate locally
// to avoid any quirk in backend path handling. Backslashes are rejected
// explicitly to prevent Windows platform edge cases.
// ValidateDocPath rejects doc paths that could cause path traversal
// (absolute paths, any ".." segment, backslashes). Defense-in-depth: callers
// must also confine the joined path to the repo root via filepath.Rel before
// any filesystem access. Backslashes are rejected explicitly to prevent
// Windows platform edge cases.
func ValidateDocPath(p string) error {
if strings.Contains(p, "\\") {
return fmt.Errorf("backslashes not allowed in doc paths")