Files
jobtrackingapp/automation/prepare_queue.py
T

171 lines
11 KiB
Python

"""Import verified original prompt bytes into a NEW queue; never import into a live control plane."""
import argparse
import hashlib
import json
from pathlib import Path
import re
import shutil
def sha(path):
return hashlib.sha256(path.read_bytes()).hexdigest()
def prepare(source, output, project):
if (output / 'backlog.json').exists():
raise SystemExit('Queue already exists; preserve it and prepare changes in a new directory for owner review')
output.mkdir(parents=True, exist_ok=True)
inventory = []
for path in sorted(source.rglob('*')):
if path.is_file() and path.suffix.lower() in ('.md', '.txt'):
inventory.append(dict(filename=str(path.relative_to(source)), bytes=path.stat().st_size, sha256=sha(path)))
def find(pattern):
matches = [p for p in source.rglob('*') if p.is_file() and re.search(pattern, p.name, re.I)]
if len(matches) > 1:
raise SystemExit('Ambiguous prompt match: ' + pattern)
return matches[0] if matches else None
def snapshot(path):
target = output / 'prompts' / path.relative_to(source)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(path, target)
return target.relative_to(output).as_posix(), sha(target)
def check(name, argv, report=None, cwd='.', configured=True):
return dict(id=name, argv=argv, cwd=cwd, report=report, configured=configured)
core_checks = [
check('backend', ['dotnet','test','JobTrackerApi.Tests/JobTrackerApi.Tests.csproj','--logger',
'trx;LogFileName=backend.trx','--results-directory','.artifacts/orchestrator/backend'],
dict(format='trx',path='.artifacts/orchestrator/backend/backend.trx',minimumPassed=1)),
check('frontend', ['cmd','/d','/c','npm','test','--','--runInBand','--json',
'--outputFile=../.artifacts/orchestrator/frontend.json'],
dict(format='jest',path='.artifacts/orchestrator/frontend.json',minimumPassed=1), 'job-tracker-ui'),
check('types', ['cmd','/d','/c','npm','run','typecheck'], cwd='job-tracker-ui'),
check('lint', ['cmd','/d','/c','npm','run','lint'], cwd='job-tracker-ui'),
check('build', ['cmd','/d','/c','npm','run','build'], cwd='job-tracker-ui'),
check('qualified-provider-browser-python-pdf-security-preservation',
['pwsh','-NoProfile','-File','automation/qualification/Run-StageQualification.ps1'],
dict(format='junit',path='.artifacts/orchestrator/qualification.xml',minimumPassed=1),
configured=False)
]
# The composite adapter is intentionally unconfigured: a local sandbox cannot invent hosted,
# provider, PDF, preservation, AI or human qualification. Owner installs a concrete trusted plan.
context = ['docs/pre-phase10-ux/ACCEPTANCE.md','docs/pre-phase10-ux/CV_QA_HANDOFF.md',
'docs/cv-integration-qa/PHASE_10_HANDOFF.md','docs/redesign/IMPLEMENTATION_PLAN.md']
definitions = [
('PHASE-10',5,r'^Prompt 5\b','feature/phase10-cleanup',
['phase10-development-entry'], ['P10-00','P10-01','P10-02','P10-03','P10-04','P10-05']),
('PRODUCT-REVIEW-01',6,r'^Prompt 6\b','planning/product-review-01',
[], ['independent-assessment','ranked-roadmap']),
('INT-GLOBAL-JOBS-01',7,r'^Prompt 7\b','feature/int-global-jobs-01',
['product-ranked-scope','global-jobs-implementation'], ['GJ-01','GJ-02','GJ-03','GJ-04','GJ-05','GJ-06','GJ-07','GJ-08']),
('I18N-01',8,r'^I18N-01\b','feature/i18n-01',
['i18n-scope'], ['locale-inventory','translation-implementation','locale-qualification']),
('LAUNCH-SITE-01',9,r'^Prompt 10\b','feature/launch-site-redesign',
[], ['homepage-research','four-designs','approved-design-implementation','homepage-qualification']),
('SECURITY-AUDIT-01',10,r'^Prompt 8\b','test/security-audit-01',
['security-test-environment-and-scope'], ['threat-model','adversarial-testing','remediation','security-retest']),
('RELEASE-READINESS-01',11,r'^prompt 11\b','test/release-readiness-01',
[], ['release-inventory','qualification','release-recommendation'])
]
stages = []
for workstream, sequence, pattern, branch, approvals, checkpoints in definitions:
if workstream == 'I18N-01':
original = source / 'I18N/00_MASTER_ORCHESTRATOR.md'
original = original if original.is_file() else None
checkpoints = [f'I18N-{n:02d}' for n in range(9)]
elif workstream == 'SECURITY-AUDIT-01':
original = source / 'jobjakt-security/MASTER_PROMPT.md'
original = original if original.is_file() else None
checkpoints = [f'SEC-{n:02d}' for n in range(17)]
else:
original = find(pattern)
filename, prompt_hash = snapshot(original) if original else (f'prompts/MISSING-{workstream}.txt', '')
source_status = 'MISSING' if not original else ('EMPTY' if original.stat().st_size == 0 else 'VERIFIED')
stage = dict(id=workstream,sequence=sequence,sourcePrompt=filename,promptSha256=prompt_hash,
sourceFilename=str(original) if original else None,sourceStatus=source_status,
dependencies=[stages[-1]['id']] if stages else [],branch=branch,
requiredOwnerApprovals=approvals,retryPolicy=dict(maxAttempts=3,maxSessions=30,backoffSeconds=30),
status='PENDING',evidence=[],reports=[],contextFiles=context,checkpoints=[],supplementalPrompts=[])
for checkpoint in checkpoints:
after = []
if workstream == 'PRODUCT-REVIEW-01' and checkpoint == 'ranked-roadmap':
after = ['product-ranked-scope']
if workstream == 'I18N-01' and checkpoint == 'I18N-08':
after = ['human-linguistic-and-market-qualification']
if checkpoint == 'four-designs':
after = ['homepage-design-selection']
if checkpoint == 'release-recommendation':
after = ['release-evidence-review'] # review only; never deployment approval
before = []
if checkpoint == 'GJ-04':
before = ['provider-licences-and-display-rights']
if checkpoint == 'SEC-13':
before = ['isolated-adversarial-targets']
if checkpoint == 'SEC-15':
after = ['material-security-findings-disposition']
artifacts = [f'docs/automation/stages/{workstream}/{checkpoint}.md']
if checkpoint == 'four-designs':
artifacts += [f'docs/automation/stages/{workstream}/concept-{letter}.png' for letter in 'ABCD']
stage['checkpoints'].append(dict(id=checkpoint,
instruction=f'Complete only {checkpoint} within {workstream}, preserving every full source requirement. '
'Read prior evidence. Stop for any source-required owner decision. No production actions.',
approvalsBefore=before,approvalsAfter=after,deferrableGates=[],requiredArtifacts=artifacts,
checks=core_checks,evidenceRequirements=[
'Process exits and exact structured suite membership; no silent skips/retries',
'Preservation IDs, canonical owners, old-data and document compatibility',
'Separate local/provider/hosted/human/AI/production decisions',
'All source-prompt deliverables and stage-specific acceptance criteria; independent review required']))
if workstream == 'INT-GLOBAL-JOBS-01':
pack = project / 'docs/planning/global-jobs-prompts'
if pack.is_dir():
for path in sorted(pack.glob('*.md')):
target = output / 'prompts/global-jobs-pack' / path.name
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(path, target)
stage['supplementalPrompts'].append(dict(path=target.relative_to(output).as_posix(),sha256=sha(target)))
match = re.search(r'GJ_(\d\d)_', path.name)
if match:
stage['supplementalPrompts'][-1]['checkpoints'] = ['GJ-' + match[1]]
planning = project / 'docs/global-jobs/00'
if planning.is_dir():
for path in sorted(planning.glob('*.md')):
target = output / 'prompts/global-jobs-planning' / path.name
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copyfile(path,target)
stage['supplementalPrompts'].append(dict(path=target.relative_to(output).as_posix(),sha256=sha(target)))
if workstream in ('I18N-01','SECURITY-AUDIT-01'):
pack = source / ('I18N' if workstream == 'I18N-01' else 'jobjakt-security')
for path in sorted(pack.rglob('*')):
if path.is_file():
saved_path, saved_hash = snapshot(path)
if saved_path != filename:
stage['supplementalPrompts'].append(dict(path=saved_path,sha256=saved_hash))
match = re.search(r'I18N_(\d\d)_', path.name) if workstream == 'I18N-01' else re.match(r'(\d\d)_',path.name)
if match:
stage['supplementalPrompts'][-1]['checkpoints'] = [('I18N-' if workstream == 'I18N-01' else 'SEC-') + match[1]]
elif path.name == 'ALL_PHASE_PROMPTS.md':
stage['supplementalPrompts'][-1]['checkpoints'] = [] # preserved duplicate compilation, individual full prompts drive sessions
# Qualitative source requirements cannot be accepted by the implementation model itself.
stage['checkpoints'][-1]['approvalsAfter'].append('stage-requirements-review')
stages.append(stage)
# Preserve UI subprompts without falsely classifying them as the missing I18N contract.
for path in source.glob('prompt 9 UI/*.txt'):
snapshot(path)
manifest = dict(schemaVersion=1,executionEnabled=False,minimumCodexVersion='0.163.0',
baselineCommit='',baselineBranch='test/pre-phase10-ux-acceptance',
remote='origin',integrationBranch='codex/roadmap-integration',stages=stages)
# Resolve the requested local commit instead of guessing its expansion.
import subprocess
manifest['baselineCommit'] = subprocess.check_output(['git','-C',str(project),'rev-parse','5032ccb^{commit}'],text=True).strip()
(output / 'backlog.json').write_text(json.dumps(manifest,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
(output / 'SOURCE_INVENTORY.json').write_text(json.dumps(inventory,ensure_ascii=False,indent=2)+'\n',encoding='utf-8')
print(f'Prepared {len(stages)} ordered stages; execution disabled. Original source bytes preserved.')
if __name__ == '__main__':
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--source',type=Path,required=True)
parser.add_argument('--output',type=Path,required=True)
parser.add_argument('--project',type=Path,required=True)
args = parser.parse_args()
prepare(args.source,args.output,args.project)