Why: the 8788 project-member runtime delivery finished (code merged via #526/#530) but its card and operation records lived only on an unpushed task branch, leaving the worktree impossible to recycle and the record invisible on main. Evidence: card status "已完成(运行目标)" on feature/project-member-runtime 835d807; branch diff against merge-base touches only .task (card + 29 operation records); credential scan finds only variable-based Authorization headers and sha256 digests. Impact: adds .task/archive/feature-project-member-runtime.md and .task/artifacts/project-member-runtime/**; no code, gitlink or policy change. Refs: worktree cleanup 2026-10-18 Co-authored-by: Cursor <cursoragent@cursor.com>
35 lines
2.6 KiB
Python
35 lines
2.6 KiB
Python
import json,os,re,sqlite3,subprocess,urllib.request,urllib.error
|
|
from pathlib import Path
|
|
W=Path('/tmp/project-member-runtime-8788-20261013');DATA=Path('/home/weichen/projects/Agent-System-Dev/runtime/dev/data')
|
|
ADMIN=Path('/home/weichen/projects/Agent-System-RDM-wt-feature-project-member-access/bin/agent-system-admin')
|
|
NAME='project-member-runtime-20261013';HOST='prj_8d20020e5b79';BASE='http://127.0.0.1:8788'
|
|
assert (W/'backup-proof.json').is_file()
|
|
assert not (W/'credentials.json').exists(),'credentials already created; reconcile before retry'
|
|
with sqlite3.connect('file:'+str(DATA/'agent-system.db')+'?mode=ro',uri=True) as c:
|
|
assert c.execute('select max(version) from schema_migrations').fetchone()[0]==30
|
|
assert c.execute('select count(*) from api_keys where name=? and revoked=0',(NAME,)).fetchone()[0]==0
|
|
assert c.execute("select count(*) from users where owner in ('weichen','test')").fetchone()[0]==2
|
|
creds={};p=W/'credentials.json'
|
|
env=dict(os.environ);env.pop('AGENT_SYSTEM_DSN',None)
|
|
for owner in ['weichen','test']:
|
|
r=subprocess.run([str(ADMIN),'apikey','create','--owner',owner,'--name',NAME,'--data',str(DATA)],capture_output=True,text=True,env=env)
|
|
assert r.returncode==0, 'temporary credential creation failed'
|
|
key=r.stdout.strip();assert re.fullmatch(r'ask_[A-Za-z0-9_-]+',key),'unexpected credential format'
|
|
creds[owner]=key;p.write_text(json.dumps(creds)+'\n');p.chmod(0o600)
|
|
def req(owner,path,method='GET',body=None):
|
|
r=urllib.request.Request(BASE+path,method=method,data=None if body is None else json.dumps(body).encode(),headers={'Authorization':'Bearer '+creds[owner],'Content-Type':'application/json','Origin':BASE})
|
|
try:
|
|
with urllib.request.urlopen(r,timeout=20) as x:return x.status,json.load(x)
|
|
except urllib.error.HTTPError as e:return e.code,json.loads(e.read())
|
|
result={}
|
|
for owner in creds:
|
|
status,me=req(owner,'/api/auth/me');assert status==200 and me['owner']==owner
|
|
status,projects=req(owner,'/api/projects');assert status==200
|
|
result[owner]={'authenticated_as':me['owner'],'visible_project_ids':[x['id'] for x in projects]}
|
|
assert HOST in result['weichen']['visible_project_ids'] and HOST not in result['test']['visible_project_ids']
|
|
status,_=req('test','/api/projects/'+HOST+'/conversations','POST',{'title':'membership pre-deploy denial probe'})
|
|
assert status in (403,404),'old member create unexpectedly admitted; reconcile new test data'
|
|
result['member_create_before_status']=status
|
|
(W/'before-http.json').write_text(json.dumps(result,ensure_ascii=False,indent=2)+'\n')
|
|
print(json.dumps({'source':'SELF','temporary_credentials':'created for existing weichen/test; values protected; passwords unchanged','before':result},ensure_ascii=False))
|