added git commit after task
Foundation checks / foundation (push) Successful in 2m48s

This commit is contained in:
wt committed 2026-10-14 18:06:35 +07:00
1 parent c1d4345316
commit 522311022f
12 files changed
+337 -22

No files matched your search

+31 -5
View File
@@ -17,6 +17,7 @@ import uuid
sys.path.insert(0, str(Path(__file__).resolve().parent))
from tasks import Tracker, atomic_write, now, read_json
from git_commits import TaskCommits
EXCLUDED_DIRS = {'.git', 'node_modules', '.venv', '__pycache__', '.state.lock', '.runner.lock', 'dist', 'coverage'}
GENERATED = {'planning/tasks.json', 'planning/TASK_MATRIX.md', 'planning/task_matrix.csv',
@@ -49,6 +50,7 @@ class Runner:
self.root = Path(root).resolve()
self.tracker = Tracker(self.root)
self.config = config
self.commits = TaskCommits(self.root, config.get('git_commits', True))
self.lock_path = self.root / '.runner.lock'
self.max_sessions = config.get('max_sessions', 1000)
self.timeout = config.get('timeout_seconds', 1800)
@@ -112,6 +114,10 @@ Read master_prompt and instructions below in order. Your scope is only this card
At a blocker: write one issue JSON, call tasks.py escalate, then EXIT immediately.
Do not solve the new remediation, do not unblock yourself, do not launch another agent.
After submit/approve/reject EXIT. The coordinator will launch the next fresh process.
Before submit/approve/reject write state/commit_messages/{agent}.txt with a concrete
English imperative subject (15..100 chars), blank line, Changes: and Validation:
describing only actual changes/checks. Never run git add/commit/reset/push yourself.
The coordinator commits each transition before launching another agent.
'''
paths = ['AGENTS.md', 'README.md', 'master_prompt.md', 'roles/common.md', f'roles/{item["role"]}.md',
'docs/WEB_PROFILE.md', 'docs/ARCHITECTURE.md', 'docs/CONTRACTS.md', 'docs/WORKFLOW.md', 'docs/AUTOMATION.md']
@@ -178,6 +184,7 @@ After submit/approve/reject EXIT. The coordinator will launch the next fresh pro
prompt_path = session / 'prompt.md'
# Validate command before claiming; missing local configuration is not a product task blocker.
argv = self.command(item, agent, prompt_path)
git_head = self.commits.preflight()
if item['action'] == 'claim':
self.tracker.mutate('claim', item['id'], agent, role=item['role'])
previous = dict(self.tracker.state()['tasks'][item['id']])
@@ -186,7 +193,9 @@ After submit/approve/reject EXIT. The coordinator will launch the next fresh pro
except (ValueError, OSError) as exc:
result = self.execution_issue(item, agent, 'Context preparation failed: ' + str(exc), previous=previous)
self.tracker.render()
return {'agent': agent, 'task': item['id'], 'action': item['action'], 'result': result}
record = {'agent': agent, 'task': item['id'], 'action': item['action'], 'result': result}
self.checkpoint(item, agent, result, git_head, record)
return record
atomic_write(prompt_path, prompt)
record = {'agent': agent, 'task': item['id'], 'role': item['role'], 'action': item['action'], 'started_at': now(), 'status': 'running'}
atomic_write(session / 'run.json', json.dumps(record, ensure_ascii=False, indent=2))
@@ -259,8 +268,21 @@ After submit/approve/reject EXIT. The coordinator will launch the next fresh pro
atomic_write(session / 'run.json', json.dumps(record, ensure_ascii=False, indent=2))
self.tracker.render()
self.tracker.validate()
if violations and self.commits.enabled:
record['commit_error'] = 'Scope violation: no automatic commit; inspect preserved changes'
else:
self.checkpoint(item, agent, result, git_head, record)
atomic_write(session / 'run.json', json.dumps(record, ensure_ascii=False, indent=2))
return record
def checkpoint(self, item, agent, result, git_head, record):
try:
record['commit'] = self.commits.commit(item, agent,
self.tracker.state()['tasks'][item['id']], result, git_head,
self.tracker.task(item['id'])['title'])
except (ValueError, OSError, subprocess.TimeoutExpired) as exc:
record['commit_error'] = str(exc)
def run(self, stage=None, dry_run=False):
if stage and stage not in {s['id'] for s in self.tracker.definitions()['stages']}:
raise ValueError('Unknown stage: ' + stage)
@@ -284,6 +306,8 @@ After submit/approve/reject EXIT. The coordinator will launch the next fresh pro
if not item:
return self.summary('idle', records)
records.append(self.launch(item))
if records[-1].get('commit_error'):
return self.summary('git_error', records)
if any(r.get('external_wait') for r in self.tracker.state()['tasks'].values()):
return self.summary('external', records)
return self.summary('session_limit', records)
@@ -292,10 +316,12 @@ After submit/approve/reject EXIT. The coordinator will launch the next fresh pro
state = self.tracker.state()
done = all(r['status'] == 'done' for r in state['tasks'].values())
external = {k: r.get('reason') for k, r in state['tasks'].items() if r.get('external_wait')}
return {'stop': 'complete' if done else 'needs_input' if external and stop in {'idle', 'external'} else stop,
return {'stop': 'git_error' if stop == 'git_error' else 'complete' if done else 'needs_input' if external and stop in {'idle', 'external'} else stop,
'launched': len(records), 'revision': state['revision'], 'external_blockers': external,
'unfinished': sum(r['status'] != 'done' for r in state['tasks'].values()),
'last_session': records[-1].get('agent') if records else None}
'last_session': records[-1].get('agent') if records else None,
'last_commit': records[-1].get('commit') if records else None,
'commit_error': records[-1].get('commit_error') if records else None}
def stop_process_tree(process):
@@ -349,8 +375,8 @@ def main():
config['max_sessions'] = args.max_sessions
result = Runner(root, config).run(stage=args.stage, dry_run=args.dry_run)
print(json.dumps(result, ensure_ascii=False, indent=2))
return 3 if result.get('stop') == 'needs_input' else 0
except (ValueError, OSError, KeyError) as exc:
return 2 if result.get('stop') == 'git_error' else 3 if result.get('stop') == 'needs_input' else 0
except (ValueError, OSError, KeyError, subprocess.TimeoutExpired) as exc:
print(json.dumps({'stop': 'configuration_or_workflow_error', 'reason': str(exc)}, ensure_ascii=False), file=sys.stderr)
return 2