P19-03-TABLE: P19-03 分岐の到達性:原7名×3ルートの全到達表と未制作欠損監査
This commit is contained in:
+27
-18
@@ -4,7 +4,7 @@
|
||||
|
||||
## 変更範囲
|
||||
|
||||
このディレクトリの必須5ファイルのみを追加した。原本、原子要件、work packages、decisions、TASK_SPEC、選定記録、frozen pins、ゲーム入力、marker registryは変更していない。git書込み、他worker起動、Runner操作、ゲーム起動は行っていない。素材追加なし。
|
||||
修復開始点e0420d57a381a1644357f0590536e1d47c9bca9fから、このディレクトリの指定5ファイルのみを変更した。原本、原子要件、work packages、decisions、TASK_SPEC、選定記録、frozen pins、ゲーム入力、marker registryは変更していない。git書込み、他worker起動、Runner操作、ゲーム起動は行っていない。素材追加なし。
|
||||
|
||||
## 要件と検査の対応
|
||||
|
||||
@@ -26,36 +26,45 @@
|
||||
|
||||
指定されたoriginal_relationship_state.gdには7名のrosterはなく、汎用subject_id等の明示入力検証のみがある。既存unique catalogのヒロインはheroine_anriのみ。監査IDには既存atomic entity.key(anri/fine/tomari/amelia/liz/mimi/satsuki)を使い、他6名の実装catalog ID、ライバル対応、初期値を創作していない。この実装欠損は表の欠損であり、原7名を削減する理由にしていない。
|
||||
|
||||
## 実行記録
|
||||
## Repair 1: 4指摘の対応
|
||||
|
||||
以下は生成・静的観測であり正式試験結果ではない。
|
||||
編集前に build/owner-handoff/p19-review-r01/ の ACTUAL_OPUS_REPAIR_DIRECTION.json、ACTUAL_WHOLE_UNIT_REVIEW.json、SPEC_HASH_REPRESENTATIONS.json を読んだ。
|
||||
|
||||
- MEDIUM: coassignment_attack / zero_break / love_actions / threshold_route_loss と旧UIのlegacy_evidenceを追加。固定anchorの実際の本文・行・Git blob OID・file-bytes SHA-256を保持する。原本準拠statusはmissing、旧実装分類はlegacy_not_originalに統一。P18-01は一対一変換を禁止し、旧数値・日次制限は原本として未承認と明記した。UI定数は実際には98–101行であり、89–97行の無関係なラベルを除いた。共通ルール参照により各該当セルへ結び付けた。所有者・原本欠損・natural_route_complete=falseを維持。
|
||||
- LOW 独立試験: validate_legacyは候補とcanonicalの比較とは独立に、git showで固定anchorのソースを読み、引用行本文とgit rev-parseによるblob OID、現ファイルとの一致を検査する。test_independent_legacy_source_evidenceは実際のJSONと実ファイルを開き、git hash-object --stdin(書込みなし)でOIDを独立計算し、関数名とshield_broken/EXCLUSIVE_LOCKEDも確認。test_legacy_evidence_required_independentlyは対象全項目の証拠削除・空配列・無条件not implemented・古いOID/本文を拒否する。
|
||||
- LOW 中断参照: 全21セルのinterruption_cancellationに原本148・153行の本文とbaseline hash、および343–355行のconfirm_route_lockをlegacy_not_originalとして追加。人物固有欠損と担当は維持。
|
||||
- LOW ハッシュ表示: 263a01b943d8056727335fb76977c010298e1c229077f8da76e48683ba2e89bb は TASK_SPEC.json file-bytes SHA-256。3af3d9b931edc5449796662ad69bb6f67c80b48827a86565b1952931db68dd15 は Runner git-show stripped-UTF-8 contract spec_sha256。上記SPEC_HASH_REPRESENTATIONS.jsonのunchanged_file=trueが証拠であり、両者は同じ無変更ファイルを表す。checker定数と出力にも区別を反映。Git blob OIDは別のgit_blob_oidフィールドでありfile-bytes SHA-256とは区別する。
|
||||
|
||||
## 実行記録(今回のローカル確認)
|
||||
|
||||
以下は正式Runner実行ではない。記録済み修復指示に基づき3登録コマンドをローカル実行した。
|
||||
|
||||
生成 — exit 0
|
||||
|
||||
```sh
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root . --write
|
||||
python3 -B /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement/docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement --write
|
||||
```
|
||||
|
||||
生成時に2回実行し、どちらもexit 0。actual_commit=8ca9263fee1ec4374d97c01fda82b95e1df6a977、selected_files=1368、file_equality=true、anchor=1b07b87d3736e777932315a4bdde8b70cbf9cb23、native_evidence_reuse_accepted=false。最終回はUI証拠・catalog行範囲・Markdown共通検証関数の更新後。--writeは全固定入力を照合してJSON/Markdownを生成するが、自己試験またはRunner受入を代替しない。
|
||||
|
||||
別途read-only Pythonでrun_all_tests.pyと同じsource選択を再現し、1368/1368、missing=[]、new=[]、changed=[]、engine SHA一致を確認した。JSONの静的読込で21セルと61原子レコードを確認した。原本表示には `nl -ba docs/traceability/original/BASELINE.txt | sed -n '85,165p'` を使用した。原資料/既存コードはcat、sed、rgとJSON読込で確認した。
|
||||
|
||||
初期の読込用here-documentはshellの一時ファイル作成制限によりexit 1。その後python3 -cで同じ静的読込を行った。製品ファイルへの影響はない。
|
||||
p19-table — exit 0
|
||||
|
||||
```sh
|
||||
python3 -c 'import ast;from pathlib import Path;p=Path("docs/plans/original-completion-2026-10-02/execution/p19-03");[(ast.parse((p/n).read_text()),print(n,"syntax parsed")) for n in ["check_reachability_table.py","test_reachability_table.py"]]'
|
||||
git diff --stat
|
||||
git status --short
|
||||
python3 -B /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement/docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement --check
|
||||
```
|
||||
|
||||
構文読込は2ファイルともexit 0。git差分確認では既存trackedファイルの変更なし、追加は許可ディレクトリの必須5ファイルのみ。試験実行ではない。
|
||||
|
||||
ユーザーの最新指示「試験はRunnerが正式実行します」に従い、下記は**未実行**。合格を主張しない。
|
||||
p19-negative — exit 0
|
||||
|
||||
```sh
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root . --check
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p19-03/test_reachability_table.py --project-root .
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root . --source-binding-only
|
||||
python3 -B /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement/docs/plans/original-completion-2026-10-02/execution/p19-03/test_reachability_table.py --project-root /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement
|
||||
```
|
||||
|
||||
p19-source-binding — exit 0
|
||||
|
||||
```sh
|
||||
python3 -B /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement/docs/plans/original-completion-2026-10-02/execution/p19-03/check_reachability_table.py --project-root /Users/korizatou114/Projects/TheTree-managed-worktrees/p19-03-reachability/P19-03-TABLE-implement --source-binding-only
|
||||
```
|
||||
|
||||
--writeの実際の呼出しは同一checkout内で相対スクリプトパスと --project-root . を使用(上記は絶対パス表記)。--checkはMarkdownのバイト完全一致を確認。自己試験は12件OK。source bindingは1368ファイル一致、file_equality=true、actual_commit=e0420d57a381a1644357f0590536e1d47c9bca9f、native_evidence_reuse_accepted=false。git diff --checkはexit 0。構文は上記Python実行で確認。最初の編集用here-documentは一時ファイル作成制限でexit 1、変更は行われず、python3 -cで再実行してexit 0。native full・ゲーム起動・git書込み・Runner操作・他エージェント起動は行っていない。
|
||||
|
||||
## 残る親成果・ゲームの欠損
|
||||
|
||||
- P18-01: 原本3軸/盾の通常UI、承認済み初期値、本来の全単位受入。P18-02/03: 同配置・軽減・ブレイク・接触・閾値・確定遷移。既存旧汎用モデルの閾値を原本へ転用しない。
|
||||
|
||||
+1161
-219
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+70
-7
@@ -13,7 +13,10 @@ BASELINE = 'docs/traceability/original/BASELINE.txt'
|
||||
ANCHOR = '1b07b87d3736e777932315a4bdde8b70cbf9cb23'
|
||||
BASE = '8ca9263fee1ec4374d97c01fda82b95e1df6a977'
|
||||
BASE_HASH = '75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a'
|
||||
SPEC_HASH = '263a01b943d8056727335fb76977c010298e1c229077f8da76e48683ba2e89bb'
|
||||
SPEC_FILE_BYTES_SHA256 = '263a01b943d8056727335fb76977c010298e1c229077f8da76e48683ba2e89bb'
|
||||
# Proof: build/owner-handoff/p19-review-r01/SPEC_HASH_REPRESENTATIONS.json
|
||||
# Both hashes describe the same unchanged TASK_SPEC.json.
|
||||
RUNNER_CONTRACT_SPEC_SHA256 = '3af3d9b931edc5449796662ad69bb6f67c80b48827a86565b1952931db68dd15'
|
||||
PINS_HASH = '6c778226a4cc6d0b0d682500759e7d4229c926a27bd2e247b00b9c3e5f1a2fb0'
|
||||
ROUTES = {'love': '純愛', 'ntr': 'NTR', 'corruption': '堕落'}
|
||||
PEOPLE = [('anri', 'アンリ'), ('fine', 'フィーネ'), ('tomari', 'トマリ'),
|
||||
@@ -26,6 +29,36 @@ SERVICE = 'scripts/relationship/relationship_service.gd'
|
||||
RUNTIME = 'autoload/simulation_runtime.gd'
|
||||
CATALOG = 'data/characters/mvp_unique_characters.json'
|
||||
|
||||
EVENT = 'scripts/events/event_service.gd'
|
||||
PREVIEW = 'scripts/relationship/placement_risk_preview_service.gd'
|
||||
LEGACY_REASON = 'P18-01 forbids one-to-one conversion; legacy numbers/daily limits are not approved originals.'
|
||||
LEGACY_RANGES = {
|
||||
'coassignment_attack': [(SERVICE,264,312),(RUNTIME,6850,6850),(EVENT,1039,1039),(PREVIEW,536,565),(PREVIEW,619,640)],
|
||||
'zero_break': [(SERVICE,264,312),(RUNTIME,6850,6850),(EVENT,1039,1039)],
|
||||
'love_actions': [(SERVICE,246,261)],
|
||||
'threshold_route_loss': [(SERVICE,315,340),(SERVICE,343,355),(RUNTIME,342,343),(EVENT,1047,1047)],
|
||||
'legacy_event_ui': [('scenes/main/main.gd',98,101)],
|
||||
}
|
||||
|
||||
|
||||
def validate_legacy(root, document):
|
||||
rules = {r['id']: r for r in document['common_rules']}
|
||||
for identity, ranges in LEGACY_RANGES.items():
|
||||
rule = rules[identity]
|
||||
require(rule['status'] == 'missing' and rule['legacy_classification'] == 'legacy_not_original',
|
||||
'legacy/original classification')
|
||||
require(rule['implementation'] == 'original conformance missing; see legacy_evidence'
|
||||
and rule['legacy_reason'] == LEGACY_REASON, 'unqualified legacy absence')
|
||||
entries = rule.get('legacy_evidence')
|
||||
require(type(entries) is list and len(entries) == len(ranges), 'missing legacy evidence')
|
||||
for entry, (path, start, end) in zip(entries, ranges):
|
||||
data = blob(root, ANCHOR, path)
|
||||
oid = subprocess.check_output(['git','rev-parse',ANCHOR+':'+path],cwd=root).decode().strip()
|
||||
require(entry == {'path':path,'anchor':ANCHOR,'git_blob_oid':oid,
|
||||
'sha256':sha(data),'lines':[start,end],
|
||||
'text':'\n'.join(data.decode().splitlines()[start-1:end])}, 'legacy text/blob/range')
|
||||
require(data == (root/path).read_bytes(), 'legacy current source mismatch')
|
||||
|
||||
|
||||
def sha(data):
|
||||
return hashlib.sha256(data).hexdigest()
|
||||
@@ -63,7 +96,7 @@ def selection(root):
|
||||
|
||||
def binding(root):
|
||||
raw = lambda p: (root / p).read_bytes()
|
||||
require(sha(raw(OUT + 'TASK_SPEC.json')) == SPEC_HASH, 'immutable spec changed')
|
||||
require(sha(raw(OUT + 'TASK_SPEC.json')) == SPEC_FILE_BYTES_SHA256, 'immutable spec changed')
|
||||
spec = decode(raw(OUT + 'TASK_SPEC.json'))
|
||||
require(sha(raw(OUT + 'FROZEN_GAME_INPUT_PINS.json')) == spec['frozen_game_input_pins_sha256'] == PINS_HASH,
|
||||
'frozen pins SHA differs from immutable spec')
|
||||
@@ -86,7 +119,9 @@ def binding(root):
|
||||
require(raw(p) == blob(root, ANCHOR, p), 'stale audited source: ' + p)
|
||||
return {'actual_commit': subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=root).decode().strip(),
|
||||
'selected_files': len(actual), 'anchor': ANCHOR, 'file_equality': True,
|
||||
'native_evidence_reuse_accepted': False}
|
||||
'native_evidence_reuse_accepted': False,
|
||||
'TASK_SPEC_file_bytes_sha256': SPEC_FILE_BYTES_SHA256,
|
||||
'Runner_git_show_stripped_UTF8_contract_spec_sha256': RUNNER_CONTRACT_SPEC_SHA256}
|
||||
|
||||
|
||||
def expected(root):
|
||||
@@ -108,7 +143,7 @@ def expected(root):
|
||||
'lines': [start, end], 'text': '\n'.join(data.decode().splitlines()[start - 1:end])}
|
||||
def item(identity, start, end, note, owner, status='missing', source=None):
|
||||
return {'id': identity, 'authority': cite(start, end), 'status': status,
|
||||
'implementation': source or 'not implemented', 'owner': owner,
|
||||
'implementation': source or 'original conformance missing', 'owner': owner,
|
||||
'observation': note, 'gap': None if status == 'implemented' else '欠損: ' + note}
|
||||
# Only narrow storage facts are implemented; simulation/route completion never follows.
|
||||
rules = [
|
||||
@@ -121,7 +156,7 @@ def expected(root):
|
||||
item('identity_registration', 138, 138, 'Runtimeは登録済み人物・相互対応を検証。既存カタログのヒロインはアンリのみ。通常ルート入口ではない。',
|
||||
'P19-01', 'partial', evidence(RUNTIME, 6791, 6812)),
|
||||
item('legacy_event_ui', 154, 157, '既存UIには汎用関係イベントの確認/保留ラベルがある。原本の行動・決闘選択・自然ルート到達は欠損。',
|
||||
'P19-01', 'partial', evidence('scenes/main/main.gd', 89, 101)),
|
||||
'P19-01', 'partial', evidence('scenes/main/main.gd', 98, 101)),
|
||||
]
|
||||
clauses = [
|
||||
('defense_reduction', 141, 141, 'P18-02'), ('post_break_metric', 142, 143, 'P18-02'),
|
||||
@@ -136,6 +171,17 @@ def expected(root):
|
||||
for identity, start, end, owner in clauses:
|
||||
rules.append(item(identity, start, end,
|
||||
'原本の共通要求。専用原本モデルにはこの更新・表示の実装なし。旧汎用モデルの数値やルートを代用しない。', owner))
|
||||
for rule in rules:
|
||||
if rule['id'] in LEGACY_RANGES:
|
||||
rule.update(status='missing', implementation='original conformance missing; see legacy_evidence',
|
||||
legacy_classification='legacy_not_original', legacy_reason=LEGACY_REASON)
|
||||
rule['gap'] = '欠損: ' + rule['observation'] + ' ' + LEGACY_REASON
|
||||
rule['legacy_evidence'] = []
|
||||
for path, start, end in LEGACY_RANGES[rule['id']]:
|
||||
entry = evidence(path, start, end)
|
||||
entry['git_blob_oid'] = subprocess.check_output(
|
||||
['git','rev-parse',ANCHOR+':'+path],cwd=root).decode().strip()
|
||||
rule['legacy_evidence'].append(entry)
|
||||
# Read complete selected atomic records, not only their condition strings.
|
||||
atoms = decode(raw(PLAN + 'atomic_requirements.json'))['requirements']
|
||||
atoms = [x for x in atoms if set(x.get('source_group_ids', [])) & {'B-7.' + str(i) for i in range(1, 7)}]
|
||||
@@ -156,7 +202,7 @@ def expected(root):
|
||||
'post_break_metric', 'barrier_block', 'coassignment_attack', 'zero_break', 'contact_after_break'],
|
||||
'duel_effects': ['duel_types', 'duel_win', 'duel_loss'],
|
||||
'route_selection_effects': ['threshold_route_loss', route + '_actions', 'route_skills'],
|
||||
'interruption_cancellation': ['departure'], 'save_handling': ['snapshot_container'],
|
||||
'interruption_cancellation': ['departure', 'threshold_route_loss', 'duel_loss'], 'save_handling': ['snapshot_container'],
|
||||
'history_marks': ['history_fields', 'history_cycle_parentage', 'portrait_marks'],
|
||||
'current_reachability': ['identity_registration', 'threshold_route_loss']}
|
||||
categories = {}
|
||||
@@ -169,6 +215,12 @@ def expected(root):
|
||||
'原本未定義/D08 pending: 人物固有のイベント列・条件・中断取消・進行保存・履歴と通常到達は未制作/未証明。共通要求から固有仕様を創作しない。', owner)
|
||||
gap['character_authority'] = cite(118 + i)
|
||||
categories[category] = {'common_rule_refs': refs[category], 'character_specific': gap}
|
||||
if category == 'interruption_cancellation':
|
||||
categories[category].update(route_termination_authorities=[cite(148),cite(153)],
|
||||
legacy_classification='legacy_not_original', legacy_reason=LEGACY_REASON,
|
||||
legacy_evidence=[e for r in rules if r['id']=='threshold_route_loss'
|
||||
for e in r['legacy_evidence'] if e['path']==SERVICE and e['lines']==[343,355]])
|
||||
|
||||
cells.append({'character_id': key, 'name': name, 'route_id': route, 'route': route_name,
|
||||
'natural_route_complete': False, 'categories': categories})
|
||||
return {'task_id': 'P19-03-TABLE', 'schema_version': 1,
|
||||
@@ -190,8 +242,10 @@ def expected(root):
|
||||
'accounting': '14/88 unchanged; formal Runner validation, independent whole-unit Opus review and normal integration/final required; no game acceptance'}
|
||||
|
||||
|
||||
def verify(candidate, canonical):
|
||||
def verify(candidate, canonical, root=None):
|
||||
require(type(candidate) is dict, 'document must be an object')
|
||||
if root is not None:
|
||||
validate_legacy(root, candidate)
|
||||
# Exact canonical equality also rejects additions, type coercion, invented numbers,
|
||||
# owner/status promotion and unsupported completion, including nested metadata.
|
||||
require(json.dumps(candidate, ensure_ascii=False, sort_keys=True) ==
|
||||
@@ -215,6 +269,10 @@ def markdown(d):
|
||||
r['observation'], '', '原本 %s:%s–%s' % (BASELINE, *r['authority']['lines']), '',
|
||||
'```text', r['authority']['text'], '```', '',
|
||||
'実装証拠: ' + json.dumps(r['implementation'], ensure_ascii=False, sort_keys=True), '']
|
||||
if 'legacy_evidence' in r:
|
||||
lines += ['旧実装: ' + r['legacy_classification'] + ' / ' + r['legacy_reason'], '',
|
||||
'旧証拠 (git_blob_oid: Git blob OID; sha256: file-bytes SHA-256): ' +
|
||||
json.dumps(r['legacy_evidence'], ensure_ascii=False, sort_keys=True), '']
|
||||
for c in d['cells']:
|
||||
lines += ['## ' + c['character_id'] + ' / ' + c['name'] + ' / ' + c['route'], '']
|
||||
for category, record in c['categories'].items():
|
||||
@@ -222,6 +280,9 @@ def markdown(d):
|
||||
lines += ['- ' + category + ': 共通 [' + ', '.join(record['common_rule_refs']) +
|
||||
'] / missing / ' + g['gap'] + ' / owner: ' + g['owner'] +
|
||||
' / 原本 ' + str(g['authority']['lines']) + '・人物 ' + str(g['character_authority']['lines'])]
|
||||
if category == 'interruption_cancellation':
|
||||
lines += [' 原本中断参照: ' + json.dumps(record['route_termination_authorities'], ensure_ascii=False),
|
||||
' 旧確定ロック legacy_not_original: ' + json.dumps(record['legacy_evidence'], ensure_ascii=False)]
|
||||
lines.append('')
|
||||
lines += ['## ルート別適用性', '']
|
||||
for route, support in d['route_support'].items():
|
||||
@@ -245,10 +306,12 @@ def main():
|
||||
result = binding(root)
|
||||
if not args.source_binding_only:
|
||||
canonical = expected(root)
|
||||
validate_legacy(root, canonical)
|
||||
if args.write:
|
||||
(root / OUT / 'REACHABILITY_TABLE.json').write_text(json.dumps(canonical, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
|
||||
(root / OUT / 'REACHABILITY_TABLE.md').write_text(markdown(canonical), encoding='utf-8')
|
||||
else:
|
||||
validate_legacy(root, decode((root / OUT / 'REACHABILITY_TABLE.json').read_bytes()))
|
||||
verify_records(decode((root / OUT / 'REACHABILITY_TABLE.json').read_bytes()), canonical,
|
||||
(root / OUT / 'REACHABILITY_TABLE.md').read_bytes())
|
||||
print(json.dumps({'audit_integrity': 'pass', 'formal_acceptance': 'pending_runner_and_independent_review', **result}))
|
||||
|
||||
@@ -29,6 +29,39 @@ class AuditTests(unittest.TestCase):
|
||||
self.assertEqual({r['status'] for r in value['common_rules']}, {'implemented', 'partial', 'missing'})
|
||||
audit.verify(value, self.canonical)
|
||||
|
||||
def test_independent_legacy_source_evidence(self):
|
||||
import subprocess
|
||||
document = audit.decode((ROOT / audit.OUT / 'REACHABILITY_TABLE.json').read_bytes())
|
||||
audit.validate_legacy(ROOT, document)
|
||||
for rule in document['common_rules']:
|
||||
for entry in rule.get('legacy_evidence', []):
|
||||
data = subprocess.check_output(['git', 'show', audit.ANCHOR + ':' + entry['path']], cwd=ROOT)
|
||||
self.assertEqual(data, (ROOT / entry['path']).read_bytes())
|
||||
self.assertEqual(entry['git_blob_oid'], subprocess.check_output(
|
||||
['git','hash-object','--stdin'], input=data, cwd=ROOT).decode().strip())
|
||||
start, end = entry['lines']
|
||||
self.assertEqual(entry['text'], '\n'.join(data.decode().splitlines()[start-1:end]))
|
||||
rules = {r['id']: r for r in document['common_rules']}
|
||||
self.assertIn('func restore_shield', rules['love_actions']['legacy_evidence'][0]['text'])
|
||||
self.assertIn('shield_broken', rules['zero_break']['legacy_evidence'][0]['text'])
|
||||
self.assertIn('EXCLUSIVE_LOCKED', rules['threshold_route_loss']['legacy_evidence'][1]['text'])
|
||||
|
||||
def test_legacy_evidence_required_independently(self):
|
||||
for identity in audit.LEGACY_RANGES:
|
||||
for mutation in ('drop', 'empty', 'absence', 'blob', 'text'):
|
||||
changed = copy.deepcopy(self.canonical)
|
||||
rule = next(r for r in changed['common_rules'] if r['id'] == identity)
|
||||
if mutation == 'drop':
|
||||
rule.pop('legacy_evidence')
|
||||
elif mutation == 'empty':
|
||||
rule['legacy_evidence'] = []
|
||||
elif mutation == 'absence':
|
||||
rule['implementation'] = 'not implemented'
|
||||
else:
|
||||
rule['legacy_evidence'][0]['git_blob_oid' if mutation == 'blob' else 'text'] = 'stale'
|
||||
with self.subTest(identity=identity, mutation=mutation), self.assertRaises(ValueError):
|
||||
audit.validate_legacy(ROOT, changed)
|
||||
|
||||
def test_exact_cell_product(self):
|
||||
mutations = {
|
||||
'missing': lambda d: d['cells'].pop(),
|
||||
|
||||
Reference in New Issue
Block a user