From e7b47bf1a01544b16981c935c7dd7b1f451b5672 Mon Sep 17 00:00:00 2001 From: Snider Date: Tue, 17 Mar 2026 22:48:56 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20TaskBundle=20=E2=80=94=20task=20file=20?= =?UTF-8?q?+=20directory=20of=20additionals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pattern: task name maps to file + bundle directory task/code/review.md ← the task definition task/code/review/ ← additional context files conventions.md ← checklist severity.md ← rating guide plan.yaml ← structured phases TaskBundle("code/review") returns (main, bundle, err) where bundle is map[filename]content of everything in review/. Agent name maps to lib path: core:agent-task-code-review → lib/task/code/review + review/ Converted code/ tasks from YAML plans to markdown task prompts with optional bundles. Simplifier gets patterns.md bundle. Co-Authored-By: Virgil --- pkg/prompts/lib/task/code/dead-code.md | 10 +++ pkg/prompts/lib/task/code/dead-code.yaml | 37 ---------- pkg/prompts/lib/task/code/refactor.md | 10 +++ pkg/prompts/lib/task/code/refactor.yaml | 72 ------------------- pkg/prompts/lib/task/code/review.md | 21 ++++++ .../lib/task/code/review/conventions.md | 22 ++++++ .../code/{review.yaml => review/plan.yaml} | 0 pkg/prompts/lib/task/code/review/severity.md | 21 ++++++ pkg/prompts/lib/task/code/simplifier.md | 17 +++++ .../lib/task/code/simplifier/patterns.md | 22 ++++++ pkg/prompts/lib/task/code/test-gaps.md | 10 +++ pkg/prompts/lib/task/code/test-gaps.yaml | 40 ----------- pkg/prompts/prompts.go | 38 +++++++++- pkg/prompts/prompts_test.go | 19 ++++- 14 files changed, 186 insertions(+), 153 deletions(-) create mode 100644 pkg/prompts/lib/task/code/dead-code.md delete mode 100644 pkg/prompts/lib/task/code/dead-code.yaml create mode 100644 pkg/prompts/lib/task/code/refactor.md delete mode 100644 pkg/prompts/lib/task/code/refactor.yaml create mode 100644 pkg/prompts/lib/task/code/review.md create mode 100644 pkg/prompts/lib/task/code/review/conventions.md rename pkg/prompts/lib/task/code/{review.yaml => review/plan.yaml} (100%) create mode 100644 pkg/prompts/lib/task/code/review/severity.md create mode 100644 pkg/prompts/lib/task/code/simplifier.md create mode 100644 pkg/prompts/lib/task/code/simplifier/patterns.md create mode 100644 pkg/prompts/lib/task/code/test-gaps.md delete mode 100644 pkg/prompts/lib/task/code/test-gaps.yaml diff --git a/pkg/prompts/lib/task/code/dead-code.md b/pkg/prompts/lib/task/code/dead-code.md new file mode 100644 index 0000000..fd7948f --- /dev/null +++ b/pkg/prompts/lib/task/code/dead-code.md @@ -0,0 +1,10 @@ +# Dead Code Scan + +Find and remove unreachable code, unused functions, and orphaned files. + +## Process + +1. `go vet ./...` for compiler-detected dead code +2. Search for unexported functions with zero callers +3. Check for unreachable branches (always-true conditions) +4. Verify before deleting — some code is used via reflection or build tags diff --git a/pkg/prompts/lib/task/code/dead-code.yaml b/pkg/prompts/lib/task/code/dead-code.yaml deleted file mode 100644 index 5acaf8c..0000000 --- a/pkg/prompts/lib/task/code/dead-code.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Dead Code Scan -description: Find unreachable functions, unused exports, and orphaned files -category: audit - -guidelines: - - Only flag code that is genuinely unused, not just potentially unused - - Check go.work and cross-repo imports before flagging exports as unused - - Orphaned files include leftover migrations, unused configs, stale test fixtures - -phases: - - name: Unused Exports - description: Find exported functions/types with no callers - tasks: - - "List all exported functions and types" - - "Search for callers within the package and known consumers" - - "Flag exports with zero callers (check CONSUMERS.md for cross-repo usage)" - - - name: Dead Functions - description: Find unexported functions with no internal callers - tasks: - - "List unexported (lowercase) functions" - - "Check for callers within the same package" - - "Flag functions with zero callers" - - - name: Orphaned Files - description: Find files that serve no purpose - tasks: - - "Check for .go files not imported by any other file in the package" - - "Check for test fixtures not referenced by any test" - - "Check for config files not loaded by any code" - - "Check for documentation files referencing deleted code" - - - name: Report - description: Document findings - tasks: - - "List each dead code item with file:line and last git commit that touched it" - - "Classify as safe-to-remove or needs-investigation" diff --git a/pkg/prompts/lib/task/code/refactor.md b/pkg/prompts/lib/task/code/refactor.md new file mode 100644 index 0000000..47ebd03 --- /dev/null +++ b/pkg/prompts/lib/task/code/refactor.md @@ -0,0 +1,10 @@ +# Refactor Task + +Restructure code for clarity without changing behaviour. + +## Process + +1. Identify the refactor target (function, package, pattern) +2. Write tests that lock current behaviour FIRST +3. Apply refactor in small steps, testing after each +4. Verify: same tests pass, same API surface, cleaner internals diff --git a/pkg/prompts/lib/task/code/refactor.yaml b/pkg/prompts/lib/task/code/refactor.yaml deleted file mode 100644 index 9acf5a9..0000000 --- a/pkg/prompts/lib/task/code/refactor.yaml +++ /dev/null @@ -1,72 +0,0 @@ -name: Refactor -description: Improve code quality without changing behaviour -category: development - -variables: - target: - description: Code to refactor (file, class, module) - required: true - goal: - description: What improvement to achieve - required: false - -guidelines: - - Ensure tests exist before refactoring - - Make small, incremental changes - - Commit frequently - - Run tests after each change - -phases: - - name: Assessment - description: Understand current state - tasks: - - Review existing code - - Identify code smells - - Check test coverage - - Document current behaviour - - List specific improvements - - - name: Preparation - description: Set up for safe refactoring - tasks: - - Ensure tests pass - - Add tests for uncovered behaviour - - Create feature branch - - Set up automated checks - - Document expected behaviour - - - name: Extraction - description: Extract and simplify - tasks: - - Extract methods/functions - - Extract classes if too large - - Remove duplication - - Simplify conditionals - - Improve naming - - - name: Structure - description: Improve organisation - tasks: - - Reorganise file structure - - Update namespaces - - Improve class relationships - - Apply design patterns - - Add interfaces where useful - - - name: Polish - description: Final improvements - tasks: - - Add type hints - - Improve documentation - - Remove dead code - - Optimise performance - - Ensure style consistency - - - name: Verification - description: Confirm behaviour unchanged - tasks: - - Run full test suite - - Manual testing - - Performance comparison - - Code review - - Merge and monitor diff --git a/pkg/prompts/lib/task/code/review.md b/pkg/prompts/lib/task/code/review.md new file mode 100644 index 0000000..a916b69 --- /dev/null +++ b/pkg/prompts/lib/task/code/review.md @@ -0,0 +1,21 @@ +# Code Review Task + +Review all changed files for bugs, security issues, and convention violations. + +## Process + +1. Run `git diff --name-only origin/main..HEAD` to find changed files +2. Read each changed file +3. Check against the conventions in `review/conventions.md` +4. Rate each finding by confidence (0-100, report >= 50) +5. Output findings by severity + +## Output + +``` +[SEVERITY] file.go:LINE (confidence: N) +Description of the issue. +Suggested fix. +``` + +End with: `X critical, Y high, Z medium, W low findings.` diff --git a/pkg/prompts/lib/task/code/review/conventions.md b/pkg/prompts/lib/task/code/review/conventions.md new file mode 100644 index 0000000..22030b4 --- /dev/null +++ b/pkg/prompts/lib/task/code/review/conventions.md @@ -0,0 +1,22 @@ +# Core Conventions Checklist + +## Error Handling +- [ ] `coreerr.E("pkg.Method", "msg", err)` — always 3 args +- [ ] Never `fmt.Errorf` or `errors.New` +- [ ] Import as `coreerr "forge.lthn.ai/core/go-log"` + +## File I/O +- [ ] `coreio.Local.Read/Write/EnsureDir` — never `os.ReadFile/WriteFile` +- [ ] `WriteMode(path, content, 0600)` for sensitive files (keys, hashes) +- [ ] Import as `coreio "forge.lthn.ai/core/go-io"` + +## Safety +- [ ] Check `err != nil` BEFORE `resp.StatusCode` +- [ ] Type assertions use comma-ok: `v, ok := x.(Type)` +- [ ] No hardcoded paths (`/Users/`, `/home/`, `host-uk`) +- [ ] No tokens/secrets in error messages or logs + +## Style +- [ ] UK English in comments (colour, organisation, initialise) +- [ ] SPDX-License-Identifier: EUPL-1.2 on every file +- [ ] Test naming: `_Good`, `_Bad`, `_Ugly` diff --git a/pkg/prompts/lib/task/code/review.yaml b/pkg/prompts/lib/task/code/review/plan.yaml similarity index 100% rename from pkg/prompts/lib/task/code/review.yaml rename to pkg/prompts/lib/task/code/review/plan.yaml diff --git a/pkg/prompts/lib/task/code/review/severity.md b/pkg/prompts/lib/task/code/review/severity.md new file mode 100644 index 0000000..a1720e9 --- /dev/null +++ b/pkg/prompts/lib/task/code/review/severity.md @@ -0,0 +1,21 @@ +# Severity Guide + +## CRITICAL (90+ confidence) +- Security vulnerability (injection, traversal, leaked secrets) +- Nil pointer dereference (panic in production) +- Data loss risk + +## HIGH (75+ confidence) +- Convention violation that causes bugs (wrong error handling) +- Missing error check on external call +- Race condition + +## MEDIUM (50+ confidence) +- Convention violation (style, naming) +- Missing test for new code +- Unnecessary complexity + +## LOW (25-49 confidence) +- Nitpick (could be intentional) +- Minor style inconsistency +- Suggestion for improvement diff --git a/pkg/prompts/lib/task/code/simplifier.md b/pkg/prompts/lib/task/code/simplifier.md new file mode 100644 index 0000000..cc1e70e --- /dev/null +++ b/pkg/prompts/lib/task/code/simplifier.md @@ -0,0 +1,17 @@ +# Code Simplifier Task + +Simplify recently modified code without changing behaviour. + +## Process + +1. Run `git diff --name-only origin/main..HEAD` to find changed files +2. Read each file, identify simplification opportunities +3. Apply changes one file at a time +4. `go build ./...` after each change to verify +5. If build breaks, revert + +## Rules + +- NEVER change public API +- NEVER change behaviour +- NEVER add features or comments diff --git a/pkg/prompts/lib/task/code/simplifier/patterns.md b/pkg/prompts/lib/task/code/simplifier/patterns.md new file mode 100644 index 0000000..d9586c0 --- /dev/null +++ b/pkg/prompts/lib/task/code/simplifier/patterns.md @@ -0,0 +1,22 @@ +# Simplification Patterns + +## Consolidate +- Three similar blocks → extract helper function +- Duplicate error handling → shared handler +- Repeated string → constant + +## Flatten +- Nested if/else → early return +- Deep indentation → guard clauses +- Long switch → map lookup + +## Remove +- Unused variables, imports, functions +- Wrapper functions that just delegate +- Dead branches (always true/false conditions) +- Comments that restate the code + +## Tighten +- Long function (>50 lines) → split +- God struct → focused types +- Mixed concerns → separate files diff --git a/pkg/prompts/lib/task/code/test-gaps.md b/pkg/prompts/lib/task/code/test-gaps.md new file mode 100644 index 0000000..4b5cf74 --- /dev/null +++ b/pkg/prompts/lib/task/code/test-gaps.md @@ -0,0 +1,10 @@ +# Test Coverage Gaps + +Find untested code paths and write tests for them. + +## Process + +1. `go test -cover ./...` to identify coverage +2. Focus on exported functions with 0% coverage +3. Write tests using Good/Bad/Ugly naming +4. Prioritise: error paths > happy paths > edge cases diff --git a/pkg/prompts/lib/task/code/test-gaps.yaml b/pkg/prompts/lib/task/code/test-gaps.yaml deleted file mode 100644 index 2bf87b7..0000000 --- a/pkg/prompts/lib/task/code/test-gaps.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Test Coverage Gaps -description: Find functions without test coverage and missing edge case tests -category: audit - -guidelines: - - Focus on exported functions first (public API) - - Error paths are more important than happy paths (they're usually untested) - - Tests should follow _Good/_Bad/_Ugly naming convention - - Use testify assert/require, table-driven preferred - -phases: - - name: Coverage Analysis - description: Identify untested exported functions - tasks: - - "List all exported functions and methods" - - "Check each has at least one test calling it" - - "Flag functions with zero test coverage" - - "Note which are critical paths (called by many consumers)" - - - name: Error Path Analysis - description: Find untested error conditions - tasks: - - "For each function that returns error, check if error paths are tested" - - "Check for nil/empty input handling tests" - - "Check for boundary condition tests (zero, max, negative)" - - "Flag error paths with no test coverage" - - - name: Missing Edge Cases - description: Find obvious gaps in existing tests - tasks: - - "Check concurrent access tests for types with mutexes" - - "Check for tests with hardcoded paths or environment assumptions" - - "Check for tests that only test the happy path" - - - name: Report - description: Document findings with priority - tasks: - - "List untested functions with file:line, consumer count, and priority" - - "List untested error paths with the error condition" - - "Suggest specific test cases to add" diff --git a/pkg/prompts/prompts.go b/pkg/prompts/prompts.go index 3158c24..ce6c1db 100644 --- a/pkg/prompts/prompts.go +++ b/pkg/prompts/prompts.go @@ -30,7 +30,7 @@ import ( //go:embed lib/prompt/*.md var promptFS embed.FS -//go:embed lib/task +//go:embed all:lib/task var taskFS embed.FS //go:embed lib/flow/*.md @@ -57,10 +57,10 @@ func Template(slug string) (string, error) { return Task(slug) } -// Task returns a structured task plan by slug (written as PLAN.md). +// Task returns a task definition by slug. // Slugs: "bug-fix", "new-feature", "code/review", "code/refactor", etc. func Task(slug string) (string, error) { - for _, ext := range []string{".yaml", ".yml", ".md"} { + for _, ext := range []string{".md", ".yaml", ".yml"} { data, err := taskFS.ReadFile("lib/task/" + slug + ext) if err == nil { return string(data), nil @@ -69,6 +69,38 @@ func Task(slug string) (string, error) { return "", fs.ErrNotExist } +// TaskBundle returns the task definition plus all additional files in its bundle directory. +// For "code/review": returns review.md content + map of {filename: content} from review/. +// The bundle directory has the same name as the task file (without extension). +func TaskBundle(slug string) (string, map[string]string, error) { + // Get the main task definition + main, err := Task(slug) + if err != nil { + return "", nil, err + } + + // Look for a bundle directory with the same name + bundleDir := "lib/task/" + slug + entries, err := fs.ReadDir(taskFS, bundleDir) + if err != nil { + // No bundle — just the task definition + return main, nil, nil + } + + bundle := make(map[string]string) + for _, e := range entries { + if e.IsDir() { + continue + } + data, err := taskFS.ReadFile(bundleDir + "/" + e.Name()) + if err == nil { + bundle[e.Name()] = string(data) + } + } + + return main, bundle, nil +} + // Flow returns a build/release workflow by slug. // Slugs: "go", "php", "ts", "docker", "release", etc. func Flow(slug string) (string, error) { diff --git a/pkg/prompts/prompts_test.go b/pkg/prompts/prompts_test.go index 08630a7..cecbf4f 100644 --- a/pkg/prompts/prompts_test.go +++ b/pkg/prompts/prompts_test.go @@ -31,7 +31,24 @@ func TestTask_Good(t *testing.T) { func TestTask_Good_Nested(t *testing.T) { content, err := Task("code/review") require.NoError(t, err) - assert.Contains(t, content, "name:") + assert.Contains(t, content, "Code Review") +} + +func TestTaskBundle_Good(t *testing.T) { + main, bundle, err := TaskBundle("code/review") + require.NoError(t, err) + assert.Contains(t, main, "Code Review") + assert.NotNil(t, bundle) + assert.Contains(t, bundle, "conventions.md") + assert.Contains(t, bundle, "severity.md") + assert.Contains(t, bundle["conventions.md"], "coreerr.E") +} + +func TestTaskBundle_Good_NoBundleDir(t *testing.T) { + main, bundle, err := TaskBundle("bug-fix") + require.NoError(t, err) + assert.Contains(t, main, "name:") + assert.Nil(t, bundle) } func TestTask_Bad_NotFound(t *testing.T) {