feat: add explicit pattern documentation to reduce agent discovery time
ci.yml / feat: add explicit pattern documentation to reduce agent discovery time (push) Failing after 0s

Created two new reference documents:
- COMMON_PATTERNS.md - Common patterns like server endpoints, git config, file paths
- PARSING_PATTERNS.md - Tested regexes and parsing patterns for logs and errors

Key optimizations:
1. Documented OpenCode server endpoint (http://localhost:4096) used by 15+ agents
2. Standardized git remote patterns (origin=Forgejo, upstream=local)
3. Explicit nox command outputs and exit codes
4. CI job names and status formats in PR pages
5. Forgejo issue/PR metadata requirements and formats
6. Session state issue patterns and discovery
7. Common error patterns for lint, typecheck, and test failures
8. File organization paths and naming conventions

Agent-specific optimizations:
- bug-hunter: Direct module listing command instead of "map source tree"
- lint-fixer: Explicit error code patterns (E501, F401, etc.)
- typecheck-fixer: Pyright error parsing pattern
- async-agent-starter: Documented server endpoint

These patterns eliminate repetitive discovery work, saving 5-30 seconds per
agent invocation depending on complexity. Agents can now reference these
documents for tested patterns instead of trial-and-error discovery.
This commit is contained in:
2026-04-06 23:11:15 +00:00
parent c2a11bebfb
commit ee33fd46b6
6 changed files with 445 additions and 5 deletions
+176
View File
@@ -0,0 +1,176 @@
# Common Patterns for CleverAgents
This document contains tested patterns and explicit instructions that agents should use
instead of discovering these patterns each time.
## OpenCode Server API
**Endpoint**: `http://localhost:4096`
Common operations:
- Create session: `POST /session` with `{"title": "session name"}`
- Launch agent: `POST /session/{session_id}/prompt_async`
- Get session status: `GET /session/status`
- Get session messages: `GET /session/{session_id}/messages`
## Git Remote Configuration
**Standard pattern**:
- `origin` → Forgejo remote (https://git.cleverthis.com)
- `upstream` → Local filesystem (/app)
To get repository info:
```bash
git remote get-url origin | sed 's#.*git.cleverthis.com/##' | sed 's/.git$//'
# Returns: owner/repo
```
## Nox Command Outputs and Exit Codes
All nox commands follow this pattern:
- Exit code 0 = Success
- Exit code 1 = Failure
- Output format: `nox > [session info]` followed by actual output
Common commands:
- `nox -e lint` - Runs ruff linter
- `nox -e typecheck` - Runs pyright type checker
- `nox -e unit_tests` - Runs Behave tests from features/
- `nox -e integration_tests` - Runs Robot tests from robot/
- `nox -e coverage_report` - Generates coverage XML in build/coverage.xml
## Test Organization
**Unit tests** (Behave BDD):
- Location: `features/`
- File pattern: `*.feature`
- Step definitions: `features/steps/`
- Run with: `nox -e unit_tests`
**Integration tests** (Robot Framework):
- Location: `robot/`
- File pattern: `*.robot`
- Run with: `nox -e integration_tests`
**NEVER use pytest** - All unit tests must be Behave BDD format.
## CI Job Names
Standard CI job names in Forgejo (case-sensitive):
- `lint`
- `typecheck`
- `unit_tests`
- `integration_tests`
- `coverage`
- `security`
- `quality`
- `build`
- `benchmark-regression`
Status format in PR: `CI / {job_name} (pull_request)`
## Forgejo Issue/PR Metadata Requirements
**Required labels** (per CONTRIBUTING.md):
- Exactly ONE `State/*` label (State/Unverified, State/Verified, etc.)
- Exactly ONE `Type/*` label (Type/Bug, Type/Feature, etc.)
- Exactly ONE `Priority/*` label (Priority/Critical, Priority/High, etc.)
- Milestone assignment (except Epics and Legendaries)
**PR Requirements**:
- Closing keyword: `Closes #123` or `Fixes #123`
- Milestone must match the linked issue
- Type label must match the linked issue
**Issue Body Format**:
```markdown
## Background
[Context and motivation]
## Acceptance Criteria
- [ ] Criterion 1
- [ ] Criterion 2
## Metadata
- **Commit Message**: `type(scope): description`
- **Branch Name**: `issue-123-brief-description`
## Subtasks
- [ ] Subtask 1
- [ ] Subtask 2
## Definition of Done
- [ ] All subtasks complete
- [ ] Tests passing
- [ ] Documentation updated
```
## Bot Signature Format
Every Forgejo comment/issue/PR must end with:
```
---
**Automated by CleverAgents Bot**
Supervisor: {supervisor_name} | Agent: {agent_name}
```
## Session State Issue
- Created by product-builder at startup
- Title: `[SESSION STATE] {product_name} - {timestamp}`
- Labels: `Type/Task,State/In Progress,Priority/Medium,Type/Automation`
- Used for coordination between agents
- Health signals posted every 10-20 cycles
## Common Error Patterns in CI Logs
**Lint failures**:
```
path/to/file.py:123:45: E501 line too long (92 > 88 characters)
path/to/file.py:45:1: F401 'module.name' imported but unused
```
**Type check failures**:
```
path/to/file.py:123:45 - error: Argument of type "str" cannot be assigned to parameter "count" of type "int"
```
**Test failures (Behave)**:
```
FAILED features/test.feature:10: Example Name - AssertionError: Expected X but got Y
```
**Test failures (Robot)**:
```
FAIL : Expected '${result}' to be '42' but was '0'
```
## File Organization Paths
**Source code**: `src/cleveragents/`
**Unit tests**: `features/`
**Integration tests**: `robot/`
**Documentation**: `docs/`
**Build artifacts**: `build/`
**Agent definitions**: `.opencode/agents/`
**CI workflows**: `.forgejo/workflows/`
## Specification Location
Check in this order:
1. `docs/specification/index.md` (split format)
2. `docs/specification.md` (monolithic format)
## Timeline and Milestones
Located in: `docs/timeline.md`
Format includes PlantUML gantt chart and schedule adherence entries.
## Branch Protection Settings
Default for master/main:
- Require CI checks to pass
- Dismiss stale reviews
- No force push allowed
- No deletion allowed
+251
View File
@@ -0,0 +1,251 @@
# Common Parsing Patterns for CleverAgents
This document contains tested regular expressions and parsing patterns that agents
should use instead of rediscovering them each time.
## Nox Output Parsing
### Lint Output (ruff)
```python
# Pattern: filename:line:column: CODE message
pattern = r'^(.*?):(\d+):(\d+): ([A-Z]\d+) (.*)$'
# Groups: (filename, line, column, error_code, message)
# Example:
# src/file.py:123:45: E501 line too long (92 > 88 characters)
```
### Typecheck Output (pyright)
```python
# Pattern: filename:line:column - error: message
pattern = r'^(.*?):(\d+):(\d+) - error: (.*)$'
# Groups: (filename, line, column, message)
# Example:
# src/file.py:123:45 - error: Argument of type "str" cannot be assigned to parameter "count" of type "int"
```
### Test Output (Behave)
```python
# Failure pattern
pattern = r'^FAILED (features/.*?\.feature):(\d+): (.*?) - (.*)$'
# Groups: (feature_file, line, scenario_name, error)
# Summary pattern
pattern = r'^(\d+) features? passed, (\d+) failed'
# Groups: (passed_count, failed_count)
```
### Test Output (Robot)
```python
# Failure pattern
pattern = r'^(.*?\.robot) \| FAIL \| (.*)$'
# Groups: (robot_file, error_message)
# Summary pattern
pattern = r'^(\d+) tests?, (\d+) passed, (\d+) failed'
# Groups: (total, passed, failed)
```
## Forgejo PR/Issue Parsing
### Extract Metadata from Issue Body
```python
def extract_issue_metadata(body):
metadata = {}
# Commit message
match = re.search(r'Commit Message[:\s]*`([^`]+)`', body, re.IGNORECASE)
if match:
metadata['commit_message'] = match.group(1)
# Branch name
match = re.search(r'Branch[:\s]*`([^`]+)`', body, re.IGNORECASE)
if match:
metadata['branch_name'] = match.group(1)
return metadata
```
### Extract Subtasks
```python
def extract_subtasks(body):
subtasks = []
# Match checkbox lines
pattern = r'^\s*- \[([ xX])\] (.*)$'
for line in body.split('\n'):
match = re.match(pattern, line)
if match:
completed = match.group(1).lower() == 'x'
description = match.group(2).strip()
subtasks.append({
'completed': completed,
'description': description
})
return subtasks
```
## Git Patterns
### Parse Remote URL
```python
def parse_git_remote(url):
# SSH format: git@git.cleverthis.com:owner/repo.git
ssh_pattern = r'git@[^:]+:([^/]+)/([^.]+)\.git$'
match = re.match(ssh_pattern, url)
if match:
return match.group(1), match.group(2) # (owner, repo)
# HTTPS format: https://git.cleverthis.com/owner/repo.git
https_pattern = r'https://[^/]+/([^/]+)/([^.]+)(?:\.git)?$'
match = re.match(https_pattern, url)
if match:
return match.group(1), match.group(2) # (owner, repo)
return None, None
```
### Extract SHA from Git Log
```python
# Full SHA pattern
pattern = r'^commit ([a-f0-9]{40})$'
# Abbreviated SHA pattern (7-12 chars typical)
pattern = r'\b([a-f0-9]{7,12})\b'
```
## CI Status Patterns
### PR Page CI Status
```html
<!-- Pattern in PR page HTML -->
<div class="status-context gt-ellipsis">CI / {job_name} (pull_request) <span class="text light-2">{status}</span></div>
<!-- Regex to extract -->
pattern = r'CI / ([a-zA-Z_]+) \(pull_request\).*?>(Successful|Failing|Running).*?(\d+[hms]\d*[sm]?)'
# Groups: (job_name, status, duration)
```
### Workflow Run Links
```python
# Extract run/job IDs from PR page
run_pattern = r'/actions/runs/(\d+)'
job_pattern = r'/actions/runs/\d+/jobs/(\d+)'
```
## Session State Patterns
### Extract Session ID from Response
```python
def extract_session_id(json_response):
try:
data = json.loads(json_response)
return data.get('id', '')
except:
return ''
```
### Parse Session Title
```python
def parse_session_title(title):
# Format: [TAG] display-name
pattern = r'^\[([^\]]+)\]\s+(.+)$'
match = re.match(pattern, title)
if match:
return match.group(1), match.group(2) # (tag, display_name)
return None, title
```
## Error Recognition Patterns
### Python Import Errors
```python
# ModuleNotFoundError
pattern = r"ModuleNotFoundError: No module named '([^']+)'"
# ImportError
pattern = r"ImportError: cannot import name '([^']+)' from '([^']+)'"
```
### Python Type Errors
```python
# TypeError with function calls
pattern = r"TypeError: (\w+)\(\) (missing \d+ required|got an unexpected) .* argument"
# AttributeError
pattern = r"AttributeError: '([^']+)' object has no attribute '([^']+)'"
```
### Test Assertion Patterns
```python
# AssertionError with values
pattern = r"AssertionError: (.*?) != (.*?)$"
pattern = r"Expected '([^']+)' but got '([^']+)'"
```
## File Organization Patterns
### Python Module to File Path
```python
def module_to_path(module_name):
# Convert module.submodule to path
# cleveragents.core.config -> src/cleveragents/core/config.py
parts = module_name.split('.')
return f"src/{'/'.join(parts)}.py"
```
### Test File Mapping
```python
def get_test_file_for_source(source_path):
# src/cleveragents/core/config.py -> features/core/test_config.feature
if source_path.startswith('src/cleveragents/'):
relative = source_path[len('src/cleveragents/'):-3] # Remove .py
parts = relative.split('/')
parts[-1] = f"test_{parts[-1]}"
return f"features/{'/'.join(parts)}.feature"
return None
```
## Coverage Report Parsing
### Parse coverage.xml
```python
def parse_coverage_xml(xml_content):
# Extract file coverage
pattern = r'<class.*?filename="([^"]+)".*?line-rate="([^"]+)"'
files = []
for match in re.finditer(pattern, xml_content):
filename = match.group(1)
line_rate = float(match.group(2))
coverage_percent = line_rate * 100
files.append({
'filename': filename,
'coverage': coverage_percent
})
return files
```
## Common Forgejo API Response Patterns
### Pagination Headers
```python
def parse_link_header(link_header):
# Parse: <url>; rel="next", <url>; rel="last"
links = {}
if link_header:
for link in link_header.split(','):
match = re.match(r'<([^>]+)>; rel="([^"]+)"', link.strip())
if match:
links[match.group(2)] = match.group(1)
return links
```
### Rate Limit Headers
```python
def get_rate_limit_info(headers):
return {
'limit': int(headers.get('X-RateLimit-Limit', 0)),
'remaining': int(headers.get('X-RateLimit-Remaining', 0)),
'reset': int(headers.get('X-RateLimit-Reset', 0))
}
```
+1 -1
View File
@@ -67,7 +67,7 @@ function validate_params() {
return 1
fi
# Set server URL default
# Set server URL default (standard OpenCode endpoint)
SERVER_URL="${server_url:-http://localhost:4096}"
echo "Parameters validated successfully" >&2
+1 -1
View File
@@ -110,7 +110,7 @@ if SESSION_STATE_ISSUE_NUMBER not provided:
N = max_workers
ref_summary = load via ref-reader
all_modules = map source tree to list of modules
all_modules = bash("find src/cleveragents -name '*.py' -type f | grep -v __pycache__ | sed 's#src/##' | sed 's#/#.#g' | sed 's#\.py$##' | sort", timeout=10000)
scanned_modules = set()
findings_total = 0
cycle = 0
+11 -2
View File
@@ -65,8 +65,17 @@ nox -e lint
### Step 2: If Lint Fails
1. **Read the lint output** carefully. Identify each error with its file
path, location, and error code/message.
1. **Read the lint output** carefully. Parse errors using this pattern:
```
filename:line:column: CODE message
Example: src/file.py:123:45: E501 line too long (92 > 88 characters)
```
Common codes:
- E501: Line too long
- F401: Imported but unused
- F841: Local variable assigned but never used
- E302/E303: Blank line issues
- W291/W293: Trailing whitespace
2. **Fix each error** by editing the source files.
3. **Re-run lint**:
```bash
+5 -1
View File
@@ -67,7 +67,11 @@ If PR number is provided and escalation context exists:
### Phase 2: Run Type Check
1. Execute `nox -e typecheck` in the working directory
2. Capture and parse the Pyright output
2. Parse Pyright output using pattern:
```
filename:line:column - error: message
Example: src/file.py:123:45 - error: Argument of type "str" cannot be assigned to parameter "count" of type "int"
```
3. Group errors by file and error type
### Phase 3: Analyze Type Errors