프로그램을 회사에 맞추고 계속 고치려면 바뀌는 값뿐 아니라 유지해야 할 약속도 정해야 한다. 나는 설정, 자료 변환, 오류 기록, 호환성, 수정안 검토와 인계를 작은 순수 함수로 나눈 독립 합성 모듈을 만들었다.
모든 입력은 고정 예제이며 실제 업체의 설정과 자료를 사용하지 않는다. 아래 실행 기록은 실제 로컬 예제에서 저장한 결과다. 데이터베이스 변경, 외부 AI 호출, 운영 배포와 실제 복구는 수행하지 않는다.
설정과 변환은 먼저 검사할 제안이다
configuration은 문법 버전 1과 회사 ID, 필드 목록, 기본 수량만 받는다. 품목과 수량은 필수이고 메모만 선택이다. 필수 필드 제거, 중복과 모르는 키는 거절한다. 반환값은 복사본이며 실제 동적 화면이나 회사별 설정 저장소는 없다. 허용된 차이를 표현하는 계약과 화면을 그리는 기능을 구분했다.
migration_preview는 합성 v1의 qty를 v2의 quantity로 옮기고 draft 상태를 추가한다. 모든 행을 먼저 검증하며 하나라도 잘못되면 부분 제안을 내지 않는다. 원본은 바꾸지 않고 applied는 거짓이다. 이미 v2라면 검증한 복사본을 돌려준다. 실제 자료의 기본 상태를 추측하는 도구도, 운영 DB를 변환하는 실행기도 아니다.
진단과 호환성도 허용 범위를 명시한다
error_record는 고정 오류 코드와 작업 이름, 제한된 추적 ID만 받는다. 예외 문장과 요청 본문을 넘기는 통로가 없다. 실제 로그 수집기나 보관 정책은 아니며 다른 계층의 출력까지 검사하지 않는다. 식별자 형식이 맞더라도 호출자가 비밀의 의미를 넣을 수 있으므로 실제 서버가 생성한 불투명한 값과 호출 규칙이 필요하다.
compatibility는 릴리스가 읽는 스키마 목록과 쓰는 스키마를 자료 버전과 비교한다. 읽기만 가능한 경우와 쓰기까지 가능한 경우를 구분하고 지원하지 않으면 막는다. 계약에 적힌 사실을 계산할 뿐 실행 코드의 호환성을 실측하지는 않는다. 운영 배포, 데이터 역변환과 실제 롤백을 수행하지 않는 미리보기라는 경계를 결과에도 남겼다.
수정안은 내용과 기준 버전에 묶는다
proposal은 수량과 메모의 허용된 변경만 받는다. 회사 범위, 기대 revision과 작성자, 전후 차이를 내용 지문에 묶는다. review는 다른 검토자와 정해진 역할을 요구하고 현재 기준에서 제안을 다시 계산해 비교한다. apply_reviewed도 같은 기준을 재확인한 뒤 새 값을 계산한다. 제안 뒤 상태나 내용이 바뀌면 예전 검토 결과를 적용할 수 없다.
이것은 인증 시스템이 아니다. 역할과 검토 영수증은 신뢰할 서버 문맥에서 받아야 하며 클라이언트가 주장한 값을 그대로 쓰면 안 된다. 해시도 서명이 아니다. 함수는 순수 계산이고 영구 승인 기록이나 동시 거래 잠금이 없다. 실제 서비스는 서버의 권한 검사와 조건부 저장 거래를 연결해야 한다. 예제에 AI라는 작성자 문자열이 있어도 외부 모델을 호출하지 않는다.
코드와 구현 살펴보기
독립 합성 전체 구현
"""Synthetic preview contracts; callers supply trusted identities and reads."""
from copy import deepcopy
from hashlib import sha256
import json,re
def integer(value,minimum=1,maximum=99):
if type(value) is not int or not minimum<=value<=maximum:raise ValueError('integer')
return value
def identifier(value):
if not isinstance(value,str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,40}',value):raise ValueError('identifier')
return value
def configuration(value):
if not isinstance(value,dict) or set(value)!={'version','company_id','fields','default_quantity'}:raise ValueError('configuration keys')
if type(value['version']) is not int or value['version']!=1:raise ValueError('configuration version')
identifier(value['company_id']);integer(value['default_quantity'])
fields=value['fields']
if not isinstance(fields,list) or any(x not in ('item','quantity','memo') for x in fields) or len(fields)!=len(set(fields)) or not {'item','quantity'}<=set(fields):raise ValueError('required or unknown fields')
return deepcopy(value)
def validate_row(row,version):
keys={'id','qty'} if version==1 else {'id','quantity','status'}
if not isinstance(row,dict) or set(row)!=keys:raise ValueError('row keys')
identifier(row['id']);integer(row['qty'] if version==1 else row['quantity'])
if version==2 and row['status'] not in ('draft','sent'):raise ValueError('row status')
def migration_preview(document):
if not isinstance(document,dict) or set(document)!={'schema','rows'}:raise ValueError('document keys')
version=document['schema']
if type(version) is not int or version not in (1,2):raise ValueError('unsupported schema')
rows=document['rows']
if not isinstance(rows,list) or len(rows)>100:raise ValueError('rows')
for row in rows:validate_row(row,version)
if len({r['id'] for r in rows})!=len(rows):raise ValueError('duplicate id')
result=deepcopy(document) if version==2 else {'schema':2,'rows':[{'id':r['id'],'quantity':r['qty'],'status':'draft'} for r in rows]}
return {'from_schema':version,'to_schema':2,'changed':version!=2,'proposed':result,'applied':False}
def error_record(code,operation,trace_id):
if code not in ('INVALID_INPUT','VERSION_CONFLICT','ACCESS_DENIED','UNEXPECTED'):raise ValueError('error code')
if operation not in ('read','save','preview'):raise ValueError('operation')
return {'code':code,'operation':operation,'trace_id':identifier(trace_id),'retry':code=='UNEXPECTED'}
def compatibility(code,data_schema):
if not isinstance(code,dict) or set(code)!={'release','reads','writes'}:raise ValueError('release keys')
identifier(code['release']);integer(data_schema,1,2);integer(code['writes'],1,2)
reads=code['reads']
if not isinstance(reads,list) or not reads or any(type(x) is not int or x not in (1,2) for x in reads) or len(reads)!=len(set(reads)) or code['writes'] not in reads:raise ValueError('read schemas')
readable=data_schema in reads
# A writer of another schema is not permitted to overwrite this data.
writable=readable and data_schema==code['writes']
return {'release':code['release'],'readable':readable,'writable':writable,'mode':'read_write' if writable else ('read_only' if readable else 'blocked'),'executed':False}
def validate_state(state):
if not isinstance(state,dict) or set(state)!={'company_id','revision','quantity','memo'}:raise ValueError('state keys')
identifier(state['company_id']);integer(state['revision'],1,1000000);integer(state['quantity'])
if not isinstance(state['memo'],str) or not re.fullmatch(r'[A-Za-z0-9 _-]{0,40}',state['memo']):raise ValueError('memo')
def proposal(state,changes,author):
validate_state(state);identifier(author)
if not isinstance(changes,dict) or not changes or not set(changes)<={'quantity','memo'}:raise ValueError('change fields')
after={**state,**changes};validate_state(after)
diff={k:{'before':state[k],'after':changes[k]} for k in sorted(changes) if changes[k]!=state[k]}
if not diff:raise ValueError('no change')
payload={'company_id':state['company_id'],'expected_revision':state['revision'],'author':author,'diff':diff}
digest=sha256(json.dumps(payload,sort_keys=True,separators=(',',':')).encode()).hexdigest()
return {**payload,'fingerprint':digest}
def review(state,proposed,reviewer,role):
identifier(reviewer)
if role!='reviewer' or reviewer==proposed['author']:raise ValueError('review role')
expected=proposal(state,{k:v['after'] for k,v in proposed['diff'].items()},proposed['author'])
if proposed!=expected:raise ValueError('changed proposal or base')
return {'fingerprint':expected['fingerprint'],'reviewer':reviewer,'approved':True}
def apply_reviewed(state,proposed,receipt):
# Receipt must come from a trusted review service, not a client-submitted claim.
if not isinstance(receipt,dict) or set(receipt)!={'fingerprint','reviewer','approved'} or receipt['approved'] is not True:raise ValueError('review receipt')
expected=review(state,proposed,receipt['reviewer'],'reviewer')
if receipt!=expected:raise ValueError('receipt mismatch')
out=deepcopy(state)
for field,change in proposed['diff'].items():out[field]=change['after']
out['revision']=integer(out['revision']+1,1,1000000);return out
def handoff_manifest(revision,files,items):
integer(revision,1,1000000)
if not isinstance(files,dict) or not files or len(files)>20:raise ValueError('files')
for name,digest in files.items():
if not isinstance(name,str) or not re.fullmatch(r'[A-Za-z0-9_-]{1,40}\.(?:py|json|txt)',name) or not isinstance(digest,str) or not re.fullmatch(r'[a-f0-9]{64}',digest):raise ValueError('file hash')
if not isinstance(items,list) or not items or len(items)>100:raise ValueError('items')
ids=[]
for item in items:
if not isinstance(item,dict) or set(item)!={'id','status','evidence'}:raise ValueError('item keys')
ids.append(identifier(item['id']))
if item['status'] not in ('todo','done'):raise ValueError('item status')
if not isinstance(item['evidence'],list) or any(x not in files for x in item['evidence']) or (item['status']=='done' and not item['evidence']):raise ValueError('completion evidence')
if len(ids)!=len(set(ids)):raise ValueError('item duplicate')
return {'schema':1,'expected_revision':revision,'files':deepcopy(files),'items':deepcopy(items)}
def verify_handoff(manifest,current_revision,contents):
integer(current_revision,1,1000000)
rebuilt=handoff_manifest(manifest['expected_revision'],manifest['files'],manifest['items'])
if manifest!=rebuilt or current_revision!=manifest['expected_revision']:raise ValueError('state changed')
for name,digest in manifest['files'].items():
if name not in contents or not isinstance(contents[name],bytes) or sha256(contents[name]).hexdigest()!=digest:raise ValueError('artifact changed')
return {'verified':True,'done':sum(x['status']=='done' for x in manifest['items']),'remaining':[x['id'] for x in manifest['items'] if x['status']=='todo'],'executed':False}인계와 실행 증거를 값으로 확인한다
handoff_manifest는 제한된 파일 이름과 SHA-256, 기대 자료 revision, 작업 ID와 상태를 담는다. done에는 목록에 있는 증거 파일이 필요하고 todo는 남은 일로 반환한다. verify_handoff는 전달받은 바이트와 해시, 현재 revision을 비교한다. 실제 파일 시스템을 스스로 찾아 최신 상태를 읽거나 문서의 지시를 실행하지 않는다.
로컬 테스트 18개가 통과했고 패키지를 새 폴더에 풀어 다시 실행했다. 기록 선택기는 설정, 변환, 오류, 호환성, 수정안과 인계를 보여 준다. 증거가 존재하는 것과 그 내용이 충분한지는 다르다. 신뢰할 인계 출처와 실제 완료 판단은 사람이 검토해야 한다. 이 모듈은 증거의 형식과 바이트 일치를 확인할 뿐 원래 주장의 진실성을 인증하지 않는다.
운영 경계를 붙인 뒤에 재사용한다
실무에 연결할 때는 회사 범위와 사용자 역할의 출처부터 정해야 한다. 미리보기에서 적용으로 넘어가는 순간에는 현재 자료를 다시 읽고 같은 버전인지 거래 안에서 확인해야 한다. 파일 해시를 비교하는 쪽도 검증할 원본과 기준 문서를 신뢰할 수 있어야 한다. 아무 곳에서 받은 영수증과 지문을 보안 권한처럼 사용하면 안 된다.
ZIP에는 전체 구현, 테스트, 실행기, 고정 결과와 안내가 있다. 업체 자료가 없는 독립 합성 코드이며 수정과 재사용을 허용한다. 별도로 네 모듈을 연결한 회귀 검사에서는 자료 변환 뒤 코드 호환성과 검토 충돌 뒤 초안 보존도 확인했다. 나는 각 기능을 많이 나열하기보다 무엇이 제안이고 무엇이 확인이며 무엇이 실제 실행인지 분명한 계약을 남기고 싶었다.