P22-02: 素材契約と権利(原条件全体、記録だけの完了代用なし)

This commit is contained in:
Agent Control Runner
2026-10-13 18:12:34 +09:00
parent 10c62e1fa7
commit c6ccc5a6ab
6 changed files with 286 additions and 0 deletions
@@ -0,0 +1,19 @@
# P22-02 素材契約(原成果未完了)
原30パッケージ/88成果・部分点なしを維持する。614 identityは360施設、176人物、2状態群、76その他であり、614画像ではない。495 candidate、119 missing、19 sourceを全て保持する。選定済完成資産は0。D08/D09の最終内容・数量はunknown。
asset-contracts.jsonは公開JSON共有参照(d/l/s/r)で、accepted_inputの全フィールドとcurrent契約の全フィールドを損失なく保持する。各identityの命名はid、候補名はcandidate_refの実path、形式と現raw SHAはcandidate_pool、historical適用性はcandidate_refs、creatorと不足は各契約にある。差替鎖は歴史的representation→現candidate→未選定replacement。missingをcode/file/fallbackで充足しない。最終の解像度・frame/layout・命名・差替参照は未確定として扱う。
Kenney Tiny Town/Tiny Dungeon/Interface Soundsのpublisher/member/license/manifest証拠はevidence_pinsの文書を完全再利用する。配布元URLとCC0記録はcandidate_poolおよび既存ライセンス証拠を参照。内部BGM合成recipeは内部由来であり、第三者CC0や最終聴感・権利受入へ昇格しない。自己制作・別許諾を排除しない。未知creator/rightsを推測しない。
checkerの通常0終了は記録整合性のみ。--require-readyは原条件未達なら2終了。READYには空でない完成資産、全exact useの選定・実利用・権利、未制作/開示不足の解消、必要な画面/聴感、Root静的検証と意味のあるnegative、独立Opusレビュー、統合・最終受入が必要。workerは正式試験も受入も主張しない。
## 次の製品変更提案(この実装では適用しない)
- scripts/ui/pixel_tower_art.gd、standard_room_art.gd、infirmary_room_art.gd: 各施設Lv1–3/表裏/外観内装のidから選定representationへ明示対応を追加。共有atlasや専用一部装飾を全施設に流用して完成扱いしない。最小差分は選定されたidの参照と該当描画分岐のみ。実画面で各対応と未制作開示を確認する。
- scripts/ui/pixel_people.gd: character条件ごとの選定と必要variant参照を明示し、2状態群は状態表現として接続する。数量は承認されるまでunknown。該当人物・状態の画面確認が必要。
- assets/themes/japanese_fantasy/visual-theme-v1.tres、scenes/main/world_preview.gd、scenes/main/main.gd: 世界/skin候補のcreator/rightsを解決してから選定素材への参照のみ差替。サイズ、alpha、atlas region、nearest/mipmapと全参照整合を確認し実表示で確認する。
- scripts/ui/paired_bgm.gdと2 candidate WAV: internal-origin-observationsのrecipeとbyte証拠を保持し、creator/権利の承認と両contextの聴感を取得してから最終選定。UI4音もpublisher証拠だけで最終聴感を代用しない。
- missing_candidateの119 identityは各契約のsource_anchors/usage_conditionを制作依頼に渡し、Kenney適合候補を先に確認する。最小asset/reference差分は選定後にのみ確定できる。未確定の絵や数量を捏造しない。
Runnerの検証対象: 全614 coverage、全decoded semantic hash、raw input/source/evidence pins、historical適用性、PNG寸法、参照・source omission、duplicate、cycle/bad index/key/type、unknown rights/false finished、数量変更。negativeはledgerの該当値の改変・削除、source/寸法・reference変更を含める。完全packetを500000 bytes未満で確認し、欠落・切捨てで縮小しない。その後に必要な製品実装・usability・独立レビュー・統合・finalを行う。
@@ -0,0 +1,19 @@
# Root context: fixed spec hash representation
This is coordination input, not a spec change or acceptance.
{
"task": "P22-02",
"base_commit": "10c62e1fa7b71576598583f434824b59816d4705",
"spec_ref": "docs/spec/acceptance/P22-02.json",
"contract_spec_sha256": "f252de3c5cff7ebfa62322ae2f2df09fb80a44bc59eb286b76d4acb4c12ca875",
"raw_spec_sha256": "c26c0b12cf7e7a7c46e671acde2e00762904732c1cd6381a53836e3d7b3f9824",
"runner_strip_spec_sha256": "f252de3c5cff7ebfa62322ae2f2df09fb80a44bc59eb286b76d4acb4c12ca875",
"raw_worktree_equals_base_git_blob": true,
"spec_changed": false,
"contract_changed": false,
"root_normalized_hash_verification_passed": true,
"reason": "Runner git helper uses stdout.strip(), then UTF8; source_pins and accepted input raw SHA remain raw bytes, not stripped"
}
Compare contract spec via git show base:spec, stdout.strip(), encode UTF8 and SHA256. Raw spec includes terminal newline and has a different raw-byte SHA. This is expected. It is not permission to edit/normalize any file. For source_pins and accepted input, use the full raw bytes as before. Read accepted input only with targeted Python. Do not block for a terminal newline difference when the Runner hash and exact base raw blob are both proven equal. Preserve all original conditions and current unmade gaps.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,75 @@
"""Build only the authorized ledger, preserving complete JSON fields."""
import hashlib
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[5]
HERE = Path(__file__).resolve().parent
INPUT = 'docs/spec/inputs/P22-02-accepted-usage.json'
CURRENT = 'docs/plans/original-completion-2026-10-02/checkpoints/p22-02-slot-contract-r02/all-slot-current-candidate-contracts.json'
EVIDENCE = 'docs/plans/original-completion-2026-10-02/checkpoints/p22-02-current-contract-r01'
def build():
keys, strings, pool = [], [], []
key_ids, string_ids, object_ids = {}, {}, {}
def key_id(key):
if key not in key_ids:
key_ids[key] = len(keys)
keys.append(key)
return key_ids[key]
def encode(value):
if isinstance(value, str):
if value not in string_ids:
string_ids[value] = len(strings)
strings.append(value)
return ['s', string_ids[value]]
if isinstance(value, (dict, list)):
signature = json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(',', ':'))
if signature in object_ids:
return ['r', object_ids[signature]]
if isinstance(value, dict):
node = ['d']
for key, item in value.items():
node.extend([key_id(key), encode(item)])
else:
node = ['l'] + [encode(item) for item in value]
index = len(pool)
pool.append(node)
object_ids[signature] = index
return ['r', index]
return value
pins = {}
for path in [ROOT / CURRENT, *sorted((ROOT / EVIDENCE).iterdir())]:
if path.is_file():
pins[str(path.relative_to(ROOT))] = hashlib.sha256(path.read_bytes()).hexdigest()
ledger = {
'accepted_input': json.loads((ROOT / INPUT).read_text()),
'current': json.loads((ROOT / CURRENT).read_text()),
'evidence_pins': pins,
'original_conditions': {
'all_exact_uses_selected_and_usable': False,
'all_creator_and_rights_evidence_accepted': False,
'all_naming_resolution_layout_replacement_references_accepted': False,
'all_mandatory_unmade_and_disclosure_gaps_resolved': False,
'applicable_display_and_hearing_evidence': False,
'independent_opus_review': False,
'root_static_and_meaningful_negative_validation': False,
'integration_and_final_original_30_88_acceptance': False,
},
'selection_contract': {
'selected_assets': [],
'candidate_names': 'Exact candidate_pool path names; no rename or final assignment',
'replacement_chain': 'Accepted historical representation -> current candidate_ref (prior applicability retained) -> selected replacement UNKNOWN',
'format': 'candidate_pool observed_format for physical media; representation_kind and exact raw source for code. Final per-use format/layout UNKNOWN.',
'rights': 'candidate_pool rights_provenance and complete pinned publisher/license/manifest evidence; internal recipe is origin only, not final rights or hearing acceptance',
'quantity': 'D08/D09 UNKNOWN. 2 state groups are not images. Never infer final file/frame/variant counts from 614 identities.',
},
}
files = encode(ledger)
packet = {'keys': keys, 'strings': strings, 'pool': pool, 'files': files, 'encoding': 'd=flat key-id/value; l=list; s=string index; r=complete object/list index'}
raw = json.dumps(packet, ensure_ascii=False, separators=(',', ':')).encode() + b'\n'
(HERE / 'asset-contracts.json').write_bytes(raw)
print(json.dumps({'ledger_bytes': len(raw), 'formal_tests': 'not_run'}))
if __name__ == '__main__':
build()
@@ -0,0 +1,17 @@
{
"task_id": "P22-02",
"formal_tests": "not_run; Runner owns execution",
"original_unit_ready": false,
"acceptance_claimed": false,
"spec_comparison": "UTF8 decode.strip.encode SHA only for contract spec",
"spec_sha256": "f252de3c5cff7ebfa62322ae2f2df09fb80a44bc59eb286b76d4acb4c12ca875",
"spec_raw_sha256": "c26c0b12cf7e7a7c46e671acde2e00762904732c1cd6381a53836e3d7b3f9824",
"accepted_input_raw_sha256": "45fa52acf0c8bd4dd58c3b40b681cb92066f05d617df157fc18370a5e8816281",
"ledger_bytes": 453678,
"packet_bytes_before_this_validation_metadata": 472408,
"syntax_ast_parse": "passed; not formal validation",
"complete_packet_limit": 500000,
"runner_required": ["exact static validation", "meaningful negatives", "packet byte total", "applicable display and hearing", "independent Opus review", "integration and final original acceptance"],
"expected_exit_codes": {"record_integrity_if_valid": 0, "require_ready_with_current_unmet_conditions": 2, "integrity_error": 1},
"original_incomplete": ["614 exact uses lack completed selected usable assets; 495 candidates and 119 missing are not completion", "final per-use naming/resolution/frame/layout/replacement references", "creator and rights gaps including 20 media; internal BGM origin is not final approval", "mandatory unmade/disclosure gaps; D08/D09 content and quantities unknown", "applicable display/hearing evidence", "Root static validation and meaningful negatives", "independent Opus review", "integration and final original 30 packages/88 delivery units acceptance"]
}
+155
View File
@@ -0,0 +1,155 @@
#!/usr/bin/env python3
"""Read-only P22 record integrity; exit 2 for unmet original readiness."""
import argparse
import hashlib
import json
from pathlib import Path
import struct
import sys
ROOT = Path(__file__).resolve().parents[1]
OUT = 'docs/plans/original-completion-2026-10-02/execution/p22-02/'
INPUT = 'docs/spec/inputs/P22-02-accepted-usage.json'
CURRENT = 'docs/plans/original-completion-2026-10-02/checkpoints/p22-02-slot-contract-r02/all-slot-current-candidate-contracts.json'
SPEC_SHA = 'f252de3c5cff7ebfa62322ae2f2df09fb80a44bc59eb286b76d4acb4c12ca875'
INPUT_SHA = '45fa52acf0c8bd4dd58c3b40b681cb92066f05d617df157fc18370a5e8816281'
def need(ok, message):
if not ok:
raise ValueError(message)
def sha(raw):
return hashlib.sha256(raw).hexdigest()
def semantic(value):
return sha(json.dumps(value, ensure_ascii=False, sort_keys=True, separators=(',', ':')).encode())
def pairs(items):
result = {}
for key, value in items:
need(key not in result, 'duplicate JSON key: ' + key)
result[key] = value
return result
def read(path):
return json.loads((ROOT / path).read_text(), object_pairs_hook=pairs)
def decode(doc, node=None):
need(type(doc) is dict and all(type(doc.get(k)) is list for k in ('keys', 'strings', 'pool')), 'wrong codec tables')
need(all(type(k) is str for k in doc['keys']) and len(set(doc['keys'])) == len(doc['keys']), 'duplicate/wrong key table')
active = set()
def index(values, i):
need(type(i) is int and 0 <= i < len(values), 'bad reference index')
return values[i]
def walk(n):
if not isinstance(n, list):
need(n is None or type(n) in (str, int, float, bool), 'wrong scalar type')
return n
need(bool(n) and type(n[0]) is str, 'wrong node type')
tag = n[0]
if tag == 'l':
return [walk(x) for x in n[1:]]
if tag == 'd':
need(len(n) % 2 == 1, 'malformed flat dictionary')
result = {}
for i in range(1, len(n), 2):
key = index(doc['keys'], n[i])
need(type(key) is str and key not in result, 'duplicate or invalid decoded key')
result[key] = walk(n[i + 1])
return result
need(tag in ('s', 'r') and len(n) == 2, 'unknown node')
if tag == 's':
value = index(doc['strings'], n[1])
need(type(value) is str, 'wrong shared string type')
return value
i = n[1]
value = index(doc['pool'], i)
need(i not in active and isinstance(value, list) and value[0] in ('d', 'l'), 'cycle or wrong shared object type')
active.add(i)
result = walk(value)
active.remove(i)
return result
result = walk(doc['files'] if node is None else node)
for i in range(len(doc['pool'])):
walk(['r', i])
return result
def check(path):
spec = (ROOT / 'docs/spec/acceptance/P22-02.json').read_bytes()
need(sha(spec.decode().strip().encode()) == SPEC_SHA, 'contract spec SHA mismatch')
need(sha((ROOT / INPUT).read_bytes()) == INPUT_SHA, 'accepted input raw SHA mismatch')
accepted = read(INPUT)
original = decode(accepted)
records = {}
for name, rows in original.items():
need(type(rows) is list, 'decoded file must be a list')
pin = accepted['source_files'][name]
need(len(rows) == pin['records'] and semantic(rows) == pin['semantic_sha256'], 'decoded semantic file mismatch: ' + name)
for row in rows:
identity = row.get('slot_id', row.get('group_id'))
need(type(identity) is str and identity not in records, 'duplicate or invalid identity')
records[identity] = (name, row)
need(len(records) == 614, 'accepted identity coverage')
packed = read(path)
ledger = decode(packed)
need(ledger['accepted_input'] == accepted, 'lossless accepted input mismatch')
current = read(CURRENT)
need(ledger['current'] == current, 'current contract fields changed/omitted or false promotion')
evidence_dir = ROOT / 'docs/plans/original-completion-2026-10-02/checkpoints/p22-02-current-contract-r01'
expected_evidence = {CURRENT} | {str(p.relative_to(ROOT)) for p in evidence_dir.iterdir() if p.is_file()}
need(set(ledger['evidence_pins']) == expected_evidence, 'evidence omission/extra')
selection = ledger['selection_contract']
need(selection['selected_assets'] == [], 'unsupported selected asset promotion')
need('D08/D09 UNKNOWN' in selection['quantity'] and '2 state groups are not images' in selection['quantity'], 'invented quantities/state image promotion')
condition_keys = {'all_exact_uses_selected_and_usable', 'all_creator_and_rights_evidence_accepted', 'all_naming_resolution_layout_replacement_references_accepted', 'all_mandatory_unmade_and_disclosure_gaps_resolved', 'applicable_display_and_hearing_evidence', 'independent_opus_review', 'root_static_and_meaningful_negative_validation', 'integration_and_final_original_30_88_acceptance'}
need(set(ledger['original_conditions']) == condition_keys and all(v is False for v in ledger['original_conditions'].values()), 'unsupported original evidence promotion')
for evidence, digest in ledger['evidence_pins'].items():
need(sha((ROOT / evidence).read_bytes()) == digest, 'evidence raw SHA mismatch: ' + evidence)
seen = set()
used = set()
for contract in current['contracts']:
identity = contract['id']
need(identity in records and identity not in seen, 'contract identity coverage/duplicate')
seen.add(identity)
name, record = records[identity]
need(contract['source_file'] == name and semantic(record) == contract['accepted_record_semantic_sha256'], 'accepted record mismatch')
for anchor in contract['source_anchors']:
raw = (ROOT / anchor['path']).read_bytes()
need(sha(raw) == anchor['sha256'], 'anchor raw SHA')
need(raw.decode().splitlines()[anchor['line'] - 1] == anchor['text'], 'anchor line/reference mismatch')
for ref in contract['candidate_refs']:
source = ref['candidate_ref']
need(source in current['candidate_pool'], 'omitted candidate source')
used.add(source)
actual = current['candidate_pool'][source]['current_raw_sha256']
need(ref['prior_source_still_equal'] == (ref['prior_raw_sha256'] == actual), 'historical applicability mismatch')
need(seen == set(records), 'omitted identities')
need(used == set(current['candidate_pool']) and len(used) == 19, 'source coverage')
for source, fact in current['candidate_pool'].items():
raw = (ROOT / source).read_bytes()
need(sha(raw) == fact['current_raw_sha256'], 'current source raw SHA: ' + source)
fmt = fact['observed_format']
if source.endswith('.png'):
need(raw[:8] == b'\x89PNG\r\n\x1a\n', 'PNG signature')
width, height = struct.unpack('>II', raw[16:24])
need((width, height) == (fmt['png_width'], fmt['png_height']), 'dimension mismatch')
pending = [c['id'] for c in current['contracts'] if not c['original_asset_completion'] or c['missing_conditions'] or c['finished_asset'] is None]
# Nonvacuous: all accepted uses plus external original acceptance conditions.
ready = bool(seen) and not pending and all(ledger['original_conditions'].values())
return {'record_integrity': True, 'original_unit_ready': ready, 'pending_identity_count': len(pending), 'original_conditions': ledger['original_conditions'], 'acceptance_claimed': False}
def main():
parser = argparse.ArgumentParser()
parser.add_argument('--ledger', default=OUT + 'asset-contracts.json')
parser.add_argument('--require-ready', action='store_true')
args = parser.parse_args()
try:
result = check(args.ledger)
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
return 2 if args.require_ready and not result['original_unit_ready'] else 0
except (ValueError, KeyError, TypeError, IndexError, OSError, RecursionError) as exc:
print(json.dumps({'record_integrity': False, 'error': str(exc), 'acceptance_claimed': False}))
return 1
if __name__ == '__main__':
sys.exit(main())