File size: 10,684 Bytes
b67a1e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | #!/usr/bin/env python3
from __future__ import annotations
import json
from datetime import datetime, timezone
from pathlib import Path
ROOT = Path('/home/smlflg/Projekte/AgentProjectMinenraeumer/ProjectIntakeTower')
PENDING = ROOT / 'jobs' / 'pending'
DRAFTS = ROOT / 'github' / 'issue_drafts'
TRIAGE = ROOT / 'triage'
PENDING.mkdir(parents=True, exist_ok=True)
DRAFTS.mkdir(parents=True, exist_ok=True)
now = datetime.now(timezone.utc).replace(microsecond=0).isoformat()
common_forbidden = [
'modify source project files unless Samuel explicitly changes this contract',
'delete/archive/move source projects',
'commit', 'push', 'merge',
'read or print secrets',
'create GitHub issues',
'run dependency installs that write into source projects',
]
def job(job_id, project_name, project_path, task_type, objective, extra_allowed, expected, verifier_commands, inputs=None, stop_extra=None, allowed_files_extra=None, mode='review_only'):
outdir = f'jobs/done/{job_id}'
return {
'job_id': job_id,
'project_name': project_name,
'project_path': project_path,
'mode': mode,
'task_type': task_type,
'objective': objective,
'allowed_actions': [
'read non-secret files in allowed source paths',
'read ProjectIntakeTower inventory/evidence/triage files',
'write artifacts only inside ProjectIntakeTower/jobs/done/<job_id>/',
] + extra_allowed,
'forbidden_actions': common_forbidden,
'allowed_files': [
f'{project_path}/** read-only, excluding secrets and dependency/build/cache dirs',
f'{ROOT}/inventory/** read-only',
f'{ROOT}/triage/** read-only',
f'{ROOT}/{outdir}/** write',
] + (allowed_files_extra or []),
'expected_artifacts': [f'{outdir}/{x}' for x in expected],
'required_artifacts': [f'{outdir}/{x}' for x in expected],
'verifier_commands': verifier_commands,
'verifier': {'type': 'command', 'command': verifier_commands[0] if verifier_commands else 'python3 verifier/verify_artifacts.py'},
'stop_conditions': [
'credentials required',
'write outside ProjectIntakeTower needed',
'destructive action needed',
'project ownership or intent unclear',
'secret-like file must be opened to proceed',
] + (stop_extra or []),
'scope': {
'allowed_paths': [project_path, str(ROOT)],
'allowed_tools': ['read_file', 'search_files', 'terminal', 'write_file'],
'allowed_actions': ['read-only inspection', 'write handoff artifacts under ProjectIntakeTower/jobs/done'],
'forbidden_actions': ['modify source project', 'commit', 'push', 'merge', 'delete', 'read secrets', 'create issues'],
},
'inputs': inputs or {},
'timeout_minutes': 45,
'max_iterations': 2,
'if_blocked': 'write status=needs_human in result.json and stop',
'rejoin_policy': 'summarize status, artifact_paths, verifier result, needs_human, next_action',
'created_by': 'operator_intake_command',
'created_at': now,
}
jobs = [
job(
'intake-audit-komandozentral', 'KomandoZentral', '/home/smlflg/Projekte/KomandoZentral', 'audit',
'Audit KomandoZentral read-only and produce an owner-facing audit: purpose, state, risks, verifier status, and one next action.',
['run non-destructive metadata/test discovery commands only if they do not mutate the repo'],
['result.json', 'audit.md'],
['python3 verifier/verify_artifacts.py --job jobs/pending/intake-audit-komandozentral.json --require-done'],
{'project_card': 'inventory/project_cards/komandozentral-46af91fa1c.md', 'evidence_file': 'inventory/evidence/komandozentral-46af91fa1c.json'}
),
job(
'intake-generate-tests-werbinich', 'WerBinIch', '/home/smlflg/Projekte/WerBinIch', 'test_design',
'Generate a test plan and ready-to-apply test patch for WerBinIch without editing the source repo.',
['inspect existing code/tests read-only', 'write proposed test files as patch/diff artifacts under ProjectIntakeTower only'],
['result.json', 'test_plan.md', 'proposed_tests.patch'],
['python3 verifier/verify_artifacts.py --job jobs/pending/intake-generate-tests-werbinich.json --require-done'],
{'project_card': 'inventory/project_cards/werbinich-4b2f31741d.md', 'evidence_file': 'inventory/evidence/werbinich-4b2f31741d.json'},
stop_extra=['source repo test creation requires owner approval before applying patch']
),
job(
'intake-consolidate-agent-control-tower-repos', 'Agent Control Tower repos', '/home/smlflg/Projekte', 'consolidation_plan',
'Consolidate the Agent Control Tower family conceptually: map overlaps, pick system of record candidates, and propose merge/archive sequence. No repo edits.',
['compare related project cards/evidence', 'produce consolidation matrix and recommended owner decision tree'],
['result.json', 'consolidation_matrix.md', 'owner_decision_tree.md'],
['python3 verifier/verify_artifacts.py --job jobs/pending/intake-consolidate-agent-control-tower-repos.json --require-done'],
{
'candidate_projects': [
'Zentrale /home/smlflg/Projekte/Zentrale',
'KomandoZentral /home/smlflg/Projekte/KomandoZentral',
'ACT-agentcommandotower /home/smlflg/Projekte/ACT-agentcommandotower',
'AIOS-Cockpit /home/smlflg/Projekte/AIOS-Cockpit',
'MultiHarnessStudio /home/smlflg/Projekte/MultiHarnessStudio',
'FusionKomandoZentraleZentrale /home/smlflg/Projekte/FusionKomandoZentraleZentrale'
]
},
allowed_files_extra=[
'/home/smlflg/Projekte/Zentrale/** read-only, excluding secrets and dependency/build/cache dirs',
'/home/smlflg/Projekte/ACT-agentcommandotower/** read-only, excluding secrets and dependency/build/cache dirs',
'/home/smlflg/Projekte/AIOS-Cockpit/** read-only, excluding secrets and dependency/build/cache dirs',
'/home/smlflg/Projekte/MultiHarnessStudio/** read-only, excluding secrets and dependency/build/cache dirs',
'/home/smlflg/Projekte/FusionKomandoZentraleZentrale/** read-only, excluding secrets and dependency/build/cache dirs',
]
),
job(
'intake-archive-dead-prototype-xyz', 'XYZ prototype candidate', '/home/smlflg/Projekte', 'archive_review',
'Find the intended dead prototype XYZ candidate from inventory evidence and produce an archive recommendation. Do not move/delete/archive anything.',
['search inventory/project cards for XYZ-like candidates', 'write archive recommendation only'],
['result.json', 'archive_review.md'],
['python3 verifier/verify_artifacts.py --job jobs/pending/intake-archive-dead-prototype-xyz.json --require-done'],
{'known_status': 'No exact project named XYZ found in current inventory search; human confirmation required before any archive action.'},
stop_extra=['exact XYZ target cannot be identified', 'archive/delete/move requested']
),
job(
'intake-build-verifier-hermes-profile-health', 'Hermes profile health', '/home/smlflg/.hermes/profiles', 'verify',
'Run and improve the ProjectIntakeTower Hermes profile health verifier; summarize unhealthy profiles and safe next actions.',
['run verifier/verify_hermes_profile_health.py', 'inspect report only; do not read .env/auth/token files'],
['result.json', 'hermes_profile_health_summary.md'],
['python3 verifier/verify_artifacts.py --job jobs/pending/intake-build-verifier-hermes-profile-health.json --require-done'],
{'verifier_script': 'verifier/verify_hermes_profile_health.py', 'report': 'verifier/hermes_profile_health_report.json'},
allowed_files_extra=[f'{ROOT}/verifier/verify_hermes_profile_health.py read/write', f'{ROOT}/verifier/hermes_profile_health_report.json read/write']
),
]
for j in jobs:
(PENDING / f"{j['job_id']}.json").write_text(json.dumps(j, ensure_ascii=False, indent=2), encoding='utf-8')
draft = [
f"# [ProjectIntakeTower] {j['objective']}", '',
'## Intake command', '',
f"- Job: `{j['job_id']}`",
f"- Project: `{j['project_name']}`",
f"- Path/scope: `{j['project_path']}`",
f"- Mode: `{j['mode']}`",
'', '## Safety', '',
'- Review-only by default.',
'- No source project modifications.',
'- No secrets, commits, pushes, deletes, or automatic issue creation.',
'', '## Expected artifacts', '',
]
for a in j['expected_artifacts']:
draft.append(f'- `{a}`')
draft += ['', '## Verifier', '', f"`{j['verifier']['command']}`", '']
(DRAFTS / f"{j['job_id']}.md").write_text('\n'.join(draft), encoding='utf-8')
intake_md = TRIAGE / 'INTAKE_COMMANDS.md'
intake_lines = ['# Intake Commands', '', f'Generated: {now}', '', '| Command | Job | Status | Human gate |', '|---|---|---|---|']
rows = [
('Audit KommandoZentral', 'intake-audit-komandozentral', 'ready', 'no destructive action'),
('Generate tests for WerBinIch', 'intake-generate-tests-werbinich', 'ready as patch artifact', 'apply patch requires approval'),
('Consolidate Agent Control Tower repos', 'intake-consolidate-agent-control-tower-repos', 'ready', 'merge/archive decisions require approval'),
('Archive dead prototype XYZ', 'intake-archive-dead-prototype-xyz', 'needs_human target ambiguous', 'yes: exact XYZ target not found'),
('Build verifier for Hermes profile health', 'intake-build-verifier-hermes-profile-health', 'verifier script created; job ready', 'profile fixes require approval'),
]
for cmd, jid, status, gate in rows:
intake_lines.append(f'| {cmd} | `{jid}` | {status} | {gate} |')
intake_md.write_text('\n'.join(intake_lines)+'\n', encoding='utf-8')
owner = ROOT / 'OWNER_DIGEST.md'
old = owner.read_text(encoding='utf-8') if owner.exists() else '# OWNER DIGEST\n'
section = '\n## Explicit intake commands queued\n\n' + '\n'.join(f'- {cmd}: `jobs/pending/{jid}.json` — {status}' for cmd, jid, status, gate in rows) + '\n'
if '## Explicit intake commands queued' in old:
old = old.split('## Explicit intake commands queued')[0].rstrip() + section
else:
old = old.rstrip() + '\n' + section
owner.write_text(old, encoding='utf-8')
print(json.dumps({'status':'succeeded','jobs_written':len(jobs),'drafts_written':len(jobs),'intake_commands':str(intake_md.relative_to(ROOT))}, indent=2))
|