feat: TaskBundle — task file + directory of additionals

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 <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-17 22:48:56 +00:00
parent aed8226edd
commit e7b47bf1a0
14 changed files with 186 additions and 153 deletions

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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.`

View file

@ -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`

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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) {

View file

@ -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) {