Skip to content

Commit 7ee7ac4

Browse files
authored
Merge pull request #8 from BraveNewCapital/copilot/update-repo-architect-branch-names
feat: complete repo_architect methodology — lane scoping, enhanced validation, entrypoint_consolidation, 57 tests, campaign report
2 parents b7e4d15 + 30fc14e commit 7ee7ac4

File tree

4 files changed

+1691
-56
lines changed

4 files changed

+1691
-56
lines changed

.github/workflows/repo-architect.yml

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ on:
1212
- analyze
1313
- report
1414
- mutate
15+
- campaign
1516
github_model:
16-
description: 'GitHub Models model id'
17-
required: true
18-
default: 'openai/gpt-4.1'
17+
description: 'GitHub Models model id (overrides preferred model)'
18+
required: false
19+
default: ''
1920
type: string
2021
report_path:
2122
description: 'Primary report path'
@@ -31,6 +32,16 @@ on:
3132
- '1'
3233
- '2'
3334
- '3'
35+
max_slices:
36+
description: 'Campaign max slices (campaign mode only)'
37+
required: false
38+
default: '3'
39+
type: string
40+
lanes:
41+
description: 'Comma-separated lane order (mutate and campaign modes)'
42+
required: false
43+
default: 'parse_errors,import_cycles,entrypoint_consolidation,hygiene,report'
44+
type: string
3445
schedule:
3546
- cron: '17 * * * *'
3647

@@ -62,22 +73,38 @@ jobs:
6273
git config user.name "github-actions[bot]"
6374
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
6475
76+
- name: Ensure artifact directories exist
77+
run: |
78+
mkdir -p .agent docs/repo_architect
79+
6580
- name: Run repo architect
6681
env:
6782
GITHUB_TOKEN: ${{ github.token }}
6883
GITHUB_REPO: ${{ github.repository }}
6984
GITHUB_BASE_BRANCH: ${{ github.event.repository.default_branch }}
85+
REPO_ARCHITECT_BRANCH_SUFFIX: ${{ github.run_id }}-${{ github.run_attempt }}
86+
REPO_ARCHITECT_PREFERRED_MODEL: openai/gpt-5.4
87+
REPO_ARCHITECT_FALLBACK_MODEL: openai/gpt-4.1
7088
run: |
7189
MODE="${{ github.event.inputs.mode }}"
7290
MODEL="${{ github.event.inputs.github_model }}"
7391
REPORT_PATH="${{ github.event.inputs.report_path }}"
7492
MUTATION_BUDGET="${{ github.event.inputs.mutation_budget }}"
93+
MAX_SLICES="${{ github.event.inputs.max_slices }}"
94+
LANES="${{ github.event.inputs.lanes }}"
7595
if [ -z "$MODE" ]; then MODE="report"; fi
76-
if [ -z "$MODEL" ]; then MODEL="openai/gpt-4.1"; fi
7796
if [ -z "$REPORT_PATH" ]; then REPORT_PATH="docs/repo_architect/runtime_inventory.md"; fi
7897
if [ -z "$MUTATION_BUDGET" ]; then MUTATION_BUDGET="1"; fi
79-
export GITHUB_MODEL="$MODEL"
80-
python repo_architect.py --allow-dirty --mode "$MODE" --report-path "$REPORT_PATH" --mutation-budget "$MUTATION_BUDGET"
98+
if [ -z "$MAX_SLICES" ]; then MAX_SLICES="3"; fi
99+
if [ -z "$LANES" ]; then LANES="parse_errors,import_cycles,entrypoint_consolidation,hygiene,report"; fi
100+
EXTRA_ARGS=""
101+
if [ -n "$MODEL" ]; then EXTRA_ARGS="$EXTRA_ARGS --github-model $MODEL"; fi
102+
if [ "$MODE" = "campaign" ]; then
103+
EXTRA_ARGS="$EXTRA_ARGS --max-slices $MAX_SLICES --lanes $LANES"
104+
elif [ "$MODE" = "mutate" ]; then
105+
EXTRA_ARGS="$EXTRA_ARGS --lanes $LANES"
106+
fi
107+
python repo_architect.py --allow-dirty --mode "$MODE" --report-path "$REPORT_PATH" --mutation-budget "$MUTATION_BUDGET" $EXTRA_ARGS
81108
82109
- name: Upload repo architect artifacts
83110
if: always()
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# repo-architect Operator Guide
2+
3+
## Overview
4+
5+
`repo_architect.py` is a single-file autonomous agent that:
6+
- Analyses the Python codebase for parse errors, import cycles, entrypoints, and hygiene issues.
7+
- Generates or refreshes architecture documentation under `docs/repo_architect/`.
8+
- Creates mutation branches and pull-requests that fix real code issues.
9+
- Can run repeatedly without branch collisions or stale-branch failures.
10+
11+
---
12+
13+
## Model Selection (preferred / fallback)
14+
15+
The agent tries a **preferred** model first and automatically retries with a **fallback** model when the preferred one is unavailable or returns an unknown-model error.
16+
17+
| Priority | Source |
18+
|---|---|
19+
| 1 | `--preferred-model` CLI flag |
20+
| 2 | `REPO_ARCHITECT_PREFERRED_MODEL` env var |
21+
| 3 | Hard-coded default (`openai/gpt-5.4`) |
22+
23+
| Fallback priority | Source |
24+
|---|---|
25+
| 1 | `--fallback-model` CLI flag |
26+
| 2 | `REPO_ARCHITECT_FALLBACK_MODEL` env var |
27+
| 3 | Hard-coded default (`openai/gpt-4.1`) |
28+
29+
**Failure tolerance:** if both model calls fail (network, quota, unavailability), the run continues without model-generated output instead of aborting. The JSON result records `fallback_reason` and `fallback_occurred`.
30+
31+
The workflow exports:
32+
```yaml
33+
REPO_ARCHITECT_PREFERRED_MODEL: openai/gpt-5.4
34+
REPO_ARCHITECT_FALLBACK_MODEL: openai/gpt-4.1
35+
```
36+
37+
---
38+
39+
## Unique Branch Strategy
40+
41+
Every mutation branch is suffixed with a per-run unique token to prevent branch-push collisions.
42+
43+
**Suffix precedence:**
44+
1. `REPO_ARCHITECT_BRANCH_SUFFIX` env var (if set and non-empty)
45+
2. `GITHUB_RUN_ID-GITHUB_RUN_ATTEMPT` (when both are present)
46+
3. UTC timestamp fallback (`YYYYmmddHHMMSS`)
47+
48+
The suffix is sanitised to `[A-Za-z0-9._-]` only.
49+
50+
**Push collision recovery:** Before pushing, the agent checks whether the remote branch already exists. If it does, a fresh unique branch name is generated. A single retry is also performed if the push is rejected due to a non-fast-forward error.
51+
52+
The workflow exports:
53+
```yaml
54+
REPO_ARCHITECT_BRANCH_SUFFIX: ${{ github.run_id }}-${{ github.run_attempt }}
55+
```
56+
57+
---
58+
59+
## Lane Priority Rules
60+
61+
In `mutate` and `campaign` modes the agent selects exactly one lane per slice, in this priority order:
62+
63+
| Priority | Lane | Behaviour |
64+
|---|---|---|
65+
| 1 | `parse_errors` | Model-assisted syntax fix for one or more files with parse errors. Skipped if no parse errors exist or model is unavailable. |
66+
| 2 | `import_cycles` | Model-assisted import cycle break (TYPE_CHECKING guard / lazy import). Skipped if no cycles or model unavailable. |
67+
| 3 | `entrypoint_consolidation` | Annotates one redundant backend server entrypoint with a `# DEPRECATED` comment when ≥ 4 backend entrypoints exist. Model-assisted. |
68+
| 4 | `hygiene` | Remove explicitly `# DEBUG`-marked `print()` statements. No model required. |
69+
| 5 | `report` | Refresh the architecture documentation packet. Only selected when no higher-priority code mutation is possible. |
70+
71+
A **report-only mutation is never produced when parse errors exist** unless all code-lane attempts fail (in which case `no_safe_code_mutation_reason` is populated in the JSON output).
72+
73+
### Scoping lanes in mutate mode
74+
75+
You can restrict which lanes a mutate run considers via `--lanes` or `--lane`:
76+
77+
```bash
78+
# Only try hygiene lane
79+
python repo_architect.py --mode mutate --lane hygiene --allow-dirty
80+
81+
# Only try parse_errors and import_cycles
82+
python repo_architect.py --mode mutate --lanes parse_errors,import_cycles --allow-dirty
83+
```
84+
85+
This also works via environment variables: `REPO_ARCHITECT_LANES` or `REPO_ARCHITECT_LANE`.
86+
87+
The workflow passes `--lanes` to both mutate and campaign modes, so the `lanes` workflow_dispatch input controls lane selection in every mutation-capable mode.
88+
89+
---
90+
91+
## Campaign Mode
92+
93+
Campaign mode executes up to `--max-slices` mutation slices across the lane priority order, re-analysing between each slice so later lanes see the updated repository state.
94+
95+
```bash
96+
python repo_architect.py \
97+
--mode campaign \
98+
--allow-dirty \
99+
--max-slices 3 \
100+
--lanes parse_errors,import_cycles,entrypoint_consolidation,hygiene,report \
101+
--stop-on-failure
102+
```
103+
104+
**CLI flags:**
105+
106+
| Flag | Default | Description |
107+
|---|---|---|
108+
| `--max-slices` | `3` | Maximum slices to attempt |
109+
| `--lanes` | all 5 lanes | Comma-separated lane order (also works in mutate mode) |
110+
| `--stop-on-failure` | `false` | Abort remaining slices on first failure |
111+
| `--preferred-model` | (env / default) | Override preferred model for this run |
112+
| `--fallback-model` | (env / default) | Override fallback model for this run |
113+
114+
Campaign results are emitted as `status: campaign_complete` JSON and also saved to `.agent/campaign_summary.json`.
115+
116+
---
117+
118+
## Output Contract (machine-readable JSON)
119+
120+
Every run emits a JSON object on stdout. Key fields:
121+
122+
| Field | Description |
123+
|---|---|
124+
| `status` | `analyzed`, `analysis_only`, `mutated`, `no_meaningful_delta`, `no_safe_mutation_available`, `campaign_complete` |
125+
| `mode` | Active mode |
126+
| `lane` | Selected mutation lane (`none` if no mutation) |
127+
| `lanes_active` | List of lanes considered (from `--lanes` / env or default lane order) |
128+
| `architecture_score` | 1–100 composite health score |
129+
| `requested_model` | Model that was requested |
130+
| `actual_model` | Model that actually responded (may differ if fallback occurred) |
131+
| `fallback_reason` | Reason fallback was triggered (or `null`) |
132+
| `fallback_occurred` | `true` if the fallback model was used |
133+
| `no_safe_code_mutation_reason` | Explanation when no code-level mutation was safe |
134+
| `branch` | Git branch name pushed (or `null`) |
135+
| `changed_files` | List of files modified |
136+
| `validation` | Output from `py_compile` or equivalent validation |
137+
| `artifact_files` | All artifact paths generated in this run |
138+
| `pull_request_url` | PR URL if created |
139+
140+
---
141+
142+
## Workflow Dispatch Modes
143+
144+
| Mode | Effect |
145+
|---|---|
146+
| `analyze` | Builds analysis, writes `.agent/` artifacts, no branch or PR |
147+
| `report` | Refreshes `docs/repo_architect/` documentation |
148+
| `mutate` | Attempts one code or report mutation in lane-priority order |
149+
| `campaign` | Runs up to `max_slices` slices serially across lanes |
150+
151+
---
152+
153+
## Validation Policy
154+
155+
Each mutation lane runs the following validation before pushing:
156+
157+
| Lane | Validation |
158+
|---|---|
159+
| `parse_errors` | `ast.parse` on model-generated content; file must parse cleanly or it is rejected |
160+
| `import_cycles` | `ast.parse` on model-generated content; import smoke test attempted (warn-only) |
161+
| `entrypoint_consolidation` | `ast.parse` on model-generated content |
162+
| `hygiene` | `python -m py_compile` on all touched Python files |
163+
| `report` | Verify report files were written; hash included in output |
164+
165+
Validation failures abort the mutation and **never push a broken branch**.
166+
167+
---
168+
169+
## Running Tests
170+
171+
```bash
172+
python -m unittest tests.test_repo_architect -v
173+
```
174+
175+
The test suite covers: branch suffix generation, model fallback, `ast.parse` gate, campaign aggregation, output schema stability, lane priority selection, `entrypoint_consolidation` threshold/validation, lane scoping in mutate mode (`--lane`, `--lanes`, env vars), and enhanced validation (import smoke for import_cycles lane).

0 commit comments

Comments
 (0)