feat(automation): enforce assessment scope and document activation blockers
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
"""Read-only verification of a trusted, source-bound assessment receipt; no services."""
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def verify(repository, receipt_path, expected_sha256):
|
||||
repository = Path(repository).resolve()
|
||||
raw = Path(receipt_path).read_bytes()
|
||||
if len(raw) > 8 * 1024 * 1024 or hashlib.sha256(raw).hexdigest() != expected_sha256:
|
||||
raise ValueError('Assessment receipt differs from the protected manifest pin')
|
||||
receipt = json.loads(raw)
|
||||
if (receipt.get('schemaVersion') != 1 or receipt.get('scope') != 'P10-00-ASSESSMENT-ONLY'
|
||||
or receipt.get('integrity') != 'PASS' or not receipt.get('files')
|
||||
or not receipt.get('nativeReports') or not receipt.get('retainedDecisions')):
|
||||
raise ValueError('Incomplete assessment qualification receipt')
|
||||
for name, expected in receipt['files'].items():
|
||||
path = (repository / name).resolve()
|
||||
if not path.is_relative_to(repository) or not path.is_file():
|
||||
raise ValueError('Frozen assessment source missing or outside repository: ' + name)
|
||||
if hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
||||
raise ValueError('Frozen assessment source changed; fresh affected qualification required: ' + name)
|
||||
evidence_root = Path(receipt_path).resolve().parent
|
||||
for name, expected in receipt.get('evidenceFiles', {}).items():
|
||||
path = (evidence_root / name).resolve()
|
||||
if not path.is_relative_to(evidence_root) or not path.is_file() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
|
||||
raise ValueError('Accepted handoff evidence changed or missing: ' + name)
|
||||
return dict(kind='REUSED_SOURCE_BOUND_EVIDENCE', receiptSha256=expected_sha256,
|
||||
nativeReports=receipt['nativeReports'],
|
||||
retainedDecisions=receipt['retainedDecisions'],
|
||||
limits='P10-00 assessment only. No fresh product runs or implementation qualification.')
|
||||
@@ -19,6 +19,7 @@ import time
|
||||
import uuid
|
||||
import xml.etree.ElementTree as ET
|
||||
import local_mode
|
||||
import assessment_adapter
|
||||
|
||||
|
||||
class GateError(RuntimeError):
|
||||
@@ -165,6 +166,13 @@ def validate_manifest(m, root=None):
|
||||
if check['id'] in check_ids or not check.get('argv'):
|
||||
raise GateError('Duplicate check or empty test command')
|
||||
check_ids.add(check['id'])
|
||||
if check.get('adapter') == 'assessment-evidence-inspection':
|
||||
if not m.get('executionScope') or not re.fullmatch(r'[0-9a-f]{64}',check.get('receiptSha256','')):
|
||||
raise GateError('Assessment adapter requires a restricted policy and pinned receipt')
|
||||
if check.get('configured') is not False:
|
||||
raise GateError('Evidence inspection cannot qualify the command-running composite adapter')
|
||||
if root and sha_file(under(root,check['receipt'])) != check['receiptSha256']:
|
||||
raise GateError('Assessment qualification receipt changed')
|
||||
if not all(isinstance(a, str) and '\x00' not in a for a in check['argv']):
|
||||
raise GateError('Command arguments must be strings')
|
||||
if check.get('report') and check['report']['format'] not in ('trx', 'jest', 'playwright', 'junit'):
|
||||
@@ -180,6 +188,17 @@ def validate_manifest(m, root=None):
|
||||
raise GateError('Supplemental prompt missing/changed')
|
||||
if sequences != sorted(set(sequences)):
|
||||
raise GateError('Queue sequence must be unique and ascending')
|
||||
scope = m.get('executionScope')
|
||||
if scope:
|
||||
stage = next((s for s in m['stages'] if s['id'] == scope.get('stage')), None)
|
||||
cp = next((x for x in stage['checkpoints'] if x['id'] == scope.get('checkpoint')), None) if stage else None
|
||||
if (scope.get('mode') != 'assessment-only' or scope.get('stage') != 'PHASE-10'
|
||||
or scope.get('checkpoint') != 'P10-00' or not cp
|
||||
or scope.get('reportDirectory') != 'docs/automation/stages/PHASE-10'
|
||||
or 'candidate-specific-cleanup-scope' not in cp['approvalsAfter']):
|
||||
raise GateError('Assessment policy requires an exact checkpoint and protected stop')
|
||||
if not re.fullmatch(r'[0-9a-f]{40}', scope.get('handoffCommit','')) or not SAFE_BRANCH.fullmatch(scope.get('handoffBranch','')):
|
||||
raise GateError('Invalid published handoff identity')
|
||||
|
||||
|
||||
class StateStore:
|
||||
@@ -553,6 +572,8 @@ class ProcessRunner:
|
||||
return data
|
||||
|
||||
def run_check(self, engine, check, directory):
|
||||
if check.get('adapter') == 'assessment-evidence-inspection':
|
||||
raise GateError('Evidence inspection cannot replace command-running composite qualification')
|
||||
if local_mode.single(engine.config):
|
||||
local_mode.require_unattended(engine.root,engine.config)
|
||||
working = engine.repo if check['cwd'] == '.' else under(engine.repo, check['cwd'])
|
||||
@@ -581,6 +602,9 @@ class Engine:
|
||||
self.root = Path(root).resolve()
|
||||
self.manifest = manifest
|
||||
self.config = config
|
||||
if manifest.get('executionScope'):
|
||||
self.config = dict(config)
|
||||
self.config['assessmentReportDirectory'] = manifest['executionScope']['reportDirectory']
|
||||
self.repo = Path(config['repository']).resolve()
|
||||
self.store = StateStore(self.root)
|
||||
self.runner = runner or ProcessRunner()
|
||||
@@ -621,6 +645,8 @@ class Engine:
|
||||
if kind in ('environment-qualified', 'roadmap-execution'):
|
||||
request.update(stage='QUEUE', checkpoint='POLICY', baselineCommit=self.manifest['baselineCommit'],
|
||||
candidateCommit=None, evidence=[], detail='Entire exact manifest; development only; no production authority')
|
||||
if self.manifest.get('executionScope'):
|
||||
request['executionScope'] = self.manifest['executionScope']
|
||||
path = self.root / 'requests' / (digest(request) + '.json')
|
||||
atomic(path, request)
|
||||
approval = self.approval_verifier(request)
|
||||
@@ -656,7 +682,7 @@ class Engine:
|
||||
return True
|
||||
|
||||
def policy_hash(self):
|
||||
files = ['controller.py', 'local_mode.py', 'Probe-JobjaktSandbox.ps1',
|
||||
files = ['controller.py', 'local_mode.py', 'assessment_adapter.py', 'Probe-JobjaktSandbox.ps1',
|
||||
'Approve-JobjaktBacklog.ps1', 'config.json', 'schemas/result.schema.json']
|
||||
return digest({name: sha_file(self.root / name) for name in files if (self.root / name).exists()})
|
||||
|
||||
@@ -683,6 +709,8 @@ class Engine:
|
||||
_,sid = local_mode.detect_identity()
|
||||
local_mode.validate_identity(self.config,sid)
|
||||
local_mode.require_unattended(self.root,self.config)
|
||||
if self.manifest.get('executionScope') and not local_mode.single(self.config):
|
||||
raise GateError('Restricted assessment filesystem profile currently qualifies single-user-local only')
|
||||
certificate = under(self.root, self.config['publicKeyCertificate'])
|
||||
if not certificate.is_file() or sha_file(certificate) != self.config['ownerCertificateSha256']:
|
||||
raise GateError('Owner public certificate is missing or differs from protected configuration pin')
|
||||
@@ -800,9 +828,17 @@ class Engine:
|
||||
if ss['entryBaseline'] is None:
|
||||
if self.git('status', '--porcelain', '--untracked-files=all'):
|
||||
raise GateError('New stage requires a clean worktree, including untracked files')
|
||||
self.verify_remote(self.manifest['baselineBranch'] if not any(
|
||||
x['status'] == 'COMPLETED' for x in state['stages'].values())
|
||||
else self.manifest['integrationBranch'], state['integrationCommit'])
|
||||
scope = self.manifest.get('executionScope')
|
||||
if scope and not any(x['status'] == 'COMPLETED' for x in state['stages'].values()):
|
||||
self.verify_remote(scope['handoffBranch'], scope['handoffCommit'])
|
||||
self.git('merge-base', '--is-ancestor', state['integrationCommit'], scope['handoffCommit'])
|
||||
changed = self.git('diff','--name-only',state['integrationCommit'],scope['handoffCommit']).splitlines()
|
||||
if any(not name.startswith('docs/automation/handoffs/pre-phase10/') for name in changed):
|
||||
raise GateError('Published handoff changed outside the evidence envelope')
|
||||
else:
|
||||
self.verify_remote(self.manifest['baselineBranch'] if not any(
|
||||
x['status'] == 'COMPLETED' for x in state['stages'].values())
|
||||
else self.manifest['integrationBranch'], state['integrationCommit'])
|
||||
baseline = state['integrationCommit']
|
||||
branches = self.git('for-each-ref', '--format=%(refname:short)', 'refs/heads').splitlines()
|
||||
if stage['branch'] in branches:
|
||||
@@ -923,7 +959,8 @@ checkpoint; complete means ready for independent checks. Do not infer acceptance
|
||||
return False
|
||||
ss.pop('requestedDecision')
|
||||
prompt = self.prompt(stage, cp, ss, state)
|
||||
if not cp['checks'] or any(not check.get('configured', True) for check in cp['checks']):
|
||||
if not cp['checks'] or any(check.get('adapter') == 'assessment-evidence-inspection'
|
||||
or not check.get('configured', True) for check in cp['checks']):
|
||||
raise GateError('Independent test adapters must be configured and owner-qualified before execution')
|
||||
ss['attempts'] += 1
|
||||
ss['sessions'] += 1
|
||||
@@ -937,6 +974,7 @@ checkpoint; complete means ready for independent checks. Do not infer acceptance
|
||||
sourceCommit=ss['checkpointBase'], manifestSha256=digest(self.manifest), at=utc()))
|
||||
self.store.write(state) # durable RUNNING before launching child
|
||||
result = self.runner.run_session(self, stage, cp, directory, prompt)
|
||||
self.verify_assessment_writes()
|
||||
ss['lastResult'] = result
|
||||
state['recentSessions'].append(dict(id=session_id, stage=stage['id'], checkpoint=cp['id'],
|
||||
outcome=result['outcome'], summary=redact(result['summary'])[:600], directory=str(directory)))
|
||||
@@ -963,6 +1001,7 @@ checkpoint; complete means ready for independent checks. Do not infer acceptance
|
||||
ss['verifiedChecks'] = []
|
||||
for check in cp['checks']:
|
||||
ss['verifiedChecks'].append(self.runner.run_check(self, check, directory / 'checks' / check['id']))
|
||||
self.verify_assessment_writes()
|
||||
ss['evidence'] = []
|
||||
for name in cp['requiredArtifacts']:
|
||||
artifact = under(self.repo, name)
|
||||
@@ -998,6 +1037,17 @@ checkpoint; complete means ready for independent checks. Do not infer acceptance
|
||||
self.store.write(state)
|
||||
return self.finish_checkpoint(stage, cp, ss, state)
|
||||
|
||||
def verify_assessment_writes(self):
|
||||
scope = self.manifest.get('executionScope')
|
||||
if not scope:
|
||||
return
|
||||
prefix = scope['reportDirectory'] + '/'
|
||||
changed = self.git('diff', '--name-only', 'HEAD', '-z').split('\0')
|
||||
changed += self.git('ls-files', '--others', '--exclude-standard', '-z').split('\0')
|
||||
deleted = self.git('diff', '--name-only', '--diff-filter=D', 'HEAD')
|
||||
if deleted or any(name and (not name.startswith(prefix) or not name.endswith('.md')) for name in changed):
|
||||
raise GateError('Read-only assessment modified a forbidden path or deleted a file')
|
||||
|
||||
def content_hash(self):
|
||||
# Git blob hashes include tracked modifications and untracked source without reading secrets into logs.
|
||||
files = self.git('ls-files', '--cached', '--others', '--exclude-standard', '-z').split('\0')
|
||||
@@ -1086,6 +1136,9 @@ checkpoint; complete means ready for independent checks. Do not infer acceptance
|
||||
self.store.write(state)
|
||||
return
|
||||
try:
|
||||
scope = self.manifest.get('executionScope')
|
||||
if scope and (stage['id'], cp['id']) != (scope['stage'], scope['checkpoint']):
|
||||
raise GateError('Restricted assessment scope ends after P10-00; new owner-scoped policy required')
|
||||
# Check source before requesting approval or modifying Git.
|
||||
self.prompt(stage, cp, ss, state)
|
||||
if ss['entryBaseline'] is None:
|
||||
@@ -1179,7 +1232,7 @@ def install(source, root, repository, mode='dedicated-runner'):
|
||||
if root.is_relative_to(repo) or repo.is_relative_to(root):
|
||||
raise GateError('Install control plane outside worker checkout')
|
||||
root.mkdir(parents=True)
|
||||
for name in ('controller.py', 'local_mode.py', 'backlog.json', 'config.example.json'):
|
||||
for name in ('controller.py', 'local_mode.py', 'assessment_adapter.py', 'backlog.json', 'config.example.json'):
|
||||
shutil.copyfile(source / name, root / name)
|
||||
for name in ('schemas', 'prompts'):
|
||||
shutil.copytree(source / name, root / name)
|
||||
@@ -1200,7 +1253,7 @@ def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('command', choices=['init', 'validate', 'dry-run', 'start', 'resume', 'status',
|
||||
'pause', 'retry', 'current', 'logs', 'recover', 'configure',
|
||||
'setup','auth','auth-status','qualify','approve','prepare','key','enable-unattended'])
|
||||
'setup','auth','auth-status','qualify','approve','prepare','prepare-entry','key','enable-unattended'])
|
||||
parser.add_argument('--control-root', type=Path, required=True)
|
||||
parser.add_argument('--repository')
|
||||
parser.add_argument('--stage')
|
||||
@@ -1218,7 +1271,7 @@ def main(argv=None):
|
||||
and not read_json(root/'backlog.json')['executionEnabled']
|
||||
and not any(s['sessions'] for s in state['stages'].values())):
|
||||
with RepoLock(Path(existing['repository'])):
|
||||
for name in ('controller.py','local_mode.py'):
|
||||
for name in ('controller.py','local_mode.py','assessment_adapter.py'):
|
||||
shutil.copyfile(source/name,root/name)
|
||||
for name in ('Run-JobjaktBacklog.ps1','Approve-JobjaktBacklog.ps1','Probe-JobjaktSandbox.ps1',
|
||||
'Use-JobjaktBacklog.ps1','Register-JobjaktBacklogTask.ps1'):
|
||||
@@ -1326,6 +1379,34 @@ def main(argv=None):
|
||||
'-ApprovalPath',str(root/'approvals'/request.name),'-Thumbprint',thumb,'-ExpectedOwnerSid',sid,
|
||||
'-Disposition',disposition,'-Decision',decision])
|
||||
return result.returncode
|
||||
elif args.command == 'prepare-entry':
|
||||
with RepoLock(engine.repo):
|
||||
validate_manifest(engine.manifest,root)
|
||||
state=engine.store.read()
|
||||
scope=engine.manifest.get('executionScope')
|
||||
if not scope or not engine.manifest['executionEnabled']:
|
||||
raise GateError('Adopt the signed restricted assessment policy first')
|
||||
if (root/'active-process.json').exists() or state['recoveryRequired'] or any(s['sessions'] for s in state['stages'].values()):
|
||||
raise GateError('Entry preparation requires an unused reconciled queue')
|
||||
local_mode.require_unattended(root,engine.config)
|
||||
if engine.git('status','--porcelain','--untracked-files=all') or engine.git('rev-parse','HEAD') != engine.manifest['baselineCommit']:
|
||||
raise GateError('Assessment entry requires a clean exact baseline checkout')
|
||||
engine.verify_remote(scope['handoffBranch'],scope['handoffCommit'])
|
||||
engine.git('merge-base','--is-ancestor',engine.manifest['baselineCommit'],scope['handoffCommit'])
|
||||
stage=next(s for s in engine.manifest['stages'] if s['id']==scope['stage'])
|
||||
cp=next(x for x in stage['checkpoints'] if x['id']==scope['checkpoint'])
|
||||
engine.prompt(stage,cp,state['stages'][stage['id']],state)
|
||||
if not any(check['id']=='qualified-provider-browser-python-pdf-security-preservation'
|
||||
and check.get('configured') is True for check in cp['checks']):
|
||||
raise GateError('P10-00 composite adapter is not qualified')
|
||||
for check in cp['checks']:
|
||||
if check.get('adapter') == 'assessment-evidence-inspection' or not check.get('configured'):
|
||||
raise GateError('P10-00 composite adapter is not qualified')
|
||||
for kind in ['environment-qualified','roadmap-execution',*stage['requiredOwnerApprovals'],*cp['approvalsBefore']]:
|
||||
if not engine.request(stage,cp,kind,state):
|
||||
print('Protected entry request prepared; approve it and run prepare-entry again. No worker launched.')
|
||||
return 3
|
||||
print('All exact restricted entry approvals verified. Owner may deliberately run start. No worker launched.')
|
||||
elif args.command == 'prepare':
|
||||
with RepoLock(engine.repo):
|
||||
state = engine.store.read()
|
||||
|
||||
@@ -61,8 +61,17 @@ def validate_config(root, config):
|
||||
|
||||
def filesystem_rules(root, config):
|
||||
repo = Path(config['repository']).resolve()
|
||||
rules = {':root':'deny', ':minimal':'read', repo.as_posix():'write',
|
||||
assessment = config.get('assessmentReportDirectory')
|
||||
rules = {':root':'deny', ':minimal':'read', repo.as_posix():'read' if assessment else 'write',
|
||||
Path(config['workerTemp']).resolve().as_posix():'write'}
|
||||
if assessment:
|
||||
if assessment != 'docs/automation/stages/PHASE-10':
|
||||
raise ValueError('Invalid assessment report directory')
|
||||
for name in (assessment, '.artifacts/orchestrator-probes'):
|
||||
path = (repo/name).resolve()
|
||||
if not path.is_relative_to(repo):
|
||||
raise ValueError('Assessment output escapes repository')
|
||||
rules[path.as_posix()] = 'write'
|
||||
for name in config.get('toolReadRoots',[]):
|
||||
path = Path(name).resolve()
|
||||
if path == Path(path.anchor) or path == Path.home():
|
||||
@@ -94,7 +103,7 @@ def permission_args(root, config, command):
|
||||
|
||||
def binding(root,config):
|
||||
value = {k:v for k,v in config.items() if k != 'executionPolicy'}
|
||||
for name in ('local_mode.py','controller.py','Probe-JobjaktSandbox.ps1'):
|
||||
for name in ('local_mode.py','controller.py','assessment_adapter.py','Probe-JobjaktSandbox.ps1'):
|
||||
source = Path(root)/name
|
||||
if source.exists():
|
||||
value[name+'Sha256'] = hashlib.sha256(source.read_bytes()).hexdigest()
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Trusted operator preparation. Validates published native evidence; runs no product suite.
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import crypto from 'node:crypto';
|
||||
import zlib from 'node:zlib';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
const [repository, destination] = process.argv.slice(2);
|
||||
if (!repository || !destination) throw new Error('Usage: node prepare_assessment.mjs <published-handoff-checkout> <new-output-directory>');
|
||||
const root = fs.realpathSync(repository), output = path.resolve(destination);
|
||||
if (fs.existsSync(output)) throw new Error('Use a new receipt directory; preserve previous qualification');
|
||||
const git = (...args) => execFileSync('rtk', ['proxy','git','-C',root,...args], {encoding:'utf8'}).trim();
|
||||
const handoff = '5cb903b989742b7a96cb22ddf4c0e24993b18980';
|
||||
const baseline = 'fc5b9d377199ce51f480a920a3772108094929e3';
|
||||
if (git('rev-parse','HEAD') !== handoff || git('status','--porcelain')) throw new Error('Require the exact clean published handoff checkout');
|
||||
const remote = git('ls-remote','--exit-code','origin','refs/heads/test/pre-phase10-ux-acceptance');
|
||||
if (remote.split(/\s/)[0] !== handoff) throw new Error('Handoff publication differs');
|
||||
if (git('rev-parse',handoff+'^') !== baseline) throw new Error('Baseline relationship differs');
|
||||
const relative = 'docs/automation/handoffs/pre-phase10';
|
||||
const directory = path.join(root,relative);
|
||||
const {validatePackage, measureNative} = await import(pathToFileURL(path.join(directory,'verify.mjs')).href);
|
||||
const verified = validatePackage();
|
||||
if (verified.integrity !== 'PASS' || !verified.observedWorkingTreeClean) throw new Error('Handoff integrity failed');
|
||||
const evidence = JSON.parse(fs.readFileSync(path.join(directory,'TEST_EVIDENCE.json')));
|
||||
const artifact = id => {
|
||||
const row=evidence.artifacts.find(item=>item.id===id);
|
||||
if (!row) throw new Error('Missing native artifact');
|
||||
const raw=fs.readFileSync(path.join(directory,row.path));
|
||||
return row.path.endsWith('.gz') ? zlib.gunzipSync(raw) : raw;
|
||||
};
|
||||
const hash = raw => crypto.createHash('sha256').update(raw).digest('hex');
|
||||
const files = {};
|
||||
// Freeze all tracked non-envelope sources, including tests/config/security/preservation documentation.
|
||||
for (const name of git('ls-files','-z').split('\0').filter(Boolean)) {
|
||||
if (!name.startsWith(relative+'/')) files[name]=hash(fs.readFileSync(path.join(root,name)));
|
||||
}
|
||||
const nativeReports = evidence.reports.map(report=>{
|
||||
let observation=JSON.parse(artifact(report.processArtifactId));
|
||||
const parts=report.processPointer.slice(1).split('/').map(value=>value.replaceAll('~1','/').replaceAll('~0','~'));
|
||||
for (const key of parts.slice(0,-1)) observation=observation[key];
|
||||
return {...report, measured:report.nativeFormat ? measureNative(report.nativeFormat,artifact(report.artifactId).toString('utf8')) : null,
|
||||
nativeProcess:observation};
|
||||
});
|
||||
fs.mkdirSync(output,{recursive:true});
|
||||
const evidenceFiles={};
|
||||
for (const name of git('ls-files',relative).split(/\r?\n/).filter(Boolean)) {
|
||||
const local=path.relative(directory,path.join(root,name));
|
||||
const target=path.join(output,'handoff',local);
|
||||
fs.mkdirSync(path.dirname(target),{recursive:true});
|
||||
fs.copyFileSync(path.join(root,name),target);
|
||||
evidenceFiles['handoff/'+local.split(path.sep).join('/')]=hash(fs.readFileSync(target));
|
||||
}
|
||||
const receipt={schemaVersion:1,scope:'P10-00-ASSESSMENT-ONLY',integrity:'PASS',
|
||||
generatedUtc:new Date().toISOString(),baselineCommit:baseline,handoffCommit:handoff,
|
||||
testedCommit:evidence.testedCommit,files,evidenceFiles,nativeReports,
|
||||
retainedDecisions:JSON.parse(fs.readFileSync(path.join(directory,'ACCEPTANCE.json'))).verdicts,
|
||||
revisedS01:verified.revisedS01,verifierObservation:verified,
|
||||
limits:'Source-bound reuse for unchanged read-only assessment. Original failures and conditional/blocked decisions retained. No fresh product, security, model, human or production acceptance.'};
|
||||
fs.writeFileSync(path.join(output,'receipt.json'),JSON.stringify(receipt,null,2)+'\n');
|
||||
console.log(JSON.stringify({integrity:'PASS',receipt:path.join(output,'receipt.json'),sha256:hash(fs.readFileSync(path.join(output,'receipt.json'))),frozenSources:Object.keys(files).length,nativeReports:nativeReports.length,productSuitesExecuted:0},null,2));
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,80 @@
|
||||
"""Synthetic native assessment qualification only. Never starts the development queue."""
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import sys
|
||||
import uuid
|
||||
import controller as c
|
||||
import local_mode
|
||||
|
||||
|
||||
def main():
|
||||
root, worker, report_path = map(Path,sys.argv[1:])
|
||||
config=c.read_json(root/'config.json')
|
||||
policy=c.read_json(root/'proposed-p10-assessment.json')
|
||||
config['repository']=str(worker.resolve())
|
||||
engine=c.Engine(root,policy,config)
|
||||
if c.read_json(root/'backlog.json')['executionEnabled'] or any(s['sessions'] for s in engine.store.read()['stages'].values()):
|
||||
raise c.GateError('Qualification requires an unused disabled queue')
|
||||
with c.RepoLock(worker):
|
||||
prior=(root/'qualification.json').read_bytes() if (root/'qualification.json').exists() else None
|
||||
try:
|
||||
native=local_mode.qualify(engine)
|
||||
finally:
|
||||
if prior is not None:
|
||||
(root/'qualification.json').write_bytes(prior)
|
||||
else:
|
||||
(root/'qualification.json').unlink(missing_ok=True)
|
||||
if native['status'] != 'PASS':
|
||||
c.atomic(report_path,dict(native=native,actualQueueStarted=False,assessment='NOT_RUN'))
|
||||
raise c.GateError('Native signing isolation qualification failed; no further child launched')
|
||||
token=uuid.uuid4().hex
|
||||
probe=worker/'.artifacts/orchestrator-probes'/token
|
||||
probe.mkdir(parents=True)
|
||||
canary=worker/('.assessment-readonly-'+token)
|
||||
canary.write_text('SYNTHETIC READ-ONLY CANARY')
|
||||
output=worker/'docs/automation/stages/PHASE-10'/('native-probe-'+token+'.md')
|
||||
output.parent.mkdir(parents=True,exist_ok=True)
|
||||
logs=root/'probe'/('assessment-'+token)
|
||||
receipt=root/'assessment-activation-01'
|
||||
try:
|
||||
shutil.copytree(receipt,probe/'receipt')
|
||||
shutil.copyfile(Path(__file__).with_name('assessment_adapter.py'),probe/'assessment_adapter.py')
|
||||
plan=dict(worker=str(worker.resolve()),receipt=str(probe/'receipt/receipt.json'),
|
||||
pin=c.sha_file(receipt/'receipt.json'),canary=str(canary),output=str(output))
|
||||
c.atomic(probe/'plan.json',plan)
|
||||
(probe/'run.py').write_text('''import json, pathlib
|
||||
import assessment_adapter
|
||||
p=json.loads(pathlib.Path(__file__).with_name('plan.json').read_text())
|
||||
r=assessment_adapter.verify(p['worker'],p['receipt'],p['pin'])
|
||||
canary=pathlib.Path(p['canary'])
|
||||
assert canary.read_text()=='SYNTHETIC READ-ONLY CANARY'
|
||||
try:
|
||||
with canary.open('r+b'): pass
|
||||
except PermissionError: write_denied=True
|
||||
else: write_denied=False
|
||||
try: canary.unlink()
|
||||
except PermissionError: delete_denied=True
|
||||
else: delete_denied=False
|
||||
pathlib.Path(p['output']).write_text('Synthetic native report creation')
|
||||
assert write_denied and delete_denied
|
||||
print(json.dumps(dict(evidenceIntegrity='PASS',sourceWriteDenied=write_denied,sourceDeleteDenied=delete_denied,reportWrite=True,kind=r['kind'],nativeReports=len(r['nativeReports']))))
|
||||
''',encoding='utf-8')
|
||||
argv=[config['codexExecutable'],'sandbox','-C',str(worker),
|
||||
*local_mode.permission_args(root,engine.config,'sandbox'),
|
||||
config['pythonExecutable'],'-B',str(probe/'run.py')]
|
||||
engine.runner.supervise(engine,argv,logs,180)
|
||||
observation=json.loads((logs/'stdout.log').read_text())
|
||||
c.atomic(report_path,dict(native=native,assessment=observation,
|
||||
candidateCommit=engine.git('rev-parse','HEAD'),actualQueueStarted=False,
|
||||
compositeRuntime='BLOCKED_DOCKER_AND_SERVICE_BOUNDARY_UNQUALIFIED',logs=str(logs)))
|
||||
finally:
|
||||
canary.unlink(missing_ok=True)
|
||||
output.unlink(missing_ok=True)
|
||||
if probe.resolve().is_relative_to(worker.resolve()/'.artifacts/orchestrator-probes'):
|
||||
shutil.rmtree(probe)
|
||||
print('Synthetic native assessment checks complete; composite command runtime remains BLOCKED')
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
{"actualQueueStarted":false,"assessment":{"evidenceIntegrity":"PASS","kind":"REUSED_SOURCE_BOUND_EVIDENCE","nativeReports":34,"reportWrite":true,"sourceDeleteDenied":true,"sourceWriteDenied":true},"bindingMatchesProjectedProfile":true,"candidateCommit":"fc5b9d377199ce51f480a920a3772108094929e3","compositeRuntime":"BLOCKED_DOCKER_AND_SERVICE_BOUNDARY_UNQUALIFIED","logs":"C:\\Users\\Connor\\.jobjakt-backlog\\single-user-local\\probe\\assessment-b2c85d9a53474a7db172c73969586734","native":{"binding":"3b1ec3040285e5991dac5fc5e60cb362f7ea54691b9f790a6a496aadc12ec5db","failure":null,"issuedUtc":"2026-10-30T08:35:40.581635+00:00","ownerKeyDenied":true,"probes":{"files":[{"category":"approvals","readDenied":true,"writeDenied":true},{"category":"configuration","readDenied":true,"writeDenied":true},{"category":"key-file","readDenied":true,"writeDenied":true},{"category":"normal-codex","readDenied":true,"writeDenied":true},{"category":"other-repository","readDenied":true,"writeDenied":true},{"category":"personal","readDenied":true,"writeDenied":true},{"category":"production-canary","readDenied":true,"writeDenied":true}],"networkDenied":true,"ownerKeyDenied":true,"positiveControl":true,"signingKeyDenied":true},"scope":"Synthetic filesystem/network/signing canaries; real owner key probed","status":"PASS"}}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"activeProcessMarker":false,"actualSessions":0,"baselineCommit":"fc5b9d377199ce51f480a920a3772108094929e3","executionEnabled":false,"executionPolicy":"operator-controlled","executionWorktree":"C:\\Users\\Connor\\.codex\\worktrees\\p10-assessment\\Job tracker","handoffCommit":"5cb903b989742b7a96cb22ddf4c0e24993b18980","installedRepository":"C:\\Users\\Connor\\.codex\\worktrees\\autonomous-roadmap-01\\Job tracker","installedSourceMatches":true,"materialisedLineEndingOnlyFiles":47,"nativeQualification":{"binding":"b1bdcfd929af4829d316481c376e8c92d1b3182b61199080d2f915279f51ec7f","failure":null,"issuedUtc":"2026-10-30T08:35:24.011490+00:00","ownerKeyDenied":true,"probes":{"files":[{"category":"approvals","readDenied":true,"writeDenied":true},{"category":"configuration","readDenied":true,"writeDenied":true},{"category":"key-file","readDenied":true,"writeDenied":true},{"category":"normal-codex","readDenied":true,"writeDenied":true},{"category":"other-repository","readDenied":true,"writeDenied":true},{"category":"personal","readDenied":true,"writeDenied":true},{"category":"production-canary","readDenied":true,"writeDenied":true}],"networkDenied":true,"ownerKeyDenied":true,"positiveControl":true,"signingKeyDenied":true},"scope":"Synthetic filesystem/network/signing canaries; real owner key probed","status":"PASS"},"ownerKeyConfigured":true,"pending":[{"kind":"manifest-policy-update","request":"C:\\Users\\Connor\\.jobjakt-backlog\\single-user-local\\requests\\d637caf72c3fed5910f7c28e75dc82324d8febc12597ddb5627ae022af49a622.json"}],"publicCertificatePinMatches":true,"qualificationBindingMatches":true,"recoveryRequired":false}
|
||||
@@ -0,0 +1 @@
|
||||
{"exitCode":2,"modelSessionsAfter":0,"modelSessionsBefore":0,"stderr":"Adopt the signed restricted assessment policy first\n","stdout":""}
|
||||
@@ -0,0 +1 @@
|
||||
{"clean":true,"executionBranch":"codex/p10-assessment-ready","executionCommit":"fc5b9d377199ce51f480a920a3772108094929e3","handoffChanges":["docs/automation/handoffs/pre-phase10/ACCEPTANCE.json","docs/automation/handoffs/pre-phase10/ENTRY_PROPOSAL.json","docs/automation/handoffs/pre-phase10/TEST_EVIDENCE.json","docs/automation/handoffs/pre-phase10/evidence/p10-entry-proposal.gz","docs/automation/handoffs/pre-phase10/evidence/p10-final-gate-processes.gz","docs/automation/handoffs/pre-phase10/evidence/p10-proposal-check-source.gz","docs/automation/handoffs/pre-phase10/evidence/p10-protected-entry-native.gz"],"handoffParent":"fc5b9d377199ce51f480a920a3772108094929e3","policyFileMatchesInstalledProposal":true,"proposedManifestSha256":"0ff54dadbae7dde08e68c247e1365fd54f063e5b09066cb9e9cd577afe11a061","remoteHeads":["140e182516e86c8718054cf0907593fcd6a38aa7\trefs/heads/codex/autonomous-roadmap-single-user","5cb903b989742b7a96cb22ddf4c0e24993b18980\trefs/heads/test/pre-phase10-ux-acceptance"]}
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"tests": 81,
|
||||
"failures": 0,
|
||||
"errors": 0,
|
||||
"skipped": 0,
|
||||
"sourceStable": true,
|
||||
"sourceHashes": {
|
||||
"automation/assessment_adapter.py": "6f6b4e7b96f1e9ca896e1d2a9ab26c12bf38d603dbf087acf61969f5d3906a21",
|
||||
"automation/controller.py": "f2c05a3bccb43cc81f76e35e7adefd2e590934ae99f2a1fdc9206844e8732d88",
|
||||
"automation/local_mode.py": "ccf4a5e8c208d17d87b47dd1aca8ebf540af27a274ae7333da9fb8075cb9b5b2",
|
||||
"automation/prepare_queue.py": "6cad22305bc354ccbae2f031cb2b324596b3a7d42503b954c27f1e63e47a5f7d",
|
||||
"automation/qualify_assessment.py": "0891520943779da0ff722d8deba62151d5c1dfad1329026310baab216459374d",
|
||||
"automation/prepare_assessment.mjs": "a13f3d0458d7e4006cdfc07c3de2d563a6795a8cd4421be2b7de36fb70d68c10",
|
||||
"automation/tests/test_assessment.py": "d3d30aaa808ead6927a444c1f91aa583835d3a787893b8f67db6b68261aa243c",
|
||||
"automation/tests/test_controller.py": "458da3c60571e61aad42987ce156842efd73ef0e2aa0c7bce719d36d626d7e7d",
|
||||
"automation/tests/test_local_mode.py": "524d40ede6379138f729c06876e43b6ef1cb15ee427947f2358a0439cb02ff69",
|
||||
"scripts/Run-JobjaktBacklog.ps1": "2b11f5016560890262f0ca96bdba5a13240b0250c6bf6a96b43c33929cbcd070"
|
||||
},
|
||||
"elapsedSeconds": 161.283,
|
||||
"scope": "Synthetic controller and adapter tests; no actual backlog or product suite"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"constraints": "PASS",
|
||||
"metaSchemaValidation": "NOT_PERFORMED_DRAFT_2020_META_SCHEMA_UNAVAILABLE",
|
||||
"errors": null
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
test_assessment_profile_keeps_product_read_only (test_assessment.ReadOnlyProfileTests.test_assessment_profile_keeps_product_read_only) ... ok
|
||||
test_receipt_rejects_changed_product_or_changed_receipt (test_assessment.ReceiptTests.test_receipt_rejects_changed_product_or_changed_receipt) ... ok
|
||||
test_assessment_reaches_protected_stop_and_cannot_advance (test_assessment.ScopeTests.test_assessment_reaches_protected_stop_and_cannot_advance) ... ok
|
||||
test_assessment_reports_are_permitted (test_assessment.ScopeTests.test_assessment_reports_are_permitted) ... ok
|
||||
test_entry_cannot_prepare_approvals_without_composite_adapter (test_assessment.ScopeTests.test_entry_cannot_prepare_approvals_without_composite_adapter) ... ok
|
||||
test_evidence_inspection_cannot_be_marked_runtime_qualified (test_assessment.ScopeTests.test_evidence_inspection_cannot_be_marked_runtime_qualified) ... ok
|
||||
test_generic_approval_cannot_launch_next_checkpoint (test_assessment.ScopeTests.test_generic_approval_cannot_launch_next_checkpoint) ... ok
|
||||
test_inspection_requires_explicit_false_and_never_launches_worker (test_assessment.ScopeTests.test_inspection_requires_explicit_false_and_never_launches_worker) ... ok
|
||||
test_native_isolation_failure_stops_before_additional_probe (test_assessment.ScopeTests.test_native_isolation_failure_stops_before_additional_probe) ... ok
|
||||
test_policy_cannot_relabel_p10_01_as_assessment (test_assessment.ScopeTests.test_policy_cannot_relabel_p10_01_as_assessment) ... ok
|
||||
test_scope_rejects_product_write_and_deletion (test_assessment.ScopeTests.test_scope_rejects_product_write_and_deletion) ... ok
|
||||
test_scope_requires_protected_stop (test_assessment.ScopeTests.test_scope_requires_protected_stop) ... ok
|
||||
test_accidental_main_checkout_rejected (test_controller.EngineTests.test_accidental_main_checkout_rejected) ... ok
|
||||
test_after_approval_binds_tested_commit_without_rerunning_worker (test_controller.EngineTests.test_after_approval_binds_tested_commit_without_rerunning_worker) ... ok
|
||||
test_changed_prompt_blocks (test_controller.EngineTests.test_changed_prompt_blocks) ... ok
|
||||
test_conditional_can_advance_only_with_explicit_signed_deferral (test_controller.EngineTests.test_conditional_can_advance_only_with_explicit_signed_deferral) ... ok
|
||||
test_conditional_requires_explicit_deferrable_gates (test_controller.EngineTests.test_conditional_requires_explicit_deferrable_gates) ... ok
|
||||
test_corrupt_state_recovers_last_good_without_success (test_controller.EngineTests.test_corrupt_state_recovers_last_good_without_success) ... ok
|
||||
test_cryptographically_approved_resume_e2e (test_controller.EngineTests.test_cryptographically_approved_resume_e2e) ... ok
|
||||
test_dependency_cannot_be_bypassed (test_controller.EngineTests.test_dependency_cannot_be_bypassed) ... ok
|
||||
test_design_revision_reruns_current_checkpoint_and_keeps_owner_selection (test_controller.EngineTests.test_design_revision_reruns_current_checkpoint_and_keeps_owner_selection) ... ok
|
||||
test_disabled_manifest_never_executes (test_controller.EngineTests.test_disabled_manifest_never_executes) ... ok
|
||||
test_double_corruption_fails_closed (test_controller.EngineTests.test_double_corruption_fails_closed) ... ok
|
||||
test_duplicate_lock_rejected (test_controller.EngineTests.test_duplicate_lock_rejected) ... ok
|
||||
test_execution_window_stops_before_worker (test_controller.EngineTests.test_execution_window_stops_before_worker) ... ok
|
||||
test_failed_stage_stops_and_exhausts_retry_budget (test_controller.EngineTests.test_failed_stage_stops_and_exhausts_retry_budget) ... ok
|
||||
test_failing_checks_never_commit_or_advance (test_controller.EngineTests.test_failing_checks_never_commit_or_advance) ... ok
|
||||
test_interruption_is_not_success_and_resume_has_budget (test_controller.EngineTests.test_interruption_is_not_success_and_resume_has_budget) ... ok
|
||||
test_manifest_initially_disabled (test_controller.EngineTests.test_manifest_initially_disabled) ... ok
|
||||
test_missing_approval_does_not_run_model (test_controller.EngineTests.test_missing_approval_does_not_run_model) ... ok
|
||||
test_missing_prompt_blocks (test_controller.EngineTests.test_missing_prompt_blocks) ... ok
|
||||
test_model_cannot_approve_itself (test_controller.EngineTests.test_model_cannot_approve_itself) ... ok
|
||||
test_pause_persists_and_resume_clears_it (test_controller.EngineTests.test_pause_persists_and_resume_clears_it) ... ok
|
||||
test_push_failure_preserves_tested_commit (test_controller.EngineTests.test_push_failure_preserves_tested_commit) ... ok
|
||||
test_push_verification_mismatch_stops (test_controller.EngineTests.test_push_verification_mismatch_stops) ... ok
|
||||
test_recovered_running_without_marker_requires_reconciliation (test_controller.EngineTests.test_recovered_running_without_marker_requires_reconciliation) ... ok
|
||||
test_restart_running_marker_cannot_mean_completed (test_controller.EngineTests.test_restart_running_marker_cannot_mean_completed) ... ok
|
||||
test_same_repo_other_control_root_cannot_take_lock (test_controller.EngineTests.test_same_repo_other_control_root_cannot_take_lock) ... ok
|
||||
test_success_orders_stages_and_verifies_remote (test_controller.EngineTests.test_success_orders_stages_and_verifies_remote) ... ok
|
||||
test_tampered_evidence_cannot_be_approved (test_controller.EngineTests.test_tampered_evidence_cannot_be_approved) ... ok
|
||||
test_unavailable_remote_preserves_commit_and_retries_publication_only (test_controller.EngineTests.test_unavailable_remote_preserves_commit_and_retries_publication_only) ... ok
|
||||
test_unconfigured_test_adapter_blocks_before_model (test_controller.EngineTests.test_unconfigured_test_adapter_blocks_before_model) ... ok
|
||||
test_unsigned_recovery_cannot_clear_running_guard (test_controller.EngineTests.test_unsigned_recovery_cannot_clear_running_guard) ... ok
|
||||
test_forward_dependency_rejected (test_controller.QueueTests.test_forward_dependency_rejected) ... ok
|
||||
test_production_branches_rejected (test_controller.QueueTests.test_production_branches_rejected) ... ok
|
||||
test_real_queue_is_ordered_and_missing_sources_are_explicit (test_controller.QueueTests.test_real_queue_is_ordered_and_missing_sources_are_explicit) ... ok
|
||||
test_empty_report_is_not_a_pass (test_controller.ReportTests.test_empty_report_is_not_a_pass) ... ok
|
||||
test_json_log_redaction_preserves_event_structure (test_controller.ReportTests.test_json_log_redaction_preserves_event_structure) ... ok
|
||||
test_path_escape_rejected (test_controller.ReportTests.test_path_escape_rejected) ... ok
|
||||
test_playwright_retry_and_expected_failure_are_not_pass (test_controller.ReportTests.test_playwright_retry_and_expected_failure_are_not_pass) ... ok
|
||||
test_redacts_common_credentials (test_controller.ReportTests.test_redacts_common_credentials) ... ok
|
||||
test_report_membership_and_skip_enforcement (test_controller.ReportTests.test_report_membership_and_skip_enforcement) ... ok
|
||||
test_cli_nonzero_exit_never_accepts (test_controller.SubprocessTests.test_cli_nonzero_exit_never_accepts) ... ok
|
||||
test_cli_protocol_real_process_and_complete_prompt (test_controller.SubprocessTests.test_cli_protocol_real_process_and_complete_prompt) ... ok
|
||||
test_killed_controller_terminates_worker_and_grandchild (test_controller.SubprocessTests.test_killed_controller_terminates_worker_and_grandchild) ... ok
|
||||
test_marker_write_failure_does_not_leave_worker (test_controller.SubprocessTests.test_marker_write_failure_does_not_leave_worker) ... ok
|
||||
test_no_newline_output_obeys_log_budget (test_controller.SubprocessTests.test_no_newline_output_obeys_log_budget) ... ok
|
||||
test_prompt_stall_still_obeys_timeout (test_controller.SubprocessTests.test_prompt_stall_still_obeys_timeout) ... ok
|
||||
test_real_check_report_must_match_expected_membership (test_controller.SubprocessTests.test_real_check_report_must_match_expected_membership) ... ok
|
||||
test_worker_cannot_execute_before_job_assignment (test_controller.SubprocessTests.test_worker_cannot_execute_before_job_assignment) ... ok
|
||||
test_pending_recovery_can_be_signed_from_idle_operator_terminal (test_local_mode.LocalFlowTests.test_pending_recovery_can_be_signed_from_idle_operator_terminal) ... ok
|
||||
test_single_user_concurrent_controller_and_interrupted_guard (test_local_mode.LocalFlowTests.test_single_user_concurrent_controller_and_interrupted_guard) ... ok
|
||||
test_single_user_main_checkout_is_still_prohibited (test_local_mode.LocalFlowTests.test_single_user_main_checkout_is_still_prohibited) ... ok
|
||||
test_single_user_missing_approval_stops_before_worker (test_local_mode.LocalFlowTests.test_single_user_missing_approval_stops_before_worker) ... ok
|
||||
test_single_user_pause_and_publication_recovery (test_local_mode.LocalFlowTests.test_single_user_pause_and_publication_recovery) ... ok
|
||||
test_single_user_subprocess_protocol_and_ordered_publication (test_local_mode.LocalFlowTests.test_single_user_subprocess_protocol_and_ordered_publication) ... ok
|
||||
test_auth_home_inside_worker_is_rejected (test_local_mode.LocalPolicyTests.test_auth_home_inside_worker_is_rejected) ... ok
|
||||
test_blocked_status_rejects_otherwise_successful_probes (test_local_mode.LocalPolicyTests.test_blocked_status_rejects_otherwise_successful_probes) ... ok
|
||||
test_different_or_missing_sid_rejected (test_local_mode.LocalPolicyTests.test_different_or_missing_sid_rejected) ... ok
|
||||
test_existing_normal_auth_home_is_rejected (test_local_mode.LocalPolicyTests.test_existing_normal_auth_home_is_rejected) ... ok
|
||||
test_failed_probe_and_unconfigured_owner_key_cannot_qualify (test_local_mode.LocalPolicyTests.test_failed_probe_and_unconfigured_owner_key_cannot_qualify) ... ok
|
||||
test_missing_or_stale_qualification_never_enables_unattended (test_local_mode.LocalPolicyTests.test_missing_or_stale_qualification_never_enables_unattended) ... ok
|
||||
test_mxc_is_explicit_without_legacy_or_unrestricted_fallback (test_local_mode.LocalPolicyTests.test_mxc_is_explicit_without_legacy_or_unrestricted_fallback) ... ok
|
||||
test_noninteractive_approval_is_refused (test_local_mode.LocalPolicyTests.test_noninteractive_approval_is_refused) ... ok
|
||||
test_operator_controlled_policy_cannot_launch_a_worker (test_local_mode.LocalPolicyTests.test_operator_controlled_policy_cannot_launch_a_worker) ... ok
|
||||
test_plugins_are_not_an_implicit_same_user_capability (test_local_mode.LocalPolicyTests.test_plugins_are_not_an_implicit_same_user_capability) ... ok
|
||||
test_probe_summary_rejects_partial_and_failed_boundaries (test_local_mode.LocalPolicyTests.test_probe_summary_rejects_partial_and_failed_boundaries) ... ok
|
||||
test_profile_denies_root_control_auth_and_normal_configuration (test_local_mode.LocalPolicyTests.test_profile_denies_root_control_auth_and_normal_configuration) ... ok
|
||||
test_same_chatgpt_auth_is_explicit_and_tokens_are_not_copied (test_local_mode.LocalPolicyTests.test_same_chatgpt_auth_is_explicit_and_tokens_are_not_copied) ... ok
|
||||
test_same_windows_identity_supported (test_local_mode.LocalPolicyTests.test_same_windows_identity_supported) ... ok
|
||||
test_tool_or_temp_roots_cannot_reopen_protected_parent (test_local_mode.LocalPolicyTests.test_tool_or_temp_roots_cannot_reopen_protected_parent) ... ok
|
||||
|
||||
----------------------------------------------------------------------
|
||||
Ran 81 tests in 161.136s
|
||||
|
||||
OK
|
||||
@@ -0,0 +1 @@
|
||||
{"observations":[{"status":"BLOCKED","stderr":"Failed to resolve full path of the current executable [C:\\Users\\Connor\\.dotnet\\dotnet.exe]\n","stdout":"","tool":"accepted-dotnet"},{"status":"BLOCKED","stderr":"No pyvenv.cfg file\n","stdout":"","tool":"accepted-docling-python"},{"stderr":"","stdout":"v24.19.0\n","tool":"node"},{"stderr":"","stdout":"\nHost:\n Version: 9.0.20\n Architecture: x64\n Commit: 3879076d9a\n RID: win-x64\n\n.NET SDKs installed:\n No SDKs were found.\n\n.NET runtimes installed:\n Microsoft.NETCore.App 8.0.30 [C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App]\n Microsoft.NETCore.App 9.0.20 [C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App]\n Microsoft.WindowsDesktop.App 8.0.30 [C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App]\n Microsoft.WindowsDesktop.App 9.0.20 [C:\\Program Files\\dotnet\\shared\\Microsoft.WindowsDesktop.App]\n\nOther architectures found:\n None\n\nEnvironment variables:\n Not set\n\nglobal.json file:\n C:\\Users\\Connor\\.codex\\worktrees\\autonomous-roadmap-01\\Job tracker\\global.json\n\nLearn more:\n https://aka.ms/dotnet/info\n\nDownload .NET:\n https://aka.ms/dotnet/download\n","tool":"dotnet"},{"stderr":"WARNING: Error loading config file: open C:\\Users\\Connor\\.docker\\config.json: Access is denied.\npermission denied while trying to connect to the docker API at npipe:////./pipe/docker_engine\n","stdout":"\n","tool":"docker"}],"profile":"Existing native MXC, no permissions widened","realProductSuitesExecuted":0}
|
||||
@@ -11,6 +11,17 @@
|
||||
"baselineBranch": {"$ref": "#/$defs/branch"},
|
||||
"remote": {"type": "string", "pattern": "^[A-Za-z0-9_-]+$"},
|
||||
"integrationBranch": {"$ref": "#/$defs/branch"},
|
||||
"executionScope": {
|
||||
"type": "object", "additionalProperties": false,
|
||||
"required": ["stage", "checkpoint", "mode", "reportDirectory", "handoffCommit", "handoffBranch"],
|
||||
"properties": {
|
||||
"stage": {"const": "PHASE-10"}, "checkpoint": {"const": "P10-00"},
|
||||
"mode": {"const": "assessment-only"},
|
||||
"reportDirectory": {"const": "docs/automation/stages/PHASE-10"},
|
||||
"handoffCommit": {"type": "string", "pattern": "^[0-9a-f]{40}$"},
|
||||
"handoffBranch": {"$ref": "#/$defs/branch"}
|
||||
}
|
||||
},
|
||||
"stages": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/stage"}}
|
||||
},
|
||||
"$defs": {
|
||||
@@ -56,11 +67,16 @@
|
||||
"check": {
|
||||
"type": "object",
|
||||
"required": ["id", "argv", "cwd", "report"],
|
||||
"if": {"required": ["adapter"], "properties": {"adapter": {"const": "assessment-evidence-inspection"}}},
|
||||
"then": {"required": ["configured"], "properties": {"configured": {"const": false}}},
|
||||
"properties": {
|
||||
"id": {"type": "string", "pattern": "^[A-Za-z0-9-]+$"},
|
||||
"argv": {"type": "array", "minItems": 1, "items": {"type": "string"}},
|
||||
"cwd": {"type": "string"},
|
||||
"configured": {"type": "boolean"},
|
||||
"adapter": {"enum": ["assessment-evidence-inspection"]},
|
||||
"receipt": {"type": "string"},
|
||||
"receiptSha256": {"type": "string", "pattern": "^[0-9a-f]{64}$"},
|
||||
"report": {"type": ["object", "null"], "properties": {
|
||||
"format": {"enum": ["trx", "jest", "playwright", "junit"]},
|
||||
"path": {"type": "string"},
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
"""Synthetic scope tests. No product backlog, model, signing or services."""
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
import controller as c
|
||||
import local_mode
|
||||
import test_controller as fixture
|
||||
|
||||
|
||||
def scope():
|
||||
return dict(stage='PHASE-10', checkpoint='P10-00', mode='assessment-only',
|
||||
reportDirectory='docs/automation/stages/PHASE-10',
|
||||
handoffCommit='1'*40, handoffBranch='test/handoff')
|
||||
|
||||
|
||||
class ScopeTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
fixture.EngineTests.setUp(self)
|
||||
first=self.manifest['stages'][0]
|
||||
first['id']='PHASE-10'
|
||||
first['checkpoints'][0]['id']='P10-00'
|
||||
self.manifest['stages'][1]['dependencies']=['PHASE-10']
|
||||
state=self.engine.store.read()
|
||||
state['stages']['PHASE-10']=state['stages'].pop('ONE')
|
||||
state['manifestSha256']=c.digest(self.manifest)
|
||||
self.engine.store.write(state)
|
||||
tearDown = fixture.EngineTests.tearDown
|
||||
git_at = staticmethod(fixture.EngineTests.git_at)
|
||||
|
||||
def test_scope_requires_protected_stop(self):
|
||||
self.manifest['executionScope'] = scope()
|
||||
with self.assertRaisesRegex(c.GateError, 'protected stop'):
|
||||
c.validate_manifest(self.manifest)
|
||||
|
||||
def test_scope_rejects_product_write_and_deletion(self):
|
||||
self.manifest['executionScope'] = scope()
|
||||
(self.repo/'README').write_text('unauthorised')
|
||||
with self.assertRaisesRegex(c.GateError, 'assessment'):
|
||||
self.engine.verify_assessment_writes()
|
||||
(self.repo/'README').unlink()
|
||||
with self.assertRaises(c.GateError):
|
||||
self.engine.verify_assessment_writes()
|
||||
|
||||
def test_assessment_reports_are_permitted(self):
|
||||
self.manifest['executionScope'] = scope()
|
||||
output=self.repo/scope()['reportDirectory']/'P10-00.md'
|
||||
output.parent.mkdir(parents=True)
|
||||
output.write_text('Synthetic assessment')
|
||||
self.engine.verify_assessment_writes()
|
||||
|
||||
def test_generic_approval_cannot_launch_next_checkpoint(self):
|
||||
first=self.manifest['stages'][0]
|
||||
first['checkpoints'].append(copy.deepcopy(first['checkpoints'][0]))
|
||||
first['checkpoints'][1]['id']='implementation'
|
||||
self.manifest['executionScope']=scope()
|
||||
state=self.engine.store.read()
|
||||
state['manifestSha256']=c.digest(self.manifest)
|
||||
state['stages']['PHASE-10']['checkpoint']=1
|
||||
self.engine.store.write(state)
|
||||
self.engine.run()
|
||||
self.assertEqual(0,self.runner.calls)
|
||||
self.assertIn('assessment', self.engine.store.read()['latestFailure'])
|
||||
|
||||
def test_assessment_reaches_protected_stop_and_cannot_advance(self):
|
||||
output='docs/automation/stages/PHASE-10/P10-00.md'
|
||||
class ReportRunner(fixture.SyntheticRunner):
|
||||
def run_session(inner, engine, stage, checkpoint, directory, prompt):
|
||||
inner.calls += 1
|
||||
report=engine.repo/output
|
||||
report.parent.mkdir(parents=True,exist_ok=True)
|
||||
report.write_text('Synthetic assessment only')
|
||||
return dict(outcome='complete',summary='synthetic',blockers=[],requestedApprovals=[],checkpoint=checkpoint['id'])
|
||||
first=self.manifest['stages'][0]
|
||||
cp=first['checkpoints'][0]
|
||||
cp['requiredArtifacts']=[output]
|
||||
cp['approvalsAfter']=['candidate-specific-cleanup-scope']
|
||||
first['checkpoints'].append(copy.deepcopy(cp))
|
||||
first['checkpoints'][1]['id']='implementation'
|
||||
self.manifest['executionScope']={**scope(),'handoffCommit':self.manifest['baselineCommit'],'handoffBranch':'codex/base'}
|
||||
state=self.engine.store.read()
|
||||
state['manifestSha256']=c.digest(self.manifest)
|
||||
self.engine.store.write(state)
|
||||
self.engine.runner=ReportRunner()
|
||||
self.engine.approval_verifier=lambda request: request['kind']!='candidate-specific-cleanup-scope'
|
||||
self.engine.run()
|
||||
self.assertEqual(1,self.engine.runner.calls)
|
||||
self.assertEqual('candidate-specific-cleanup-scope', self.engine.store.read()['stages']['PHASE-10']['awaiting'][0]['kind'])
|
||||
self.engine.approval_verifier=lambda request: True
|
||||
self.engine.run()
|
||||
self.assertEqual(1,self.engine.runner.calls)
|
||||
self.assertIn('assessment',self.engine.store.read()['latestFailure'])
|
||||
|
||||
def test_policy_cannot_relabel_p10_01_as_assessment(self):
|
||||
self.manifest['executionScope']={**scope(),'checkpoint':'P10-01'}
|
||||
cp=self.manifest['stages'][0]['checkpoints'][0]
|
||||
cp['id']='P10-01'
|
||||
cp['approvalsAfter']=['candidate-specific-cleanup-scope']
|
||||
with self.assertRaisesRegex(c.GateError,'protected stop'):
|
||||
c.validate_manifest(self.manifest)
|
||||
|
||||
def test_evidence_inspection_cannot_be_marked_runtime_qualified(self):
|
||||
self.manifest['executionScope']=scope()
|
||||
cp=self.manifest['stages'][0]['checkpoints'][0]
|
||||
cp['approvalsAfter']=['candidate-specific-cleanup-scope']
|
||||
cp['checks'][0].update(adapter='assessment-evidence-inspection',configured=True,receiptSha256='a'*64)
|
||||
with self.assertRaisesRegex(c.GateError,'cannot qualify'):
|
||||
c.validate_manifest(self.manifest)
|
||||
|
||||
def test_inspection_requires_explicit_false_and_never_launches_worker(self):
|
||||
self.manifest['executionScope']=scope()
|
||||
cp=self.manifest['stages'][0]['checkpoints'][0]
|
||||
cp['approvalsAfter']=['candidate-specific-cleanup-scope']
|
||||
cp['checks'][0].update(adapter='assessment-evidence-inspection',receiptSha256='a'*64)
|
||||
cp['checks'][0].pop('configured',None)
|
||||
with self.assertRaisesRegex(c.GateError,'cannot qualify'):
|
||||
c.validate_manifest(self.manifest)
|
||||
state=self.engine.store.read()
|
||||
with self.assertRaisesRegex(c.GateError,'adapters must be configured'):
|
||||
self.engine.checkpoint(self.manifest['stages'][0],cp,state['stages']['PHASE-10'],state)
|
||||
self.assertEqual(0,self.runner.calls)
|
||||
|
||||
def test_native_isolation_failure_stops_before_additional_probe(self):
|
||||
import qualify_assessment
|
||||
policy=copy.deepcopy(self.manifest)
|
||||
policy['executionEnabled']=False
|
||||
c.atomic(self.control/'backlog.json',policy)
|
||||
c.atomic(self.control/'proposed-p10-assessment.json',policy)
|
||||
c.atomic(self.control/'config.json',self.config)
|
||||
report=self.root/'failed-native.json'
|
||||
with mock.patch.object(qualify_assessment.local_mode,'qualify',return_value={'status':'FAIL'}), \
|
||||
mock.patch.object(sys,'argv',['qualify',str(self.control),str(self.repo),str(report)]):
|
||||
with self.assertRaisesRegex(c.GateError,'no further child'):
|
||||
qualify_assessment.main()
|
||||
self.assertEqual('NOT_RUN',c.read_json(report)['assessment'])
|
||||
self.assertFalse((self.repo/'.artifacts').exists())
|
||||
|
||||
def test_entry_cannot_prepare_approvals_without_composite_adapter(self):
|
||||
self.manifest['executionScope']={**scope(),'handoffCommit':self.manifest['baselineCommit'],'handoffBranch':'codex/base'}
|
||||
cp=self.manifest['stages'][0]['checkpoints'][0]
|
||||
cp['approvalsAfter']=['candidate-specific-cleanup-scope']
|
||||
cp['checks']=[]
|
||||
c.atomic(self.control/'backlog.json',self.manifest)
|
||||
c.atomic(self.control/'config.json',self.config)
|
||||
with mock.patch.object(c,'Engine',return_value=self.engine), \
|
||||
mock.patch.object(local_mode,'require_unattended'):
|
||||
with self.assertRaisesRegex(c.GateError,'composite adapter is not qualified'):
|
||||
c.main(['prepare-entry','--control-root',str(self.control)])
|
||||
self.assertEqual(0,self.runner.calls)
|
||||
self.assertFalse((self.control/'requests').exists())
|
||||
|
||||
|
||||
class ReadOnlyProfileTests(unittest.TestCase):
|
||||
def test_assessment_profile_keeps_product_read_only(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root=Path(directory)
|
||||
config=dict(repository=str(root/'worker'), workerTemp=str(root/'temp'),
|
||||
workerCodeXHome=str(root/'control/auth'), normalCodeXHome=str(root/'normal'),
|
||||
assessmentReportDirectory='docs/automation/stages/PHASE-10')
|
||||
rules=local_mode.filesystem_rules(root/'control',config)
|
||||
self.assertEqual('read',rules[(root/'worker').resolve().as_posix()])
|
||||
self.assertEqual('write',rules[(root/'worker/docs/automation/stages/PHASE-10').resolve().as_posix()])
|
||||
|
||||
|
||||
class ReceiptTests(unittest.TestCase):
|
||||
def test_receipt_rejects_changed_product_or_changed_receipt(self):
|
||||
import assessment_adapter as adapter
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
repo=Path(directory)
|
||||
(repo/'product.txt').write_text('accepted synthetic source')
|
||||
receipt=dict(schemaVersion=1, scope='P10-00-ASSESSMENT-ONLY',
|
||||
integrity='PASS', files={'product.txt':c.sha_file(repo/'product.txt')},
|
||||
nativeReports=[dict(id='synthetic',status='PASS',exitCode=0,
|
||||
membershipSha256='a'*64)], retainedDecisions={'production':'NO-GO'})
|
||||
path=repo/'receipt.json'
|
||||
c.atomic(path,receipt)
|
||||
pin=c.sha_file(path)
|
||||
self.assertEqual('REUSED_SOURCE_BOUND_EVIDENCE',adapter.verify(repo,path,pin)['kind'])
|
||||
(repo/'product.txt').write_text('changed')
|
||||
with self.assertRaises(ValueError):
|
||||
adapter.verify(repo,path,pin)
|
||||
with self.assertRaises(ValueError):
|
||||
adapter.verify(repo,path,'0'*64)
|
||||
@@ -0,0 +1,80 @@
|
||||
# AUTO-ACTIVATION-01 — blocked before activation
|
||||
|
||||
30 October 2026. **P10-00 execution remains NO-GO.** The command-running composite application adapter is not qualified inside the existing security boundary. The queue remains disabled and operator-controlled. No autonomous/model session, owner signature, Phase 10 assessment, product edit, migration, main merge or deployment was performed.
|
||||
|
||||
## What is verified
|
||||
|
||||
| Deliverable | Result |
|
||||
|---|---|
|
||||
| Composite application adapter | **BLOCKED.** Existing command-running provider/browser/Python/PDF/security/preservation adapter remains `configured:false`. The new evidence-inspection utility is deliberately prevented from being marked runtime-qualified. |
|
||||
| Real application evidence | Existing published handoff verifier passes. Its 34 native report records, exact process exits, original test membership, source bindings and conditional decisions are preserved. A receipt freezes 3,483 non-envelope source files. **These are reused executions, not newly run product suites.** |
|
||||
| Controller tests | 81 distinct tests pass, zero failures/errors/skips, source stable. Includes the original 69, twelve assessment tests, cryptographic approval/resume, missing-approval refusal and disposable-Git protected-stop simulation. Do not add focused reruns to 81. |
|
||||
| Native sandbox | Current installed real-key/files/network canaries PASS. Separately tested projected assessment profile: source read succeeds, source write/delete fail, report creation succeeds; frozen-source/evidence inspection succeeds inside MXC. |
|
||||
| Authentication | Installed `auth-status` reports `CHATGPT_SIGNED_IN`. No tokens copied/read; account identity and a live model request were not verified. |
|
||||
| Owner signing | Existing nonexportable key retained. Pinned public certificate matches, host signing positive control succeeds and worker access to that actual key is denied. No replacement owner key or owner approval was created. |
|
||||
| Exact product baseline | `fc5b9d377199ce51f480a920a3772108094929e3`, published as the direct parent of `5cb903b989742b7a96cb22ddf4c0e24993b18980`. |
|
||||
| Published handoff | Fresh remote `test/pre-phase10-ux-acceptance` equals `5cb903b989742b7a96cb22ddf4c0e24993b18980`. Parent/envelope changes are confined to the handoff directory. No baseline substitution. |
|
||||
| Execution checkout | `C:\Users\Connor\.codex\worktrees\p10-assessment\Job tracker`, branch `codex/p10-assessment-ready`, exact baseline and clean Git index/worktree. 47 checkout line-ending differences were materialised as exact accepted bytes; each Git blob was checked identical before refreshing index stat information. |
|
||||
| Policy | Disabled proposal prepared, preserving all seven stages/order. P10-00 scope is explicitly assessment-only with protected `candidate-specific-cleanup-scope` stop. Latest handoff decisions are pinned supplemental prompt content. |
|
||||
| P10-01 | Hard rejected outside the P10-00 execution cap, even with generic synthetic approvals. Its implementation adapter remains unconfigured. A new exact owner-controlled implementation policy and runtime qualification are required. |
|
||||
| Actual state | Zero roadmap sessions, no recovery needed, no scheduling. Existing installed repository and baseline policy remain unchanged; the clean execution checkout has **not** been selected or activated. |
|
||||
|
||||
## The remaining runtime blocker
|
||||
|
||||
Native probes used the existing MXC policy without widening permissions:
|
||||
|
||||
- Docker cannot read the owner's Docker configuration or access `docker_engine`. Giving an autonomous worker the general host Docker socket would undermine the isolation boundary.
|
||||
- The accepted user-local `.NET` executable cannot resolve its executable under this profile. The separately installed system `.NET` runtime starts but reports **no SDKs**; that is not a passing backend test run.
|
||||
- The accepted Docling virtual environment, outside the worker checkout, cannot resolve `pyvenv.cfg` under the current profile. Starting the normal isolated Python interpreter is not Docling qualification.
|
||||
- Worker network access, including loopback, remains denied. Real browser/API/MariaDB test services have not been qualified through a bounded isolated executor.
|
||||
|
||||
The accepted command catalogue includes the actual backend/frontend/build/browser/MariaDB/Python/Docling/CV/PDF commands and their native exits/membership. It is an inventory of previous executions, **not an installed test runtime**. Security, human/device, hosted CI, model and production limitations remain separate and unchanged. Existing PDF/S01 acceptance was not reopened.
|
||||
|
||||
Completing AUTO-ADAPTER-01 requires a qualified test executor that can run these exact applicable commands against disposable services without exposing the owner's general Docker socket, files, signing key or production resources to the worker. That executor is **not implemented/qualified by this change**. Do not turn `configured` on, approve evidence reuse as a substitute, or run `enable-unattended`, `start` or `resume` to bypass it. A second account, subscription or VM has not been prescribed.
|
||||
|
||||
## Prepared policy and protected requests
|
||||
|
||||
Installed proposal:
|
||||
|
||||
```text
|
||||
C:\Users\Connor\.jobjakt-backlog\single-user-local\proposed-p10-assessment.json
|
||||
```
|
||||
|
||||
Review copy: `automation/qualification/p10-assessment.policy.json`.
|
||||
|
||||
The controller has generated one genuine **unsigned disabled** `manifest-policy-update` request:
|
||||
|
||||
```text
|
||||
C:\Users\Connor\.jobjakt-backlog\single-user-local\requests\d637caf72c3fed5910f7c28e75dc82324d8febc12597ddb5627ae022af49a622.json
|
||||
```
|
||||
|
||||
It binds the proposed manifest hash. Its baseline field describes the **currently installed old policy**, not acceptance of that old product baseline. The proposed manifest names the correct `fc5b9d3` baseline and separate `5cb903b` envelope. Even approving/adopting this proposal would leave execution disabled and adapters unqualified. **No owner action on this request is necessary while the runtime blocker remains.**
|
||||
|
||||
Environment, roadmap, Phase 10 entry and restricted-execution requests were not made eligible or signed. Generating misleading eligible requests before mandatory adapter qualification would bypass the handoff gate. Valid future approvals must bind the final installed policy/code/configuration, correct execution checkout, baseline/envelope, full prompt hashes, qualified runtime reports and exact scope. Changing those inputs invalidates applicability.
|
||||
|
||||
The `prepare-entry` command is implemented to prepare successive protected entry requests without launching a worker. It requires an adopted enabled restricted policy, fresh qualified runtime, clean exact baseline and configured adapters. It refuses the current disabled queue with native exit 2 and zero sessions. The RTK wrapper may map nonzero child exits to 1.
|
||||
|
||||
When the actual runtime blocker is resolved, the existing protected sequence remains: configure the genuinely qualified final manifest, owner `approve`, configure the same exact file, refresh qualification after real config changes, owner `enable-unattended`, then `prepare-entry` / owner `approve` until the exact grants are verified. Each grant authorises only its requested scope: environment evidence; exact capped policy; Phase 10 entry; restricted P10-00 assessment. The owner then deliberately invokes `start`. **There is no safe activation command for the present unqualified manifest.**
|
||||
|
||||
## Commands safe to run now
|
||||
|
||||
```powershell
|
||||
. "$HOME\.jobjakt-backlog\single-user-local\Use-JobjaktBacklog.ps1"
|
||||
Jobjakt status
|
||||
Jobjakt auth-status
|
||||
Jobjakt dry-run
|
||||
```
|
||||
|
||||
`dry-run` should still report execution disabled and the unqualified composite adapter. The existing owner key/authentication setup needs no repetition. The refreshed qualification is valid only for its recorded code/config binding and 24-hour window; retargeting to the execution checkout or adopting the restricted profile needs the applicable requalification.
|
||||
|
||||
## Evidence
|
||||
|
||||
- `automation/reports/activation/results.json` and `tests.txt`: 81 current tests and exact source hashes.
|
||||
- `automation/reports/activation-native.json`: real restricted-profile/key/source/evidence probes.
|
||||
- `automation/reports/activation/control-state.json`: disabled queue, zero sessions, public pin and current installed binding checks.
|
||||
- `automation/reports/activation/entry-rejection.json`: actual no-execution entry refusal.
|
||||
- `automation/reports/activation/accepted-command-catalog.json`: prior genuine command/process/report identities; no new product execution claim.
|
||||
- `automation/reports/activation/tool-probes.json`: actual runtime capability failures.
|
||||
- `automation/reports/activation/schema-validation.json`: manifest constraints checked with existing Ajv; Draft 2020 meta-schema validation was unavailable. Runtime manifest validation also passes.
|
||||
|
||||
Earlier failed development-test attempts were corrected; the final 81-test run supersedes them. Historical product totals and original PDF oracle failures remain intact. No new PDF export, model inference, provider suite, human/device check, hosted CI or production qualification was performed.
|
||||
@@ -0,0 +1,15 @@
|
||||
# AUTO-ACTIVATION-01
|
||||
|
||||
Implement the owner's 30 October activation request in the existing isolated tooling worktree. Stop before execution or signing. Preserve the existing key/authentication, seven workstream order and all later gates.
|
||||
|
||||
- [x] Resolve published handoff 5cb903b and its direct parent candidate fc5b9d3; keep their identities separate.
|
||||
- [x] Check existing authentication and native real-key isolation. Docker pipe access is correctly denied; no system .NET SDK. Do not widen service access.
|
||||
- [x] Inspect source-bound evidence using the existing handoff verifier, exact native report membership and frozen source hashes. Retain conditional/security/human dispositions. Evidence inspection is explicitly prevented from substituting for the mandatory command-running composite adapter.
|
||||
- [x] Add a restricted P10-00 policy, report-only filesystem permissions and independent write-set verification. Reject P10-01 regardless of a generic roadmap signature. Test missing approvals and the protected stop using disposable Git remotes.
|
||||
- [x] Prepare a clean execution checkout at fc5b9d3, exact unsigned disabled-policy request and an owner command for preparing successive eligible entry requests without launching a worker.
|
||||
- [x] Run existing controller tests, new adapter/scope tests and native assessment probes; preserve their distinct evidence. Document remaining owner actions.
|
||||
- [ ] AUTO-ADAPTER-01 remains BLOCKED: command-running application/service runtime not isolated and qualified. Stop before activation; do not expose general Docker/socket or widen worker access to bypass this barrier.
|
||||
|
||||
Ruling: the evidence-reuse inspector cannot satisfy the owner's explicit real composite runtime gate. Keep proposed and installed policies disabled, retain configured:false, and do not generate eligible environment/entry grants. The 81-test final run and native probes qualify control-plane changes only, not actual roadmap readiness. See ACTIVATION_01.md.
|
||||
|
||||
Changes reach controller and installed packaging, local filesystem policy, manifest schema, evidence adapter, PowerShell command dispatch, tests and operator documentation. Product files, existing acceptance evidence and the signing key remain outside the change scope. The adapter uses the existing trusted handoff verifier to prepare a receipt, then verifies exact immutable evidence/source bytes; this avoids a second report parser and unnecessary product reruns. Fresh provider runtime qualification remains a separate barrier to implementation.
|
||||
@@ -1,5 +1,7 @@
|
||||
# Run from your existing Windows and ChatGPT account
|
||||
|
||||
**30 October 2026:** see [current activation status](ACTIVATION_01.md) before activation. Existing authentication and owner-key setup are verified; do not repeat them. The real composite runtime remains unqualified and the queue remains disabled.
|
||||
|
||||
`single-user-local` removes the second Windows-account/VM requirement. It keeps the original queue, signed decisions, Git guards, recovery, locking and finite resource budgets. **Phase 10 and the actual roadmap remain disabled.** The current accepted development baseline is still an explicit policy decision, not something setup infers from main.
|
||||
|
||||
## Setup once
|
||||
@@ -104,4 +106,4 @@ It uses your existing login, limited privileges, logon plus 15-minute continuati
|
||||
|
||||
Real canaries verify the tested native-worker boundaries. They are not an exhaustive proof against every Windows API, credential provider or future sandbox defect. Unrestricted applications running under your same Windows identity are outside the worker boundary and can still act as you. This is not equivalent to an independently administered account/VM. Apps, plugins, MCP and web search remain disabled in this mode; a later security scan/provider task needing them must pause for independently qualified capabilities, not silently enable your normal configuration.
|
||||
|
||||
At handoff: setup and canary qualification have been exercised under the current login; the dedicated home is **not signed in**, the real owner key is **not configured**, operation is **operator-controlled**, and the queue is **disabled**. See [acceptance and evidence](SINGLE_USER_ACCEPTANCE.md). Your next steps are the same-account `auth`, operator `key`, and real-key `qualify` commands; actual roadmap activation still waits for formal Phase 10 entry.
|
||||
The initial AUTO-02 [acceptance snapshot](SINGLE_USER_ACCEPTANCE.md) predates the owner's sign-in and real-key setup. Those missing-auth/key statements are historical; [ACTIVATION_01.md](ACTIVATION_01.md) records the verified current setup and remaining runtime blocker. Operation is still operator-controlled and the queue is disabled.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Autonomous roadmap follow-up
|
||||
|
||||
30 October AUTO-ACTIVATION-01: pre-Phase-10 preparation is conditionally closed at published handoff `5cb903b`; proposed product baseline remains its parent `fc5b9d3`. Existing owner key/authentication and real-key isolation are verified. **Activation remains blocked by the real command-running composite runtime.** See [ACTIVATION_01.md](ACTIVATION_01.md) for current evidence and safe commands; the historical provisioning bullets below do not supersede that report.
|
||||
|
||||
AUTONOMOUS-ROADMAP-02 adds the normal-login route in [SINGLE_USER.md](SINGLE_USER.md). Single-user setup and native canary qualification are implemented; the real owner key, same-account sign-in and Phase 10 authorization remain owner actions. The separate-account provisioning below is now optional; single-user MXC qualification is the alternative.
|
||||
|
||||
Implementation checkpoints AUTO-00–08 are complete in the qualified scope described in ACCEPTANCE.md. The following are deliberately **not authorized/completed** by installing the controller:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#Requires -Version 7.2
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)][ValidateSet('init','validate','dry-run','start','resume','status','pause','retry','current','logs','recover','configure','setup','auth','auth-status','qualify','approve','prepare','key','enable-unattended')][string]$Command,
|
||||
[Parameter(Mandatory)][ValidateSet('init','validate','dry-run','start','resume','status','pause','retry','current','logs','recover','configure','setup','auth','auth-status','qualify','approve','prepare','prepare-entry','key','enable-unattended')][string]$Command,
|
||||
[string]$ControlRoot,
|
||||
[string]$Repository,
|
||||
[string]$Stage,
|
||||
|
||||
Reference in New Issue
Block a user