Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e25213e91 | ||
|
|
27b18b75c9 | ||
|
|
1b08d6b0fc | ||
|
|
20d0ae5990 | ||
|
|
2dde7596a8 | ||
|
|
fb8b7919db | ||
|
|
55f1fb49aa | ||
|
|
4ab7ab8712 | ||
|
|
7af6fafaf0 | ||
|
|
d393e4d90f | ||
|
|
8418560aaf | ||
|
|
16207d353a | ||
|
|
b543c26c95 | ||
|
|
accd07c91d |
@@ -1,177 +1,34 @@
|
||||
# 初期監査の検証器
|
||||
# Checker protocol — historical repair29 description
|
||||
|
||||
## repair7 定義(以前の版識別説明に優先)
|
||||
Repair30 is a partial, blocked implementation. CURRENT_STATUS.md and
|
||||
REPAIR30_STATUS.md supersede the current-role, authority and execution claims
|
||||
below. M is c0ea009; product manifests exclude checker, selected native/full
|
||||
SourceRefs include it. Q is the actual recomputed merge-tree; C requires separate
|
||||
normal-integration qualification. All four remaining D05 policies are approved.
|
||||
Do not run the old collection/index recipe as fresh-Q evidence. No 500000-byte
|
||||
limit applies. The historical protocol text below is retained for provenance.
|
||||
|
||||
registered は deb1bcb、execution_origin は 9767b0a、checker は実際に
|
||||
実行したcheckerのcommitとbytes、product_target は
|
||||
`{main_anchor, submitted, composed_tree}` とする。checkerのSHAを製品版に
|
||||
代用しない。提出後にしか確定しない自己blob照合は未実施としてRootが検証する。
|
||||
Worker **SOURCE-READY / NOTRUN**. [CURRENT_STATUS.md](CURRENT_STATUS.md) is canonical for execution facts; creation-time NOT RUN in REPAIR22–28 and runbooks is historical. [ORDER_AUDIT.md](ORDER_AUDIT.md) and order-audit.json give the eleven-phase/seven-domain audit. [D05_REMAINING_ORDER_CANDIDATE.md](D05_REMAINING_ORDER_CANDIDATE.md) is approval material only.
|
||||
|
||||
`duty_payments` は決算日のpayroll settlement取引のうち、
|
||||
ShiftInstance.record_duty_minute の勤務recordとstaff_id/day単位で
|
||||
結合できる取引の件数。期待値は1のまま。独立した通貨項目ではない。
|
||||
collectorはpayroll行と勤務recordの実rawを結合し、結合不能なら失敗する。
|
||||
B = `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
The checker remains a61 (`a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850`), with its registry unchanged. Fifteen conditions + EV, seven domains, three safety outcomes and30/88 denominator remain fixed. Original credit13/88 unchanged; P04 remains incomplete.
|
||||
|
||||
融資の公開返済API `repay_loan_principal` とledger分類
|
||||
`LOAN_PRINCIPAL_REPAID` / `financing_principal_outflows` は存在する。
|
||||
自動返済や全UIの完成は主張しない。「融資機構がない」という説明は撤回する。
|
||||
| Evidence | Root-measured current fact |
|
||||
| --- | --- |
|
||||
| B/p04-s4-native-r28 | 4/4 PASS; PIDs49940,50044,50078,50105 all ended; guards0; cleanup0. |
|
||||
| B/audit-support/p04-root-fb8b-s4-r01 | S4 controls and native receipts PASS. |
|
||||
| B/audit-support/p04-root-fb8b-local-r01 | Local8 PASS and registered validation fb8b PASS28.095s. |
|
||||
| B/audit-support/p04-raw-collect-r22 | Current collection, preserving raw and exact relocation/series SHA256 bindings. |
|
||||
| B/p04-evidence/index.json | Schema2; missing_conditions=[]; all15 conditions + EV mechanically verified. |
|
||||
|
||||
midnight_replayは同minuteの決算API再呼出しによる実測までpending。
|
||||
1442完了やservice request再送を1440売上・決算API再呼出しの証明にしない。
|
||||
shift_completionはapproved-shift-current-r01.gdの1080観測を使う。
|
||||
D05のEV停止帰属、停電と休業の厳密な順序、費用時点の未確定部分はpending。
|
||||
全case収集、登録validate、独立whole review、統合準備の順序を維持する。
|
||||
本修理での試験・engine実行はworkerでは行わない。
|
||||
Root-measured native product5f6bc11d/main9b1a18c1. Current product is B/audit-support/p04-r17-full-inputs-r01/product, not deb11300. Static current hashes and handler/function lines, plus exact proof pins, are in REPAIR29_STATIC_SOURCE.json. The index preserves source/config/input/environment applicability and native ownership joins; mechanical PASS does not grant D05 approval or original completion.
|
||||
|
||||
## repair6 現在値(2026-10-16)
|
||||
C NATIVE_RUNS/native_ownership/verify_native remains unchanged. C shift proof joins actual owned start/end PIDs, serviceID, original priceSnapshot, service.payment key and receipt entries; no fixed PID or ledger number. S1 typed raw joins pre-travel ACTIVE reservation/task/staff/cell to boarded trip; JSON summaries do not replace typed evidence.
|
||||
|
||||
旧値: C全体は `native ordering bytes` によりblocked、実applicabilityは拒否、
|
||||
whole native未確認。以下の「今回のPython確認と入力不整合」は旧bundleの履歴。
|
||||
新値: 補正済み `build_dir/audit-support/native-first-current/` に対し
|
||||
verify_native、ownership/save/ledger個別検査、applicabilityが成功した。
|
||||
checker_native_fixtures.py、checker_negative_fixtures.py、
|
||||
checker_applicability_fixtures.py --native-bundle は各1回実行しexit 0。
|
||||
旧23負例と新2負例(signature行だけのSHA、後続コメントを除いたSHA)を全て拒否。
|
||||
既存fixture14件も拒否、A/Bの登録版・production版・行移動正対照成功、3負例拒否。
|
||||
正規bundleを合成補正せず検査し、結果・旧値・新版SHAはc-native-validation.jsonに保存。
|
||||
全文rawの `old == new` および
|
||||
`sha256(new[function]) == production_body_sha256 == registered_body_sha256` が必須。
|
||||
whole gateは未実行。原15条件+EV、7領域、安全性3結果、30/88の分母、予算、
|
||||
repair上限は変更せず、formal_original_credit=0。正式受入はRunner・独立レビュー・
|
||||
オーナー判断待ち。以下の旧blocked記述を現在のC検査結果として扱わない。
|
||||
Only S4 supplies isolated power_stop. It preserves native ownership, full product1338 plus sole QA override, Writer/Reader packages, exact Runtime typed restoration and replay, actual NO_POWER, operating dependency, powered/ACTIVE kitchen, positive global mana and open operator. Raw graph/revision, source-derived hold, intervening held minutes, zero stopped charge, original identity and one recovery sale are joined without canonicalization or dropped fields. S4 causes NO_POWER by directly calling disconnect_nodes; it is not global-depletion proof. Global depletion appears only in B1 combined diagnostic; B1 has RELATED_FACILITY_INACTIVE and supplies neither isolated power_stop nor priority approval.
|
||||
|
||||
## C段階追補(本節がrepair4以前の状態説明に優先)
|
||||
The low authority finding remains explicit: destruction_stop is bound to BASELINE:183, a mana-depletion line, not destruction authority. An explicit D05 destruction rule is required; pin/registry correction only after approval through normal repair. Observation and mechanical verification do not resolve this gap. A01 midnight ownership upkeep, dependency hold and18:00 are approved and are not re-asked. Other mandatory priorities remain pending under D05(a–d).
|
||||
|
||||
`verify_native(base)` は供給済み `native-first-current` 形式の有限adapter。
|
||||
Rootのprobe/Reader/wrapperは起動・再実装しない。A/B、verify、coverage、
|
||||
boundary_conditions、15条件、reachable EV、7領域、3安全性結果は維持する。
|
||||
既存scalar検査は宣言の整合性検査として残すが、これだけでは成功しない。
|
||||
Full178 used checker f5 (`f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324`),21615 checks; exact receipt preserved at B/audit-support/p18-regression-support-final-r01/native-full178/runner-ended.json (runner-input.json selected_source_map preserves all1339 selected inputs; source-start.json records initial product sources). **No full1339 run against a61 exists.** Suites/parse not executing Python support checker-independent product applicability only. Static full178 selected-map comparison found one test-source difference (tests/business/test_required_kitchen.gd: full3a9c92f8…, current5dcc86d6…); no game-source difference. Runner must preserve that test applicability binding; no blanket1338 identity is claimed. Do not relabel the old full proof as a61 validation.
|
||||
|
||||
whole gateのindexには追加で `native_bundle` が必要。
|
||||
`manifest` は evidence directory相対の `OWNERSHIP_RAW_PROVENANCE.json`、
|
||||
`sha256` はその実SHA。manifestの親をbundle rootとして以下を照合する。
|
||||
絶対パス・親参照・symlinkは既存safe規則で拒否する。
|
||||
native production_commitはtested HEADと一致させる。別版資料をA/Bの一致だけで
|
||||
流用しない。今回のproduction版とworker checkoutは区別したまま保持する。
|
||||
|
||||
1. SOURCE_MANIFESTの76 fileとOWNERSHIP_RAW_PROVENANCEの96 fileは、
|
||||
各bytesとSHAを実体から再計算する。由来のsource絶対パスは開かない。
|
||||
3 runのimport/parse/repro、formalのwriter/reader、合計10プロセスは
|
||||
started→process→endedのPID、owner、argv、時刻順、実終了codeを照合する。
|
||||
execution・phase receipt・actual phase・cleanupのPID集合と埋込記録も一致必須。
|
||||
stdout/stderr/native logのSHA、通常終了、cleanupのwait結果とerrors空を要求する。
|
||||
Writer/Readerは別PID・別profileで、profile receipt・環境・5 slotのパスも結合する。
|
||||
2. production_commitは3結果資料とapplicabilityで一致し、specの6登録pinと
|
||||
production Git blobから計算した6 current pinを区別する。各runのsource_beforeと
|
||||
selected_source_mapにも結合する。20関数は既存A/Bと同じ全文raw比較を行う。
|
||||
後続トップレベルdoc commentを含む抽出全文rawのSHAだけを許可する。
|
||||
signature行のみ、コメント除外prefix等の候補は許可しない。
|
||||
3. `observations.json` の5点と対応 `midnight-*.json` を結合する。
|
||||
receiptはtransaction IDを一意にし、row重複を拒否し、順序・多重度を保持した
|
||||
台帳差分との完全一致を要求する。Readerの生receipt、first/replayも同じ行に結合する。
|
||||
SERVICE_SALEにservice_idを要求しない。after snapshotの
|
||||
`/systems/runtime/economy/idempotency` の `service.payment.<service_id>` から
|
||||
transaction_idを得て、sale・guest wallet・facility revenue・cash sweepを結合する。
|
||||
固定のPID・価格・transaction番号を使わない。元価格と時間はproduction Gitの
|
||||
facility .tres、run inputのpin、初期facility/service/requestと照合する。
|
||||
PAUSEDはstateに加え `interruption_reason=RELATED_FACILITY_INACTIVE` を要求し、
|
||||
進捗・service ID・予約IDを保持、売上0、財布差分0を再計算する。
|
||||
request存在系列、予約requester/facility/ACTIVE→CONSUMED、復帰1回課金を照合する。
|
||||
最後のGuest despawn時は残存台帳から財布を再計算し、Guest辞書の存在を偽定しない。
|
||||
maintenanceは同一transaction・施設・金額のaccrual/paymentを対にする。
|
||||
発生1439と支払い1440は同分扱いせず、観測したmidnightとその直前として照合する。
|
||||
4. 正式保存は5点×4member×writer/reader=40実ファイルを照合する。
|
||||
execution.formal_before/afterの20 member map一致と実SHA/bytes一致を要求する。
|
||||
typed 14件はwriter-persisted 5、reader-loaded 5、reader-replayed 4(before以外)。
|
||||
原order-evidence .varおよびrun固定input pinと実SHAを照合する。
|
||||
.var decodeは行わず、値の判定はnative JSON receipt/stateを根拠とする。
|
||||
|
||||
このadapterが返す条件はdependency_stop/recovery_completion/paused_midnightの
|
||||
有限系列だけ。正式保存member同一性は、全save_restore条件達成を意味しない。
|
||||
whole gateは固定15条件+EVとこの結果の差集合を未収集としてfailする。
|
||||
非ゼロ給与×シフト交代×00:00、command再送、停電→閉店同Tick、退出/予約解放、
|
||||
破壊、EV等をscalar=trueで埋めることはできない。原条件の加点は常に0。
|
||||
|
||||
### 今回のPython確認と入力不整合
|
||||
|
||||
供給rawのownership/save/ledger個別検査は成功。applicability資料は
|
||||
`native ordering bytes` で拒否する。4関数のbody SHAが複数行シグネチャの
|
||||
途中までしか含まないためで、詳細はEVIDENCE_COLLECTION.mdに記録した。
|
||||
実raw全体を合格とは報告しない。
|
||||
|
||||
checker_native_fixtures.pyはallowed_paths内の一時コピーで23負例を検査する。
|
||||
通常の負例ではmanifest SHAも再計算し、hashだけを理由とする失敗を避ける。
|
||||
1byte改竄、member欠落、PID/profile同一化、ended異常、cleanup欠落、
|
||||
receipt二重化/削除、PAUSED売上、予約孤立、財布差分、idempotency誤結合、
|
||||
commit/pin不一致、typed/member変更、scalar自己申告を全て拒否する。
|
||||
PAUSED売上と予約孤立にはJSON側も同時変更する対抗例を含む。
|
||||
一時コピーのapplicabilityのみGit全文hashへ差し替えた合成正対照はあるが、
|
||||
これを正規native証拠として保存・採用しない。
|
||||
|
||||
旧synthetic_controlはnative rawを持たないため現在はexit 1が正しい。
|
||||
既存13負例も維持し、全14件を拒否。A/Bの正対照と3負例も維持する。
|
||||
本番の未収集indexに対する重複validateは行わない。
|
||||
|
||||
## repair4差分(未完成・blocked)
|
||||
|
||||
今回のA/B実装では、以下の旧説明の「6不変pin=実checkout」および
|
||||
reachabilityのstart/end/text形式を置き換えた。詳細と未供給入力は
|
||||
`EVIDENCE_COLLECTION.md` を参照。Cのowned_outer/raw台帳結合とD/Eの残項目が
|
||||
未完成のため、このcheckerを正式受入には使用しない。
|
||||
|
||||
- `registered_pins` はspec.current_source_pinsと一致し、登録Git blob SHAをcheckerが再計算する。
|
||||
- `versions` はexecution_origin(9767b0aの完全SHA)、registered(deb1bcbの完全SHA)、
|
||||
tested(HEAD)、integration(null)の4キー。candidate_commitもHEADを維持。
|
||||
- `applicability_delta` は登録版..HEADのautoload/scriptsのGD変更関数の
|
||||
path/function配列。checkerの計算順と完全一致。`delta_cases` は
|
||||
`path:function` → 条件ID → engine case ID。条件集合はchecker内固定の保守的判定。
|
||||
- 全autoload/scripts GDがsource_map必須で、実checkoutとHEADのraw一致も必須。
|
||||
- reachability.source_refsはpath/sha256/function/anchor_sha256/line_count。
|
||||
絶対行位置は使用せず、関数内のraw本文アンカーを一意に解決する。
|
||||
- 20順序関数は固定抽出規則で登録Git blobとtestedをraw比較する。
|
||||
|
||||
Python確認結果: 既存合成正対照1件exit 0、負例13件exit 1。
|
||||
別のA/B対照は登録=testedと実production Git blobの合法delta(25変更単位)の
|
||||
2件成功、両版で+7行のアンカー解決成功、delta欠落/境界case欠落/版誤表示の
|
||||
3負例を拒否した。全てengine証拠ではなくPython検証のみ。
|
||||
owned/count/restore/別候補receipt流用の新負例は未実装であり、上記に含めない。
|
||||
|
||||
以下はrepair3時点の記録。置換対象以外もC修正完了の証明として扱わない。
|
||||
|
||||
`python3 tools/check_p04_order_evidence.py --verify-only` は環境変数 `BUILD_OUTPUT_DIR` の `p04-evidence/index.json` を読み、書込み・試験・Runner操作をしない。欠落時は非0。原30/88の受入・完了を確定しない。
|
||||
|
||||
D05-P04-DEPENDENCYとD05-P04-SHIFT-1800は承認済み。固定必須条件と証拠が揃えばmachine_verified=true、formal_acceptance=false、exit 0。残るD05の帰属pendingを一括failにしない。必須authority pending・証拠欠落は非0。独立レビュー・通常統合までは正式受入0。
|
||||
|
||||
内部JSON形式は `P04_ORDER_EVIDENCE_PROTOCOL.json` を基に次の有限サブセットを実装した。重複JSONキー、NaN/Infinity、絶対・親参照・symlink・重複artifact pathを拒否する。全ハッシュは生バイトSHA256。indexに合格booleanや試験数があっても判定には使用しない。
|
||||
|
||||
- `schema_version: 1`, `task_id: P04-01`, checkout HEADと一致する `candidate_commit`, `formal_acceptance: false`。
|
||||
- `source_map`: repository相対パス→SHA256。specの6不変pinと旧map内autoload/scriptsの全GDScript依存を必須とする。追加sourceも実ファイルと照合する。
|
||||
- `environment`: 非空 `os` と `engine`, `configuration`, `inputs` の証拠ディレクトリ相対パス→SHA256 map。engineは実行バイナリのコピー。OSの自己申告だけではnative証明にならない。
|
||||
- `artifacts`: ID→`path`, `bytes`(整数), `sha256`, `type`。typeは observation/state/receipt/stdout/native_log/save/authority のいずれか。
|
||||
- `authority`: ref→`status`(original/approved), `artifact_id`。raw文書の存在を確認するが内容の原規約適合は独立レビューを要する。pendingは失敗。
|
||||
- `domains`: 正確な7領域名→`source_refs`, `authority_refs`, `cases`, `invariants`, `applicability`。source_refは `path/function/sha256`。applicabilityはindexと同じ `source_map/environment`。
|
||||
- 各caseは一意な `id` とartifact IDの `input/before/operation/after/receipt`。receipt JSONは正整数 `start_pid`、同じ `end_pid`、整数 `exit_code: 0`、`engine_ended: true`、一致する `source_map/environment`、raw `stdout/native_log` artifact IDを要求する。receiptのcase_idとinput/before/operation/afterのartifact_hashesを照合する。caseにもraw_origin=engineを要求する。ログの意味と実行由来の独立確認はRoot/reviewerが行う。
|
||||
- 各invariantは `artifact_id/json_pointer/operator/authority_ref/raw_origin`。raw_originはengineのみ。root derivationはformula検証未対応のため拒否する。RFC6901 pointerでraw JSONから値を読む。eqはexpectedとの型を含むliteral一致、nonnegativeはboolean以外の数値が0以上、count_eqは配列/辞書長と整数expectedの一致。same_literal/distinctは `paired_artifact_id/paired_json_pointer` の値との型を含む比較。未知operatorは失敗。
|
||||
|
||||
## 固定必須条件
|
||||
|
||||
checkerのREQUIREDが唯一の必須条件定義であり、indexから条件を受け取らない。conditionsはそのIDと完全一致するmap。各値はcase_id、固定順のauthority_refs、nativeWindows: pending。各条件は指定domainのengine caseと全metricのeq invariantを必要とする。観測はcase.afterの /conditions/<condition_id>/<metric> に置き、invariantのcase_id、artifact_id、pointer、expectedを固定定義と照合する。pass booleanや試験数は使わない。測定値は実際のengine観測から出すこと。手書き合成fixtureはchecker分岐検証専用であり、受入証拠には使用不可。
|
||||
|
||||
- 停止課金0: power_stop / destruction_stop / dependency_stop。依存先停止は進捗差分0・予約とservice ID保持も必要。
|
||||
- 完了・課金1回: shift_completion(1080、交代前)/ recovery_completion / same_minute_replay(再送・重複生成0)。
|
||||
- 孤立0: closure_release / exit_release / patience_release / paused_midnight。直接休業ではD-034の開始済み継続と待機予約解放を区別。停止日跨ぎは停止中課金0と復帰完了課金各1回。
|
||||
- 決算1回: midnight_settlement / midnight_replay / save_restore。売上・給与・維持費・勤務分の支払い各1回、非ゼロ給与を必須とし、帰属先は新しく決めない。
|
||||
- night_staff_absent: 1081、夜番0、WAITING_FOR_STAFF。
|
||||
- overtime_task: 既存interrupt_level=0の残業と勤務分記録1回(staff_service:1409-1412)。
|
||||
|
||||
authorityの参照名はREQUIREDに固定。BASELINE文書、D-034文書、既存A01を記録した入力草案のraw bytesをcheckoutと照合する。A01を再承認しない。承認2条件はauthority typeの同梱D05_OWNER_APPROVAL.jsonと同一SHA256(4583ee9aad45c4db183d72df4420df48aa8e0897e3b5efda13016f0b5cdbd68e)を要求する。origin=direct_user_reply、formal_acceptance=falseと承認IDも確認する。
|
||||
|
||||
save_restore条件にはsave_artifact(type=save)とrestore_receiptを追加する。元caseのreceiptのsaved_sha256、readerのrestored_sha256を保存raw hashと照合し、別の正整数PID・終了・exit 0・source/environment一致・stdout/native_logを要求する。保存packageの意味的完全性・正式保存APIの利用は生証拠の独立レビュー対象。
|
||||
|
||||
reachabilityはEV/transportのみ。各source_refはpath/sha256/start/end/textで、固定REACH_REFSの経路を全て含み、実際の行本文と一致すること。reason必須。救護搬送は課金予約reach=false。EVはスタッフ移動前予約経路があるためreach=trueとcase_idを必要とする。停電domainのengine case.afterのev.reserved_before_travel=true、power_stopped=true、orphan_reservations=0のeq invariantを要求する。客の到着後予約だけを根拠にEV全体を除外できない。
|
||||
|
||||
specは契約文字列hashと照合し、checkoutの末尾LF一つの表現差だけを許容する。その他のraw hashは正規化しない。
|
||||
|
||||
## worker確認
|
||||
|
||||
checker_negative_fixtures.pyはallowed_paths内の一時ディレクトリをBUILD_OUTPUT_DIRに使用し、終了後削除する。py_compile、対照用合成index(exit 0、正式受入false)、指定10負例(各exit 1、対応エラー)を確認済み。実engine試験・Runner操作は0。Runnerがp04-acceptanceを正式実行し、Rootが現版適用性、別セッションのreviewerが生証拠由来と意味を独立確認する。
|
||||
Remaining Runner work: owner D05 decisions after review, any subsequent normal repair, support53cb normal registered rebind, final full run and final applicability binding, formal independent review, integration P04 first then support on main9b1a18c1, and individually applicable nativeWindows obligations. Missing original authority or unknown mandatory rules remain gaps regardless of mechanical PASS. No probe or test is requested or run by this docs-only repair.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# P04-01 current status — repair31
|
||||
|
||||
**BLOCKED: partial implementation; do not start the once-only native/full run.**
|
||||
Formal completed status is **16/88**, with **zero P04 credit**.
|
||||
|
||||
D05 is six of six actually approved; adoption and exact-Q verification are pending.
|
||||
All four later D05 policies were approved by the actual human on 2026-10-18.
|
||||
D05_REMAINING_APPROVAL.json preserves exact answers and binds both supplied
|
||||
records by path/SHA256. Dependency hold, 18:00 and A01 upkeep remain approved.
|
||||
The old candidates are superseded, not deleted.
|
||||
|
||||
Frozen M is c0ea009b28a7649aca6a433a1a24dc0be5e7ab81. Actual tree enumeration
|
||||
found 1368 product files excluding the checker and 3644 metadata paths.
|
||||
Native/full selected SourceRefs must additionally include the checker.
|
||||
Final S, genuine non-production Q and later normal C are not supplied by this
|
||||
worker. No Git writes, engine execution, Runner operations or delegation.
|
||||
|
||||
The checker has partial authority/role corrections; the seven-domain audit uses
|
||||
M source. S5 executable probes, fresh-Q collector/wrapper/index plumbing,
|
||||
full-proof joins and independent normal-C qualification remain incomplete.
|
||||
Normal C now fails closed without the authoritative Root/Runner receipt contract.
|
||||
The isolated Git fixtures remain Root-run because `/tmp` creation was denied.
|
||||
See [REPAIR31_STATUS.md](REPAIR31_STATUS.md) for precise remaining work and the
|
||||
corrected Root execution order; [s5/PENDING.md](s5/PENDING.md) records S5 gaps.
|
||||
|
||||
Historical S1–S4/r22, old registered validation and f5 full178 retain their
|
||||
original identities. They are not fresh Q evidence. All 15 conditions plus EV
|
||||
still require exact-version evidence; nativeWindows remains individually
|
||||
pending. Static fixture results confer no original acceptance credit.
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"origin": "direct_user_reply",
|
||||
"formal_acceptance": false,
|
||||
"source_bindings": [
|
||||
{
|
||||
"path": "build/owner-handoff/p04-after-p26-closure-r01/D05_ACTUAL_HUMAN_APPROVAL.json",
|
||||
"sha256": "cd365ded1b777a0327d090709f0a4161f5005562819183455d9f2e106fba3e96"
|
||||
},
|
||||
{
|
||||
"commit": "c0ea009b28a7649aca6a433a1a24dc0be5e7ab81",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01-owner-approval-20261018/D05_ACTUAL_HUMAN_APPROVAL.json",
|
||||
"sha256": "cd365ded1b777a0327d090709f0a4161f5005562819183455d9f2e106fba3e96"
|
||||
}
|
||||
],
|
||||
"approved_choices": [
|
||||
{
|
||||
"id": "D05-P04-DESTRUCTION",
|
||||
"source_id": "destruction_completion_order",
|
||||
"answer_exact": "現行順序を承認する(Opus推奨)",
|
||||
"approved_question_policy": "コマンド破壊をサービス完了前に処理。事故・襲撃による破壊は完了・課金後に処理してその分の売上を有効とし、次の分で失敗処理と予約解放。"
|
||||
},
|
||||
{
|
||||
"id": "D05-P04-MIDNIGHT-DAY",
|
||||
"source_id": "midnight_revenue_day",
|
||||
"answer_exact": "新しい日に計上し、前日決算・維持費を完了より先に確定する(Opus推奨・現状維持)",
|
||||
"approved_question_policy": "00:00完了のサービス売上は新しい日に計上。前日決算・維持費は完了より先に確定。既承認の維持費規則と18:00交代順序を維持。"
|
||||
},
|
||||
{
|
||||
"id": "D05-P04-PAUSE-REASON",
|
||||
"source_id": "pause_reason_priority",
|
||||
"answer_exact": "2点とも現行動作を確定する(Opus推奨)",
|
||||
"approved_question_policy": "停電と依存先休業が同時なら休業を理由表示の優先とする。どちらも進捗・課金停止、予約保持。"
|
||||
},
|
||||
{
|
||||
"id": "D05-P04-EXIT",
|
||||
"source_id": "exit_command_ai_order",
|
||||
"answer_exact": "2点とも現行動作を確定する(Opus推奨)",
|
||||
"approved_question_policy": "退出コマンドは完了前、AIの予定退出は判断中の客だけを対象として完了後に処理。"
|
||||
}
|
||||
],
|
||||
"prior_approved_rules_preserved": [
|
||||
"依存先休業中は進捗・課金停止、予約保持、復帰後1回だけ完了",
|
||||
"17:59→18:00昼番作業を先に確定し完了・課金後に交代",
|
||||
"既承認維持費規則"
|
||||
],
|
||||
"supersedes": [
|
||||
"D05_REMAINING_ORDER_CANDIDATE.md",
|
||||
"D05_REMAINING_ORDER_CANDIDATE.json"
|
||||
],
|
||||
"original_unit_complete": false
|
||||
}
|
||||
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"revision": "repair29",
|
||||
"status": "superseded by D05_REMAINING_APPROVAL.json; candidate text below is historical",
|
||||
"approval": "D05_REMAINING_APPROVAL.json",
|
||||
"apply_to_product_or_checker": false,
|
||||
"review_flow": "Root asks owner after review. This document neither requests approval from worker nor records it.",
|
||||
"proof_legend": {
|
||||
"R": "B/audit-support/p04-raw-collect-r22",
|
||||
"I": "B/p04-evidence/index.json schema2",
|
||||
"pins": "REPAIR29_STATIC_SOURCE.json; R/MANIFEST.json and I raw_references/versions"
|
||||
},
|
||||
"choices": [
|
||||
{
|
||||
"id": "a",
|
||||
"topic": "Power vs closure/dependency",
|
||||
"status": "pending D05",
|
||||
"observation": {
|
||||
"status": "observed-current",
|
||||
"behavior": "RELATED_FACILITY_INACTIVE selected before NO_POWER; both pause service with no charge.",
|
||||
"source": "FacilityService _prepare_service_for_tick:853–874, phase FACILITY_COMPLETION after MANA projection",
|
||||
"proof": "Root-measured R/s4.json isolated NO_POWER; R/s3.json B1 combined RELATED diagnostic; REPAIR29_STATIC_SOURCE.json source pins"
|
||||
},
|
||||
"inputs": "Same service and minute, related facility inactive and power unavailable; include direct closure separately under D-034.",
|
||||
"A": {
|
||||
"recommended": true,
|
||||
"rule": "Keep current RELATED-first reason selection.",
|
||||
"outcome": "Service held, progress/payment paused, reservation and serviceID retained.",
|
||||
"side_effects": "Reason reports dependency while both causes apply; no new hold limit.",
|
||||
"dependencies": "Approved dependency hold, D-034 direct closure, existing MANA-before-completion order."
|
||||
},
|
||||
"B": {
|
||||
"rule": "NO_POWER wins reason selection when both conditions fail.",
|
||||
"outcome": "Same no-charge hold safety; reason reports power.",
|
||||
"side_effects": "Reason/event and recovery-precedence semantics would change; concrete implementation and exact-version proof require normal repair.",
|
||||
"dependencies": "Must retain no-charge while either legitimate hold persists; preserve approved dependency reservation/identity behavior."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "b",
|
||||
"topic": "Destruction",
|
||||
"status": "pending D05",
|
||||
"observation": {
|
||||
"status": "observed-current",
|
||||
"behavior": "Command destruction in APPLY precedes completion. Incident destruction in INCIDENTS follows it; B4a shows25G charge then destruction in same tick.",
|
||||
"source": "Runtime _process_tick_commands:5743, _advance_facilities:8482, _advance_defense:8577 / _apply_defense_outcome_boundary:9804",
|
||||
"proof": "Root-measured R/s3.json B4a/B4b; REPAIR29_STATIC_SOURCE.json"
|
||||
},
|
||||
"inputs": "A service due for completion in a minute with command or incident destruction of its facility.",
|
||||
"A": {
|
||||
"recommended": true,
|
||||
"rule": "Keep command-before-completion and incident-after-completion behavior.",
|
||||
"outcome": "Command destruction prevents completion; incident may follow a valid completion/charge that minute; next-tick broken failure/release remains.",
|
||||
"side_effects": "Completion and destruction can coexist in one tick as B4a; not a universal no-sale-on-destruction rule.",
|
||||
"dependencies": "P04 stopped-charge and orphan-reservation safety; explicit D05 destruction authority required."
|
||||
},
|
||||
"B": {
|
||||
"rule": "An incident in the same minute suppresses that minute completion (or voids it if already posted).",
|
||||
"outcome": "No retained completion/sale for the incident-destruction minute.",
|
||||
"side_effects": "Would require preemption or atomic reversal of completion, inventory, payment and rewards; no unapproved numbers or refund amounts supplied.",
|
||||
"dependencies": "Owner must specify the suppress/void transaction semantics; preserve idempotency, no orphan reservations and approved shift/hold rules; normal implementation and new proof required."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "c",
|
||||
"topic": "Exit",
|
||||
"status": "pending D05",
|
||||
"observation": {
|
||||
"status": "observed-current",
|
||||
"behavior": "Command exit APPLY before completion/charging; AI PLANNED_EXIT after them; LEAVING despawns later.",
|
||||
"source": "Runtime _execute_leave_facility_queue:7553; _advance_guest_ai_decisions:8738–8762",
|
||||
"proof": "Root-measured R/s3.json B2a/B2b/B3 and B1 departure history; source pins REPAIR29_STATIC_SOURCE.json"
|
||||
},
|
||||
"inputs": "Exit command or AI exit due at a minute with a completion-ready service. Current AI PLANNED_EXIT applies to DECIDING guests; do not assume it preempts an active service.",
|
||||
"A": {
|
||||
"recommended": true,
|
||||
"rule": "Keep current command/AI distinction and state predicates.",
|
||||
"outcome": "Command leave/release happens first; eligible AI planned exit follows completion/charging and queue processing.",
|
||||
"side_effects": "AI due time alone does not universally cancel current-minute completion; later despawn remains separate.",
|
||||
"dependencies": "D-034, reservation ownership/release, phase6 completion before phase10 AI."
|
||||
},
|
||||
"B": {
|
||||
"rule": "An exit due this minute preempts completion.",
|
||||
"outcome": "Applicable due exits release/cancel before completion and no completion sale occurs for that preempted service.",
|
||||
"side_effects": "Changes eligibility for exit and service cancellation; would change simultaneous completion/payment observations.",
|
||||
"dependencies": "Owner must define applicable due-exit states; preserve no orphan reservation, no duplicate charge and approved dependency/shift behavior; normal repair and exact proof required."
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "d",
|
||||
"topic": "Cost time/day attribution beyond A01 and18:00",
|
||||
"status": "pending D05",
|
||||
"observation": {
|
||||
"status": "observed-current",
|
||||
"behavior": "Sale dated at post-increment minute. At00:00 TIME_BOUNDARIES settlement/upkeep precede completions;00:00 sales go to new day because daily-close period_end is exclusive.",
|
||||
"source": "Kernel advance_one_minute:122; Runtime _apply_service_transaction:11785 /11826–11838; coordinator _process_daily_close:1125 /1136",
|
||||
"proof": "Root-measured R/s2a.json paid midnight/replay; I C; REPAIR29_STATIC_SOURCE.json"
|
||||
},
|
||||
"inputs": "Service completes on calendar boundary; account period ends at boundary minute. A01 ownership upkeep and18:00 shift interval remain fixed.",
|
||||
"A": {
|
||||
"recommended": true,
|
||||
"rule": "Keep post-increment sale time and new-day attribution for00:00 sales.",
|
||||
"outcome": "Closing day excludes boundary-minute completion; new day receives sale.",
|
||||
"side_effects": "Closing summary is finalized before those sales; upkeep ownership rule unchanged.",
|
||||
"dependencies": "Exclusive daily-close interval, sale timestamp, idempotent close/replay; P04 mandatory timing/attribution separate from D04/P05 numeric cost math."
|
||||
},
|
||||
"B": {
|
||||
"rule": "Boundary-minute sales belong to closing day.",
|
||||
"outcome": "Closing day includes those sales.",
|
||||
"side_effects": "Would require a distinct accounting attribution or revised boundary transaction sequencing; affects daily summaries/reconciliation/replay. No numeric cost policy is invented.",
|
||||
"dependencies": "Owner must settle attribution mechanics and same-boundary replay; preserve A01 and18:00, all P04 safety criteria; normal repair and exact-version proof required."
|
||||
}
|
||||
}
|
||||
],
|
||||
"bundle": {
|
||||
"label": "accept current-behavior A for a-d",
|
||||
"status": "pending D05",
|
||||
"recommended": true,
|
||||
"coupling_notes": [
|
||||
"a preserves the approved hold and no-charge safety independent of reason priority.",
|
||||
"b and c use the same completion boundary: commands before, incidents and AI after. Changing either requires separate applicability and replay checks.",
|
||||
"d relies on TIME_BOUNDARIES before completion; changing attribution can affect the transaction history used by b/c and settlement idempotency.",
|
||||
"A01 ownership upkeep, dependency hold and18:00 interval are already approved and are excluded from renewed approval.",
|
||||
"Owner may explicitly rule some remaining items non-mandatory; no such ruling exists here. Unknowns stay pending; mandatory acceptance is never relaxed."
|
||||
]
|
||||
},
|
||||
"authority_gap": "destruction_stop currently binds BASELINE:183, which is mana depletion, not destruction authority. Explicit D05 destruction rule required. Pin/registry repair only after approval through normal repair.",
|
||||
"power_scope": "S4 NO_POWER uses direct disconnect_nodes; not proof of global depletion, which exists only in B1 combined diagnostic.",
|
||||
"original_unit_complete": false,
|
||||
"superseded_by": "D05_REMAINING_APPROVAL.json"
|
||||
}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
> Superseded: D05_REMAINING_APPROVAL.json records the actual human approval of all four policies. The candidate text below is retained as history.
|
||||
|
||||
# D05 remaining order candidate — repair29
|
||||
|
||||
**Approval candidate only. SOURCE-READY / NOTRUN.** Root asks the owner after review. Nothing is applied to product, checker or registry; observation is not approval. A01 midnight ownership upkeep, dependency hold and18:00 are already approved and are not re-asked.
|
||||
|
||||
Structured choices: [D05_REMAINING_ORDER_CANDIDATE.json](D05_REMAINING_ORDER_CANDIDATE.json). Source path/line/SHA256 pins: [REPAIR29_STATIC_SOURCE.json](REPAIR29_STATIC_SOURCE.json); phase map: [ORDER_AUDIT.md](ORDER_AUDIT.md). R = B/audit-support/p04-raw-collect-r22; I = B/p04-evidence/index.json. These are Root-measured exact-version proof references, with raw/provenance pins in R/MANIFEST.json, I and the static manifest.
|
||||
|
||||
## (a) Power vs closure/dependency — pending D05
|
||||
|
||||
**Observed-current:** RELATED_FACILITY_INACTIVE selected before NO_POWER; both pause service with no charge. Source: FacilityService _prepare_service_for_tick:853–874, phase FACILITY_COMPLETION after MANA projection. Proof: Root-measured R/s4.json isolated NO_POWER; R/s3.json B1 combined RELATED diagnostic; REPAIR29_STATIC_SOURCE.json source pins.
|
||||
|
||||
**Inputs:** Same service and minute, related facility inactive and power unavailable; include direct closure separately under D-034.
|
||||
|
||||
**Option A (recommended; keep current behavior):** Keep current RELATED-first reason selection.
|
||||
|
||||
- Outcomes: Service held, progress/payment paused, reservation and serviceID retained.
|
||||
- Side effects: Reason reports dependency while both causes apply; no new hold limit.
|
||||
- Dependencies: Approved dependency hold, D-034 direct closure, existing MANA-before-completion order.
|
||||
|
||||
**Option B:** NO_POWER wins reason selection when both conditions fail.
|
||||
|
||||
- Outcomes: Same no-charge hold safety; reason reports power.
|
||||
- Side effects: Reason/event and recovery-precedence semantics would change; concrete implementation and exact-version proof require normal repair.
|
||||
- Dependencies: Must retain no-charge while either legitimate hold persists; preserve approved dependency reservation/identity behavior.
|
||||
|
||||
## (b) Destruction — pending D05
|
||||
|
||||
**Observed-current:** Command destruction in APPLY precedes completion. Incident destruction in INCIDENTS follows it; B4a shows25G charge then destruction in same tick. Source: Runtime _process_tick_commands:5743, _advance_facilities:8482, _advance_defense:8577 / _apply_defense_outcome_boundary:9804. Proof: Root-measured R/s3.json B4a/B4b; REPAIR29_STATIC_SOURCE.json.
|
||||
|
||||
**Inputs:** A service due for completion in a minute with command or incident destruction of its facility.
|
||||
|
||||
**Option A (recommended; keep current behavior):** Keep command-before-completion and incident-after-completion behavior.
|
||||
|
||||
- Outcomes: Command destruction prevents completion; incident may follow a valid completion/charge that minute; next-tick broken failure/release remains.
|
||||
- Side effects: Completion and destruction can coexist in one tick as B4a; not a universal no-sale-on-destruction rule.
|
||||
- Dependencies: P04 stopped-charge and orphan-reservation safety; explicit D05 destruction authority required.
|
||||
|
||||
**Option B:** An incident in the same minute suppresses that minute completion (or voids it if already posted).
|
||||
|
||||
- Outcomes: No retained completion/sale for the incident-destruction minute.
|
||||
- Side effects: Would require preemption or atomic reversal of completion, inventory, payment and rewards; no unapproved numbers or refund amounts supplied.
|
||||
- Dependencies: Owner must specify the suppress/void transaction semantics; preserve idempotency, no orphan reservations and approved shift/hold rules; normal implementation and new proof required.
|
||||
|
||||
## (c) Exit — pending D05
|
||||
|
||||
**Observed-current:** Command exit APPLY before completion/charging; AI PLANNED_EXIT after them; LEAVING despawns later. Source: Runtime _execute_leave_facility_queue:7553; _advance_guest_ai_decisions:8738–8762. Proof: Root-measured R/s3.json B2a/B2b/B3 and B1 departure history; source pins REPAIR29_STATIC_SOURCE.json.
|
||||
|
||||
**Inputs:** Exit command or AI exit due at a minute with a completion-ready service. Current AI PLANNED_EXIT applies to DECIDING guests; do not assume it preempts an active service.
|
||||
|
||||
**Option A (recommended; keep current behavior):** Keep current command/AI distinction and state predicates.
|
||||
|
||||
- Outcomes: Command leave/release happens first; eligible AI planned exit follows completion/charging and queue processing.
|
||||
- Side effects: AI due time alone does not universally cancel current-minute completion; later despawn remains separate.
|
||||
- Dependencies: D-034, reservation ownership/release, phase6 completion before phase10 AI.
|
||||
|
||||
**Option B:** An exit due this minute preempts completion.
|
||||
|
||||
- Outcomes: Applicable due exits release/cancel before completion and no completion sale occurs for that preempted service.
|
||||
- Side effects: Changes eligibility for exit and service cancellation; would change simultaneous completion/payment observations.
|
||||
- Dependencies: Owner must define applicable due-exit states; preserve no orphan reservation, no duplicate charge and approved dependency/shift behavior; normal repair and exact proof required.
|
||||
|
||||
## (d) Cost time/day attribution beyond A01 and18:00 — pending D05
|
||||
|
||||
**Observed-current:** Sale dated at post-increment minute. At00:00 TIME_BOUNDARIES settlement/upkeep precede completions;00:00 sales go to new day because daily-close period_end is exclusive. Source: Kernel advance_one_minute:122; Runtime _apply_service_transaction:11785 /11826–11838; coordinator _process_daily_close:1125 /1136. Proof: Root-measured R/s2a.json paid midnight/replay; I C; REPAIR29_STATIC_SOURCE.json.
|
||||
|
||||
**Inputs:** Service completes on calendar boundary; account period ends at boundary minute. A01 ownership upkeep and18:00 shift interval remain fixed.
|
||||
|
||||
**Option A (recommended; keep current behavior):** Keep post-increment sale time and new-day attribution for00:00 sales.
|
||||
|
||||
- Outcomes: Closing day excludes boundary-minute completion; new day receives sale.
|
||||
- Side effects: Closing summary is finalized before those sales; upkeep ownership rule unchanged.
|
||||
- Dependencies: Exclusive daily-close interval, sale timestamp, idempotent close/replay; P04 mandatory timing/attribution separate from D04/P05 numeric cost math.
|
||||
|
||||
**Option B:** Boundary-minute sales belong to closing day.
|
||||
|
||||
- Outcomes: Closing day includes those sales.
|
||||
- Side effects: Would require a distinct accounting attribution or revised boundary transaction sequencing; affects daily summaries/reconciliation/replay. No numeric cost policy is invented.
|
||||
- Dependencies: Owner must settle attribution mechanics and same-boundary replay; preserve A01 and18:00, all P04 safety criteria; normal repair and exact-version proof required.
|
||||
|
||||
## Bundle: accept current-behavior A for a-d
|
||||
|
||||
Recommended candidate, not approved.
|
||||
|
||||
- a preserves the approved hold and no-charge safety independent of reason priority.
|
||||
- b and c use the same completion boundary: commands before, incidents and AI after. Changing either requires separate applicability and replay checks.
|
||||
- d relies on TIME_BOUNDARIES before completion; changing attribution can affect the transaction history used by b/c and settlement idempotency.
|
||||
- A01 ownership upkeep, dependency hold and18:00 interval are already approved and are excluded from renewed approval.
|
||||
- Owner may explicitly rule some remaining items non-mandatory; no such ruling exists here. Unknowns stay pending; mandatory acceptance is never relaxed.
|
||||
|
||||
destruction_stop currently binds BASELINE:183, which is mana depletion, not destruction authority. Explicit D05 destruction rule required. Pin/registry repair only after approval through normal repair.
|
||||
|
||||
S4 NO_POWER uses direct disconnect_nodes; not proof of global depletion, which exists only in B1 combined diagnostic.
|
||||
|
||||
P04 timing/attribution remains mandatory; D04/P05 numeric cost math is separate. No acceptance criterion is relaxed. P04 remains incomplete; original13/88 unchanged.
|
||||
+19
-224
@@ -1,233 +1,28 @@
|
||||
# P04-01: C段階の結合とrepair4収集契約の履歴
|
||||
# Evidence collection — historical repair29 description
|
||||
|
||||
## repair6 現在値(2026-10-16、以下の旧blocked記述に優先)
|
||||
Repair30 supersedes current-use claims below: existing S1–S4/r22/full178 records
|
||||
remain historical. New Q evidence is required, with checker explicitly selected.
|
||||
The fresh-Q collection path and S5 drivers are not ready in this partial repair.
|
||||
See REPAIR30_STATUS.md; do not start native/full or formal acceptance yet.
|
||||
|
||||
旧値: 実applicabilityは `native ordering bytes` で拒否、C全体blocked、
|
||||
whole native未確認。新値: 補正済みbundleのverify_native成功、
|
||||
ownership/save/ledger個別検査とapplicability成功。供給bundleへの書込みは0。
|
||||
全文rawの登録版/production一致と両body SHAの全文SHA一致だけを認める。
|
||||
後続コメント除外prefixを許す暫定処理は削除した。
|
||||
Worker **SOURCE-READY / NOTRUN** (static Read/SHA256 only). No probes, tests, engine or Runner execution in this repair. [CURRENT_STATUS.md](CURRENT_STATUS.md) overrides historical creation-time NOT RUN entries in REPAIR22–28 and runbooks, which remain untouched. Do not repeat their completed execution steps merely because their original prose says pending.
|
||||
|
||||
下記3本のPythonコマンドを各1回実行しexit 0を確認。
|
||||
native fixtureは既存23負例+signature行SHA/コメント除外SHAの2負例を全て拒否。
|
||||
合成補正コピーは廃止し、補正済み供給rawをそのまま正対照に使用。
|
||||
既存fixture14件拒否、A/Bの登録版・production版・行移動正対照成功、3負例拒否、
|
||||
native applicability成功。実行結果、旧検査値、新旧bundle pin、検査コードSHAは
|
||||
c-native-validation.jsonに記録。verify_nativeは有限C検査でありwhole gateは未実行。
|
||||
原未達の条件、formal_original_credit=0、15条件+EV/7領域/安全性3結果、
|
||||
30/88の分母、予算、repair上限を維持する。Root追加paid/再送は今回の範囲外。
|
||||
engine、全回帰、製品、Runner、config、DB、git書込み、他agentは全て0。
|
||||
正式検証・独立レビュー・オーナー判断待ち。以下の「今回」は旧検査履歴を指す。
|
||||
B = `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
|
||||
## C段階の追補対象
|
||||
| Completed material | Current fact (Root-measured) |
|
||||
| --- | --- |
|
||||
| B/p04-s4-native-r28 | Four phases PASS; PIDs49940,50044,50078,50105 all ended, guards0, cleanup0. |
|
||||
| B/audit-support/p04-root-fb8b-s4-r01 | S4 control/native receipts preserved. |
|
||||
| B/audit-support/p04-root-fb8b-local-r01 | Local8 PASS; registered validation on fb8b PASS28.095s. |
|
||||
| B/audit-support/p04-raw-collect-r22 | Completed collection of preserved C/series applicability; no new C/S1/S2a/S2b/S3 native runs. |
|
||||
| B/p04-evidence | Current schema2 index/generation invocation, missing_conditions=[], all15 conditions + EV mechanically verified. |
|
||||
|
||||
供給rawの現在位置は `build_dir/audit-support/native-first-current/`。
|
||||
所有権、台帳・service・予約、正式保存の結合を追加する対象であり、
|
||||
この資料の存在だけでは原受入を満たさない。formal_original_creditは0を維持する。
|
||||
Native product5f6bc11d and main9b1a18c1 remain the Root-measured versions. REPAIR29_STATIC_SOURCE.json hashes current B/audit-support/p04-r17-full-inputs-r01/product (1338 files), not old deb11300, and pins current collection/index. Per-series raw/source/config/input/environment joins remain in the immutable collection and schema2 index; this worker neither regenerates nor normalizes raw.
|
||||
|
||||
## 今回のC実装と入力blocker
|
||||
S4 is isolated NO_POWER from direct disconnect_nodes, not global mana depletion. Global depletion is observed only in B1 combined diagnostic. B1 supplies no isolated power_stop or priority approval. Destruction authority BASELINE:183 is a known wrong category (mana depletion); explicit owner D05 destruction rule is required before normal registry/pin repair. Product/checker/registry remain unchanged here.
|
||||
|
||||
変更はchecker、Python fixtureと本ディレクトリ内のdocsだけ。
|
||||
Root helperはコピー・再実装せず、既存native成果物を読む。
|
||||
結合の正確な規則はCHECKER_PROTOCOL.mdのC追補を参照。
|
||||
新規engine/全回帰/Runner操作/git書込み/他agent起動は全て0。
|
||||
旧39rawを読了・採用した数も0。repair4-context.jsonの旧履歴は保持した。
|
||||
Full178 used checker **f5**,21615 checks; exact receipt preserved at B/audit-support/p18-regression-support-final-r01/native-full178/runner-ended.json and exact selected inputs at runner-input.json selected_source_map (source-start.json records initial product sources). f5 SHA256 is f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324. Product suites/316-file GDScript parse do not execute Python; this does not establish an exact1339 proof against a61. Static comparison with the full178 selected_source_map found one test-source difference (tests/business/test_required_kitchen.gd: full3a9c92f8…, current5dcc86d6…) and no game-source difference. Runner must retain explicit applicability for that test; no blanket product1338 identity with that full178 receipt is claimed. **No full1339 run exists against a61** (a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850). Keep the old full and partial receipts distinct and intact.
|
||||
|
||||
供給rawでは次の個別Python検査が成功した。
|
||||
Remaining steps belong to Runner: review this repair; Root obtains owner D05(a–d) decisions or explicit non-mandatory rulings; unknown mandatory rules remain pending. Any approved checker/registry/product change uses a normal repair. Perform support53cb normal registered rebind, final full run against the final source/checker and final source/config/input/environment applicability binding, independent review and integration (P04 first, then support on main9b1a18c1). NativeWindows obligations stay individually pending if required. This document does not launch or authorize duplicate probes and does not replace Runner scheduling.
|
||||
|
||||
- OWNERSHIP_RAW_PROVENANCEの96 fileのbytes/SHAと10プロセスのlifecycle/cleanup。
|
||||
- Writer/Readerの5点×4member、元20member不変、typed .var 14件の実SHA結合。
|
||||
- 生receipt 8行、service.paymentのidempotency経由での1回課金、財布差分、
|
||||
PAUSED時の進捗・予約保持、復帰後の消費、維持費発生1439→支払い1440。
|
||||
|
||||
ただし `POST_P18_CURRENT_SOURCE_APPLICABILITY.json` の4関数の
|
||||
registered_body_sha256/production_body_sha256は本文を含まない。
|
||||
実Git blobで検算すると、以下の関数先頭からのprefix SHAに一致した。
|
||||
|
||||
| path | function | hash対象のbyte数(本文を含まない) |
|
||||
|---|---|---:|
|
||||
| autoload/simulation_runtime.gd | _complete_service_transaction | 140 |
|
||||
| scripts/facility/facility_service.gd | _prepare_service_for_tick | 87 |
|
||||
| scripts/facility/facility_service.gd | _complete_service | 113 |
|
||||
| scripts/facility/facility_service.gd | _fail_service | 119 |
|
||||
|
||||
4つとも複数行引数の後の `) -> ...` より前で終わる。全文比較は別途成立するが、
|
||||
報告されたbody hashとの一致は成立しないため、実bundleは
|
||||
`native ordering bytes` でfailする。workerはこの供給資料もmanifestも変更していない。
|
||||
Root側で同じproduction Git blobの全文を対象にapplicabilityとそのmanifestを
|
||||
再生成する必要がある。engineの再起動を必要とする不整合ではない。
|
||||
旧暫定処理では後続トップレベルdoc commentの有無だけの2関数差をraw prefix一致
|
||||
として扱っていた。repair6で撤去し、現在は全文SHA一致のみ必須とする。
|
||||
|
||||
この入力不整合が残るため、C段階の実raw全体の成功は未確認(blocked)。
|
||||
これは原P04-01未達とは別のblockerであり、修正済みの合成コピーで全C検査が
|
||||
通ることを原証拠の合格へ換算しない。
|
||||
|
||||
## 有限の検証
|
||||
|
||||
実行したのは次のPython確認のみ。全て一時ファイルをallowed_paths内へ限定し削除する。
|
||||
|
||||
```sh
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p04-01/checker_native_fixtures.py /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build/audit-support/native-first-current
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p04-01/checker_negative_fixtures.py
|
||||
python3 -B docs/plans/original-completion-2026-10-02/execution/p04-01/checker_applicability_fixtures.py --native-bundle /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build/audit-support/native-first-current
|
||||
```
|
||||
|
||||
Cの結果は `c-native-validation.json`。23負例を拒否し、実rawの3個別検査成功、
|
||||
実applicability拒否、修正した合成applicabilityの組合せ正対照成功を区別する。
|
||||
既存fixtureはscalarのみの旧正対照を含む14件を拒否。
|
||||
A/Bは登録版・production版の正対照と行移動の正対照、3負例、C資料の拒否を確認した。
|
||||
未収集indexに対するwhole gate/validateは再実行していない。
|
||||
|
||||
## 固定の原未達
|
||||
|
||||
非ゼロ給与のシフト交代×00:00、command再送、停電→閉店同Tick、退出/予約孤立、
|
||||
破壊、EVは未収集。正式保存のSHA同一性は全原条件のsave_restore証明とは別。
|
||||
15条件、reachable EV、7領域、安全性3結果を減らさず、未収集をfalse/pending扱いとする。
|
||||
原30/88のformal_original_credit=0、独立レビュー・通常統合未実施を維持する。
|
||||
|
||||
以下はrepair4時点の履歴であり、C追補の実装・検証状態には上記が優先する。
|
||||
|
||||
本修正は未完成であり、収集開始・machine gate・原受入の承認ではない。
|
||||
原30/88、7領域、部分点0を維持する。D05全体はpending、既存の承認2条件を
|
||||
超える規則は作らない。D-034/A01の再承認は求めない。
|
||||
|
||||
## 読取確認
|
||||
|
||||
`.review-material` はこのcheckoutには存在しない。指定された実体
|
||||
`build_dir/audit-support/p04-native-existing-sources/` のwrapper 4本とGD probe
|
||||
6本、および `tools/run_all_tests.py`、`tools/regression/owned_outer.py` を確認した。
|
||||
10本のraw SHA/sizeは配布SOURCE_MANIFEST.jsonと一致し、`repair4-context.json`
|
||||
に保存した。外部旧worktreeのartifactパスは由来としてのみ扱った。
|
||||
|
||||
実checkout HEADは `9767b0a28ca8d252520307dbbac60a1d8cfb87ab`、Runtimeは
|
||||
`d52a995ae715542bbc550f68d35889540658275d12e04ae0b21ac276f5843f39`。
|
||||
提供されたproduction資料の版は `5f6bc11d6bb6e92d22d787989b219fef3a435bc1`、
|
||||
Runtimeは `7f01b2dcdf28bc965c7588173c2dc70054633faf7204b1ac38ba0f4369331b47`。
|
||||
productionのGit objectも存在する。production資料を実checkoutのsource_mapとして
|
||||
流用しない。登録版は `deb1bcb2374eb378b840ca345db952bd8572cb6e`。
|
||||
|
||||
## A/Bの変更
|
||||
|
||||
checkerはspecのcurrent_source_pinsを登録pinとして保持し、登録Git blobのSHAを
|
||||
独立計算する。indexはregistered_pinsとversionsのexecution_origin/registered/
|
||||
tested/integrationを分離する。integrationは未統合のためnullのみ。
|
||||
testedのautoload/scripts全GDはcheckoutとHEAD blobの一致を必須にする。
|
||||
|
||||
20関数の抽出はchecker内固定。行頭func/static funcから次の行頭関数までの
|
||||
raw bytesを比較し、コメント・空白・改行を正規化しない。先頭宣言は
|
||||
`<top-level>` 単位としてdiffに含める。追加・削除関数も含む。
|
||||
`git diff --no-ext-diff --no-renames --name-only -z base..HEAD -- autoload/ scripts/`
|
||||
で変更GDを列挙し、Git blobから変更関数を計算する。applicability_deltaは
|
||||
path/function順の配列と完全一致させる。未知の変更も無影響と推定せず、
|
||||
delta_casesに保守的な全条件被覆を要求する。境界case IDの存在は後続coverageで
|
||||
検証するが、**Cの生証拠結合は未実装なので、この段階では適用可の最終判断に使えない**。
|
||||
|
||||
REACH_REFSはpath/function/anchor_sha256/line_countに変更。実tested関数内で
|
||||
本文raw bytesのアンカーを一意に解決する。旧固定範囲が隣接関数へ跨がる
|
||||
reserve/rescue箇所は関数単位に分割した。行位置が7行移動しても解決し、
|
||||
本文変更・欠落・重複アンカーは失敗する。
|
||||
|
||||
## 新collectorを作らず残す既存実行パターン
|
||||
|
||||
Root側の既存wrapper修正で保持すべき順序は、`r.source_refs(R)` →
|
||||
明示QA上書きしたrefs → `r.copy_source` によるprivate stage → bootstrap →
|
||||
cacheの `cp -c` と別inode確認 → parse-paths/contracts → `execute_phase` →
|
||||
finallyの `owned_outer.finish_captured` → fixed_guardと不変性確認。
|
||||
このworkerはwrapperを起動・変更していない。適用可能なパラメータ化patchと
|
||||
全不足probeはまだ提出できていないため、以下は実行済みの契約ではなく残作業である。
|
||||
|
||||
- R/OUT/QA/testpath/cache/input manifestは最終tested checkoutに対して固定する。
|
||||
1299件数や旧source-after.json一致をauthorityにしない。現r.source_refsがauthority。
|
||||
- checkout mapと、testpathにQAを明示上書きしたselected mapを別に記録する。
|
||||
source_map=tested、probeの元pathとSHA、QA_overrides、実stageの同SHAを結合する。
|
||||
候補製品source overrideは持ち込まない。
|
||||
- source_before/after、default_player_before/after、doc4_before/after、stage inventory、
|
||||
parse-paths、contracts、wrapper raw、phase receipt、started/ended、
|
||||
outer-owned-finally、execution.jsonを保存する。booleanだけでは不変性の代用にならない。
|
||||
- case receiptはphaseのactual_pidとexecution.actual_captured_PIDs、cleanupの全PID集合に
|
||||
結合する。cleanup_errorsが空、全childのengine_ended、終了codeを実成果物間で照合する。
|
||||
- 各条件のscalarに加え、生台帳・service/settlement ID・予約配列とのcount_eqまたは
|
||||
same_literalが必要。save_restoreはWriter保存直前と正式Reader復元直後の
|
||||
台帳・予約のsame_literal、および別PIDを要求する。nonceやログ文言では代用しない。
|
||||
|
||||
## 負履歴(起動禁止)
|
||||
|
||||
10本すべてのSHAはrepair4-context.jsonに記録した。特に以下を再実行しない。
|
||||
|
||||
- shift-end-repro-r03.gdは1080で「課金なし・予約保持」を期待し、承認済みの
|
||||
完了段階先行と矛盾する。対応native-r03.pyには旧1299 pinもある。
|
||||
- formal-boundary-reload-native-r03.pyは旧candidate sealと製品2件overrideを使う。
|
||||
SHAを書換えて別候補receiptとして使わない。
|
||||
- formal-boundary-reload-r01.gdのmanifest.source_refs==1299も旧前提。
|
||||
- midnight native-r01とpower native-r04も固定1299と旧full-run cacheに依存する。
|
||||
power readerは旧Writerの二つの正式保存slotに依存し、fresh Writerではない。
|
||||
|
||||
## 15+EVの被覆確認(未収集)
|
||||
|
||||
同一プロセス内でも条件ごとのseed、before/after、case receiptを保持する。
|
||||
以下のartifact名は既存probeが出すもの。不足欄を合格扱いにしない。
|
||||
|
||||
| 条件 | 既存probe / phase / artifact | 不足 |
|
||||
|---|---|---|
|
||||
| midnight_settlement, midnight_replay | midnight-boundary-r01 / repro / observations.json, midnight-*.var | countとraw台帳の結合、同minute再送の明示 |
|
||||
| paused_midnight | midnight-boundary-r01は開業サービスのみ | 停止した同一serviceの日跨ぎ系列 |
|
||||
| night_staff_absent | shift-end-repro-r03 / repro / 18-01観測 | 旧誤期待を除いた独立caseと非完了service |
|
||||
| power_stop, recovery_completion | power-formal-recovery-r04 / reader-a,b / recovery-*.var | fresh停止入力、台帳JSON、旧Writer依存の解消 |
|
||||
| dependency_stop | 供給6 probe内に専用系列なし | 関連施設停止・進捗/予約/service ID保持 |
|
||||
| exit_release | exit-boundary-r01 / repro / queued-*.var, observations.json | raw stateとcount結合 |
|
||||
| patience_release, closure_release | 供給6 probe内に専用系列なし | 待機解放、開始済み継続を区別したcase |
|
||||
| same_minute_replay | exit/destruction/midnightはsnapshot巻戻し再実行 | 同minuteコマンド再送の独立条件 |
|
||||
| shift_completion | shift-end-repro-r03 / repro / 18-00各phase観測 | 誤期待の修正、課金1回のraw結合 |
|
||||
| overtime_task | 供給6 probe内に専用系列なし | interrupt_level=0残業・勤務記録1回 |
|
||||
| destruction_stop | destruction-completion-boundary-r02 / repro / unfinished-*.var | raw台帳結合。completeは別帰結として維持 |
|
||||
| save_restore | formal-boundary-reload-r01 / writer,reader / *.var, writer.json, reader.json | final版の入力seal、JSON台帳/予約、owned PID結合 |
|
||||
| EV | 供給6 probe内に専用系列なし | staff予約→EV移動→停止→到着の実状態 |
|
||||
|
||||
transportはreach=falseのソース根拠のみで、別engine条件は追加しない。
|
||||
|
||||
## 再利用の有限条件と未供給資料
|
||||
|
||||
input.jsonのevidence_refsは旧証拠の参照であり、生証拠そのものではない。
|
||||
今回の供給先で確認できたのはsource、監査、適用性診断とgate失敗ログである。
|
||||
旧39件のraw receipt/state/packageを照合して採用した件数は0。
|
||||
「20関数一致」だけで39件を採用することは禁止する。
|
||||
|
||||
必要な追加のローカルraw資料:
|
||||
|
||||
1. 旧39件のexecution/phase/started/ended/outer-owned-finally/log/state一式と
|
||||
元source/config/input/environment pin。既存input.jsonのreference SHAで照合する。
|
||||
2. formal-boundary-reload-input-r02.json
|
||||
(wrapper記録SHA `195cb767e597008d1767aa0b6c8300711b83e8cf101863166e7932d9bd4ed612`)
|
||||
と、そのwhole_typed_inputs、元exact_execution、保存package全4メンバー。
|
||||
3. power original writer.json
|
||||
(wrapper記録SHA `cf3850c262012e5e52743bfb8b41af7ef5608689fb10c059c8a494216b0aff52`)
|
||||
と参照slot。旧candidateは比較用負履歴に限定する。
|
||||
|
||||
原則は最終の正確な版で再実行する。登録版証拠を再利用する有限条件は、A(i)の
|
||||
20関数raw一致、A(ii)の全delta一致、A(iii)の変更境界をtested engine caseで被覆し、
|
||||
さらに当該旧caseのsource/config/input/environmentとraw状態を独立照合できること。
|
||||
relationship restore/schema変更は正式Writer/Reader再実行、build/path/arrivalに
|
||||
関係する変更は当該経路に到達するcaseの再実行が必要。今回の資料だけでは再利用0。
|
||||
|
||||
## Rootのコマンドと残る作業
|
||||
|
||||
現在実行できるのは次のPython確認のみ(engineプロセス0)。
|
||||
|
||||
```sh
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p04-01/checker_applicability_fixtures.py
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p04-01/checker_negative_fixtures.py
|
||||
BUILD_OUTPUT_DIR=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build python3 tools/check_p04_order_evidence.py --verify-only
|
||||
```
|
||||
|
||||
最後のコマンドはindex未収集ならexit 1が期待値。Python正対照はengine証明ではない。
|
||||
**Cのchecker強化、Dのparameter patch/不足probe、Eのowned/count/restore/他候補流用負例は未完成。**
|
||||
旧checkerのscalar/receipt自己申告を受入可能な証拠とみなしてはならない。
|
||||
現在のcheckerを正式受入に使用せず、修正全体をblockedとしてRootへ返す。
|
||||
|
||||
native収集の正確なコマンド列は、上記入力と未完成patchがないため提示できない。
|
||||
仮のコマンドを実行可能と偽って提示しない。共有stageでimport+parseを2プロセス、
|
||||
midnight/power/exit/shift/destruction/save Writer/save Reader/EVを8プロセスに
|
||||
束ねられれば計10プロセスが計画目安になるが、これは確定数・目標数ではない。
|
||||
条件ごとに必要な状態系列とguard成立を優先する。
|
||||
Rootのnative収集→validate→別セッション独立レビュー→通常統合は未実施。
|
||||
A01 midnight ownership upkeep, dependency hold and18:00 are already approved. P04 timing/attribution remains mandatory, separate from D04/P05 numeric cost math. Original13/88 credit,15 conditions + EV, seven domains, three safety outcomes and30/88 denominator remain unchanged. Original unit is incomplete.
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# Concrete32 external Runner-record boundary
|
||||
|
||||
The supplied normal-gate reference was read directly: verify, has_evidence,
|
||||
prepare_integration, approve and integrate (893–1092), and the SQL schema
|
||||
(474–483). P26 normal-candidate-example files are format samples only.
|
||||
|
||||
`normal_c_join.py` uses read-only SQLite transactions over actual Root-selected
|
||||
Runner records. It joins submitted S, candidate C, frozen main_base M, approval,
|
||||
validation/review on S and integration/final on C. It checks the exact validation
|
||||
roster, raw review result and referenced validation logs, and hashes evidence
|
||||
inputs and the logical record snapshot. Root must establish that both databases
|
||||
are actual Runner records: arbitrary SQLite files cannot authenticate themselves.
|
||||
|
||||
Runner integrate deletes approvals after final verification. Root retains an
|
||||
actual consistent Runner snapshot after approve, supplied as
|
||||
`--approved-runner-db`; `--runner-db` supplies later final evidence. These are
|
||||
read-only inputs, not a new Runner API. The worker creates neither snapshot.
|
||||
Missing records fail. No Git, Runner or engine is invoked. Outputs confer no
|
||||
acceptance or Git/Q/source/settings/markers/policy/engine/input qualification.
|
||||
|
||||
All partial31 files, including the existing checker, remain unchanged as directed.
|
||||
Its NORMAL_C_RECEIPT_CONTRACT fail-closed path therefore still blocks formal C
|
||||
qualification. This new join is not wired into that gate and cannot bypass it.
|
||||
Integration of the boundary remains an implementation gap. No actual Q/C or
|
||||
ordinary-gate proof was produced. This is not a new human D05 policy question.
|
||||
|
||||
Root runs `normal_c_join_fixtures.py --scratch DIR`: synthetic positive, wrong
|
||||
C/M, missing kind, failed check, extra/missing validation ID, task mismatch,
|
||||
medium review, missing artifact, wrong approval and missing final. Unexecuted.
|
||||
`scratch_fixture_adapter.py --scratch DIR --fixture NAME -- ...` routes preserved
|
||||
fixture TemporaryDirectory calls to explicit external scratch and clears inherited
|
||||
GIT_* routing. Existing assertions are unchanged; candidate/role fixtures still
|
||||
exercise partial31 semantics. No fixture output belongs in source.
|
||||
@@ -1,52 +1,28 @@
|
||||
# P04-01 監査状態
|
||||
# P04-01 order audit — repair31
|
||||
|
||||
原単位は未完了。D05-P04-DEPENDENCY と D05-P04-SHIFT-1800 は direct_user_reply により承認済み(同梱 D05_OWNER_APPROVAL.json、SHA256 4583ee9aad45c4db183d72df4420df48aa8e0897e3b5efda13016f0b5cdbd68e)。承認は試験合格を意味しない。A01の00:00保有時間按分維持費は承認済みで再確認しない。
|
||||
Formal status is **16/88, P04 credit 0**. Policy approvals are recorded; implementation and exact-Q evidence remain incomplete. No original-unit completion is claimed.
|
||||
|
||||
正式試験はRunnerが実行する。旧証跡参照は来歴であり、現版の生証跡・ソース・設定・入力・環境の適用性確認なしに合格として再利用しない。
|
||||
D05 is **six of six actually approved**; adoption into raw enforcement and
|
||||
verification are pending. Normal-C qualification now rejects matching-parent/tree
|
||||
commits without the authoritative Root/Runner receipt contract. That contract's
|
||||
path, SHA256 and provenance verification remain a concrete context gap.
|
||||
|
||||
## 現行ソースの観測
|
||||
M is `c0ea009b28a7649aca6a433a1a24dc0be5e7ab81`. Source hashes, full product inventory, metadata membership and function line numbers were read from its actual Git tree in [REPAIR30_SOURCE_FACTS.json](REPAIR30_SOURCE_FACTS.json). Product manifest excludes the checker; native/full selected SourceRefs must include it. Q and C do not yet exist for this worker revision.
|
||||
|
||||
`scripts/core/simulation_kernel.gd:123-153` の順序は APPLY_COMMANDS → 時計増分・日境界signal → TIME_BOUNDARIES → MANA → CONSTRUCTION → LOGISTICS → FACILITY_COMPLETION → VITALS_AND_ENVIRONMENT → INCIDENTS_AND_EVENTS → FACILITY_QUEUES → AI_DECISIONS → PUBLISH_SNAPSHOT。同phase内はhandler IDの辞書順(242行)。Runtimeの登録は364-427行。時計や製品ロジックは変更していない。
|
||||
The four actual human answers are preserved verbatim in [D05_REMAINING_APPROVAL.json](D05_REMAINING_APPROVAL.json), bound to both supplied copies by path and SHA256. Existing dependency, 18:00 and A01 approvals remain unchanged. The candidate documents are superseded history.
|
||||
|
||||
| 領域 | 現行呼出しと先後 | 原本・承認・不足 |
|
||||
| --- | --- | --- |
|
||||
| 閉店 | `_advance_facilities` の後、`_advance_guest_queues` が `_release_closed_business_queues` を実行し、patienceより先に予約解放を試みる。 | 直接休業はD-034の開始済サービス継続。依存先休業は承認済D05の進捗・課金停止、予約・ID保持、復帰後1回完了。旧business試験は部分候補。open serviceの00:00証拠を代用しない。 |
|
||||
| 停電 | `_advance_mana` → `mana_power_projection.apply` → FacilityService `_prepare_service_for_tick`。NO_POWERはpause、復電はresume。 | 草案の961停止/962復電/963不変は生証跡未確認。進捗・予約・帳簿・通知を現版で確認する必要がある。 |
|
||||
| 破壊 | commandはAPPLY_COMMANDSで先行。`_advance_defense` は完了後のINCIDENTS。BROKENはサービス準備時に失敗。 | 先行破壊44と後段完了45を区別。失敗診断も保持し、原本適合と断定しない。 |
|
||||
| 退出 | 公開queue leave → 完了 → INCIDENTS → queue patience解放 → AI再計画/予定退出。LEAVINGのdespawnは入状態分より後。 | 公開leave・自動patience・overdue退出の正式保存/別PID証跡は参照のみ。孤立0を現版で証明する必要がある。 |
|
||||
| 課金 | `_complete_service` callback → `_complete_service_transaction` → `_apply_service_transaction`。予約消費後、service IDによるpayment keyで記帳し、通知scopeを終了。 | 原価格・時間を変えない。静的なidempotency keyの存在や試験数だけで二重課金・二重生成なしとしない。 |
|
||||
| シフト交代 | 完了後の `_advance_staff_ai` 内でstaff更新・commute・staffing投影。 | 原本64/67行の18:00交代を保持。承認済D05により[17:59,18:00)は昼番、18:00完了・課金後に交代。夜番は[18:00,18:01)から。1080診断を承認規約に格上げしない。 |
|
||||
| 00:00決算 | TIME_BOUNDARIESのweather/upkeep/operationsが完了より先。OperationsCoordinatorのdaily closeと同分service売上は別区間。 | 原本71/72行、承認済A01を保持。1440一回/1441不変の生証跡と適用性が必要。給与0fixtureを非ゼロ給与証拠に数えない。 |
|
||||
| Domain | Approved rule | Current M source | Evidence |
|
||||
| --- | --- | --- | --- |
|
||||
| 閉店 | D-034 preserves started services on direct closure and releases waiting requests. Dependency closure stops progress and charging, retains reservations/service identity, and completes once after recovery. RELATED_FACILITY_INACTIVE wins over NO_POWER when both apply. | autoload/simulation_runtime.gd:5765 _process_tick_commands; scripts/facility/facility_service.gd:853 _prepare_service_for_tick; autoload/simulation_runtime.gd:12672 _release_closed_business_queues | Fresh Q pending; nativeWindows pending |
|
||||
| 停電 | BASELINE:183 covers real mana depletion. Legitimately stopped service has no progress or charge and preserves reservations. D05-P04-PAUSE-REASON makes dependency closure the displayed reason when dependency closure and power loss coincide. | autoload/simulation_runtime.gd:10716 _advance_mana; scripts/facility/facility_service.gd:853 _prepare_service_for_tick | Fresh Q pending; nativeWindows pending |
|
||||
| 破壊 | Command destruction precedes completion. Accident/raid destruction follows completion and charging in minute N; that sale remains valid. Failure and reservation release for affected unfinished service occurs in N+1. D05-P04-DESTRUCTION is the authority, not BASELINE:183. | autoload/simulation_runtime.gd:5765 _process_tick_commands; autoload/simulation_runtime.gd:8599 _advance_defense; autoload/simulation_runtime.gd:9826 _apply_defense_outcome_boundary; scripts/facility/facility_service.gd:853 _prepare_service_for_tick | Fresh Q pending; nativeWindows pending |
|
||||
| 退出 | D-034 release safety remains. Command exit precedes completion; AI planned exit follows completion and only applies to a deciding guest. Later despawn is distinct. | autoload/simulation_runtime.gd:7575 _execute_leave_facility_queue; autoload/simulation_runtime.gd:8760 _advance_guest_ai_decisions | Fresh Q pending; nativeWindows pending |
|
||||
| 課金 | Charge only completed service, once per idempotency key; retain original price snapshot and reservation/ledger joins. At 00:00 prior-day settlement and upkeep precede completion; the sale belongs to the new day. | autoload/simulation_runtime.gd:1876 _advance_facility_upkeep; autoload/simulation_runtime.gd:11817 _complete_service_transaction; autoload/simulation_runtime.gd:11833 _apply_service_transaction; scripts/operations/operations_coordinator.gd:1125 _process_daily_close | Fresh Q pending; nativeWindows pending |
|
||||
| シフト交代 | Approved 18:00 order remains: completion and payment before handover. No renewal of prior approval. | autoload/simulation_runtime.gd:8504 _advance_facilities; autoload/simulation_runtime.gd:8680 _advance_staff_ai | Fresh Q pending; nativeWindows pending |
|
||||
| 00:00決算 | A01 ownership-based upkeep is unchanged. Prior-day settlement/upkeep precede 00:00 service completion; its sale belongs to the new day. Replay/restore must not duplicate settlement. | scripts/core/simulation_kernel.gd:254 _handle_time_boundaries; autoload/simulation_runtime.gd:1919 _advance_operations_time; scripts/operations/operations_coordinator.gd:882 _process_minute_boundaries; scripts/operations/operations_coordinator.gd:1125 _process_daily_close | Fresh Q pending; nativeWindows pending |
|
||||
|
||||
## 再利用の制限
|
||||
The preserved S1–S4, r22 index, old registered validation and f5 full178 run retain their historical identities. Current runtime deltas conservatively require fresh proof for all 15 conditions plus EV. A test count, approval record or byte-equal ordering function alone does not establish original acceptance. No thresholds or original unknown values are changed.
|
||||
|
||||
詳細ハッシュ・7領域の不足は `order-audit.json` に記録した。ローカル旧mapはSHA256 `df8328ea8b6a7bc400caad8a52f6ead710c01942a726e5a6b530b82411c55da5`、1299件、欠落0件。主要6ソースの指定pinは一致。旧mapとの差は14件で、契約記載のRuntime/main/preview/performance/path/2試験の7件に加え、Kenney同梱サンプル/URL7件がバイト不一致だった。追加7件は後述のRunner支援入力で分類し、素材を編集していない。
|
||||
The eleven phases remain APPLY_COMMANDS, TIME_BOUNDARIES, MANA, CONSTRUCTION, LOGISTICS, FACILITY_COMPLETION, VITALS_AND_ENVIRONMENT, INCIDENTS_AND_EVENTS, FACILITY_QUEUES, AI_DECISIONS, PUBLISH_SNAPSHOT. See the structured audit for preserved phase analysis and the new source manifest for exact M references.
|
||||
|
||||
修正入力で旧Runtimeのローカルrawコピーを受領し、SHA256 `2d7e2cea5424585e9aa4302ab66d1fc4dbaefe743ce4328fccc1efa2f19b82e4` を確認した。`git diff --no-index` による現Runtimeとの差分は4箇所で、客候補経路batch、施設入口の経路費用比較、客/スタッフの移動時間推定、connector credit helper抽出に関わる。Runtime内のphase登録・施設完了順序はこの差分では変わらないが、変更されたPathServiceと到達性・選択・到着・勤務見積りへの影響を個別確認する必要がある。追加7素材差分はRunner supportがCRLF→LFのみと報告し、現raw hashを照合した。旧raw素材は未供給のため正規化一致を独立再現したとは扱わない。
|
||||
|
||||
ローカル入力は旧summary/map/draftであり、生の保存package・stdout・engineログ・開始終了記録・設定・入力・環境証拠は提供されていない。外部worktreeのartifactパスは来歴に留め、読んでいない。正式保存8境界27状態、fresh Reader A/B、P04-02/fullの成功記載は未検証参照で、P04-01受入を意味しない。
|
||||
|
||||
## 提出状態と有限検証計画
|
||||
|
||||
今回の承認2条件と既存権限(BASELINE:64/67/71/72/183、D-034、A01)を分け、checker内の固定条件レジストリに対応付けた。費用時点・日跨ぎの帰属など残るD05はpendingのまま補完しないが、一括failの理由にはしない。売上・給与・維持費・勤務分の各1回、非ゼロ給与、停止の00:00またぎでも完了課金1回・予約孤立0は必須である。全条件を満たすとmachine_verified=true、formal_acceptance=false、exit 0。必須条件のauthorityがpendingなら失敗する。
|
||||
|
||||
承認4文書は全てバイト数とSHA256を再計算し、SOURCE_MANIFESTと一致。必要な承認JSONのみ2073 bytesを同梱した。初期空き容量約1.8GiB、旧39 artifactの複製なし。39件の原パスは来歴であり、今回のミラーは承認4文書のみ。生証拠・入力・設定・環境が不足しているので、境界の適用性合格は宣言しない。
|
||||
|
||||
順序関数20本は今回個別に本文を再ハッシュし、供給されたhistorical_body_sha256と20/20一致した(詳細はJSON repair3)。旧監査の29本とは合算しない。旧Runtime rawも再ハッシュし、git diff --no-indexでは5個の@@区間(候補batchの開始・終了が別区間)、論理的な変更は記録の4箇所と確認した。関数一致は経路・候補・到着の適用性を保証しない。PathService変更を含め、停電・破壊・退出の影響ケースはRootが生証拠と設定を照合し、不適用の場合だけ新しい有限境界証拠を作る。production Runtime 4696df4aとcheckout d52a995の差も統合前にRootが確認する。
|
||||
|
||||
### 到達性
|
||||
|
||||
- 営業時間による閉店: autoload/scriptsの検索ではset_opening_hoursの定義とサービスの委譲のみ。既定[0,1440)で製品呼出しなし。直接休業と依存先休業の証拠は引き続き必須。
|
||||
- 客の通常EV移動: Runtime:8898-8922の到着後にqueueを要求し、7421-7445で予約する。この移動前予約経路は対象外。
|
||||
- スタッフEV: 対象外にできない。公開create_staff_task(Runtime:329、6733-6742)でrequired_itemsを渡せる。task_dispatcher:256-261、293-294、309-314が移動前に予約し、Runtime:9141-9170が在庫予約を実施、12009-12013で移動へ進み、8755-8763、8946-8987からEVを利用する。set_powered(elevator_service:279-284)による停電はこの予約保持経路に到達する。checkerはEV全体のreach=falseを拒否し、予約保持中停電のengine caseと孤立0 invariantを要求する。EV停止規則や期限は新設しない。
|
||||
- 救護搬送: Runtime:12023-12030は治療状態を更新するだけ、12976-13050は救護タスク生成・移動・取消でrequired_items・課金処理を持たない。物理搬送自体でなく、課金・予約の到達性がfalse。静的根拠の行・本文・hashを必須とし、7領域の分母に加えない。
|
||||
|
||||
### 有限検証と残件
|
||||
|
||||
workerはpython3 -m py_compileとchecker_negative_fixtures.pyを実行。index欠落、重複キー、symlink、hash不一致、stale pin、未実行receipt、pending authority、必須条件欠落、根拠なしreach=false、承認hash不一致の10例が期待した理由でexit 1。合成対照のみexit 0であり、エンジン由来の製品証拠ではない。最初の対照実行はspec pinで失敗した。契約のspec文字列は末尾LFなし、checkout JSONは末尾LF一つ付きであり、その厳密な表現差のみ許容後、上記確認が通った。仕様ファイルは変更していない。
|
||||
|
||||
1. Runnerがp04-acceptanceを正式実行する。
|
||||
2. Rootが旧証拠の適用性と不足を独立確認する。非ゼロ給与の00:00、停止の00:00またぎ、再送、正式保存→別PID復元、夜番不在18:01、既存残業タスク、予約保持中EV停電の証拠を揃える。
|
||||
3. 別セッション独立レビューと通常統合のfinalまで正式受入加算0。nativeWindowsは条件ごとにpending。
|
||||
|
||||
エンジン実行・Runner操作・Git書込み・他エージェント起動は0。原30パッケージ/88成果の既存1単位内の準備であり、成果追加0、正式完了0。
|
||||
See [CURRENT_STATUS.md](CURRENT_STATUS.md) and [REPAIR31_STATUS.md](REPAIR31_STATUS.md) before any Root execution.
|
||||
|
||||
+73
-1
@@ -61,5 +61,77 @@
|
||||
"unexecuted proof, incomplete7domain or required boundary coverage",
|
||||
"selfreported pass/test count as substitute for actual state invariants"
|
||||
],
|
||||
"implementation_note": "Implement this finite mechanical protocol and document exact supported predicates. If evidence is absent, output actual missing IDs and exit nonzero. No requirement to create fake pass evidence. Do not change source/Clock/game numbers; no engine/test run by worker."
|
||||
"implementation_note": "Implement this finite mechanical protocol and document exact supported predicates. If evidence is absent, output actual missing IDs and exit nonzero. No requirement to create fake pass evidence. Do not change source/Clock/game numbers; no engine/test run by worker.",
|
||||
"revision": "repair29",
|
||||
"status": "Worker SOURCE-READY / NOTRUN; Root-measured S4r28/local8/r22/schema2 index/registered validation PASS on fb8b; no original acceptance",
|
||||
"accepted_index_schema_versions": [
|
||||
1,
|
||||
2
|
||||
],
|
||||
"schema2_note": "Current candidate-role/index binding uses the existing schema2 implementation. Legacy field descriptions below are not a replacement schema.",
|
||||
"current_order_table": "order-audit.json",
|
||||
"series_collection": "BUILD_OUTPUT_DIR/audit-support/p04-raw-collect-r22",
|
||||
"power_stop_binding": {
|
||||
"only_series": "S4",
|
||||
"status": "Root-measured S4r28 4/4 PASS; all four owned PIDs ended; guards0 cleanup0",
|
||||
"required_reason": "NO_POWER",
|
||||
"related_facility_operating": true,
|
||||
"kitchen_power_available": true,
|
||||
"global_mana": ">0",
|
||||
"operator_open": true,
|
||||
"formal_points": [
|
||||
"boundary",
|
||||
"held",
|
||||
"recovered"
|
||||
],
|
||||
"canonicalization": false,
|
||||
"dropped_fields": false,
|
||||
"native_path": "B/p04-s4-native-r28",
|
||||
"controls_path": "B/audit-support/p04-root-fb8b-s4-r01",
|
||||
"PIDs": [
|
||||
49940,
|
||||
50044,
|
||||
50078,
|
||||
50105
|
||||
],
|
||||
"cause": "probe calls disconnect_nodes directly; isolated NO_POWER, not proof of global depletion",
|
||||
"global_depletion_scope": "Only B1 combined diagnostic observes global depletion; B1 supplies no isolated power_stop or priority approval"
|
||||
},
|
||||
"B1": {
|
||||
"scope": "combined dependency/closure diagnostic only",
|
||||
"power_stop_evidence": false,
|
||||
"priority_approval": false
|
||||
},
|
||||
"D05_power_vs_closure": "pending D05(a); observation RELATED before NO_POWER is not approval; choices in D05_REMAINING_ORDER_CANDIDATE.json",
|
||||
"history": "Creation-time NOT RUN in REPAIR22–28 and runbooks is historical; CURRENT_STATUS.md canonical table overrides execution status. Historical files untouched.",
|
||||
"version_policy": "Full178 used checker f5,21615 checks, exact receipt preserved. No full1339 against a61 exists. Product-only reuse is distinct from current-checker validation. support53cb normal rebind, final full run, independent review and P04-then-support integration on main9b remain Runner steps.",
|
||||
"root_measurements": {
|
||||
"label": "Root-measured",
|
||||
"local8": "B/audit-support/p04-root-fb8b-local-r01 PASS",
|
||||
"collection": "B/audit-support/p04-raw-collect-r22",
|
||||
"index": "B/p04-evidence/index.json schema2; missing_conditions=[];15 conditions + EV verified",
|
||||
"registered_validation": "fb8b PASS28.095s; local-r01/registered-validation.result.json",
|
||||
"native_product": "5f6bc11d",
|
||||
"main": "9b1a18c1",
|
||||
"canonical_status": "CURRENT_STATUS.md",
|
||||
"exact_sha256_pins": "REPAIR29_STATIC_SOURCE.json"
|
||||
},
|
||||
"destruction_stop_binding": {
|
||||
"status": "pending D05",
|
||||
"current_registry_authority": "BASELINE:183",
|
||||
"gap": "mana depletion, not destruction authority",
|
||||
"action": "No checker or registry edits before owner approval; explicit destruction rule and subsequent normal repair required"
|
||||
},
|
||||
"remaining_order_choices": "D05_REMAINING_ORDER_CANDIDATE.json; pending, not applied",
|
||||
"full178_applicability_context": {
|
||||
"selected_map": "B/audit-support/p18-regression-support-final-r01/native-full178/runner-input.json",
|
||||
"difference": [
|
||||
{
|
||||
"path": "tests/business/test_required_kitchen.gd",
|
||||
"current_sha256": "5dcc86d66bdc47003472d6c30d6889eb514e016003265965bdd8496d123d5ca5",
|
||||
"full178_sha256": "3a9c92f80c24064ef0858fba10bd7caab63203932942837f7e142349578f6b2f"
|
||||
}
|
||||
],
|
||||
"status": "SOURCE-READY / NOTRUN; test-source applicability retained as Runner responsibility; no blanket1338 identity claim"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# REPAIR17 — blocked on required raw access
|
||||
|
||||
This is not accepted or completed. Stage R is not implemented. No new condition
|
||||
is claimed as proven. This document records an observed input-access blocker,
|
||||
not a replacement implementation or a proposal to trust Root summaries.
|
||||
|
||||
## Checkout and scope
|
||||
|
||||
- Observed HEAD: `accd07c91d4e34d9b7c7fc20e389df2424b167e1` (the requested reused checkout).
|
||||
- Registered contract base: `deb1bcb2374eb378b840ca345db952bd8572cb6e`.
|
||||
- Only this status file was created. The checker, `verify_native`, C checks,
|
||||
existing fixtures, probes, originals, and logs were not modified.
|
||||
- No git writes, Runner operations, engine runs, or other agents were used.
|
||||
|
||||
## Reproduced blocker
|
||||
|
||||
Let `B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
The four `B/<series>/raw/{writer,reader}.json` files are readable. Their literal
|
||||
`slots` mappings locate the formal packages. Attempting `Path.read_bytes()` on
|
||||
every `journal.log`, `manifest.json`, `thumbnail.png`, and `world.bin` in those
|
||||
slots returned `PermissionError: [Errno 1] Operation not permitted`:
|
||||
|
||||
| Series | Slots per role | Unreadable original + copy members |
|
||||
| --- | ---: | ---: |
|
||||
| `p04-s1-native-r02` | 14 | 112 |
|
||||
| `p04-s2a-native-r03` | 1 | 8 |
|
||||
| `p04-s2b-native-r02` | 6 | 48 |
|
||||
| `p04-s3-native-r01` | 22 | 176 |
|
||||
|
||||
For example, this exact required S3 original cannot be read:
|
||||
|
||||
```text
|
||||
/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-078167a92c6e4888a6b4ee4a005fd858-standard-runtime-writer/saves/p04_s3/B1-after/world.bin
|
||||
```
|
||||
|
||||
Its Reader counterpart is also inaccessible:
|
||||
|
||||
```text
|
||||
/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-b1fd1956bd28493cbac1e00f3a775232-standard-runtime-reader/saves/p04_s3/B1-after/world.bin
|
||||
```
|
||||
|
||||
Searching each series directory for `world.bin` found only the unrelated
|
||||
`stage/tests/fixtures/required_kitchen_legacy14/formal-package/world.bin`.
|
||||
There is no `.review-material` directory in this checkout. Root result SHA
|
||||
fields cannot replace re-hashing the actual original and Reader member bytes.
|
||||
|
||||
Current-game source access also fails: reading the first source_before entry,
|
||||
`/Users/korizatou114/Projects/とりにく工房ゲーム制作部/AGENTS.md`, returned the same error.
|
||||
Each run's fixed_input_pins contains three inaccessible entries. The remaining
|
||||
entries re-hashed successfully (S1: 13, S2a: 14, S2b: 10, S3: 15).
|
||||
Each staged source tree has 1337 hashes matching source_before and one differing
|
||||
test script (`tests/s3/test_runtime_staff_assignment.gd`); this is not a proof
|
||||
that the current game's full 1338-file source set still matches.
|
||||
|
||||
Required unblock: make these declared original inputs readable to the worker,
|
||||
or supply an authorized, provenance-bound relocation of their full bytes,
|
||||
including both formal roles and the current-game source/fixed inputs. No new
|
||||
engine run is requested. Permission denial must not be interpreted as missing
|
||||
files, matching hashes, or failed product behavior.
|
||||
|
||||
## Evidence and remaining conditions
|
||||
|
||||
Readable raw locations were inspected under all four series, including
|
||||
runner-input/source_before, lifecycle receipts, raw inventory, and raw Writer
|
||||
and Reader records. D05 supplement bytes were read. ROOT result files were
|
||||
used to inspect locators, never as proof of passing conditions.
|
||||
|
||||
- S1: EV remains unverified in this run; no paid-service power claim.
|
||||
- S2a: midnight_settlement, midnight_replay, save_restore remain unverified.
|
||||
- S2b: night_staff_absent, overtime_task and same-minute rejection remain unverified.
|
||||
- S3: closure_release, exit_release, patience_release and B4 release-once remain unverified.
|
||||
- power_stop, destruction_stop, shift_completion, same_minute_replay remain unverified.
|
||||
- Existing C verification was neither changed nor rerun; no fresh C result is claimed.
|
||||
- Required native-Windows conditions remain pending individually; this run supplies no Windows proof.
|
||||
|
||||
Stage A remains pending: actual HEAD + filesystem candidate binding, the known
|
||||
`MAIN_ANCHOR` `9694...`, candidate_tree trust, and `3139` metadata holes are
|
||||
untouched. No final index or composition was generated.
|
||||
|
||||
## Footprint and verification
|
||||
|
||||
Read-only `df -h` reported about 3.1 GiB available. No evidence payload was
|
||||
copied. `B/audit-support/p04-raw-collect-r17/` did not exist at the access audit
|
||||
and was not created. A trustworthy collection footprint cannot be finalized
|
||||
without access to the required member bytes; no collector was run.
|
||||
|
||||
Only Python read/hash/access diagnostics were executed. Fixture suites were
|
||||
not run and are not reported as passing. `p04_native_series.py`,
|
||||
`collect_p04_native.py`, and `checker_series_fixtures.py` were not created, so
|
||||
there are no valid Root collector/new-fixture commands to advertise yet.
|
||||
Implementation and its required positive/adversarial checks remain outstanding
|
||||
after the required inputs become readable.
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
{"estimated_payload_bytes": 2970454, "files": 5, "available_disk_bytes": 4596576256, "large_payloads_copied": 0}
|
||||
{"collection": "/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build/audit-support/p04-raw-collect-r17", "manifest_sha256": "953a798831043abdf7d54909e8e041cb3fbe6d901ca8affa661ea28e62ca5975", "actual_payload_bytes": 2970454, "formal_acceptance": false}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"C_provenance_sha256": "51d746879b4197445a09cd44a88dd918c365fa98025d9cd652a740cee09a9040", "C_source_manifest_sha256": "dbaab3c293c191eb5f3aff34854945e2d564f824df50a92802b3a7eda55321a8", "error": "candidate binding stage A pending; uncollected original boundaries: power_stop, save_restore, shift_completion", "formal_acceptance": false, "machine_verified": false, "missing_conditions": ["power_stop", "save_restore", "shift_completion"], "nativeWindows": {"EV": "pending", "closure_release": "pending", "dependency_stop": "pending", "destruction_stop": "pending", "exit_release": "pending", "midnight_replay": "pending", "midnight_settlement": "pending", "night_staff_absent": "pending", "overtime_task": "pending", "patience_release": "pending", "paused_midnight": "pending", "power_stop": "pending", "recovery_completion": "pending", "same_minute_replay": "pending", "save_restore": "pending", "shift_completion": "pending"}, "relocation_manifest_sha256": "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8", "series_bindings": {"s1": "9792569af9c8ecef177ffdeb880b814fb2878231f23e77c3749e18c228ff0ceb", "s2a": "746b58cf1aff95b082c35f6cc2c29556f146a7c56cce0475f4f2c091168605a7", "s2b": "e6d5412c14809825cf368af1d93972bc9c4e41f4c09afa414732b007c9cc7589", "s3": "a9b4b80d54c18ae2a1dc82514052e22d6174ff79999c76fa622282709759b433"}, "series_manifest_sha256": "953a798831043abdf7d54909e8e041cb3fbe6d901ca8affa661ea28e62ca5975", "stage_A": "candidate binding stage A pending", "task_id": "P04-01", "verified_conditions": ["EV", "closure_release", "dependency_stop", "destruction_stop", "exit_release", "midnight_replay", "midnight_settlement", "night_staff_absent", "overtime_task", "patience_release", "paused_midnight", "recovery_completion", "same_minute_replay"]}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
{
|
||||
"s1": {
|
||||
"facts": {
|
||||
"paid_service_power_claim": false
|
||||
},
|
||||
"formal_acceptance": false,
|
||||
"owned_pids": [
|
||||
64745,
|
||||
64788,
|
||||
64818,
|
||||
64850
|
||||
],
|
||||
"phase_pairs": 0,
|
||||
"product_content": "5f6bc11d6bb6e92d22d787989b219fef3a435bc1",
|
||||
"raw_reference_count": 97,
|
||||
"relocated_reference_count": 1464,
|
||||
"relocation_manifest_sha256": "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8",
|
||||
"series": "s1",
|
||||
"source_files": 1338,
|
||||
"verified_conditions": [
|
||||
"EV"
|
||||
],
|
||||
"verifier_sha256": "61f6647be4d2ec95db0599b94a3c6acb5f91ca43501256548f5ae161ab2f0e4f"
|
||||
},
|
||||
"s2a": {
|
||||
"facts": {
|
||||
"paid_originals": {
|
||||
"completed_service_id": "service_00000027",
|
||||
"same_minute_replay": "request resend, not advance_to settlement replay",
|
||||
"shift_completion": "missing: zero SERVICE_SALE rows at 1080",
|
||||
"transaction_id": "ledger_00000085"
|
||||
},
|
||||
"payroll_gold": 255,
|
||||
"raw_getter_bytes_equal": false,
|
||||
"sales_gold": 25,
|
||||
"save_restore_gap": "Raw getter also changes typed arrays to untyped arrays; authorized normalization permits only StringName/String and ordering",
|
||||
"settlement_replay_is_request_resend": false,
|
||||
"upkeep_gold": 5
|
||||
},
|
||||
"formal_acceptance": false,
|
||||
"owned_pids": [
|
||||
85229,
|
||||
85263,
|
||||
85303,
|
||||
85425
|
||||
],
|
||||
"phase_pairs": 0,
|
||||
"product_content": "5f6bc11d6bb6e92d22d787989b219fef3a435bc1",
|
||||
"raw_reference_count": 486,
|
||||
"relocated_reference_count": 1362,
|
||||
"relocation_manifest_sha256": "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8",
|
||||
"series": "s2a",
|
||||
"source_files": 1338,
|
||||
"verified_conditions": [
|
||||
"midnight_replay",
|
||||
"midnight_settlement",
|
||||
"same_minute_replay"
|
||||
],
|
||||
"verifier_sha256": "61f6647be4d2ec95db0599b94a3c6acb5f91ca43501256548f5ae161ab2f0e4f"
|
||||
},
|
||||
"s2b": {
|
||||
"facts": {
|
||||
"same_minute_rejection": "MINUTE_ALREADY_APPLIED"
|
||||
},
|
||||
"formal_acceptance": false,
|
||||
"owned_pids": [
|
||||
98696,
|
||||
98744,
|
||||
98770,
|
||||
98810
|
||||
],
|
||||
"phase_pairs": 12,
|
||||
"product_content": "5f6bc11d6bb6e92d22d787989b219fef3a435bc1",
|
||||
"raw_reference_count": 163,
|
||||
"relocated_reference_count": 1397,
|
||||
"relocation_manifest_sha256": "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8",
|
||||
"series": "s2b",
|
||||
"source_files": 1338,
|
||||
"verified_conditions": [
|
||||
"night_staff_absent",
|
||||
"overtime_task"
|
||||
],
|
||||
"verifier_sha256": "61f6647be4d2ec95db0599b94a3c6acb5f91ca43501256548f5ae161ab2f0e4f"
|
||||
},
|
||||
"s3": {
|
||||
"facts": {
|
||||
"B1": {
|
||||
"amount": 0,
|
||||
"reservation_states": [
|
||||
"ACTIVE",
|
||||
"RELEASED"
|
||||
],
|
||||
"sales": 0
|
||||
},
|
||||
"B2a": {
|
||||
"amount": 25,
|
||||
"reservation_states": [
|
||||
"CONSUMED"
|
||||
],
|
||||
"sales": 1
|
||||
},
|
||||
"B2b": {
|
||||
"amount": 0,
|
||||
"reservation_states": [
|
||||
"RELEASED"
|
||||
],
|
||||
"sales": 0
|
||||
},
|
||||
"B3": {
|
||||
"amount": 0,
|
||||
"reservation_states": [
|
||||
"RELEASED"
|
||||
],
|
||||
"sales": 0
|
||||
},
|
||||
"B4a": {
|
||||
"amount": 25,
|
||||
"reservation_states": [
|
||||
"CONSUMED"
|
||||
],
|
||||
"sales": 1
|
||||
},
|
||||
"B4b": {
|
||||
"amount": 0,
|
||||
"reservation_states": [
|
||||
"RELEASED"
|
||||
],
|
||||
"sales": 0
|
||||
}
|
||||
},
|
||||
"formal_acceptance": false,
|
||||
"owned_pids": [
|
||||
10448,
|
||||
10493,
|
||||
10523,
|
||||
10578
|
||||
],
|
||||
"phase_pairs": 352,
|
||||
"product_content": "5f6bc11d6bb6e92d22d787989b219fef3a435bc1",
|
||||
"raw_reference_count": 891,
|
||||
"relocated_reference_count": 1530,
|
||||
"relocation_manifest_sha256": "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8",
|
||||
"series": "s3",
|
||||
"source_files": 1338,
|
||||
"verified_conditions": [
|
||||
"closure_release",
|
||||
"destruction_stop",
|
||||
"exit_release",
|
||||
"patience_release"
|
||||
],
|
||||
"verifier_sha256": "61f6647be4d2ec95db0599b94a3c6acb5f91ca43501256548f5ae161ab2f0e4f"
|
||||
}
|
||||
}
|
||||
+367
@@ -0,0 +1,367 @@
|
||||
{
|
||||
"formal_acceptance": false,
|
||||
"results": [
|
||||
{
|
||||
"class": "small_synthetic_positive",
|
||||
"result": "pass",
|
||||
"series": "shared"
|
||||
},
|
||||
{
|
||||
"class": "full_body_positive",
|
||||
"result": "pass",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "wrong_source_pin",
|
||||
"error": "SHA drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "signature_only_pin",
|
||||
"error": "full-function-body pin mismatch",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "symlink",
|
||||
"error": "symlink: /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/P04-01-implement/docs/plans/original-completion-2026-10-02/execution/p04-01/.series-fixtures-ici55vnl/symlink.gd",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "traversal",
|
||||
"error": "raw traversal",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "unmapped_original_path",
|
||||
"error": "unmapped original path: /native/unknown.gd",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "relocated_manifest_native_mismatch",
|
||||
"error": "byte length drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "relocated_byte_manifest_mismatch",
|
||||
"error": "SHA drift: relocation /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "JSON_only_getter_substitute",
|
||||
"error": "unsupported Variant flags 8827:25959 at 0",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "forged_scalar",
|
||||
"error": "SHA drift: p04-s1-native-r02/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "changed_raw_byte",
|
||||
"error": "SHA drift: p04-s1-native-r02/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "formal_member_SHA_drift",
|
||||
"error": "SHA drift: p04-s1-native-r02/execution.json",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "unowned_missing_PID_end",
|
||||
"error": "missing PID end",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "altered_phase_pair",
|
||||
"error": "SHA drift: p04-s1-native-r02/raw/reader-landing-held.var",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "relabeled_interruption_reason",
|
||||
"error": "SHA drift: p04-s1-native-r02/raw/writer-landing-depleted.json",
|
||||
"result": "rejected",
|
||||
"series": "s1"
|
||||
},
|
||||
{
|
||||
"class": "full_body_positive",
|
||||
"result": "pass",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "wrong_source_pin",
|
||||
"error": "SHA drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "signature_only_pin",
|
||||
"error": "full-function-body pin mismatch",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "symlink",
|
||||
"error": "symlink: /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/P04-01-implement/docs/plans/original-completion-2026-10-02/execution/p04-01/.series-fixtures-ici55vnl/symlink.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "traversal",
|
||||
"error": "raw traversal",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "unmapped_original_path",
|
||||
"error": "unmapped original path: /native/unknown.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "relocated_manifest_native_mismatch",
|
||||
"error": "byte length drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "relocated_byte_manifest_mismatch",
|
||||
"error": "SHA drift: relocation /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "JSON_only_getter_substitute",
|
||||
"error": "unsupported Variant flags 8827:25959 at 0",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "forged_scalar",
|
||||
"error": "SHA drift: p04-s2a-native-r03/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "changed_raw_byte",
|
||||
"error": "SHA drift: p04-s2a-native-r03/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "formal_member_SHA_drift",
|
||||
"error": "SHA drift: p04-s2a-native-r03/execution.json",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "unowned_missing_PID_end",
|
||||
"error": "missing PID end",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "altered_phase_pair",
|
||||
"error": "SHA drift: p04-s2a-native-r03/raw/reader-C3-recall.var",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "relabeled_interruption_reason",
|
||||
"error": "SHA drift: p04-s2a-native-r03/raw/writer-C1-midnight.json",
|
||||
"result": "rejected",
|
||||
"series": "s2a"
|
||||
},
|
||||
{
|
||||
"class": "full_body_positive",
|
||||
"result": "pass",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "wrong_source_pin",
|
||||
"error": "SHA drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "signature_only_pin",
|
||||
"error": "full-function-body pin mismatch",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "symlink",
|
||||
"error": "symlink: /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/P04-01-implement/docs/plans/original-completion-2026-10-02/execution/p04-01/.series-fixtures-ici55vnl/symlink.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "traversal",
|
||||
"error": "raw traversal",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "unmapped_original_path",
|
||||
"error": "unmapped original path: /native/unknown.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "relocated_manifest_native_mismatch",
|
||||
"error": "byte length drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "relocated_byte_manifest_mismatch",
|
||||
"error": "SHA drift: relocation /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "JSON_only_getter_substitute",
|
||||
"error": "unsupported Variant flags 8827:25959 at 0",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "forged_scalar",
|
||||
"error": "SHA drift: p04-s2b-native-r02/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "changed_raw_byte",
|
||||
"error": "SHA drift: p04-s2b-native-r02/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "formal_member_SHA_drift",
|
||||
"error": "SHA drift: p04-s2b-native-r02/execution.json",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "unowned_missing_PID_end",
|
||||
"error": "missing PID end",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "altered_phase_pair",
|
||||
"error": "SHA drift: p04-s2b-native-r02/raw/reader-night_absent-1080-before-facility.var",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "relabeled_interruption_reason",
|
||||
"error": "SHA drift: p04-s2b-native-r02/raw/writer-night_absent-1081.json",
|
||||
"result": "rejected",
|
||||
"series": "s2b"
|
||||
},
|
||||
{
|
||||
"class": "full_body_positive",
|
||||
"result": "pass",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "wrong_source_pin",
|
||||
"error": "SHA drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "signature_only_pin",
|
||||
"error": "full-function-body pin mismatch",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "symlink",
|
||||
"error": "symlink: /Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/P04-01-implement/docs/plans/original-completion-2026-10-02/execution/p04-01/.series-fixtures-ici55vnl/symlink.gd",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "traversal",
|
||||
"error": "raw traversal",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "unmapped_original_path",
|
||||
"error": "unmapped original path: /native/unknown.gd",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "relocated_manifest_native_mismatch",
|
||||
"error": "byte length drift: native /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "relocated_byte_manifest_mismatch",
|
||||
"error": "SHA drift: relocation /native/body.gd",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "JSON_only_getter_substitute",
|
||||
"error": "unsupported Variant flags 8827:25959 at 0",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "forged_scalar",
|
||||
"error": "SHA drift: p04-s3-native-r01/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "changed_raw_byte",
|
||||
"error": "SHA drift: p04-s3-native-r01/raw/writer.json",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "formal_member_SHA_drift",
|
||||
"error": "SHA drift: p04-s3-native-r01/execution.json",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "unowned_missing_PID_end",
|
||||
"error": "missing PID end",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "altered_phase_pair",
|
||||
"error": "SHA drift: p04-s3-native-r01/raw/reader-B1-phase-960-TIME_BOUNDARIES.var",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
},
|
||||
{
|
||||
"class": "relabeled_interruption_reason",
|
||||
"error": "SHA drift: p04-s3-native-r01/raw/writer-B1-after.json",
|
||||
"result": "rejected",
|
||||
"series": "s3"
|
||||
}
|
||||
],
|
||||
"scope": "file-only Stage R controls; immutable descriptor and primitive negatives"
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
# REPAIR18 — executable Stage R, blocked on remaining conditions
|
||||
|
||||
This is **not accepted or completed**. Executable R artifacts and a reference-only
|
||||
collection were produced and the file-only checks were run. The whole gate
|
||||
fails. Original completion credit remains **13/88**, with no additional credit.
|
||||
The repair counter and `max_repair_attempts: null` were not changed.
|
||||
|
||||
## Scope and preservation
|
||||
|
||||
- Checkout: `accd07c91d4e34d9b7c7fc20e389df2424b167e1`; registered base:
|
||||
`deb1bcb2374eb378b840ca345db952bd8572cb6e`.
|
||||
- Changed `tools/check_p04_order_evidence.py` only to add R composition and its
|
||||
separate read-only CLI, call composition from the whole missing-check, and
|
||||
retain an unconditional Stage A failure.
|
||||
- Created `p04_native_series.py`, `collect_p04_native.py`,
|
||||
`checker_series_fixtures.py`, this status, and the four output files listed below.
|
||||
- All existing checker functions except `verify` and `main` were compared with
|
||||
HEAD and are byte-unchanged, including `verify_native` and all C helpers.
|
||||
All three existing fixture files are byte-unchanged. Their failures were not
|
||||
hidden by editing assertions or Stage A code.
|
||||
- `REPAIR17_STATUS.md` was never written. Its SHA256 is
|
||||
`6497c335739f266c9026aa6499cf5c49ae80d590d6aa2d0f90d32adc08018061`.
|
||||
- No git writes, new worktree/branch, agents, Runner operations, engine runs,
|
||||
full regression, permission requests, production edits, input edits, or final
|
||||
`B/p04-evidence/index.json` writes occurred. This did not revalidate accd.
|
||||
|
||||
## Inputs and actual collection
|
||||
|
||||
Here `B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`,
|
||||
`R=B/audit-support/p04-r17-full-inputs-r01`, and
|
||||
`E=docs/plans/original-completion-2026-10-02/execution/p04-01`.
|
||||
|
||||
The first input identity check verified `R/MANIFEST.json`: **1,244,135 bytes**,
|
||||
SHA256 `a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8`.
|
||||
All **1,721** relocated members were used and rehashed across the series,
|
||||
covering 71,488,772 logical bytes. All four complete native `source_before`
|
||||
sets equal the supplied 1,338-file product set at content anchor
|
||||
`5f6bc11d6bb6e92d22d787989b219fef3a435bc1`. The one native QA override is checked
|
||||
separately against its supplied probe bytes; it is not represented as product
|
||||
content. Full-function-body pins include trailing comments and declarations.
|
||||
|
||||
External original paths remain provenance strings. The resolver uses exact
|
||||
`original_to_relocated` keys, and checks each used file against both native
|
||||
descriptors and relocation descriptors. There is no prefix substitution or
|
||||
external-path fallback. Formal slot membership, directory membership, separate
|
||||
Writer/Reader paths and inodes, symlinks, traversal and duplicates are checked.
|
||||
All **344** formal members in **86** original/Reader slots were rehashed.
|
||||
|
||||
The collector created only `B/audit-support/p04-raw-collect-r17/`. It did not
|
||||
exist before creation. It contains four series reference manifests and one
|
||||
collection manifest; no large evidence snapshots were copied.
|
||||
|
||||
Actual collector output (exit 0):
|
||||
|
||||
```json
|
||||
{"estimated_payload_bytes":2970454,"files":5,"available_disk_bytes":4596576256,"large_payloads_copied":0}
|
||||
{"actual_payload_bytes":2970454,"manifest_sha256":"953a798831043abdf7d54909e8e041cb3fbe6d901ca8affa661ea28e62ca5975","formal_acceptance":false}
|
||||
```
|
||||
|
||||
The estimate was printed before directory creation or output writes. The
|
||||
collection is deterministic and refuses an existing destination on rerun.
|
||||
Per-series manifests bind native raw references, relocation references, and
|
||||
verifier bytes by SHA. Composition reruns C and all series verifiers and
|
||||
compares the recomputed results and references with those manifests; editing a
|
||||
`verified_conditions` list cannot add credit.
|
||||
|
||||
## Conditions derived from literal raw
|
||||
|
||||
| Verifier | Native raw | Relocated formal members | Conditions proved |
|
||||
| --- | --- | --- | --- |
|
||||
| S1 | `B/p04-s1-native-r02/raw/` | `R/formal/p04-s1-native-r02/{writer,reader}/` | `EV` |
|
||||
| S2a | `B/p04-s2a-native-r03/raw/` | `R/formal/p04-s2a-native-r03/{writer,reader}/C3/` | `midnight_settlement`, `midnight_replay` |
|
||||
| S2b | `B/p04-s2b-native-r02/raw/` | `R/formal/p04-s2b-native-r02/{writer,reader}/` | `night_staff_absent`, `overtime_task` |
|
||||
| S3 | `B/p04-s3-native-r01/raw/` | `R/formal/p04-s3-native-r01/{writer,reader}/` | `closure_release`, `exit_release`, `patience_release`, `destruction_stop` (B4b) |
|
||||
| Paid/resend supplement, collected with S2a | `B/audit-support/p04-current-native-series-r01/p04-idempotency-paid-midnight-native-r01/order-evidence/` | Manifest pin resolved through `R/MANIFEST.json`, full raw already inside B | `same_minute_replay` (request resend) |
|
||||
| Unchanged C | `B/audit-support/native-first-current/` | Existing C bundle, unchanged | `dependency_stop`, `recovery_completion`, `paused_midnight` |
|
||||
|
||||
All product reads resolve to `R/product/` through manifest keys. Fixed-input
|
||||
relocations and every original formal provenance string are retained in the
|
||||
four generated series manifests; they are not replaced with guessed paths.
|
||||
|
||||
Owned import/parse/Writer/Reader PIDs checked against starts, ends, owners,
|
||||
commands, raw stream hashes, exit gates and cleanup/guard records:
|
||||
|
||||
| Series | PIDs | Formal slots per role | Exact raw phase pairs |
|
||||
| --- | --- | ---: | ---: |
|
||||
| S1 | 64745, 64788, 64818, 64850 | 14 | 0 (14 exact typed snapshot restore points instead) |
|
||||
| S2a | 85229, 85263, 85303, 85425 | 1 | 0 (typed settlement/replay and formal Runtime restore comparisons) |
|
||||
| S2b | 98696, 98744, 98770, 98810 | 6 | 12 |
|
||||
| S3 | 10448, 10493, 10523, 10578 | 22 | 352 JSON/typed phase files |
|
||||
|
||||
- S1 checks task/trip/resource identity, safe landing, no-safe-landing hold,
|
||||
preserved reservation identity and release. It makes no paid-service power claim.
|
||||
- S2a derives a 25G sale/one transaction, 255G payroll/one transaction
|
||||
(Akane 120, Bardo 135 joined to actual staff and shifts), and 5G upkeep in
|
||||
three rows/one transaction. `advance_to(1440)` has processed minutes 0 and
|
||||
daily closes 0, with entire typed Runtime bytes unchanged. Formal Reader
|
||||
Runtime restore and recall bytes match exactly.
|
||||
- The supplemental request-resend raw preserves typed economy, facilities,
|
||||
inventory, guests and staff at the same minute, and joins the one completed
|
||||
service `service_00000027` to sale transaction `ledger_00000085`. This is not
|
||||
settlement-replay proof; S2a supplies that distinct proof.
|
||||
- S2b derives `STAFF_UNAVAILABLE`/`WAITING_FOR_STAFF`, the absent night workers,
|
||||
overtime task/staff/shift joins and one duty minute. Its same-minute call is
|
||||
rejected as `MINUTE_ALREADY_APPLIED`, with entire typed Runtime unchanged.
|
||||
- S3 retains `RELATED_FACILITY_INACTIVE` in B1, never relabeling it `NO_POWER`.
|
||||
The directly closed facility retains its started service while the dependent
|
||||
facility interruption pauses it; the queued reservation is released.
|
||||
B2a completes/consumes once; B2b and B3 release without a sale.
|
||||
B4b's queued event completes, durability becomes 0/`BROKEN`, then the service
|
||||
fails with `BROKEN` at 961, releases its reservation and has zero sale rows.
|
||||
|
||||
## Unresolved raw/specification gaps
|
||||
|
||||
1. **`power_stop` missing.** EV evidence is not a paid-service power proof;
|
||||
B1 is a dependency interruption. No relabeling or invented D05 rule was used.
|
||||
2. **`shift_completion` missing.** The actual supplied paid-shift originals at
|
||||
`B/audit-support/p04-current-native-series-r01/p04-paid-shift-midnight-native-r01/order-evidence/`
|
||||
have zero `SERVICE_SALE` rows in all three 17:59/completion-phase/18:00
|
||||
snapshots. The `paid` filenames do not establish a charged completion at 1080.
|
||||
3. **`save_restore` withheld under the fixed normalization restriction.** The
|
||||
entire formal Runtime does restore exactly. However, the getter comparison
|
||||
changes not only StringName/String and dictionary order but typed arrays to
|
||||
untyped arrays. Examples are `/anomalies`, `/anomalies/0/allocations`,
|
||||
`/anomalies/0/unallocated_by_staff`, `/anomalies/0/facility_ids`,
|
||||
`/settlement/input_payments` and `/settlement/rows`. Native array headers
|
||||
include `1c000100...` versus `1c000000...`. The raw getter byte inequality is
|
||||
preserved and parsed from `.var`; JSON is not substituted. Normalization was
|
||||
not broadened beyond the instruction, so this condition remains missing.
|
||||
4. **B4a zero-charge assertion is unsupported.**
|
||||
`B/p04-s3-native-r01/raw/writer-B4a-next.{json,var}` contains one 25G sale at
|
||||
960 and a completed service with consumed reservation. B4b has zero sales.
|
||||
The implementation records both facts, rather than declaring B4a charge 0.
|
||||
Destruction-stop credit above is specifically the B4b failed-service branch.
|
||||
|
||||
D05 approval and supplement full bytes in `p04-whole-closure-r01` were read.
|
||||
They approve dependency closure and the 18:00 ordering choice, not an invented
|
||||
blanket policy for all D05 domains. The supplied raw discrepancies require
|
||||
owner-level resolution or appropriate evidence; this worker did not amend scope.
|
||||
|
||||
Each of these native-Windows conditions remains individually **pending**:
|
||||
`EV`, `power_stop`, `destruction_stop`, `dependency_stop`, `shift_completion`,
|
||||
`recovery_completion`, `same_minute_replay`, `closure_release`, `exit_release`,
|
||||
`patience_release`, `paused_midnight`, `midnight_settlement`, `midnight_replay`,
|
||||
`save_restore`, `night_staff_absent`, `overtime_task`. The machine output also
|
||||
contains an explicit per-condition pending map.
|
||||
|
||||
## Actual validation outputs
|
||||
|
||||
- Static Python compilation: **4 files passed** (in-memory compile; no engine).
|
||||
- Existing C fixture: **exit 0**, 5 positive controls passed and 25 negatives
|
||||
rejected. `verify_native` reports 10 owned processes and its original three
|
||||
conditions. No C condition names were edited.
|
||||
- Existing negative fixture: **exit 1**, at `synthetic_scalar_only_rejected`:
|
||||
`AssertionError: ('synthetic_scalar_only_rejected', {'task_id': 'P04-01', 'machine_verified': False, 'formal_acceptance': False, 'error': "'product_binding'"})`.
|
||||
- Existing applicability fixture: **exit 1**:
|
||||
`AssertionError: negative fixture accepted: wrong_tested_revision`.
|
||||
These two suites therefore could not be confirmed to pass/reject as requested.
|
||||
Their files and the functions involved were unchanged; they were not repaired
|
||||
by touching the deferred A holes, and no baseline rerun is claimed.
|
||||
- New series fixtures: **exit 0**, **5 small positive controls and 56 rejected
|
||||
negatives**, 14 negative classes per series. Controls cover immutable raw and
|
||||
descriptor overlays, source/body pins, PID endings, paths, relocation byte
|
||||
drift and typed-vs-JSON rejection. Immutable descriptor hashes are deliberately
|
||||
not refreshed; the tests do not claim to be new engine evidence.
|
||||
- Real series verifiers ran successfully during collection and were rerun by
|
||||
composition. Outputs are recorded in `REPAIR18_REAL_SERIES_OUTPUT.json`.
|
||||
- Separate composition CLI: **exit 1**, with 13 raw-derived conditions and:
|
||||
|
||||
```text
|
||||
candidate binding stage A pending; uncollected original boundaries: power_stop, save_restore, shift_completion
|
||||
```
|
||||
|
||||
Full new outputs: `REPAIR18_COLLECTOR_OUTPUT.jsonl`,
|
||||
`REPAIR18_SERIES_FIXTURE_OUTPUT.json`, `REPAIR18_REAL_SERIES_OUTPUT.json`,
|
||||
`REPAIR18_COMBINED_OUTPUT.json`. The 13 verified boundary conditions are not
|
||||
13 newly accepted units and do not change the original 13/88 credit.
|
||||
|
||||
## Exact Root commands
|
||||
|
||||
Run from this existing checkout. These commands perform file-only work:
|
||||
|
||||
```sh
|
||||
B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build
|
||||
E=docs/plans/original-completion-2026-10-02/execution/p04-01
|
||||
python3 -B "$E/collect_p04_native.py" --build "$B"
|
||||
python3 -B "$E/checker_series_fixtures.py" --build "$B"
|
||||
python3 -B "$E/p04_native_series.py" --build "$B"
|
||||
python3 -B "$E/checker_negative_fixtures.py"
|
||||
python3 -B "$E/checker_applicability_fixtures.py" --native-bundle "$B/audit-support/native-first-current"
|
||||
python3 -B "$E/checker_native_fixtures.py" "$B/audit-support/native-first-current"
|
||||
BUILD_OUTPUT_DIR="$B" python3 -B tools/check_p04_order_evidence.py --verify-series-only
|
||||
```
|
||||
|
||||
The collector command above was already executed successfully; another execution
|
||||
must refuse the now-existing destination. Review the existing collection rather
|
||||
than overwriting it. The final command is intentionally failing; it does not
|
||||
need or generate a final index and never performs candidate composition.
|
||||
|
||||
## Stage A and subsequent dependencies
|
||||
|
||||
Stage A remains pending: actual HEAD/filesystem candidate binding,
|
||||
`MAIN_ANCHOR` `9694...`, `candidate_tree` trust and the `3139` metadata holes
|
||||
are untouched. `--verify-only` still requires the actual final evidence index;
|
||||
its missing-check unions recomputed C/R results and still fails for any missing
|
||||
condition, and it cannot succeed while Stage A is pending.
|
||||
|
||||
Required sequence remains **R → A → final index → mandatory whole validation →
|
||||
independent review → ordinary integration**. Outstanding raw/specification gaps
|
||||
and the two existing fixture failures prevent representing this dispatch as a
|
||||
completed or accepted implementation.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Repair19 persistence explanation corrigendum
|
||||
|
||||
This corrects the old Root explanation that the live day-summary comparison
|
||||
differed only in StringName/String and dictionary order. That explanation missed
|
||||
generic Array headers. It also corrects the superseded proposal to explain this
|
||||
path using `_to_snapshot_variant`.
|
||||
|
||||
The exact relocated source is
|
||||
`B/audit-support/p04-r17-full-inputs-r01/product/scripts/operations/operations_coordinator.gd`.
|
||||
Its 130891 bytes have SHA256
|
||||
`871a248ee101b6df543fd5cf0440b5d4b0f02c21fa145a99f0687888c1bc6f90`.
|
||||
Full bodies, including bytes through the next named function, were read directly
|
||||
and compared with `T/ROOT_ACTUAL_PERSISTENCE_CALL_CHAIN.json`, where
|
||||
`T=B/audit-support/p04-repair19-close-r01`. All five bodies, lengths and hashes
|
||||
match that supplied file:
|
||||
|
||||
| Function | Line | Bytes | SHA256 |
|
||||
| --- | ---: | ---: | --- |
|
||||
| `get_day_summary` | 577 | 153 | `449eef926054cf76c39d6b4c016da3d775d41f2b343b4f8ad57b62c594cb32bb` |
|
||||
| `create_snapshot` | 620 | 3988 | `8c6e4f000e6df6f0f996445a57c4c17223cae4e1df57a9c2e3a605cf4d9f5309` |
|
||||
| `_snapshot_root_report` | 2943 | 2062 | `96084d76beeac11d165753a82160f789cd47b4dec4b6bb8cd6599a4f5ed08efe` |
|
||||
| `_visit_snapshot_tree` | 2992 | 3045 | `70792369c05882b813632a3c96f934928e9a089d420700cfc0d4c8fa971972a0` |
|
||||
| `_to_snapshot_variant` | 3083 | 550 | `f7db3cf2d495bb117fc7f6795633daa272c03d8eb7f99977e462e0a9df20cd8a` |
|
||||
|
||||
`get_day_summary` returns `_day_summaries[day].duplicate(true)` directly. It
|
||||
exposes an internal API representation; the product getter need not change.
|
||||
`create_snapshot` first validates with capture=true. In that pass,
|
||||
`day_summaries` is not among the reusable encoded fields. It then assembles
|
||||
`saved_days` and calls `_snapshot_root_report(raw_snapshot, field_cache, false)`.
|
||||
That report invokes `_visit_snapshot_tree` with encoding enabled. The visitor
|
||||
creates untyped arrays, converts StringName to String, stringifies permitted
|
||||
dictionary keys and sorts the resulting String keys with native sorting.
|
||||
|
||||
The separate `_to_snapshot_variant` helper is not on this call chain. Its
|
||||
`sort_custom(_variant_string_less)`, lack of finite-float/budget checks and
|
||||
pass-through of unsupported types prevent using it as a general equivalent.
|
||||
|
||||
The supplied `T/ROOT_S2A_TYPED_ARRAY_HEADER_AUDIT.json` lists the following six
|
||||
changes. These are explicitly attributed to that diagnostic; they have not been
|
||||
independently rerun by a repair19 fixture in this dispatch.
|
||||
|
||||
| Path below `day_summary` | Writer header | Writer element kind | Reader header | Length |
|
||||
| --- | --- | --- | --- | ---: |
|
||||
| `/anomalies` | `1c000100` | Dictionary (27) | `1c000000` | 1 |
|
||||
| `/anomalies/0/allocations` | `1c000100` | Dictionary (27) | `1c000000` | 1 |
|
||||
| `/anomalies/0/facility_ids` | `1c000100` | String (4) | `1c000000` | 3 |
|
||||
| `/anomalies/0/unallocated_by_staff` | `1c000100` | Dictionary (27) | `1c000000` | 0 |
|
||||
| `/settlement/input_payments` | `1c000100` | Dictionary (27) | `1c000000` | 5 |
|
||||
| `/settlement/rows` | `1c000100` | Dictionary (27) | `1c000000` | 5 |
|
||||
|
||||
Every listed Reader array is untyped. Raw getter byte equality remains **false**.
|
||||
This diagnostic is separate from the source-defined persisted snapshot.
|
||||
Neither this explanation nor an optional projection may weaken exact native
|
||||
expected/restored typed-byte comparison, entire Runtime equality, formal 4+4
|
||||
member equality, settlement counts, duplicate-payment checks or replay checks.
|
||||
No projection implementation or new save_restore verification is claimed here.
|
||||
The executable R18 normalization gate is still unchanged, so save_restore is
|
||||
still open pending the exact encoder port and its required validation.
|
||||
@@ -0,0 +1,78 @@
|
||||
# Repair19 — incomplete; execution constraint clarification pending
|
||||
|
||||
This is not a ready submission and not formal acceptance. Original credit stays
|
||||
13/88. All 15 conditions plus EV retain their existing scope; nativeWindows
|
||||
remains pending for each. No product, acceptance, domain or outcome was changed.
|
||||
|
||||
## Blocking instruction conflict
|
||||
|
||||
The recorded repair19 direction requires local fixture reruns, isolated temp-Git
|
||||
negative and positive controls, and removing the Stage-A refusal only after
|
||||
those tests pass. The final worker instructions prohibit Git writes and say
|
||||
tests are formally executed by Runner. This dispatch has requested clarification
|
||||
on whether that prohibition exempts the specifically requested file-only and
|
||||
isolated temp-Git fixtures. There is no answer at the time of this report.
|
||||
No exemption has been assumed. This is an execution-constraint blocker, not a
|
||||
claim that pending formal acceptance itself blocks implementation.
|
||||
|
||||
## Changes actually made in this dispatch
|
||||
|
||||
- `p04_native_series.py`: retain both JSON and Variant Writer/Reader phase-byte
|
||||
comparisons, but increment the S3 pair metric only for `.var`. The requested
|
||||
interpretation is 352 checked file pairs representing 176 observations. A
|
||||
new execution output confirming that count has not been generated.
|
||||
- `p04_native_series.py`: add the B1 `power_stop` join for the five observations
|
||||
at 959–963, positive finite original reserve, actual public mana and power
|
||||
flags, direct closure, same service identity/progress/reservations, queued B
|
||||
release/stock/patience, recovery payment and unchanged next ledger. Bind the
|
||||
saved 961 recovery command and the APPLY_COMMANDS phase. Preserve the actual
|
||||
`RELATED_FACILITY_INACTIVE` reason. This code is untested; its addition to the
|
||||
verifier's returned conditions is conditional on every assertion succeeding
|
||||
when Runner executes it. No successful power_stop result is claimed here.
|
||||
- `REPAIR19_CORRIGENDUM.md`: correct the StringName/order-only explanation,
|
||||
distinguish raw getter from public persistence, list all six supplied typed
|
||||
Array header changes, and record the actual source call-chain full-body pins.
|
||||
|
||||
## Static observations and preservation
|
||||
|
||||
- In-memory Python `compile` of the edited series module succeeded. No fixture,
|
||||
native, Godot, full-regression or Runner run occurred.
|
||||
- The exact relocated operations source was read. All five complete function
|
||||
bodies, lengths and hashes equal
|
||||
`B/audit-support/p04-repair19-close-r01/ROOT_ACTUAL_PERSISTENCE_CALL_CHAIN.json`.
|
||||
The hash table and exact call-chain explanation are in the corrigendum.
|
||||
- The checker region from `NATIVE_RUNS` through `verify_native`, inclusive of
|
||||
the C constants and helpers, equals its HEAD bytes. Region SHA256:
|
||||
`5d778b3cfab7c139ca03812489508ce9aae90c1d17f8c2249c340f30d1496b3b`.
|
||||
- No R17/R18 output or status file was edited. The existing uncommitted R18
|
||||
checker changes remain. No Git write, agent, integration or Runner operation
|
||||
occurred. No native evidence was created or modified.
|
||||
- No collector was run. Both the existing r17 collection and the required fresh
|
||||
r19 destination remain untouched by this dispatch. Since the series verifier
|
||||
bytes changed, the old r17 collection cannot validate these new verifier bytes;
|
||||
it must never be overwritten or represented as a repair19 collection.
|
||||
|
||||
## Remaining implementation and validation
|
||||
|
||||
Repair19 is incomplete. R2's C-backed paid shift join, R3's exact encoder port
|
||||
and fixtures, Stage A's actual Git-role verifier, new index generator, fixture
|
||||
protocol repairs and all requested fixture executions remain to be completed.
|
||||
The old Stage-A unconditional refusal remains in place. The old
|
||||
`verify_product_target` has not been edited or replaced in this dispatch.
|
||||
The 9b1a18c anchor resolves to
|
||||
`9b1a18c1eb25ca1300dcd59da78423c0dda2f498`; the old checker constant has not yet
|
||||
been changed as part of a complete Stage-A repair.
|
||||
|
||||
No new output JSON is reported, and no old fixture success is reused as success
|
||||
for the edited module. B4a/B4b semantics, frozen save_restore measurements and
|
||||
its current open result remain unchanged. The 25 C negatives and 56 series
|
||||
negatives have not been rerun.
|
||||
|
||||
There are no valid repair19 index-generation or submitted/merged-candidate Root
|
||||
commands yet: the required generator and role verifier are not implemented.
|
||||
Providing plausible commands for nonexistent functionality would be misleading.
|
||||
The completed follow-up must supply those exact commands after implementing and
|
||||
validating the protocol, retain all existing outputs, and collect exclusively
|
||||
into the fresh `B/audit-support/p04-raw-collect-r19` destination.
|
||||
|
||||
Here `B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Repair20 persistence corrigendum
|
||||
|
||||
The old Root explanation, “StringName/order only”, omitted six generic Array
|
||||
header changes. `get_day_summary` returns the internal day's `duplicate(true)`;
|
||||
it does not return the canonical persisted representation. Its raw Writer and
|
||||
Reader bytes remain unequal. All original raw files and values are retained.
|
||||
No product getter change is proposed.
|
||||
|
||||
| Path under day_summary | Writer header | Element kind | Reader header | Length |
|
||||
| --- | --- | --- | --- | ---: |
|
||||
| anomalies | 1c000100 | Dictionary (27) | 1c000000 | 1 |
|
||||
| anomalies/0/allocations | 1c000100 | Dictionary (27) | 1c000000 | 1 |
|
||||
| anomalies/0/facility_ids | 1c000100 | String (4) | 1c000000 | 3 |
|
||||
| anomalies/0/unallocated_by_staff | 1c000100 | Dictionary (27) | 1c000000 | 0 |
|
||||
| settlement/input_payments | 1c000100 | Dictionary (27) | 1c000000 | 5 |
|
||||
| settlement/rows | 1c000100 | Dictionary (27) | 1c000000 | 5 |
|
||||
|
||||
These are the six entries in `T/ROOT_S2A_TYPED_ARRAY_HEADER_AUDIT.json`.
|
||||
The new code derives them again from typed nodes and compares the list to that
|
||||
diagnostic. This execution is **NOT RUN** in repair20.
|
||||
|
||||
The persistence chain is `create_snapshot` (620) →
|
||||
`_snapshot_root_report(raw_snapshot, field_cache, false)` (2943) →
|
||||
`_visit_snapshot_tree(..., encode=true)` (2992). The earlier capture=true pass
|
||||
does not cache encoded day_summaries. `_to_snapshot_variant` is not on this path.
|
||||
|
||||
The projection is confined to raw getter → persisted day1. It retains all
|
||||
scalar Variant bytes, including numeric type, rejects nonfinite floats and
|
||||
unsupported values, stringifies permitted keys, rejects canonical collisions,
|
||||
sorts String keys, rebuilds untyped Arrays, and enforces collection, depth, node,
|
||||
single-string and aggregate-string limits. Unsupported dictionary-key types
|
||||
are rejected, never silently normalized. The optional admitted subset does not
|
||||
include composite-key Godot stringification. No projection is allowed on the
|
||||
native persisted expected/restored comparison.
|
||||
|
||||
Full Runtime Writer/Reader/restored equality, formal 4+4 member equality,
|
||||
settlement/duplicate-payment checks and the actual advance_to replay checks
|
||||
remain independent strict requirements. If getter projection still differs
|
||||
from the native canonical_day1 bytes, `save_restore` remains open and reports
|
||||
that exact mismatch. No new successful equality is claimed without Root's run.
|
||||
@@ -0,0 +1,183 @@
|
||||
# Repair20 — source submission; tests NOT RUN
|
||||
|
||||
Source-only repair. No Git command, fixture, collector, series, Godot/native,
|
||||
Runner, agent, commit, branch or integration operation was executed. The only
|
||||
code validation performed was Python `compile(..., 'exec')` in memory, without
|
||||
imports/execution of the edited modules. No acceptance is claimed. Original
|
||||
credit remains 13/88; all 15 conditions + EV, seven domains and three safety
|
||||
outcomes retain their scope. nativeWindows remains pending for every condition.
|
||||
The next submission SHA is assigned by Runner and is not predeclared here.
|
||||
|
||||
Changed files:
|
||||
|
||||
- `tools/check_p04_order_evidence.py`: real checkout/role binding, isolated
|
||||
object stores under B, first-1080 R2 join, schema2 recomputation and r20 collection.
|
||||
- `p04_native_series.py`: B1 departure/despawn/full-state joins, explicit 352/176
|
||||
S3 checks, confined getter projection, persistence pins and six-header diagnostic.
|
||||
- `collect_p04_native.py`: exclusive new collection at
|
||||
`B/audit-support/p04-raw-collect-r20`.
|
||||
- `checker_negative_fixtures.py`: retain existing content-case IDs and meanings;
|
||||
isolate their content layer explicitly in the test harness, while the separate
|
||||
real-Git fixture exercises production role binding without a bypass.
|
||||
- New `build_p04_index.py`, `checker_role_fixtures.py`,
|
||||
`checker_encoder_fixtures.py`, `checker_join_fixtures.py`.
|
||||
- New `REPAIR20_STATUS.md` and `REPAIR20_CORRIGENDUM.md`.
|
||||
|
||||
In-memory compilation: all eleven Python files (checker plus ten modules in E)
|
||||
compile. This is syntax validation only; every test below is **NOT RUN**.
|
||||
The retained C region, `NATIVE_RUNS` through `verify_native`, is byte-for-byte
|
||||
equal to T's retained source, SHA256
|
||||
`5d778b3cfab7c139ca03812489508ce9aae90c1d17f8c2249c340f30d1496b3b`.
|
||||
`verify_product_target` itself is also byte-for-byte retained and no longer called.
|
||||
Existing repair17/18/19 status, corrigendum, raw outputs and T's retained archives
|
||||
were not modified. Existing C 25 negatives and series 56 negatives remain required.
|
||||
|
||||
## R3 source pins
|
||||
|
||||
Product source: `scripts/operations/operations_coordinator.gd`, 130891 bytes,
|
||||
SHA256 `871a248ee101b6df543fd5cf0440b5d4b0f02c21fa145a99f0687888c1bc6f90`.
|
||||
Runtime verification checks the complete bodies and cross-checks the actual
|
||||
body bytes, lengths and hashes against `T/ROOT_ACTUAL_PERSISTENCE_CALL_CHAIN.json`.
|
||||
|
||||
| Function | SHA256 |
|
||||
| --- | --- |
|
||||
| get_day_summary | 449eef926054cf76c39d6b4c016da3d775d41f2b343b4f8ad57b62c594cb32bb |
|
||||
| create_snapshot | 8c6e4f000e6df6f0f996445a57c4c17223cae4e1df57a9c2e3a605cf4d9f5309 |
|
||||
| _snapshot_root_report | 96084d76beeac11d165753a82160f789cd47b4dec4b6bb8cd6599a4f5ed08efe |
|
||||
| _visit_snapshot_tree | 70792369c05882b813632a3c96f934928e9a089d420700cfc0d4c8fa971972a0 |
|
||||
|
||||
The corrigendum lists all six Array header changes. Native persisted comparisons
|
||||
are direct typed-byte comparisons. Raw getter inequality remains diagnostic.
|
||||
The optional projection admits only the supported persistence subset; unsupported
|
||||
composite dictionary keys fail closed. Its negative controls include field loss,
|
||||
extra fields, both numeric-type directions, value changes, nonfinite floats,
|
||||
invalid keys, collisions, unsaveable values, wrong call chain and use on native
|
||||
persisted comparisons. The positive changes only Array headers and StringName.
|
||||
|
||||
## Condition disposition on this untested source
|
||||
|
||||
No condition is newly proven by this dispatch. All tests are **NOT RUN**.
|
||||
|
||||
| Condition | State and reason pending Root execution |
|
||||
| --- | --- |
|
||||
| power_stop | Open: new B1 959–963 join; real B at 959/960, normal recorded departure then absence, unchanged one-time release, stock 1, wallet 50, no B charge; recovery 25 at 962 and no extra 963 charge. |
|
||||
| destruction_stop | Pending: retained S3 B4b BROKEN failure and zero-sale/release predicates. |
|
||||
| dependency_stop | Pending: unchanged C paused service identity/progress/reservation proof. |
|
||||
| shift_completion | Open: new C first-1080 join; PIDs 77462/77504/77536 ended, 1338-file content 5f, 44→45, ledger_00000006/25G, DAY2 present before handover and zero after, no extra at 1081. |
|
||||
| recovery_completion | Pending: unchanged C recovery transaction/idempotency join. |
|
||||
| same_minute_replay | Pending: retained real paid request-resend join; never substituted for settlement replay. |
|
||||
| closure_release | Pending: retained S3 closure/release plus despawn-aware B1 checks. |
|
||||
| exit_release | Pending: retained S3 B2 release/completion ordering. |
|
||||
| patience_release | Pending: retained S3 B3 patience expiration and release. |
|
||||
| paused_midnight | Pending: unchanged C paused-midnight series. |
|
||||
| midnight_settlement | Pending: retained S2a nonzero sales/payroll/upkeep/duty joins. |
|
||||
| midnight_replay | Pending: actual advance_to replay, zero processed minutes and zero daily closes, exact Runtime bytes. |
|
||||
| save_restore | Open: direct full typed Runtime and formal 4+4 equality remain mandatory; optional getter projection must equal actual persisted day1. A remaining mismatch is explicitly reported, never projected out of native restore. |
|
||||
| night_staff_absent | Pending: retained S2b WAITING_FOR_STAFF at 1081. |
|
||||
| overtime_task | Pending: retained S2b interrupt-level-zero task/staff/shift and once-only duty joins. |
|
||||
| EV | Pending: retained S1 reachable reserved staff power-stop proof and pinned static chain. |
|
||||
|
||||
Stage A is implemented but **NOT RUN**. Submission is inferred from actual Git
|
||||
ancestry and scoped diff; the composed product is labelled prospective, not a
|
||||
tested worker game. Integration/final requires an actual two-parent merge and
|
||||
exact recomputed merge tree, native product map/modes and submitted checker/doc
|
||||
patch. Every tracked file and executing module must match HEAD bytes/mode;
|
||||
untracked/ignored files and symlinks fail. Unknown/deleted sources and candidate,
|
||||
code-only or phase declarations cannot authorize a role. Metadata membership
|
||||
comes from the real 9b1a18c tree, without the old count assumptions.
|
||||
|
||||
Schema2 retains native source/config/input/environment, real lifecycle PIDs,
|
||||
formal proofs, complete raw references, authority and original-audit uncertainty,
|
||||
domain and reachability mappings. Its entire payload is recomputed from raw by
|
||||
the same checker. It contains no candidate==HEAD pin or Root-passed flag.
|
||||
`generation-invocation.json` separately records the actual invocation/HEAD.
|
||||
|
||||
## Root execution order (all commands below NOT RUN)
|
||||
|
||||
Run from the clean, actual auto-submitted checkout. Root supplies actual clean
|
||||
merged/final checkout paths later; do not substitute invented SHAs. Preserve all
|
||||
prior output directories and use exclusive fresh output destinations.
|
||||
|
||||
```sh
|
||||
export B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build
|
||||
export E=docs/plans/original-completion-2026-10-02/execution/p04-01
|
||||
export BUILD_OUTPUT_DIR="$B"
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
```
|
||||
|
||||
1. Local file-only and fixture controls on the actual new SHA:
|
||||
|
||||
```sh
|
||||
python3 "$E/checker_negative_fixtures.py"
|
||||
python3 "$E/checker_applicability_fixtures.py" --native-bundle "$B/audit-support/native-first-current"
|
||||
python3 "$E/checker_native_fixtures.py" "$B/audit-support/native-first-current"
|
||||
python3 "$E/checker_series_fixtures.py" --build "$B"
|
||||
python3 "$E/checker_encoder_fixtures.py"
|
||||
python3 "$E/checker_join_fixtures.py" --build "$B"
|
||||
```
|
||||
|
||||
2. Real series against B, then the exclusive r20 collection:
|
||||
|
||||
```sh
|
||||
python3 "$E/p04_native_series.py" --build "$B"
|
||||
python3 "$E/collect_p04_native.py" --build "$B"
|
||||
```
|
||||
|
||||
3. After auto-submit, generate the fresh normal Runner index. B/p04-evidence was
|
||||
absent at static inspection. If it is now occupied, retain it and choose an
|
||||
explicit fresh directory with P04_EVIDENCE_DIR for manual checks; do not overwrite.
|
||||
The normal Runner uses B/p04-evidence and must receive that actual generated index.
|
||||
|
||||
```sh
|
||||
python3 "$E/build_p04_index.py" --build "$B" --output "$B/p04-evidence" \
|
||||
--invocation validate \
|
||||
--registered-ref deb1bcb2374eb378b840ca345db952bd8572cb6e \
|
||||
--tested-ref 5f6bc11d6bb6e92d22d787989b219fef3a435bc1
|
||||
```
|
||||
|
||||
4. Checker on submitted, merged and final actual checkouts. Real temp-Git fixtures
|
||||
need the generated index, so their execution follows step 3, not a fabricated
|
||||
pre-submission index. They create throwaway repositories/objects only under B.
|
||||
|
||||
```sh
|
||||
python3 "$E/checker_role_fixtures.py" --build "$B" --evidence "$B/p04-evidence"
|
||||
python3 tools/check_p04_order_evidence.py --verify-only
|
||||
python3 "$P04_MERGED_CHECKOUT/tools/check_p04_order_evidence.py" --verify-only
|
||||
python3 "$P04_FINAL_CHECKOUT/tools/check_p04_order_evidence.py" --verify-only
|
||||
```
|
||||
|
||||
The same index works in all three roles. For separate invocation records, the
|
||||
generator also accepts `--invocation prepare-integration` and `--invocation final`
|
||||
with explicit fresh output paths while executing from those actual checkouts.
|
||||
These labels are receipt metadata and grant no role. Root must respect the normal
|
||||
Runner state machine: its prepare_integration requires prior formal review.
|
||||
An isolated local prospective-merge control does not replace that review or move
|
||||
Runner state. `runner.validate("P04-01")`, `runner.prepare_integration("P04-01")`
|
||||
and `runner.integrate("P04-01")` remain Root operations only.
|
||||
|
||||
5. Mandatory whole validation and full 178-game regression once on exact final
|
||||
sources. The supplied Runner core exposes
|
||||
`runner.verify("P04-01", final_checkout, actual_final_sha, "final")`; retain every
|
||||
configured mandatory validation, then run the existing guarded full-game runner:
|
||||
|
||||
```sh
|
||||
python3 "$P04_FINAL_CHECKOUT/tools/run_all_tests.py" \
|
||||
--project-root "$P04_FINAL_CHECKOUT" --output "$B/p04-final-product-full-r01"
|
||||
```
|
||||
|
||||
Do not change timeout, marker contracts or suite policy, splice phases, or reuse
|
||||
historical 177-suite proof. Confirm the final selected source map and 178-suite
|
||||
roster, and preserve source/default-profile invariance. This command is the one
|
||||
full run, not permission for an additional `make test` native run.
|
||||
|
||||
6. Independent Opus review using Root's existing reviewer route, with exact
|
||||
source/validation materials mirrored into the frozen checkout's .review-material.
|
||||
The supplied core's formal method is `runner.review("P04-01")`; that method reviews
|
||||
the actual submitted revision and requires its validation evidence. Any additional
|
||||
final-composition Opus review must explicitly receive the actual final revision.
|
||||
The external commander/Opus CLI entry point and installed Runner configuration
|
||||
were not supplied in this checkout, so no guessed shell command is asserted for
|
||||
them. Root retains its existing formal validation/review/integration workflow.
|
||||
|
||||
The absent external entry-point/configuration details are handoff context gaps,
|
||||
not evidence of a pass, and not a blocker solely because Root testing is pending.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Repair21 — source submission; tests NOTRUN
|
||||
|
||||
P04-01 source-only performance repair. Changes are restricted to the checker,
|
||||
the new `bulk_reader_equivalence.py` fixture, and this status file. No Git
|
||||
command, test, fixture, collector, Runner, native/Godot run, agent, commit,
|
||||
branch or integration operation was executed by the worker.
|
||||
|
||||
`read_blobs` validates full lowercase 40-hex OIDs before starting a process,
|
||||
deduplicates/sorts them, and uses one `git cat-file --batch` process per nonempty
|
||||
request. Empty requests start no process. `check_output` inherits stderr and
|
||||
raises for nonzero exit status just like `git_bytes`. The separately injectable
|
||||
byte parser verifies ordered OIDs, blob type, decimal size, exact framing and
|
||||
complete output consumption. There is deliberately no blob cache, so no object
|
||||
can be reused across `_GIT_ENV` namespaces. The two bulk call sites preserve
|
||||
selected-path sorted manifests and every actual-file byte/mode comparison.
|
||||
|
||||
No existing require message or assertion was removed. The `NATIVE_RUNS` through
|
||||
`verify_native` region, commit-type checks, ancestry, merge-tree isolation,
|
||||
status/untracked/symlink/module checks, schema/encoder, six existing suites and
|
||||
role case IDs were not edited. Checker self-blob reads remain unchanged.
|
||||
|
||||
The fixture source covers binary NUL/CR/LF/trailing LF, empty and large blobs,
|
||||
unicode `ls-tree -z` path resolution, duplicates, empty input, malformed/short
|
||||
OIDs, missing objects, tree/commit objects, invalid/truncated/oversized/garbage
|
||||
responses, process failure, one-process sorted input, and private object
|
||||
visibility inside versus outside and across isolated namespaces. It has not run.
|
||||
|
||||
Static validation: `ast.parse` and in-memory `compile(..., 'exec')` completed for
|
||||
the checker and new fixture. Neither module was imported or executed. This is
|
||||
syntax validation only. Tests: **NOTRUN**. Runtime equivalence and completion
|
||||
within the registered 180 seconds remain for Root to measure.
|
||||
|
||||
The existing schema2 index at B/p04-evidence/index.json was read as data, along
|
||||
with the existing builder and role fixture. Its registered/execution/tested
|
||||
roles remain deb1bcb/9767b0a/5f6bc11 and original_credit remains 13/88.
|
||||
The existing index and r20 collection are untouched. No new generation is
|
||||
needed if Root confirms the recomputed payload is identical. If generation is
|
||||
needed, use a new explicit destination, never overwrite existing evidence.
|
||||
Original P04-01 remains incomplete; criteria and nativeWindows pending status
|
||||
are unchanged. No original credit or acceptance is claimed.
|
||||
|
||||
## Root execution order — all NOTRUN
|
||||
|
||||
Use the clean new auto-submitted checkout, whose SHA is assigned after this
|
||||
source submission. Do not use the registered base SHA as the new submission.
|
||||
|
||||
```sh
|
||||
export B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build
|
||||
export E=docs/plans/original-completion-2026-10-02/execution/p04-01
|
||||
export BUILD_OUTPUT_DIR="$B"
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
python3 "$E/bulk_reader_equivalence.py"
|
||||
python3 "$E/checker_negative_fixtures.py"
|
||||
python3 "$E/checker_applicability_fixtures.py" --native-bundle "$B/audit-support/native-first-current"
|
||||
python3 "$E/checker_native_fixtures.py" "$B/audit-support/native-first-current"
|
||||
python3 "$E/checker_series_fixtures.py" --build "$B"
|
||||
python3 "$E/checker_encoder_fixtures.py"
|
||||
python3 "$E/checker_join_fixtures.py" --build "$B"
|
||||
python3 "$E/checker_role_fixtures.py" --build "$B" --evidence "$B/p04-evidence"
|
||||
```
|
||||
|
||||
Next run the registered validation on the **new auto-submitted SHA**, retaining
|
||||
its existing 180-second timeout and actual timed receipt. The supplied formal
|
||||
API call is exactly:
|
||||
|
||||
```python
|
||||
runner.validate("P04-01")
|
||||
```
|
||||
|
||||
The external Runner launcher/configuration is not present in the supplied task
|
||||
context; use Root's already registered invocation, without substituting an
|
||||
invented CLI, changing timeout, or treating direct checker timing as registered
|
||||
validation. Record actual SHA, elapsed time and exit status from that invocation.
|
||||
|
||||
Then run direct real-candidate controls from actual clean submitted, merged and
|
||||
final checkouts, preserving the formal review/integration state machine:
|
||||
|
||||
```sh
|
||||
python3 tools/check_p04_order_evidence.py --verify-only
|
||||
python3 "$P04_MERGED_CHECKOUT/tools/check_p04_order_evidence.py" --verify-only
|
||||
python3 "$P04_FINAL_CHECKOUT/tools/check_p04_order_evidence.py" --verify-only
|
||||
```
|
||||
|
||||
After required formal validation/review and exact final-source binding, run the
|
||||
full178 product regression once, using a fresh output destination:
|
||||
|
||||
```sh
|
||||
python3 "$P04_FINAL_CHECKOUT/tools/run_all_tests.py" \
|
||||
--project-root "$P04_FINAL_CHECKOUT" --output "$B/p04-final-product-full-r21"
|
||||
```
|
||||
|
||||
Do not regenerate raw collections, overwrite index/r20 evidence, alter existing
|
||||
case semantics, or run an additional native/full suite to compensate for a
|
||||
timeout. Any unrelated improvement is deferred. Pending Root execution is not
|
||||
a worker specification gap and does not change this submission's scope.
|
||||
+348
File diff suppressed because one or more lines are too long
+73
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"scope": "AST/static source checks and in-memory compile ONLY; no module imports or execution of task code",
|
||||
"python": [
|
||||
{
|
||||
"path": "tools/check_p04_order_evidence.py",
|
||||
"bytes": 78399,
|
||||
"sha256": "a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/p04_native_series.py",
|
||||
"bytes": 72591,
|
||||
"sha256": "667c95e10c451239292b08589706bfef978de730b321151ae7845dd10a06c411",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_join_fixtures.py",
|
||||
"bytes": 7888,
|
||||
"sha256": "f89ee6b835a8b0dc47f804686f7e338e7178c24a5a30a8a25bcd8bf59dfbea0c",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_series_fixtures.py",
|
||||
"bytes": 6779,
|
||||
"sha256": "f91b2d7003fd5c8cd196f849ad392d37479317e7d109eb584ccde5e5073e5bee",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/collect_p04_native.py",
|
||||
"bytes": 2996,
|
||||
"sha256": "d727230de0af94dfc7e68bec40e84ecaa86a5b98d5e5bc16c2a35fe9c67fa12d",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/build_p04_index.py",
|
||||
"bytes": 9246,
|
||||
"sha256": "47ae05b7bec617063e5d4fca2d8687f25e2eee0f809b5676ec91cf21c3223773",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s4/run_s4_native.py",
|
||||
"bytes": 18623,
|
||||
"sha256": "03a0aaab7e824d1b516ee4c6b456913439515c03daa687b5822cdfefb4d54c46",
|
||||
"AST": "parsed",
|
||||
"in_memory_compile": "compiled; not executed"
|
||||
}
|
||||
],
|
||||
"gd_validation": "static text/declaration review only; engine parse NOT RUN",
|
||||
"gd_sha256": "ceb592d853fd50063f20496c7b6df05257528dbe1c0d0d80bc6a8ccb1f9dd6d7",
|
||||
"C_frozen": {
|
||||
"exact_equal_to_immutable_full_stage": true,
|
||||
"bytes": 20918,
|
||||
"sha256": "5d778b3cfab7c139ca03812489508ce9aae90c1d17f8c2249c340f30d1496b3b",
|
||||
"body_with_one_final_LF_sha256": "33ae2af6afb98b48919ed679ab8519b7da7ddfda68c00f71c557d5cc9aad4a6c"
|
||||
},
|
||||
"required_registry_unchanged": true,
|
||||
"required_conditions": 15,
|
||||
"EV": "additional existing obligation, unchanged",
|
||||
"domains": 7,
|
||||
"product_input_files": 1338,
|
||||
"parse_gd_files": 316,
|
||||
"native_runs": 0,
|
||||
"fixture_runs": 0,
|
||||
"tests": "NOT RUN",
|
||||
"S4_raw": "pending Root execution",
|
||||
"formal_acceptance": false
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
# Repair22 — source-ready submission; NOT RUN
|
||||
|
||||
This batch changes only `tools/check_p04_order_evidence.py` and this P04-01
|
||||
documentation subtree. No Git command, test/fixture, engine, Runner operation,
|
||||
agent, manual commit or integration was performed. The normal autosubmit owns
|
||||
the submitted revision; the contract base SHA is not a new evidence version.
|
||||
|
||||
S3 B1 no longer supplies `power_stop`. Its full diagnostic checks remain under
|
||||
`dependency_closure_diagnostic`, including the actual dependency reason,
|
||||
closure/release, departure history and recovery sale. It approves no priority.
|
||||
Strict power-vs-closure order remains D05 pending/unknown. The existing approvals
|
||||
cover dependency hold and the18:00 shift interval only; A01 is not reopened.
|
||||
|
||||
S4 is a new minimal single-case QA probe using the existing ActorHardening,
|
||||
physical-kitchen and guarded Writer/Reader pattern. It cuts the actual sole
|
||||
cafeteria edge at959, waits for normal evaluation/projection at960, retains the
|
||||
held state at961, reconnects that same edge, and waits normally through the
|
||||
source-derived hold until970. Source constants were re-read as10/10. No forced
|
||||
evaluation, hold bypass, direct power/progress/patience/capacity writes or
|
||||
product edits are introduced. Kitchen Facility.OPEN / network ACTIVE, positive
|
||||
mana, operating dependency and open operator distinguish it from B1.
|
||||
|
||||
Three formal Writer points and separate-process Reader restores retain every
|
||||
typed Runtime field and all four SaveService members. Reconnect resend at970
|
||||
must leave the full Runtime unchanged; normal971 and Reader replay add no
|
||||
extra sale/completion. Full intermediate phase raw is preserved. All graph
|
||||
nodes, cut/restored edge sets, revisions, allocation and derived dependency
|
||||
restoration are checked. Normal post-completion guest redecision/despawn is
|
||||
retained, not forced or concealed; no exit accompanies the outage/hold.
|
||||
|
||||
R now derives shift PID and sale IDs from the existing full raw joins. S1 joins
|
||||
the existing pre-travel reservation/task/staff cell to the boarded task/trip,
|
||||
and checks actual mana0 and elevator loss of power. The existing join fixtures
|
||||
are retained and extended with the requested semantic negatives and consistent
|
||||
dynamic-ID positive controls. Existing C25 and all other fixture sources remain
|
||||
in place. No fixture result is claimed: S4 positives await actual Root raw.
|
||||
|
||||
ORDER_AUDIT.md and order-audit.json are one current seven-domain table with
|
||||
normative rules, source, C/S1/S2a/S2b/S3 evidence, pending S4, approved authority
|
||||
and pending D05. REPAIR17–21 remain untouched historical documents and outputs;
|
||||
their current-status claims are superseded by repair22. The current protocol,
|
||||
collection plan and this status agree: no final acceptance.
|
||||
|
||||
Static validation is recorded in REPAIR22_STATIC_VALIDATION.json:
|
||||
|
||||
- Seven Python files parsed with AST and compiled in memory; none imported or
|
||||
executed. GDScript received static text/source review only; engine parse NOT RUN.
|
||||
- The entire frozen C region is byte-identical to the preserved full-stage
|
||||
checker. Its one-final-LF body hash is
|
||||
`33ae2af6afb98b48919ed679ab8519b7da7ddfda68c00f71c557d5cc9aad4a6c`, matching
|
||||
the supplied repair21 C_FULL_BLOCK_EXACT receipt. Existing C checks are intact.
|
||||
- The complete15-condition registry matches that immutable source; EV, seven
|
||||
domains and three safety outcomes are unchanged. Product1338 inputs and the
|
||||
316-file `.gd` parse roster were counted from the actual relocation manifest.
|
||||
- The five supplied public power bodies were compared to their actual relocated
|
||||
source. Full bodies, hashes, timing and restore analysis are retained in
|
||||
REPAIR22_SOURCE_FACTS.json.
|
||||
|
||||
The exact finite Root plan is `s4/S4_ROOT_RUN.md`: one fresh own-B four-phase
|
||||
collection, with explicit arguments and full input/QA/source footprints. All
|
||||
old C/S1/S2a/S2b/S3 raw and all R17 sources remain accessible and are not rerun.
|
||||
S4 remains **missing/pending Root execution**. Future native/index/cross-gate
|
||||
work does not block this source submission. The updated collector/index
|
||||
references use a fresh r22 destination; no r17/r20 evidence is overwritten.
|
||||
|
||||
Preserve the21615-check proof. Static full-map comparison found exactly the
|
||||
unchanged product1338 plus the old checker in its1339 map. The new checker hash
|
||||
differs; never claim1339 byte identity. The178 product suites do not invoke the
|
||||
checker and `.py` is excluded from the316 parse roster, so reuse requires the
|
||||
explicit QA-only source/config/input/environment applicability argument in
|
||||
EVIDENCE_COLLECTION.md. New-checker validation and S4 are separate, pending.
|
||||
|
||||
Root must rebind support53cb through its normal registered task once the new
|
||||
source is known, without metadata bypass. Only if an exact final1339 full proof
|
||||
is required, perform one run after all fixes, S4 raw, local checks, eight suites
|
||||
and rebind are bundled; do not splice versions or perform a blanket full now.
|
||||
|
||||
No required source context is missing for this preparation. Remaining D05
|
||||
choices are explicitly pending and not silently implemented. Formal validation,
|
||||
independent review, exact final-source applicability and normal integration
|
||||
remain pending. CurrentAlphaScope,30 packages/88 deliveries, original13/88 and
|
||||
P04 main9b are unchanged. P04 receives no credit; nothing is integrated.
|
||||
@@ -0,0 +1,34 @@
|
||||
# Repair 23 — submitted, NOT RUN
|
||||
|
||||
S4 source preparation only. The wrapper now defaults to fresh import without
|
||||
accessing cache receipts or cache directories. It checks that private `.godot`
|
||||
is absent immediately before import and requires the complete 316-script parse
|
||||
roster. The complete 1338-file source check and sole QA override remain intact.
|
||||
Optional cache mode retains the original registered-path, passed-run, source
|
||||
identity, engine identity and original-cache preservation guards.
|
||||
|
||||
Engine identity is independently pinned to
|
||||
`c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf`.
|
||||
Read-only SHA256 inspection found identical bytes in the installed executable
|
||||
and the preserved r22 staged executable; neither was launched.
|
||||
|
||||
Current wrapper SHA256:
|
||||
`6d5f4e05e87607413bfb5498dc9c9e17c2bfd96bf751c9e645050ca6f8e69d5c`.
|
||||
The current runbook binds this SHA. The series verifier already binds the wrapper
|
||||
to current checkout bytes dynamically, so no series metadata change is needed.
|
||||
Historical REPAIR22_STATIC_VALIDATION.json is preserved as historical evidence,
|
||||
not rewritten to claim validation of the new wrapper.
|
||||
|
||||
Root must use fresh staging `audit-support/p04-s4-source-r23` and fresh plan
|
||||
`p04-s4-plan-r23.json`. Output remains `p04-s4-native-r22`. Old staging and plan
|
||||
are preserved. All R22 fixes and other suite wrappers/checks are retained.
|
||||
|
||||
Checker SHA256 remains
|
||||
`a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850`;
|
||||
S4 probe SHA256 remains
|
||||
`ceb592d853fd50063f20496c7b6df05257528dbe1c0d0d80bc6a8ccb1f9dd6d7`.
|
||||
No game input file was edited. Review was limited to source reading and hashes.
|
||||
No Git, tests, fixtures, check-only execution, engine, Runner, agents or manual
|
||||
commit were run. Native S4 evidence, root native/data/index work, independent
|
||||
review and formal acceptance remain pending. The expected validate MANIFEST
|
||||
failure is unchanged. Original P04-01 completion is not claimed.
|
||||
@@ -0,0 +1,77 @@
|
||||
# Repair24 — source-ready submission
|
||||
|
||||
Bounded continuation of P04-01 request-repair24; tests NOT RUN.
|
||||
Existing wrapper edits and the untracked identity fixture were read first and
|
||||
preserved/corrected. Only this execution subtree was written. No Git, engine,
|
||||
Runner, agent, commit or integration operation was performed.
|
||||
|
||||
Changed source/runbook SHA256 (exact bytes):
|
||||
|
||||
| File | SHA256 |
|
||||
| --- | --- |
|
||||
| s4/run_s4_native.py | dda495d33566eae14c8a707e77f8ec9dc6c7bd043076a091e90dd53f6a7490db |
|
||||
| p04_native_series.py | 348197759c169c2094db3b1114ca3abe5525b8d4bc9aca611d4579871a130b40 |
|
||||
| s4/engine_identity_negative_fixtures.py | f9148ef79cb62ec6631e7e3f23e1d3b062434f7152977aab3b9d18d82672255e |
|
||||
| s4/S4_ROOT_RUN.md | c1a00e3dad8d91d28df1a7f868f6e4e905f1df721baac964865e341c84356135 |
|
||||
|
||||
This new status document records the hashes above; its own hash must be taken
|
||||
from the submitted checkout, outside its own contents.
|
||||
|
||||
Wrapper pins changed: the own-B bare Godot copy is replaced with exactly
|
||||
`/Applications/Godot.app/Contents/MacOS/Godot`, rejecting symlinks and any
|
||||
different resolved path. Its constant SHA remains
|
||||
`c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf`.
|
||||
Engine identity no longer depends on an old cache runner-input. Fresh NO-CACHE
|
||||
is the default at plan and native collection. Optional explicit cache mode
|
||||
rejects failed receipts before reading their inputs or cache contents.
|
||||
No default cache or cache PASS is required. No engine is copied, re-signed or
|
||||
modified, and no codesign/xattr/security operation is added.
|
||||
|
||||
Before-launch, during-collection and explicit after-cleanup re-pins retain the
|
||||
full runtime descriptor (path/resolved path, bytes, SHA, device, inode, mode,
|
||||
mtime and ctime). Runner-input and execution retain equal before/during/after
|
||||
identity records and execution retains labeled observations. fixed_guard still
|
||||
re-pins the engine. The verifier permits only this exact installed engine
|
||||
outside B, reads/hashes it exactly and checks the recorded identities.
|
||||
Fixed inputs and QA remain own-B only; actual command[0] must equal the pin.
|
||||
|
||||
Defaults and Root staging are now:
|
||||
`p04-s4-native-r24`, `p04-s4-plan-r24.json`,
|
||||
`audit-support/p04-s4-source-r24`. Only RUNS S4 changes to r24.
|
||||
C/S1-S3 bindings, other series and the frozen C block (20916) are unchanged.
|
||||
Source1338, parse316, sole QA override, default-player protection, owned
|
||||
four-distinct-PID capture, live-engine refusal, disk admission, fresh output,
|
||||
cleanup-before-fixed-guard, formal Writer/Reader, NO_POWER, typed declarations,
|
||||
forbidden mutations/bypasses and mana constants guards remain.
|
||||
|
||||
Untouched checker: SHA256
|
||||
`a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850`,
|
||||
78399 B. Untouched probe: SHA256
|
||||
`ceb592d853fd50063f20496c7b6df05257528dbe1c0d0d80bc6a8ccb1f9dd6d7`.
|
||||
All 1338 game files are unchanged; no game source was written. No new claim of
|
||||
executed 1338-file verification is made.
|
||||
|
||||
The formal reviewer's five findings are carried forward unchanged, with no
|
||||
closure claimed. Their individual review text was not supplied in this dispatch
|
||||
or found in the local execution records searched; it is not reconstructed here.
|
||||
Main 9b remains frozen at original 13/88. Support 53cb remains review pending.
|
||||
D05 remains pending. No acceptance denominator, specification, main integration
|
||||
or original completion state changes.
|
||||
|
||||
r22 is an immutable failed proof and remains ineligible. Diagnostics:
|
||||
`audit-support/p04-root-d393-s4-r01/ENGINE_STAGING_DIAGNOSTICS.json`.
|
||||
Its pre-banner staged-copy failure is an environment/staging fault; no product
|
||||
logic or probe/checker repair is claimed. Root uses the installed executable
|
||||
directly and preserves r22. Do not rerun S1-S3, C or full178.
|
||||
|
||||
Validation: static AST parsing and in-memory compile completed for the wrapper,
|
||||
series verifier and identity fixture; no module was imported or executed.
|
||||
Read-only SHA/byte inspection completed for the four listed files, checker and
|
||||
probe. These are source checks only. Tests NOT RUN: identity fixtures, all other
|
||||
fixtures/suites, check-only plan generation, native S4, full178, registered
|
||||
validation and independent review. No unexecuted result is reported as passed.
|
||||
|
||||
Registered validation stays exit 2 until Root generates the current
|
||||
MANIFEST.json collection; the gate is not changed. This is the carried-forward
|
||||
expected gate state, not a newly executed validation result. Root execution,
|
||||
current exact-version evidence and formal independent review remain pending.
|
||||
+1802
File diff suppressed because it is too large
Load Diff
+9522
File diff suppressed because it is too large
Load Diff
+71
@@ -0,0 +1,71 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"scope": "Worker static controls only; no engine/registered validation/acceptance",
|
||||
"formal_acceptance": false,
|
||||
"original_credit": 0,
|
||||
"results": [
|
||||
{
|
||||
"check": "checker_approval_fixtures",
|
||||
"status": "pass",
|
||||
"positive": 6,
|
||||
"negative": 24,
|
||||
"scope": "path/hash/status + literal human answers"
|
||||
},
|
||||
{
|
||||
"check": "checker_negative_fixtures",
|
||||
"status": "pass"
|
||||
},
|
||||
{
|
||||
"check": "checker_applicability_fixtures",
|
||||
"status": "pass",
|
||||
"M": "c0ea009b28a7649aca6a433a1a24dc0be5e7ab81",
|
||||
"changed_function_units": 58
|
||||
},
|
||||
{
|
||||
"check": "checker_native_fixtures",
|
||||
"status": "pass",
|
||||
"scope": "mutations of historical supplied raw; not a native engine run"
|
||||
},
|
||||
{
|
||||
"check": "checker_series_fixtures",
|
||||
"status": "pass",
|
||||
"scope": "historical/synthetic controls"
|
||||
},
|
||||
{
|
||||
"check": "checker_encoder_fixtures",
|
||||
"status": "pass"
|
||||
},
|
||||
{
|
||||
"check": "checker_join_fixtures",
|
||||
"status": "pass",
|
||||
"scope": "historical/synthetic controls"
|
||||
},
|
||||
{
|
||||
"check": "bulk_reader_equivalence",
|
||||
"status": "not_run",
|
||||
"reason": "Contains git init/hash-object -w and isolated-object writes; worker Git writes prohibited"
|
||||
},
|
||||
{
|
||||
"check": "checker_role_fixtures",
|
||||
"status": "not_run",
|
||||
"reason": "Contains clone/checkout/add/commit/merge/reset; worker Git writes prohibited; fresh Q index is also not available"
|
||||
},
|
||||
{
|
||||
"check": "Python AST / top-level execution JSON",
|
||||
"status": "pass"
|
||||
},
|
||||
{
|
||||
"check": "git diff --check",
|
||||
"status": "pass"
|
||||
}
|
||||
],
|
||||
"exclusions": [
|
||||
"No fresh-Q positive fixture claimed",
|
||||
"No fake-normal-C qualification claimed",
|
||||
"No S5 native execution",
|
||||
"No full184/329",
|
||||
"No p04-acceptance on current S"
|
||||
],
|
||||
"note": "Recorded fixture outcomes are not exact-version native proofs. Final comment/document edits do not alter executed fixture semantics. Final source is unqualified for native admission; see REPAIR30_STATUS.md."
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Repair 30 — BLOCKED / partial implementation
|
||||
|
||||
This revision is **not ready for Root native admission**. Formal status remains
|
||||
**16/88, P04=0**. Outstanding items include unfinished implementation, not merely
|
||||
the fresh evidence Root is expected to produce after submission.
|
||||
|
||||
Implemented within allowed_paths:
|
||||
|
||||
- Four actual human approvals, exact answer/policy text, both source paths and
|
||||
SHA256 bindings; candidate documents marked superseded.
|
||||
- Per-ID path/hash authority binding for six D05 IDs; required authority refs
|
||||
corrected without changing the 15-condition + EV roster or thresholds.
|
||||
- Frozen M is c0ea009b28a7649aca6a433a1a24dc0be5e7ab81. Counts/membership derive
|
||||
from Git objects: 1368 product files excluding checker, 3644 metadata paths.
|
||||
Ordering functions retain registered byte equality; applicability has 58
|
||||
changed function units. Full details are in REPAIR30_SOURCE_FACTS.json.
|
||||
- Q must equal the recomputed merge-tree object. A commit cannot impersonate Q.
|
||||
C must have frozen M as first parent, legal S as second parent and the same
|
||||
entire tree as Q. Selected native sources explicitly include the checker;
|
||||
product equality continues to exclude it.
|
||||
- Seven-domain audit refreshed with actual M hashes/function lines. All
|
||||
condition-specific nativeWindows obligations remain pending.
|
||||
|
||||
Implementation/context gaps blocking readiness:
|
||||
|
||||
1. S5's four executable drivers and raw semantic adapters are **not written**.
|
||||
s5/PENDING.md records concrete source constraints. Additional approved
|
||||
destruction/day/pause-reason/exit measurements are not enforced from S5 raw
|
||||
bytes yet. REQUIRED retains the existing safety metrics.
|
||||
2. p04_native_series.py, collect_p04_native.py, S1–S4 wrappers and parts of the
|
||||
schema2 builder still bind historical run names/manifests/raw pins. Connect
|
||||
fresh Q material without replacing historical records or inventing future
|
||||
hashes. The builder now reads tested revision from native provenance and
|
||||
includes the checker, so the old bundle cannot satisfy the new role check.
|
||||
3. Full184/329 runner-input has no source_before or QA_overrides, unlike native
|
||||
QA inputs. It needs its own exact source/settings/markers/policy/engine/input
|
||||
and ended-receipt join. The native input loop is not a full-proof verifier.
|
||||
4. Parent/tree equality alone cannot establish normal prepare-integration
|
||||
provenance: a synthetic commit can have identical parents/tree. The supplied
|
||||
plan does not define the concrete normal-candidate qualification receipt,
|
||||
path and hash contract. Independent qualification remains pending; no
|
||||
invented Runner API or authority has been added.
|
||||
5. Bulk and role fixtures perform Git writes, including temporary objects,
|
||||
commits and merges. They were not run under the worker Git-write prohibition.
|
||||
Updated role negatives are unexecuted; genuine fresh-Q positive and
|
||||
fake-normal-C rejection remain unqualified.
|
||||
|
||||
Static results are in REPAIR30_STATIC_CHECKS.json. Historical/synthetic fixture
|
||||
success is not new game evidence. No engine, formal registered gate, Runner
|
||||
operation, Git write or other agent was invoked. Product/test-tree/Clock/values
|
||||
remain unchanged.
|
||||
|
||||
Required Root order after remaining implementation is repaired:
|
||||
|
||||
1. All eight static controls plus role/applicability/approval negatives.
|
||||
2. Genuine Q from final S and frozen M; fresh real native S1–S5 once.
|
||||
3. Inspect free disk and verified CoW, then full184/329 once on the same final Q
|
||||
including final checker; bind test_required_kitchen separately.
|
||||
4. Truthful index from exact raw material.
|
||||
5. Registered p04-acceptance on S with --verify-only.
|
||||
6. Independent whole-unit review.
|
||||
7. Normal prepare-integration/final on independently qualified C, equal to Q for
|
||||
source/settings/markers/policy/engine/inputs.
|
||||
|
||||
Keep live Root records under .runtime while M is frozen. Old S1–S4/r22 and f5
|
||||
full178 remain historical. No known-fail formal gate is a progress check.
|
||||
No sources/evidence are removed for the obsolete 500000-byte cap.
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"scope": "Worker static controls only; no native run or formal gate",
|
||||
"formal_acceptance": false,
|
||||
"original_credit": 0,
|
||||
"results": [
|
||||
{
|
||||
"check": "checker_approval_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "six positive and 24 negative authority controls"
|
||||
},
|
||||
{
|
||||
"check": "checker_negative_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "checker_applicability_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "checker_native_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "checker_series_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "checker_encoder_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "checker_join_fixtures",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"scope": "historical/synthetic control only; no fresh Q proof"
|
||||
},
|
||||
{
|
||||
"check": "Python AST / execution JSON",
|
||||
"status": "pass",
|
||||
"python_files": 22,
|
||||
"json_files": 17
|
||||
},
|
||||
{
|
||||
"check": "git diff --check",
|
||||
"status": "pass"
|
||||
},
|
||||
{
|
||||
"check": "normal C fail-closed in-memory controls",
|
||||
"status": "pass",
|
||||
"cases": [
|
||||
"missing authority contract rejected",
|
||||
"caller-forged authority contract rejected"
|
||||
],
|
||||
"scope": "not a Git role fixture"
|
||||
},
|
||||
{
|
||||
"check": "bulk_reader_equivalence",
|
||||
"status": "blocked",
|
||||
"exit_code": 1,
|
||||
"reason": "/tmp directory creation raised PermissionError (Operation not permitted), before any Git invocation",
|
||||
"execution": "fixture body unexecuted; Root-run"
|
||||
},
|
||||
{
|
||||
"check": "checker_role_fixtures",
|
||||
"status": "unexecuted",
|
||||
"reason": "isolated temporary directory unavailable; fresh-Q evidence also unavailable; Root-run"
|
||||
},
|
||||
{
|
||||
"check": "checker_candidate_fixtures",
|
||||
"status": "unexecuted",
|
||||
"reason": "isolated temporary directory unavailable; Root-run"
|
||||
}
|
||||
],
|
||||
"exclusions": [
|
||||
"S5 drivers and adapters not implemented",
|
||||
"fresh-Q pipeline not implemented",
|
||||
"full184/329 verifier not implemented",
|
||||
"normal-C authoritative receipt path/hash/provenance contract not supplied",
|
||||
"no genuine-Q or qualified-C positive claimed"
|
||||
],
|
||||
"note": "Existing REPAIR30_SOURCE_FACTS and REPAIR30_STATIC_CHECKS were reused without modification. Counts here describe static file parsing, not acceptance or runtime source membership."
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# Repair 31 — BLOCKED / incomplete implementation
|
||||
|
||||
Formal status remains **16/88, P04 credit 0**. **Do not admit the once-only
|
||||
native/full batch.** This revision does not finish BF35.
|
||||
|
||||
D05 is **six of six actually approved**. There is no policy question for the
|
||||
human. Adoption into enforced raw evidence and exact-Q verification are pending.
|
||||
A01 remains approved. The per-ID path/SHA256 bindings in the checker and the
|
||||
superseded candidate documents are preserved. The authority fixtures exercise
|
||||
positive, missing, wrong-hash, pending-status and wrong-path cases for every ID.
|
||||
|
||||
Repair30's SOURCE_FACTS and STATIC_CHECKS remain historical, unchanged inputs.
|
||||
This repair adds a fail-closed normal-C qualification boundary and records the
|
||||
receipt content requirements in `NORMAL_C_RECEIPT_FIELDS`. Even a commit with
|
||||
the correct two parents and Q tree is insufficient. The authoritative Root/Runner
|
||||
receipt path, digest and provenance verification contract have not been supplied;
|
||||
`NORMAL_C_RECEIPT_CONTRACT` remains unset. No Runner API or authority is inferred.
|
||||
An index-provided receipt cannot resolve this gap. This blocks every C, including
|
||||
a legitimate C, until that concrete contract and its verifier are supplied.
|
||||
|
||||
The schema2 builder's undefined `tested` reference is fixed by checking the CLI
|
||||
reference against the constructed payload's tested version. This does not make
|
||||
its historical pipeline usable for fresh Q.
|
||||
|
||||
Bulk and role fixtures now allocate under `/tmp`, outside the source repository,
|
||||
with inherited Git routing removed. Role fixtures reject unqualified no-ff merges
|
||||
and synthetic commits with identical parents/tree. The additional candidate
|
||||
fixture constructs such a commit only in a temporary bare clone. These Git
|
||||
fixtures are **Root-run / unexecuted**: the worker's bulk attempt failed on
|
||||
temporary directory creation with `PermissionError: Operation not permitted`,
|
||||
before any Git invocation. They were not redirected into the worktree.
|
||||
|
||||
Remaining implementation, separate from the genuine authority context gap:
|
||||
|
||||
1. Four executable S5 drivers and their raw semantic adapters are still missing.
|
||||
`s5/PENDING.md` remains accurate. All approved destruction/day/reason/exit
|
||||
policies must be enforced alongside the existing safety conditions.
|
||||
2. Fresh-Q run identity/provenance plumbing in native series, collector, S1–S4
|
||||
wrappers and schema2 builder is unfinished. Historical names, pins and source
|
||||
manifests have not been relabeled.
|
||||
3. The separate full184/329 verifier and its exact source/settings/markers/policy/
|
||||
engine/input/ended-receipt joins, including separate test_required_kitchen
|
||||
binding, are unfinished. The native input loop is not a substitute.
|
||||
4. Normal-C positive qualification needs the actual authoritative receipt contract
|
||||
described above; matching Git structure alone is deliberately rejected.
|
||||
5. Genuine-Q positive and fake-Q/full-role fixtures remain unexecuted. The new
|
||||
synthetic-C Git fixture also remains unexecuted; static parsing is not proof
|
||||
that it ran.
|
||||
|
||||
See REPAIR31_STATIC_CHECKS.json for actual worker results. No engine, formal gate,
|
||||
Runner operation, repository Git write, integration or other agent was invoked.
|
||||
Product code, Clock, values, tests, spec, denominator, thresholds and permissions
|
||||
are unchanged. No material was removed for the obsolete byte cap.
|
||||
|
||||
After the missing implementation and context are resolved, Root retains the
|
||||
recorded order: static controls; genuine Q and fresh S1–S5 once; disk/verified CoW
|
||||
then full184/329 once on final Q with final checker; truthful index; verify-only
|
||||
on S; independent whole-unit review; prepare-integration/final on qualified C.
|
||||
Live records stay under `.runtime`. No known-failing gate is a progress check.
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"scope": "new concrete32 static preparation only",
|
||||
"results": [
|
||||
{
|
||||
"path": "normal_c_join.py",
|
||||
"check": "AST and in-memory compile",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"sha256": "3156e1df928ae15ab311b61eb27e21ef27cf123cb7097646a81a3f836c30ba33"
|
||||
},
|
||||
{
|
||||
"path": "normal_c_join_fixtures.py",
|
||||
"check": "AST and in-memory compile",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"sha256": "fba1634aafbb5c6030bec073b75b609a20d10d5ca6a6f0eddc270a12023830a3"
|
||||
},
|
||||
{
|
||||
"path": "scratch_fixture_adapter.py",
|
||||
"check": "AST and in-memory compile",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"sha256": "4994101754c38568007f2d7e1e0c8a2221d26216f464c496a7258ff89c601994"
|
||||
},
|
||||
{
|
||||
"path": "s5/run_s5_native.py",
|
||||
"check": "AST and in-memory compile",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"sha256": "928409e17984330498b2637e33957740bcdd91d427df5ee60d21a86671646fa2"
|
||||
},
|
||||
{
|
||||
"path": "s5/s5_power_adapter.py",
|
||||
"check": "AST and in-memory compile",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"sha256": "6b77e1ac7f1bdfd031321f106d8a6fcf465a3c1dd30e697d2b1e7c7285139f15"
|
||||
},
|
||||
{
|
||||
"path": "s5/p04_s5_power_probe.gd",
|
||||
"check": "text-only forbidden operation and declaration controls",
|
||||
"exit_code": 0,
|
||||
"status": "pass",
|
||||
"godot_parse": "unexecuted",
|
||||
"sha256": "248da3f1dbca644e2fd62fcf283a4e499e0631ff23f8dc44dc77b872ef1be5ba"
|
||||
},
|
||||
{
|
||||
"check": "normal-gate reference supplied index SHA256",
|
||||
"status": "pass",
|
||||
"exit_code": 0,
|
||||
"sha256": "4c6067bbd57034764873a468ff161a6882d232503043d5640fc83dea0b455856"
|
||||
}
|
||||
],
|
||||
"fixtures": "unexecuted",
|
||||
"formal_validation": "unexecuted",
|
||||
"engines_launched": 0,
|
||||
"git_commands": 0,
|
||||
"runner_operations": 0,
|
||||
"whole_goal_accepted": false,
|
||||
"original_credit": 0,
|
||||
"existing_partial31_files_edited": 0
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
# Repair 32 — partial implementation submitted; no acceptance claim
|
||||
|
||||
Only new concrete32 files were added. Existing partial31 files, including the
|
||||
checker and s5/PENDING.md, were not edited. No condition credit is claimed:
|
||||
formal 16/88, original 30/88, P04 credit 0, all 15 conditions plus EV, frozen
|
||||
Mc0 and all six approved D05 items remain unchanged.
|
||||
|
||||
Implemented, unexecuted:
|
||||
- S5 power driver pair and S4-derived wrapper: public bath pure depletion and
|
||||
cafeteria/kitchen reason-priority depletion; public spend/recovery commands;
|
||||
observed completion; same-key replay; full typed/JSON phase points; formal
|
||||
save/independent Reader load/replay. No disconnect or direct resource mutation.
|
||||
- Hashed raw semantic adapter for existing power_stop, dependency_stop and
|
||||
recovery_completion measurement keys. It does not qualify native provenance.
|
||||
- Read-only normal_c_join over actual Runner task/candidate/main_base, approval,
|
||||
validation/review and integration/final records. No future C hash is frozen.
|
||||
Root must retain an actual approved Runner snapshot before integrate deletes
|
||||
approval; later final records come from the current Runner database.
|
||||
- Root-run synthetic normal join controls and explicit-scratch adapter for
|
||||
preserved Git fixtures. No fixtures were executed.
|
||||
- S5_ROOT_RUN.md and NORMAL_C_BOUNDARY32.md explain boundaries and limitations.
|
||||
|
||||
Actual verification: Python AST/compile for five new Python files and textual
|
||||
probe controls passed. No pycache, engine, Godot parse, Git command, Runner
|
||||
operation, fixture, formal validation, independent review or integration ran.
|
||||
The shell rejected a here-document temporary-file allocation; subsequent commands
|
||||
used python -c. An attempted documentation addition at the repository root was
|
||||
rejected and no file was created there; the document was added within allowed_paths.
|
||||
|
||||
Remaining implementation limitations:
|
||||
1. The old checker still deliberately rejects normal C: preserving partial31
|
||||
means this standalone Runner-record join is not connected to its role output.
|
||||
It does not assert Git structure/Q equivalence and cannot bypass that gate.
|
||||
2. The S5 wrapper retains historical S4 template identity/count bindings. Fresh-Q
|
||||
plumbing is outside this step and remains pending; this is not batch admission.
|
||||
3. S5 destruction/midnight/exit, fresh-Q/full184/329, truthful collection/index,
|
||||
exact-version applicability, independent review and ordinary gates remain
|
||||
pending. None was attempted together or counted as a new unit.
|
||||
4. Scratch adapters preserve the old fixtures' assertions; positive normal-C
|
||||
structure fixtures under the eventual integrated boundary are still pending.
|
||||
|
||||
Runner/Root must run the permitted static fixtures in external scratch, review
|
||||
the added source, and resolve boundary integration before the established
|
||||
once-only genuine-Q/native/full sequence. No evidence or completion is invented.
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"status": "blocked",
|
||||
"checks": [
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "tools/check_p04_order_evidence.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_role_fixtures.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/normal_c_join.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/normal_c_join_fixtures.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/run_s5_native.py",
|
||||
"output": "compiled without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "pure_static_ast",
|
||||
"observation": "normal_c_join.verify parameters",
|
||||
"output": [
|
||||
"database",
|
||||
"approved_database",
|
||||
"candidate",
|
||||
"submission",
|
||||
"hashes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "pure_static_source",
|
||||
"observation": "checker contains runner_inputs",
|
||||
"output": true
|
||||
}
|
||||
],
|
||||
"fixtures": [
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_applicability_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_approval_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_encoder_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_join_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_native_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_negative_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_role_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_series_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/normal_c_join_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s4/engine_identity_negative_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s4/receipt_lifecycle_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
}
|
||||
],
|
||||
"engine_runs": 0,
|
||||
"fixture_runs": 0,
|
||||
"condition_credit": 0,
|
||||
"original_completion": false
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# REPAIR33 — blocked, no implementation or acceptance claim
|
||||
|
||||
P04-01 remains incomplete. Only this status and REPAIR33_STATIC_CHECKS.json
|
||||
were added. Checker, fixtures, wrapper and S5 run documents remain unchanged.
|
||||
No Git writes, Runner operations, fixture execution or engine runs occurred.
|
||||
|
||||
The instruction to return blocked for missing specifications applies to these
|
||||
unresolved input contracts:
|
||||
|
||||
- The Root-supplied Q descriptor has no supplied schema or existing definition
|
||||
in the inspected checker, S5 wrapper or local execution documentation.
|
||||
`verify_roles` requires `product_binding` to contain exactly `tested_product`
|
||||
and `runner_inputs`. Each runner input references an existing native receipt,
|
||||
wrapper and materials; it does not define M/S fields or the wrapper's fresh-Q
|
||||
configuration input. Specify the descriptor fields/types and how M, S, Q,
|
||||
staged product, settings, markers, policy and material bindings are obtained
|
||||
without inventing another input contract. Future hashes are not requested.
|
||||
- The requested combined control includes matching Runner/Root records, but
|
||||
`normal_c_join.verify(database, approved_database, candidate, submission,
|
||||
hashes)` accepts only Runner databases and caller-supplied commit strings.
|
||||
Its CLI has no Root exact-candidate record input or verification. Specify
|
||||
that record's schema and binding, or explicitly confirm that Root performs
|
||||
this join outside this verifier and define the combined control boundary.
|
||||
The verifier was neither weakened nor called from the checker.
|
||||
|
||||
These are contract questions, not requests for permission expansion. Runner
|
||||
core, Owner CLI and the reference core were not read. No dependency on them
|
||||
was introduced. No speculative partial implementation was made.
|
||||
|
||||
Actual verification: six existing Python files compiled with py_compile;
|
||||
generated bytecode was placed under the allowed execution directory and
|
||||
removed. Pure AST inspection confirmed the normal_c_join parameter list;
|
||||
a source assertion confirmed runner_inputs is present. Actual outputs are
|
||||
recorded in REPAIR33_STATIC_CHECKS.json. All twelve discovered fixture files
|
||||
are individually listed there as unexecuted; Root runs them later with its
|
||||
own scratch directory. No test or acceptance result is inferred from syntax.
|
||||
|
||||
An initial shell here-document command failed before Python started with
|
||||
`can't create temp file for here document: operation not permitted`.
|
||||
It produced no verification result. The checks were then run using python3
|
||||
`-c`, without shell temporary-file redirection.
|
||||
|
||||
Explicitly pending: this REPAIR33 implementation; S5 destruction, midnight
|
||||
and exit drivers; fresh-Q plumbing for S1–S4 and the collector; all 184
|
||||
executable suites and the 329 parse verifier; the truthful index; Root's
|
||||
fixed run order, independent review and normal integration/final joins.
|
||||
|
||||
Formal 16/88, original 30/88, seven domains plus EV, all six D05 approvals
|
||||
and P04 credit 0 remain fixed. M remains
|
||||
`c0ea009b28a7649aca6a433a1a24dc0be5e7ab81`. No condition credit, original
|
||||
completion, self-reported acceptance or cap change is claimed.
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"status": "submitted",
|
||||
"checks": [
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "tools/check_p04_order_evidence.py",
|
||||
"output": "tools/check_p04_order_evidence.py: py_compile completed without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py",
|
||||
"output": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py: py_compile completed without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_role_fixtures.py",
|
||||
"output": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_role_fixtures.py: py_compile completed without syntax errors",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "ast",
|
||||
"output": "AST: documentation roster is definition-only; qualifier retains identity require and structural return with provenance false; role candidate initialized, assigned and returned; pure control function has no subprocess/Git calls.",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "py_compile",
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py",
|
||||
"output": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py: py_compile completed without syntax errors (final literal inputs)",
|
||||
"exit_code": 0
|
||||
},
|
||||
{
|
||||
"kind": "ast",
|
||||
"output": "AST: pure candidate controls contain four literal 40-hex inputs.",
|
||||
"exit_code": 0
|
||||
}
|
||||
],
|
||||
"fixtures": [
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_applicability_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_approval_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_candidate_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_encoder_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_join_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_native_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_negative_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_role_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/checker_series_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/normal_c_join_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s4/engine_identity_negative_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s4/receipt_lifecycle_fixtures.py",
|
||||
"execution": "unexecuted; Root only"
|
||||
}
|
||||
],
|
||||
"pure_candidate_controls": "unexecuted; Root may use checker_candidate_fixtures.py --pure-only",
|
||||
"engine_runs": 0,
|
||||
"fixture_runs": 0,
|
||||
"condition_credit": 0,
|
||||
"original_completion": false
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
# REPAIR34 — structural step submitted
|
||||
|
||||
P04-01 remains incomplete. This step changes only the checker structural C
|
||||
qualification and the two local fixture boundaries, plus this status and the
|
||||
static-check record. The checker returns structure_qualified true and
|
||||
provenance_verified_by_checker false with C/S/M/C_tree/Q joins. Prospective
|
||||
roles return candidate_structure null. The provenance roster is documentation
|
||||
only, never consumed. Other parent, tree, checker-blob and binding guards
|
||||
were left unchanged.
|
||||
|
||||
Candidate fixtures now expect honest structural qualification for a manufactured
|
||||
same-tree C. A separate --pure-only path contains three identity negatives and
|
||||
one exact positive with literal 40-hex inputs; it requires no Git or engine.
|
||||
The two role cases expect structural verification and separately record actual
|
||||
downstream --verify-only returncode/stdout/stderr without asserting acceptance.
|
||||
Both boundaries assert stderr has no 'qualification context gap'. Other
|
||||
negative expectations remain unchanged. These fixtures have not been executed;
|
||||
their downstream outcomes are pending Root execution, not inferred here.
|
||||
|
||||
Actual verification is limited to py_compile for the three changed Python
|
||||
files and AST checks recorded with their real output in
|
||||
REPAIR34_STATIC_CHECKS.json. Temporary compilation bytecode was created only
|
||||
inside the allowed execution directory and removed. Every discovered fixture,
|
||||
including the pure candidate controls, is unexecuted. An initial editing
|
||||
command failed with a Python SyntaxError before any file writes; edits were
|
||||
then applied in place. That command was not a validation check.
|
||||
|
||||
Explicitly pending in this same unit:
|
||||
|
||||
- normal_c_join and its fixtures: unchanged and unexecuted here; post-final
|
||||
Root SQL candidate/M/S/approval/validation/review/integration/final joins
|
||||
remain mandatory before original credit.
|
||||
- Wrapper/descriptor: separate pre-run configuration with Root-derived M/S/Q
|
||||
and pinned source/material references, not after-run binding.runner_inputs
|
||||
receipts. Root JSON and exact-candidate qualification remain pending.
|
||||
- S5 destruction, midnight and exit drivers; S1–S4 and collector fresh Q.
|
||||
- All 184 executable suites / 329 parse verification; truthful index.
|
||||
- Root run order, independent review, integration/final and provenance joins.
|
||||
|
||||
scratch_fixture_adapter.py routing is unchanged. No Git commands or writes,
|
||||
engine runs, fixture runs, Runner operations, permissions or cap changes were
|
||||
performed. Runner core and Owner CLI were not read; this step does not depend
|
||||
on them. External provenance remains a required pending context, not a reason
|
||||
to block the authorized structural step.
|
||||
|
||||
All seven domains plus EV, all six D05 approvals, formal 16/88, original 30/88
|
||||
and P04 credit 0 remain fixed. Frozen M is
|
||||
c0ea009b28a7649aca6a433a1a24dc0be5e7ab81. No condition credit, self-reported
|
||||
acceptance, original completion or new counted unit is claimed.
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
{
|
||||
"validation": "py_compile and ast only",
|
||||
"results": [
|
||||
{
|
||||
"file": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/run_s5_native.py",
|
||||
"py_compile": "passed",
|
||||
"ast.parse": "passed",
|
||||
"ast.required_functions": "passed",
|
||||
"ast.no_fixed_counts": "passed"
|
||||
},
|
||||
{
|
||||
"file": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_wrapper_fixtures.py",
|
||||
"py_compile": "passed",
|
||||
"ast.parse": "passed"
|
||||
}
|
||||
],
|
||||
"fixtures": [
|
||||
{
|
||||
"name": "partial-triple",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "non-hex",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "short",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "mixed-modes",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "historical-native",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "no-flags",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "fresh-q",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "historical-check-only",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "git-dirty-file",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "git-untracked-file",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "git-wrong-head-tree",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "git-wrong-submission",
|
||||
"status": "unexecuted"
|
||||
},
|
||||
{
|
||||
"name": "git-wrong-main",
|
||||
"status": "unexecuted"
|
||||
}
|
||||
],
|
||||
"accepted": false
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
# REPAIR35 — submitted preparation, no acceptance credit
|
||||
|
||||
Scope is the S5 wrapper fresh-Q mode, pure mode-selection fixtures and status
|
||||
documents only. The wrapper now binds M/S/Q_tree, the clean checkout carrier,
|
||||
selection authority, selected source bytes and Q marker contracts. Fixed-input
|
||||
rechecks require identical provenance. Cache comparisons remove only checker
|
||||
and TEST from both maps and otherwise compare keys and hashes. R17 is explicitly
|
||||
check-only/read-only. Fresh-Q counts are derived; historical material is listed
|
||||
as historical_not_inputs rather than pinned as fresh-Q input.
|
||||
|
||||
R34 structural C, Root normal_c_join, probe, adapter, ownership/PID/cleanup,
|
||||
Writer/Reader, formal mirror and inventory remain unchanged. Product, Clock,
|
||||
values, tests/, Runner, core and Owner CLI were read-only. No Git commands,
|
||||
engines, Runner operations or fixture execution were performed. No /tmp or
|
||||
permission additions were used. A shell heredoc could not be created by the
|
||||
environment; that edit attempt exited before running Python or changing files.
|
||||
Edits were then applied directly in place.
|
||||
|
||||
Only py_compile and AST static validation are recorded in
|
||||
REPAIR35_STATIC_CHECKS.json. Pure fixtures (partial triple, non-hex, short,
|
||||
mixed modes, historical native, no flags, full triple, historical check-only)
|
||||
and all five Root Git negatives are **unexecuted**. No functional pass is claimed.
|
||||
|
||||
Pending: S5 destruction/midnight/exit drivers; fresh Q for S1–S4 and collector;
|
||||
184 suites / 329 parse; truthful index; Root run order; Root exact-candidate
|
||||
qualification. Root next builds Qt and parentless X, stages under its own B,
|
||||
then runs check-only, pure fixtures and Git negatives as described in
|
||||
s5/S5_ROOT_RUN.md. No native/batch admission until the pending prerequisites
|
||||
are ready.
|
||||
|
||||
All 7 domains + EV, formal 16/88, original 30/88, P04 credit 0, all six D05
|
||||
approvals, frozen M c0ea009b28a7649aca6a433a1a24dc0be5e7ab81 and allowed_paths
|
||||
remain fixed. This is an internal step of P04-01, no new counted unit and no
|
||||
completion claim. Unchanged support gates including owned_outer.py were not
|
||||
read or executed; their qualification remains with Root, as recorded in the
|
||||
direction. Source commit identity was not independently checked because Git
|
||||
commands are prohibited for this worker.
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"py_compile_ast": [
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/run_s5_native.py",
|
||||
"sha256": "e96b2793d7352ac52e16505c9c4cde3d151497763be3cb47926338df070b05e5",
|
||||
"py_compile": "passed",
|
||||
"ast": "passed",
|
||||
"top_level_functions": [
|
||||
"r17_relocation_guard",
|
||||
"probe_guard",
|
||||
"module",
|
||||
"pin",
|
||||
"load_uid_helper",
|
||||
"make_uid_policy",
|
||||
"engine_pin",
|
||||
"select_mode",
|
||||
"fresh_q_guard",
|
||||
"main"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_uid_controls.py",
|
||||
"sha256": "17069b87b98c385e1298152c9c5c2f23c8710b79eb19aef073c64ce215f6c902",
|
||||
"py_compile": "passed",
|
||||
"ast": "passed",
|
||||
"top_level_functions": [
|
||||
"module",
|
||||
"main"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "docs/plans/original-completion-2026-10-02/execution/p04-01/s5/s5_wrapper_fixtures.py",
|
||||
"sha256": "1a4daebf46051e975a9cc740660c4672371ea6a9e6cb04cbc1d458ece5f0151d",
|
||||
"py_compile": "passed",
|
||||
"ast": "passed",
|
||||
"top_level_functions": [
|
||||
"main"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# REPAIR36 — bounded preparation submitted, no acceptance credit
|
||||
|
||||
Changes are limited to the S5 wrapper, new pure UID controls and these documents.
|
||||
Actual-Q AST helper loading, finite derived UID planning, import sealing,
|
||||
private-globals phase instrumentation, phase receipts and cleanup rechecks
|
||||
supplement the existing expected inventory. Historical selection, existing
|
||||
require gates, ownership/PID, engine, cache and formal mirror rules remain.
|
||||
The helper is loaded from real Q before controls construct synthetic refs.
|
||||
|
||||
Only py_compile and AST checks are executed and recorded in
|
||||
REPAIR36_STATIC_CHECKS.json. Wrapper fixtures, UID controls, check-only plans,
|
||||
Git negatives and every native diagnostic are unexecuted. No functional pass,
|
||||
full regression, 15+EV batch, original credit or completion is claimed.
|
||||
No Git, engine, Runner or other agent operations were performed. A shell
|
||||
heredoc edit failed before Python execution; edits then used apply_patch.
|
||||
|
||||
Context gap: Q runner API was read from
|
||||
build/owner-handoff/p04-after-p26-closure-r01/q35-uid-diagnostic/run_all_tests.py.reference;
|
||||
Root must confirm the actual Q runner matches that mirror (320ca668…)
|
||||
at runtime. Helper implementation was read from the adjacent P13 reference;
|
||||
runtime loading uses only the actual pinned Q, never either mirror.
|
||||
|
||||
Pending: S5 destruction/midnight/exit drivers; S1–S4/collector fresh Q;
|
||||
derived 184 suites / 329 parse; truthful index; Root exact-candidate qualification.
|
||||
Root order is py_compile → wrapper fixtures → UID controls with explicit scratch
|
||||
→ fresh check-only plan → exactly one new-name diagnostic. The failed r35
|
||||
diagnostic is preserved. All 7 domains + EV, formal 16/88, original 30/88,
|
||||
P04 credit 0, all six D05 approvals pending, M c0ea009 and allowed_paths stay fixed.
|
||||
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build a fresh evidence index from supplied native bytes after auto-submit.
|
||||
|
||||
The index has no candidate identity or Root-passed flag. Invocations share the
|
||||
same payload; checkout roles are established afresh by the checker.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
|
||||
|
||||
def checker():
|
||||
spec = importlib.util.spec_from_file_location('p04_index_checker', ROOT / 'tools/check_p04_order_evidence.py')
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def descriptor(path):
|
||||
data = path.read_bytes()
|
||||
return {'path': str(path.absolute()), 'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
|
||||
|
||||
def payload(build, gate=None):
|
||||
gate = gate or checker()
|
||||
build = Path(build).resolve(strict=True)
|
||||
native = build / 'audit-support/native-first-current'
|
||||
c = gate.verify_native(native)
|
||||
raw = gate.combine_native_series(build, c)
|
||||
tested = c['production_commit']
|
||||
source = gate.selected_manifest(gate.tree_entries(tested))
|
||||
source_map = {p: v['sha256'] for p, v in source.items()}
|
||||
# Resolve content from supplied roots only; never open original provenance
|
||||
# paths, never omit required source/config/input/environment material.
|
||||
supplied = {}
|
||||
roots = [build / 'audit-support' / name for name in
|
||||
('native-first-current', 'p04-whole-closure-r01', 'p04-current-native-series-r01',
|
||||
'p04-r17-full-inputs-r01')]
|
||||
for root in roots:
|
||||
if not root.exists():
|
||||
continue
|
||||
for path in sorted(root.rglob('*')):
|
||||
if path.is_file():
|
||||
gate.safe(root, path.relative_to(root).as_posix())
|
||||
pin = descriptor(path)
|
||||
supplied.setdefault(pin['sha256'], pin)
|
||||
runners = []
|
||||
for run in gate.NATIVE_RUNS:
|
||||
path = native / run / 'runner-input.json'
|
||||
inputs = gate.load(path)
|
||||
row = descriptor(path)
|
||||
row['materials'] = {}
|
||||
for pin in inputs.get('fixed_input_pins', []) + [inputs['QA']]:
|
||||
gate.require(pin['sha256'] in supplied, 'missing supplied runner material: ' + pin['path'])
|
||||
actual = supplied[pin['sha256']]
|
||||
gate.require(actual['bytes'] == pin['bytes'], 'runner material length')
|
||||
row['materials'][pin['path']] = actual
|
||||
gate.require(inputs['wrapper_sha256'] in supplied, 'missing supplied native wrapper')
|
||||
row['wrapper'] = supplied[inputs['wrapper_sha256']]
|
||||
runners.append(row)
|
||||
# C references retain every lifecycle, raw phase, source, environment and
|
||||
# formal member mapping; Stage R retains its complete original->relocated map.
|
||||
references = {}
|
||||
for row in gate.load(native / 'SOURCE_MANIFEST.json')['files']:
|
||||
references['C/' + row['copy']] = descriptor(gate.safe(native, row['copy']))
|
||||
for row in gate.load(native / 'OWNERSHIP_RAW_PROVENANCE.json')['copied']:
|
||||
references['C/' + row['target']] = descriptor(gate.safe(native, row['target']))
|
||||
series = {}
|
||||
collection = build / 'audit-support/p04-raw-collect-r22'
|
||||
for name in ('s1', 's2a', 's2b', 's3', 's4'):
|
||||
series[name] = gate.load(collection / (name + '.json'))
|
||||
relocation = gate.load(build / 'audit-support/p04-r17-full-inputs-r01/MANIFEST.json')
|
||||
authorities = {}
|
||||
for ref in sorted({r for _, refs, _ in gate.REQUIRED.values() for r in refs}):
|
||||
relative = (gate.OWNER_BINDINGS[ref][0] if ref.startswith('D05-P04-') else
|
||||
'docs/traceability/original/BASELINE.txt' if ref.startswith('BASELINE:') else
|
||||
'docs/DESIGN_DECISIONS.md' if ref == 'D-034' else
|
||||
'docs/plans/original-completion-2026-10-02/execution/p04-01-input-r01/ORDER_CONTRACT_DRAFT.md')
|
||||
path = gate.safe(gate.ROOT, relative)
|
||||
if ref.startswith('D05-P04-'):
|
||||
gate.owner_authority(ref, {'source': relative, 'status': 'approved'}, path)
|
||||
authorities[ref] = {'source': relative, 'sha256': gate.sha(path),
|
||||
'status': 'approved' if not ref.startswith('BASELINE:') else 'original'}
|
||||
conditions = {cid: {'case_id': cid, 'authority_refs': list(refs), 'nativeWindows': 'pending',
|
||||
'raw_verified': cid in raw['verified_conditions'],
|
||||
'required_measurements': metrics}
|
||||
for cid, (_, refs, metrics) in gate.REQUIRED.items()}
|
||||
domains = {domain: {'conditions': [cid for cid, (d, _, _) in gate.REQUIRED.items() if d == domain],
|
||||
'source_refs': [{'path': p, 'sha256': source_map[p], 'functions': list(fs)}
|
||||
for p, fs in gate.ORDER_FUNCTIONS.items()],
|
||||
'authority_refs': sorted({r for _, (d, refs, _) in gate.REQUIRED.items()
|
||||
if d == domain for r in refs})}
|
||||
for domain in sorted(gate.DOMAINS)}
|
||||
reachability = {}
|
||||
for name, rows in gate.REACH_REFS.items():
|
||||
refs = []
|
||||
for path, function, digest, lines in rows:
|
||||
gate.resolve_anchor(gate.git_bytes('show', tested + ':' + path), function, digest, lines)
|
||||
refs.append({'path': path, 'sha256': source_map[path], 'function': function,
|
||||
'anchor_sha256': digest, 'line_count': lines})
|
||||
reachability[name] = {'reach': name == 'EV', 'case_id': 'EV' if name == 'EV' else None, 'source_refs': refs,
|
||||
'raw_verified': 'EV' in raw['verified_conditions'] if name == 'EV' else None}
|
||||
delta = gate.source_delta(gate.REGISTERED_COMMIT, tested)
|
||||
index = {'schema_version': 2, 'task_id': 'P04-01', 'formal_acceptance': False,
|
||||
'versions': {'registered': gate.REGISTERED_COMMIT, 'execution_origin': gate.EXECUTION_ORIGIN,
|
||||
'tested': tested},
|
||||
'product_binding': {'tested_product': tested, 'runner_inputs': runners},
|
||||
'source_map': source_map, 'registered_pins': gate.load(gate.SPEC)['current_source_pins'],
|
||||
'applicability_delta': delta,
|
||||
'delta_cases': {r['path'] + ':' + r['function']: {cid: cid for cid in
|
||||
gate.boundary_conditions(r['path'], r['function'])} for r in delta},
|
||||
'domains': domains, 'authority': authorities, 'conditions': conditions,
|
||||
'reachability': reachability, 'raw_conditions': raw, 'raw_references': references,
|
||||
'series': series, 'source_config_input_environment_and_formal': relocation,
|
||||
'native_bundle': descriptor(native / 'OWNERSHIP_RAW_PROVENANCE.json'),
|
||||
'original_audit': {'sha256': gate.sha(gate.ROOT / gate.DOC_SCOPE / 'order-audit.json'),
|
||||
'content': gate.load(gate.ROOT / gate.DOC_SCOPE / 'order-audit.json')},
|
||||
'review_status': 'pending_review', 'original_credit': '16/88; P04=0',
|
||||
'safety_outcomes': ['no charging while legitimately stopped',
|
||||
'no double midnight settlement', 'no orphan reservations']}
|
||||
gate.applicability(index, gate.load(gate.SPEC), source_map, tested)
|
||||
return index
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--build', required=True, type=Path)
|
||||
parser.add_argument('--output', required=True, type=Path, help='fresh directory; exclusive creation')
|
||||
parser.add_argument('--invocation', required=True, choices=('validate', 'prepare-integration', 'final'))
|
||||
parser.add_argument('--registered-ref', required=True)
|
||||
parser.add_argument('--tested-ref', required=True)
|
||||
args = parser.parse_args()
|
||||
gate = checker()
|
||||
os.environ['BUILD_OUTPUT_DIR'] = str(args.build.resolve(strict=True))
|
||||
output = args.output.absolute()
|
||||
gate.require(not output.exists() and not output.is_symlink(), 'output must be fresh')
|
||||
for path in output.parents:
|
||||
gate.require(not path.is_symlink(), 'output parent symlink')
|
||||
index = payload(args.build, gate)
|
||||
gate.require(args.registered_ref == gate.REGISTERED_COMMIT and
|
||||
args.tested_ref == index['versions']['tested'],
|
||||
'source refs differ from immutable evidence roles')
|
||||
with gate.isolated_objects():
|
||||
_, role = gate.verify_roles(index, args.build.resolve())
|
||||
# The historical invocation is separate from the reusable evidence index.
|
||||
receipt = {'invocation': args.invocation, 'actual_head': role['actual_head'],
|
||||
'actual_tree': role['actual_tree'], 'role': role['role'], 'formal_acceptance': False}
|
||||
output.mkdir(parents=False)
|
||||
for name, value in (('index.json', index), ('generation-invocation.json', receipt)):
|
||||
with (output / name).open('x', encoding='utf-8') as stream:
|
||||
json.dump(value, stream, ensure_ascii=False, sort_keys=True, indent=2)
|
||||
stream.write('\n')
|
||||
print(json.dumps({'evidence_directory': str(output), **receipt}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Isolated disposable repository controls; never original acceptance evidence."""
|
||||
import importlib.util
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
|
||||
|
||||
def rejected(operation):
|
||||
try:
|
||||
operation()
|
||||
except (ValueError, subprocess.CalledProcessError):
|
||||
return
|
||||
raise AssertionError('invalid input/response accepted')
|
||||
|
||||
|
||||
def run():
|
||||
spec = importlib.util.spec_from_file_location('bulk_gate', ROOT / 'tools/check_p04_order_evidence.py')
|
||||
gate = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gate)
|
||||
with tempfile.TemporaryDirectory(prefix='p04-bulk-fixture-', dir='/tmp') as temporary:
|
||||
base = Path(temporary)
|
||||
assert not base.resolve().is_relative_to(ROOT.resolve())
|
||||
repo = base / 'repo'
|
||||
repo.mkdir()
|
||||
gate.ROOT = repo
|
||||
env = gate.git_environment()
|
||||
env.update(GIT_AUTHOR_NAME='fixture', GIT_AUTHOR_EMAIL='fixture@invalid',
|
||||
GIT_COMMITTER_NAME='fixture', GIT_COMMITTER_EMAIL='fixture@invalid')
|
||||
|
||||
def git(*args, data=None):
|
||||
return subprocess.check_output(['git', '-c', 'core.hooksPath=/dev/null', *args],
|
||||
input=data, cwd=repo, env=env)
|
||||
|
||||
git('init', '--quiet')
|
||||
payloads = [b'\x00binary\r\nline\n', b'', bytes(range(256)) * 32768]
|
||||
oids = [git('hash-object', '-w', '--stdin', data=data).decode().strip() for data in payloads]
|
||||
name = '日本語の木.dat'
|
||||
(repo / name).write_bytes(payloads[0])
|
||||
git('add', '--', name)
|
||||
git('commit', '--quiet', '-m', 'fixture')
|
||||
entries = gate.tree_entries('HEAD') # ls-tree -z resolves the unicode path.
|
||||
assert entries[name][1] == oids[0]
|
||||
expected = {oid: git('cat-file', 'blob', oid) for oid in oids}
|
||||
original_output = subprocess.check_output
|
||||
with patch.object(gate.subprocess, 'check_output', wraps=original_output) as process:
|
||||
assert gate.read_blobs(reversed(oids + [oids[0]])) == expected
|
||||
assert process.call_count == 1
|
||||
assert process.call_args.kwargs['input'] == ('\n'.join(sorted(set(oids))) + '\n').encode()
|
||||
with patch.object(gate.subprocess, 'check_output', side_effect=AssertionError('process started')):
|
||||
assert gate.read_blobs([]) == {}
|
||||
for invalid in ('abc', 'g' * 40, 'a' * 39, 'a' * 41, 'HEAD', oids[0] + '\n', None):
|
||||
rejected(lambda: gate.read_blobs([invalid]))
|
||||
for oid in ('0' * 40, git('rev-parse', 'HEAD').decode().strip(),
|
||||
git('rev-parse', 'HEAD^{tree}').decode().strip()):
|
||||
rejected(lambda: gate.read_blobs([oid]))
|
||||
|
||||
oid = oids[0]
|
||||
header = oid.encode() + b' blob '
|
||||
valid = header + b'3\na\x00\n\n'
|
||||
assert gate.parse_blob_batch(valid, [oid]) == {oid: b'a\x00\n'}
|
||||
bad = [b'', header + b'3', header + b'3\na', valid[:-1],
|
||||
header + b'999\na\n', header + b'1\nabc\n', valid + b'garbage',
|
||||
oid.encode() + b' missing\n', oid.encode() + b' ambiguous\n',
|
||||
header + b'-1\n', header + b'x\n', header + b'1.0\n',
|
||||
b'f' * 40 + b' blob 0\n\n', oid.encode() + b' tree 0\n\n']
|
||||
for response in bad:
|
||||
rejected(lambda: gate.parse_blob_batch(response, [oid]))
|
||||
with patch.object(gate.subprocess, 'check_output', return_value=response):
|
||||
rejected(lambda: gate.read_blobs([oid]))
|
||||
rejected(lambda: gate.parse_blob_batch(valid + valid, [oid]))
|
||||
rejected(lambda: gate.parse_blob_batch(valid, [oid, oids[1]]))
|
||||
with patch.object(gate.subprocess, 'check_output',
|
||||
side_effect=subprocess.CalledProcessError(7, ['git'])):
|
||||
rejected(lambda: gate.read_blobs([oid]))
|
||||
|
||||
with patch.dict(os.environ, BUILD_OUTPUT_DIR=str(base)):
|
||||
with gate.isolated_objects():
|
||||
private_data = b'isolated-only unique payload\x00\n'
|
||||
private = subprocess.check_output(
|
||||
['git', 'hash-object', '-w', '--stdin'], input=private_data,
|
||||
cwd=repo, env=gate._GIT_ENV).decode().strip()
|
||||
assert gate.read_blobs([private]) == {private: private_data}
|
||||
rejected(lambda: gate.read_blobs([private]))
|
||||
with gate.isolated_objects():
|
||||
rejected(lambda: gate.read_blobs([private]))
|
||||
assert gate.read_blobs(oids) == expected
|
||||
assert gate._GIT_ENV is None
|
||||
print('bulk reader equivalence controls completed')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
run()
|
||||
+1
-1
@@ -14,7 +14,7 @@ ROOT = Path(__file__).resolve().parents[5]
|
||||
spec = importlib.util.spec_from_file_location('p04_gate', ROOT / 'tools/check_p04_order_evidence.py')
|
||||
gate = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gate)
|
||||
PRODUCTION = '5f6bc11d6bb6e92d22d787989b219fef3a435bc1'
|
||||
PRODUCTION = gate.MAIN_ANCHOR
|
||||
|
||||
|
||||
def run(native_bundle=None):
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read-only authority controls; no Git writes, engine or acceptance execution."""
|
||||
import copy
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
spec = importlib.util.spec_from_file_location('p04_authority', ROOT / 'tools/check_p04_order_evidence.py')
|
||||
gate = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gate)
|
||||
|
||||
|
||||
def run():
|
||||
results = []
|
||||
original = gate.load(ROOT / gate.DOC_SCOPE / 'D05_REMAINING_APPROVAL.json')
|
||||
source = gate.git_bytes('show', 'c0ea009b28a7649aca6a433a1a24dc0be5e7ab81:' +
|
||||
'docs/plans/original-completion-2026-10-02/execution/' +
|
||||
'p04-01-owner-approval-20261018/D05_ACTUAL_HUMAN_APPROVAL.json')
|
||||
import hashlib
|
||||
assert all(row['sha256'] == hashlib.sha256(source).hexdigest()
|
||||
for row in original['source_bindings'])
|
||||
human = {row['id']: row for row in json.loads(source)['approved']}
|
||||
for choice in original['approved_choices']:
|
||||
assert all(choice[k] == human[choice['source_id']][k]
|
||||
for k in ('answer_exact', 'approved_question_policy'))
|
||||
for ref, (relative, digest) in gate.OWNER_BINDINGS.items():
|
||||
entry = {'source': relative, 'status': 'approved'}
|
||||
path = ROOT / relative
|
||||
gate.owner_authority(ref, entry, path)
|
||||
results.append({'id': ref, 'case': 'positive', 'result': 'accepted'})
|
||||
for label in ('missing', 'wrong_hash', 'pending', 'wrong_path'):
|
||||
changed = copy.deepcopy(entry)
|
||||
if label == 'missing':
|
||||
changed = {}
|
||||
elif label == 'pending':
|
||||
changed['status'] = 'pending'
|
||||
elif label == 'wrong_path':
|
||||
changed['source'] = gate.DOC_SCOPE + 'D05_REMAINING_ORDER_CANDIDATE.json'
|
||||
try:
|
||||
if label == 'wrong_hash':
|
||||
with patch.object(gate, 'sha', return_value='0' * 64):
|
||||
gate.owner_authority(ref, changed, path)
|
||||
else:
|
||||
gate.owner_authority(ref, changed, path)
|
||||
except ValueError as exc:
|
||||
results.append({'id': ref, 'case': label, 'result': 'rejected', 'error': str(exc)})
|
||||
else:
|
||||
raise AssertionError('negative accepted: ' + ref + ':' + label)
|
||||
return {'scope': 'authority fixtures only; no native or acceptance proof', 'results': results}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(json.dumps(run(), ensure_ascii=False, indent=2))
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Candidate structure controls; no provenance or whole-role/native credit."""
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
|
||||
|
||||
def load_gate():
|
||||
spec = importlib.util.spec_from_file_location('candidate_gate', ROOT / 'tools/check_p04_order_evidence.py')
|
||||
gate = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(gate)
|
||||
return gate
|
||||
|
||||
|
||||
def expected_candidate(commit, tree, main, submitted, composed):
|
||||
return {'structure_qualified': True, 'provenance_verified_by_checker': False,
|
||||
'provenance_owner': 'actual Runner.prepare_integration/integrate and Root normal_c_join AFTER final before original credit',
|
||||
'join': {'C': commit, 'S': submitted, 'M': main, 'C_tree': tree, 'Q': composed}}
|
||||
|
||||
|
||||
def run_pure():
|
||||
"""Three identity negatives and one exact positive; no Git or engine."""
|
||||
gate = load_gate()
|
||||
commit = '1111111111111111111111111111111111111111'
|
||||
tree = '2222222222222222222222222222222222222222'
|
||||
submitted = '3333333333333333333333333333333333333333'
|
||||
other = '4444444444444444444444444444444444444444'
|
||||
for args in ((commit, other, gate.MAIN_ANCHOR, submitted, tree),
|
||||
(commit, tree, other, submitted, tree),
|
||||
(submitted, tree, gate.MAIN_ANCHOR, submitted, tree)):
|
||||
try:
|
||||
gate.qualify_normal_candidate(*args)
|
||||
except ValueError as exc:
|
||||
assert str(exc) == 'normal candidate identity mismatch', str(exc)
|
||||
else:
|
||||
raise AssertionError('invalid candidate structure qualified')
|
||||
args = (commit, tree, gate.MAIN_ANCHOR, submitted, tree)
|
||||
assert gate.qualify_normal_candidate(*args) == expected_candidate(*args)
|
||||
return {'scope': 'pure structure only', 'negative_controls': 3,
|
||||
'exact_positive_controls': 1, 'formal_acceptance': False}
|
||||
|
||||
|
||||
def run():
|
||||
gate = load_gate()
|
||||
env = gate.git_environment()
|
||||
env.update(GIT_AUTHOR_NAME='P04 isolated fixture', GIT_AUTHOR_EMAIL='fixture@invalid',
|
||||
GIT_COMMITTER_NAME='P04 isolated fixture', GIT_COMMITTER_EMAIL='fixture@invalid')
|
||||
with tempfile.TemporaryDirectory(prefix='p04-candidate-fixture-', dir='/tmp') as temp:
|
||||
repo = Path(temp) / 'objects.git'
|
||||
assert not repo.resolve().is_relative_to(ROOT.resolve())
|
||||
subprocess.run(['git', '-c', 'core.hooksPath=/dev/null', 'clone', '--bare', '--shared',
|
||||
str(ROOT), str(repo)], env=env, check=True, capture_output=True)
|
||||
|
||||
def git(*args):
|
||||
return subprocess.check_output(['git', '-c', 'core.hooksPath=/dev/null', *args],
|
||||
cwd=repo, env=env, text=True).strip()
|
||||
|
||||
submission = git('rev-parse', 'HEAD')
|
||||
main = gate.MAIN_ANCHOR
|
||||
tree = git('merge-tree', '--write-tree', main, submission)
|
||||
synthetic = git('commit-tree', tree, '-p', main, '-p', submission,
|
||||
'-m', 'synthetic C; no Root qualification')
|
||||
assert git('show', '-s', '--format=%P', synthetic).split() == [main, submission]
|
||||
assert git('rev-parse', synthetic + '^{tree}') == tree
|
||||
result = gate.qualify_normal_candidate(synthetic, tree, main, submission, tree)
|
||||
assert result == expected_candidate(synthetic, tree, main, submission, tree)
|
||||
return {'scope': 'isolated candidate structure only; provenance remains external',
|
||||
'synthetic_equal_parents_and_tree': result, 'formal_acceptance': False}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--pure-only', action='store_true', help='no Git, engine or scratch directory')
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(run_pure() if args.pure_only else run(), indent=2))
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Optional getter projection controls; these are never native proof."""
|
||||
import json
|
||||
import struct
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
import p04_native_series as s
|
||||
|
||||
|
||||
def string(value, kind=4):
|
||||
data = value.encode()
|
||||
return struct.pack('<II', kind, len(data)) + data + b'\0' * ((-len(data)) % 4)
|
||||
|
||||
|
||||
def integer(value):
|
||||
return struct.pack('<Ii', 2, value)
|
||||
|
||||
|
||||
def dictionary(rows):
|
||||
return struct.pack('<II', 27, len(rows)) + b''.join(k + v for k, v in rows)
|
||||
|
||||
|
||||
def array(values, typed=None):
|
||||
return (struct.pack('<I', 28 if typed is None else 28 | (1 << 16)) +
|
||||
(b'' if typed is None else struct.pack('<I', typed)) +
|
||||
struct.pack('<I', len(values)) + b''.join(values))
|
||||
|
||||
|
||||
def run():
|
||||
results = []
|
||||
canonical = dictionary([(string('a'), array([string('item')])), (string('n'), integer(7))])
|
||||
getter = dictionary([(string('n', 21), integer(7)),
|
||||
(string('a', 21), array([string('item', 21)], typed=21))])
|
||||
assert s.persisted_day_projection(s.variant(getter)) == canonical
|
||||
results.append({'case': 'typed_array_headers_and_StringName_only', 'result': 'accepted'})
|
||||
|
||||
def reject(name, action, error=None):
|
||||
try:
|
||||
action()
|
||||
except ValueError as exc:
|
||||
assert error is None or error in str(exc), (name, str(exc))
|
||||
results.append({'case': name, 'result': 'rejected', 'error': str(exc)})
|
||||
else:
|
||||
raise AssertionError('negative accepted: ' + name)
|
||||
|
||||
def compare(data):
|
||||
s.require(s.persisted_day_projection(s.variant(data)) == canonical, 'persisted value mismatch')
|
||||
|
||||
reject('dropped_field', lambda: compare(dictionary([(string('n'), integer(7))])))
|
||||
reject('extra_field', lambda: compare(dictionary([(string('a'), array([string('item')])),
|
||||
(string('n'), integer(7)), (string('x'), integer(0))])))
|
||||
reject('int_to_float', lambda: compare(dictionary([(string('a'), array([string('item')])),
|
||||
(string('n'), struct.pack('<If', 3, 7.0))])))
|
||||
floating = dictionary([(string('n'), struct.pack('<If', 3, 7.0))])
|
||||
reject('float_to_int', lambda: s.require(s.persisted_day_projection(s.variant(floating)) ==
|
||||
dictionary([(string('n'), integer(7))]), 'numeric type changed'))
|
||||
reject('value_change', lambda: compare(dictionary([(string('a'), array([string('item')])),
|
||||
(string('n'), integer(8))])))
|
||||
reject('nonfinite_float', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(string('n'), struct.pack('<If', 3, float('inf')))]))), 'NON_FINITE_NUMBER')
|
||||
reject('invalid_key_type', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(struct.pack('<II', 1, 1), integer(1))]))), 'INVALID_DICTIONARY_KEY')
|
||||
reject('key_collision', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(integer(1), integer(1)), (string('1'), integer(2))]))), 'DUPLICATE_CANONICAL_KEY')
|
||||
reject('unsaveable_variant', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(string('x'), struct.pack('<Iff', 5, 1.0, 2.0))]))), 'UNSAVEABLE_VARIANT')
|
||||
reject('wrong_to_snapshot_variant_chain', lambda: s.persisted_day_projection(s.variant(getter),
|
||||
chain='_to_snapshot_variant'), 'projection call chain')
|
||||
reject('projection_on_native_persisted_comparison', lambda: s.persisted_day_projection(s.variant(getter),
|
||||
comparison='native_expected_restored'), 'projection forbidden')
|
||||
reject('single_string_budget', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(string('x'), string('x' * 4097))]))), 'STRING_TOO_LONG')
|
||||
reject('collection_budget', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(string('x'), array([integer(0)] * 20001))]))), 'COLLECTION_TOO_LARGE')
|
||||
nested = integer(1)
|
||||
for _ in range(33):
|
||||
nested = array([nested])
|
||||
reject('depth_budget', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(string('x'), nested)]))), 'MAX_VARIANT_DEPTH')
|
||||
# The collision prepass must win over invalid-key and child errors.
|
||||
reject('collision_error_precedence', lambda: s.persisted_day_projection(s.variant(dictionary([
|
||||
(struct.pack('<II', 1, 1), struct.pack('<If', 3, float('inf'))),
|
||||
(string('true'), integer(0))]))), 'DUPLICATE_CANONICAL_KEY')
|
||||
return {'scope': 'synthetic projection controls only', 'results': results}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(json.dumps(run(), indent=2))
|
||||
@@ -0,0 +1,155 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Real B1/R2 positive controls and semantic mutation negatives for Root."""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
import p04_native_series as series
|
||||
from build_p04_index import checker
|
||||
|
||||
|
||||
def run(build):
|
||||
gate = checker()
|
||||
base = build / 'audit-support/native-first-current'
|
||||
results = []
|
||||
|
||||
def reject(name, action):
|
||||
try:
|
||||
action()
|
||||
except (ValueError, KeyError, TypeError) as exc:
|
||||
results.append({'case': name, 'result': 'rejected', 'error': str(exc)})
|
||||
else:
|
||||
raise AssertionError('negative accepted: ' + name)
|
||||
|
||||
context = series.Series(build, 's3')
|
||||
series.dependency_closure_diagnostic(context)
|
||||
reject('B1_cannot_supply_power_stop', lambda: series.power_stop(context))
|
||||
results.append({'case': 'B1_real_despawn_positive', 'result': 'accepted'})
|
||||
original_value = context.value
|
||||
for name, label, mutate in (
|
||||
('dependency_reason_relabelled_as_NO_POWER', 'after', lambda r: r['service'].update(interruption_reason='NO_POWER')),
|
||||
('B1_missing_at_closure', 'after', lambda r: r['guests'].update({'p04.s3.guest.b': {}})),
|
||||
('B1_synthetic_later_guest', 'next', lambda r: r['guests'].update({'p04.s3.guest.b': {'patience_remaining': 45}})),
|
||||
('B1_wrong_release_minute', 'next', lambda r: r['reservations'][1].update(terminal_at=961)),
|
||||
('B1_changed_wallet', 'recovered', lambda r: r['wallets'].update({'p04.s3.guest.b': 25})),
|
||||
('B1_missing_departure_history', 'next', lambda r: r['snapshot']['systems']['runtime']['guests'].update(persistent_records=[])),
|
||||
):
|
||||
def altered(key, label=label, mutate=mutate):
|
||||
value = copy.deepcopy(original_value(key))
|
||||
if key == 'writer-B1-' + label:
|
||||
mutate(value)
|
||||
return value
|
||||
with patch.object(context, 'value', altered):
|
||||
reject(name, lambda: series.dependency_closure_diagnostic(context))
|
||||
gate.verify_shift_completion(base)
|
||||
results.append({'case': 'R2_real_first_1080_positive', 'result': 'accepted'})
|
||||
original_load = gate.load
|
||||
observation_path = base / 'actual-native/order-evidence/observations.json'
|
||||
for name, mutate in (
|
||||
('R2_paid_midnight_substitution', lambda o: o['observations'][2]['service'].update(completed_at=1442)),
|
||||
('R2_wrong_transaction', lambda o: o['observations'][2]['receipts'][0].update(id='ledger_00000099')),
|
||||
('R2_missing_DAY2', lambda o: o['observations'][2].update(active_staff=[])),
|
||||
('R2_after_staff_still_present', lambda o: o['observations'][3].update(active_staff=['p04.order.server.a'])),
|
||||
('R2_next_extra_receipt', lambda o: o['observations'][5]['receipts'].append(copy.deepcopy(o['first_receipts'][0]))),
|
||||
):
|
||||
def altered(path, mutate=mutate):
|
||||
value = original_load(path)
|
||||
if path == observation_path:
|
||||
mutate(value)
|
||||
return value
|
||||
with patch.object(gate, 'load', altered):
|
||||
reject(name, lambda: gate.verify_shift_completion(base))
|
||||
# Dynamic IDs are legitimate when every raw join agrees. These are semantic
|
||||
# overlays of already verified raw, not replacement native evidence.
|
||||
shift = gate.verify_shift_completion(base)
|
||||
old_pids = gate.native_ownership(base)
|
||||
replacements = {pid: pid + 1000000 for pid in old_pids.values()}
|
||||
replacements[shift['transaction_id']] = 'dynamic-sale-repair22'
|
||||
|
||||
def remap(value):
|
||||
if isinstance(value, dict): return {k: remap(v) for k, v in value.items()}
|
||||
if isinstance(value, list): return [remap(v) for v in value]
|
||||
if isinstance(value, (str, int)) and not isinstance(value, bool):
|
||||
return replacements.get(value, value)
|
||||
return value
|
||||
|
||||
with patch.object(gate, 'load', lambda path: remap(original_load(path))):
|
||||
dynamic = gate.verify_shift_completion(base)
|
||||
assert dynamic['transaction_id'] == 'dynamic-sale-repair22'
|
||||
results.append({'case': 'R2_dynamic_PID_and_transaction_positive', 'result': 'accepted'})
|
||||
wrong_end = base / 'actual-native' / gate.NATIVE_RUNS['actual-native'][0] / 'ended.json'
|
||||
|
||||
def wrong_pid(path):
|
||||
value = original_load(path)
|
||||
if path == wrong_end: value['actual_pid'] += 1
|
||||
return value
|
||||
|
||||
with patch.object(gate, 'load', wrong_pid):
|
||||
reject('R2_wrong_PID_join', lambda: gate.verify_shift_completion(base))
|
||||
|
||||
s1 = series.Series(build, 's1')
|
||||
series.s1_semantics(s1)
|
||||
s1_value = s1.value
|
||||
for name, mutate in (
|
||||
('S1_pre_travel_mismatch', lambda r: r['reserved_before_travel'].update(staff_cell=(6, 0, 1))),
|
||||
('S1_no_actual_outage', lambda r: r['reserve_exhausted_before_tick']['snapshot']['game_state']['resources'].update(mana=1)),
|
||||
):
|
||||
def altered(key, mutate=mutate):
|
||||
value = copy.deepcopy(s1_value(key))
|
||||
if key == 'writer-landing-observations': mutate(value)
|
||||
return value
|
||||
with patch.object(s1, 'value', altered):
|
||||
reject(name, lambda: series.s1_semantics(s1))
|
||||
|
||||
# S4 is deliberately unavailable until the single Root collection exists.
|
||||
# Never substitute B1 or manufacture a positive raw fixture for it.
|
||||
s4 = series.S4Series(build)
|
||||
series.power_stop(s4)
|
||||
results.append({'case': 'S4_real_NO_POWER_positive', 'result': 'accepted'})
|
||||
s4_value = s4.value
|
||||
|
||||
def facility_change(row, field, value, kitchen=False):
|
||||
target = row['kitchen' if kitchen else 'facility']
|
||||
target[field] = value
|
||||
for f in series.runtime(row)['facilities']['instances']:
|
||||
if f['instance_id'] == target['instance_id']: f[field] = value
|
||||
|
||||
for name, mutate in (
|
||||
('S4_related_facility_operating_false', lambda r: r.update(related_facility_operating=False)),
|
||||
('S4_kitchen_unpowered', lambda r: facility_change(r, 'power_available', False, kitchen=True)),
|
||||
('S4_closure_confound', lambda r: facility_change(r, 'operator_open', False)),
|
||||
('S4_no_actual_outage', lambda r: facility_change(r, 'power_available', True)),
|
||||
('S4_wrong_transaction_join', lambda r: series.runtime(r)['economy']['idempotency'].clear()),
|
||||
):
|
||||
def altered(key, mutate=mutate, name=name):
|
||||
value = copy.deepcopy(s4_value(key))
|
||||
label = 'recovered' if name == 'S4_wrong_transaction_join' else 'boundary'
|
||||
if key == 'writer-S4-' + label: mutate(value)
|
||||
return value
|
||||
with patch.object(s4, 'value', altered):
|
||||
reject(name, lambda: series.power_stop(s4))
|
||||
# Rename every occurrence, including service.payment.<serviceID> keys.
|
||||
original = s4_value('writer-S4-before')['service']
|
||||
paid = series.sales(s4_value('writer-S4-recovered'))[0]
|
||||
id_map = {original['service_id']: 'dynamic-service-22', paid['transaction_id']: 'dynamic-sale-22',
|
||||
'service.payment.' + original['service_id']: 'service.payment.dynamic-service-22'}
|
||||
|
||||
def dynamic_ids(value):
|
||||
if isinstance(value, dict): return {id_map.get(k, k): dynamic_ids(v) for k, v in value.items()}
|
||||
if isinstance(value, list): return [dynamic_ids(v) for v in value]
|
||||
return id_map.get(value, value) if isinstance(value, str) else value
|
||||
|
||||
with patch.object(s4, 'value', lambda key: dynamic_ids(s4_value(key))):
|
||||
assert series.power_stop(s4)['transaction_id'] == 'dynamic-sale-22'
|
||||
results.append({'case': 'S4_dynamic_service_and_sale_positive', 'result': 'accepted'})
|
||||
return {'scope': 'semantic controls only; no new native execution', 'results': results}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--build', type=Path, required=True)
|
||||
print(json.dumps(run(parser.parse_args().build), indent=2))
|
||||
+18
-7
@@ -9,6 +9,7 @@ from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
CHECKER = ROOT / 'tools/check_p04_order_evidence.py'
|
||||
@@ -47,10 +48,13 @@ def run():
|
||||
add('decisions', (ROOT / 'docs/DESIGN_DECISIONS.md').read_bytes(), 'authority')
|
||||
add('a01', (ROOT / 'docs/plans/original-completion-2026-10-02/execution/p04-01-input-r01/ORDER_CONTRACT_DRAFT.md').read_bytes(), 'authority')
|
||||
add('owner', (ROOT / 'docs/plans/original-completion-2026-10-02/execution/p04-01/D05_OWNER_APPROVAL.json').read_bytes(), 'authority')
|
||||
add('remaining_owner', (ROOT / gate.DOC_SCOPE / 'D05_REMAINING_APPROVAL.json').read_bytes(), 'authority')
|
||||
authority = {ref: dict(status='approved', artifact_id=(
|
||||
'owner' if ref.startswith('D05-P04-') else 'baseline' if ref.startswith('BASELINE:')
|
||||
('owner' if gate.OWNER_BINDINGS[ref][0].endswith('/D05_OWNER_APPROVAL.json') else 'remaining_owner') if ref.startswith('D05-P04-') else 'baseline' if ref.startswith('BASELINE:')
|
||||
else 'decisions' if ref == 'D-034' else 'a01'))
|
||||
for _, refs, _ in gate.REQUIRED.values() for ref in refs}
|
||||
for ref, (relative, digest) in gate.OWNER_BINDINGS.items():
|
||||
authority[ref]['source'] = relative
|
||||
add('stdout', b'SYNTHETIC fixture log', 'stdout')
|
||||
add('native_log', b'SYNTHETIC fixture log', 'native_log')
|
||||
domains = {d: dict(source_refs=[dict(path='autoload/simulation_runtime.gd', function='_advance_facilities',
|
||||
@@ -124,12 +128,19 @@ def run():
|
||||
(evidence / 'index.json').write_text(json.dumps(candidate))
|
||||
if name == 'duplicate_key':
|
||||
(evidence / 'index.json').write_text('{"schema_version":1,"schema_version":1}')
|
||||
proc = subprocess.run(['python3', str(CHECKER), '--verify-only'],
|
||||
env=dict(os.environ, BUILD_OUTPUT_DIR=str(base)), text=True, capture_output=True)
|
||||
output = json.loads(proc.stdout)
|
||||
assert proc.returncode == (0 if error is None else 1), (name, proc.stdout, proc.stderr)
|
||||
# These are content-layer unit cases. Stage-A real Git/FS controls
|
||||
# live in checker_role_fixtures.py; no production bypass is added.
|
||||
head = index['candidate_commit']
|
||||
with patch.object(gate, 'verify_roles', return_value=(head, {})), patch.dict(
|
||||
os.environ, {'BUILD_OUTPUT_DIR': str(base), 'P04_EVIDENCE_DIR': str(evidence)}):
|
||||
try:
|
||||
gate.verify(base)
|
||||
code, output = 0, {}
|
||||
except (ValueError, OSError, KeyError, TypeError) as exc:
|
||||
code, output = 1, {'error': str(exc)}
|
||||
assert code == (0 if error is None else 1), (name, output)
|
||||
assert error is None or error in output.get('error',''), (name, output)
|
||||
results.append(dict(fixture=name, exit_code=proc.returncode, result=output))
|
||||
results.append(dict(fixture=name, exit_code=code, result=output))
|
||||
check('synthetic_scalar_only_rejected', lambda i: None, 'native bundle required')
|
||||
check('forged_registered_pin', lambda i: i['registered_pins'].update({'autoload/simulation_runtime.gd':'0'*64}), 'registered pin declaration')
|
||||
check('false_delta', lambda i: i['applicability_delta'].append({'path':'autoload/simulation_runtime.gd','function':'initialize'}), 'git diff/applicability_delta mismatch')
|
||||
@@ -141,7 +152,7 @@ def run():
|
||||
(evidence / 'stdout.json').symlink_to(evidence / 'native_log.json')
|
||||
check('symlink', symlink, 'symlink')
|
||||
check('hash_mismatch', lambda i: i['artifacts']['stdout'].update(sha256='0'*64), 'artifact hash')
|
||||
check('stale_pin', lambda i: i['source_map'].update({'autoload/simulation_runtime.gd':'0'*64}), 'stale pin')
|
||||
check('stale_pin', lambda i: i['source_map'].update({'autoload/simulation_runtime.gd':'0'*64}), 'tested game differs from Git')
|
||||
def receipt_mutation(i):
|
||||
aid = 'power_stop_receipt'
|
||||
value = gate.load(evidence / artifacts[aid]['path'])
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Real temp-Git role controls. Root executes after auto-submit; never evidence."""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[5]
|
||||
|
||||
|
||||
def run(build, evidence):
|
||||
build, evidence = build.resolve(strict=True), evidence.resolve(strict=True)
|
||||
original = json.loads((evidence / 'index.json').read_text())
|
||||
results = []
|
||||
env = dict(os.environ, BUILD_OUTPUT_DIR=str(build), PYTHONDONTWRITEBYTECODE='1',
|
||||
GIT_CONFIG_NOSYSTEM='1', GIT_AUTHOR_NAME='P04 fixture', GIT_AUTHOR_EMAIL='fixture@invalid',
|
||||
GIT_COMMITTER_NAME='P04 fixture', GIT_COMMITTER_EMAIL='fixture@invalid')
|
||||
for name in ('GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_OBJECT_DIRECTORY',
|
||||
'GIT_ALTERNATE_OBJECT_DIRECTORIES'):
|
||||
env.pop(name, None)
|
||||
# Never let a task build path put Git objects inside the source repository.
|
||||
# /tmp is explicit so an inherited TMPDIR cannot redirect writes into it.
|
||||
with tempfile.TemporaryDirectory(prefix='p04-role-fixtures-', dir='/tmp') as temp:
|
||||
base = Path(temp)
|
||||
assert not base.resolve().is_relative_to(ROOT.resolve())
|
||||
checkout = base / 'checkout'
|
||||
subprocess.run(['git', 'clone', '--shared', '--no-checkout', str(ROOT), str(checkout)],
|
||||
env=env, check=True, capture_output=True)
|
||||
|
||||
def git(*args):
|
||||
return subprocess.check_output(['git', '-c', 'core.hooksPath=/dev/null', *args], cwd=checkout, env=env).decode().strip()
|
||||
|
||||
actual = git('rev-parse', 'HEAD')
|
||||
anchor = 'c0ea009b28a7649aca6a433a1a24dc0be5e7ab81'
|
||||
ancestry = subprocess.run(['git', 'merge-base', '--is-ancestor', anchor, actual], cwd=checkout, env=env)
|
||||
submission = git('show', '-s', '--format=%P', actual).split()[1] if ancestry.returncode == 0 else actual
|
||||
git('checkout', '--detach', submission)
|
||||
# Objects created by clone commits/merge-tree remain under build. The
|
||||
# shared canonical objects are consulted as read-only alternates only.
|
||||
probe = base / 'probe.py'
|
||||
probe.write_text(
|
||||
'import importlib.util,json,sys\nfrom pathlib import Path\n'
|
||||
'sys.dont_write_bytecode=True\n'
|
||||
's=importlib.util.spec_from_file_location("gate",Path.cwd()/"tools/check_p04_order_evidence.py")\n'
|
||||
'g=importlib.util.module_from_spec(s);sys.modules[s.name]=g;s.loader.exec_module(g)\n'
|
||||
'with g.isolated_objects():\n'
|
||||
' g.verify_roles(json.loads(Path(sys.argv[1]).read_text()),Path(sys.argv[2]))\n')
|
||||
index_path = base / 'index.json'
|
||||
|
||||
def check(name, index=None, positive=False, full=False, no_context_gap=False):
|
||||
index_path.write_text(json.dumps(original if index is None else index))
|
||||
if full:
|
||||
directory = base / name
|
||||
directory.mkdir()
|
||||
(directory / 'index.json').write_text(index_path.read_text())
|
||||
command = [sys.executable, 'tools/check_p04_order_evidence.py', '--verify-only']
|
||||
process_env = dict(env, P04_EVIDENCE_DIR=str(directory))
|
||||
else:
|
||||
command = [sys.executable, str(probe), str(index_path), str(build)]
|
||||
process_env = env
|
||||
process = subprocess.run(command, cwd=checkout, env=process_env, capture_output=True, text=True)
|
||||
if no_context_gap:
|
||||
assert 'qualification context gap' not in process.stderr, (name, process.stderr)
|
||||
if positive is not None:
|
||||
assert (process.returncode == 0) == positive, (name, process.stdout, process.stderr)
|
||||
results.append({'case': name, 'result': 'command_completed' if process.returncode == 0 else 'command_rejected',
|
||||
'returncode': process.returncode, 'formal_acceptance': False,
|
||||
'stdout': process.stdout, 'stderr': process.stderr})
|
||||
|
||||
check('real_submission_positive', positive=True)
|
||||
game = checkout / 'project.godot'
|
||||
original_game = game.read_bytes()
|
||||
game.write_bytes(original_game + b'\n')
|
||||
check('HEAD_tree_FS_mismatch')
|
||||
game.write_bytes(original_game)
|
||||
game.unlink()
|
||||
game.symlink_to(base / 'outside')
|
||||
(base / 'outside').write_bytes(original_game)
|
||||
check('symlink_substitution')
|
||||
game.unlink()
|
||||
game.write_bytes(original_game)
|
||||
unknown = checkout / 'scripts/p04_unknown.gd'
|
||||
unknown.write_text('extends RefCounted\n')
|
||||
check('untracked_substitution')
|
||||
git('add', 'scripts/p04_unknown.gd')
|
||||
git('commit', '-m', 'fixture unknown source')
|
||||
check('unknown_source_commit')
|
||||
git('reset', '--hard', submission)
|
||||
game.unlink()
|
||||
git('add', '-u')
|
||||
git('commit', '-m', 'fixture deleted source')
|
||||
check('deleted_source_commit')
|
||||
git('reset', '--hard', submission)
|
||||
changed = copy.deepcopy(original)
|
||||
changed['product_binding']['tested_product'] = submission
|
||||
check('wrong_tested_revision', changed)
|
||||
for name, key, value in [('forged_candidate', 'candidate_tree', '0' * 40),
|
||||
('code_only_real_candidate', 'code_only', True),
|
||||
('phase_bypass', 'phase', 'candidate')]:
|
||||
changed = copy.deepcopy(original)
|
||||
changed[key] = value
|
||||
check(name, changed)
|
||||
git('checkout', '--detach', anchor)
|
||||
git('merge', '--no-ff', '--no-edit', submission)
|
||||
merged = git('rev-parse', 'HEAD')
|
||||
# A normal commit C cannot be relabelled as tested non-production Q.
|
||||
changed = copy.deepcopy(original)
|
||||
changed['product_binding']['tested_product'] = merged
|
||||
check('normal_C_cannot_impersonate_Q', changed)
|
||||
changed = copy.deepcopy(original)
|
||||
changed['product_binding']['tested_product'] = git('rev-parse', anchor + '^{tree}')
|
||||
check('main_tree_cannot_impersonate_Q', changed)
|
||||
# A merge made by this fixture has no independent normal-C provenance.
|
||||
# Structure qualifies; provenance and credit remain external.
|
||||
check('unqualified_merge_structure', positive=True, no_context_gap=True)
|
||||
check('unqualified_merge_downstream', positive=None, full=True, no_context_gap=True)
|
||||
synthetic = git('commit-tree', git('rev-parse', merged + '^{tree}'),
|
||||
'-p', anchor, '-p', submission, '-m', 'synthetic normal C')
|
||||
git('checkout', '--detach', synthetic)
|
||||
check('synthetic_identical_parents_tree_structure', positive=True, no_context_gap=True)
|
||||
check('synthetic_identical_parents_tree_downstream', positive=None, full=True, no_context_gap=True)
|
||||
git('checkout', '--detach', merged)
|
||||
game.unlink()
|
||||
git('add', '-u')
|
||||
git('commit', '--amend', '--no-edit')
|
||||
changed = copy.deepcopy(original)
|
||||
changed['product_binding']['metadata'] = ['project.godot']
|
||||
check('metadata_hiding_deleted_source', changed)
|
||||
git('reset', '--hard', merged)
|
||||
changed = copy.deepcopy(original)
|
||||
changed['conditions']['power_stop']['required_measurements']['charges'] = 9
|
||||
check('forged_index', changed, full=True)
|
||||
return {'scope': 'fixture controls only', 'formal_acceptance': False,
|
||||
'condition_credit': 0, 'results': results}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--build', required=True, type=Path)
|
||||
parser.add_argument('--evidence', required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
print(json.dumps(run(args.build, args.evidence), indent=2))
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Stage R file-only controls. Synthetic primitive controls plus real raw controls.
|
||||
|
||||
Tests never alter supplied originals or the relocation. In-memory overlays are
|
||||
used for native descriptor/raw adversaries; filesystem adversaries are disposable.
|
||||
"""
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import sys
|
||||
import tempfile
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
import p04_native_series as s
|
||||
|
||||
|
||||
def run(build):
|
||||
results = []
|
||||
|
||||
def reject(series, name, action):
|
||||
try:
|
||||
action()
|
||||
except (ValueError, KeyError, OSError, TypeError, UnicodeError) as exc:
|
||||
results.append({'series': series, 'class': name, 'result': 'rejected', 'error': str(exc)})
|
||||
else:
|
||||
raise AssertionError('negative accepted: ' + series + '/' + name)
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix='.series-fixtures-', dir=s.HERE) as tmp:
|
||||
base = Path(tmp)
|
||||
data = b'func boundary():\n\treturn 42\n# included trailing comment\n'
|
||||
path = base / 'body.gd'
|
||||
path.write_bytes(data)
|
||||
pin = {'sha256': s.digest(data), 'bytes': len(data)}
|
||||
# A deliberately small resolver fixture bypasses the production-only
|
||||
# fixed 1721-file constructor, but exercises its exact read path.
|
||||
resolver = s.Resolver.__new__(s.Resolver)
|
||||
resolver.base, resolver.used = base, {}
|
||||
resolver.mapping = {'/native/body.gd': dict(pin, relocated=str(path))}
|
||||
resolver.manifest = {'product_source': {'body.gd': dict(pin, path='/native/body.gd')}}
|
||||
assert resolver.read('/native/body.gd', pin) == data
|
||||
assert s.variant(struct.pack('<Ii', 2, 42)).plain() == 42
|
||||
assert s.checked(data, pin, 'positive') == data
|
||||
results.append({'series': 'shared', 'class': 'small_synthetic_positive', 'result': 'pass'})
|
||||
link = base / 'symlink.gd'
|
||||
link.symlink_to(path)
|
||||
for name in s.NATIVE_PINS:
|
||||
context = s.Series.__new__(s.Series)
|
||||
context.build, context.run, context.references = base, s.RUNS[name], {}
|
||||
body_row = {'path': 'body.gd', 'function': 'boundary',
|
||||
'production_body_sha256': s.digest(data), 'registered_body_sha256': s.digest(data)}
|
||||
s.source_body_pins(resolver, [body_row])
|
||||
results.append({'series': name, 'class': 'full_body_positive', 'result': 'pass'})
|
||||
reject(name, 'wrong_source_pin', lambda: resolver.read('/native/body.gd', dict(pin, sha256='0' * 64)))
|
||||
signature = s.digest(data.splitlines(keepends=True)[0])
|
||||
reject(name, 'signature_only_pin', lambda: s.source_body_pins(resolver, [dict(
|
||||
body_row, production_body_sha256=signature, registered_body_sha256=signature)]))
|
||||
reject(name, 'symlink', lambda: s.ordinary(link))
|
||||
reject(name, 'traversal', lambda: context.read_build('../body.gd'))
|
||||
reject(name, 'unmapped_original_path', lambda: resolver.read('/native/unknown.gd', pin))
|
||||
reject(name, 'relocated_manifest_native_mismatch', lambda: resolver.read('/native/body.gd', dict(pin, bytes=len(data)+1)))
|
||||
path.write_bytes(data + b'!')
|
||||
try:
|
||||
reject(name, 'relocated_byte_manifest_mismatch', lambda: resolver.read('/native/body.gd', pin))
|
||||
finally:
|
||||
path.write_bytes(data)
|
||||
reject(name, 'JSON_only_getter_substitute', lambda: s.variant(b'{"getter": {}, "passed": true}'))
|
||||
|
||||
# Each series' real verifier is called for descriptor/raw overlays.
|
||||
# The native descriptor pins are intentionally not refreshed: these
|
||||
# runs and their original native descriptors are immutable inputs.
|
||||
original_read = s.Series.read_build
|
||||
run_name = s.RUNS[name]
|
||||
targets = {
|
||||
'forged_scalar': run_name + '/raw/writer.json',
|
||||
'changed_raw_byte': run_name + '/raw/writer.json',
|
||||
'formal_member_SHA_drift': run_name + '/execution.json',
|
||||
'unowned_missing_PID_end': run_name + '/writer/ended.json',
|
||||
'altered_phase_pair': run_name + '/raw/' + {
|
||||
's1': 'reader-landing-held.var', 's2a': 'reader-C3-recall.var',
|
||||
's2b': 'reader-night_absent-1080-before-facility.var',
|
||||
's3': 'reader-B1-phase-960-TIME_BOUNDARIES.var'}[name],
|
||||
'relabeled_interruption_reason': run_name + '/raw/' + {
|
||||
's1': 'writer-landing-depleted.json', 's2a': 'writer-C1-midnight.json',
|
||||
's2b': 'writer-night_absent-1081.json', 's3': 'writer-B1-after.json'}[name],
|
||||
}
|
||||
for label, target in targets.items():
|
||||
def overlay(self, relative, descriptor=None, target=target, label=label):
|
||||
if relative != target:
|
||||
return original_read(self, relative, descriptor)
|
||||
raw = s.ordinary(self.build / relative).read_bytes()
|
||||
if label in ('changed_raw_byte', 'altered_phase_pair'):
|
||||
raw += b' '
|
||||
else:
|
||||
obj = s.loads(raw)
|
||||
if label == 'unowned_missing_PID_end':
|
||||
obj['event'] = 'ENGINE_STARTED'
|
||||
elif label == 'forged_scalar':
|
||||
obj.update(passed=True, verified_conditions=list(s.RUNS), slots={})
|
||||
elif label == 'formal_member_SHA_drift':
|
||||
slot = next(iter(obj['formal_before']))
|
||||
obj['formal_before'][slot]['world.bin']['sha256'] = '0' * 64
|
||||
elif label == 'relabeled_interruption_reason':
|
||||
if 'service' in obj:
|
||||
obj['service']['interruption_reason'] = 'NO_POWER'
|
||||
else:
|
||||
obj['task']['state_reason'] = 'NO_POWER'
|
||||
raw = json.dumps(obj).encode()
|
||||
if descriptor is not None:
|
||||
s.checked(raw, descriptor, relative)
|
||||
return raw
|
||||
with patch.object(s.Series, 'read_build', overlay):
|
||||
reject(name, label, lambda name=name: getattr(s, 'verify_' + name)(build))
|
||||
return {'scope': 'file-only Stage R controls; immutable descriptor and primitive negatives',
|
||||
'formal_acceptance': False, 'results': results}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--build', type=Path, required=True)
|
||||
print(json.dumps(run(parser.parse_args().build), indent=2, sort_keys=True))
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Deterministic reference-only Stage R collector; never writes the final index."""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
import p04_native_series as series
|
||||
|
||||
|
||||
def encoded(value):
|
||||
return (json.dumps(value, sort_keys=True, indent=2, ensure_ascii=False) + '\n').encode()
|
||||
|
||||
|
||||
def collect(build):
|
||||
build = Path(build).absolute()
|
||||
destination = build / 'audit-support/p04-raw-collect-r22'
|
||||
series.require(not destination.exists() and not destination.is_symlink(), 'collection destination already exists')
|
||||
for p in (destination.parent, *destination.parents):
|
||||
series.require(not p.is_symlink(), 'collection parent symlink')
|
||||
payloads = {}
|
||||
bindings = {}
|
||||
for name in series.RUNS:
|
||||
result = getattr(series, 'verify_' + name)(build)
|
||||
result['verifier_sha256'] = series.digest(Path(series.__file__).read_bytes())
|
||||
data = encoded(result)
|
||||
filename = name + '.json'
|
||||
payloads[filename] = data
|
||||
bindings[name] = {'path': filename, 'bytes': len(data), 'sha256': series.digest(data),
|
||||
'relocation_manifest_sha256': series.MANIFEST_SHA256}
|
||||
manifest = {'protocol': 'p04-native-series-collection-v1', 'formal_acceptance': False,
|
||||
'stage_A': 'candidate binding stage A pending', 'series': bindings,
|
||||
'relocation_manifest_sha256': series.MANIFEST_SHA256,
|
||||
'relocation_manifest': 'audit-support/p04-r17-full-inputs-r01/MANIFEST.json',
|
||||
'collector_sha256': series.digest(Path(__file__).read_bytes()),
|
||||
'large_payloads_copied': 0}
|
||||
payloads['MANIFEST.json'] = encoded(manifest)
|
||||
estimate = sum(len(v) for v in payloads.values())
|
||||
# The estimate precedes all writes. Reserve extra directory/allocation space.
|
||||
print(json.dumps({'estimated_payload_bytes': estimate, 'files': len(payloads),
|
||||
'available_disk_bytes': shutil.disk_usage(destination.parent).free,
|
||||
'large_payloads_copied': 0}), flush=True)
|
||||
series.require(shutil.disk_usage(destination.parent).free > estimate + 1048576, 'insufficient disk for pointers')
|
||||
destination.mkdir() # exclusive creation: a previous collection is never overwritten
|
||||
for name, data in sorted(payloads.items()):
|
||||
with (destination / name).open('xb') as output:
|
||||
output.write(data)
|
||||
print(json.dumps({'collection': str(destination), 'manifest_sha256': series.digest(payloads['MANIFEST.json']),
|
||||
'actual_payload_bytes': estimate, 'formal_acceptance': False}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--build', type=Path, required=True)
|
||||
try:
|
||||
collect(parser.parse_args().build)
|
||||
except (ValueError, OSError, KeyError, TypeError) as exc:
|
||||
print(json.dumps({'collected': False, 'error': str(exc)}))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only, read-only join of actual Runner records; never grants acceptance.
|
||||
|
||||
Root must select the real Runner state.sqlite, not a hand-written export. This
|
||||
checks consistency, not authenticity of an arbitrary caller-supplied database.
|
||||
No Git, engine, Runner API, SQL mutation or temporary file is used.
|
||||
Root retains a read-only Runner snapshot taken after approve; integrate deletes
|
||||
the live approval. Final evidence is read separately from the current database.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sqlite3
|
||||
|
||||
MAIN = "c0ea009b28a7649aca6a433a1a24dc0be5e7ab81"
|
||||
SPEC = "9ec722e7dfb0bee178d291624a026420c08412574e238bba1fde012c7a6f7881"
|
||||
|
||||
|
||||
def require(value, message):
|
||||
if not value:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def unique(pairs):
|
||||
out = {}
|
||||
for key, value in pairs:
|
||||
require(key not in out, "duplicate JSON key")
|
||||
out[key] = value
|
||||
return out
|
||||
|
||||
|
||||
def read(path, hashes):
|
||||
path = Path(path)
|
||||
require(path.is_file() and not path.is_symlink(), "ordinary record required: " + str(path))
|
||||
require("normal-candidate-example" not in path.parts, "format samples are not authority")
|
||||
data = path.read_bytes()
|
||||
hashes[str(path)] = hashlib.sha256(data).hexdigest()
|
||||
result = json.loads(data, object_pairs_hook=unique)
|
||||
require(isinstance(result, dict) and not result.get("example_only_not_P04_authority"), "actual record required")
|
||||
return result
|
||||
|
||||
|
||||
def verify(database, approved_database, candidate, submission, hashes):
|
||||
for sha in (candidate, submission):
|
||||
require(re.fullmatch(r"[0-9a-f]{40}", sha), "full commit SHA required")
|
||||
database = database.resolve(strict=True)
|
||||
require(database.is_file(), "Runner database required")
|
||||
# mode=ro deliberately reads live WAL consistently; immutable=1 would hide it.
|
||||
with sqlite3.connect(database.as_uri() + "?mode=ro", uri=True) as db:
|
||||
db.row_factory = sqlite3.Row
|
||||
db.execute("PRAGMA query_only=ON")
|
||||
db.execute("BEGIN")
|
||||
task = db.execute("SELECT * FROM tasks WHERE id=?", ("P04-01",)).fetchone()
|
||||
require(task is not None, "Runner task missing")
|
||||
require(task["submitted"] == submission and task["candidate"] == candidate
|
||||
and task["main_base"] == MAIN and candidate != submission,
|
||||
"Runner candidate/submission/main_base mismatch")
|
||||
require(task["state"] in ("approved", "completed"), "Runner approval state required")
|
||||
contract = json.loads(task["contract"], object_pairs_hook=unique)
|
||||
require(contract["id"] == "P04-01" and contract["spec_sha256"] == SPEC
|
||||
and contract["validation_ids"] == ["p04-acceptance"]
|
||||
and contract["acceptance_ids"] == ["P04-01"], "immutable contract mismatch")
|
||||
approved_database = approved_database.resolve(strict=True)
|
||||
with sqlite3.connect(approved_database.as_uri() + "?mode=ro", uri=True) as approved:
|
||||
approved.row_factory = sqlite3.Row
|
||||
approved.execute("PRAGMA query_only=ON")
|
||||
approved.execute("BEGIN")
|
||||
approved_task = approved.execute("SELECT * FROM tasks WHERE id=?", ("P04-01",)).fetchone()
|
||||
require(approved_task is not None and approved_task["state"] == "approved"
|
||||
and all(approved_task[k] == task[k] for k in ("submitted", "candidate", "main_base", "contract")),
|
||||
"Root-retained actual approved Runner snapshot mismatch")
|
||||
approval = approved.execute("SELECT * FROM approvals WHERE task_id=?", ("P04-01",)).fetchone()
|
||||
require(approval is not None and approval["candidate"] == candidate
|
||||
and approval["main_base"] == MAIN and approval["owner"].strip(), "exact Runner approval required")
|
||||
rows = []
|
||||
for kind, sha in (("validation", submission), ("review", submission),
|
||||
("integration", candidate), ("final", candidate)):
|
||||
records = db.execute("SELECT * FROM evidence WHERE task_id=? AND kind=? AND commit_sha=? AND passed=1",
|
||||
("P04-01", kind, sha)).fetchall()
|
||||
require(records, "Runner evidence missing: " + kind)
|
||||
for record in records:
|
||||
value = read(record["artifact"], hashes)
|
||||
require(value.get("task_id") == "P04-01" and value.get("commit") == sha, "evidence identity mismatch")
|
||||
if kind == "review":
|
||||
require(value.get("status") == "pass" and value.get("acceptance_ids") == ["P04-01"], "independent review failed")
|
||||
require(isinstance(value.get("findings"), list) and all(
|
||||
f.get("severity") == "low" for f in value["findings"]), "review finding blocks join")
|
||||
else:
|
||||
require(value.get("kind") == kind and value.get("passed") is True, "verification kind/result mismatch")
|
||||
checks = value.get("checks")
|
||||
require(isinstance(checks, list) and len(checks) == 1, "exact validation roster required")
|
||||
check = checks[0]
|
||||
require(check.get("id") == "p04-acceptance" and type(check.get("exit_code")) is int
|
||||
and check["exit_code"] == 0 and check.get("passed") is True, "failed validation check")
|
||||
artifact = Path(check["artifact"])
|
||||
require(artifact.is_dir(), "registered validation artifact directory missing")
|
||||
for name in ("command.json", "stdout.log", "stderr.log"):
|
||||
path = artifact / name
|
||||
require(path.is_file() and not path.is_symlink(), "registered validation log missing")
|
||||
hashes[str(path)] = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
rows.append(dict(record))
|
||||
# Hash the logical transaction snapshot, not mutable SQLite/WAL bytes.
|
||||
snapshot = dict(task=dict(task), approval=dict(approval), evidence=rows)
|
||||
digest = hashlib.sha256(json.dumps(snapshot, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
|
||||
return dict(protocol="p04-normal-c-runner-join-v1", runner_database=str(database),
|
||||
approved_runner_database=str(approved_database),
|
||||
runner_records_sha256=digest, join={"C": candidate, "S": submission, "M": MAIN},
|
||||
runner_records_consistent=True, structure_verified=False,
|
||||
source_Q_applicability_verified=False, input_sha256=hashes,
|
||||
whole_goal_accepted=False, unit_accepted=False)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--runner-db", type=Path, required=True)
|
||||
parser.add_argument("--approved-runner-db", type=Path, required=True,
|
||||
help="Root-retained actual Runner snapshot after approve, before integrate deletes approval")
|
||||
parser.add_argument("--candidate", required=True)
|
||||
parser.add_argument("--submission", required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
print(json.dumps(verify(args.runner_db, args.approved_runner_db, args.candidate, args.submission, {}), sort_keys=True))
|
||||
except (OSError, ValueError, KeyError, TypeError, sqlite3.Error) as error:
|
||||
print(json.dumps({"runner_records_consistent": False, "error": str(error), "whole_goal_accepted": False}))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-run synthetic boundary fixtures. Never P04 authority; no default scratch."""
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
import sys
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
spec = importlib.util.spec_from_file_location("normal_c_join", HERE / "normal_c_join.py")
|
||||
join = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(join)
|
||||
|
||||
|
||||
def fixture(root, change):
|
||||
root.mkdir()
|
||||
submission, candidate = "1" * 40, "2" * 40
|
||||
contract = dict(id="P04-01", spec_sha256=join.SPEC,
|
||||
validation_ids=["p04-acceptance"], acceptance_ids=["P04-01"])
|
||||
db_path = root / "runner.sqlite"
|
||||
with sqlite3.connect(db_path) as db:
|
||||
db.executescript("CREATE TABLE tasks(id,contract,state,submitted,candidate,main_base);"
|
||||
"CREATE TABLE approvals(task_id,candidate,main_base,owner,created);"
|
||||
"CREATE TABLE evidence(id,task_id,kind,commit_sha,artifact,passed);")
|
||||
db.execute("INSERT INTO tasks VALUES(?,?,?,?,?,?)",
|
||||
("P04-01", json.dumps(contract), "approved", submission, candidate, join.MAIN))
|
||||
db.execute("INSERT INTO approvals VALUES(?,?,?,?,?)", ("P04-01", candidate, join.MAIN, "fixture-only", 1.0))
|
||||
for number, kind in enumerate(("validation", "review", "integration", "final")):
|
||||
sha = submission if kind in ("validation", "review") else candidate
|
||||
value = dict(task_id="P04-01", commit=sha)
|
||||
if kind == "review":
|
||||
value.update(status="pass", acceptance_ids=["P04-01"], findings=[])
|
||||
if change == "medium":
|
||||
value["findings"] = [{"severity": "medium", "message": "synthetic"}]
|
||||
else:
|
||||
logs = root / (kind + "-logs")
|
||||
logs.mkdir()
|
||||
for name in ("command.json", "stdout.log", "stderr.log"):
|
||||
(logs / name).write_text("{}" if name.endswith("json") else "synthetic fixture only\n")
|
||||
check = dict(id="p04-acceptance", exit_code=0, artifact=str(logs), passed=True)
|
||||
value.update(kind=kind, passed=True, checks=[check])
|
||||
if kind == "final":
|
||||
if change == "wrong_C": value["commit"] = "3" * 40
|
||||
if change == "kind_missing": del value["kind"]
|
||||
if change == "failed_check": check["exit_code"] = 1
|
||||
if change == "extra_id": value["checks"].append(dict(check, id="extra"))
|
||||
if change == "missing_id": value["checks"] = []
|
||||
if change == "task_mismatch": value["task_id"] = "P26-01"
|
||||
if change == "missing_artifact": check["artifact"] = str(root / "missing")
|
||||
artifact = root / (kind + ".json")
|
||||
artifact.write_text(json.dumps(value))
|
||||
db.execute("INSERT INTO evidence VALUES(?,?,?,?,?,?)", (number, "P04-01", kind, sha, str(artifact), 1))
|
||||
if change == "wrong_M": db.execute("UPDATE tasks SET main_base=?", ("3" * 40,))
|
||||
if change == "wrong_approval": db.execute("UPDATE approvals SET candidate=?", ("3" * 40,))
|
||||
if change == "no_final": db.execute("DELETE FROM evidence WHERE kind='final'")
|
||||
return db_path, submission, candidate
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--scratch", type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
root = args.scratch.resolve()
|
||||
if root.exists() or root.is_relative_to(HERE.parents[4]) or root == Path("/tmp").resolve() or Path("/tmp").resolve() in root.parents:
|
||||
raise ValueError("fresh external scratch required; /tmp and repository are prohibited")
|
||||
root.mkdir(parents=True)
|
||||
results = []
|
||||
for name in ("positive", "wrong_C", "wrong_M", "kind_missing", "failed_check", "extra_id",
|
||||
"missing_id", "task_mismatch", "medium", "missing_artifact", "wrong_approval", "no_final"):
|
||||
path, submission, candidate = fixture(root / name, name)
|
||||
try:
|
||||
result = join.verify(path, path, candidate, submission, {})
|
||||
if name != "positive" or result["whole_goal_accepted"] is not False:
|
||||
raise AssertionError("unexpected qualification: " + name)
|
||||
except ValueError:
|
||||
if name == "positive": raise
|
||||
results.append(name)
|
||||
print(json.dumps(dict(synthetic_controls=results, whole_goal_accepted=False)))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,95 @@
|
||||
# P04-01 S3 code-ready preparation
|
||||
|
||||
変更は本ディレクトリのGD、Python wrapper、本書の3本のみ。製品修正0。
|
||||
本提出は原P04-01の残る5条件の検証コード準備であり、原成果の加点は0。
|
||||
原7領域、3安全結果、15条件+EV、30/88、部分点0を維持する。
|
||||
S1r02、S2ar03、S2b native r02は保持し再実行しない。
|
||||
|
||||
## 固定入力と6実ケース
|
||||
|
||||
最初に供給SOURCE_MANIFEST.jsonの7ファイルについて全byte SHA256とbyte数の一致を確認した。
|
||||
manifest自体のSHAは `d4993398f7619ca37bcd64c32b79bdee892d44e31f332cc0dd5f31fb7bc28314`。
|
||||
wrapperは同manifest、7供給ファイル、Rootの現行source audit、S2bの3ファイル、本3ファイルをpinする。
|
||||
選択rootの全sourceもsource_refsでpinし、その後にQAを1件だけ明示overrideする。
|
||||
override先はS2bと同じ `tests/s3/test_runtime_staff_assignment.gd`。
|
||||
これはprivate stageだけのoverrideで、継承先ActorHardeningを置き換えない。
|
||||
|
||||
| case | seed | 境界と検証対象 |
|
||||
| --- | --- | --- |
|
||||
| B1 | 420410 | 959に開始済Aと実queue B。960に有限mana枯渇と対象への直接休業。960/961はAの進捗・同じ予約を保持し課金0、Bはrelease・在庫返却・忍耐不消費。元reserveへ公開復旧し、休業のまま962にAだけ1回課金。963で重複なし。 |
|
||||
| B2a | 420430 | 原定義のduration/priceで960完了・課金後に通常退出、961 Despawnで二重課金なし。 |
|
||||
| B2b | 420431 | 959の実queueを960の公開leaveでadmission前にrelease。961通常退出、962 Despawn。課金0。 |
|
||||
| B3 | 420435 | 元traveler.base_patience、実サーバー不足queue。template同様にfixture時刻+base_patienceを境界とし、通常tickで枯渇。ABANDONING→DECIDING→LEAVING→Despawn、返却1回・課金0。planned_exitは元templateの境界値。 |
|
||||
| B4a | 420420 | 元diagnostic automatic DEFENSE Event、foreground=false、durability=0。960の完了・1回課金が後段Event破壊に先行。961で重複なし。 |
|
||||
| B4b | 420421 | 元durationの入場offset1。960は進捗後にEvent破壊、961でFAILED・release・返却1回、課金0。 |
|
||||
|
||||
7件目は作らない。各caseはfresh Runtime/Clock/GameState、厨房は現行ActorHardeningと
|
||||
required_kitchen_setupの物理配置FLEX fixture。自然hire/unlockの証拠ではない。
|
||||
サービス価格・時間は定義取得、capacity/staff_min変更なし、忍耐・elapsedの直接変更なし。
|
||||
旧1299 wrapper、未承認18:00 predicate、本番Operations enableは使用しない。
|
||||
|
||||
D-034(供給DESIGN_DECISIONS.md 252–262行、特に258行)の直接休業では開始済を継続する。
|
||||
関連厨房不稼働によるpauseとは区別する。interruption_reasonは実snapshotのまま残し、
|
||||
RELATED_FACILITY_INACTIVEをNO_POWERへ置換しない。
|
||||
enum snapshotは各create_snapshotのString定義に従い、stateをintへ変換しない。
|
||||
|
||||
## Rawと正式保存
|
||||
|
||||
Writerの各before/after/next、B1 recovered/duplicate_guard、B2b/B3 despawnを正式保存する(計22slot)。
|
||||
各点の全Runtime typed snapshot、入力・seed・serial・実ID・Guest/wallet/planned_exit、
|
||||
service/request/reservation、在庫、ledger、Event instanceを無加工の.varとJSONで一度だけ保存する。
|
||||
各tickの全11phase開始点も同様に保存し、phase順と境界内部の状態を残す。
|
||||
case/indexはrawのpath/SHAを参照し、巨大snapshotのaggregate多重コピーを作らない。
|
||||
全体rawをobsolete 500000-byte capに合わせて省略・圧縮しない。
|
||||
public command入力と結果、継承fixture分を含むcommand_completed結果をcase別rawに保存する。
|
||||
fixture自身の公開command入力・IDは全Runtime command queue履歴にも残る。
|
||||
Operationsは正式snapshotのdisabled/coordinator={}と実coordinator不在を別々に記録し、無関係なgetterを呼ばない。
|
||||
|
||||
公開SaveService.save_snapshot/inspect_snapshotの成功・全typed一致・live Runtime不変を確認する。
|
||||
Readerは別PID/profileで22slotの全member(journal.log/manifest.json/thumbnail.png/world.bin)をコピーし、
|
||||
原/copyの全SHA、load/replay後の両者不変を確認する。余分なmember/directoryは失敗する。
|
||||
全点を公開load/restoreしてWriterの全Runtime typed bytesと一致させる。
|
||||
さらに各beforeをfresh loadし、通常clockで同じ全境界を再生する。
|
||||
B1のfuture recoveryについてserial、idempotency key、command ID、issue minute、元reserveをbeforeに保存する。
|
||||
Readerはserial0から始め、保存済probe入力を復元して未来コマンドだけを実行する。setup keyを再利用しない。
|
||||
全pointとphaseのtyped比較からフィールドを削除せず、JSON正規化や要約への置換も行わない。
|
||||
再生中のphaseはWriterと同じ順・全typed状態、pointは入力・receiptまで含む全typed一致を必須とする。
|
||||
|
||||
## Root手順
|
||||
|
||||
Rootだけが現行製品1338でfresh plan→nativeを1回実行する。
|
||||
worker checkout planを製品nativeへ転用しない。既存plan/outputは上書きしない。
|
||||
|
||||
```sh
|
||||
export BUILD_OUTPUT_DIR='/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build'
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p04-01/s3/run_s3_native.py --check-only --plan "$BUILD_OUTPUT_DIR/p04-s3-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s3-native-r01"
|
||||
python3 docs/plans/original-completion-2026-10-02/execution/p04-01/s3/run_s3_native.py --plan "$BUILD_OUTPUT_DIR/p04-s3-root-plan-r01.json" --output "$BUILD_OUTPUT_DIR/p04-s3-native-r01"
|
||||
```
|
||||
|
||||
製品root既定値: `/Users/korizatou114/Projects/とりにく工房ゲーム制作部`。
|
||||
出力先: `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build/p04-s3-native-r01/`。
|
||||
一つのprivate stage/cacheでimport1回、parse1回、Writer1回、別PID Reader1回。
|
||||
S2bのstrict guard、policy/watchdog、marker/diagnostic/ownership/audio gateを踏襲する。
|
||||
source/cacheは別inode、owned finally後に全source/default profile/private source/pin/original cacheを検査する。
|
||||
Writer formal slot不変も失敗時を含めて検査し、4phase・4実PIDの終了とreceipt PIDを必須とする。
|
||||
check-onlyはengine0、private/default profileアクセス0、所有helper未読込。cache receiptの実読取エラーはplanに残しnativeを拒否する。
|
||||
|
||||
## 残gapとworker検証
|
||||
|
||||
workerはpy_compile、check-only、型/参照の静的確認だけを行う。
|
||||
Godot parse・native・全回帰・Runner操作・git書込み・他agent起動は0。
|
||||
GDの正式parseと6ケースの実成立、全正式Save/Load/replay一致はRoot nativeまで未実証。
|
||||
S3 raw取得後のcollector/A仕上げ、原15+EV全証拠の適用性照合、D05の原未確定規約、
|
||||
必要なOS別条件、正式独立レビュー・統合は本提出で確定しない。
|
||||
未知の原規約を新たに作らずpendingとし、P04全体完成を主張しない。
|
||||
|
||||
worker実施記録: py_compile成功(出力はtask buildのp04-s3-worker-pycompile.pyc)。
|
||||
GDの明示型変数・loop・関数戻り値、禁止されたpatience/elapsed直接代入なし、継承先literal参照、
|
||||
15のservice method参照、Runtime method/signal参照、括弧整合を静的確認した。Godot parseの代替ではない。
|
||||
製品rootでのcheck-onlyはtools/run_all_tests.py読取りがPermissionErrorでexit2。
|
||||
明示worktree rootではcheck-only成功(source1300、QA1、fixed pins18、engine0)。
|
||||
終了済cacheのrunner-ended.jsonはPermissionErrorであり、planのcache_accessに実path/errorを記録した。
|
||||
製品1338のcheck-only成功やcache再利用の成立は主張しない。Rootが自身の環境で確認する。
|
||||
初回worktree planはbuild/p04-s3-worker-check-r01.json。最終3ファイルのpin確認にはfreshな
|
||||
build/p04-s3-worker-check-r02.jsonを使用し、r01は保持する。
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
extends "res://tests/s3/test_runtime_actor_hardening.gd"
|
||||
|
||||
const CASES: Array[String] = ["B1", "B2a", "B2b", "B3", "B4a", "B4b"]
|
||||
const SEEDS: Dictionary = {"B1": 420410, "B2a": 420430, "B2b": 420431, "B3": 420435, "B4a": 420420, "B4b": 420421}
|
||||
const GUEST: StringName = &"p04.s3.guest.a"
|
||||
const REQUEST: StringName = &"p04.s3.request.a"
|
||||
const QUEUED_GUEST: StringName = &"p04.s3.guest.b"
|
||||
const QUEUED_REQUEST: StringName = &"p04.s3.request.b"
|
||||
const SERVERS: Array[StringName] = [&"p04.order.server.a", &"p04.order.server.b"]
|
||||
var _evidence_dir: String = ""
|
||||
var _mode: String = ""
|
||||
var _case: String = ""
|
||||
var _serial: int = 0
|
||||
var _facility_id: StringName = &""
|
||||
var _service_id: StringName = &""
|
||||
var _boundary: int = 960
|
||||
var _price: int = 0
|
||||
var _duration: int = 0
|
||||
var _patience: int = 0
|
||||
var _reserve: int = 0
|
||||
var _recovery: Dictionary = {}
|
||||
var _commands: Array = []
|
||||
var _results: Array = []
|
||||
var _receipts: Array = []
|
||||
var _phases: Array = []
|
||||
var _points: Dictionary = {}
|
||||
var _branches: Dictionary = {}
|
||||
var _slots: Dictionary = {}
|
||||
|
||||
func _run() -> void:
|
||||
_evidence_dir = OS.get_environment("THE_TREE_S3_EVIDENCE")
|
||||
_mode = OS.get_environment("THE_TREE_S3_MODE")
|
||||
if _evidence_dir.is_empty() or _mode not in ["writer", "reader"]:
|
||||
quit(2)
|
||||
return
|
||||
_expect(DirAccess.make_dir_recursive_absolute(_evidence_dir) == OK, "external evidence directory")
|
||||
if _mode == "writer":
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
_writer_case()
|
||||
else:
|
||||
_reader()
|
||||
_write(_mode, {&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), &"slots": _slots,
|
||||
&"branches": _branches, &"checks": _checks, &"failures": _failures,
|
||||
&"whole_goal_accepted": false, &"unit_accepted": false}, true)
|
||||
_cleanup()
|
||||
print("S3 closure: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(1 if _failures else 0)
|
||||
|
||||
func _fresh() -> void:
|
||||
_serial = 0
|
||||
_facility_id = &""
|
||||
_service_id = &""
|
||||
_boundary = 960
|
||||
_price = 0
|
||||
_duration = 0
|
||||
_patience = 0
|
||||
_reserve = 0
|
||||
_recovery = {}
|
||||
_commands = []
|
||||
_results = []
|
||||
_receipts = []
|
||||
_phases = []
|
||||
_points = {}
|
||||
_reset_runtime(SEEDS[_case])
|
||||
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_runtime.command_completed.connect(func(result: CommandResult) -> void:
|
||||
_results.append({&"minute": _clock.absolute_minute, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "actual public command completion " + str(result.command_id))
|
||||
)
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary, at: int = -1) -> CommandResult:
|
||||
_serial += 1
|
||||
var key: StringName = StringName("p04.s3.%s.command.%d" % [_case, _serial])
|
||||
var result: CommandResult = _runtime.submit_command(kind, payload, &"player", &"", 10, at, key)
|
||||
_commands.append({&"serial": _serial, &"kind": kind, &"payload": payload.duplicate(true),
|
||||
&"at": at, &"issued_at": _clock.absolute_minute, &"idempotency_key": key, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "public command " + String(kind))
|
||||
return result
|
||||
|
||||
func _queue(guest_id: StringName, request_id: StringName, exit_at: int) -> void:
|
||||
_cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"gold": _price * 2,
|
||||
&"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO, &"planned_exit_at": exit_at}})
|
||||
_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": request_id, &"guest_ids": [guest_id]})
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(guest_id)
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(request_id)
|
||||
_expect(guest != null and request != null, "actual Guest and request exist")
|
||||
if guest == null or request == null: return
|
||||
_expect(guest.state == GuestRuntime.State.QUEUING and guest.queue_request_id == request_id, "actual queued ownership before advancing clock")
|
||||
_expect(request.quoted_price == _price and request.service_minutes == _duration and request.reserved_inventory_ids.size() == 1, "definition price/duration and one meal reservation")
|
||||
for id: StringName in request.reserved_inventory_ids:
|
||||
var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id)
|
||||
_expect(reservation != null and reservation.is_active() and reservation.requester_id == request_id, "public queue actually reserves meal")
|
||||
|
||||
func _prepare() -> bool:
|
||||
_fresh()
|
||||
var facility: FacilityInstance = _build_cafeteria(true)
|
||||
if facility == null: return false
|
||||
_facility_id = facility.instance_id
|
||||
var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id)
|
||||
var traveler: GuestDefinition = _runtime.guest_service.get_definition(&"traveler")
|
||||
_price = definition.base_price
|
||||
_duration = definition.base_service_minutes
|
||||
_patience = traveler.base_patience
|
||||
if _case == "B3":
|
||||
_expect(_patience > 1 and _patience < 240, "original patience precedes reservation expiry")
|
||||
if _patience <= 1 or _patience >= 240: return false
|
||||
_boundary = _clock.absolute_minute + _patience
|
||||
_expect(facility.operator_open and facility.is_within_opening_hours(_clock.absolute_minute) and facility.related_facility_operating and facility.active_staff_ids.size() < facility.staff_min, "real no-server queue with operating kitchen")
|
||||
else:
|
||||
for id: StringName in SERVERS:
|
||||
_cmd(&"register_staff", {&"staff_id": id, &"role_id": &"server", &"options": {&"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i.ZERO}})
|
||||
_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"DAY", &"facility_id": _facility_id})
|
||||
var queue_at: int = 959 if _case == "B2b" else 960 - _duration - 1 + (1 if _case == "B4b" else 0)
|
||||
_expect(queue_at > _clock.absolute_minute, "normal ticks reach queue time")
|
||||
_clock.advance_minutes(queue_at - _clock.absolute_minute)
|
||||
_expect(facility.active_staff_ids == SERVERS, "unchanged real server attendance")
|
||||
for id: StringName in SERVERS:
|
||||
var worker: StaffRuntime = _runtime.staff_service.get_staff(id)
|
||||
_expect(_runtime.staff_service.requires_attendance(id) and StaffCommuteService.is_at_facility(worker, facility) and worker.current_cell != Vector3i.ZERO, "ordinary public server commute")
|
||||
if _case in ["B4a", "B4b"]:
|
||||
var action: EventAction = EventAction.new().configure(&"p04.destroy.action", EventAction.Type.SET_FACILITY_STATE, {&"facility_id": _facility_id, &"conditions": {&"durability": 0, &"suspension_reason": &"DESTROYED"}})
|
||||
_cmd(&"register_event_definition", {&"definition": {&"event_id": &"p04.destroy.event", &"category": &"DEFENSE", &"foreground": false, &"transaction_policy": &"ROLLBACK_ALL", &"automatic_actions": [action.create_snapshot()]}})
|
||||
_expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.order.shift.stock"), "original public stock helper")
|
||||
var exit_at: int = _boundary if _case in ["B2a", "B2b", "B3"] else _boundary + _duration
|
||||
_queue(GUEST, REQUEST, exit_at)
|
||||
if _case == "B3":
|
||||
_clock.advance_minutes(_patience - 1)
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(GUEST)
|
||||
_expect(guest.state == GuestRuntime.State.QUEUING and guest.patience_remaining == 1 and _runtime.facility_service.get_active_services(_facility_id).is_empty(), "normal ticks exhaust original patience, no manual mutation")
|
||||
elif _case == "B2b":
|
||||
_cmd(&"leave_facility_queue", {&"facility_id": _facility_id, &"request_id": REQUEST}, 960)
|
||||
else:
|
||||
_clock.advance_one_minute()
|
||||
var active: Array = _runtime.facility_service.get_active_services(_facility_id)
|
||||
_expect(active.size() == 1, "one public service starts")
|
||||
if active.size() != 1: return false
|
||||
var service: ServiceInstance = active[0]
|
||||
_service_id = service.service_id
|
||||
var offset: int = 1 if _case == "B4b" else 0
|
||||
_expect(service.duration_minutes == _duration and service.price_snapshot == _price and service.started_at + _duration == 960 + offset, "original service completion alignment")
|
||||
_clock.advance_minutes(959 - _clock.absolute_minute)
|
||||
_expect(service.elapsed_minutes == _duration - 1 - offset, "measured boundary-before progress")
|
||||
if _case == "B1":
|
||||
_expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.s3.stock.b"), "second actual meal at 959")
|
||||
_queue(QUEUED_GUEST, QUEUED_REQUEST, 960 + _duration)
|
||||
_reserve = _state.get_resource(&"mana")
|
||||
_expect(_reserve > 0, "original positive finite mana reserve")
|
||||
_cmd(&"change_resources", {&"deltas": {&"mana": -_reserve}}, 960)
|
||||
_cmd(&"set_facility_policy", {&"facility_id": _facility_id, &"patch": {&"business_open": false}}, 960)
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
_recovery = {&"serial": _serial + 1, &"kind": &"change_resources", &"payload": {&"deltas": {&"mana": _reserve}},
|
||||
&"idempotency_key": StringName("p04.s3.%s.command.%d" % [_case, _serial + 1]),
|
||||
&"command_id": snapshot[&"systems"][&"runtime"][&"next_command_id"], &"at": -1, &"issue_minute": 961}
|
||||
if _case in ["B4a", "B4b"]:
|
||||
_cmd(&"enqueue_event", {&"event_id": &"p04.destroy.event", &"actor_ids": [], &"options": {&"event_instance_id": &"p04.destroy.instance"}})
|
||||
_expect(_clock.absolute_minute == _boundary - 1, "exact boundary-before time")
|
||||
return _clock.absolute_minute == _boundary - 1
|
||||
|
||||
func _connect_observations() -> void:
|
||||
_runtime.economy_service.transaction_posted.connect(func(id: StringName, entries: Array) -> void:
|
||||
var rows: Array = []
|
||||
for entry: LedgerEntry in entries: rows.append(entry.to_dictionary())
|
||||
_receipts.append({&"id": id, &"minute": _clock.absolute_minute, &"entries": rows})
|
||||
)
|
||||
_clock.tick_phase_started.connect(func(phase: int, minute: int) -> void:
|
||||
var label: String = "phase-%d-%s" % [minute, SimulationKernel.TickPhase.keys()[phase]]
|
||||
var row: Dictionary = _capture()
|
||||
row[&"phase"] = SimulationKernel.TickPhase.keys()[phase]
|
||||
_assert_phase(row)
|
||||
_phases.append(_write(label, row))
|
||||
)
|
||||
|
||||
func _assert_phase(point: Dictionary) -> void:
|
||||
if point[&"minute"] != _boundary: return
|
||||
var phase: String = point[&"phase"]
|
||||
if _case in ["B4a", "B4b"]:
|
||||
if phase == "INCIDENTS_AND_EVENTS":
|
||||
_expect(point[&"facility"][&"durability"] > 0 and point[&"event"][&"state"] == "QUEUED", "facility phase precedes diagnostic destruction")
|
||||
_expect(point[&"service"][&"state"] == ("COMPLETED" if _case == "B4a" else "ACTIVE"), "actual service state before Event phase")
|
||||
_expect(point[&"receipts"].size() == (1 if _case == "B4a" else 0), "completion payment occurs before destruction, not while stopped")
|
||||
if phase == "FACILITY_QUEUES":
|
||||
_expect(point[&"facility"][&"durability"] == 0 and point[&"event"][&"state"] == "COMPLETED", "Event committed before queue phase")
|
||||
if _case == "B3" and phase == "AI_DECISIONS":
|
||||
var guest: Dictionary = point[&"guests"][String(GUEST)]
|
||||
_expect(guest[&"state"] == "ABANDONING" and guest[&"patience_remaining"] == 0 and guest[&"queue_request_id"].is_empty(), "actual auto-abandon before AI redecision")
|
||||
if _case == "B2b" and phase == "FACILITY_COMPLETION":
|
||||
_expect(point[&"requests"][String(REQUEST)].is_empty() and point[&"active_services"] == 0 and point[&"available_meals"] == 1, "scheduled leave released before admission")
|
||||
if _case == "B1" and phase == "AI_DECISIONS":
|
||||
var queued: Dictionary = point[&"guests"][String(QUEUED_GUEST)]
|
||||
_expect(not queued.is_empty() and queued[&"queue_request_id"].is_empty() and queued[&"patience_remaining"] == _patience, "direct closure releases B before AI with no patience penalty")
|
||||
|
||||
func _capture() -> Dictionary:
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
var operations: Dictionary = snapshot[&"systems"][&"runtime"][&"operations"]
|
||||
var facility: FacilityInstance = _runtime.facility_service.get_instance(_facility_id)
|
||||
var service: ServiceInstance = _runtime.facility_service.get_service(_service_id)
|
||||
var event: EventInstance = _runtime.event_service.get_instance(&"p04.destroy.instance")
|
||||
var actors: Dictionary = {}
|
||||
var requests: Dictionary = {}
|
||||
var wallets: Dictionary = {}
|
||||
for id: StringName in [GUEST, QUEUED_GUEST]:
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(id)
|
||||
actors[String(id)] = guest.create_snapshot() if guest != null else {}
|
||||
wallets[String(id)] = _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(id)))
|
||||
for id: StringName in [REQUEST, QUEUED_REQUEST]:
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(id)
|
||||
requests[String(id)] = request.create_snapshot() if request != null else {}
|
||||
var reservations: Array = []
|
||||
for reservation: InventoryReservation in _runtime.inventory_service.get_reservations(false): reservations.append(reservation.create_snapshot())
|
||||
var ledger: Array = []
|
||||
for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary())
|
||||
return {&"snapshot": snapshot, &"minute": _clock.absolute_minute,
|
||||
&"inputs": {&"case": _case, &"seed": SEEDS[_case], &"serial": _serial, &"facility_id": _facility_id, &"service_id": _service_id,
|
||||
&"boundary": _boundary, &"price": _price, &"duration": _duration, &"patience": _patience, &"reserve": _reserve, &"recovery": _recovery.duplicate(true),
|
||||
&"kitchen_premise": "ActorHardening physical FLEX fixture; not natural hire/unlock"},
|
||||
&"facility": facility.create_snapshot(), &"service": service.create_snapshot() if service != null else {},
|
||||
&"event": event.create_snapshot() if event != null else {}, &"guests": actors, &"requests": requests, &"wallets": wallets,
|
||||
&"reservations": reservations, &"ledger": ledger, &"receipts": _receipts.duplicate(true),
|
||||
&"player_cash": _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT),
|
||||
&"available_meals": _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal"),
|
||||
&"active_services": _runtime.facility_service.get_active_services(_facility_id).size(),
|
||||
&"operations_enabled": operations[&"enabled"], &"saved_coordinator": operations[&"coordinator"],
|
||||
&"actual_coordinator_absent": _runtime.operations_coordinator == null, &"unrelated_getters_called": false}
|
||||
|
||||
func _labels() -> Array[String]:
|
||||
if _case == "B1": return ["before", "after", "next", "recovered", "duplicate_guard"]
|
||||
if _case in ["B2b", "B3"]: return ["before", "after", "next", "despawn"]
|
||||
return ["before", "after", "next"]
|
||||
|
||||
func _advance(label: String) -> void:
|
||||
if label == "recovered":
|
||||
_expect(_serial + 1 == _recovery[&"serial"] and _clock.absolute_minute == _recovery[&"issue_minute"], "saved future recovery serial and issue minute")
|
||||
var result: CommandResult = _cmd(_recovery[&"kind"], _recovery[&"payload"], _recovery[&"at"])
|
||||
var command: Dictionary = _commands.back()
|
||||
_expect(result.command_id == _recovery[&"command_id"] and command[&"idempotency_key"] == _recovery[&"idempotency_key"], "same future public recovery command ID/key")
|
||||
_clock.advance_one_minute()
|
||||
|
||||
func _writer_case() -> void:
|
||||
if not _prepare(): return
|
||||
_connect_observations()
|
||||
var before: Dictionary = _capture()
|
||||
_assert_before(before)
|
||||
_save_point("before", before)
|
||||
for label: String in _labels():
|
||||
if label == "before": continue
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
_assert_outcome(before, actual)
|
||||
_save_point(label, actual)
|
||||
var branch: Dictionary = {&"points": _points.duplicate(true), &"phases": _phases.duplicate(true),
|
||||
&"commands": _write("commands", {&"submitted": _commands, &"completed_including_fixture": _results})}
|
||||
_branches[_case] = _write("case", branch)
|
||||
|
||||
func _assert_before(point: Dictionary) -> void:
|
||||
_expect(not point[&"operations_enabled"] and point[&"actual_coordinator_absent"] and point[&"saved_coordinator"] == {}, "Operations disabled and actual absence measured")
|
||||
var guest: Dictionary = point[&"guests"][String(GUEST)]
|
||||
_expect(guest[&"planned_exit_at"] >= _boundary, "planned exit does not short-circuit queue/service")
|
||||
var expected_count: int = 2 if _case == "B1" else 1
|
||||
_expect(point[&"reservations"].size() == expected_count, "only actual requested meals reserved")
|
||||
for reservation: Dictionary in point[&"reservations"]:
|
||||
_expect(reservation[&"state"] == "ACTIVE" and reservation[&"purpose"] == "facility_service", "actual ACTIVE reservation before boundary")
|
||||
_expect(point[&"available_meals"] == 0, "all stocked meals are reserved")
|
||||
if _case == "B1":
|
||||
var queued: Dictionary = point[&"guests"][String(QUEUED_GUEST)]
|
||||
_expect(queued[&"state"] == "QUEUING" and queued[&"patience_remaining"] == _patience, "B exists at 959 with unchanged original patience")
|
||||
|
||||
func _assert_reservation(before: Dictionary, point: Dictionary, request_id: StringName, state: String, terminal: int) -> void:
|
||||
var prior: Array = []
|
||||
var current: Array = []
|
||||
for row: Dictionary in before[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): prior.append(row)
|
||||
for row: Dictionary in point[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): current.append(row)
|
||||
_expect(prior.size() == 1 and current.size() == 1, "one original reservation per request")
|
||||
if prior.size() != 1 or current.size() != 1: return
|
||||
_expect(prior[0][&"reservation_id"] == current[0][&"reservation_id"] and current[0][&"state"] == state and current[0][&"terminal_at"] == terminal, "same reservation ID/state/terminal time; no second release")
|
||||
if state == "ACTIVE":
|
||||
_expect(var_to_bytes(prior[0]) == var_to_bytes(current[0]), "entire held reservation unchanged")
|
||||
var request: Dictionary = point[&"requests"][String(request_id)]
|
||||
_expect(not request.is_empty() and request[&"reserved_inventory_ids"].has(current[0][&"reservation_id"]), "active reservation has live request owner")
|
||||
else:
|
||||
_expect(point[&"requests"][String(request_id)].is_empty(), "terminal reservation has no orphan request")
|
||||
if state == "RELEASED":
|
||||
var release_count: int = 0
|
||||
var inventory: Dictionary = point[&"snapshot"][&"systems"][&"runtime"][&"inventory"]
|
||||
for operation: Dictionary in inventory[&"idempotency_results"]:
|
||||
var result: Dictionary = operation[&"result"]
|
||||
if operation[&"action"] == "release" and String(result.get(&"reservation_id", "")) == current[0][&"reservation_id"]:
|
||||
release_count += 1
|
||||
_expect(bool(result.get(&"ok", false)) and result.get(&"quantity", 0) == current[0][&"quantity"], "release returned actual reserved quantity, not an orphan repair/retry")
|
||||
_expect(release_count == 1, "one raw inventory release operation for original reservation")
|
||||
|
||||
func _assert_outcome(before: Dictionary, point: Dictionary) -> void:
|
||||
var minute: int = point[&"minute"]
|
||||
var service: Dictionary = point[&"service"]
|
||||
var guest: Dictionary = point[&"guests"][String(GUEST)]
|
||||
var paid: bool = _case in ["B2a", "B4a"] or (_case == "B1" and minute >= 962)
|
||||
_expect(point[&"wallets"][String(GUEST)] == _price * (1 if paid else 2), "actual wallet delta follows boundary")
|
||||
_expect(point[&"receipts"].size() == (1 if paid else 0), "actual payment notification exactly once or zero")
|
||||
_expect(point[&"player_cash"] == before[&"player_cash"] + (_price if paid else 0), "actual player cash delta exactly matches single price")
|
||||
if not paid:
|
||||
_expect(var_to_bytes(point[&"ledger"]) == var_to_bytes(before[&"ledger"]), "zero ledger charge while stopped or abandoned")
|
||||
if paid:
|
||||
var expected_ledger: Array = before[&"ledger"].duplicate(true)
|
||||
for receipt: Dictionary in point[&"receipts"]: expected_ledger.append_array(receipt[&"entries"])
|
||||
_expect(var_to_bytes(point[&"ledger"]) == var_to_bytes(expected_ledger), "whole ledger delta is exactly actual payment receipt, no extra posting")
|
||||
var completed_at: int = 962 if _case == "B1" else 960
|
||||
_expect(service[&"state"] == "COMPLETED" and service[&"completed_at"] == completed_at, "one actual completion")
|
||||
_assert_reservation(before, point, REQUEST, "CONSUMED", completed_at)
|
||||
_expect(point[&"active_services"] == 0, "completed service releases active slot")
|
||||
if _case == "B1":
|
||||
_expect(not point[&"facility"][&"operator_open"], "direct closure remains in force including recovery")
|
||||
_assert_reservation(before, point, QUEUED_REQUEST, "RELEASED", 960)
|
||||
var queued: Dictionary = point[&"guests"][String(QUEUED_GUEST)]
|
||||
if minute == 960: _expect(not queued.is_empty(), "released B measured on closure tick")
|
||||
if not queued.is_empty():
|
||||
_expect(queued[&"queue_request_id"].is_empty() and queued[&"patience_remaining"] == before[&"guests"][String(QUEUED_GUEST)][&"patience_remaining"], "closure releases B without patience consumption; later natural Despawn is allowed")
|
||||
_expect(point[&"wallets"][String(QUEUED_GUEST)] == _price * 2 and point[&"available_meals"] == 1, "B stock returned once and wallet unchanged")
|
||||
if not paid:
|
||||
_expect(not point[&"facility"][&"power_available"] and service[&"state"] == "PAUSED" and service[&"elapsed_minutes"] == before[&"service"][&"elapsed_minutes"], "outage holds original progress; actual interruption_reason retained in raw")
|
||||
_assert_reservation(before, point, REQUEST, "ACTIVE", -1)
|
||||
elif _case in ["B2b", "B3"]:
|
||||
_assert_reservation(before, point, REQUEST, "RELEASED", _boundary)
|
||||
_expect(point[&"active_services"] == 0 and point[&"available_meals"] == 1, "no service/orphan and exact stock return")
|
||||
if minute == _boundary:
|
||||
_expect(not guest.is_empty() and guest[&"state"] == "DECIDING" and guest[&"queue_request_id"].is_empty(), "automatic redecision after release")
|
||||
if _case == "B3": _expect(guest[&"patience_remaining"] == 0, "normal queue tick exhausted original patience")
|
||||
elif minute == _boundary + 1:
|
||||
_expect(not guest.is_empty() and guest[&"state"] == "LEAVING", "normal next-minute exit")
|
||||
else: _expect(guest.is_empty(), "normal Despawn boundary")
|
||||
elif _case == "B2a":
|
||||
_expect(point[&"available_meals"] == 0, "completed meal consumed")
|
||||
if minute == 960: _expect(not guest.is_empty() and guest[&"state"] == "LEAVING" and guest[&"state_entered_at"] == 960, "completion precedes normal planned exit")
|
||||
else: _expect(guest.is_empty(), "next-tick despawn without second payment")
|
||||
else:
|
||||
_expect(point[&"facility"][&"durability"] == 0 and point[&"facility"][&"state"] == "BROKEN" and point[&"event"][&"state"] == "COMPLETED", "actual automatic destruction Event committed")
|
||||
if _case == "B4a": _expect(point[&"available_meals"] == 0, "completion before destruction consumes meal")
|
||||
elif minute == 960:
|
||||
_expect(service[&"state"] == "ACTIVE" and service[&"elapsed_minutes"] == _duration - 1, "late Event follows facility completion phase")
|
||||
_assert_reservation(before, point, REQUEST, "ACTIVE", -1)
|
||||
else:
|
||||
_expect(service[&"state"] == "FAILED" and service[&"elapsed_minutes"] == _duration - 1 and point[&"available_meals"] == 1 and point[&"active_services"] == 0, "next facility tick fails without progress or payment")
|
||||
_assert_reservation(before, point, REQUEST, "RELEASED", 961)
|
||||
# Any remaining active reservation must still belong to the same live request
|
||||
# and started service. Terminal history is retained, never filtered away.
|
||||
for reservation: Dictionary in point[&"reservations"]:
|
||||
if reservation[&"state"] != "ACTIVE": continue
|
||||
var request: Dictionary = point[&"requests"].get(reservation[&"requester_id"], {})
|
||||
_expect(not request.is_empty() and request[&"reserved_inventory_ids"].has(reservation[&"reservation_id"]) and not service.is_empty() and service[&"inventory_reservations"].has(reservation[&"reservation_id"]), "no active reservation orphan")
|
||||
|
||||
func _save_point(label: String, point: Dictionary) -> void:
|
||||
_points[label] = _write(label, point)
|
||||
var path: String = "user://saves/p04_s3/" + _case + "-" + label
|
||||
var saved: Dictionary = SaveService.save_snapshot(path, point[&"snapshot"])
|
||||
var inspected: Dictionary = SaveService.inspect_snapshot(path)
|
||||
_expect(bool(saved.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "public formal save/inspect")
|
||||
_expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(point[&"snapshot"]), "all typed fields preserved by formal package")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(point[&"snapshot"]), "formal saving leaves Runtime unchanged")
|
||||
_slots[_case + "-" + label] = ProjectSettings.globalize_path(path)
|
||||
# Snapshot bytes exist once in point raw and once in the required formal
|
||||
# package; indexes do not embed inspection/load copies of those bytes.
|
||||
|
||||
func _write(label: String, value: Variant, global_name: bool = false) -> Dictionary:
|
||||
var name: String = label if global_name else _mode + "-" + _case + "-" + label
|
||||
var reference: Dictionary = {}
|
||||
for extension: String in ["var", "json"]:
|
||||
var path: String = _evidence_dir.path_join(name + "." + extension)
|
||||
_expect(not FileAccess.file_exists(path), "raw point written exactly once " + name)
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
|
||||
_expect(file != null, "raw file " + name)
|
||||
if file == null: continue
|
||||
if extension == "var": file.store_buffer(var_to_bytes(value))
|
||||
else: file.store_string(JSON.stringify(value, "\t"))
|
||||
file.flush()
|
||||
file.close()
|
||||
reference[extension] = {&"path": path, &"sha256": FileAccess.get_sha256(path)}
|
||||
return reference
|
||||
|
||||
func _read(reference: Dictionary) -> Dictionary:
|
||||
var item: Dictionary = reference["var"]
|
||||
var path: String = item["path"]
|
||||
_expect(FileAccess.get_sha256(path) == item["sha256"], "raw typed SHA")
|
||||
var json_ref: Dictionary = reference["json"]
|
||||
_expect(FileAccess.get_sha256(json_ref["path"]) == json_ref["sha256"], "raw JSON SHA")
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
||||
_expect(file != null, "typed input readable")
|
||||
if file == null: return {}
|
||||
var value: Variant = bytes_to_var(file.get_buffer(file.get_length()))
|
||||
file.close()
|
||||
_expect(value is Dictionary, "typed input dictionary")
|
||||
return value if value is Dictionary else {}
|
||||
|
||||
func _restore_inputs(point: Dictionary) -> void:
|
||||
var inputs: Dictionary = point[&"inputs"]
|
||||
_expect(_serial == 0, "Reader starts with serial zero before importing external inputs")
|
||||
_serial = inputs[&"serial"]
|
||||
_facility_id = inputs[&"facility_id"]
|
||||
_service_id = inputs[&"service_id"]
|
||||
_boundary = inputs[&"boundary"]
|
||||
_price = inputs[&"price"]
|
||||
_duration = inputs[&"duration"]
|
||||
_patience = inputs[&"patience"]
|
||||
_reserve = inputs[&"reserve"]
|
||||
_recovery = inputs[&"recovery"].duplicate(true)
|
||||
|
||||
func _load_point(path: String, expected: Dictionary) -> bool:
|
||||
_fresh()
|
||||
_restore_inputs(expected)
|
||||
var loaded: Dictionary = SaveService.load_snapshot(path)
|
||||
_expect(bool(loaded.get(&"ok", false)), "public formal load")
|
||||
if not bool(loaded.get(&"ok", false)): return false
|
||||
_expect(var_to_bytes(loaded[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all saved typed fields equal Writer")
|
||||
var restored: bool = _runtime.restore_snapshot(loaded[&"snapshot"])
|
||||
_expect(restored, "public Runtime restore")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected[&"snapshot"]), "all restored typed fields equal Writer")
|
||||
return restored
|
||||
|
||||
func _reader() -> void:
|
||||
var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ)
|
||||
_expect(file != null, "Writer receipt")
|
||||
if file == null: return
|
||||
var writer: Dictionary = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
_expect(int(writer["pid"]) != OS.get_process_id() and writer["profile"] != OS.get_user_data_dir(), "independent Reader PID/profile")
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
var writer_case: Dictionary = _read(writer["branches"][branch])
|
||||
var hashes: Dictionary = {}
|
||||
var restored_refs: Dictionary = {}
|
||||
for label: String in _labels():
|
||||
var key: String = branch + "-" + label
|
||||
var original: String = writer["slots"][key]
|
||||
var target: String = ProjectSettings.globalize_path("user://saves/p04_s3/" + key)
|
||||
_expect(DirAccess.make_dir_recursive_absolute(target) == OK, "private Reader formal slot")
|
||||
var directory: DirAccess = DirAccess.open(original)
|
||||
_expect(directory != null, "Writer formal slot readable")
|
||||
if directory == null: return
|
||||
var members: PackedStringArray = directory.get_files()
|
||||
members.sort()
|
||||
_expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "all formal members enumerated")
|
||||
var member_hashes: Dictionary = {}
|
||||
for member: String in members:
|
||||
var sha: String = FileAccess.get_sha256(original.path_join(member))
|
||||
_expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy every formal member")
|
||||
var copied_sha: String = FileAccess.get_sha256(target.path_join(member))
|
||||
_expect(not sha.is_empty() and sha == copied_sha, "original/copy member SHA equality")
|
||||
member_hashes[member] = {&"original_before": sha, &"copy_before": copied_sha}
|
||||
hashes[key] = {&"original": original, &"copy": target, &"members": member_hashes}
|
||||
_slots[key] = target
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
if not _load_point(target, expected): return
|
||||
restored_refs[label] = _write(label + "-restored", _capture())
|
||||
var before: Dictionary = _read(writer_case[&"points"]["before"])
|
||||
if not _load_point(_slots[branch + "-before"], before): return
|
||||
_assert_before(_capture())
|
||||
_connect_observations()
|
||||
var replay_refs: Dictionary = {}
|
||||
for label: String in _labels():
|
||||
if label == "before": continue
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
_expect(var_to_bytes(actual) == var_to_bytes(expected), "replay complete typed point including Runtime, inputs, receipts " + label)
|
||||
_assert_outcome(before, actual)
|
||||
replay_refs[label] = _write(label + "-replay", actual)
|
||||
var expected_phases: Array = writer_case[&"phases"]
|
||||
_expect(_phases.size() == expected_phases.size(), "complete phase count")
|
||||
if _phases.size() == expected_phases.size():
|
||||
for index: int in range(_phases.size()):
|
||||
_expect(var_to_bytes(_read(_phases[index])) == var_to_bytes(_read(expected_phases[index])), "same phase order and all typed phase state")
|
||||
for key: String in hashes:
|
||||
var row: Dictionary = hashes[key]
|
||||
for member: String in row[&"members"]:
|
||||
var item: Dictionary = row[&"members"][member]
|
||||
item[&"original_after"] = FileAccess.get_sha256(String(row[&"original"]).path_join(member))
|
||||
item[&"copy_after"] = FileAccess.get_sha256(String(row[&"copy"]).path_join(member))
|
||||
_expect(item[&"original_before"] == item[&"original_after"] and item[&"copy_before"] == item[&"copy_after"], "original/copy unchanged after all loads and replay")
|
||||
_branches[branch] = _write("case", {&"restored": restored_refs, &"replay": replay_refs, &"phases": _phases,
|
||||
&"member_hashes": hashes, &"recovery_commands": _commands, &"replay_completed_commands": _results})
|
||||
@@ -0,0 +1,293 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S3-only native entry. --check-only never loads ownership helpers or starts Godot."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
BUILD = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build")
|
||||
PRODUCT = Path("/Users/korizatou114/Projects/とりにく工房ゲーム制作部")
|
||||
CACHE_RUN = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p18-01-model/.runtime/P18-01-CODE/build/p18-relationship-evidence")
|
||||
TEST = "tests/s3/test_runtime_staff_assignment.gd"
|
||||
QA = HERE / "p04_s3_closure_probe.gd"
|
||||
ENGINE = Path("/Applications/Godot.app/Contents/MacOS/Godot")
|
||||
MANIFEST = BUILD / "audit-support/p04-s3-existing-source-r01/SOURCE_MANIFEST.json"
|
||||
MANIFEST_SHA = "d4993398f7619ca37bcd64c32b79bdee892d44e31f332cc0dd5f31fb7bc28314"
|
||||
AUDIT = BUILD / "audit-support/p04-whole-closure-r01/ROOT_S2B_EXISTING_SOURCE_AUDIT.json"
|
||||
CASES = ["B1", "B2a", "B2b", "B3", "B4a", "B4b"]
|
||||
LABELS = {"B1": ["before", "after", "next", "recovered", "duplicate_guard"],
|
||||
"B2a": ["before", "after", "next"], "B2b": ["before", "after", "next", "despawn"],
|
||||
"B3": ["before", "after", "next", "despawn"],
|
||||
"B4a": ["before", "after", "next"], "B4b": ["before", "after", "next"]}
|
||||
SLOTS = sorted(case + "-" + label for case in CASES for label in LABELS[case])
|
||||
MATERIALS = [MANIFEST, AUDIT, HERE.parent / "s2b/run_s2b_native.py",
|
||||
HERE.parent / "s2b/p04_s2b_shift_probe.gd", HERE.parent / "s2b/S2B_ROOT_RUN.md"]
|
||||
|
||||
def template_guard(root):
|
||||
if pin(MANIFEST)["sha256"] != MANIFEST_SHA:
|
||||
raise ValueError("supplied seven-source manifest changed")
|
||||
manifest = json.loads(MANIFEST.read_text())
|
||||
if len(manifest["files"]) != 7:
|
||||
raise ValueError("exact seven source files required")
|
||||
for row in manifest["files"]:
|
||||
actual = pin(row["supplied"])
|
||||
if (actual["bytes"], actual["sha256"]) != (row["bytes"], row["sha256"]):
|
||||
raise ValueError("supplied template SHA mismatch: " + row["supplied"])
|
||||
audit = json.loads(AUDIT.read_text())
|
||||
for row in audit["source_pins"]:
|
||||
actual = pin(root / row["path"])
|
||||
if (actual["bytes"], actual["sha256"]) != (row["production_bytes"], row["production_sha256"]):
|
||||
raise ValueError("Root source audit mismatch: " + row["path"])
|
||||
probe = QA.read_text()
|
||||
if ":=" in probe:
|
||||
raise ValueError("probe requires explicit typed declarations")
|
||||
import re
|
||||
if re.search(r"\bvar\s+\w+\s*=", probe) or re.search(r"\bfor\s+\w+\s+in\b", probe):
|
||||
raise ValueError("untyped declaration/loop")
|
||||
if re.search(r"\.(?:patience_remaining|elapsed_minutes)\s*=(?!=)", probe):
|
||||
raise ValueError("manual patience/progress mutation")
|
||||
for forbidden in ["._update_shift_states(", ".record_duty_minute(", "get_day_summary(",
|
||||
"configure_operations("]:
|
||||
if forbidden in probe:
|
||||
raise ValueError("out-of-scope mutation/getter: " + forbidden)
|
||||
|
||||
|
||||
def module(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
value = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(value)
|
||||
return value
|
||||
|
||||
def pin(path):
|
||||
path = Path(path)
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise ValueError("missing exact input: " + str(path))
|
||||
data = path.read_bytes()
|
||||
return dict(path=str(path), bytes=len(data), sha256=hashlib.sha256(data).hexdigest())
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--project-root", type=Path, default=PRODUCT)
|
||||
parser.add_argument("--output", type=Path, default=BUILD / "p04-s3-native-r01")
|
||||
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s3-plan-r01.json")
|
||||
parser.add_argument("--cache-run", type=Path, default=CACHE_RUN)
|
||||
parser.add_argument("--godot", type=Path, default=ENGINE)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
args = parser.parse_args()
|
||||
root = args.project_root.resolve()
|
||||
out = args.output.resolve()
|
||||
if not out.is_relative_to(BUILD.resolve()) or not args.plan.resolve().is_relative_to(BUILD.resolve()):
|
||||
raise ValueError("plan/output must stay in task build")
|
||||
r = module("s3_standard_runner", root / "tools/run_all_tests.py")
|
||||
# Preserve the reviewed source_refs -> exactly one QA override order.
|
||||
source_before = r.source_refs(root)
|
||||
refs = dict(source_before)
|
||||
refs[TEST] = pin(QA)
|
||||
template_guard(root)
|
||||
expected = {n: dict(bytes=p["bytes"], sha256=p["sha256"]) for n, p in refs.items()}
|
||||
fixed = [pin(p) for p in MATERIALS + [Path(row["supplied"]) for row in json.loads(MANIFEST.read_text())["files"]] + [
|
||||
QA, Path(__file__), HERE / "S3_ROOT_RUN.md",
|
||||
root / "tests/transport/test_runtime_transport.gd",
|
||||
root / "tests/staff/test_rescue_elevator.gd",
|
||||
root / "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json",
|
||||
]]
|
||||
selected = dict(root_before=source_before, map={n: p["sha256"] for n, p in refs.items()}, timeout=1200)
|
||||
cache_note = None
|
||||
cache_receipts = []
|
||||
for leaf in ["runner-ended.json", "runner-input.json"]:
|
||||
path = args.cache_run / leaf
|
||||
try:
|
||||
cache_receipts.append(pin(path))
|
||||
except OSError as caught:
|
||||
cache_note = dict(path=str(path), error=type(caught).__name__ + ": " + str(caught),
|
||||
required_before_native=True)
|
||||
break
|
||||
plan = dict(protocol="p04-s3-preparation-v1", project_root=str(root), output=str(out),
|
||||
source_before=source_before, selected_source_map=selected["map"],
|
||||
QA_overrides=[TEST], fixed_input_pins=fixed, cache_run=str(args.cache_run),
|
||||
cache_receipts=cache_receipts, cache_access=cache_note,
|
||||
phases=["import", "parse", "writer", "reader"],
|
||||
native_engine_launches=0, whole_goal_accepted=False, unit_accepted=False)
|
||||
# Check-only only reads source/materials and writes the plan in task build.
|
||||
# It deliberately does not read default-player data, inspect processes or stat Godot.
|
||||
if args.check_only:
|
||||
args.plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
if args.plan.exists():
|
||||
r.require(r.read(args.plan) == plan, "existing plan differs; use a fresh plan path")
|
||||
else:
|
||||
r.write(args.plan, plan)
|
||||
print(json.dumps(dict(check_only=True, engines=0, source_files=len(source_before),
|
||||
QA_overrides=[TEST], fixed_pins=len(fixed), plan=str(args.plan),
|
||||
plan_sha256=r.sha(args.plan), cache_access=cache_note), ensure_ascii=False))
|
||||
return 0
|
||||
r.require(root == PRODUCT.resolve() and len(source_before) == 1338, "Root native requires product 1338; worker plan is not native evidence")
|
||||
r.require(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first")
|
||||
def fixed_guard():
|
||||
template_guard(root)
|
||||
for value in fixed + cache_receipts:
|
||||
r.require(pin(value["path"]) == value, "fixed input changed: " + value["path"])
|
||||
fixed_guard()
|
||||
# Native admission is Root-only and fails closed on unreadable cache.
|
||||
r.require(cache_note is None, "Root must read exact cache receipts: " + str(cache_note))
|
||||
old = args.cache_run
|
||||
r.require(r.read(old / "runner-ended.json")["passed"], "ended cache run must have passed")
|
||||
old_map = r.read(old / "runner-input.json")["selected_source_map"]
|
||||
current_map = {n: p["sha256"] for n, p in source_before.items()}
|
||||
r.require(old_map == current_map, "ended cache selected source must equal current product")
|
||||
protected = r.default_player(root)
|
||||
outer = module("s3_owned_outer", r.SUPPORT / "owned_outer.py")
|
||||
r.require(not outer.native.helper["live_engines"](), "another Godot owns CPU")
|
||||
engine = args.godot.resolve()
|
||||
r.require(engine.is_file(), "Root native Godot path")
|
||||
r.require(shutil.disk_usage(out.parent).free >= 1024**3 + engine.stat().st_size + 136*1024**2,
|
||||
"reviewed native disk admission")
|
||||
r.require(not out.exists(), "fresh output required; preserve previous attempts")
|
||||
out.mkdir()
|
||||
stage = out / "stage"
|
||||
stage.mkdir()
|
||||
(out / "starters").mkdir()
|
||||
(out / "bootstrap").mkdir()
|
||||
phases = []
|
||||
previous = {}
|
||||
error = None
|
||||
guards = []
|
||||
cleanup = []
|
||||
cleanup_errors = []
|
||||
cache_before = None
|
||||
formal_before = None
|
||||
formal_after = None
|
||||
original_phase = outer.run_phase
|
||||
def run_phase(argv, folder, timeout, env, **kwargs):
|
||||
env = dict(env)
|
||||
if folder.name in ["writer", "reader"]:
|
||||
env.update(THE_TREE_S3_EVIDENCE=str(out / "raw"), THE_TREE_S3_MODE=folder.name)
|
||||
return original_phase(argv, folder, timeout, env, **kwargs)
|
||||
outer.run_phase = run_phase
|
||||
r.write(out / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(),
|
||||
plan=pin(args.plan), engines_launched=0))
|
||||
try:
|
||||
r.write(out / "source_before.json", source_before)
|
||||
r.write(out / "selected-source-map.json", refs)
|
||||
# Private source stage first, then literal reviewed bootstrap.
|
||||
for name, value in refs.items():
|
||||
target = stage / name
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
r.copy_source(Path(value["path"]), target)
|
||||
r.require(r.inventory(stage, stage=True) == expected, "whole private source")
|
||||
for name, value in r.read(r.SUPPORT / "bootstrap-scripts.json").items():
|
||||
data = value["text"].encode()
|
||||
r.require(hashlib.sha256(data).hexdigest() == value["sha256"], "bootstrap pin")
|
||||
(out / "bootstrap" / name).write_bytes(data)
|
||||
cache = old / "stage/.godot"
|
||||
cache_before = r.inventory(cache)
|
||||
r.require(bool(cache_before), "nonempty ended cache")
|
||||
for name in cache_before:
|
||||
src = cache / name
|
||||
dst = stage / ".godot" / name
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(["/bin/cp", "-c", str(src), str(dst)], check=True)
|
||||
r.require((src.stat().st_dev, src.stat().st_ino) != (dst.stat().st_dev, dst.stat().st_ino),
|
||||
"cache clone is a separate inode")
|
||||
r.require(r.inventory(stage / ".godot") == cache_before, "whole cache clone hashes")
|
||||
r.write(out / "cache-reuse.json", dict(ended_run=str(old), receipts=cache_receipts,
|
||||
whole_cache=cache_before, independent_inode=True,
|
||||
ordinary_import_required=True, ordinary_parse_required=True))
|
||||
paths = ["res://" + n for n in sorted(refs) if n.endswith(".gd")]
|
||||
r.write(out / "parse-paths.json", paths)
|
||||
markers = module("s3_markers", root / "tools/strict_test_markers.py")
|
||||
contracts = markers.load_contracts(root / "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json")
|
||||
contracts[TEST] = dict(test=TEST, template="S3 closure: <checks> checks, <failures> failures", required=True)
|
||||
policy = r.read(r.SUPPORT / "suite-policy.json")
|
||||
r.require(TEST not in policy["suite_policies"], "no normal suite watchdog/policy override")
|
||||
r.write(out / "contracts.json", contracts)
|
||||
r.write(out / "runner-input.json", dict(plan, owner_runner_pid=os.getpid(),
|
||||
wrapper_sha256=r.sha(__file__), QA=pin(QA), runner_sha256=r.sha(root / "tools/run_all_tests.py"),
|
||||
default_player_before=protected, fixed_input_pins=fixed,
|
||||
functional_watchdog_seconds=1200, source_adoption=False))
|
||||
diagnostics = module("s3_diagnostics", r.SUPPORT / "diagnostic_gate.py")
|
||||
owners = module("s3_owners", r.SUPPORT / "ordinary_proof_gate.py")
|
||||
audio = module("s3_audio", r.SUPPORT / "audio_evidence_gate.py")
|
||||
public = (stage / "project.godot").read_bytes()
|
||||
for phase, test in [("import", None), ("parse", None), ("writer", TEST), ("reader", TEST)]:
|
||||
if phase == "reader":
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_before = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(sorted(formal_before) == SLOTS, "all 22 formal points for six fresh cases")
|
||||
row = r.execute_phase(str(engine), stage, out, phase, test, selected, expected,
|
||||
previous, policy, contracts, paths, public, protected, root,
|
||||
outer, markers, diagnostics, owners, audio)
|
||||
phases.append(row)
|
||||
# execute_phase uses the same test log name; retain each phase's exact log too.
|
||||
if test and row.get("log"):
|
||||
(out / (phase + "-combined-raw.log")).write_bytes((out / row["log"]).read_bytes())
|
||||
row["strict_runner_log_path"] = row["log"]
|
||||
row["log"] = phase + "-combined-raw.log"
|
||||
r.write(out / (phase + "-actual-phase.json"), row)
|
||||
print(json.dumps(dict(phase=phase, passed=row["passed"], error=row.get("validation_error"))), flush=True)
|
||||
r.require(row["passed"], "first strict phase failure: " + phase)
|
||||
if phase in ["writer", "reader"]:
|
||||
receipt = r.read(out / ("raw/" + phase + ".json"))
|
||||
r.require(receipt["checks"] > 0 and receipt["failures"] == 0,
|
||||
"S3 actual outcome receipt")
|
||||
r.require(sorted(receipt["branches"]) == CASES,
|
||||
"exact six independent Writer/Reader cases")
|
||||
r.require(sorted(receipt["slots"]) == SLOTS, "all formal point slots")
|
||||
r.require(receipt["pid"] == row["actual_pid"], "actual native PID/receipt")
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
reader = r.read(out / "raw/reader.json")
|
||||
r.require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "distinct formal processes")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Reader leaves Writer packages unchanged")
|
||||
except BaseException as caught:
|
||||
error = type(caught).__name__ + ": " + str(caught)
|
||||
finally:
|
||||
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
|
||||
try:
|
||||
r.write(out / "outer-owned-finally.json", dict(cleanup=cleanup, errors=cleanup_errors))
|
||||
except BaseException as caught:
|
||||
guards.append("finally receipt: " + type(caught).__name__ + ": " + str(caught))
|
||||
# Fixed guard always follows ownership cleanup, even if setup or an engine failed.
|
||||
try:
|
||||
fixed_guard()
|
||||
after = r.source_refs(root)
|
||||
r.write(out / "source_after.json", after)
|
||||
r.require(after == source_before, "whole product source unchanged")
|
||||
r.require(r.default_player(root) == protected, "default player unchanged")
|
||||
if (stage / "project.godot").exists():
|
||||
r.require(r.inventory(stage, stage=True) == dict(expected, **previous), "whole private source unchanged")
|
||||
if cache_before is not None:
|
||||
r.require(r.inventory(old / "stage/.godot") == cache_before, "original cache unchanged")
|
||||
if formal_before is not None:
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Writer formal packages unchanged after Reader, including failure")
|
||||
except BaseException as caught:
|
||||
guards.append(type(caught).__name__ + ": " + str(caught))
|
||||
pids = [p.pid for p in outer.OWNED]
|
||||
passed = (error is None and not guards and not cleanup_errors and len(phases) == 4
|
||||
and all(p["passed"] for p in phases) and len(pids) == len(set(pids)) == 4
|
||||
and all(p["engine_ended"] for p in cleanup))
|
||||
execution = dict(passed=passed, error=error, guard_errors=guards, phases=phases,
|
||||
actual_captured_PIDs=pids, cleanup=cleanup, cleanup_errors=cleanup_errors,
|
||||
formal_before=formal_before, formal_after=formal_after,
|
||||
whole_goal_accepted=False, unit_accepted=False)
|
||||
r.write(out / "raw-inventory.json", r.inventory(out / "raw"))
|
||||
r.write(out / "PID-set.json", dict(owner_runner_pid=os.getpid(), engine_PIDs=pids))
|
||||
r.write(out / "execution.json", execution)
|
||||
r.write(out / "runner-ended.json", dict(execution, ended=r.now()))
|
||||
print(json.dumps(dict(passed=passed, error=error, guard_errors=guards, output=str(out))))
|
||||
return 0 if passed else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as caught:
|
||||
print(type(caught).__name__ + ": " + str(caught), file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"status": "NOTRUN",
|
||||
"task_id": "P04-01",
|
||||
"starting_submission": "55f1fb49aaefa5c1b86de1c3eda72392ef6b58f2",
|
||||
"source": "audit-support/p04-s4-source-r28",
|
||||
"plan": "p04-s4-plan-r28.json",
|
||||
"native": "p04-s4-native-r28",
|
||||
"regression_status": "NOTRUN",
|
||||
"native_status": "NOTRUN",
|
||||
"unit_complete": false,
|
||||
"D05": "pending",
|
||||
"files": {
|
||||
"s4/p04_s4_power_probe.gd": {
|
||||
"bytes": 26912,
|
||||
"sha256": "56f8e2786e3aff82097baa06afb4cd13e6e7c06c3d1afa43b9ee35c78a25a999"
|
||||
},
|
||||
"s4/run_s4_native.py": {
|
||||
"bytes": 23233,
|
||||
"sha256": "2d3be39d2b310abac058e27336ac816c234043a0a1dd7b2c52d84994907dd31b"
|
||||
},
|
||||
"s4/S4_ROOT_RUN.md": {
|
||||
"bytes": 21862,
|
||||
"sha256": "ff463e1e6ce8847f658eddc91f1cec3eab3bb8bcadd9a75272138e06083116a2"
|
||||
},
|
||||
"s4/phase_cut_regression.py": {
|
||||
"bytes": 3919,
|
||||
"sha256": "1362078fdd819d057f1f1bab369b9629a11e089a3453da8af4ce21cec251dbad"
|
||||
},
|
||||
"s4/receipt_lifecycle_fixtures.py": {
|
||||
"bytes": 12340,
|
||||
"sha256": "629571495787fdcda82a6a74d98649d4ea424d6fc9c27c5cafa55d4b2881c71f"
|
||||
},
|
||||
"p04_native_series.py": {
|
||||
"bytes": 75900,
|
||||
"sha256": "c1cc5d49dbf4c8422a2208297c665fde8abd569e2c9f8239603d8218ae1ed8fb"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
# S4 finite Root collection r28 — NOT RUN
|
||||
|
||||
This adds only the existing `power_stop` evidence obligation. It adds no counted
|
||||
condition, product change, acceptance or priority approval. Run once after this
|
||||
source batch is auto-submitted. Do not rerun C/S1/S2a/S2b/S3. Do not run the full
|
||||
product suite now. Preserve all existing raw and REPAIR17–21 history.
|
||||
|
||||
From the clean auto-submitted checkout, stage these three exact reviewed files
|
||||
in the fresh own-B source directory. Run the installed executable directly; do not stage or copy it. This staging is a Root action, not a worker
|
||||
execution. The wrapper refuses QA outside own B. QA and fixed inputs stay within registered B; only the exact installed engine is external; external manifest paths are lookup keys and
|
||||
provenance only. Never open their original production locations.
|
||||
|
||||
```sh
|
||||
export B=/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build
|
||||
export E=docs/plans/original-completion-2026-10-02/execution/p04-01
|
||||
export PYTHONDONTWRITEBYTECODE=1
|
||||
python3 "$E/s4/engine_identity_negative_fixtures.py"
|
||||
python3 "$E/s4/receipt_lifecycle_fixtures.py"
|
||||
mkdir "$B/audit-support/p04-s4-source-r28"
|
||||
cp "$E/s4/run_s4_native.py" "$E/s4/p04_s4_power_probe.gd" "$E/s4/S4_ROOT_RUN.md" "$B/audit-support/p04-s4-source-r28/"
|
||||
python3 "$B/audit-support/p04-s4-source-r28/run_s4_native.py" \
|
||||
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
|
||||
--output "$B/p04-s4-native-r28" --plan "$B/p04-s4-plan-r28.json" \
|
||||
--godot /Applications/Godot.app/Contents/MacOS/Godot --check-only
|
||||
python3 "$B/audit-support/p04-s4-source-r28/run_s4_native.py" \
|
||||
--project-root "$B/audit-support/p04-r17-full-inputs-r01/product" \
|
||||
--output "$B/p04-s4-native-r28" --plan "$B/p04-s4-plan-r28.json" \
|
||||
--godot /Applications/Godot.app/Contents/MacOS/Godot
|
||||
```
|
||||
|
||||
Root must stop if either fixture or check-only fails; the commands are not an
|
||||
unconditional batch. Both fixtures start zero engines. The check-only invocation
|
||||
only prepares the pinned plan: zero engine launches. The final invocation
|
||||
is one collection, exactly four sequential owned processes: import,
|
||||
parse, Writer, Reader. Each retains the framework's native diagnostics,
|
||||
ownership, exit/end checks, full stream SHA, default-player protection, private
|
||||
profiles, source-before/after guards and cleanup. Failure stops the collection;
|
||||
do not overwrite or silently repeat it. The ordinary framework creates its
|
||||
normal private user profiles; these are process outputs, not external source
|
||||
inputs. Reader uses only the existing IO1 formal package mapping.
|
||||
|
||||
Exact input footprints:
|
||||
|
||||
- Historical wrapper hashes do not pin this repair. The fresh plan pins
|
||||
its exact bytes and Stage R checks them against the current source checkout.
|
||||
- `B/audit-support/p04-r17-full-inputs-r01/MANIFEST.json`, SHA256
|
||||
`a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8`.
|
||||
- All 1338 files below that relocation's `product/`, each matched to the
|
||||
manifest by bytes/SHA. No source is omitted and no old path is followed.
|
||||
- Its `fixed/6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7/completion_marker_contracts.json`.
|
||||
- `B/audit-support/p04-review-repair22-r01/ACTUAL_CURRENT_PUBLIC_POWER_FUNCTIONS.json`.
|
||||
- The three staged S4 files above, pinned by the plan, and the installed executable
|
||||
`/Applications/Godot.app/Contents/MacOS/Godot`, pinned directly against the
|
||||
constant SHA before launch, during collection, and explicitly after cleanup.
|
||||
Runner-input retains an immutable initial descriptor and observation prefix;
|
||||
execution and engine-identity retain the final before/during/after identity. The only stage override
|
||||
is `tests/s3/test_runtime_staff_assignment.gd` with the S4 probe. Harness and
|
||||
kitchen fixture are the unchanged product files; inherited sources are
|
||||
included in the full1338 map. The parse roster is all316 `.gd` inputs.
|
||||
- Fresh mode is the default: no cache receipt or cache directory is accessed,
|
||||
read or cloned. The private `stage/.godot` must be absent immediately before
|
||||
the actual full import. All316 scripts are parsed before Writer and Reader.
|
||||
- The engine SHA256 is
|
||||
`c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf`,
|
||||
required directly of the installed executable. Fresh mode does not obtain engine identity from a cache.
|
||||
- Optional `--cache-run` retains the registered path, passed-run, complete-source
|
||||
and engine identity guards. The known red run remains ineligible. The commands
|
||||
above use no cache. Preserve the old r22 staging and plan unchanged. r22 is an immutable failed
|
||||
proof and remains ineligible. Its diagnostics are at
|
||||
`audit-support/p04-root-d393-s4-r01/ENGINE_STAGING_DIAGNOSTICS.json`.
|
||||
|
||||
Exact output footprints are `B/p04-s4-plan-r28.json` and the fresh
|
||||
`B/p04-s4-native-r28/**`: private selected source stage, complete phase receipts
|
||||
and logs, raw JSON/Variant points and all phase observations, full source maps,
|
||||
PID and cleanup receipts. `formal/{writer,reader}/S4-{boundary,held,recovered}/`
|
||||
mirrors all four formal members (`journal.log`, `manifest.json`, `thumbnail.png`,
|
||||
`world.bin`) with a provenance-only `formal-relocation.json`. R reads these
|
||||
own-B mirrors, never profile paths from old manifests.
|
||||
|
||||
Scenario and source-derived timing:
|
||||
|
||||
1. Use the existing public cafeteria + physical FLEX kitchen fixture and normal
|
||||
DAY server commute. Queue using the definition's original price and duration;
|
||||
advance naturally to duration−1 at959. Derive the cafeteria's sole neighbour
|
||||
from the actual graph and call public `disconnect_nodes` on exactly that edge.
|
||||
2. Advance normally to960. The source's modulo10 evaluation finds an isolated
|
||||
consumer with zero generation/storage. Normal `mana_power_projection.apply`
|
||||
makes only the cafeteria unpowered; preparation reaches `NO_POWER` with its
|
||||
dependency operating. Save **boundary** at960, then **held** at961. Mana stays
|
||||
positive; operator stays open; linked kitchen is Facility `OPEN` and network
|
||||
consumer `ACTIVE`. No closure, damage, shift or exit triggers accompany outage.
|
||||
3. At961 reconnect the same edge. Verify the complete original edge set and
|
||||
graph invariants. Advance each minute, retaining full phase raw. The saved
|
||||
hold deadline is960+10=970; the next permitted normal evaluation is970.
|
||||
Progress, reservation and sale stay held through969. Save **recovered** at970
|
||||
after one completion/payment. Resend connect_nodes on the same edge at970,
|
||||
comparing the complete Runtime before/after;971 is the duplicate guard. Ordinary guest
|
||||
redecision/despawn after completed service is retained in raw, never forced
|
||||
or suppressed; the outage and held service have no exit confound.
|
||||
4. A distinct Reader copies all four members at each point. Normal SaveService
|
||||
load and Runtime restore must reproduce the full typed snapshot, including
|
||||
graph and all unrelated fields. No canonicalization, dropped fields, forced
|
||||
evaluation or hold bypass. Replay from the960 formal boundary through961,
|
||||
reconnect,970 and971; compare complete typed points and phase states. No
|
||||
additional condition is credited for this formal preservation check.
|
||||
|
||||
`REPAIR22_SOURCE_FACTS.json` contains full actual source bodies and graph restore
|
||||
invariants. The source normalizes saved node IDs/reasons, checks valid endpoints,
|
||||
rebuilds symmetric edges, preserves revision/evaluation time and checks storage
|
||||
capacity and consumer allocation. Transient `_last_report` is cleared on normal
|
||||
restore and is not part of Runtime snapshot equality. The derived
|
||||
`related_facility_operating` flag is measured separately and checked after restore.
|
||||
|
||||
This source-only dispatch authorizes no worker execution. Root stages the three
|
||||
files, creates the r28 plan with --check-only, then collects once to the fresh
|
||||
r28 output. Do not read or write r24 outputs, cleanup, stages or plans.
|
||||
Do not rerun S1-S3, C or full178. No engine copy, signing, xattr,
|
||||
codesign call or security-setting change is part of this procedure.
|
||||
Native evidence, registered validation, independent review, support53cb rebind
|
||||
and formal acceptance remain pending. D05 remains pending.
|
||||
|
||||
## REPAIR26 receipt lifecycle audit (static; NOT RUN)
|
||||
|
||||
All names below are relative to the fresh output unless marked otherwise.
|
||||
`r.write` is the unchanged exclusive `open('x')` writer in
|
||||
`tools/run_all_tests.py`. No retry, overwrite, replace or atomic rename is used
|
||||
for a JSON proof. A write attempt may fail or leave a partial file; it is never
|
||||
retried. “At most once” describes wrapper control flow, not a durability claim.
|
||||
The initial input is serialized once with `engine=engine_descriptor`,
|
||||
`engine_identity_before=engine_descriptor` and a deep-copied initial identity.
|
||||
`repin()` itself is unchanged (it returns None); each per-phase receipt uses
|
||||
the descriptor it just appended and the index of that exact observation.
|
||||
|
||||
| Writer / destination | Maximum attempts per run / uniqueness | Success and failure reachability |
|
||||
| --- | --- | --- |
|
||||
| `r.write(args.plan)` outside output | 1, check-only branch returns immediately | Native branch only reads the exact plan; existing plan rejected in check-only |
|
||||
| `runner-started.json` | 1 | First write inside protected setup; exception enters cleanup |
|
||||
| `source_before.json` | 1 | Setup before source copy; no fallback write |
|
||||
| `selected-source-map.json` | 1 | Setup before source copy; no fallback write |
|
||||
| `r.copy_source` to `stage/<source name>` | 1 per unique source-map key | Fresh stage; loop stops on setup failure; source files are not proof receipts |
|
||||
| `write_bytes` to `bootstrap/<name>` | 1 per pinned bootstrap key | The reviewed map names `profile_guard.gd` and `parse_full_dependencies.gd`; setup only |
|
||||
| `/bin/cp -c` to `stage/.godot/<name>` | 1 per cache inventory key | Optional eligible-cache branch only; fresh mode never accesses cache |
|
||||
| `cache-reuse.json` | 0 or 1 | Only after all optional cache clones and guards |
|
||||
| `parse-paths.json` | 1 | Setup after 316-script guard |
|
||||
| `contracts.json` | 1 | Setup after unchanged suite-policy guard |
|
||||
| `runner-input.json` | 1 | Setup; no final read/rewrite; absent if earlier setup fails |
|
||||
| `engine-identity-before-<phase>.json` | 1 each for import, parse, writer, reader | Immediately after that phase's repin; failure stops loop, never resumes it |
|
||||
| `write_bytes` to `writer-combined-raw.log`, `reader-combined-raw.log` | 1 each | Binary copy immediately after returned phase row, even a failed row if it has a log; later phases never reuse names |
|
||||
| `<phase>-actual-phase.json` | 1 each for four unique phases | Before `require(row['passed'])`; mid-loop failure retains its row and stops later phases |
|
||||
| `outer-owned-finally.json` | 1 | Single finally after setup/loop success or failure; cleanup called once; cleanup exception recorded; receipt write exception becomes guard error |
|
||||
| `source_after.json` | 0 or 1 | Single post-cleanup guarded block, after fixed_guard/source_refs succeed; later guard failure does not re-enter or retry the write |
|
||||
| `shutil.copyfile` to `formal/<role>/<slot>/<member>` | 1 per tuple | Only preliminary pass; two unique roles, unique slot map keys, four unique member keys, fresh target directory; exception stops collection |
|
||||
| `formal-relocation.json` | 0 or 1 | Only after successful complete formal mirror |
|
||||
| `raw-inventory.json` | 0 or 1 | Once after mirror branch, including setup/phase/cleanup failure; earlier output-collection exception can skip it |
|
||||
| `PID-set.json` | 0 or 1 | Once after raw inventory; earlier output-collection exception can skip it |
|
||||
| `engine-identity.json` | 1 | One final attempt after cleanup and both after-cleanup/after-output-collection repin attempts, including caught setup/phase/cleanup/collection failures; own write failure records guard error and makes both passed fields false |
|
||||
| `execution.json` | 1 | After identity attempt, with final observations, identity and guard errors |
|
||||
| `runner-ended.json` | 1 | After execution; no retry if final receipt storage itself fails |
|
||||
|
||||
The wrapper has no `write_text` call. The unchanged `execute_phase` dependency
|
||||
also writes the following. Its shared text log is an explicit exception to
|
||||
write-once naming, and is not presented as two independent phase proofs.
|
||||
|
||||
| Dependency destination | Writer and lifecycle |
|
||||
| --- | --- |
|
||||
| `<phase>-startup-profile-before.json` | Exclusive `write`, once per distinct phase before engine start |
|
||||
| `<phase>-profile.json` | Guard report path for parse/writer/reader, one per phase; actual Guard checks file absence before writing |
|
||||
| `starters/<phase>.gd` | `write_text`, once each for writer/reader |
|
||||
| `<phase>/combined-raw.log` | `write_text`, once each for import/parse |
|
||||
| `tests__s3__test_runtime_staff_assignment.gd.log` (general `out/<test>.log`) | Both writer and reader call `write_text` on this same per-test name; reader replaces the shared text. Wrapper preserves separate binary copies as `writer-combined-raw.log` and `reader-combined-raw.log`, retaining `strict_runner_log_path` and the original log hash |
|
||||
| `<phase>-receipt.json` | Exclusive `write`, once per phase after its finally and guards |
|
||||
| `stage/project.godot` | `write_bytes` for private startup settings and restoration in each phase finally; mutable private source, not immutable proof |
|
||||
| `<phase>/**`, private profiles, raw QA outputs | Delegated owned-native capture/engine/QA outputs; not wrapper `r.write`/`write_bytes`/`write_text` calls; their existing ownership/diagnostic/formal guards remain unchanged |
|
||||
|
||||
Path coverage: success traverses each distinct phase once. A failed parse row is
|
||||
recorded, then `require(row['passed'])` jumps to the single finally; no writer or
|
||||
reader names are used. A protected setup failure also enters that finally, then
|
||||
attempts post-cleanup guards and final identity/execution/end receipts. Cleanup
|
||||
exceptions are caught separately, set `cleanup_errors`, and do not prevent final
|
||||
receipts. A post-cleanup fixed/source guard failure is caught without retrying
|
||||
`source_after`; formal/output-collection exceptions now become guard errors and
|
||||
still reach the final repin and identity receipt. A final identity write failure
|
||||
is caught without touching existing bytes and forces failure in execution/end.
|
||||
|
||||
Admission failures before the protected setup (invalid plan/product/engine,
|
||||
live engine, insufficient disk, existing output, directory creation failure)
|
||||
remain fail-closed and do not promise final receipts. They have no repeated
|
||||
wrapper proof write. Persistent storage failure writing execution/end can still
|
||||
prevent a complete terminal receipt pair; no code can guarantee receipt storage
|
||||
when that storage fails. Such runs cannot provide accepted evidence.
|
||||
|
||||
## Fixture scope and remaining gaps
|
||||
|
||||
`receipt_lifecycle_fixtures.py` imports the real wrapper and standard writer.
|
||||
It builds 1338 synthetic source entries including 316 scripts in resolved fresh
|
||||
temporary paths; engine identity, template_guard, disk/default-player, helpers
|
||||
and phase execution are mocked. Four distinct fake PIDs and complete synthetic
|
||||
formal slot members exercise wrapper guards and copying without starting an
|
||||
engine. The actual identity section of `S4Series.__init__` is compiled from its
|
||||
AST, and actual `verify_s4_engine` checks a non-executable temporary data file
|
||||
under fixture-only patched constants. Other verifier sections are not claimed
|
||||
as covered by this synthetic fixture.
|
||||
|
||||
Cases cover success after check-only, parse failure, cleanup exception, setup
|
||||
copy failure, post-cleanup collection failure and final identity write failure.
|
||||
Every exclusive write attempt is counted. Input bytes are compared with their
|
||||
creation bytes before negative mutation. Fresh-output refusal, each missing or
|
||||
tampered phase receipt and mismatched initial-before are negative checks. The
|
||||
fixture's deliberate proof mutations are confined to its disposable temporary
|
||||
files. No registered plan/output/stage is read or changed.
|
||||
|
||||
Reasoning only about the 7af6 wrapper: import writes `engine-identity.json` via
|
||||
exclusive create. Before parse, the same name raises FileExistsError, so the
|
||||
four-phase success assertion cannot pass. After cleanup the old final write
|
||||
tries that same name again and escapes main, preventing final execution/end.
|
||||
Even if that collision alone were removed, the old runner-input final rewrite
|
||||
would hit the exclusive writer, and the required initial/per-phase receipts
|
||||
are absent. No 7af6 execution was performed to establish this reasoning.
|
||||
|
||||
Worker performed source inspection and file edits only: no fixtures, check-only,
|
||||
tests, Git, engines, Runner, agents or commits. Root still must run both fixtures
|
||||
and check-only before any native r27 collection, then exact-version verification
|
||||
and independent review. The registered validation gate remains unchanged at
|
||||
7af6, exit 2, fresh MANIFEST missing; this is the supplied gate record, not a new
|
||||
worker run. Native/product proof, full formal checks, D05 and original P04-01
|
||||
acceptance remain pending. This repair is submitted preparation, not completion.
|
||||
|
||||
|
||||
## REPAIR27 snapshot staff expectation (source-only; NOT RUN)
|
||||
|
||||
Root supplied the r26 result: 15 failures of 934 S4 checks, all at
|
||||
no shift confound; engine-identity/lifecycle fixtures and cleanup passed.
|
||||
This is supplied history, not a worker execution. Preserve r26 raw output,
|
||||
formal records, logs, source stage and plan unchanged.
|
||||
|
||||
The r27 probe SHA256 is `b5e5c81a0762427880416759e30e0a0144ce26749f1db1f26837c3287eeecf35`.
|
||||
FacilityInstance.create_snapshot serializes active_staff_ids through
|
||||
_names_to_strings as Array[String]. The snapshot expectation is now explicit
|
||||
SNAPSHOT_SERVERS: Array[String], containing exactly server a then server b.
|
||||
Runtime registration and attendance still use unchanged SERVERS:
|
||||
Array[StringName]. No values are normalized, fields dropped or invariants
|
||||
relaxed. Inspection of all S4 StringName constant uses found no other snapshot
|
||||
comparison with this mismatch; reservation IDs already compare with
|
||||
String(request_id). S3 lines 111–118 compare live runtime values and remain
|
||||
unchanged.
|
||||
|
||||
The wrapper dynamically pins QA bytes through pin(QA) into the plan and
|
||||
selected source map. S4Series dynamically hashes the current probe and checks
|
||||
the exact receipt/map binding; only its RUNS['s4'] binding advances to r27.
|
||||
The documented hash does not replace dynamic verification.
|
||||
Engine identity and receipt lifecycle logic, the a61 checker, frozen C and all
|
||||
1338 product inputs remain unchanged. The lifecycle mock's disposable output
|
||||
and plan names advance to r27; its behavior is unchanged.
|
||||
|
||||
Worker performed source inspection, edits and SHA256 calculation only. No
|
||||
fixtures, tests, check-only, Git, engine, Runner, agents or commits were run.
|
||||
Root re-runs only the fresh S4 collection and any needed fixture validation,
|
||||
using the r27 commands above. Exact-version evidence, registered validation,
|
||||
independent review, D05 and original acceptance remain pending; this is
|
||||
submitted preparation, not completion.
|
||||
|
||||
## REPAIR28 phase cut (source-only; NOTRUN)
|
||||
|
||||
Root's supplied r27 own-B diagnosis is preserved as history: import, parse and
|
||||
Writer passed (934 checks, zero failures); Reader ran 1445 checks with exactly
|
||||
one failure, complete phase count. ACTUAL_TYPED_PHASE_DIAGNOSIS.json reports
|
||||
all 121 actual Reader phase .var files byte-identical to matching Writer files.
|
||||
This worker did not open or rerun those raw materials. Keep all r27 failed raw,
|
||||
formals, logs, source stages, plans and diagnosis unchanged.
|
||||
|
||||
The kernel applies commands at the old minute, advances absolute_minute, then
|
||||
emits TIME_BOUNDARIES and the remaining phases. The new helper includes minute
|
||||
960 APPLY_COMMANDS using the enum key expression used by the Writer. It also
|
||||
includes all later rows. Before byte comparison the probe asserts the exact
|
||||
121-entry kernel roster, uniqueness, and Writer order. Both existing complete
|
||||
phase count and complete typed var_to_bytes equality checks remain unchanged.
|
||||
Writer row data, typed staff fix, runtime SERVERS, Clock and raw format stay
|
||||
unchanged.
|
||||
|
||||
The current RUNS s4 binding, wrapper defaults and disposable lifecycle fixture
|
||||
path names use r28. Only these path strings change in the wrapper and lifecycle
|
||||
fixture; their engine/lifecycle behavior is unchanged. Stage the three current
|
||||
files using the commands at the top to create fresh source-r28, plan-r28 and
|
||||
native-r28 paths. Do not reuse r27 as passing evidence.
|
||||
|
||||
Regression control status: NOTRUN. Root supplies full paths to ALL 132 actual
|
||||
r27 Writer phase .var files and ALL 121 actual Reader phase .var files:
|
||||
python3 "$E/s4/phase_cut_regression.py" --writer <full Writer phase paths...> --reader <full Reader phase paths...>
|
||||
Use actual own-B raw paths supplied by Root, not fabricated fixtures or old
|
||||
external provenance paths. Paths may be in any argument order; the control
|
||||
reconstructs kernel order from typed minute/phase, checks the complete Writer
|
||||
and Reader rosters, then compares unchanged full bytes. It requires old cut
|
||||
120/rejected, new cut 121/equal, deleted boundary APPLY_COMMANDS/rejected and
|
||||
duplicate/rejected. Mutations are in-memory lists only. Its result is a raw
|
||||
regression control, never an engine/native/acceptance PASS. Missing raw paths
|
||||
remain a Root execution input, not a worker-generated substitute.
|
||||
|
||||
Worker made source edits and calculated bindings only; no tests, fixtures,
|
||||
Git, engine, Runner, agents or commits. The normal validation on submitted
|
||||
55f1fb49aaefa5c1b86de1c3eda72392ef6b58f2 ran once and exited 2 because the current
|
||||
MANIFEST is missing (Root-supplied history). The gate, checker a61, 1338 inputs,
|
||||
C/S1-3/full178 artifacts, five carried formal review findings, and frozen
|
||||
original 13/88 main 9b remain unchanged. D05, independent review, exact-version
|
||||
native proof and original unit acceptance remain pending. This repair does
|
||||
not complete the unit.
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only engine identity controls; tests NOT RUN by worker."""
|
||||
import copy
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
HERE = Path(__file__).resolve().parent
|
||||
spec = importlib.util.spec_from_file_location("series", HERE.parent / "p04_native_series.py")
|
||||
series = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(series)
|
||||
spec = importlib.util.spec_from_file_location("s4", HERE / "run_s4_native.py")
|
||||
s4 = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(s4)
|
||||
|
||||
|
||||
def run():
|
||||
# Read only the real installed executable. Never copy or write an engine.
|
||||
pin = s4.engine_pin(s4.ENGINE)
|
||||
identity = dict(before=pin, during=[pin], after=pin)
|
||||
results = []
|
||||
|
||||
def reject(label, action):
|
||||
try:
|
||||
action()
|
||||
except (ValueError, OSError, KeyError, TypeError) as caught:
|
||||
results.append(dict(case=label, result="rejected", error=str(caught)))
|
||||
else:
|
||||
raise AssertionError("negative accepted: " + label)
|
||||
|
||||
# Same bytes/SHA cannot admit an own-B bare copy or another outside-B path.
|
||||
for label, path in [
|
||||
("copied bare engine in B with same SHA", s4.BUILD / "audit-support/p04-s4-source-r26/Godot"),
|
||||
("other outside-B path", Path("/Applications/OtherGodot.app/Contents/MacOS/Godot")),
|
||||
]:
|
||||
other = dict(pin, path=str(path))
|
||||
reject(label, lambda other=other: series.verify_s4_engine(other, identity))
|
||||
reject(label + " wrapper", lambda path=path: s4.engine_pin(path))
|
||||
reject("SHA mismatch", lambda: series.verify_s4_engine(dict(pin, sha256="0" * 64), identity))
|
||||
with patch.object(s4, "ENGINE_SHA256", "0" * 64):
|
||||
reject("wrapper SHA mismatch", lambda: s4.engine_pin(s4.ENGINE))
|
||||
changed = copy.deepcopy(identity)
|
||||
changed["after"]["inode"] += 1
|
||||
reject("after-identity mismatch", lambda: series.verify_s4_engine(pin, changed))
|
||||
reject("wrapper after-identity mismatch",
|
||||
lambda: s4.engine_pin(s4.ENGINE, dict(pin, inode=pin["inode"] + 1)))
|
||||
series.verify_s4_engine(pin, identity)
|
||||
results.append(dict(case="exact installed path", result="accepted"))
|
||||
return dict(scope="read-only engine identity fixtures", results=results, formal_acceptance=False)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(json.dumps(run(), indent=2, sort_keys=True))
|
||||
@@ -0,0 +1,452 @@
|
||||
extends "res://tests/s3/test_runtime_actor_hardening.gd"
|
||||
|
||||
const CASES: Array[String] = ["S4"]
|
||||
const SEEDS: Dictionary = {"S4": 420440}
|
||||
const GUEST: StringName = &"p04.s4.guest.a"
|
||||
const REQUEST: StringName = &"p04.s4.request.a"
|
||||
const QUEUED_GUEST: StringName = &"p04.s4.guest.b"
|
||||
const QUEUED_REQUEST: StringName = &"p04.s4.request.b"
|
||||
const SERVERS: Array[StringName] = [&"p04.order.server.a", &"p04.order.server.b"]
|
||||
const SNAPSHOT_SERVERS: Array[String] = ["p04.order.server.a", "p04.order.server.b"]
|
||||
var _evidence_dir: String = ""
|
||||
var _mode: String = ""
|
||||
var _case: String = ""
|
||||
var _serial: int = 0
|
||||
var _facility_id: StringName = &""
|
||||
var _service_id: StringName = &""
|
||||
var _boundary: int = 960
|
||||
var _price: int = 0
|
||||
var _duration: int = 0
|
||||
var _patience: int = 0
|
||||
var _reserve: int = 0
|
||||
var _recovery: Dictionary = {}
|
||||
var _commands: Array = []
|
||||
var _results: Array = []
|
||||
var _receipts: Array = []
|
||||
var _phases: Array = []
|
||||
var _points: Dictionary = {}
|
||||
var _branches: Dictionary = {}
|
||||
var _slots: Dictionary = {}
|
||||
|
||||
func _run() -> void:
|
||||
_evidence_dir = OS.get_environment("THE_TREE_S4_EVIDENCE")
|
||||
_mode = OS.get_environment("THE_TREE_S4_MODE")
|
||||
if _evidence_dir.is_empty() or _mode not in ["writer", "reader"]:
|
||||
quit(2)
|
||||
return
|
||||
_expect(DirAccess.make_dir_recursive_absolute(_evidence_dir) == OK, "external evidence directory")
|
||||
if _mode == "writer":
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
_writer_case()
|
||||
else:
|
||||
_reader()
|
||||
_write(_mode, {&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), &"slots": _slots,
|
||||
&"branches": _branches, &"checks": _checks, &"failures": _failures,
|
||||
&"whole_goal_accepted": false, &"unit_accepted": false}, true)
|
||||
_cleanup()
|
||||
print("S4 power: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(1 if _failures else 0)
|
||||
|
||||
func _fresh() -> void:
|
||||
_serial = 0
|
||||
_facility_id = &""
|
||||
_service_id = &""
|
||||
_boundary = 960
|
||||
_price = 0
|
||||
_duration = 0
|
||||
_patience = 0
|
||||
_reserve = 0
|
||||
_recovery = {}
|
||||
_commands = []
|
||||
_results = []
|
||||
_receipts = []
|
||||
_phases = []
|
||||
_points = {}
|
||||
_reset_runtime(SEEDS[_case])
|
||||
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_runtime.command_completed.connect(func(result: CommandResult) -> void:
|
||||
_results.append({&"minute": _clock.absolute_minute, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "actual public command completion " + str(result.command_id))
|
||||
)
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary, at: int = -1) -> CommandResult:
|
||||
_serial += 1
|
||||
var key: StringName = StringName("p04.s4.%s.command.%d" % [_case, _serial])
|
||||
var result: CommandResult = _runtime.submit_command(kind, payload, &"player", &"", 10, at, key)
|
||||
_commands.append({&"serial": _serial, &"kind": kind, &"payload": payload.duplicate(true),
|
||||
&"at": at, &"issued_at": _clock.absolute_minute, &"idempotency_key": key, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "public command " + String(kind))
|
||||
return result
|
||||
|
||||
func _queue(guest_id: StringName, request_id: StringName, exit_at: int) -> void:
|
||||
_cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"gold": _price * 2,
|
||||
&"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO, &"planned_exit_at": exit_at}})
|
||||
_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": request_id, &"guest_ids": [guest_id]})
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(guest_id)
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(request_id)
|
||||
_expect(guest != null and request != null, "actual Guest and request exist")
|
||||
if guest == null or request == null: return
|
||||
_expect(guest.state == GuestRuntime.State.QUEUING and guest.queue_request_id == request_id, "actual queued ownership before advancing clock")
|
||||
_expect(request.quoted_price == _price and request.service_minutes == _duration and request.reserved_inventory_ids.size() == 1, "definition price/duration and one meal reservation")
|
||||
for id: StringName in request.reserved_inventory_ids:
|
||||
var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id)
|
||||
_expect(reservation != null and reservation.is_active() and reservation.requester_id == request_id, "public queue actually reserves meal")
|
||||
|
||||
func _prepare() -> bool:
|
||||
_fresh()
|
||||
var facility: FacilityInstance = _build_cafeteria(true)
|
||||
if facility == null: return false
|
||||
_facility_id = facility.instance_id
|
||||
var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id)
|
||||
_price = definition.base_price
|
||||
_duration = definition.base_service_minutes
|
||||
_patience = _runtime.guest_service.get_definition(&"traveler").base_patience
|
||||
for id: StringName in SERVERS:
|
||||
_cmd(&"register_staff", {&"staff_id": id, &"role_id": &"server", &"options": {&"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i.ZERO}})
|
||||
_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"DAY", &"facility_id": _facility_id})
|
||||
_clock.advance_minutes(_boundary - _duration - 1 - _clock.absolute_minute)
|
||||
_expect(facility.active_staff_ids == SERVERS, "normal public staff attendance")
|
||||
_expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.s4.stock"), "one actual meal")
|
||||
_queue(GUEST, REQUEST, _boundary + _duration)
|
||||
_clock.advance_one_minute()
|
||||
var active: Array = _runtime.facility_service.get_active_services(_facility_id)
|
||||
_expect(active.size() == 1, "one started service")
|
||||
if active.size() != 1: return false
|
||||
var service: ServiceInstance = active[0]
|
||||
_service_id = service.service_id
|
||||
_expect(service.started_at + _duration == _boundary, "source definition duration aligns completion")
|
||||
_clock.advance_minutes(_boundary - 1 - _clock.absolute_minute)
|
||||
var graph: Dictionary = _runtime.mana_network_service.create_snapshot()
|
||||
var adjacent: Array = []
|
||||
for edge: Array in graph[&"edges"]:
|
||||
if edge.has(String(_facility_id)):
|
||||
adjacent.append(edge[1] if edge[0] == String(_facility_id) else edge[0])
|
||||
_expect(adjacent.size() == 1, "actual sole cafeteria neighbour")
|
||||
if adjacent.size() != 1: return false
|
||||
_recovery = {&"neighbour": StringName(adjacent[0]), &"original_graph": graph,
|
||||
&"interval": ManaNetworkService.EVALUATION_INTERVAL_MINUTES,
|
||||
&"hold": ManaNetworkService.MINIMUM_OFF_HOLD_MINUTES}
|
||||
_expect(_boundary % int(_recovery[&"interval"]) == 0, "boundary is normal evaluation")
|
||||
_expect(_runtime.mana_network_service.disconnect_nodes(_facility_id, _recovery[&"neighbour"]), "public disconnect exact existing edge")
|
||||
_expect(_runtime.mana_network_service.validate_invariants()[&"valid"], "symmetric cut graph invariants")
|
||||
_reserve = _state.get_resource(&"mana")
|
||||
_expect(_reserve > 0 and service.elapsed_minutes == _duration - 1, "positive mana and natural duration-1 progress")
|
||||
return _clock.absolute_minute == _boundary - 1
|
||||
|
||||
func _connect_observations() -> void:
|
||||
_runtime.economy_service.transaction_posted.connect(func(id: StringName, entries: Array) -> void:
|
||||
var rows: Array = []
|
||||
for entry: LedgerEntry in entries: rows.append(entry.to_dictionary())
|
||||
_receipts.append({&"id": id, &"minute": _clock.absolute_minute, &"entries": rows})
|
||||
)
|
||||
_clock.tick_phase_started.connect(func(phase: int, minute: int) -> void:
|
||||
var label: String = "phase-%d-%s" % [minute, SimulationKernel.TickPhase.keys()[phase]]
|
||||
var row: Dictionary = _capture()
|
||||
row[&"phase"] = SimulationKernel.TickPhase.keys()[phase]
|
||||
_assert_phase(row)
|
||||
_phases.append(_write(label, row))
|
||||
)
|
||||
|
||||
func _assert_phase(point: Dictionary) -> void:
|
||||
# Every phase is retained as complete typed raw; semantics are checked at points.
|
||||
_expect(point[&"snapshot"][&"game_state"][&"resources"][&"mana"] > 0, "no global depletion")
|
||||
|
||||
func _capture() -> Dictionary:
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
var operations: Dictionary = snapshot[&"systems"][&"runtime"][&"operations"]
|
||||
var facility: FacilityInstance = _runtime.facility_service.get_instance(_facility_id)
|
||||
var service: ServiceInstance = _runtime.facility_service.get_service(_service_id)
|
||||
var event: EventInstance = _runtime.event_service.get_instance(&"p04.destroy.instance")
|
||||
var kitchen: FacilityInstance = _runtime.facility_service.get_instance(facility.related_facility_id)
|
||||
var actors: Dictionary = {}
|
||||
var requests: Dictionary = {}
|
||||
var wallets: Dictionary = {}
|
||||
for id: StringName in [GUEST, QUEUED_GUEST]:
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(id)
|
||||
actors[String(id)] = guest.create_snapshot() if guest != null else {}
|
||||
wallets[String(id)] = _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(id)))
|
||||
for id: StringName in [REQUEST, QUEUED_REQUEST]:
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(id)
|
||||
requests[String(id)] = request.create_snapshot() if request != null else {}
|
||||
var reservations: Array = []
|
||||
for reservation: InventoryReservation in _runtime.inventory_service.get_reservations(false): reservations.append(reservation.create_snapshot())
|
||||
var ledger: Array = []
|
||||
for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary())
|
||||
return {&"snapshot": snapshot, &"minute": _clock.absolute_minute,
|
||||
&"inputs": {&"case": _case, &"seed": SEEDS[_case], &"serial": _serial, &"facility_id": _facility_id, &"service_id": _service_id,
|
||||
&"boundary": _boundary, &"price": _price, &"duration": _duration, &"patience": _patience, &"reserve": _reserve, &"recovery": _recovery.duplicate(true),
|
||||
&"kitchen_premise": "ActorHardening physical FLEX fixture; not natural hire/unlock"},
|
||||
&"related_facility_operating": facility.related_facility_operating, &"kitchen": kitchen.create_snapshot(), &"graph_invariants": _runtime.mana_network_service.validate_invariants(),
|
||||
&"facility": facility.create_snapshot(), &"service": service.create_snapshot() if service != null else {},
|
||||
&"event": event.create_snapshot() if event != null else {}, &"guests": actors, &"requests": requests, &"wallets": wallets,
|
||||
&"reservations": reservations, &"ledger": ledger, &"receipts": _receipts.duplicate(true),
|
||||
&"player_cash": _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT),
|
||||
&"available_meals": _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal"),
|
||||
&"active_services": _runtime.facility_service.get_active_services(_facility_id).size(),
|
||||
&"operations_enabled": operations[&"enabled"], &"saved_coordinator": operations[&"coordinator"],
|
||||
&"actual_coordinator_absent": _runtime.operations_coordinator == null, &"unrelated_getters_called": false}
|
||||
|
||||
func _labels() -> Array[String]:
|
||||
return ["boundary", "held", "recovered"]
|
||||
|
||||
func _advance(label: String) -> void:
|
||||
if label == "duplicate_guard":
|
||||
var before_replay: Dictionary = _runtime.create_snapshot()
|
||||
var accepted: bool = _runtime.mana_network_service.connect_nodes(_facility_id, _recovery[&"neighbour"])
|
||||
var after_replay: Dictionary = _runtime.create_snapshot()
|
||||
_expect(accepted and var_to_bytes(before_replay) == var_to_bytes(after_replay), "public reconnect replay changes no Runtime field")
|
||||
_write("reconnect-replay", {&"before": before_replay, &"after": after_replay, &"accepted": accepted})
|
||||
if label == "recovered":
|
||||
_expect(_clock.absolute_minute == _boundary + 1, "reconnect only after held observation")
|
||||
_expect(_runtime.mana_network_service.connect_nodes(_facility_id, _recovery[&"neighbour"]), "reconnect exact cut edge")
|
||||
var graph: Dictionary = _runtime.mana_network_service.create_snapshot()
|
||||
_expect(var_to_bytes(graph[&"edges"]) == var_to_bytes(_recovery[&"original_graph"][&"edges"]), "all original edges restored")
|
||||
_expect(_runtime.mana_network_service.validate_invariants()[&"valid"], "reconnected graph invariants")
|
||||
var node: Dictionary = _runtime.mana_network_service.get_node(_facility_id)
|
||||
var resume_at: int = int(node[&"hold_until"])
|
||||
var interval: int = int(_recovery[&"interval"])
|
||||
while resume_at % interval != 0: resume_at += 1
|
||||
_expect(resume_at > _clock.absolute_minute and resume_at < _boundary + _duration, "finite normal hold/evaluation recovery before exit")
|
||||
while _clock.absolute_minute < resume_at:
|
||||
_clock.advance_one_minute()
|
||||
if _clock.absolute_minute < resume_at: _assert_stopped(_capture())
|
||||
else:
|
||||
_clock.advance_one_minute()
|
||||
|
||||
func _writer_case() -> void:
|
||||
if not _prepare(): return
|
||||
var before: Dictionary = _capture()
|
||||
_assert_before(before)
|
||||
_points["before"] = _write("before", before)
|
||||
_connect_observations()
|
||||
for label: String in _labels():
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
_assert_outcome(before, actual)
|
||||
_save_point(label, actual)
|
||||
_advance("duplicate_guard")
|
||||
var guard: Dictionary = _capture()
|
||||
_assert_outcome(before, guard)
|
||||
_points["duplicate_guard"] = _write("duplicate_guard", guard)
|
||||
_branches[_case] = _write("case", {&"points": _points.duplicate(true), &"phases": _phases.duplicate(true),
|
||||
&"commands": _write("commands", {&"submitted": _commands, &"completed_including_fixture": _results})})
|
||||
|
||||
func _assert_before(point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
_expect(point[&"service"][&"state"] == "ACTIVE" and point[&"facility"][&"power_available"], "before normal projection remains powered")
|
||||
_expect(point[&"service"][&"elapsed_minutes"] == _duration - 1 and point[&"reservations"].size() == 1, "natural duration-1 and one reservation")
|
||||
|
||||
func _assert_unconfounded(point: Dictionary) -> void:
|
||||
_expect(not point[&"operations_enabled"] and point[&"actual_coordinator_absent"], "existing isolated fixture")
|
||||
_expect(point[&"facility"][&"operator_open"] and point[&"related_facility_operating"], "open operator and operating dependency")
|
||||
_expect(point[&"facility"][&"durability"] > 0 and point[&"event"].is_empty(), "no destruction event")
|
||||
_expect(point[&"kitchen"][&"power_available"] and point[&"kitchen"][&"state"] == "OPEN", "kitchen stays active/powered")
|
||||
_expect(point[&"snapshot"][&"game_state"][&"resources"][&"mana"] > 0, "positive global reserve")
|
||||
_expect(point[&"facility"][&"active_staff_ids"] == SNAPSHOT_SERVERS, "no shift confound")
|
||||
if point[&"service"][&"state"] != "COMPLETED":
|
||||
_expect(point[&"guests"][String(GUEST)][&"planned_exit_at"] > point[&"minute"] and point[&"guests"][String(GUEST)][&"state"] == "RECEIVING_SERVICE", "no exit at outage/hold")
|
||||
_expect(point[&"graph_invariants"][&"valid"], "graph allocation/storage/symmetry valid")
|
||||
_expect(_runtime.mana_network_service.get_consumer_state(StringName(point[&"kitchen"][&"instance_id"])) == ManaNetworkService.ConsumerState.ACTIVE, "kitchen network consumer ACTIVE")
|
||||
|
||||
func _assert_stopped(point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
_expect(not point[&"facility"][&"power_available"] and point[&"service"][&"state"] == "PAUSED" and point[&"service"][&"interruption_reason"] == "NO_POWER", "actual isolated NO_POWER path")
|
||||
_expect(point[&"service"][&"elapsed_minutes"] == _duration - 1 and point[&"receipts"].is_empty(), "stopped progress held and no payment")
|
||||
|
||||
func _assert_reservation(before: Dictionary, point: Dictionary, request_id: StringName, state: String, terminal: int) -> void:
|
||||
var prior: Array = []
|
||||
var current: Array = []
|
||||
for row: Dictionary in before[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): prior.append(row)
|
||||
for row: Dictionary in point[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): current.append(row)
|
||||
_expect(prior.size() == 1 and current.size() == 1, "one original reservation per request")
|
||||
if prior.size() != 1 or current.size() != 1: return
|
||||
_expect(prior[0][&"reservation_id"] == current[0][&"reservation_id"] and current[0][&"state"] == state and current[0][&"terminal_at"] == terminal, "same reservation ID/state/terminal time; no second release")
|
||||
if state == "ACTIVE":
|
||||
_expect(var_to_bytes(prior[0]) == var_to_bytes(current[0]), "entire held reservation unchanged")
|
||||
var request: Dictionary = point[&"requests"][String(request_id)]
|
||||
_expect(not request.is_empty() and request[&"reserved_inventory_ids"].has(current[0][&"reservation_id"]), "active reservation has live request owner")
|
||||
else:
|
||||
_expect(point[&"requests"][String(request_id)].is_empty(), "terminal reservation has no orphan request")
|
||||
if state == "RELEASED":
|
||||
var release_count: int = 0
|
||||
var inventory: Dictionary = point[&"snapshot"][&"systems"][&"runtime"][&"inventory"]
|
||||
for operation: Dictionary in inventory[&"idempotency_results"]:
|
||||
var result: Dictionary = operation[&"result"]
|
||||
if operation[&"action"] == "release" and String(result.get(&"reservation_id", "")) == current[0][&"reservation_id"]:
|
||||
release_count += 1
|
||||
_expect(bool(result.get(&"ok", false)) and result.get(&"quantity", 0) == current[0][&"quantity"], "release returned actual reserved quantity, not an orphan repair/retry")
|
||||
_expect(release_count == 1, "one raw inventory release operation for original reservation")
|
||||
|
||||
func _assert_outcome(before: Dictionary, point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
var service: Dictionary = point[&"service"]
|
||||
_expect(service[&"service_id"] == before[&"service"][&"service_id"], "same service ID")
|
||||
var paid: bool = service[&"state"] == "COMPLETED"
|
||||
if not paid:
|
||||
_assert_stopped(point)
|
||||
_expect(var_to_bytes(point[&"ledger"]) == var_to_bytes(before[&"ledger"]), "zero stopped ledger delta")
|
||||
_assert_reservation(before, point, REQUEST, "ACTIVE", -1)
|
||||
else:
|
||||
var node: Dictionary = _runtime.mana_network_service.get_node(_facility_id)
|
||||
_expect(point[&"facility"][&"power_available"] and service[&"elapsed_minutes"] == _duration and service[&"completed_at"] >= int(node[&"hold_until"]), "resume and complete after hold")
|
||||
_expect(point[&"receipts"].size() == 1, "one payment only")
|
||||
var expected: Array = before[&"ledger"].duplicate(true)
|
||||
for receipt: Dictionary in point[&"receipts"]: expected.append_array(receipt[&"entries"])
|
||||
_expect(var_to_bytes(expected) == var_to_bytes(point[&"ledger"]), "whole ledger exactly one receipt")
|
||||
_assert_reservation(before, point, REQUEST, "CONSUMED", service[&"completed_at"])
|
||||
_expect(point[&"wallets"][String(GUEST)] == _price * (1 if paid else 2) and point[&"player_cash"] == before[&"player_cash"] + (_price if paid else 0), "exact one price or zero")
|
||||
|
||||
func _save_point(label: String, point: Dictionary) -> void:
|
||||
_points[label] = _write(label, point)
|
||||
var path: String = "user://saves/p04_s4/" + _case + "-" + label
|
||||
var saved: Dictionary = SaveService.save_snapshot(path, point[&"snapshot"])
|
||||
var inspected: Dictionary = SaveService.inspect_snapshot(path)
|
||||
_expect(bool(saved.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "public formal save/inspect")
|
||||
_expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(point[&"snapshot"]), "all typed fields preserved by formal package")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(point[&"snapshot"]), "formal saving leaves Runtime unchanged")
|
||||
_slots[_case + "-" + label] = ProjectSettings.globalize_path(path)
|
||||
# Snapshot bytes exist once in point raw and once in the required formal
|
||||
# package; indexes do not embed inspection/load copies of those bytes.
|
||||
|
||||
func _write(label: String, value: Variant, global_name: bool = false) -> Dictionary:
|
||||
var name: String = label if global_name else _mode + "-" + _case + "-" + label
|
||||
var reference: Dictionary = {}
|
||||
for extension: String in ["var", "json"]:
|
||||
var path: String = _evidence_dir.path_join(name + "." + extension)
|
||||
_expect(not FileAccess.file_exists(path), "raw point written exactly once " + name)
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
|
||||
_expect(file != null, "raw file " + name)
|
||||
if file == null: continue
|
||||
if extension == "var": file.store_buffer(var_to_bytes(value))
|
||||
else: file.store_string(JSON.stringify(value, "\t"))
|
||||
file.flush()
|
||||
file.close()
|
||||
reference[extension] = {&"path": path, &"sha256": FileAccess.get_sha256(path)}
|
||||
return reference
|
||||
|
||||
func _read(reference: Dictionary) -> Dictionary:
|
||||
var item: Dictionary = reference["var"]
|
||||
var path: String = item["path"]
|
||||
_expect(FileAccess.get_sha256(path) == item["sha256"], "raw typed SHA")
|
||||
var json_ref: Dictionary = reference["json"]
|
||||
_expect(FileAccess.get_sha256(json_ref["path"]) == json_ref["sha256"], "raw JSON SHA")
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
||||
_expect(file != null, "typed input readable")
|
||||
if file == null: return {}
|
||||
var value: Variant = bytes_to_var(file.get_buffer(file.get_length()))
|
||||
file.close()
|
||||
_expect(value is Dictionary, "typed input dictionary")
|
||||
return value if value is Dictionary else {}
|
||||
|
||||
func _restore_inputs(point: Dictionary) -> void:
|
||||
var inputs: Dictionary = point[&"inputs"]
|
||||
_expect(_serial == 0, "Reader starts with serial zero before importing external inputs")
|
||||
_serial = inputs[&"serial"]
|
||||
_facility_id = inputs[&"facility_id"]
|
||||
_service_id = inputs[&"service_id"]
|
||||
_boundary = inputs[&"boundary"]
|
||||
_price = inputs[&"price"]
|
||||
_duration = inputs[&"duration"]
|
||||
_patience = inputs[&"patience"]
|
||||
_reserve = inputs[&"reserve"]
|
||||
_recovery = inputs[&"recovery"].duplicate(true)
|
||||
|
||||
func _load_point(path: String, expected: Dictionary) -> bool:
|
||||
_fresh()
|
||||
_restore_inputs(expected)
|
||||
var loaded: Dictionary = SaveService.load_snapshot(path)
|
||||
_expect(bool(loaded.get(&"ok", false)), "public formal load")
|
||||
if not bool(loaded.get(&"ok", false)): return false
|
||||
_expect(var_to_bytes(loaded[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all saved typed fields equal Writer")
|
||||
var restored: bool = _runtime.restore_snapshot(loaded[&"snapshot"])
|
||||
_expect(restored, "public Runtime restore")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected[&"snapshot"]), "all restored typed fields equal Writer")
|
||||
return restored
|
||||
|
||||
func _is_replayed_phase(row: Dictionary) -> bool:
|
||||
return int(row[&"minute"]) > _boundary or (int(row[&"minute"]) == _boundary and row[&"phase"] == SimulationKernel.TickPhase.keys()[SimulationKernel.TickPhase.APPLY_COMMANDS])
|
||||
|
||||
func _assert_expected_phase_roster(references: Array) -> void:
|
||||
var expected_pairs: Array = []
|
||||
for minute: int in range(_boundary, _boundary + 11):
|
||||
for phase: int in range(SimulationKernel.TickPhase.size()):
|
||||
expected_pairs.append([minute if phase == SimulationKernel.TickPhase.APPLY_COMMANDS else minute + 1, SimulationKernel.TickPhase.keys()[phase]])
|
||||
var actual_pairs: Array = []
|
||||
var seen: Dictionary = {}
|
||||
for reference: Dictionary in references:
|
||||
var row: Dictionary = _read(reference)
|
||||
var pair: Array = [int(row[&"minute"]), row[&"phase"]]
|
||||
_expect(not seen.has(pair), "expected phase roster has no duplicate minute/phase")
|
||||
seen[pair] = true
|
||||
actual_pairs.append(pair)
|
||||
_expect(references.size() == 121 and expected_pairs.size() == 121, "expected phase roster exactly 11 phases x 11 replayed minutes")
|
||||
_expect(actual_pairs == expected_pairs, "expected phase roster in Writer/kernel order with no missing minute/phase")
|
||||
|
||||
func _reader() -> void:
|
||||
var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ)
|
||||
_expect(file != null, "Writer receipt")
|
||||
if file == null: return
|
||||
var writer: Dictionary = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
_expect(int(writer["pid"]) != OS.get_process_id() and writer["profile"] != OS.get_user_data_dir(), "independent Reader PID/profile")
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
var writer_case: Dictionary = _read(writer["branches"][branch])
|
||||
var hashes: Dictionary = {}
|
||||
var restored_refs: Dictionary = {}
|
||||
for label: String in _labels():
|
||||
var key: String = branch + "-" + label
|
||||
var original: String = writer["slots"][key]
|
||||
var target: String = ProjectSettings.globalize_path("user://saves/p04_s4/" + key)
|
||||
_expect(DirAccess.make_dir_recursive_absolute(target) == OK, "private Reader formal slot")
|
||||
var directory: DirAccess = DirAccess.open(original)
|
||||
_expect(directory != null, "Writer formal slot readable")
|
||||
if directory == null: return
|
||||
var members: PackedStringArray = directory.get_files()
|
||||
members.sort()
|
||||
_expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "all formal members enumerated")
|
||||
var member_hashes: Dictionary = {}
|
||||
for member: String in members:
|
||||
var sha: String = FileAccess.get_sha256(original.path_join(member))
|
||||
_expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy every formal member")
|
||||
var copied_sha: String = FileAccess.get_sha256(target.path_join(member))
|
||||
_expect(not sha.is_empty() and sha == copied_sha, "original/copy member SHA equality")
|
||||
member_hashes[member] = {&"original_before": sha, &"copy_before": copied_sha}
|
||||
hashes[key] = {&"original": original, &"copy": target, &"members": member_hashes}
|
||||
_slots[key] = target
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
if not _load_point(target, expected): return
|
||||
restored_refs[label] = _write(label + "-restored", _capture())
|
||||
var before: Dictionary = _read(writer_case[&"points"]["before"])
|
||||
var boundary: Dictionary = _read(writer_case[&"points"]["boundary"])
|
||||
if not _load_point(_slots[branch + "-boundary"], boundary): return
|
||||
_assert_stopped(_capture())
|
||||
_connect_observations()
|
||||
var replay_refs: Dictionary = {}
|
||||
for label: String in ["held", "recovered", "duplicate_guard"]:
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
_expect(var_to_bytes(actual) == var_to_bytes(expected), "replay complete typed point including Runtime, inputs, receipts " + label)
|
||||
_assert_outcome(before, actual)
|
||||
replay_refs[label] = _write(label + "-replay", actual)
|
||||
var expected_phases: Array = []
|
||||
for reference: Dictionary in writer_case[&"phases"]:
|
||||
if _is_replayed_phase(_read(reference)): expected_phases.append(reference)
|
||||
_assert_expected_phase_roster(expected_phases)
|
||||
_expect(_phases.size() == expected_phases.size(), "complete phase count")
|
||||
if _phases.size() == expected_phases.size():
|
||||
for index: int in range(_phases.size()):
|
||||
_expect(var_to_bytes(_read(_phases[index])) == var_to_bytes(_read(expected_phases[index])), "same phase order and all typed phase state")
|
||||
for key: String in hashes:
|
||||
var row: Dictionary = hashes[key]
|
||||
for member: String in row[&"members"]:
|
||||
var item: Dictionary = row[&"members"][member]
|
||||
item[&"original_after"] = FileAccess.get_sha256(String(row[&"original"]).path_join(member))
|
||||
item[&"copy_after"] = FileAccess.get_sha256(String(row[&"copy"]).path_join(member))
|
||||
_expect(item[&"original_before"] == item[&"original_after"] and item[&"copy_before"] == item[&"copy_after"], "original/copy unchanged after all loads and replay")
|
||||
_branches[branch] = _write("case", {&"restored": restored_refs, &"replay": replay_refs, &"phases": _phases,
|
||||
&"member_hashes": hashes, &"recovery_commands": _commands, &"replay_completed_commands": _results})
|
||||
@@ -0,0 +1,83 @@
|
||||
"""Read actual r27 phase .var paths only; no engine, fixtures or acceptance claim.
|
||||
|
||||
Root supplies every Writer and Reader phase file via --writer and --reader.
|
||||
Argument order is immaterial: observation order is reconstructed from kernel
|
||||
phase/minute keys, never from lexicographic filenames. Typed bytes stay intact.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
from p04_native_series import require, variant
|
||||
|
||||
PHASES = ('APPLY_COMMANDS', 'TIME_BOUNDARIES', 'MANA', 'CONSTRUCTION',
|
||||
'LOGISTICS', 'FACILITY_COMPLETION', 'VITALS_AND_ENVIRONMENT',
|
||||
'INCIDENTS_AND_EVENTS', 'FACILITY_QUEUES', 'AI_DECISIONS', 'PUBLISH_SNAPSHOT')
|
||||
BOUNDARY = 960
|
||||
ROSTER = [(minute if phase == PHASES[0] else minute + 1, phase)
|
||||
for minute in range(BOUNDARY, BOUNDARY + 11) for phase in PHASES]
|
||||
|
||||
|
||||
def load(paths, role):
|
||||
rows, pins = [], []
|
||||
for path in paths:
|
||||
require(path.is_absolute() and path.is_file() and not path.is_symlink(), 'full regular raw path required')
|
||||
require(path.name.startswith(role + '-S4-phase-') and path.suffix == '.var', 'actual role phase .var required')
|
||||
raw = path.read_bytes()
|
||||
node = variant(raw)
|
||||
minute, phase = node.at('minute'), node.at('phase')
|
||||
require(minute.kind == 2 and phase.kind == 4 and phase.value in PHASES, 'typed minute/phase')
|
||||
rows.append(((minute.value, phase.value), raw))
|
||||
pins.append(dict(path=str(path), bytes=len(raw), sha256=hashlib.sha256(raw).hexdigest()))
|
||||
rows.sort(key=lambda row: (row[0][0] + (row[0][1] == PHASES[0]), PHASES.index(row[0][1])))
|
||||
return rows, pins
|
||||
|
||||
|
||||
def validate(expected, reader):
|
||||
require(len(expected) == len(reader) == 121, 'complete phase count')
|
||||
require([key for key, _ in expected] == ROSTER, 'Writer roster missing/duplicate/order')
|
||||
require([key for key, _ in reader] == ROSTER, 'Reader roster missing/duplicate/order')
|
||||
require(all(a == b for a, b in zip(expected, reader)), 'same phase order and all typed phase bytes')
|
||||
|
||||
|
||||
def rejected(expected, reader):
|
||||
try:
|
||||
validate(expected, reader)
|
||||
except ValueError:
|
||||
return True
|
||||
raise ValueError('negative control unexpectedly accepted')
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--writer', nargs='+', type=Path, required=True)
|
||||
parser.add_argument('--reader', nargs='+', type=Path, required=True)
|
||||
args = parser.parse_args()
|
||||
writer, wpins = load(args.writer, 'writer')
|
||||
reader, rpins = load(args.reader, 'reader')
|
||||
require(len(writer) == 132 and len({key for key, _ in writer}) == 132, 'complete unique actual Writer input')
|
||||
require([key for key, _ in writer] ==
|
||||
[(minute if phase == PHASES[0] else minute + 1, phase)
|
||||
for minute in range(BOUNDARY - 1, BOUNDARY + 11) for phase in PHASES],
|
||||
'full Writer kernel roster')
|
||||
old = [row for row in writer if row[0][0] > BOUNDARY]
|
||||
new = [row for row in writer if row[0][0] > BOUNDARY or
|
||||
(row[0][0] == BOUNDARY and row[0][1] == PHASES[0])]
|
||||
require(len(old) == 120, 'old cut must produce 120')
|
||||
old_rejected = rejected(old, reader)
|
||||
validate(new, reader)
|
||||
require(new[0][0] == (BOUNDARY, PHASES[0]), 'boundary APPLY_COMMANDS first')
|
||||
deleted_rejected = rejected(new[1:], reader)
|
||||
duplicated_rejected = rejected([new[0], *new], reader)
|
||||
print(json.dumps(dict(status='CONTROL_CONFIRMED', engine_run=False, acceptance=False,
|
||||
old_count=len(old), old_rejected=old_rejected, new_count=len(new),
|
||||
all_typed_bytes_in_order=True, deleted_apply_rejected=deleted_rejected,
|
||||
duplicate_rejected=duplicated_rejected, writer=wpins, reader=rpins), indent=2))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only synthetic receipt lifecycle fixtures. No engine/process is started.
|
||||
|
||||
Uses the real main, exclusive JSON writer, source_refs and inventories. The
|
||||
identity section is compiled directly from S4Series.__init__, not reimplemented.
|
||||
This is not native/product/ownership/formal acceptance evidence.
|
||||
"""
|
||||
import ast
|
||||
from collections import Counter
|
||||
from contextlib import ExitStack, redirect_stdout
|
||||
import copy
|
||||
import importlib.util
|
||||
import io
|
||||
import json
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
HERE = Path(__file__).resolve().parent
|
||||
ROOT = next(p for p in HERE.parents if (p / 'tools/run_all_tests.py').is_file())
|
||||
|
||||
|
||||
def load(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
value = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(value)
|
||||
return value
|
||||
|
||||
|
||||
s4 = load('receipt_s4', HERE / 'run_s4_native.py')
|
||||
r = load('receipt_standard', ROOT / 'tools/run_all_tests.py')
|
||||
series = load('receipt_series', HERE.parent / 'p04_native_series.py')
|
||||
|
||||
|
||||
def identity_code():
|
||||
tree = ast.parse((HERE.parent / 'p04_native_series.py').read_text())
|
||||
cls = next(n for n in tree.body if isinstance(n, ast.ClassDef) and n.name == 'S4Series')
|
||||
init = next(n for n in cls.body if isinstance(n, ast.FunctionDef) and n.name == '__init__')
|
||||
start = next(i for i, n in enumerate(init.body) if isinstance(n, ast.Expr) and
|
||||
isinstance(n.value, ast.Call) and isinstance(n.value.func, ast.Name) and
|
||||
n.value.func.id == 'verify_s4_engine')
|
||||
end = next(i for i in range(start + 1, len(init.body))
|
||||
if "execution['error']" in ast.unparse(init.body[i]))
|
||||
return compile(ast.Module(body=init.body[start:end], type_ignores=[]), '<real S4 identity section>', 'exec')
|
||||
|
||||
|
||||
def verify_identity(out, descriptor):
|
||||
# The real engine verifier reads only the synthetic ordinary file here.
|
||||
# Production constants/functions on disk are never changed.
|
||||
with patch.object(series, 'S4_ENGINE_PATH', descriptor['path']), \
|
||||
patch.object(series, 'S4_ENGINE_SHA256', descriptor['sha256']):
|
||||
namespace = dict(vars(series), inputs=r.read(out / 'runner-input.json'),
|
||||
execution=r.read(out / 'execution.json'),
|
||||
self=SimpleNamespace(read=lambda name: (out / name).read_bytes()))
|
||||
exec(identity_code(), namespace)
|
||||
|
||||
|
||||
def rejected(action):
|
||||
try:
|
||||
action()
|
||||
except (ValueError, AssertionError, OSError, KeyError, TypeError):
|
||||
return
|
||||
raise AssertionError('negative fixture was accepted')
|
||||
|
||||
|
||||
def case(base, failure=None):
|
||||
product = base / 'product'
|
||||
support = base / 'support'
|
||||
staged = base / 'source'
|
||||
for path in (product, support, staged):
|
||||
path.mkdir(parents=True)
|
||||
|
||||
def put(path, data):
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
|
||||
# Real 1338-entry source_refs and 316-entry script roster, synthetic bytes.
|
||||
scripts = [s4.TEST, 'tests/transport/test_runtime_transport.gd',
|
||||
'tests/staff/test_rescue_elevator.gd']
|
||||
scripts += ['scripts/fixture_%03d.gd' % i for i in range(313)]
|
||||
for name in scripts:
|
||||
put(product / name, b'extends Node\n')
|
||||
for name in ('project.godot', 'export_presets.cfg', 'Makefile', 'AGENTS.md'):
|
||||
put(product / name, b'fixture\n')
|
||||
put(product / 'tools/run_all_tests.py', (ROOT / 'tools/run_all_tests.py').read_bytes())
|
||||
for i in range(1017):
|
||||
put(product / ('data/fixture_%04d.txt' % i), b'fixture\n')
|
||||
assert len(r.source_refs(product)) == 1338
|
||||
qa = staged / 'p04_s4_power_probe.gd'
|
||||
put(qa, b'extends Node\n# synthetic override\n')
|
||||
put(staged / 'run_s4_native.py', (HERE / 'run_s4_native.py').read_bytes())
|
||||
put(staged / 'S4_ROOT_RUN.md', b'synthetic fixture material\n')
|
||||
contracts = base / 'contracts.json'
|
||||
r.write(contracts, {})
|
||||
material = base / 'material.json'
|
||||
r.write(material, {})
|
||||
r.write(support / 'suite-policy.json', {'suite_policies': {}})
|
||||
r.write(support / 'bootstrap-scripts.json', {
|
||||
'parse_full_dependencies.gd': {'text': 'fixture\n', 'sha256': r.sha(product / 'Makefile')}})
|
||||
engine = base / 'synthetic-engine-data'
|
||||
put(engine, b'not an executable; never launched\n')
|
||||
st = engine.stat()
|
||||
descriptor = dict(path=str(engine), resolved_path=str(engine), bytes=st.st_size,
|
||||
sha256=r.sha(engine), mtime_ns=st.st_mtime_ns, mode=st.st_mode,
|
||||
device=st.st_dev, inode=st.st_ino, ctime_ns=st.st_ctime_ns)
|
||||
out = base / 'p04-s4-native-r28'
|
||||
plan = base / 'p04-s4-plan-r28.json'
|
||||
attempts, created, phases = Counter(), {}, []
|
||||
cleanup_calls = []
|
||||
real_write, real_inventory = r.write, r.inventory
|
||||
protected = dict(path=str(base / 'default-player'), existed=False, files={}, logs_and_locks_included=True)
|
||||
|
||||
def write(path, value):
|
||||
path = Path(path)
|
||||
attempts[path] += 1
|
||||
if failure == 'identity-write' and path == out / 'engine-identity.json':
|
||||
raise OSError('injected final identity write error')
|
||||
real_write(path, value) # unchanged open('x'), including collisions
|
||||
created[path] = path.read_bytes()
|
||||
|
||||
def finish(owned):
|
||||
cleanup_calls.append(list(owned))
|
||||
if failure == 'cleanup':
|
||||
raise RuntimeError('injected cleanup error')
|
||||
return [dict(pid=p.pid, engine_ended=True) for p in owned], []
|
||||
|
||||
outer = SimpleNamespace(native=SimpleNamespace(helper={'live_engines': lambda: []}),
|
||||
OWNED=[SimpleNamespace(pid=91001+i) for i in range(4)],
|
||||
finish_captured=finish, run_phase=lambda *a, **k: None)
|
||||
|
||||
def execute(engine_arg, stage, output, phase, test, selected, expected, previous,
|
||||
policy, contracts_arg, paths, public, player, root, *helpers):
|
||||
phases.append(phase)
|
||||
assert engine_arg == str(engine) and output == out and player == protected
|
||||
assert r.source_refs(root) == selected['root_before']
|
||||
assert r.inventory(stage, stage=True) == dict(expected, **previous)
|
||||
pid = outer.OWNED[len(phases)-1].pid
|
||||
row = dict(phase=phase, passed=not (failure == 'parse' and phase == 'parse'), actual_pid=pid)
|
||||
if test:
|
||||
slots = {}
|
||||
for slot in s4.SLOTS:
|
||||
directory = base / 'profiles' / phase / slot
|
||||
for member in ('journal.log', 'manifest.json', 'thumbnail.png', 'world.bin'):
|
||||
put(directory / member, (slot + '/' + member).encode())
|
||||
slots[slot] = str(directory)
|
||||
(out / 'raw').mkdir(exist_ok=True)
|
||||
write(out / ('raw/' + phase + '.json'), dict(checks=1, failures=0, branches=['S4'],
|
||||
slots=slots, pid=pid, profile=str(base / 'profiles' / phase)))
|
||||
# Reproduce the runner's shared text log and let main copy its bytes.
|
||||
log = test.replace('/', '__') + '.log'
|
||||
(out / log).write_text(phase + '\n')
|
||||
row['log'] = log
|
||||
return row
|
||||
|
||||
def inventory(path, **kwargs):
|
||||
if failure == 'output' and Path(path) == out / 'raw':
|
||||
raise OSError('injected post-cleanup inventory error')
|
||||
return real_inventory(path, **kwargs)
|
||||
|
||||
def copy_source(source, target):
|
||||
if failure == 'setup':
|
||||
raise OSError('injected setup copy error')
|
||||
shutil.copyfile(source, target)
|
||||
|
||||
def modules(name, path):
|
||||
if name == 's4_standard_runner':
|
||||
return r
|
||||
if name == 's4_owned_outer':
|
||||
return outer
|
||||
if name == 's4_markers':
|
||||
return SimpleNamespace(load_contracts=lambda path: {})
|
||||
if name in ('s4_diagnostics', 's4_owners', 's4_audio'):
|
||||
return SimpleNamespace()
|
||||
raise AssertionError('unexpected helper import: ' + name)
|
||||
|
||||
with ExitStack() as stack:
|
||||
for obj, values in [(s4, dict(BUILD=base, PRODUCT=product, HERE=staged, QA=qa,
|
||||
ENGINE=engine, CONTRACTS=contracts, MATERIALS=[material],
|
||||
__file__=str(staged / 'run_s4_native.py'), module=modules)),
|
||||
(r, dict(SUPPORT=support, write=write, execute_phase=execute,
|
||||
copy_source=copy_source, inventory=inventory))]:
|
||||
for key, value in values.items():
|
||||
stack.enter_context(patch.object(obj, key, value))
|
||||
pin_mock = stack.enter_context(patch.object(s4, 'engine_pin', return_value=descriptor))
|
||||
guard_mock = stack.enter_context(patch.object(s4, 'template_guard', return_value=None))
|
||||
player_mock = stack.enter_context(patch.object(r, 'default_player', return_value=protected))
|
||||
disk_mock = stack.enter_context(patch.object(s4.shutil, 'disk_usage',
|
||||
return_value=SimpleNamespace(free=10 * 1024**3)))
|
||||
stack.enter_context(redirect_stdout(io.StringIO()))
|
||||
argv = ['run_s4_native.py', '--project-root', str(product), '--output', str(out),
|
||||
'--plan', str(plan), '--godot', str(engine)]
|
||||
with patch.object(sys, 'argv', argv + ['--check-only']):
|
||||
assert s4.main() == 0
|
||||
assert not phases and not cleanup_calls and not out.exists()
|
||||
with patch.object(sys, 'argv', argv):
|
||||
result = s4.main()
|
||||
rejected(s4.main) # same output must be rejected before any new writes
|
||||
assert pin_mock.called and guard_mock.called and player_mock.called and disk_mock.called
|
||||
|
||||
assert result == (0 if failure is None else 1)
|
||||
assert len(cleanup_calls) == 1
|
||||
assert all(count == 1 for count in attempts.values()), attempts
|
||||
for leaf in ('outer-owned-finally.json', 'engine-identity.json', 'execution.json', 'runner-ended.json'):
|
||||
assert attempts[out / leaf] == 1
|
||||
if failure != 'identity-write' or leaf != 'engine-identity.json':
|
||||
assert (out / leaf).is_file()
|
||||
if failure != 'setup':
|
||||
assert (out / 'runner-input.json').read_bytes() == created[out / 'runner-input.json']
|
||||
assert r.read(out / 'runner-input.json')['engine_identity_initial']['after'] is None
|
||||
execution = r.read(out / 'execution.json')
|
||||
assert execution['passed'] is (failure is None)
|
||||
assert 'FileExistsError' not in json.dumps(execution)
|
||||
assert phases == ([] if failure == 'setup' else ['import', 'parse'] if failure == 'parse'
|
||||
else ['import', 'parse', 'writer', 'reader'])
|
||||
if failure == 'cleanup':
|
||||
assert execution['cleanup_errors'] == ['injected cleanup error']
|
||||
if failure in ('output', 'identity-write'):
|
||||
assert execution['guard_errors']
|
||||
if failure is None:
|
||||
assert len(list(out.glob('engine-identity*.json'))) == 5
|
||||
verify_identity(out, descriptor)
|
||||
assert (out / 'writer-combined-raw.log').read_bytes() == b'writer\n'
|
||||
assert (out / 'reader-combined-raw.log').read_bytes() == b'reader\n'
|
||||
# Mutations below affect only this disposable fixture's synthetic proofs.
|
||||
for phase in ('import', 'parse', 'writer', 'reader'):
|
||||
path = out / ('engine-identity-before-' + phase + '.json')
|
||||
original = path.read_bytes()
|
||||
path.write_text('{}')
|
||||
rejected(lambda: verify_identity(out, descriptor))
|
||||
path.unlink()
|
||||
rejected(lambda: verify_identity(out, descriptor))
|
||||
path.write_bytes(original)
|
||||
path = out / 'runner-input.json'
|
||||
initial = r.read(path)
|
||||
changed = copy.deepcopy(initial)
|
||||
changed['engine_identity_initial']['before']['inode'] += 1
|
||||
path.write_text(json.dumps(changed))
|
||||
rejected(lambda: verify_identity(out, descriptor))
|
||||
path.write_bytes(created[path])
|
||||
verify_identity(out, descriptor)
|
||||
return dict(case=failure or 'success', passed=True, native_engine_launches=0)
|
||||
|
||||
|
||||
def run():
|
||||
with tempfile.TemporaryDirectory(prefix='s4-receipt-fixtures-') as temporary:
|
||||
return [case(Path(temporary).resolve() / (failure or 'success'), failure)
|
||||
for failure in (None, 'parse', 'cleanup', 'setup', 'output', 'identity-write')]
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(json.dumps(dict(results=run(), formal_acceptance=False), indent=2))
|
||||
@@ -0,0 +1,405 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S4-only native entry. --check-only never loads ownership helpers or starts Godot."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import copy
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import stat
|
||||
import sys
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
BUILD = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build")
|
||||
PRODUCT = BUILD / "audit-support/p04-r17-full-inputs-r01/product"
|
||||
CACHE_RUN = BUILD / "p04-final-product-full-r01"
|
||||
TEST = "tests/s3/test_runtime_staff_assignment.gd"
|
||||
QA = HERE / "p04_s4_power_probe.gd"
|
||||
ENGINE = Path("/Applications/Godot.app/Contents/MacOS/Godot")
|
||||
ENGINE_SHA256 = "c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf"
|
||||
MANIFEST = BUILD / "audit-support/p04-r17-full-inputs-r01/MANIFEST.json"
|
||||
MANIFEST_SHA = "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8"
|
||||
CONTRACTS = BUILD / "audit-support/p04-r17-full-inputs-r01/fixed/6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7/completion_marker_contracts.json"
|
||||
CASES = ["S4"]
|
||||
LABELS = {"S4": ["boundary", "held", "recovered"]}
|
||||
SLOTS = ["S4-" + label for label in sorted(LABELS["S4"])]
|
||||
MATERIALS = [MANIFEST, BUILD / "audit-support/p04-review-repair22-r01/ACTUAL_CURRENT_PUBLIC_POWER_FUNCTIONS.json"]
|
||||
|
||||
def template_guard(root):
|
||||
if root != PRODUCT.resolve() or not HERE.is_relative_to(BUILD.resolve()):
|
||||
raise ValueError("Root must stage this exact QA/wrapper/plan inside own B")
|
||||
if pin(MANIFEST)["sha256"] != MANIFEST_SHA:
|
||||
raise ValueError("immutable full R17 relocation manifest")
|
||||
manifest = json.loads(MANIFEST.read_text())
|
||||
for name, source in manifest["product_source"].items():
|
||||
# Old absolute paths are lookup keys only. Never open them.
|
||||
row = manifest["original_to_relocated"][source["path"]]
|
||||
actual = pin(root / name)
|
||||
if Path(row["relocated"]).resolve() != root / name or any(
|
||||
actual[k] != source[k] or actual[k] != row[k] for k in ("bytes", "sha256")):
|
||||
raise ValueError("relocated product differs: " + name)
|
||||
probe = QA.read_text()
|
||||
import re
|
||||
if ":=" in probe or re.search(r"\bvar\s+\w+\s*=", probe) or re.search(r"\bfor\s+\w+\s+in\b", probe):
|
||||
raise ValueError("explicit typed declarations/loops required")
|
||||
if re.search(r"\.(?:patience_remaining|elapsed_minutes|power_available|capacity|hold_until)\s*=(?!=)", probe):
|
||||
raise ValueError("forbidden direct state mutation")
|
||||
for forbidden in ["evaluate_now(", "configure_operations(", "._update_shift_states(", ".record_duty_minute("]:
|
||||
if forbidden in probe:
|
||||
raise ValueError("forbidden bypass: " + forbidden)
|
||||
# Constants and public bodies are read from the actual selected source, not a memo.
|
||||
mana = (root / "scripts/mana/mana_network_service.gd").read_text()
|
||||
for key in ("EVALUATION_INTERVAL_MINUTES", "MINIMUM_OFF_HOLD_MINUTES"):
|
||||
if not re.search(r"const " + key + r" := 10\b", mana):
|
||||
raise ValueError("source timing changed: " + key)
|
||||
|
||||
|
||||
def module(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
value = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(value)
|
||||
return value
|
||||
|
||||
def pin(path):
|
||||
path = Path(path)
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise ValueError("missing exact input: " + str(path))
|
||||
data = path.read_bytes()
|
||||
return dict(path=str(path), bytes=len(data), sha256=hashlib.sha256(data).hexdigest())
|
||||
|
||||
def engine_pin(path, expected=None):
|
||||
path = Path(path)
|
||||
if path != ENGINE or any(p.is_symlink() for p in (path, *path.parents)):
|
||||
raise ValueError("exact installed engine required; no symlink or relocated input")
|
||||
if path.resolve(strict=True) != ENGINE:
|
||||
raise ValueError("installed engine resolved path changed")
|
||||
before = path.stat()
|
||||
if not stat.S_ISREG(before.st_mode):
|
||||
raise ValueError("installed engine must be regular")
|
||||
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
after = path.stat()
|
||||
fields = ("st_dev", "st_ino", "st_size", "st_mtime_ns", "st_ctime_ns", "st_mode")
|
||||
if any(getattr(before, k) != getattr(after, k) for k in fields):
|
||||
raise ValueError("engine changed while hashing")
|
||||
if any(p.is_symlink() for p in (path, *path.parents)) or path.resolve(strict=True) != ENGINE:
|
||||
raise ValueError("engine path changed while hashing")
|
||||
value = dict(path=str(path), resolved_path=str(path.resolve(strict=True)),
|
||||
bytes=after.st_size, mtime_ns=after.st_mtime_ns, mode=after.st_mode,
|
||||
device=after.st_dev, inode=after.st_ino, ctime_ns=after.st_ctime_ns, sha256=digest)
|
||||
if digest != ENGINE_SHA256:
|
||||
raise ValueError("engine differs from installed engine constant")
|
||||
if expected is not None and value != expected:
|
||||
raise ValueError("engine identity changed")
|
||||
return value
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--project-root", type=Path, default=PRODUCT)
|
||||
parser.add_argument("--output", type=Path, default=BUILD / "p04-s4-native-r28")
|
||||
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s4-plan-r28.json")
|
||||
parser.add_argument("--cache-run", type=Path, default=None,
|
||||
help="optional passed registered cache; default is fresh, without cache access")
|
||||
parser.add_argument("--godot", type=Path, default=ENGINE)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
args = parser.parse_args()
|
||||
engine_descriptor = engine_pin(args.godot)
|
||||
if args.cache_run is not None and args.cache_run.resolve() != CACHE_RUN.resolve():
|
||||
raise ValueError("only registered own-B full-run cache")
|
||||
root = args.project_root.resolve()
|
||||
out = args.output.resolve()
|
||||
if not out.is_relative_to(BUILD.resolve()) or not args.plan.resolve().is_relative_to(BUILD.resolve()):
|
||||
raise ValueError("plan/output must stay in task build")
|
||||
if out.exists() or out.is_symlink():
|
||||
raise ValueError("fresh output required")
|
||||
if args.check_only and (args.plan.exists() or args.plan.is_symlink()):
|
||||
raise ValueError("fresh plan required")
|
||||
r = module("s4_standard_runner", root / "tools/run_all_tests.py")
|
||||
# Preserve the reviewed source_refs -> exactly one QA override order.
|
||||
source_before = r.source_refs(root)
|
||||
refs = dict(source_before)
|
||||
refs[TEST] = pin(QA)
|
||||
template_guard(root)
|
||||
expected = {n: dict(bytes=p["bytes"], sha256=p["sha256"]) for n, p in refs.items()}
|
||||
fixed = [pin(p) for p in MATERIALS + [
|
||||
QA, Path(__file__), HERE / "S4_ROOT_RUN.md",
|
||||
root / "tests/transport/test_runtime_transport.gd",
|
||||
root / "tests/staff/test_rescue_elevator.gd",
|
||||
CONTRACTS,
|
||||
]]
|
||||
selected = dict(root_before=source_before, map={n: p["sha256"] for n, p in refs.items()}, timeout=1200)
|
||||
cache_note = None
|
||||
cache_receipts = []
|
||||
if args.cache_run is not None:
|
||||
# Reject failed caches before reading their inputs or cache contents.
|
||||
r.require(r.read(args.cache_run / "runner-ended.json")["passed"] is True,
|
||||
"failed cache is ineligible")
|
||||
for leaf in (["runner-ended.json", "runner-input.json"] if args.cache_run is not None else []):
|
||||
path = args.cache_run / leaf
|
||||
try:
|
||||
cache_receipts.append(pin(path))
|
||||
except OSError as caught:
|
||||
cache_note = dict(path=str(path), error=type(caught).__name__ + ": " + str(caught),
|
||||
required_before_native=True)
|
||||
break
|
||||
plan = dict(protocol="p04-s4-preparation-v1", project_root=str(root), output=str(out), engine_path=str(args.godot.resolve()),
|
||||
source_before=source_before, selected_source_map=selected["map"],
|
||||
QA_overrides=[TEST], fixed_input_pins=fixed,
|
||||
cache_run=str(args.cache_run) if args.cache_run is not None else None,
|
||||
import_mode="cache" if args.cache_run is not None else "fresh",
|
||||
engine_sha256=ENGINE_SHA256, engine=engine_descriptor,
|
||||
cache_receipts=cache_receipts, cache_access=cache_note,
|
||||
phases=["import", "parse", "writer", "reader"],
|
||||
native_engine_launches=0, whole_goal_accepted=False, unit_accepted=False)
|
||||
# Check-only only reads source/materials and writes the plan in task build.
|
||||
# Reads engine identity; never launches Godot or inspects players/processes.
|
||||
if args.check_only:
|
||||
args.plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
r.write(args.plan, plan)
|
||||
print(json.dumps(dict(check_only=True, engines=0, source_files=len(source_before),
|
||||
QA_overrides=[TEST], fixed_pins=len(fixed), plan=str(args.plan),
|
||||
plan_sha256=r.sha(args.plan), cache_access=cache_note), ensure_ascii=False))
|
||||
return 0
|
||||
r.require(root == PRODUCT.resolve() and len(source_before) == 1338, "Root native requires product 1338; worker plan is not native evidence")
|
||||
r.require(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first")
|
||||
engine_observations = []
|
||||
engine_identity = dict(before=engine_descriptor, during=[], after=None)
|
||||
def repin(label):
|
||||
try:
|
||||
value = engine_pin(args.godot, engine_descriptor)
|
||||
engine_observations.append(dict(stage=label, descriptor=value))
|
||||
if label == "after-cleanup":
|
||||
engine_identity["after"] = value
|
||||
else:
|
||||
engine_identity["during"].append(value)
|
||||
except BaseException as caught:
|
||||
engine_observations.append(dict(stage=label, error=str(caught)))
|
||||
raise
|
||||
def fixed_guard():
|
||||
repin("cleanup-fixed-guard")
|
||||
template_guard(root)
|
||||
for value in fixed + cache_receipts:
|
||||
r.require(pin(value["path"]) == value, "fixed input changed: " + value["path"])
|
||||
fixed_guard()
|
||||
old = args.cache_run
|
||||
if old is not None:
|
||||
# Native cache admission remains Root-only and fails closed.
|
||||
r.require(cache_note is None, "Root must read exact cache receipts: " + str(cache_note))
|
||||
r.require(r.read(old / "runner-ended.json")["passed"], "ended cache run must have passed")
|
||||
cache_input = r.read(old / "runner-input.json")
|
||||
r.require(cache_input["engine_sha256"] == ENGINE_SHA256, "cache engine identity")
|
||||
old_map = cache_input["selected_source_map"]
|
||||
current_map = {n: p["sha256"] for n, p in source_before.items()}
|
||||
r.require({k: v for k, v in old_map.items() if k != "tools/check_p04_order_evidence.py"} == current_map and len(old_map) == 1339, "immutable full cache: all 1338 product inputs equal; checker is not parsed/executed by product suites")
|
||||
protected = r.default_player(root)
|
||||
outer = module("s4_owned_outer", r.SUPPORT / "owned_outer.py")
|
||||
r.require(not outer.native.helper["live_engines"](), "another Godot owns CPU")
|
||||
engine = ENGINE
|
||||
repin("native-admission")
|
||||
r.require(shutil.disk_usage(out.parent).free >= 1024**3 + engine.stat().st_size + 136*1024**2,
|
||||
"reviewed native disk admission")
|
||||
r.require(not out.exists(), "fresh output required; preserve previous attempts")
|
||||
out.mkdir()
|
||||
stage = out / "stage"
|
||||
stage.mkdir()
|
||||
(out / "starters").mkdir()
|
||||
(out / "bootstrap").mkdir()
|
||||
phases = []
|
||||
previous = {}
|
||||
error = None
|
||||
guards = []
|
||||
cleanup = []
|
||||
cleanup_errors = []
|
||||
cache_before = None
|
||||
formal_before = None
|
||||
formal_after = None
|
||||
original_phase = outer.run_phase
|
||||
def run_phase(argv, folder, timeout, env, **kwargs):
|
||||
env = dict(env)
|
||||
if folder.name in ["writer", "reader"]:
|
||||
env.update(THE_TREE_S4_EVIDENCE=str(out / "raw"), THE_TREE_S4_MODE=folder.name)
|
||||
return original_phase(argv, folder, timeout, env, **kwargs)
|
||||
outer.run_phase = run_phase
|
||||
try:
|
||||
r.write(out / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(),
|
||||
plan=pin(args.plan), engines_launched=0))
|
||||
r.write(out / "source_before.json", source_before)
|
||||
r.write(out / "selected-source-map.json", refs)
|
||||
# Private source stage first, then literal reviewed bootstrap.
|
||||
for name, value in refs.items():
|
||||
target = stage / name
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
r.copy_source(Path(value["path"]), target)
|
||||
r.require(r.inventory(stage, stage=True) == expected, "whole private source")
|
||||
for name, value in r.read(r.SUPPORT / "bootstrap-scripts.json").items():
|
||||
data = value["text"].encode()
|
||||
r.require(hashlib.sha256(data).hexdigest() == value["sha256"], "bootstrap pin")
|
||||
(out / "bootstrap" / name).write_bytes(data)
|
||||
if old is not None:
|
||||
cache = old / "stage/.godot"
|
||||
cache_before = r.inventory(cache)
|
||||
r.require(bool(cache_before), "nonempty ended cache")
|
||||
for name in cache_before:
|
||||
src = cache / name
|
||||
dst = stage / ".godot" / name
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(["/bin/cp", "-c", str(src), str(dst)], check=True)
|
||||
r.require((src.stat().st_dev, src.stat().st_ino) != (dst.stat().st_dev, dst.stat().st_ino),
|
||||
"cache clone is a separate inode")
|
||||
r.require(r.inventory(stage / ".godot") == cache_before, "whole cache clone hashes")
|
||||
r.write(out / "cache-reuse.json", dict(ended_run=str(old), receipts=cache_receipts,
|
||||
whole_cache=cache_before, independent_inode=True,
|
||||
ordinary_import_required=True, ordinary_parse_required=True))
|
||||
paths = ["res://" + n for n in sorted(refs) if n.endswith(".gd")]
|
||||
r.require(len(paths) == 316, "complete 316-script parse roster")
|
||||
r.write(out / "parse-paths.json", paths)
|
||||
markers = module("s4_markers", root / "tools/strict_test_markers.py")
|
||||
contracts = markers.load_contracts(CONTRACTS)
|
||||
contracts[TEST] = dict(test=TEST, template="S4 power: <checks> checks, <failures> failures", required=True)
|
||||
policy = r.read(r.SUPPORT / "suite-policy.json")
|
||||
r.require(TEST not in policy["suite_policies"], "no normal suite watchdog/policy override")
|
||||
r.write(out / "contracts.json", contracts)
|
||||
r.write(out / "runner-input.json", dict(plan, owner_runner_pid=os.getpid(),
|
||||
wrapper_sha256=r.sha(__file__), QA=pin(QA), engine=engine_descriptor,
|
||||
engine_identity_before=engine_descriptor, engine_identity_initial=copy.deepcopy(engine_identity),
|
||||
runner_sha256=r.sha(root / "tools/run_all_tests.py"),
|
||||
default_player_before=protected, fixed_input_pins=fixed,
|
||||
functional_watchdog_seconds=1200, source_adoption=False))
|
||||
diagnostics = module("s4_diagnostics", r.SUPPORT / "diagnostic_gate.py")
|
||||
owners = module("s4_owners", r.SUPPORT / "ordinary_proof_gate.py")
|
||||
audio = module("s4_audio", r.SUPPORT / "audio_evidence_gate.py")
|
||||
public = (stage / "project.godot").read_bytes()
|
||||
for phase, test in [("import", None), ("parse", None), ("writer", TEST), ("reader", TEST)]:
|
||||
if phase == "reader":
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_before = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(sorted(formal_before) == SLOTS, "exact three formal points for isolated power case")
|
||||
if phase == "import" and old is None:
|
||||
r.require(not (stage / ".godot").exists() and not (stage / ".godot").is_symlink(),
|
||||
"fresh private .godot absent at actual import")
|
||||
repin("before-" + phase)
|
||||
r.write(out / ("engine-identity-before-" + phase + ".json"),
|
||||
dict(engine_observations[-1], observation_index=len(engine_observations)-1))
|
||||
row = r.execute_phase(str(engine), stage, out, phase, test, selected, expected,
|
||||
previous, policy, contracts, paths, public, protected, root,
|
||||
outer, markers, diagnostics, owners, audio)
|
||||
phases.append(row)
|
||||
# execute_phase writer/reader share out/<test>.log via write_text.
|
||||
# Keep binary per-phase copies under these existing unique names.
|
||||
# The shared runner log is not write-once; see S4_ROOT_RUN.md.
|
||||
if test and row.get("log"):
|
||||
(out / (phase + "-combined-raw.log")).write_bytes((out / row["log"]).read_bytes())
|
||||
row["strict_runner_log_path"] = row["log"]
|
||||
row["log"] = phase + "-combined-raw.log"
|
||||
r.write(out / (phase + "-actual-phase.json"), row)
|
||||
print(json.dumps(dict(phase=phase, passed=row["passed"], error=row.get("validation_error"))), flush=True)
|
||||
r.require(row["passed"], "first strict phase failure: " + phase)
|
||||
if phase in ["writer", "reader"]:
|
||||
receipt = r.read(out / ("raw/" + phase + ".json"))
|
||||
r.require(receipt["checks"] > 0 and receipt["failures"] == 0,
|
||||
"S4 actual outcome receipt")
|
||||
r.require(sorted(receipt["branches"]) == CASES,
|
||||
"exact one independent Writer/Reader case")
|
||||
r.require(sorted(receipt["slots"]) == SLOTS, "all formal point slots")
|
||||
r.require(receipt["pid"] == row["actual_pid"], "actual native PID/receipt")
|
||||
repin("after-collection")
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
reader = r.read(out / "raw/reader.json")
|
||||
r.require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "distinct formal processes")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Reader leaves Writer packages unchanged")
|
||||
except BaseException as caught:
|
||||
error = type(caught).__name__ + ": " + str(caught)
|
||||
finally:
|
||||
try:
|
||||
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
|
||||
except BaseException as caught:
|
||||
cleanup_errors.append(str(caught))
|
||||
try:
|
||||
r.write(out / "outer-owned-finally.json", dict(cleanup=cleanup, errors=cleanup_errors))
|
||||
except BaseException as caught:
|
||||
guards.append("finally receipt: " + type(caught).__name__ + ": " + str(caught))
|
||||
# Explicit re-pin after cleanup is independent of other fixed-input guards.
|
||||
try:
|
||||
repin("after-cleanup")
|
||||
except BaseException as caught:
|
||||
guards.append(type(caught).__name__ + ": " + str(caught))
|
||||
# Fixed guard always follows ownership cleanup, even if setup or an engine failed.
|
||||
try:
|
||||
fixed_guard()
|
||||
after = r.source_refs(root)
|
||||
r.write(out / "source_after.json", after)
|
||||
r.require(after == source_before, "whole product source unchanged")
|
||||
r.require(r.default_player(root) == protected, "default player unchanged")
|
||||
if (stage / "project.godot").exists():
|
||||
r.require(r.inventory(stage, stage=True) == dict(expected, **previous), "whole private source unchanged")
|
||||
if cache_before is not None:
|
||||
r.require(r.inventory(old / "stage/.godot") == cache_before, "original cache unchanged")
|
||||
if formal_before is not None:
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Writer formal packages unchanged after Reader, including failure")
|
||||
except BaseException as caught:
|
||||
guards.append(type(caught).__name__ + ": " + str(caught))
|
||||
pids = [p.pid for p in outer.OWNED]
|
||||
passed = (error is None and not guards and not cleanup_errors and len(phases) == 4
|
||||
and all(p["passed"] for p in phases) and len(pids) == len(set(pids)) == 4
|
||||
and all(p["engine_ended"] for p in cleanup))
|
||||
execution = dict(passed=passed, error=error, guard_errors=guards, phases=phases,
|
||||
actual_captured_PIDs=pids, cleanup=cleanup, cleanup_errors=cleanup_errors,
|
||||
formal_before=formal_before, formal_after=formal_after,
|
||||
whole_goal_accepted=False, unit_accepted=False)
|
||||
# IO1 has already copied all four members into a separate Reader profile.
|
||||
# Mirror those exact complete directories to own B for read-only R verification.
|
||||
# Original profile paths are provenance after this step, never verifier inputs.
|
||||
try:
|
||||
if passed:
|
||||
relocated = {}
|
||||
for role in ("writer", "reader"):
|
||||
receipt = r.read(out / ("raw/" + role + ".json"))
|
||||
relocated[role] = {}
|
||||
for slot, original in receipt["slots"].items():
|
||||
target = out / "formal" / role / slot
|
||||
target.mkdir(parents=True)
|
||||
members = r.inventory(Path(original))
|
||||
r.require(set(members) == {"journal.log", "manifest.json", "thumbnail.png", "world.bin"}, "all formal members")
|
||||
for member in members:
|
||||
shutil.copyfile(Path(original) / member, target / member)
|
||||
r.require(r.inventory(target) == members == formal_before[slot], "whole formal mirror")
|
||||
relocated[role][slot] = dict(provenance=original, relative=str(target.relative_to(out)), members=members)
|
||||
r.write(out / "formal-relocation.json", relocated)
|
||||
r.write(out / "raw-inventory.json", r.inventory(out / "raw"))
|
||||
r.write(out / "PID-set.json", dict(owner_runner_pid=os.getpid(), engine_PIDs=pids))
|
||||
except BaseException as caught:
|
||||
guards.append("output collection: " + type(caught).__name__ + ": " + str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
try:
|
||||
repin("after-output-collection")
|
||||
except BaseException as caught:
|
||||
guards.append(str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
try:
|
||||
r.write(out / "engine-identity.json",
|
||||
dict(observations=engine_observations, identity=engine_identity))
|
||||
except BaseException as caught:
|
||||
guards.append("engine identity receipt: " + type(caught).__name__ + ": " + str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
execution["engine_observations"] = engine_observations
|
||||
execution["engine_identity"] = engine_identity
|
||||
r.write(out / "execution.json", execution)
|
||||
r.write(out / "runner-ended.json", dict(execution, ended=r.now()))
|
||||
print(json.dumps(dict(passed=passed, error=error, guard_errors=guards, output=str(out))))
|
||||
return 0 if passed else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as caught:
|
||||
print(type(caught).__name__ + ": " + str(caught), file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
@@ -0,0 +1,31 @@
|
||||
# S5 — pending implementation, not a native run plan
|
||||
|
||||
No S5 executable driver or native receipt is supplied by this partial repair.
|
||||
Do not start the once-only Q native/full batch from this directory.
|
||||
|
||||
The four required cases remain unchanged:
|
||||
|
||||
1. Real mana depletion, NO_POWER, no charge/progress, reservation retained,
|
||||
recovery and one completion. S4's `disconnect_nodes` is not depletion.
|
||||
The existing cafeteria fixture has an operating kitchen prerequisite;
|
||||
global depletion can select RELATED_FACILITY_INACTIVE first. A driver must
|
||||
observe that distinction rather than relabel the reason or mutate power
|
||||
availability directly. A separate combined-cause case must prove the
|
||||
approved RELATED-before-NO_POWER reason priority.
|
||||
2. Actual accident or raid destruction during completion: valid minute-N
|
||||
sale; next-minute failure/release for unfinished affected work; compare
|
||||
command destruction before completion. S3 B4a/B4b use a registered event
|
||||
with SET_FACILITY_STATE and must not simply be renamed an actual raid.
|
||||
3. 00:00 completion: retain the S2a paid-midnight setup and full typed
|
||||
snapshots, add phase observations proving prior-day settlement/upkeep
|
||||
precede the sale, and independently join the sale to the new accounting
|
||||
day. Retain replay/no-double-settlement proof.
|
||||
4. Command exit versus AI planned exit at completion: retain actual public
|
||||
commands and guest/service/reservation/ledger joins. Observe the AI
|
||||
DECIDING predicate after completion, without forcing guest state or
|
||||
treating a due exit time as universal active-service cancellation.
|
||||
|
||||
All prices, durations, resource rules and thresholds come from the selected
|
||||
Q definitions and current public runtime. No new original values or rules
|
||||
are supplied here. These notes identify implementation work; they are not
|
||||
engine evidence and confer no condition credit.
|
||||
@@ -0,0 +1,88 @@
|
||||
# Concrete32 power pair — unexecuted, not batch admission
|
||||
|
||||
`p04_s5_power_probe.gd` supplies S5A_PURE (paid public bath, genuine mana
|
||||
depletion) and S5B_DEPENDENCY (cafeteria plus kitchen, RELATED-before-NO_POWER).
|
||||
Both use public resource commands, observed recovery, formal packages, independent
|
||||
Reader replay, phase raw and resource-command replay receipts. The bath has no
|
||||
item reservation; the dependent case retains and consumes the actual meal ID.
|
||||
|
||||
`run_s5_native.py` retains the S4 ownership/engine/cleanup safeguards. REPAIR35
|
||||
adds fresh-Q input binding and separates historical R17 relocation checks from
|
||||
the unchanged probe checks. All source and parse counts are derived. Historical
|
||||
R17 requires `--historical-r17 --check-only` and is read-only. Only historical
|
||||
mode defaults `--project-root` to PRODUCT. Fresh-Q requires an explicit root and
|
||||
all three full lowercase HEX40 inputs. R17 MANIFEST and repair22 power-functions
|
||||
JSON are historical_not_inputs in fresh-Q plans, never fresh-Q fixed pins.
|
||||
No old S1–S4 record is renamed or reused as fresh proof.
|
||||
|
||||
REPAIR36 supplies a bounded UID diagnostic, not native/batch admission. Full admission still awaits S5
|
||||
destruction/midnight/exit, S1–S4/collector fresh Q, 184 suites / 329 parse and the
|
||||
truthful index are ready. Root run order and exact-candidate qualification also
|
||||
remain pending. The native implementation is retained for later Root admission;
|
||||
this change and a check-only plan give no admission or acceptance credit.
|
||||
|
||||
After the final submission, Root alone prepares Q using these steps (not performed by the worker):
|
||||
|
||||
1. Set S to the final submission and M to
|
||||
`c0ea009b28a7649aca6a433a1a24dc0be5e7ab81`; derive Qt with
|
||||
`git merge-tree --write-tree "$M" "$S"`.
|
||||
2. Create X with `git commit-tree "$Qt" -m 'nonproduction Q checkout'` with
|
||||
**no parents**, then create a clean worktree at X. X is a nonproduction
|
||||
checkout carrier of Q, never normal Runner C. R34 and Root normal_c_join
|
||||
remain unchanged.
|
||||
3. Stage the s5 directory under Root's own B, the registered BUILD directory.
|
||||
4. Follow the REPAIR36 order below, with fresh output and plan paths:
|
||||
|
||||
```sh
|
||||
BUILD_OUTPUT_DIR="$B" python3 "$B/.../run_s5_native.py" \
|
||||
--project-root "$wt" --main "$M" --submission "$S" --tested-tree "$Qt" \
|
||||
--check-only --plan "$B/p04-s5-plan-r36.json" --output "$B/p04-s5-DIAG-r36"
|
||||
```
|
||||
|
||||
5. Pure fixtures and controls follow the order below. Their JSON has accepted=false.
|
||||
6. In separate controlled check-only cases, require rejection for: a dirty file;
|
||||
an untracked file; HEAD tree unequal to --tested-tree; wrong --submission;
|
||||
--main unequal to the frozen anchor. Preserve each actual rejection receipt
|
||||
and restore a clean carrier between cases. These Git negatives and all pure
|
||||
fixtures are unexecuted in this worker submission. No engine runs.
|
||||
|
||||
Root later stages the wrapper, probe and this document in its own build area.
|
||||
The adapter takes `--raw-root`, `--writer-sha256`, `--reader-sha256` from verified
|
||||
native records. It verifies referenced typed/JSON hashes and derives existing
|
||||
measurement keys. It does not qualify source, engine, settings or full evidence.
|
||||
|
||||
S5 destruction, midnight and exit cases remain unimplemented. The preserved
|
||||
`PENDING.md` is historical partial31 text; this document records only the added,
|
||||
unexecuted power pair. D05 six approvals, A01, frozen M, 16/88 and P04 credit 0
|
||||
are unchanged. All 7 domains + EV and original 30/88 remain fixed. Root alone
|
||||
performs later qualification; no engine is authorized for this worker.
|
||||
|
||||
## REPAIR36 UID diagnostic protocol and Root order
|
||||
|
||||
Protocol `p04-s5-fresh-q-uid-diagnostic-v1` is a diagnostic composite with
|
||||
standard_execute_phase_context=false, native_admissible=false and
|
||||
fresh_q_evidence=false. The wrapper extracts only uid_plan and uid_snapshot
|
||||
from the actual pinned Q helper using AST, binding shared to the actual Q
|
||||
runner. It does not execute the helper module. Plans derive finite UID owners
|
||||
from staged refs; import alone seals exact UID bytes. Private function globals
|
||||
replace declared_reports while preserving the execute_phase code object.
|
||||
All four phase UID receipts and the post-cleanup seal are required.
|
||||
Historical mode has no UID instrumentation and remains read-only.
|
||||
|
||||
Root order, all unexecuted by this worker:
|
||||
|
||||
1. py_compile the wrapper and both pure control files.
|
||||
2. Run s5_wrapper_fixtures.py.
|
||||
3. Run `python3 "$B/.../s5_uid_controls.py" --project-root "$wt" --scratch "$B/p04-s5-uid-controls-r36"`.
|
||||
Scratch must be absolute, absent, outside Q, with an existing parent in BUILD.
|
||||
4. Produce a fresh check-only plan with the command above.
|
||||
5. Perform exactly one fresh diagnostic with the identical command and paths,
|
||||
removing only --check-only. Use a new output name; preserve
|
||||
`p04-s5-DIAG-r35` as FAILED. This is a bounded diagnostic, not full admission.
|
||||
|
||||
Root must confirm the actual Q run_all_tests.py matches the supplied reference
|
||||
(sha256 prefix 320ca668); the worker read only the mirror for Q API context.
|
||||
Runtime helper fingerprints and source/provenance guards bind the actual Q.
|
||||
No full regression, 15+EV batch or original credit is claimed. The S5
|
||||
destruction/midnight/exit drivers, S1–S4/collector fresh Q, derived 184 suites /
|
||||
329 parse, truthful index and Root exact-candidate qualification remain pending.
|
||||
@@ -0,0 +1,466 @@
|
||||
extends "res://tests/s3/test_runtime_actor_hardening.gd"
|
||||
|
||||
const CASES: Array[String] = ["S5A_PURE", "S5B_DEPENDENCY"]
|
||||
const SEEDS: Dictionary = {"S5A_PURE": 420550, "S5B_DEPENDENCY": 420551}
|
||||
const GUEST: StringName = &"p04.s5.guest.a"
|
||||
const REQUEST: StringName = &"p04.s5.request.a"
|
||||
const QUEUED_GUEST: StringName = &"p04.s5.guest.b"
|
||||
const QUEUED_REQUEST: StringName = &"p04.s5.request.b"
|
||||
const SERVERS: Array[StringName] = [&"p04.order.server.a", &"p04.order.server.b"]
|
||||
const SNAPSHOT_SERVERS: Array[String] = ["p04.order.server.a", "p04.order.server.b"]
|
||||
var _evidence_dir: String = ""
|
||||
var _mode: String = ""
|
||||
var _case: String = ""
|
||||
var _serial: int = 0
|
||||
var _facility_id: StringName = &""
|
||||
var _service_id: StringName = &""
|
||||
var _boundary: int = 960
|
||||
var _price: int = 0
|
||||
var _duration: int = 0
|
||||
var _patience: int = 0
|
||||
var _reserve: int = 0
|
||||
var _recovery: Dictionary = {}
|
||||
var _commands: Array = []
|
||||
var _results: Array = []
|
||||
var _receipts: Array = []
|
||||
var _phases: Array = []
|
||||
var _points: Dictionary = {}
|
||||
var _branches: Dictionary = {}
|
||||
var _slots: Dictionary = {}
|
||||
var _replay_reference: Dictionary = {}
|
||||
|
||||
func _run() -> void:
|
||||
_evidence_dir = OS.get_environment("THE_TREE_S5_EVIDENCE")
|
||||
_mode = OS.get_environment("THE_TREE_S5_MODE")
|
||||
if _evidence_dir.is_empty() or _mode not in ["writer", "reader"]:
|
||||
quit(2)
|
||||
return
|
||||
_expect(DirAccess.make_dir_recursive_absolute(_evidence_dir) == OK, "external evidence directory")
|
||||
if _mode == "writer":
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
_writer_case()
|
||||
else:
|
||||
_reader()
|
||||
_write(_mode, {&"pid": OS.get_process_id(), &"profile": OS.get_user_data_dir(), &"slots": _slots,
|
||||
&"branches": _branches, &"checks": _checks, &"failures": _failures,
|
||||
&"whole_goal_accepted": false, &"unit_accepted": false}, true)
|
||||
_cleanup()
|
||||
print("S5 power: %d checks, %d failures" % [_checks, _failures])
|
||||
quit(1 if _failures else 0)
|
||||
|
||||
func _fresh() -> void:
|
||||
_serial = 0
|
||||
_facility_id = &""
|
||||
_service_id = &""
|
||||
_boundary = 960
|
||||
_price = 0
|
||||
_duration = 0
|
||||
_patience = 0
|
||||
_reserve = 0
|
||||
_recovery = {}
|
||||
_replay_reference = {}
|
||||
_commands = []
|
||||
_results = []
|
||||
_receipts = []
|
||||
_phases = []
|
||||
_points = {}
|
||||
_reset_runtime(SEEDS[_case])
|
||||
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
||||
_runtime.command_completed.connect(func(result: CommandResult) -> void:
|
||||
_results.append({&"minute": _clock.absolute_minute, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "actual public command completion " + str(result.command_id))
|
||||
)
|
||||
|
||||
func _cmd(kind: StringName, payload: Dictionary, at: int = -1) -> CommandResult:
|
||||
_serial += 1
|
||||
var key: StringName = StringName("p04.s5.%s.command.%d" % [_case, _serial])
|
||||
var result: CommandResult = _runtime.submit_command(kind, payload, &"player", &"", 10, at, key)
|
||||
_commands.append({&"serial": _serial, &"kind": kind, &"payload": payload.duplicate(true),
|
||||
&"at": at, &"issued_at": _clock.absolute_minute, &"idempotency_key": key, &"result": result.to_dictionary()})
|
||||
_expect(result.is_success(), "public command " + String(kind))
|
||||
return result
|
||||
|
||||
func _queue(guest_id: StringName, request_id: StringName, exit_at: int) -> void:
|
||||
_cmd(&"spawn_guest", {&"guest_id": guest_id, &"guest_type_id": &"traveler", &"gold": _price * 2,
|
||||
&"options": {&"state": GuestRuntime.State.DECIDING, &"current_cell": Vector3i.ZERO, &"planned_exit_at": exit_at}})
|
||||
_cmd(&"create_service_request", {&"facility_id": _facility_id, &"request_id": request_id, &"guest_ids": [guest_id]})
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(guest_id)
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(request_id)
|
||||
_expect(guest != null and request != null, "actual Guest and request exist")
|
||||
if guest == null or request == null: return
|
||||
_expect(guest.state == GuestRuntime.State.QUEUING and guest.queue_request_id == request_id, "actual queued ownership before advancing clock")
|
||||
_expect(request.quoted_price == _price and request.service_minutes == _duration and request.reserved_inventory_ids.size() == (1 if _case == "S5B_DEPENDENCY" else 0), "definition price/duration and one meal reservation")
|
||||
for id: StringName in request.reserved_inventory_ids:
|
||||
var reservation: InventoryReservation = _runtime.inventory_service.get_reservation(id)
|
||||
_expect(reservation != null and reservation.is_active() and reservation.requester_id == request_id, "public queue actually reserves meal")
|
||||
|
||||
func _prepare() -> bool:
|
||||
_fresh()
|
||||
var facility: FacilityInstance
|
||||
if _case == "S5B_DEPENDENCY":
|
||||
facility = _build_cafeteria(true)
|
||||
else:
|
||||
var bath: FacilityDefinition = _runtime.definition_registry.get_facility(&"public_bath")
|
||||
_expect(bath != null and bath.build_minutes > 0, "registered public bath definition")
|
||||
if bath == null: return false
|
||||
var build: CommandResult = _cmd(&"build_facility", {&"definition_id": &"public_bath", &"origin": Vector3i(-5, 0, WorldService.LAYER_FRONT)})
|
||||
if not build.is_success(): return false
|
||||
var began: int = _clock.absolute_minute
|
||||
while _runtime.construction_service.get_facilities().is_empty() and _clock.absolute_minute <= began + bath.build_minutes:
|
||||
_clock.advance_one_minute()
|
||||
var built: Array[Dictionary] = _runtime.construction_service.get_facilities()
|
||||
_expect(built.size() == 1 and _clock.absolute_minute - began == bath.build_minutes, "definition build duration and one constructed bath")
|
||||
if built.size() != 1: return false
|
||||
facility = _runtime.facility_service.get_instance(StringName(built[0][&"instance_id"]))
|
||||
if facility == null: return false
|
||||
_facility_id = facility.instance_id
|
||||
var definition: FacilityDefinition = _runtime.definition_registry.get_facility(facility.definition_id)
|
||||
_price = definition.base_price
|
||||
_duration = definition.base_service_minutes
|
||||
_patience = _runtime.guest_service.get_definition(&"traveler").base_patience
|
||||
_expect(_price > 0 and _duration > 1, "paid natural service definition")
|
||||
var staff_ids: Array[StringName] = []
|
||||
for index: int in range(definition.staff_min):
|
||||
var id: StringName = StringName("p04.s5.staff.%d" % index)
|
||||
staff_ids.append(id)
|
||||
_cmd(&"register_staff", {&"staff_id": id, &"role_id": &"server" if _case == "S5B_DEPENDENCY" else &"attendant", &"options": {&"state": StaffRuntime.State.OFF_DUTY, &"current_cell": Vector3i.ZERO}})
|
||||
_cmd(&"set_staff_work_assignment", {&"staff_id": id, &"template_id": &"DAY", &"facility_id": _facility_id})
|
||||
_expect(_clock.absolute_minute < _boundary - _duration - 1, "setup before fixed boundary")
|
||||
if _clock.absolute_minute >= _boundary - _duration - 1: return false
|
||||
_clock.advance_minutes(_boundary - _duration - 1 - _clock.absolute_minute)
|
||||
_expect(facility.active_staff_ids == staff_ids, "public staff attendance")
|
||||
if _case == "S5B_DEPENDENCY":
|
||||
_expect(_stock(facility.local_inventory_id, &"ingredient/basic_meal", 1, &"p04.s5.stock"), "one actual meal")
|
||||
_queue(GUEST, REQUEST, _boundary + _duration + 1)
|
||||
_clock.advance_one_minute()
|
||||
var active: Array = _runtime.facility_service.get_active_services(_facility_id)
|
||||
_expect(active.size() == 1, "one actual started service")
|
||||
if active.size() != 1: return false
|
||||
var service: ServiceInstance = active[0]
|
||||
_service_id = service.service_id
|
||||
_expect(service.started_at + _duration == _boundary, "natural completion boundary")
|
||||
_clock.advance_minutes(_boundary - 1 - _clock.absolute_minute)
|
||||
_reserve = _state.get_resource(&"mana")
|
||||
_recovery = {&"staff_ids": staff_ids, &"resume_minute": -1}
|
||||
_expect(_reserve > 0 and service.elapsed_minutes == _duration - 1, "positive reserve and duration-1 progress")
|
||||
return _clock.absolute_minute == _boundary - 1
|
||||
|
||||
func _connect_observations() -> void:
|
||||
_runtime.economy_service.transaction_posted.connect(func(id: StringName, entries: Array) -> void:
|
||||
var rows: Array = []
|
||||
for entry: LedgerEntry in entries: rows.append(entry.to_dictionary())
|
||||
_receipts.append({&"id": id, &"minute": _clock.absolute_minute, &"entries": rows})
|
||||
)
|
||||
_clock.tick_phase_started.connect(func(phase: int, minute: int) -> void:
|
||||
var label: String = "phase-%d-%s" % [minute, SimulationKernel.TickPhase.keys()[phase]]
|
||||
var row: Dictionary = _capture()
|
||||
row[&"phase"] = SimulationKernel.TickPhase.keys()[phase]
|
||||
_assert_phase(row)
|
||||
_phases.append(_write(label, row))
|
||||
)
|
||||
|
||||
func _assert_phase(point: Dictionary) -> void:
|
||||
_expect(point.has(&"snapshot") and point.has(&"mana"), "complete raw phase observation")
|
||||
|
||||
func _capture() -> Dictionary:
|
||||
var snapshot: Dictionary = _runtime.create_snapshot()
|
||||
var operations: Dictionary = snapshot[&"systems"][&"runtime"][&"operations"]
|
||||
var facility: FacilityInstance = _runtime.facility_service.get_instance(_facility_id)
|
||||
var service: ServiceInstance = _runtime.facility_service.get_service(_service_id)
|
||||
var event: EventInstance = _runtime.event_service.get_instance(&"p04.destroy.instance")
|
||||
var kitchen: FacilityInstance = _runtime.facility_service.get_instance(facility.related_facility_id)
|
||||
var actors: Dictionary = {}
|
||||
var requests: Dictionary = {}
|
||||
var wallets: Dictionary = {}
|
||||
for id: StringName in [GUEST, QUEUED_GUEST]:
|
||||
var guest: GuestRuntime = _runtime.guest_service.get_guest(id)
|
||||
actors[String(id)] = guest.create_snapshot() if guest != null else {}
|
||||
wallets[String(id)] = _runtime.economy_service.get_balance(StringName("GUEST_WALLET:" + String(id)))
|
||||
for id: StringName in [REQUEST, QUEUED_REQUEST]:
|
||||
var request: ServiceRequest = _runtime.facility_service.get_request(id)
|
||||
requests[String(id)] = request.create_snapshot() if request != null else {}
|
||||
var reservations: Array = []
|
||||
for reservation: InventoryReservation in _runtime.inventory_service.get_reservations(false): reservations.append(reservation.create_snapshot())
|
||||
var ledger: Array = []
|
||||
for entry: LedgerEntry in _runtime.economy_service.get_entries(): ledger.append(entry.to_dictionary())
|
||||
return {&"snapshot": snapshot, &"minute": _clock.absolute_minute,
|
||||
&"inputs": {&"case": _case, &"seed": SEEDS[_case], &"serial": _serial, &"facility_id": _facility_id, &"service_id": _service_id,
|
||||
&"boundary": _boundary, &"price": _price, &"duration": _duration, &"patience": _patience, &"reserve": _reserve, &"recovery": _recovery.duplicate(true),
|
||||
&"kitchen_premise": "ActorHardening physical FLEX fixture; not natural hire/unlock"},
|
||||
&"related_facility_operating": facility.related_facility_operating, &"mana": _state.get_resource(&"mana"), &"kitchen": kitchen.create_snapshot() if kitchen != null else {}, &"graph_invariants": _runtime.mana_network_service.validate_invariants(),
|
||||
&"facility": facility.create_snapshot(), &"service": service.create_snapshot() if service != null else {},
|
||||
&"event": event.create_snapshot() if event != null else {}, &"guests": actors, &"requests": requests, &"wallets": wallets,
|
||||
&"reservations": reservations, &"ledger": ledger, &"receipts": _receipts.duplicate(true),
|
||||
&"player_cash": _runtime.economy_service.get_balance(_runtime.PLAYER_CASH_ACCOUNT),
|
||||
&"available_meals": _runtime.inventory_service.available_quantity(facility.local_inventory_id, &"ingredient/basic_meal"),
|
||||
&"active_services": _runtime.facility_service.get_active_services(_facility_id).size(),
|
||||
&"operations_enabled": operations[&"enabled"], &"saved_coordinator": operations[&"coordinator"],
|
||||
&"actual_coordinator_absent": _runtime.operations_coordinator == null, &"unrelated_getters_called": false}
|
||||
|
||||
func _labels() -> Array[String]:
|
||||
return ["boundary", "held", "recovered"]
|
||||
|
||||
func _advance(label: String) -> void:
|
||||
if label == "boundary":
|
||||
_cmd(&"spend_resources", {&"cost": {&"mana": _reserve}})
|
||||
_clock.advance_one_minute()
|
||||
elif label == "recovered":
|
||||
_expect(_clock.absolute_minute == _boundary + 1, "recover only after held")
|
||||
_cmd(&"change_resources", {&"deltas": {&"mana": _reserve}})
|
||||
_recovery[&"key"] = _commands.back()[&"idempotency_key"]
|
||||
while _clock.absolute_minute < _boundary + _duration:
|
||||
_clock.advance_one_minute()
|
||||
var point: Dictionary = _capture()
|
||||
if point[&"service"][&"state"] == "COMPLETED":
|
||||
_recovery[&"resume_minute"] = _clock.absolute_minute
|
||||
break
|
||||
_assert_stopped(point)
|
||||
_expect(_recovery[&"resume_minute"] > 0, "bounded observed recovery completion")
|
||||
elif label == "duplicate_guard":
|
||||
var before_replay: Dictionary = _runtime.create_snapshot()
|
||||
var replay: CommandResult = _runtime.submit_command(&"change_resources", {&"deltas": {&"mana": _reserve}}, &"player", &"", 10, -1, _recovery[&"key"])
|
||||
var after_replay: Dictionary = _runtime.create_snapshot()
|
||||
_expect(replay.is_success() and replay.is_replay and var_to_bytes(before_replay) == var_to_bytes(after_replay), "same key replay changes no Runtime field")
|
||||
_replay_reference = _write("resource-replay", {&"before": before_replay, &"after": after_replay, &"result": replay.to_dictionary()})
|
||||
_clock.advance_one_minute()
|
||||
else:
|
||||
_clock.advance_one_minute()
|
||||
|
||||
func _writer_case() -> void:
|
||||
if not _prepare(): return
|
||||
var before: Dictionary = _capture()
|
||||
_assert_before(before)
|
||||
_points["before"] = _write("before", before)
|
||||
_connect_observations()
|
||||
for label: String in _labels():
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
_assert_outcome(before, actual)
|
||||
_save_point(label, actual)
|
||||
_advance("duplicate_guard")
|
||||
var guard: Dictionary = _capture()
|
||||
_assert_outcome(before, guard)
|
||||
_points["duplicate_guard"] = _write("duplicate_guard", guard)
|
||||
_branches[_case] = _write("case", {&"resource_replay": _replay_reference, &"points": _points.duplicate(true), &"phases": _phases.duplicate(true),
|
||||
&"commands": _write("commands", {&"submitted": _commands, &"completed_including_fixture": _results})})
|
||||
|
||||
func _assert_before(point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
_expect(point[&"mana"] > 0 and point[&"facility"][&"power_available"] and point[&"related_facility_operating"], "initial power and dependency")
|
||||
_expect(point[&"service"][&"state"] == "ACTIVE" and point[&"service"][&"elapsed_minutes"] == _duration - 1, "natural pre-completion point")
|
||||
|
||||
func _assert_unconfounded(point: Dictionary) -> void:
|
||||
_expect(not point[&"operations_enabled"] and point[&"actual_coordinator_absent"], "isolated fixture")
|
||||
_expect(point[&"facility"][&"operator_open"] and point[&"facility"][&"durability"] > 0 and point[&"event"].is_empty(), "no closure or destruction")
|
||||
_expect(point[&"facility"][&"active_staff_ids"] == _recovery[&"staff_ids"], "no shift confound")
|
||||
_expect(point[&"graph_invariants"][&"valid"], "graph invariants")
|
||||
if point[&"service"][&"state"] != "COMPLETED":
|
||||
_expect(point[&"guests"][String(GUEST)][&"state"] == "RECEIVING_SERVICE" and point[&"guests"][String(GUEST)][&"planned_exit_at"] > point[&"minute"], "guest ownership and no exit")
|
||||
|
||||
func _assert_stopped(point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
var expected_reason: String = "RELATED_FACILITY_INACTIVE" if _case == "S5B_DEPENDENCY" and not point[&"related_facility_operating"] else "NO_POWER"
|
||||
_expect(point[&"service"][&"state"] == "PAUSED" and point[&"service"][&"interruption_reason"] == expected_reason, "approved actual reason priority")
|
||||
_expect(point[&"service"][&"elapsed_minutes"] == _duration - 1 and point[&"receipts"].is_empty(), "stopped progress and no payment")
|
||||
if point[&"minute"] <= _boundary + 1:
|
||||
_expect(point[&"mana"] == 0 and not point[&"facility"][&"power_available"], "real depleted reserve projects no power")
|
||||
if _case == "S5B_DEPENDENCY":
|
||||
_expect(point[&"kitchen"][&"state"] == "NO_POWER" and not point[&"related_facility_operating"], "kitchen NO_POWER before dependent reason")
|
||||
else:
|
||||
_expect(point[&"related_facility_operating"], "pure no dependency")
|
||||
|
||||
func _assert_reservation(before: Dictionary, point: Dictionary, request_id: StringName, state: String, terminal: int) -> void:
|
||||
var prior: Array = []
|
||||
var current: Array = []
|
||||
for row: Dictionary in before[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): prior.append(row)
|
||||
for row: Dictionary in point[&"reservations"]:
|
||||
if row[&"requester_id"] == String(request_id): current.append(row)
|
||||
_expect(prior.size() == 1 and current.size() == 1, "one original reservation per request")
|
||||
if prior.size() != 1 or current.size() != 1: return
|
||||
_expect(prior[0][&"reservation_id"] == current[0][&"reservation_id"] and current[0][&"state"] == state and current[0][&"terminal_at"] == terminal, "same reservation ID/state/terminal time; no second release")
|
||||
if state == "ACTIVE":
|
||||
_expect(var_to_bytes(prior[0]) == var_to_bytes(current[0]), "entire held reservation unchanged")
|
||||
var request: Dictionary = point[&"requests"][String(request_id)]
|
||||
_expect(not request.is_empty() and request[&"reserved_inventory_ids"].has(current[0][&"reservation_id"]), "active reservation has live request owner")
|
||||
else:
|
||||
_expect(point[&"requests"][String(request_id)].is_empty(), "terminal reservation has no orphan request")
|
||||
if state == "RELEASED":
|
||||
var release_count: int = 0
|
||||
var inventory: Dictionary = point[&"snapshot"][&"systems"][&"runtime"][&"inventory"]
|
||||
for operation: Dictionary in inventory[&"idempotency_results"]:
|
||||
var result: Dictionary = operation[&"result"]
|
||||
if operation[&"action"] == "release" and String(result.get(&"reservation_id", "")) == current[0][&"reservation_id"]:
|
||||
release_count += 1
|
||||
_expect(bool(result.get(&"ok", false)) and result.get(&"quantity", 0) == current[0][&"quantity"], "release returned actual reserved quantity, not an orphan repair/retry")
|
||||
_expect(release_count == 1, "one raw inventory release operation for original reservation")
|
||||
|
||||
func _assert_outcome(before: Dictionary, point: Dictionary) -> void:
|
||||
_assert_unconfounded(point)
|
||||
var service: Dictionary = point[&"service"]
|
||||
_expect(service[&"service_id"] == before[&"service"][&"service_id"], "same service ID")
|
||||
var paid: bool = service[&"state"] == "COMPLETED"
|
||||
if not paid:
|
||||
_assert_stopped(point)
|
||||
_expect(var_to_bytes(point[&"ledger"]) == var_to_bytes(before[&"ledger"]), "entire stopped ledger unchanged")
|
||||
_expect(var_to_bytes(point[&"requests"][String(REQUEST)]) == var_to_bytes(before[&"requests"][String(REQUEST)]), "same live request and ownership")
|
||||
else:
|
||||
_expect(point[&"facility"][&"power_available"] and service[&"elapsed_minutes"] == _duration and point[&"active_services"] == 0, "one recovered completion")
|
||||
_expect(point[&"receipts"].size() == 1, "exactly one receipt")
|
||||
var expected: Array = before[&"ledger"].duplicate(true)
|
||||
for receipt: Dictionary in point[&"receipts"]: expected.append_array(receipt[&"entries"])
|
||||
_expect(var_to_bytes(expected) == var_to_bytes(point[&"ledger"]), "ledger exactly one receipt")
|
||||
if _case == "S5B_DEPENDENCY":
|
||||
_assert_reservation(before, point, REQUEST, "CONSUMED" if paid else "ACTIVE", service[&"completed_at"] if paid else -1)
|
||||
else:
|
||||
_expect(point[&"reservations"].is_empty(), "bath has no item inputs; ownership proven through request, service and guest")
|
||||
_expect(point[&"wallets"][String(GUEST)] == _price * (1 if paid else 2) and point[&"player_cash"] == before[&"player_cash"] + (_price if paid else 0), "one price or zero")
|
||||
|
||||
func _save_point(label: String, point: Dictionary) -> void:
|
||||
_points[label] = _write(label, point)
|
||||
var path: String = "user://saves/p04_s5/" + _case + "-" + label
|
||||
var saved: Dictionary = SaveService.save_snapshot(path, point[&"snapshot"])
|
||||
var inspected: Dictionary = SaveService.inspect_snapshot(path)
|
||||
_expect(bool(saved.get(&"ok", false)) and bool(inspected.get(&"ok", false)) and bool(inspected.get(&"read_only", false)), "public formal save/inspect")
|
||||
_expect(var_to_bytes(inspected.get(&"snapshot", {})) == var_to_bytes(point[&"snapshot"]), "all typed fields preserved by formal package")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(point[&"snapshot"]), "formal saving leaves Runtime unchanged")
|
||||
_slots[_case + "-" + label] = ProjectSettings.globalize_path(path)
|
||||
# Snapshot bytes exist once in point raw and once in the required formal
|
||||
# package; indexes do not embed inspection/load copies of those bytes.
|
||||
|
||||
func _write(label: String, value: Variant, global_name: bool = false) -> Dictionary:
|
||||
var name: String = label if global_name else _mode + "-" + _case + "-" + label
|
||||
var reference: Dictionary = {}
|
||||
for extension: String in ["var", "json"]:
|
||||
var path: String = _evidence_dir.path_join(name + "." + extension)
|
||||
_expect(not FileAccess.file_exists(path), "raw point written exactly once " + name)
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
|
||||
_expect(file != null, "raw file " + name)
|
||||
if file == null: continue
|
||||
if extension == "var": file.store_buffer(var_to_bytes(value))
|
||||
else: file.store_string(JSON.stringify(value, "\t"))
|
||||
file.flush()
|
||||
file.close()
|
||||
reference[extension] = {&"path": path, &"sha256": FileAccess.get_sha256(path)}
|
||||
return reference
|
||||
|
||||
func _read(reference: Dictionary) -> Dictionary:
|
||||
var item: Dictionary = reference["var"]
|
||||
var path: String = item["path"]
|
||||
_expect(FileAccess.get_sha256(path) == item["sha256"], "raw typed SHA")
|
||||
var json_ref: Dictionary = reference["json"]
|
||||
_expect(FileAccess.get_sha256(json_ref["path"]) == json_ref["sha256"], "raw JSON SHA")
|
||||
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
||||
_expect(file != null, "typed input readable")
|
||||
if file == null: return {}
|
||||
var value: Variant = bytes_to_var(file.get_buffer(file.get_length()))
|
||||
file.close()
|
||||
_expect(value is Dictionary, "typed input dictionary")
|
||||
return value if value is Dictionary else {}
|
||||
|
||||
func _restore_inputs(point: Dictionary) -> void:
|
||||
var inputs: Dictionary = point[&"inputs"]
|
||||
_expect(_serial == 0, "Reader starts with serial zero before importing external inputs")
|
||||
_serial = inputs[&"serial"]
|
||||
_facility_id = inputs[&"facility_id"]
|
||||
_service_id = inputs[&"service_id"]
|
||||
_boundary = inputs[&"boundary"]
|
||||
_price = inputs[&"price"]
|
||||
_duration = inputs[&"duration"]
|
||||
_patience = inputs[&"patience"]
|
||||
_reserve = inputs[&"reserve"]
|
||||
_recovery = inputs[&"recovery"].duplicate(true)
|
||||
|
||||
func _load_point(path: String, expected: Dictionary) -> bool:
|
||||
_fresh()
|
||||
_restore_inputs(expected)
|
||||
var loaded: Dictionary = SaveService.load_snapshot(path)
|
||||
_expect(bool(loaded.get(&"ok", false)), "public formal load")
|
||||
if not bool(loaded.get(&"ok", false)): return false
|
||||
_expect(var_to_bytes(loaded[&"snapshot"]) == var_to_bytes(expected[&"snapshot"]), "all saved typed fields equal Writer")
|
||||
var restored: bool = _runtime.restore_snapshot(loaded[&"snapshot"])
|
||||
_expect(restored, "public Runtime restore")
|
||||
_expect(var_to_bytes(_runtime.create_snapshot()) == var_to_bytes(expected[&"snapshot"]), "all restored typed fields equal Writer")
|
||||
return restored
|
||||
|
||||
func _is_replayed_phase(row: Dictionary) -> bool:
|
||||
return int(row[&"minute"]) > _boundary or (int(row[&"minute"]) == _boundary and row[&"phase"] == SimulationKernel.TickPhase.keys()[SimulationKernel.TickPhase.APPLY_COMMANDS])
|
||||
|
||||
func _assert_expected_phase_roster(references: Array) -> void:
|
||||
var expected_pairs: Array = []
|
||||
var end: int = int(_recovery[&"resume_minute"]) + 1
|
||||
for minute: int in range(_boundary, end):
|
||||
for phase: int in range(SimulationKernel.TickPhase.size()):
|
||||
expected_pairs.append([minute if phase == SimulationKernel.TickPhase.APPLY_COMMANDS else minute + 1, SimulationKernel.TickPhase.keys()[phase]])
|
||||
var actual_pairs: Array = []
|
||||
for reference: Dictionary in references:
|
||||
var row: Dictionary = _read(reference)
|
||||
actual_pairs.append([int(row[&"minute"]), row[&"phase"]])
|
||||
_expect(actual_pairs == expected_pairs, "complete dynamic replay phase roster with no omissions")
|
||||
|
||||
func _reader() -> void:
|
||||
var file: FileAccess = FileAccess.open(_evidence_dir.path_join("writer.json"), FileAccess.READ)
|
||||
_expect(file != null, "Writer receipt")
|
||||
if file == null: return
|
||||
var writer: Dictionary = JSON.parse_string(file.get_as_text())
|
||||
file.close()
|
||||
_expect(int(writer["pid"]) != OS.get_process_id() and writer["profile"] != OS.get_user_data_dir(), "independent Reader PID/profile")
|
||||
for branch: String in CASES:
|
||||
_case = branch
|
||||
var writer_case: Dictionary = _read(writer["branches"][branch])
|
||||
var hashes: Dictionary = {}
|
||||
var restored_refs: Dictionary = {}
|
||||
for label: String in _labels():
|
||||
var key: String = branch + "-" + label
|
||||
var original: String = writer["slots"][key]
|
||||
var target: String = ProjectSettings.globalize_path("user://saves/p04_s5/" + key)
|
||||
_expect(DirAccess.make_dir_recursive_absolute(target) == OK, "private Reader formal slot")
|
||||
var directory: DirAccess = DirAccess.open(original)
|
||||
_expect(directory != null, "Writer formal slot readable")
|
||||
if directory == null: return
|
||||
var members: PackedStringArray = directory.get_files()
|
||||
members.sort()
|
||||
_expect(directory.get_directories().is_empty() and members == PackedStringArray(["journal.log", "manifest.json", "thumbnail.png", "world.bin"]), "all formal members enumerated")
|
||||
var member_hashes: Dictionary = {}
|
||||
for member: String in members:
|
||||
var sha: String = FileAccess.get_sha256(original.path_join(member))
|
||||
_expect(DirAccess.copy_absolute(original.path_join(member), target.path_join(member)) == OK, "copy every formal member")
|
||||
var copied_sha: String = FileAccess.get_sha256(target.path_join(member))
|
||||
_expect(not sha.is_empty() and sha == copied_sha, "original/copy member SHA equality")
|
||||
member_hashes[member] = {&"original_before": sha, &"copy_before": copied_sha}
|
||||
hashes[key] = {&"original": original, &"copy": target, &"members": member_hashes}
|
||||
_slots[key] = target
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
if not _load_point(target, expected): return
|
||||
restored_refs[label] = _write(label + "-restored", _capture())
|
||||
var before: Dictionary = _read(writer_case[&"points"]["before"])
|
||||
var boundary: Dictionary = _read(writer_case[&"points"]["boundary"])
|
||||
if not _load_point(_slots[branch + "-boundary"], boundary): return
|
||||
_assert_stopped(_capture())
|
||||
_connect_observations()
|
||||
var replay_refs: Dictionary = {}
|
||||
for label: String in ["held", "recovered", "duplicate_guard"]:
|
||||
_advance(label)
|
||||
var actual: Dictionary = _capture()
|
||||
var expected: Dictionary = _read(writer_case[&"points"][label])
|
||||
_expect(var_to_bytes(actual) == var_to_bytes(expected), "replay complete typed point including Runtime, inputs, receipts " + label)
|
||||
_assert_outcome(before, actual)
|
||||
replay_refs[label] = _write(label + "-replay", actual)
|
||||
var expected_phases: Array = []
|
||||
for reference: Dictionary in writer_case[&"phases"]:
|
||||
if _is_replayed_phase(_read(reference)): expected_phases.append(reference)
|
||||
_assert_expected_phase_roster(expected_phases)
|
||||
_expect(_phases.size() == expected_phases.size(), "complete phase count")
|
||||
if _phases.size() == expected_phases.size():
|
||||
for index: int in range(_phases.size()):
|
||||
_expect(var_to_bytes(_read(_phases[index])) == var_to_bytes(_read(expected_phases[index])), "same phase order and all typed phase state")
|
||||
for key: String in hashes:
|
||||
var row: Dictionary = hashes[key]
|
||||
for member: String in row[&"members"]:
|
||||
var item: Dictionary = row[&"members"][member]
|
||||
item[&"original_after"] = FileAccess.get_sha256(String(row[&"original"]).path_join(member))
|
||||
item[&"copy_after"] = FileAccess.get_sha256(String(row[&"copy"]).path_join(member))
|
||||
_expect(item[&"original_before"] == item[&"original_after"] and item[&"copy_before"] == item[&"copy_after"], "original/copy unchanged after all loads and replay")
|
||||
_branches[branch] = _write("case", {&"resource_replay": _replay_reference, &"restored": restored_refs, &"replay": replay_refs, &"phases": _phases,
|
||||
&"member_hashes": hashes, &"recovery_commands": _commands, &"replay_completed_commands": _results})
|
||||
@@ -0,0 +1,596 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S5-only native entry. --check-only never loads ownership helpers or starts Godot."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import ast
|
||||
import builtins
|
||||
import inspect
|
||||
import types
|
||||
import copy
|
||||
import hashlib
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import stat
|
||||
import sys
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
BUILD = Path("/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build")
|
||||
PRODUCT = BUILD / "audit-support/p04-r17-full-inputs-r01/product"
|
||||
CACHE_RUN = BUILD / "p04-final-product-full-r01"
|
||||
TEST = "tests/s3/test_runtime_staff_assignment.gd"
|
||||
QA = HERE / "p04_s5_power_probe.gd"
|
||||
ENGINE = Path("/Applications/Godot.app/Contents/MacOS/Godot")
|
||||
ENGINE_SHA256 = "c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf"
|
||||
MANIFEST = BUILD / "audit-support/p04-r17-full-inputs-r01/MANIFEST.json"
|
||||
MANIFEST_SHA = "a15f29fa12537f6d8aaf7cabea390922a9f16e3eb9c10981e5742aafc8c4c8a8"
|
||||
CONTRACTS = BUILD / "audit-support/p04-r17-full-inputs-r01/fixed/6ce6b28c5985e8a4f13b78bfeb1f1e09f6216d874fb01e0656ec1633bfa01cd7/completion_marker_contracts.json"
|
||||
CASES = ["S5A_PURE", "S5B_DEPENDENCY"]
|
||||
LABELS = {case: ["boundary", "held", "recovered"] for case in CASES}
|
||||
SLOTS = sorted(case + "-" + label for case in CASES for label in LABELS[case])
|
||||
MATERIALS = [MANIFEST, BUILD / "audit-support/p04-review-repair22-r01/ACTUAL_CURRENT_PUBLIC_POWER_FUNCTIONS.json"]
|
||||
|
||||
def r17_relocation_guard(root):
|
||||
if root != PRODUCT.resolve() or not HERE.is_relative_to(BUILD.resolve()):
|
||||
raise ValueError("Root must stage this exact QA/wrapper/plan inside own B")
|
||||
if pin(MANIFEST)["sha256"] != MANIFEST_SHA:
|
||||
raise ValueError("immutable full R17 relocation manifest")
|
||||
manifest = json.loads(MANIFEST.read_text())
|
||||
for name, source in manifest["product_source"].items():
|
||||
# Old absolute paths are lookup keys only. Never open them.
|
||||
row = manifest["original_to_relocated"][source["path"]]
|
||||
actual = pin(root / name)
|
||||
if Path(row["relocated"]).resolve() != root / name or any(
|
||||
actual[k] != source[k] or actual[k] != row[k] for k in ("bytes", "sha256")):
|
||||
raise ValueError("relocated product differs: " + name)
|
||||
|
||||
def probe_guard(root):
|
||||
probe = QA.read_text()
|
||||
import re
|
||||
if ":=" in probe or re.search(r"\bvar\s+\w+\s*=", probe) or re.search(r"\bfor\s+\w+\s+in\b", probe):
|
||||
raise ValueError("explicit typed declarations/loops required")
|
||||
if re.search(r"\.(?:patience_remaining|elapsed_minutes|power_available|capacity|hold_until)\s*=(?!=)", probe):
|
||||
raise ValueError("forbidden direct state mutation")
|
||||
for forbidden in ["evaluate_now(", "configure_operations(", "._update_shift_states(", ".record_duty_minute("]:
|
||||
if forbidden in probe:
|
||||
raise ValueError("forbidden bypass: " + forbidden)
|
||||
# Constants and public bodies are read from the actual selected source, not a memo.
|
||||
mana = (root / "scripts/mana/mana_network_service.gd").read_text()
|
||||
for key in ("EVALUATION_INTERVAL_MINUTES", "MINIMUM_OFF_HOLD_MINUTES"):
|
||||
if not re.search(r"const " + key + r" := 10\b", mana):
|
||||
raise ValueError("source timing changed: " + key)
|
||||
|
||||
|
||||
def module(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
value = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(value)
|
||||
return value
|
||||
|
||||
def pin(path):
|
||||
path = Path(path)
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise ValueError("missing exact input: " + str(path))
|
||||
data = path.read_bytes()
|
||||
return dict(path=str(path), bytes=len(data), sha256=hashlib.sha256(data).hexdigest())
|
||||
|
||||
def load_uid_helper(root, r, refs):
|
||||
hp = root / "tools/run_p13_reference_tests.py"
|
||||
descriptor = pin(hp)
|
||||
r.require(all(descriptor[k] == refs["tools/run_p13_reference_tests.py"][k]
|
||||
for k in ("bytes", "sha256")), "actual Q UID helper pin")
|
||||
src = hp.read_bytes()
|
||||
r.require(len(src) == descriptor["bytes"] and
|
||||
hashlib.sha256(src).hexdigest() == descriptor["sha256"], "stable helper read")
|
||||
tree = ast.parse(src, filename=str(hp))
|
||||
nodes = []
|
||||
for name in ("uid_plan", "uid_snapshot"):
|
||||
matches = [n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name == name]
|
||||
r.require(len(matches) == 1, "exactly one UID helper: " + name)
|
||||
nodes.append(matches[0])
|
||||
ns = {"__builtins__": builtins, "shared": r, "re": re, "stat": stat}
|
||||
exec(compile(ast.Module(body=nodes, type_ignores=[]), str(hp), "exec"), ns)
|
||||
binding = dict(helper_path="tools/run_p13_reference_tests.py",
|
||||
helper_sha256=descriptor["sha256"], helper_bytes=descriptor["bytes"],
|
||||
functions={n.name: hashlib.sha256(ast.get_source_segment(src.decode(), n).encode()).hexdigest()
|
||||
for n in nodes}, shared_module="tools/run_all_tests.py",
|
||||
shared_sha256=r.sha(root / "tools/run_all_tests.py"))
|
||||
return ns["uid_plan"], ns["uid_snapshot"], binding
|
||||
|
||||
|
||||
def make_uid_policy(r, uid_snapshot, source, plan, out):
|
||||
state = dict(source=source, plan=plan, accepted=None, phase=None)
|
||||
|
||||
def reports(stage, exp, previous, requested):
|
||||
r.require(not previous and not requested, "S5 has no generated reports")
|
||||
snap = uid_snapshot(stage, source, plan)
|
||||
if state["accepted"] is None:
|
||||
r.require(state["phase"] == "import", "UID generation only during import")
|
||||
state["accepted"] = snap
|
||||
exp.update({n: {k: p[k] for k in ("bytes", "sha256")} for n, p in snap.items()})
|
||||
r.write(out / "accepted-uids.json", snap)
|
||||
r.require(snap == state["accepted"], "sealed UID bytes unchanged")
|
||||
return r.declared_reports(stage, exp, previous, requested)
|
||||
|
||||
execute = types.FunctionType(r.execute_phase.__code__, dict(vars(r), declared_reports=reports),
|
||||
"execute_phase", r.execute_phase.__defaults__, r.execute_phase.__closure__)
|
||||
r.require(execute.__code__ is r.execute_phase.__code__, "unchanged execute_phase code object")
|
||||
|
||||
def phase_call(*args):
|
||||
stage, phase_out, phase = args[1:4]
|
||||
r.require(phase_out == out, "UID receipt output binding")
|
||||
state["phase"] = phase
|
||||
before, after = None, None
|
||||
try:
|
||||
if phase == "import":
|
||||
r.require(state["accepted"] is None and not (out / "uid-plan.json").exists(), "single UID import")
|
||||
r.require(r.inventory(stage, stage=True) == source, "original stage before import")
|
||||
before = {}
|
||||
r.write(out / "uid-plan.json", plan)
|
||||
else:
|
||||
before = uid_snapshot(stage, source, plan)
|
||||
r.require(before == state["accepted"], "UID set before phase unchanged")
|
||||
row = execute(*args)
|
||||
if row["passed"]:
|
||||
snap = uid_snapshot(stage, source, plan)
|
||||
r.require(snap == state["accepted"], "UID set after phase unchanged")
|
||||
after = snap
|
||||
return row
|
||||
finally:
|
||||
r.write(out / (phase + "-uids.json"), dict(before=before, after=after))
|
||||
|
||||
return reports, phase_call, state
|
||||
|
||||
|
||||
def engine_pin(path, expected=None):
|
||||
path = Path(path)
|
||||
if path != ENGINE or any(p.is_symlink() for p in (path, *path.parents)):
|
||||
raise ValueError("exact installed engine required; no symlink or relocated input")
|
||||
if path.resolve(strict=True) != ENGINE:
|
||||
raise ValueError("installed engine resolved path changed")
|
||||
before = path.stat()
|
||||
if not stat.S_ISREG(before.st_mode):
|
||||
raise ValueError("installed engine must be regular")
|
||||
digest = hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
after = path.stat()
|
||||
fields = ("st_dev", "st_ino", "st_size", "st_mtime_ns", "st_ctime_ns", "st_mode")
|
||||
if any(getattr(before, k) != getattr(after, k) for k in fields):
|
||||
raise ValueError("engine changed while hashing")
|
||||
if any(p.is_symlink() for p in (path, *path.parents)) or path.resolve(strict=True) != ENGINE:
|
||||
raise ValueError("engine path changed while hashing")
|
||||
value = dict(path=str(path), resolved_path=str(path.resolve(strict=True)),
|
||||
bytes=after.st_size, mtime_ns=after.st_mtime_ns, mode=after.st_mode,
|
||||
device=after.st_dev, inode=after.st_ino, ctime_ns=after.st_ctime_ns, sha256=digest)
|
||||
if digest != ENGINE_SHA256:
|
||||
raise ValueError("engine differs from installed engine constant")
|
||||
if expected is not None and value != expected:
|
||||
raise ValueError("engine identity changed")
|
||||
return value
|
||||
|
||||
C_REL = "docs/plans/original-completion-2026-10-02/execution/completion_marker_contracts.json"
|
||||
|
||||
|
||||
def select_mode(args):
|
||||
"""Pure CLI mode selection; no filesystem or Git access."""
|
||||
triple = (args.main, args.submission, args.tested_tree)
|
||||
if any(value is not None for value in triple):
|
||||
if (not all(isinstance(value, str) and re.fullmatch(r"[0-9a-f]{40}", value)
|
||||
for value in triple) or args.historical_r17):
|
||||
raise ValueError("fresh-q requires a full HEX40 triple without --historical-r17")
|
||||
return "fresh-q"
|
||||
if args.historical_r17 and args.check_only:
|
||||
return "historical-r17-readonly"
|
||||
raise ValueError("historical R17 is read-only; require --historical-r17 --check-only")
|
||||
|
||||
|
||||
def fresh_q_guard(root, M, S, Qt):
|
||||
gate = module("s5_gate", root / "tools/check_p04_order_evidence.py")
|
||||
gate.require(Path(gate.__file__).resolve() == root / gate.CHECKER_PATH
|
||||
and not root.is_symlink(), "fresh-q checker/root location")
|
||||
gate.require(M == gate.MAIN_ANCHOR, "fresh-q main anchor")
|
||||
commit, tree, entries = gate.actual_checkout()
|
||||
gate.require(tree == Qt and commit != S and commit != M, "fresh-q checkout carrier")
|
||||
gate.require(Path(os.environ["BUILD_OUTPUT_DIR"]).resolve() == BUILD.resolve(),
|
||||
"fresh-q own BUILD_OUTPUT_DIR")
|
||||
with gate.isolated_objects():
|
||||
for value in (M, S):
|
||||
gate.require(gate.git_bytes("cat-file", "-t", value) == b"commit\n", "commit input")
|
||||
gate.submission_role(S)
|
||||
composed = gate.git_bytes("-c", "rerere.enabled=false", "merge-tree", "--write-tree", M, S).decode().strip()
|
||||
gate.require(re.fullmatch(r"[0-9a-f]{40}", composed) is not None and composed == Qt,
|
||||
"fresh-q recomputed tree")
|
||||
gate.require(hashlib.sha256(gate.git_bytes("show", M + ":tools/run_all_tests.py")).hexdigest()
|
||||
== gate.SELECTION_SHA, "selection authority")
|
||||
gate.require(gate.product_manifest(gate.tree_entries(M)) == gate.product_manifest(entries),
|
||||
"fresh-q product unchanged")
|
||||
sel = gate.selected_manifest(entries, require_checker=True)
|
||||
r = module("s5_guard_runner", root / "tools/run_all_tests.py")
|
||||
source_before = r.source_refs(root)
|
||||
gate.require(source_before.keys() == sel.keys() and TEST in sel,
|
||||
"fresh-q selected source keys")
|
||||
gate.require({name: {key: value[key] for key in ("bytes", "sha256")}
|
||||
for name, value in source_before.items()} == sel, "fresh-q selected bytes")
|
||||
gate.require(C_REL in entries, "fresh-q contracts entry")
|
||||
blob = gate.git_bytes("cat-file", "blob", entries[C_REL][1])
|
||||
contract_pin = pin(root / C_REL)
|
||||
gate.require(contract_pin["bytes"] == len(blob)
|
||||
and contract_pin["sha256"] == hashlib.sha256(blob).hexdigest(),
|
||||
"fresh-q contracts pin")
|
||||
build = BUILD.resolve()
|
||||
gate.require(HERE.is_relative_to(build), "fresh-q staged HERE inside BUILD")
|
||||
for staged in [HERE, *HERE.rglob("*")]:
|
||||
current = build
|
||||
for part in staged.relative_to(build).parts:
|
||||
current = current / part
|
||||
gate.require(not current.is_symlink(), "fresh-q staged symlink")
|
||||
gate.require(staged.is_dir() or stat.S_ISREG(staged.stat().st_mode),
|
||||
"fresh-q staged regular file")
|
||||
return dict(M=M, S=S, Q_tree=Qt, recomputed_Q=composed, checkout_commit=commit,
|
||||
checkout_role="nonproduction Root checkout carrier of tree Q; NOT normal Runner C",
|
||||
selected_manifest_sha256=gate.manifest_digest(sel), selected_file_count=len(sel),
|
||||
product_file_count=len(sel)-1, selection_authority_sha256=gate.SELECTION_SHA)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--project-root", type=Path, default=None)
|
||||
parser.add_argument("--output", type=Path, default=BUILD / "p04-s5-native-r32")
|
||||
parser.add_argument("--plan", type=Path, default=BUILD / "p04-s5-plan-r32.json")
|
||||
parser.add_argument("--cache-run", type=Path, default=None,
|
||||
help="optional passed registered cache; default is fresh, without cache access")
|
||||
parser.add_argument("--godot", type=Path, default=ENGINE)
|
||||
parser.add_argument("--check-only", action="store_true")
|
||||
parser.add_argument("--main")
|
||||
parser.add_argument("--submission")
|
||||
parser.add_argument("--tested-tree")
|
||||
parser.add_argument("--historical-r17", action="store_true")
|
||||
args = parser.parse_args()
|
||||
mode = select_mode(args)
|
||||
if mode == "fresh-q" and args.project_root is None:
|
||||
raise ValueError("fresh-q requires explicit --project-root")
|
||||
project_root = args.project_root if args.project_root is not None else PRODUCT
|
||||
if project_root.is_symlink():
|
||||
raise ValueError("project root symlink")
|
||||
engine_descriptor = engine_pin(args.godot)
|
||||
if args.cache_run is not None and args.cache_run.resolve() != CACHE_RUN.resolve():
|
||||
raise ValueError("only registered own-B full-run cache")
|
||||
root = project_root.resolve()
|
||||
out = args.output.resolve()
|
||||
if not out.is_relative_to(BUILD.resolve()) or not args.plan.resolve().is_relative_to(BUILD.resolve()):
|
||||
raise ValueError("plan/output must stay in task build")
|
||||
if out.exists() or out.is_symlink():
|
||||
raise ValueError("fresh output required")
|
||||
if args.check_only and (args.plan.exists() or args.plan.is_symlink()):
|
||||
raise ValueError("fresh plan required")
|
||||
r = module("s5_standard_runner", root / "tools/run_all_tests.py")
|
||||
# Preserve the reviewed source_refs -> exactly one QA override order.
|
||||
source_before = r.source_refs(root)
|
||||
refs = dict(source_before)
|
||||
refs[TEST] = pin(QA)
|
||||
provenance = None
|
||||
if mode == "fresh-q":
|
||||
provenance = fresh_q_guard(root, args.main, args.submission, args.tested_tree)
|
||||
else:
|
||||
r17_relocation_guard(root)
|
||||
probe_guard(root)
|
||||
expected = {n: dict(bytes=p["bytes"], sha256=p["sha256"]) for n, p in refs.items()}
|
||||
uid_source = copy.deepcopy(expected)
|
||||
contracts_path = root / C_REL if mode == "fresh-q" else CONTRACTS
|
||||
fixed = [pin(p) for p in ([] if mode == "fresh-q" else MATERIALS) + [
|
||||
QA, Path(__file__), HERE / "S5_ROOT_RUN.md",
|
||||
root / "tests/transport/test_runtime_transport.gd",
|
||||
root / "tests/staff/test_rescue_elevator.gd",
|
||||
contracts_path,
|
||||
]]
|
||||
selected = dict(root_before=source_before, map={n: p["sha256"] for n, p in refs.items()}, timeout=1200)
|
||||
cache_note = None
|
||||
cache_receipts = []
|
||||
if args.cache_run is not None:
|
||||
# Reject failed caches before reading their inputs or cache contents.
|
||||
r.require(r.read(args.cache_run / "runner-ended.json")["passed"] is True,
|
||||
"failed cache is ineligible")
|
||||
for leaf in (["runner-ended.json", "runner-input.json"] if args.cache_run is not None else []):
|
||||
path = args.cache_run / leaf
|
||||
try:
|
||||
cache_receipts.append(pin(path))
|
||||
except OSError as caught:
|
||||
cache_note = dict(path=str(path), error=type(caught).__name__ + ": " + str(caught),
|
||||
required_before_native=True)
|
||||
break
|
||||
plan = dict(protocol=("p04-s5-fresh-q-v1" if mode == "fresh-q" else "p04-s5-historical-r17-readonly-v1"),
|
||||
mode=mode, provenance=provenance, historical_r17=mode != "fresh-q",
|
||||
native_admissible=False, fresh_q_evidence=False,
|
||||
historical_not_inputs=[str(p) for p in MATERIALS] if mode == "fresh-q" else [],
|
||||
project_root=str(root), output=str(out), engine_path=str(args.godot.resolve()),
|
||||
source_before=source_before, selected_source_map=selected["map"],
|
||||
QA_overrides=[TEST], fixed_input_pins=fixed,
|
||||
cache_run=str(args.cache_run) if args.cache_run is not None else None,
|
||||
import_mode="cache" if args.cache_run is not None else "fresh",
|
||||
engine_sha256=ENGINE_SHA256, engine=engine_descriptor,
|
||||
cache_receipts=cache_receipts, cache_access=cache_note,
|
||||
phases=["import", "parse", "writer", "reader"],
|
||||
native_engine_launches=0, whole_goal_accepted=False, unit_accepted=False)
|
||||
if mode == "fresh-q":
|
||||
uid_plan, uid_snapshot, binding = load_uid_helper(root, r, source_before)
|
||||
uid_plan_map = uid_plan(refs)
|
||||
r.require(len(uid_plan_map) > 0, "nonempty derived UID plan")
|
||||
r.require(all(p["owner"] in refs and p["owner_sha256"] == refs[p["owner"]]["sha256"]
|
||||
for p in uid_plan_map.values()), "UID plan owner bindings")
|
||||
plan.update(protocol="p04-s5-fresh-q-uid-diagnostic-v1", diagnostic_composite=True,
|
||||
standard_execute_phase_context=False, uid_plan=uid_plan_map,
|
||||
uid_plan_count=len(uid_plan_map), instrumentation=dict(
|
||||
wrapper_sha256=r.sha(__file__),
|
||||
execute_phase_source_sha256=hashlib.sha256(inspect.getsource(r.execute_phase).encode()).hexdigest(),
|
||||
declared_reports_source_sha256=hashlib.sha256(inspect.getsource(r.declared_reports).encode()).hexdigest(),
|
||||
callback="private-globals declared_reports + phase wrapper", uid_helper=binding))
|
||||
# Check-only only reads source/materials and writes the plan in task build.
|
||||
# Reads engine identity; never launches Godot or inspects players/processes.
|
||||
if args.check_only:
|
||||
args.plan.parent.mkdir(parents=True, exist_ok=True)
|
||||
r.write(args.plan, plan)
|
||||
print(json.dumps(dict(check_only=True, engines=0, source_files=len(source_before),
|
||||
QA_overrides=[TEST], fixed_pins=len(fixed), plan=str(args.plan),
|
||||
plan_sha256=r.sha(args.plan), cache_access=cache_note), ensure_ascii=False))
|
||||
return 0
|
||||
if mode != "fresh-q":
|
||||
raise ValueError("historical R17 is read-only")
|
||||
r.require(len(source_before) == provenance["selected_file_count"], "fresh-q selected count")
|
||||
r.require(args.plan.is_file() and r.read(args.plan) == plan, "run exact check-only plan first")
|
||||
reports, phase_call, uid_state = make_uid_policy(r, uid_snapshot, uid_source, plan["uid_plan"], out)
|
||||
engine_observations = []
|
||||
engine_identity = dict(before=engine_descriptor, during=[], after=None)
|
||||
def repin(label):
|
||||
try:
|
||||
value = engine_pin(args.godot, engine_descriptor)
|
||||
engine_observations.append(dict(stage=label, descriptor=value))
|
||||
if label == "after-cleanup":
|
||||
engine_identity["after"] = value
|
||||
else:
|
||||
engine_identity["during"].append(value)
|
||||
except BaseException as caught:
|
||||
engine_observations.append(dict(stage=label, error=str(caught)))
|
||||
raise
|
||||
def fixed_guard():
|
||||
repin("cleanup-fixed-guard")
|
||||
r.require(fresh_q_guard(root, args.main, args.submission, args.tested_tree) == provenance,
|
||||
"fresh-q provenance changed")
|
||||
probe_guard(root)
|
||||
hp = pin(root / binding["helper_path"])
|
||||
r.require(hp["bytes"] == binding["helper_bytes"] and hp["sha256"] == binding["helper_sha256"],
|
||||
"UID helper unchanged")
|
||||
for value in fixed + cache_receipts:
|
||||
r.require(pin(value["path"]) == value, "fixed input changed: " + value["path"])
|
||||
fixed_guard()
|
||||
old = args.cache_run
|
||||
if old is not None:
|
||||
# Native cache admission remains Root-only and fails closed.
|
||||
r.require(cache_note is None, "Root must read exact cache receipts: " + str(cache_note))
|
||||
r.require(r.read(old / "runner-ended.json")["passed"], "ended cache run must have passed")
|
||||
cache_input = r.read(old / "runner-input.json")
|
||||
r.require(cache_input["engine_sha256"] == ENGINE_SHA256, "cache engine identity")
|
||||
old_map = cache_input["selected_source_map"]
|
||||
current_map = {n: p["sha256"] for n, p in source_before.items()}
|
||||
excluded = {"tools/check_p04_order_evidence.py", TEST}
|
||||
r.require({k: v for k, v in old_map.items() if k not in excluded}
|
||||
== {k: v for k, v in current_map.items() if k not in excluded},
|
||||
"immutable cache selected inputs except checker and QA override")
|
||||
protected = r.default_player(root)
|
||||
outer = module("s5_owned_outer", r.SUPPORT / "owned_outer.py")
|
||||
r.require(not outer.native.helper["live_engines"](), "another Godot owns CPU")
|
||||
engine = ENGINE
|
||||
repin("native-admission")
|
||||
r.require(shutil.disk_usage(out.parent).free >= 1024**3 + engine.stat().st_size + 136*1024**2,
|
||||
"reviewed native disk admission")
|
||||
r.require(not out.exists(), "fresh output required; preserve previous attempts")
|
||||
out.mkdir()
|
||||
stage = out / "stage"
|
||||
stage.mkdir()
|
||||
(out / "starters").mkdir()
|
||||
(out / "bootstrap").mkdir()
|
||||
phases = []
|
||||
previous = {}
|
||||
error = None
|
||||
guards = []
|
||||
cleanup = []
|
||||
cleanup_errors = []
|
||||
cache_before = None
|
||||
formal_before = None
|
||||
formal_after = None
|
||||
original_phase = outer.run_phase
|
||||
def run_phase(argv, folder, timeout, env, **kwargs):
|
||||
env = dict(env)
|
||||
if folder.name in ["writer", "reader"]:
|
||||
env.update(THE_TREE_S5_EVIDENCE=str(out / "raw"), THE_TREE_S5_MODE=folder.name)
|
||||
return original_phase(argv, folder, timeout, env, **kwargs)
|
||||
outer.run_phase = run_phase
|
||||
try:
|
||||
r.write(out / "runner-started.json", dict(started=r.now(), owner_runner_pid=os.getpid(),
|
||||
plan=pin(args.plan), engines_launched=0))
|
||||
r.write(out / "source_before.json", source_before)
|
||||
r.write(out / "selected-source-map.json", refs)
|
||||
# Private source stage first, then literal reviewed bootstrap.
|
||||
for name, value in refs.items():
|
||||
target = stage / name
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
r.copy_source(Path(value["path"]), target)
|
||||
r.require(r.inventory(stage, stage=True) == expected, "whole private source")
|
||||
for name, value in r.read(r.SUPPORT / "bootstrap-scripts.json").items():
|
||||
data = value["text"].encode()
|
||||
r.require(hashlib.sha256(data).hexdigest() == value["sha256"], "bootstrap pin")
|
||||
(out / "bootstrap" / name).write_bytes(data)
|
||||
if old is not None:
|
||||
cache = old / "stage/.godot"
|
||||
cache_before = r.inventory(cache)
|
||||
r.require(bool(cache_before), "nonempty ended cache")
|
||||
for name in cache_before:
|
||||
src = cache / name
|
||||
dst = stage / ".godot" / name
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(["/bin/cp", "-c", str(src), str(dst)], check=True)
|
||||
r.require((src.stat().st_dev, src.stat().st_ino) != (dst.stat().st_dev, dst.stat().st_ino),
|
||||
"cache clone is a separate inode")
|
||||
r.require(r.inventory(stage / ".godot") == cache_before, "whole cache clone hashes")
|
||||
r.write(out / "cache-reuse.json", dict(ended_run=str(old), receipts=cache_receipts,
|
||||
whole_cache=cache_before, independent_inode=True,
|
||||
ordinary_import_required=True, ordinary_parse_required=True))
|
||||
paths = ["res://" + n for n in sorted(refs) if n.endswith(".gd")]
|
||||
r.require(len(paths) == sum(name.endswith(".gd") for name in refs), "complete derived parse roster")
|
||||
r.write(out / "parse-paths.json", paths)
|
||||
markers = module("s5_markers", root / "tools/strict_test_markers.py")
|
||||
contracts = markers.load_contracts(contracts_path)
|
||||
contracts[TEST] = dict(test=TEST, template="S5 power: <checks> checks, <failures> failures", required=True)
|
||||
policy = r.read(r.SUPPORT / "suite-policy.json")
|
||||
r.require(TEST not in policy["suite_policies"], "no normal suite watchdog/policy override")
|
||||
r.write(out / "contracts.json", contracts)
|
||||
r.write(out / "runner-input.json", dict(plan, owner_runner_pid=os.getpid(),
|
||||
wrapper_sha256=r.sha(__file__), QA=pin(QA), engine=engine_descriptor,
|
||||
engine_identity_before=engine_descriptor, engine_identity_initial=copy.deepcopy(engine_identity),
|
||||
runner_sha256=r.sha(root / "tools/run_all_tests.py"),
|
||||
default_player_before=protected, fixed_input_pins=fixed,
|
||||
uid_helper=binding, uid_plan=plan["uid_plan"], uid_source_count=len(uid_source),
|
||||
functional_watchdog_seconds=1200, source_adoption=False))
|
||||
diagnostics = module("s5_diagnostics", r.SUPPORT / "diagnostic_gate.py")
|
||||
owners = module("s5_owners", r.SUPPORT / "ordinary_proof_gate.py")
|
||||
audio = module("s5_audio", r.SUPPORT / "audio_evidence_gate.py")
|
||||
public = (stage / "project.godot").read_bytes()
|
||||
for phase, test in [("import", None), ("parse", None), ("writer", TEST), ("reader", TEST)]:
|
||||
if phase == "reader":
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_before = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(sorted(formal_before) == SLOTS, "exact three formal points for isolated power case")
|
||||
if phase == "import" and old is None:
|
||||
r.require(not (stage / ".godot").exists() and not (stage / ".godot").is_symlink(),
|
||||
"fresh private .godot absent at actual import")
|
||||
repin("before-" + phase)
|
||||
r.write(out / ("engine-identity-before-" + phase + ".json"),
|
||||
dict(engine_observations[-1], observation_index=len(engine_observations)-1))
|
||||
row = phase_call(str(engine), stage, out, phase, test, selected, expected,
|
||||
previous, policy, contracts, paths, public, protected, root,
|
||||
outer, markers, diagnostics, owners, audio)
|
||||
phases.append(row)
|
||||
# execute_phase writer/reader share out/<test>.log via write_text.
|
||||
# Keep binary per-phase copies under these existing unique names.
|
||||
# The shared runner log is not write-once; see S5_ROOT_RUN.md.
|
||||
if test and row.get("log"):
|
||||
(out / (phase + "-combined-raw.log")).write_bytes((out / row["log"]).read_bytes())
|
||||
row["strict_runner_log_path"] = row["log"]
|
||||
row["log"] = phase + "-combined-raw.log"
|
||||
r.write(out / (phase + "-actual-phase.json"), row)
|
||||
print(json.dumps(dict(phase=phase, passed=row["passed"], error=row.get("validation_error"))), flush=True)
|
||||
r.require(row["passed"], "first strict phase failure: " + phase)
|
||||
if phase in ["writer", "reader"]:
|
||||
receipt = r.read(out / ("raw/" + phase + ".json"))
|
||||
r.require(receipt["checks"] > 0 and receipt["failures"] == 0,
|
||||
"S5 actual outcome receipt")
|
||||
r.require(sorted(receipt["branches"]) == CASES,
|
||||
"exact one independent Writer/Reader case")
|
||||
r.require(sorted(receipt["slots"]) == SLOTS, "all formal point slots")
|
||||
r.require(receipt["pid"] == row["actual_pid"], "actual native PID/receipt")
|
||||
repin("after-collection")
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
reader = r.read(out / "raw/reader.json")
|
||||
r.require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "distinct formal processes")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Reader leaves Writer packages unchanged")
|
||||
except BaseException as caught:
|
||||
error = type(caught).__name__ + ": " + str(caught)
|
||||
finally:
|
||||
try:
|
||||
cleanup, cleanup_errors = outer.finish_captured(outer.OWNED)
|
||||
except BaseException as caught:
|
||||
cleanup_errors.append(str(caught))
|
||||
try:
|
||||
r.write(out / "outer-owned-finally.json", dict(cleanup=cleanup, errors=cleanup_errors))
|
||||
except BaseException as caught:
|
||||
guards.append("finally receipt: " + type(caught).__name__ + ": " + str(caught))
|
||||
# Explicit re-pin after cleanup is independent of other fixed-input guards.
|
||||
try:
|
||||
repin("after-cleanup")
|
||||
except BaseException as caught:
|
||||
guards.append(type(caught).__name__ + ": " + str(caught))
|
||||
# Fixed guard always follows ownership cleanup, even if setup or an engine failed.
|
||||
try:
|
||||
fixed_guard()
|
||||
if uid_state["accepted"] is not None:
|
||||
r.require(uid_snapshot(stage, uid_source, plan["uid_plan"]) == uid_state["accepted"],
|
||||
"UID seal after cleanup")
|
||||
after = r.source_refs(root)
|
||||
r.write(out / "source_after.json", after)
|
||||
r.require(after == source_before, "whole product source unchanged")
|
||||
r.require(r.default_player(root) == protected, "default player unchanged")
|
||||
if (stage / "project.godot").exists():
|
||||
r.require(r.inventory(stage, stage=True) == dict(expected, **previous), "whole private source unchanged")
|
||||
if cache_before is not None:
|
||||
r.require(r.inventory(old / "stage/.godot") == cache_before, "original cache unchanged")
|
||||
if formal_before is not None:
|
||||
writer = r.read(out / "raw/writer.json")
|
||||
formal_after = {n: r.inventory(Path(p)) for n, p in writer["slots"].items()}
|
||||
r.require(formal_before == formal_after, "Writer formal packages unchanged after Reader, including failure")
|
||||
except BaseException as caught:
|
||||
guards.append(type(caught).__name__ + ": " + str(caught))
|
||||
pids = [p.pid for p in outer.OWNED]
|
||||
passed = (error is None and not guards and not cleanup_errors and len(phases) == 4
|
||||
and all(p["passed"] for p in phases) and len(pids) == len(set(pids)) == 4
|
||||
and all(p["engine_ended"] for p in cleanup)
|
||||
and uid_state["accepted"] is not None
|
||||
and set(uid_state["accepted"]) == set(plan["uid_plan"])
|
||||
and all((out / (phase + "-uids.json")).is_file() for phase in plan["phases"]))
|
||||
execution = dict(passed=passed, error=error, guard_errors=guards, phases=phases,
|
||||
actual_captured_PIDs=pids, cleanup=cleanup, cleanup_errors=cleanup_errors,
|
||||
formal_before=formal_before, formal_after=formal_after,
|
||||
whole_goal_accepted=False, unit_accepted=False)
|
||||
# IO1 has already copied all four members into a separate Reader profile.
|
||||
# Mirror those exact complete directories to own B for read-only R verification.
|
||||
# Original profile paths are provenance after this step, never verifier inputs.
|
||||
try:
|
||||
if passed:
|
||||
relocated = {}
|
||||
for role in ("writer", "reader"):
|
||||
receipt = r.read(out / ("raw/" + role + ".json"))
|
||||
relocated[role] = {}
|
||||
for slot, original in receipt["slots"].items():
|
||||
target = out / "formal" / role / slot
|
||||
target.mkdir(parents=True)
|
||||
members = r.inventory(Path(original))
|
||||
r.require(set(members) == {"journal.log", "manifest.json", "thumbnail.png", "world.bin"}, "all formal members")
|
||||
for member in members:
|
||||
shutil.copyfile(Path(original) / member, target / member)
|
||||
r.require(r.inventory(target) == members == formal_before[slot], "whole formal mirror")
|
||||
relocated[role][slot] = dict(provenance=original, relative=str(target.relative_to(out)), members=members)
|
||||
r.write(out / "formal-relocation.json", relocated)
|
||||
r.write(out / "raw-inventory.json", r.inventory(out / "raw"))
|
||||
r.write(out / "PID-set.json", dict(owner_runner_pid=os.getpid(), engine_PIDs=pids))
|
||||
except BaseException as caught:
|
||||
guards.append("output collection: " + type(caught).__name__ + ": " + str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
try:
|
||||
repin("after-output-collection")
|
||||
except BaseException as caught:
|
||||
guards.append(str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
try:
|
||||
r.write(out / "engine-identity.json",
|
||||
dict(observations=engine_observations, identity=engine_identity))
|
||||
except BaseException as caught:
|
||||
guards.append("engine identity receipt: " + type(caught).__name__ + ": " + str(caught))
|
||||
passed = False
|
||||
execution["passed"] = False
|
||||
execution["engine_observations"] = engine_observations
|
||||
execution["engine_identity"] = engine_identity
|
||||
r.write(out / "execution.json", execution)
|
||||
r.write(out / "runner-ended.json", dict(execution, ended=r.now()))
|
||||
print(json.dumps(dict(passed=passed, error=error, guard_errors=guards, output=str(out))))
|
||||
return 0 if passed else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
raise SystemExit(main())
|
||||
except Exception as caught:
|
||||
print(type(caught).__name__ + ": " + str(caught), file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Derive S5 power measurements from hashed writer/reader raw, not test counts.
|
||||
|
||||
This semantic adapter does not verify engine provenance, Q or full-suite joins.
|
||||
Its output is preparation only and cannot replace the registered evidence gate.
|
||||
"""
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
CASES = ("S5A_PURE", "S5B_DEPENDENCY")
|
||||
GUEST = "p04.s5.guest.a"
|
||||
REQUEST = "p04.s5.request.a"
|
||||
|
||||
|
||||
def require(value, message):
|
||||
if not value:
|
||||
raise ValueError(message)
|
||||
|
||||
|
||||
def unique(pairs):
|
||||
result = {}
|
||||
for key, value in pairs:
|
||||
require(key not in result, "duplicate raw key")
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
class Raw:
|
||||
def __init__(self, root):
|
||||
self.root = root.resolve(strict=True)
|
||||
self.hashes = {}
|
||||
|
||||
def pin(self, path, expected):
|
||||
path = Path(path)
|
||||
require(path.is_file() and not path.is_symlink() and path.resolve().is_relative_to(self.root), "raw reference outside root/missing")
|
||||
data = path.read_bytes()
|
||||
digest = hashlib.sha256(data).hexdigest()
|
||||
require(digest == expected, "raw reference SHA mismatch")
|
||||
self.hashes[str(path)] = digest
|
||||
return data
|
||||
|
||||
def reference(self, reference):
|
||||
require(set(reference) == {"var", "json"}, "typed and JSON raw references required")
|
||||
self.pin(reference["var"]["path"], reference["var"]["sha256"])
|
||||
return json.loads(self.pin(reference["json"]["path"], reference["json"]["sha256"]), object_pairs_hook=unique)
|
||||
|
||||
|
||||
def derive(root, writer_sha, reader_sha):
|
||||
raw = Raw(root)
|
||||
writer = json.loads(raw.pin(root / "writer.json", writer_sha), object_pairs_hook=unique)
|
||||
reader = json.loads(raw.pin(root / "reader.json", reader_sha), object_pairs_hook=unique)
|
||||
require(writer["pid"] != reader["pid"] and writer["profile"] != reader["profile"], "independent reader required")
|
||||
require(set(writer["branches"]) == set(reader["branches"]) == set(CASES), "exact case pair")
|
||||
output = {}
|
||||
for case in CASES:
|
||||
branch = raw.reference(writer["branches"][case])
|
||||
replay = raw.reference(reader["branches"][case])
|
||||
raw.reference(branch["commands"])
|
||||
for role in (branch, replay):
|
||||
guard = raw.reference(role["resource_replay"])
|
||||
require(guard["result"]["is_replay"] is True and guard["result"]["code"] == "OK"
|
||||
and guard["before"] == guard["after"], "resource replay changes no Runtime field")
|
||||
points = {name: raw.reference(ref) for name, ref in branch["points"].items()}
|
||||
require(set(points) == {"before", "boundary", "held", "recovered", "duplicate_guard"}, "full point roster")
|
||||
before = points["before"]
|
||||
price, duration = before["inputs"]["price"], before["inputs"]["duration"]
|
||||
require(price > 0 and duration > 1 and before["mana"] > 0, "paid natural service")
|
||||
service_id = before["service"]["service_id"]
|
||||
require(before["service"]["state"] == "ACTIVE" and before["service"]["elapsed_minutes"] == duration - 1, "pre-boundary progress")
|
||||
reservations = before["requests"][REQUEST]["reserved_inventory_ids"]
|
||||
require(len(reservations) == (1 if case == "S5B_DEPENDENCY" else 0), "item premise")
|
||||
for label in ("boundary", "held"):
|
||||
point = points[label]
|
||||
service = point["service"]
|
||||
require(point["minute"] == before["inputs"]["boundary"] + (label == "held"), "boundary minute")
|
||||
require(point["mana"] == 0 and point["facility"]["power_available"] is False, "real depletion projection")
|
||||
reason = "RELATED_FACILITY_INACTIVE" if case == "S5B_DEPENDENCY" else "NO_POWER"
|
||||
require(service["state"] == "PAUSED" and service["interruption_reason"] == reason
|
||||
and service["elapsed_minutes"] == duration - 1 and service["service_id"] == service_id, "stopped reason/progress/service")
|
||||
require(point["receipts"] == [] and point["ledger"] == before["ledger"]
|
||||
and point["wallets"][GUEST] == price * 2 and point["player_cash"] == before["player_cash"], "zero stopped charge")
|
||||
require(point["requests"][REQUEST] == before["requests"][REQUEST]
|
||||
and point["guests"][GUEST]["state"] == "RECEIVING_SERVICE", "live request/guest ownership")
|
||||
require(point["reservations"] == before["reservations"], "entire reservation retained")
|
||||
if case == "S5B_DEPENDENCY":
|
||||
require(point["kitchen"]["state"] == "NO_POWER" and point["related_facility_operating"] is False, "RELATED-before-NO_POWER")
|
||||
require(all(r["state"] == "ACTIVE" and r["requester_id"] == REQUEST for r in point["reservations"]), "active item owner")
|
||||
else:
|
||||
require(point["related_facility_operating"] is True, "pure power premise")
|
||||
recovered = points["recovered"]
|
||||
resume = recovered["inputs"]["recovery"]["resume_minute"]
|
||||
require(recovered["minute"] == resume and resume <= before["inputs"]["boundary"] + duration, "bounded observed resume")
|
||||
for label in ("recovered", "duplicate_guard"):
|
||||
point = points[label]
|
||||
service = point["service"]
|
||||
require(service["service_id"] == service_id and service["state"] == "COMPLETED"
|
||||
and service["elapsed_minutes"] == duration and point["active_services"] == 0, "one completed service")
|
||||
require(len(point["receipts"]) == 1 and point["ledger"] == before["ledger"] + point["receipts"][0]["entries"], "whole ledger single receipt")
|
||||
require(point["wallets"][GUEST] == price and point["player_cash"] == before["player_cash"] + price, "single price")
|
||||
require(point["receipts"] == recovered["receipts"] and point["ledger"] == recovered["ledger"], "no duplicate sale")
|
||||
if reservations:
|
||||
require(len(point["reservations"]) == 1, "same item count")
|
||||
r = point["reservations"][0]
|
||||
require(r["reservation_id"] == reservations[0] and r["state"] == "CONSUMED"
|
||||
and r["terminal_at"] == service["completed_at"] and point["requests"][REQUEST] == {}, "consumed without orphan")
|
||||
for label in ("held", "recovered", "duplicate_guard"):
|
||||
require(raw.reference(replay["replay"][label]) == points[label], "reader full raw replay mismatch")
|
||||
for label in ("boundary", "held", "recovered"):
|
||||
require(raw.reference(replay["restored"][label])["snapshot"] == points[label]["snapshot"], "formal restore snapshot mismatch")
|
||||
phases = [raw.reference(ref) for ref in branch["phases"]]
|
||||
expected_replay = [p for p in phases if p["minute"] > before["inputs"]["boundary"] or
|
||||
(p["minute"] == before["inputs"]["boundary"] and p["phase"] == "APPLY_COMMANDS")]
|
||||
require([raw.reference(ref) for ref in replay["phases"]] == expected_replay, "all reader phase raw mismatch")
|
||||
reasons = []
|
||||
for p in phases:
|
||||
reason = p["service"].get("interruption_reason", "")
|
||||
if not reasons or reasons[-1] != reason:
|
||||
reasons.append(reason)
|
||||
output[case] = dict(measurements={
|
||||
**({"power_stop": {"charges": 0}} if case == "S5A_PURE" else
|
||||
{"dependency_stop": {"charges": 0, "progress_delta": 0, "reservation_ids_preserved": True, "service_id_preserved": True}}),
|
||||
"recovery_completion": {"completions": 1, "charges": 1}},
|
||||
reason_sequence=reasons, resume_minute=resume)
|
||||
return dict(cases=output, raw_sha256=raw.hashes, native_provenance_verified=False,
|
||||
whole_goal_accepted=False, unit_accepted=False)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--raw-root", type=Path, required=True)
|
||||
parser.add_argument("--writer-sha256", required=True)
|
||||
parser.add_argument("--reader-sha256", required=True)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
print(json.dumps(derive(args.raw_root, args.writer_sha256, args.reader_sha256), sort_keys=True))
|
||||
except (OSError, ValueError, KeyError, TypeError) as error:
|
||||
print(json.dumps({"error": str(error), "whole_goal_accepted": False}))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pure Root-only UID controls; no engine or environment access."""
|
||||
import argparse
|
||||
import copy
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
|
||||
def module(name, path):
|
||||
spec = importlib.util.spec_from_file_location(name, path)
|
||||
value = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(value)
|
||||
return value
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--project-root', required=True, type=Path)
|
||||
parser.add_argument('--scratch', required=True, type=Path)
|
||||
args = parser.parse_args()
|
||||
root = args.project_root.resolve(strict=True)
|
||||
r = module('s5_uid_q_runner', root / 'tools/run_all_tests.py')
|
||||
w = module('s5_uid_wrapper', Path(__file__).with_name('run_s5_native.py'))
|
||||
# Actual Q pins precede all synthetic data. No synthetic refs enter the loader.
|
||||
uid_plan, uid_snapshot, binding = w.load_uid_helper(root, r, r.source_refs(root))
|
||||
scratch = args.scratch
|
||||
r.require(scratch.is_absolute() and not scratch.exists() and not scratch.is_symlink(),
|
||||
'absolute fresh scratch')
|
||||
parent = scratch.parent.resolve(strict=True)
|
||||
r.require(parent.is_dir() and parent.is_relative_to(w.BUILD.resolve())
|
||||
and not (parent / scratch.name).is_relative_to(root), 'scratch parent inside BUILD outside Q')
|
||||
scratch = parent / scratch.name
|
||||
scratch.mkdir()
|
||||
results = []
|
||||
|
||||
def fixture(label):
|
||||
stage = scratch / label
|
||||
stage.mkdir()
|
||||
files = {'project.godot': b'config_version=5\n',
|
||||
'scripts/special/peeping_room_service.gd': b'extends Node\n',
|
||||
'tests/special/test_a.gd': b'extends SceneTree\n',
|
||||
'scripts/other.gd': b'extends RefCounted\n',
|
||||
'scripts/other.gd.uid': b'uid://existing\n'}
|
||||
for name, data in files.items():
|
||||
path = stage / name
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(data)
|
||||
source = r.inventory(stage, stage=True)
|
||||
refs = {n: dict(p, path=str(stage / n)) for n, p in source.items()}
|
||||
plan = uid_plan(refs)
|
||||
r.require(bool(plan) and all(p['owner_sha256'] == refs[p['owner']]['sha256']
|
||||
for p in plan.values()), 'derived synthetic plan')
|
||||
for i, name in enumerate(plan):
|
||||
(stage / name).write_bytes(('uid://generated' + str(i) + '\n').encode())
|
||||
return stage, source, plan
|
||||
|
||||
stage, source, plan = fixture('positive')
|
||||
snap = uid_snapshot(stage, source, plan)
|
||||
r.require(set(snap) == set(plan), 'exact positive planned UID set')
|
||||
results.append('positive')
|
||||
|
||||
def negative(label, mutate):
|
||||
stage, source, plan = fixture(label)
|
||||
names = list(plan)
|
||||
mutate(stage, source, plan, names)
|
||||
try:
|
||||
uid_snapshot(stage, source, plan)
|
||||
except AssertionError:
|
||||
results.append(label)
|
||||
else:
|
||||
raise AssertionError('control did not reject: ' + label)
|
||||
|
||||
negative('extra', lambda s, a, p, n: (s / 'extra.uid').write_bytes(b'uid://extra\n'))
|
||||
negative('original', lambda s, a, p, n: (s / 'project.godot').write_bytes(b'changed\n'))
|
||||
negative('owner', lambda s, a, p, n: (s / p[n[0]]['owner']).write_bytes(b'changed\n'))
|
||||
negative('existing-collision', lambda s, a, p, n: (s / n[0]).write_bytes(b'uid://existing\n'))
|
||||
negative('generated-collision', lambda s, a, p, n: (s / n[0]).write_bytes((s / n[1]).read_bytes()))
|
||||
for label, raw in [('no-newline', b'uid://abc'), ('crlf', b'uid://abc\r\n'),
|
||||
('uppercase', b'uid://ABC\n'), ('too-long', b'uid://abcdefghijklmn\n')]:
|
||||
negative(label, lambda s, a, p, n, raw=raw: (s / n[0]).write_bytes(raw))
|
||||
|
||||
def hardlink(stage, source, plan, names):
|
||||
os.link(stage / names[0], scratch / 'hardlink-peer')
|
||||
|
||||
def symlink(stage, source, plan, names):
|
||||
path = stage / names[0]
|
||||
path.unlink()
|
||||
path.symlink_to(stage / names[1])
|
||||
|
||||
negative('hardlink', hardlink)
|
||||
negative('symlink', symlink)
|
||||
negative('missing', lambda s, a, p, n: (s / n[0]).unlink())
|
||||
stage, source, plan = fixture('post-seal')
|
||||
out = scratch / 'policy-receipts'
|
||||
out.mkdir()
|
||||
reports, phase_call, state = w.make_uid_policy(r, uid_snapshot, source, plan, out)
|
||||
expected = copy.deepcopy(source)
|
||||
state['phase'] = 'import'
|
||||
reports(stage, expected, {}, [])
|
||||
r.require(state['accepted'] == uid_snapshot(stage, source, plan), 'import sealed')
|
||||
state['phase'] = 'parse'
|
||||
(stage / next(iter(plan))).write_bytes(b'uid://mutated\n')
|
||||
try:
|
||||
reports(stage, expected, {}, [])
|
||||
except AssertionError:
|
||||
results.append('post-seal-reports')
|
||||
else:
|
||||
raise AssertionError('post-seal mutation accepted')
|
||||
print(json.dumps({'accepted': False, 'Root-only': True, 'controls': results,
|
||||
'uid_helper': binding, 'scratch': str(scratch)}))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Pure select_mode fixtures; no Git, engine, temporary files or environment access."""
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
|
||||
def main():
|
||||
path = Path(__file__).parent / "run_s5_native.py"
|
||||
spec = importlib.util.spec_from_file_location("s5_wrapper_fixture_target", path)
|
||||
wrapper = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(wrapper)
|
||||
cases = [
|
||||
("partial-triple", argparse.Namespace(main="a" * 40, submission=None, tested_tree=None,
|
||||
historical_r17=False, check_only=True), None),
|
||||
("non-hex", argparse.Namespace(main="z" * 40, submission="b" * 40, tested_tree="c" * 40,
|
||||
historical_r17=False, check_only=True), None),
|
||||
("short", argparse.Namespace(main="a" * 39, submission="b" * 40, tested_tree="c" * 40,
|
||||
historical_r17=False, check_only=True), None),
|
||||
("mixed-modes", argparse.Namespace(main="a" * 40, submission="b" * 40, tested_tree="c" * 40,
|
||||
historical_r17=True, check_only=True), None),
|
||||
("historical-native", argparse.Namespace(main=None, submission=None, tested_tree=None,
|
||||
historical_r17=True, check_only=False), None),
|
||||
("no-flags", argparse.Namespace(main=None, submission=None, tested_tree=None,
|
||||
historical_r17=False, check_only=False), None),
|
||||
("fresh-q", argparse.Namespace(main="a" * 40, submission="b" * 40, tested_tree="c" * 40,
|
||||
historical_r17=False, check_only=True), "fresh-q"),
|
||||
("historical-check-only", argparse.Namespace(main=None, submission=None, tested_tree=None,
|
||||
historical_r17=True, check_only=True),
|
||||
"historical-r17-readonly"),
|
||||
]
|
||||
results = []
|
||||
for name, args, expected in cases:
|
||||
try:
|
||||
actual = wrapper.select_mode(args)
|
||||
passed = expected is not None and actual == expected
|
||||
except ValueError:
|
||||
actual = "ValueError"
|
||||
passed = expected is None
|
||||
results.append(dict(name=name, result=actual, passed=passed))
|
||||
print(json.dumps(dict(accepted=False, fixtures=results, passed=all(r["passed"] for r in results))))
|
||||
return 0 if all(r["passed"] for r in results) else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only scratch routing for preserved partial31 fixture sources.
|
||||
|
||||
Only TemporaryDirectory allocation is redirected. Fixture assertions and checker
|
||||
semantics remain unchanged. No fixture is run by importing this adapter.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import runpy
|
||||
import sys
|
||||
import tempfile
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
FIXTURES = {"bulk_reader_equivalence", "checker_role_fixtures", "checker_candidate_fixtures",
|
||||
"checker_approval_fixtures", "checker_negative_fixtures", "checker_applicability_fixtures"}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--scratch", type=Path, required=True)
|
||||
parser.add_argument("--fixture", choices=sorted(FIXTURES), required=True)
|
||||
parser.add_argument("arguments", nargs=argparse.REMAINDER)
|
||||
args = parser.parse_args()
|
||||
scratch = args.scratch.resolve()
|
||||
repository = HERE.parents[4]
|
||||
if scratch.exists() or scratch.is_relative_to(repository) or scratch == Path("/tmp").resolve() or Path("/tmp").resolve() in scratch.parents:
|
||||
raise ValueError("fresh external scratch required; no repository or /tmp writes")
|
||||
scratch.mkdir(parents=True)
|
||||
old_env, old_argv = dict(os.environ), sys.argv[:]
|
||||
original = tempfile.TemporaryDirectory
|
||||
def directory(suffix=None, prefix=None, dir=None, **kwargs):
|
||||
# Explicit dir in old source is intentionally superseded only here.
|
||||
return original(suffix=suffix, prefix=prefix, dir=scratch, **kwargs)
|
||||
try:
|
||||
for name in list(os.environ):
|
||||
if name.startswith("GIT_"):
|
||||
del os.environ[name]
|
||||
os.environ["TMPDIR"] = str(scratch)
|
||||
tempfile.TemporaryDirectory = directory
|
||||
forwarded = args.arguments[1:] if args.arguments[:1] == ["--"] else args.arguments
|
||||
source = HERE / (args.fixture + ".py")
|
||||
sys.argv = [str(source), *forwarded]
|
||||
runpy.run_path(str(source), run_name="__main__")
|
||||
finally:
|
||||
tempfile.TemporaryDirectory = original
|
||||
sys.argv = old_argv
|
||||
os.environ.clear()
|
||||
os.environ.update(old_env)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
+418
-105
@@ -15,16 +15,34 @@ import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SPEC = ROOT / 'docs/spec/acceptance/P04-01.json'
|
||||
SPEC_HASH = '9ec722e7dfb0bee178d291624a026420c08412574e238bba1fde012c7a6f7881'
|
||||
DOMAINS = {'閉店', '停電', '破壊', '退出', '課金', 'シフト交代', '00:00決算'}
|
||||
OWNER_HASH = '4583ee9aad45c4db183d72df4420df48aa8e0897e3b5efda13016f0b5cdbd68e'
|
||||
REGISTERED_COMMIT = 'deb1bcb2374eb378b840ca345db952bd8572cb6e'
|
||||
EXECUTION_ORIGIN = '9767b0a28ca8d252520307dbbac60a1d8cfb87ab'
|
||||
CHECKER_PATH = 'tools/check_p04_order_evidence.py'
|
||||
DOC_SCOPE = 'docs/plans/original-completion-2026-10-02/execution/p04-01/'
|
||||
MAIN_ANCHOR = '9694f50ee63750192376ca69bc7dda3f884784c5'
|
||||
OWNER_BINDINGS = {
|
||||
ref: (DOC_SCOPE + leaf, digest)
|
||||
for refs, leaf, digest in (
|
||||
(('D05-P04-DEPENDENCY', 'D05-P04-SHIFT-1800'), 'D05_OWNER_APPROVAL.json',
|
||||
'4583ee9aad45c4db183d72df4420df48aa8e0897e3b5efda13016f0b5cdbd68e'),
|
||||
(('D05-P04-DESTRUCTION', 'D05-P04-MIDNIGHT-DAY', 'D05-P04-PAUSE-REASON',
|
||||
'D05-P04-EXIT'), 'D05_REMAINING_APPROVAL.json',
|
||||
'09b5092124574c3ccbf3b55169729a7529d9dde9b9a115d5e944fce4b960f5e4'),
|
||||
) for ref in refs
|
||||
}
|
||||
MAIN_ANCHOR = 'c0ea009b28a7649aca6a433a1a24dc0be5e7ab81'
|
||||
# Documentation only: external Runner prepare_integration/integrate and Root
|
||||
# normal_c_join joins after final. The checker never consumes this roster.
|
||||
NORMAL_C_PROVENANCE_JOIN = frozenset({
|
||||
'candidate_commit', 'candidate_tree', 'first_parent', 'second_parent',
|
||||
'tested_tree', 'prepare_integration_receipt', 'independent_review_receipt',
|
||||
'validation_receipt', 'source_manifest_sha256', 'settings_sha256',
|
||||
'markers_sha256', 'policy_sha256', 'engine_sha256', 'inputs_sha256',
|
||||
})
|
||||
SELECTION_SHA = '320ca668732b4ff72fc4449998af2327d4e5c527b06896740806aaf0487282b4'
|
||||
_GIT_ENV = None
|
||||
# Fixed membership, never supplied by the evidence index.
|
||||
@@ -41,19 +59,20 @@ ORDER_FUNCTIONS = {
|
||||
'scripts/operations/operations_coordinator.gd': ('_process_minute_boundaries',),
|
||||
}
|
||||
# Condition -> (domain, authority refs, required engine measurements).
|
||||
# Measurements describe safety/counts, not the still-pending attribution policy.
|
||||
# Existing safety/count thresholds are retained. Repair30's additional approved
|
||||
# order measurements still need the S5 raw adapter (see REPAIR30_STATUS.md).
|
||||
REQUIRED = {
|
||||
'power_stop': ('停電', ('BASELINE:183',), {'charges': 0}),
|
||||
'destruction_stop': ('破壊', ('BASELINE:183',), {'charges': 0}),
|
||||
'power_stop': ('停電', ('BASELINE:183', 'D05-P04-PAUSE-REASON'), {'charges': 0}),
|
||||
'destruction_stop': ('破壊', ('D05-P04-DESTRUCTION',), {'charges': 0}),
|
||||
'dependency_stop': ('閉店', ('D05-P04-DEPENDENCY',), {'charges': 0, 'progress_delta': 0, 'reservation_ids_preserved': True, 'service_id_preserved': True}),
|
||||
'shift_completion': ('シフト交代', ('BASELINE:64', 'BASELINE:67', 'D05-P04-SHIFT-1800'), {'minute': 1080, 'completions': 1, 'charges': 1, 'completion_before_shift': True}),
|
||||
'recovery_completion': ('課金', ('D05-P04-DEPENDENCY',), {'completions': 1, 'charges': 1}),
|
||||
'same_minute_replay': ('課金', ('D05-P04-DEPENDENCY', 'D05-P04-SHIFT-1800'), {'completions': 1, 'charges': 1, 'duplicate_generations': 0, 'same_minute_replayed': True}),
|
||||
'closure_release': ('閉店', ('D-034',), {'orphan_reservations': 0, 'released_once': True, 'started_service_continues': True}),
|
||||
'exit_release': ('退出', ('D-034',), {'orphan_reservations': 0, 'released_once': True}),
|
||||
'patience_release': ('退出', ('D-034',), {'orphan_reservations': 0, 'released_once': True}),
|
||||
'paused_midnight': ('00:00決算', ('D05-P04-DEPENDENCY', 'BASELINE:71', 'BASELINE:72'), {'orphan_reservations': 0, 'crossed_midnight_while_paused': True, 'stopped_charges': 0, 'completions': 1, 'charges': 1}),
|
||||
'midnight_settlement': ('00:00決算', ('BASELINE:71', 'BASELINE:72', 'A01'), {'settlements': 1, 'sales_payments': 1, 'payroll_payments': 1, 'upkeep_payments': 1, 'duty_payments': 1, 'nonzero_payroll': True}),
|
||||
'exit_release': ('退出', ('D-034', 'D05-P04-EXIT'), {'orphan_reservations': 0, 'released_once': True}),
|
||||
'patience_release': ('退出', ('D-034', 'D05-P04-EXIT'), {'orphan_reservations': 0, 'released_once': True}),
|
||||
'paused_midnight': ('00:00決算', ('D05-P04-DEPENDENCY', 'BASELINE:71', 'BASELINE:72', 'D05-P04-MIDNIGHT-DAY'), {'orphan_reservations': 0, 'crossed_midnight_while_paused': True, 'stopped_charges': 0, 'completions': 1, 'charges': 1}),
|
||||
'midnight_settlement': ('00:00決算', ('BASELINE:71', 'BASELINE:72', 'A01', 'D05-P04-MIDNIGHT-DAY'), {'settlements': 1, 'sales_payments': 1, 'payroll_payments': 1, 'upkeep_payments': 1, 'duty_payments': 1, 'nonzero_payroll': True}),
|
||||
'midnight_replay': ('00:00決算', ('BASELINE:71', 'BASELINE:72', 'A01'), {'settlements': 1, 'same_minute_replayed': True, 'duplicate_payments': 0}),
|
||||
'save_restore': ('00:00決算', ('BASELINE:71', 'BASELINE:72', 'A01'), {'settlements': 1, 'duplicate_payments': 0, 'formal_save_restored': True}),
|
||||
'night_staff_absent': ('シフト交代', ('BASELINE:67', 'D05-P04-SHIFT-1800'), {'minute': 1081, 'state': 'WAITING_FOR_STAFF', 'night_staff_count': 0}),
|
||||
@@ -135,10 +154,55 @@ def literal(a, b):
|
||||
|
||||
|
||||
def git_bytes(*args):
|
||||
env = _GIT_ENV if _GIT_ENV is not None else dict(os.environ, GIT_OPTIONAL_LOCKS='0')
|
||||
env = _GIT_ENV if _GIT_ENV is not None else git_environment()
|
||||
return subprocess.check_output(['git', '--no-replace-objects', *args], cwd=ROOT, env=env)
|
||||
|
||||
|
||||
def parse_blob_batch(stdout, oids):
|
||||
"""Decode length-framed output without splitting binary payloads into lines."""
|
||||
result = {}
|
||||
offset = 0
|
||||
for oid in oids:
|
||||
end = stdout.find(b'\n', offset)
|
||||
require(end >= 0, 'truncated blob batch header')
|
||||
header = stdout[offset:end].split(b' ')
|
||||
require(len(header) == 3 and header[0] == oid.encode('ascii')
|
||||
and header[1] == b'blob' and re.fullmatch(rb'[0-9]+', header[2]) is not None,
|
||||
'invalid blob batch header: ' + oid)
|
||||
size = int(header[2])
|
||||
start = end + 1
|
||||
end = start + size
|
||||
require(end < len(stdout) and stdout[end:end + 1] == b'\n',
|
||||
'truncated blob batch payload or missing LF: ' + oid)
|
||||
result[oid] = stdout[start:end]
|
||||
offset = end + 1
|
||||
require(offset == len(stdout), 'extra blob batch output')
|
||||
return result
|
||||
|
||||
|
||||
def read_blobs(oids):
|
||||
"""One process per request; no cache can cross object namespaces."""
|
||||
requested = list(oids)
|
||||
require(all(isinstance(oid, str) and re.fullmatch('[0-9a-f]{40}', oid) is not None
|
||||
for oid in requested), 'invalid full blob OID')
|
||||
requested = sorted(set(requested))
|
||||
if not requested:
|
||||
return {}
|
||||
env = _GIT_ENV if _GIT_ENV is not None else git_environment()
|
||||
# Inherit stderr and raise on nonzero status, exactly as git_bytes does.
|
||||
stdout = subprocess.check_output(['git', '--no-replace-objects', 'cat-file', '--batch'],
|
||||
input=('\n'.join(requested) + '\n').encode('ascii'),
|
||||
cwd=ROOT, env=env)
|
||||
return parse_blob_batch(stdout, requested)
|
||||
|
||||
|
||||
def git_environment():
|
||||
env = {k: v for k, v in os.environ.items() if not k.startswith('GIT_')}
|
||||
env.update(GIT_OPTIONAL_LOCKS='0', GIT_NO_REPLACE_OBJECTS='1', GIT_CONFIG_NOSYSTEM='1',
|
||||
GIT_CONFIG_GLOBAL=os.devnull)
|
||||
return env
|
||||
|
||||
|
||||
@contextmanager
|
||||
def isolated_objects():
|
||||
"""merge-tree writes only disposable objects; canonical refs/index stay untouched.
|
||||
@@ -149,11 +213,9 @@ def isolated_objects():
|
||||
global _GIT_ENV
|
||||
require(_GIT_ENV is None, 'nested object store')
|
||||
objects = git_bytes('rev-parse', '--path-format=absolute', '--git-path', 'objects').decode().strip()
|
||||
with tempfile.TemporaryDirectory(prefix='p04-object-audit-') as temporary:
|
||||
env = dict(os.environ)
|
||||
for key in ('GIT_OBJECT_DIRECTORY', 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
|
||||
'GIT_INDEX_FILE', 'GIT_QUARANTINE_PATH'):
|
||||
env.pop(key, None)
|
||||
build = Path(os.environ['BUILD_OUTPUT_DIR']).resolve(strict=True)
|
||||
with tempfile.TemporaryDirectory(prefix='p04-object-audit-', dir=build) as temporary:
|
||||
env = git_environment()
|
||||
env.update(GIT_OBJECT_DIRECTORY=temporary, GIT_ALTERNATE_OBJECT_DIRECTORIES=objects,
|
||||
GIT_OPTIONAL_LOCKS='0', GIT_NO_REPLACE_OBJECTS='1')
|
||||
_GIT_ENV = env
|
||||
@@ -190,13 +252,25 @@ def allowed_path(path):
|
||||
|
||||
def product_manifest(entries):
|
||||
result = {}
|
||||
blobs = read_blobs(oid for path, (_, oid) in entries.items()
|
||||
if selected_path(path) and path != CHECKER_PATH)
|
||||
for path, (_, oid) in sorted(entries.items()):
|
||||
if selected_path(path) and path != CHECKER_PATH:
|
||||
data = git_bytes('cat-file', 'blob', oid)
|
||||
data = blobs[oid]
|
||||
result[path] = {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
return result
|
||||
|
||||
|
||||
def selected_manifest(entries, require_checker=True):
|
||||
result = product_manifest(entries)
|
||||
if require_checker:
|
||||
require(CHECKER_PATH in entries, 'selected source missing checker')
|
||||
if CHECKER_PATH in entries:
|
||||
data = git_bytes('cat-file', 'blob', entries[CHECKER_PATH][1])
|
||||
result[CHECKER_PATH] = {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
return result
|
||||
|
||||
|
||||
def manifest_digest(manifest):
|
||||
return hashlib.sha256(json.dumps(manifest, sort_keys=True, separators=(',', ':')).encode()).hexdigest()
|
||||
|
||||
@@ -208,7 +282,7 @@ def supplied_file(directory, descriptor):
|
||||
if path.is_absolute():
|
||||
support = directory / 'audit-support'
|
||||
roots = [support / n for n in ('p04-whole-closure-r01', 'native-first-current',
|
||||
'p04-current-native-series-r01')]
|
||||
'p04-current-native-series-r01', 'p04-r17-full-inputs-r01')]
|
||||
matches = [root for root in roots if path.is_relative_to(root)]
|
||||
require(len(matches) == 1, 'unsupplied external material: ' + name)
|
||||
path = safe(matches[0], path.relative_to(matches[0]).as_posix())
|
||||
@@ -219,89 +293,148 @@ def supplied_file(directory, descriptor):
|
||||
return path
|
||||
|
||||
|
||||
def verify_product_target(index, directory, checker_commit):
|
||||
"""Four actual roles, complete source selection, and candidate equality.
|
||||
def ancestor(older, newer):
|
||||
# merge-base's nonzero status is data only for the ordinary non-ancestor case.
|
||||
try:
|
||||
git_bytes('merge-base', '--is-ancestor', older, newer)
|
||||
return True
|
||||
except subprocess.CalledProcessError as exc:
|
||||
require(exc.returncode == 1, 'cannot determine ancestry')
|
||||
return False
|
||||
|
||||
This does not establish any native boundary condition. Runner input files
|
||||
are checked against Git blobs, never against a Root-reported digest alone.
|
||||
"""
|
||||
binding = index['product_binding']
|
||||
target = binding['product_target']
|
||||
require(set(target) == {'main_anchor', 'submitted', 'composed_tree'}, 'product target shape')
|
||||
require(target['main_anchor'] == MAIN_ANCHOR, 'main anchor changed')
|
||||
for key in target:
|
||||
require(re.fullmatch('[0-9a-f]{40}', target[key]) is not None, 'full product target OID')
|
||||
for commit in (REGISTERED_COMMIT, EXECUTION_ORIGIN, target['main_anchor'], target['submitted'], checker_commit):
|
||||
require(git_bytes('cat-file', '-t', commit).strip() == b'commit', 'role is not a commit')
|
||||
require(_GIT_ENV is not None, 'merge-tree needs isolated object store')
|
||||
|
||||
def submission_role(commit):
|
||||
require(ancestor(REGISTERED_COMMIT, commit) and not ancestor(MAIN_ANCHOR, commit),
|
||||
'invalid submission ancestry')
|
||||
changed = git_bytes('diff', '--no-ext-diff', '--no-renames', '--name-only', '-z',
|
||||
REGISTERED_COMMIT, commit).split(b'\0')
|
||||
require(all(allowed_path(p.decode()) for p in changed if p), 'submission outside allowed_paths')
|
||||
|
||||
|
||||
def actual_checkout():
|
||||
"""Bind the executing modules and every worktree file to the actual HEAD."""
|
||||
commit = git_bytes('rev-parse', '--verify', 'HEAD^{commit}').decode().strip()
|
||||
tree = git_bytes('rev-parse', 'HEAD^{tree}').decode().strip()
|
||||
entries = tree_entries(tree)
|
||||
require(not git_bytes('status', '--porcelain=v1', '--untracked-files=all',
|
||||
'--ignore-submodules=none'), 'checkout is not clean')
|
||||
require(not git_bytes('ls-files', '--others', '-z'), 'untracked/ignored substitution')
|
||||
blobs = read_blobs(oid for _, oid in entries.values())
|
||||
for path, (mode, oid) in entries.items():
|
||||
actual = safe(ROOT, path)
|
||||
require(actual.read_bytes() == blobs[oid], 'HEAD/tree/FS bytes: ' + path)
|
||||
require(('100755' if actual.stat().st_mode & 0o111 else '100644') == mode,
|
||||
'HEAD/tree/FS mode: ' + path)
|
||||
# Also reject untracked directory symlinks, including ones ignored by Git.
|
||||
for parent, directories, files in os.walk(ROOT, followlinks=False):
|
||||
if Path(parent) == ROOT:
|
||||
directories[:] = [p for p in directories if p != '.git']
|
||||
files = [p for p in files if p != '.git']
|
||||
for name in directories + files:
|
||||
require(not (Path(parent) / name).is_symlink(), 'worktree symlink')
|
||||
require(Path(__file__).resolve() == safe(ROOT, CHECKER_PATH), 'checker module location')
|
||||
return commit, tree, entries
|
||||
|
||||
|
||||
def verify_roles(index, directory):
|
||||
"""Infer roles from Git, never from an index or a caller's stage label."""
|
||||
require(not ({'candidate_commit', 'candidate_tree', 'code_only', 'candidate', 'phase'} & index.keys()),
|
||||
'candidate/code-only/phase declarations are not evidence')
|
||||
candidate = None
|
||||
commit, actual_tree, actual = actual_checkout()
|
||||
require(_GIT_ENV is not None, 'isolated object store required')
|
||||
if not ancestor(MAIN_ANCHOR, commit):
|
||||
submission_role(commit)
|
||||
submitted, main, role = commit, MAIN_ANCHOR, 'prospective composition'
|
||||
else:
|
||||
parents = git_bytes('show', '-s', '--format=%P', commit).decode().split()
|
||||
require(len(parents) == 2, 'integration/final requires a two-parent no-ff merge')
|
||||
main, submitted = parents
|
||||
require(main == MAIN_ANCHOR, 'integration main is not frozen M')
|
||||
submission_role(submitted)
|
||||
role = 'integration/final'
|
||||
composed = git_bytes('-c', 'rerere.enabled=false', 'merge-tree', '--write-tree',
|
||||
target['main_anchor'], target['submitted']).decode().strip()
|
||||
require(composed == target['composed_tree'], 'recomputed composed tree mismatch')
|
||||
anchor, submission, tree = [tree_entries(t) for t in (MAIN_ANCHOR, target['submitted'], composed)]
|
||||
main, submitted).decode().strip()
|
||||
require(re.fullmatch('[0-9a-f]{40}', composed) is not None, 'conflicted composition')
|
||||
main_entries, patch, target = [tree_entries(t) for t in (main, submitted, composed)]
|
||||
anchor_entries = tree_entries(MAIN_ANCHOR)
|
||||
for path in main_entries.keys() | target.keys():
|
||||
require(main_entries.get(path) == target.get(path) or allowed_path(path),
|
||||
'composition changed game/unknown source: ' + path)
|
||||
# Metadata membership comes from the real main anchor, never a declared exclusion.
|
||||
metadata = sorted(p for p in anchor_entries if not selected_path(p))
|
||||
for path in target:
|
||||
require(selected_path(path) or allowed_path(path) or path in metadata,
|
||||
'unknown source hidden as metadata: ' + path)
|
||||
if role == 'integration/final':
|
||||
require(actual_tree == composed and actual == target, 'actual merge differs from composition')
|
||||
candidate = qualify_normal_candidate(commit, actual_tree, main, submitted, composed)
|
||||
require(target[CHECKER_PATH] == patch[CHECKER_PATH] == actual[CHECKER_PATH],
|
||||
'checker blob differs from submission')
|
||||
for path in patch:
|
||||
if allowed_path(path):
|
||||
require(target.get(path) == patch[path], 'reviewed patch differs in merge: ' + path)
|
||||
binding = index['product_binding']
|
||||
require(set(binding) == {'tested_product', 'runner_inputs'}, 'unknown product role/metadata declaration')
|
||||
tested = binding['tested_product']
|
||||
require(tested == composed and git_bytes('cat-file', '-t', tested).strip() == b'tree',
|
||||
'tested Q is not the genuine recomputed merge-tree')
|
||||
selection = git_bytes('show', MAIN_ANCHOR + ':tools/run_all_tests.py')
|
||||
require(hashlib.sha256(selection).hexdigest() == SELECTION_SHA, 'source selection authority')
|
||||
changed = git_bytes('diff', '--no-ext-diff', '--no-renames', '--name-only', '-z',
|
||||
REGISTERED_COMMIT, target['submitted']).split(b'\0')
|
||||
require(all(allowed_path(p.decode()) for p in changed if p), 'submission outside registered scope')
|
||||
for path in anchor.keys() | tree.keys():
|
||||
if anchor.get(path) != tree.get(path):
|
||||
require(allowed_path(path), 'new/changed path outside scope: ' + path)
|
||||
baseline = product_manifest(anchor)
|
||||
product = product_manifest(tree)
|
||||
require(len(baseline) == 1338 and product == baseline, 'complete selected product mismatch')
|
||||
metadata = sorted(p for p in anchor if not selected_path(p) and anchor[p] == tree.get(p))
|
||||
require(len(metadata) == 3139, 'unchanged existing metadata membership changed')
|
||||
require(set(p for p in tree if selected_path(p)) == set(product) | {CHECKER_PATH},
|
||||
'unknown selected source cannot be metadata')
|
||||
own = Path(__file__).read_bytes()
|
||||
for role, entries in (('submitted', submission), ('composed', tree),
|
||||
('checker', tree_entries(checker_commit))):
|
||||
require(CHECKER_PATH in entries and git_bytes('cat-file', 'blob', entries[CHECKER_PATH][1]) == own,
|
||||
'checker self blob not verified: ' + role)
|
||||
tested = binding['tested_product']
|
||||
require(git_bytes('cat-file', '-t', tested).strip() == b'commit'
|
||||
and product_manifest(tree_entries(tested)) == product, 'tested product manifest mismatch')
|
||||
candidate = tree_entries(index['candidate_tree'])
|
||||
require(candidate == tree, 'candidate product/QA/collector/tree bytes differ from target')
|
||||
runners = binding['runner_inputs']
|
||||
require(isinstance(runners, list) and runners, 'actual runner inputs missing')
|
||||
product = product_manifest(target)
|
||||
tested_entries = tree_entries(tested)
|
||||
expected = product_manifest(tested_entries)
|
||||
require(product == expected and
|
||||
product_manifest(main_entries) == expected and product_manifest(anchor_entries) == expected,
|
||||
'complete selected native product mismatch')
|
||||
require(all(target[p][0] == main_entries[p][0] == tested_entries[p][0] for p in product),
|
||||
'native product mode mismatch')
|
||||
selected_product = selected_manifest(target)
|
||||
require(set(selected_product) == set(product) | {CHECKER_PATH}, 'checker must be explicitly selected')
|
||||
require(index['source_map'] == {p: v['sha256'] for p, v in selected_product.items()}, 'stale pin: source map')
|
||||
runner_paths = set()
|
||||
for descriptor in runners:
|
||||
runner_path = supplied_file(directory, descriptor)
|
||||
require(runner_path not in runner_paths, 'duplicate runner input')
|
||||
runner_paths.add(runner_path)
|
||||
native = load(runner_path)
|
||||
for descriptor in binding['runner_inputs']:
|
||||
path = supplied_file(directory, descriptor)
|
||||
require(path not in runner_paths, 'duplicate runner input')
|
||||
runner_paths.add(path)
|
||||
native = load(path)
|
||||
before = native['source_before']
|
||||
require(set(before) == set(product) and all(
|
||||
{'bytes': v['bytes'], 'sha256': v['sha256']} == product[p] for p, v in before.items()),
|
||||
'runner source_before differs from complete product')
|
||||
require(set(before) == set(selected_product) and all(
|
||||
{'bytes': v['bytes'], 'sha256': v['sha256']} == selected_product[p] for p, v in before.items()),
|
||||
'runner complete source map mismatch')
|
||||
require(native['runner_sha256'] == product['tools/run_all_tests.py']['sha256'], 'runner source pin')
|
||||
overrides = native['QA_overrides']
|
||||
require(isinstance(overrides, list) and len(overrides) == 1
|
||||
and overrides[0].startswith('tests/') and overrides[0] in product, 'one declared QA override')
|
||||
selected = {p: v['sha256'] for p, v in product.items()}
|
||||
require(len(overrides) == 1 and overrides[0].startswith('tests/') and overrides[0] in product,
|
||||
'QA override membership')
|
||||
selected = {p: v['sha256'] for p, v in selected_product.items()}
|
||||
selected[overrides[0]] = native['QA']['sha256']
|
||||
require(native['selected_source_map'] == selected, 'selected source/QA override mismatch')
|
||||
# A relocation is explicit and hash-bound; never open the original path.
|
||||
materials = descriptor['materials']
|
||||
fixed = native.get('fixed_input_pins', [])
|
||||
require(isinstance(fixed, list), 'native fixed input pin list')
|
||||
needed = fixed + [native['QA']]
|
||||
require(set(materials) == {p['path'] for p in needed}, 'runner input/config material membership')
|
||||
require(native['selected_source_map'] == selected, 'selected QA source map')
|
||||
needed = native.get('fixed_input_pins', []) + [native['QA']]
|
||||
require(set(descriptor['materials']) == {p['path'] for p in needed}, 'input material membership')
|
||||
for pin in needed:
|
||||
real = supplied_file(directory, materials[pin['path']])
|
||||
require(real.stat().st_size == pin['bytes'] and sha(real) == pin['sha256'], 'native input/QA bytes')
|
||||
wrapper = supplied_file(directory, descriptor['wrapper'])
|
||||
require(sha(wrapper) == native['wrapper_sha256'], 'native wrapper bytes')
|
||||
digest = manifest_digest(product)
|
||||
require(index['versions'] == {
|
||||
'registered': REGISTERED_COMMIT, 'execution_origin': EXECUTION_ORIGIN,
|
||||
'checker': {'commit': checker_commit, 'sha256': hashlib.sha256(own).hexdigest()},
|
||||
'tested': {'product_target': target, 'manifest_sha256': digest}}, 'four version roles')
|
||||
require(index['source_map'] == {p: v['sha256'] for p, v in product.items()}, 'product source map')
|
||||
return composed, {'manifest_sha256': digest, 'existing_metadata': metadata,
|
||||
'runner_paths': runner_paths,
|
||||
'tested_product': tested, 'product_target': target}
|
||||
material = supplied_file(directory, descriptor['materials'][pin['path']])
|
||||
require(material.stat().st_size == pin['bytes'] and sha(material) == pin['sha256'], 'input bytes')
|
||||
require(sha(supplied_file(directory, descriptor['wrapper'])) == native['wrapper_sha256'], 'wrapper bytes')
|
||||
require(runner_paths, 'runner inputs missing')
|
||||
require(index['versions'] == {'registered': REGISTERED_COMMIT, 'execution_origin': EXECUTION_ORIGIN,
|
||||
'tested': tested}, 'evidence version roles')
|
||||
return composed, {'candidate_structure': candidate, 'role': role, 'actual_head': commit, 'actual_tree': actual_tree,
|
||||
'submission': submitted, 'main': main, 'existing_metadata': metadata,
|
||||
'manifest_sha256': manifest_digest(product), 'runner_paths': runner_paths,
|
||||
'tested_product': tested, 'product_file_count': len(product), 'selected_file_count': len(selected_product)}
|
||||
|
||||
|
||||
def qualify_normal_candidate(commit, tree, main, submitted, composed):
|
||||
"""Qualify structure only; Runner and Root verify provenance externally.
|
||||
|
||||
The documentation roster is never consumed. Structure grants no provenance
|
||||
verification or original credit.
|
||||
"""
|
||||
require(tree == composed and main == MAIN_ANCHOR and commit != submitted,
|
||||
'normal candidate identity mismatch')
|
||||
return {'structure_qualified': True, 'provenance_verified_by_checker': False,
|
||||
'provenance_owner': 'actual Runner.prepare_integration/integrate and Root normal_c_join AFTER final before original credit',
|
||||
'join': {'C': commit, 'S': submitted, 'M': main, 'C_tree': tree, 'Q': composed}}
|
||||
|
||||
|
||||
def function_bytes(data):
|
||||
@@ -366,6 +499,10 @@ def boundary_conditions(path, function):
|
||||
|
||||
|
||||
def applicability(index, spec, source, head):
|
||||
tested = index.get('versions', {}).get('tested')
|
||||
require(isinstance(tested, str) and tree_entries(tested) and
|
||||
product_manifest(tree_entries(tested)) == product_manifest(tree_entries(head)),
|
||||
'wrong tested revision')
|
||||
registered = spec['current_source_pins']
|
||||
require(index.get('registered_pins') == registered, 'registered pin declaration')
|
||||
for path, digest in registered.items():
|
||||
@@ -398,6 +535,19 @@ def applicability(index, spec, source, head):
|
||||
require(case_id == declaration.get('case_id'), 'boundary delta engine case: ' + key)
|
||||
|
||||
|
||||
def owner_authority(ref, entry, path):
|
||||
"""Each decision is bound to its own immutable, path-qualified record."""
|
||||
require(ref in OWNER_BINDINGS, 'unknown owner authority: ' + ref)
|
||||
relative, digest = OWNER_BINDINGS[ref]
|
||||
require(entry.get('status') == 'approved', 'pending/missing authority: ' + ref)
|
||||
require(entry.get('source') == relative, 'owner approval path mismatch: ' + ref)
|
||||
require(sha(path) == digest, 'owner approval hash mismatch: ' + ref)
|
||||
owner = load(path)
|
||||
require(owner.get('origin') == 'direct_user_reply' and owner.get('formal_acceptance') is False,
|
||||
'owner approval scope')
|
||||
require(ref in {v['id'] for v in owner['approved_choices']}, 'owner approval condition')
|
||||
|
||||
|
||||
def coverage(index, raw, source, product_tree):
|
||||
authority = index['authority']
|
||||
for ref in {r for _, refs, _ in REQUIRED.values() for r in refs}:
|
||||
@@ -406,11 +556,7 @@ def coverage(index, raw, source, product_tree):
|
||||
aid = entry.get('artifact_id')
|
||||
require(aid in raw and index['artifacts'][aid]['type'] == 'authority', 'raw authority: ' + ref)
|
||||
if ref.startswith('D05-P04-'):
|
||||
require(sha(raw[aid]) == OWNER_HASH, 'owner approval hash mismatch')
|
||||
owner = load(raw[aid])
|
||||
require(owner.get('origin') == 'direct_user_reply' and owner.get('formal_acceptance') is False,
|
||||
'owner approval scope')
|
||||
require(ref in {v['id'] for v in owner['approved_choices']}, 'owner approval condition')
|
||||
owner_authority(ref, entry, raw[aid])
|
||||
else:
|
||||
path = ('docs/traceability/original/BASELINE.txt' if ref.startswith('BASELINE:') else
|
||||
'docs/DESIGN_DECISIONS.md' if ref == 'D-034' else
|
||||
@@ -805,6 +951,150 @@ def verify_native(base):
|
||||
verified_conditions=['dependency_stop', 'recovery_completion', 'paused_midnight'])
|
||||
|
||||
|
||||
def verify_shift_completion(base):
|
||||
"""C's first 18:00 transaction, never the paid-midnight 1442 sale."""
|
||||
for row in load(safe(base, 'SOURCE_MANIFEST.json'))['files']:
|
||||
native_file(base, row['copy'], row)
|
||||
tested = native_applicability(base)
|
||||
pids = native_ownership(base)
|
||||
shift_pids = [pids['actual-native/' + phase] for phase in NATIVE_RUNS['actual-native']]
|
||||
require(len(shift_pids) == len(set(shift_pids)) == 3, 'shift ended PID identity')
|
||||
inputs = load(safe(base, 'actual-native/runner-input.json'))
|
||||
expected = selected_manifest(tree_entries(tested), require_checker=False)
|
||||
require({p: {'bytes': v['bytes'], 'sha256': v['sha256']} for p, v in inputs['source_before'].items()}
|
||||
== expected, 'shift complete native source')
|
||||
prefix = 'actual-native/order-evidence/'
|
||||
observations = load(safe(base, prefix + 'observations.json'))
|
||||
labels = ['first-before-17-59', 'first-18-00-before-facility', 'first-18-00-after-facility',
|
||||
'first-18-00-after-staff', 'first-18-00-tick-complete', 'first-18-01-tick-complete']
|
||||
points = []
|
||||
previous_ledger = None
|
||||
service_id = None
|
||||
paid_rows = None
|
||||
original_service = None
|
||||
completed_service = None
|
||||
transaction_id = None
|
||||
for i, label in enumerate(labels):
|
||||
rows = [r for r in observations['observations'] if r['label'] == label]
|
||||
require(len(rows) == 1, 'shift phase membership')
|
||||
observation = rows[0]
|
||||
snapshot = load(safe(base, prefix + label + '.json'))
|
||||
minute = [1079, 1080, 1080, 1080, 1080, 1081][i]
|
||||
require(snapshot['clock']['absolute_minute'] == observation['minute'] == minute, 'shift phase clock')
|
||||
state = snapshot['systems']['runtime']
|
||||
service = observation['service']
|
||||
if i == 0:
|
||||
service_id = service['service_id']
|
||||
original_service = service
|
||||
identity = ('service_id', 'request_id', 'facility_id', 'guest_ids', 'inventory_reservations',
|
||||
'price_snapshot', 'duration_minutes', 'started_at', 'staff_ids')
|
||||
require(all(service[k] == original_service[k] for k in identity), 'shift original service identity')
|
||||
require(service['service_id'] == service_id and service['elapsed_minutes'] == (44 if i < 2 else 45)
|
||||
and service['duration_minutes'] == 45 and service['price_snapshot'] == 25,
|
||||
'shift service identity/progress')
|
||||
candidates = state['facilities']['service_history'] + [s for f in state['facilities']['instances']
|
||||
for s in f['active_services']]
|
||||
require([s for s in candidates if s['service_id'] == service_id] == [service], 'shift raw service join')
|
||||
require(observation['reservations'] == state['inventory'], 'shift inventory join')
|
||||
reserved = [r for r in state['inventory']['reservations'] if r['reservation_id'] in service['inventory_reservations']]
|
||||
require(reserved and len(reserved) == len(service['inventory_reservations']) and all(
|
||||
r['requester_id'] == service['request_id'] and r['state'] == ('ACTIVE' if i < 2 else 'CONSUMED')
|
||||
and r['terminal_at'] == (-1 if i < 2 else 1080) for r in reserved), 'shift reservation join')
|
||||
day2 = ['p04.order.server.a', 'p04.order.server.b']
|
||||
require(observation['active_staff'] == (day2 if i < 3 else []), 'shift DAY2 before/after handover')
|
||||
facility = [f for f in state['facilities']['instances'] if f['instance_id'] == service['facility_id']]
|
||||
require(len(facility) == 1 and facility[0]['active_staff_ids'] == observation['active_staff'],
|
||||
'shift full-state active staff join')
|
||||
require([w['staff']['staff_id'] for w in observation['workers']] == day2, 'shift worker identity set')
|
||||
for worker in observation['workers']:
|
||||
require(worker['staff'] in state['staff']['staff'],
|
||||
'shift worker raw join')
|
||||
if i < 3:
|
||||
require(worker['shift'] in state['staff']['shifts'] and worker['is_present'] is True and worker['shift']['kind'] == 'DAY' and
|
||||
worker['shift']['end_at'] == 1080 and worker['shift']['actual_ended_at'] == -1,
|
||||
'shift original DAY worker present')
|
||||
else:
|
||||
ended = [r for r in state['staff']['shifts'] if r['staff_id'] == worker['staff']['staff_id']]
|
||||
require(worker['shift'] == {} and worker['is_present'] is False and
|
||||
worker['staff']['state'] == 'OFF_DUTY' and len(ended) == 1 and
|
||||
ended[0]['actual_ended_at'] == ended[0]['end_at'] == 1080,
|
||||
'shift normal handover history')
|
||||
ledger = state['economy']['entries']
|
||||
sale = [r for r in ledger if r['category'] == 'SERVICE_SALE']
|
||||
if i < 2:
|
||||
require(sale == [] and observation['receipts'] == [], 'shift precompletion charge')
|
||||
else:
|
||||
require(service['state'] == 'COMPLETED' and service['completed_at'] == 1080 and len(sale) == 1,
|
||||
'shift single completion')
|
||||
transaction = sale[0]
|
||||
if transaction_id is None:
|
||||
transaction_id = transaction['transaction_id']
|
||||
require(transaction['transaction_id'] == transaction_id and transaction['amount'] == service['price_snapshot'] and
|
||||
transaction['absolute_minute'] == 1080 and transaction['source_entity_id'] == service['facility_id']
|
||||
and transaction['credit_account'] == 'GUEST_WALLET:' + service['guest_ids'][0], 'shift sale identity')
|
||||
receipts = receipt_rows(observation['receipts'])
|
||||
require(receipts == [r for r in ledger if r['transaction_id'] == transaction_id], 'shift receipt raw join')
|
||||
require([r for r in state['economy']['idempotency'] if r['key'] == 'service.payment.' + service_id]
|
||||
== [{'key': 'service.payment.' + service_id, 'transaction_id': transaction_id}],
|
||||
'shift idempotency join')
|
||||
if i == 2:
|
||||
completed_service = service
|
||||
paid_rows = observation['receipts']
|
||||
require(paid_rows == observations['first_receipts'] and paid_rows[0]['staff_at_receipt'] == day2,
|
||||
'shift receipt precedes handover')
|
||||
else:
|
||||
require(service == completed_service and ledger == previous_ledger and
|
||||
observation['receipts'] == paid_rows, 'shift extra charge/completion')
|
||||
previous_ledger = ledger
|
||||
points.append(label)
|
||||
return {'verified_conditions': ['shift_completion'], 'phases': points, 'transaction_id': transaction_id}
|
||||
|
||||
|
||||
def combine_native_series(directory, native):
|
||||
"""Independent R composition; rerun raw checks, never trust condition scalars.
|
||||
|
||||
The C verifier and its fixtures remain unchanged. This function does not
|
||||
establish candidate applicability, and cannot grant a successful whole gate.
|
||||
"""
|
||||
import importlib.util
|
||||
module_path = ROOT / DOC_SCOPE / 'p04_native_series.py'
|
||||
module_spec = importlib.util.spec_from_file_location('p04_series', module_path)
|
||||
series = importlib.util.module_from_spec(module_spec)
|
||||
module_spec.loader.exec_module(series)
|
||||
c_base = directory / 'audit-support/native-first-current'
|
||||
require(native == verify_native(c_base), 'C result must be recomputed from raw')
|
||||
base = directory / 'audit-support/p04-raw-collect-r22'
|
||||
manifest_path = safe(base, 'MANIFEST.json')
|
||||
manifest = load(manifest_path)
|
||||
require(manifest['protocol'] == 'p04-native-series-collection-v1' and
|
||||
manifest['relocation_manifest_sha256'] == series.MANIFEST_SHA256,
|
||||
'series relocation binding')
|
||||
require(set(manifest['series']) == set(series.RUNS), 'series manifest membership')
|
||||
shift = verify_shift_completion(c_base)
|
||||
verified = set(native['verified_conditions']) | set(shift['verified_conditions'])
|
||||
bindings = {}
|
||||
for name in series.RUNS:
|
||||
binding = manifest['series'][name]
|
||||
path = safe(base, binding['path'])
|
||||
require(path.name == name + '.json' and sha(path) == binding['sha256'] and
|
||||
path.stat().st_size == binding['bytes'] and
|
||||
binding['relocation_manifest_sha256'] == series.MANIFEST_SHA256, 'series manifest SHA binding')
|
||||
recorded = load(path)
|
||||
require(recorded['verifier_sha256'] == sha(module_path), 'series verifier changed since collection')
|
||||
actual = getattr(series, 'verify_' + name)(directory)
|
||||
require({k: v for k, v in recorded.items() if k != 'verifier_sha256'} == actual,
|
||||
'series raw result/reference drift')
|
||||
verified.update(actual['verified_conditions'])
|
||||
bindings[name] = binding['sha256']
|
||||
return {'verified_conditions': sorted(verified), 'series_manifest_sha256': sha(manifest_path),
|
||||
'C_source_manifest_sha256': sha(safe(c_base, 'SOURCE_MANIFEST.json')),
|
||||
'C_provenance_sha256': sha(safe(c_base, 'OWNERSHIP_RAW_PROVENANCE.json')),
|
||||
'series_bindings': bindings, 'relocation_manifest_sha256': series.MANIFEST_SHA256,
|
||||
'missing_conditions': sorted((set(REQUIRED) | {'EV'}) - verified),
|
||||
'nativeWindows': {name: 'pending' for name in sorted(set(REQUIRED) | {'EV'})},
|
||||
'stage_A': 'candidate binding stage A pending', 'formal_acceptance': False}
|
||||
|
||||
|
||||
def verify(directory):
|
||||
# Contract hashes the supplied spec string, while the checked-in JSON has
|
||||
# one final LF. Accept only that exact representation difference.
|
||||
@@ -812,14 +1102,23 @@ def verify(directory):
|
||||
require(sha(SPEC) == SPEC_HASH or (spec_bytes.endswith(b'\n') and
|
||||
hashlib.sha256(spec_bytes[:-1]).hexdigest() == SPEC_HASH), 'dispatch spec pin mismatch')
|
||||
spec = load(SPEC)
|
||||
evidence = directory / 'p04-evidence'
|
||||
evidence = Path(os.environ.get('P04_EVIDENCE_DIR', str(directory / 'p04-evidence'))).absolute()
|
||||
require(not evidence.is_symlink(), 'symlink evidence directory')
|
||||
index = load(safe(evidence, 'index.json'))
|
||||
require(type(index.get('schema_version')) is int and index['schema_version'] == 1, 'schema_version')
|
||||
require(type(index.get('schema_version')) is int and index['schema_version'] in (1, 2), 'schema_version')
|
||||
require(index.get('task_id') == 'P04-01' and index.get('formal_acceptance') is False, 'task/acceptance')
|
||||
checker_commit = git_bytes('rev-parse', 'HEAD').decode().strip()
|
||||
require(index.get('candidate_commit') == checker_commit, 'checker invocation commit mismatch')
|
||||
head, target_result = verify_product_target(index, directory, checker_commit)
|
||||
head, target_result = verify_roles(index, directory)
|
||||
if index['schema_version'] == 2:
|
||||
import importlib.util
|
||||
module_path = ROOT / DOC_SCOPE / 'build_p04_index.py'
|
||||
module_spec = importlib.util.spec_from_file_location('p04_index', module_path)
|
||||
builder = importlib.util.module_from_spec(module_spec)
|
||||
module_spec.loader.exec_module(builder)
|
||||
expected = builder.payload(directory, sys.modules[__name__])
|
||||
require(index == expected, 'forged index/raw mapping differs from recomputation')
|
||||
require(not index['raw_conditions']['missing_conditions'], 'uncollected original boundaries: ' +
|
||||
', '.join(index['raw_conditions']['missing_conditions']))
|
||||
return target_result
|
||||
source = index.get('source_map')
|
||||
applicability(index, spec, source, head)
|
||||
oldmap = load(ROOT / 'docs/plans/original-completion-2026-10-02/execution/p04-01-input-r01/historical-source-map.json')
|
||||
@@ -903,23 +1202,37 @@ def verify(directory):
|
||||
require(native['production_commit'] == target_result['tested_product'], 'native production role mismatch')
|
||||
# This adapter covers only the supplied dependency/formal series. Never
|
||||
# promote it to the missing original scenarios through scalar booleans.
|
||||
missing = (set(REQUIRED) | {'EV'}) - set(native['verified_conditions'])
|
||||
combined = combine_native_series(directory, native)
|
||||
missing = set(combined['missing_conditions'])
|
||||
require(not missing, 'uncollected original boundaries: ' + ', '.join(sorted(missing)) +
|
||||
'; formal_original_credit=0')
|
||||
return target_result
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--verify-only', action='store_true', required=True)
|
||||
parser.parse_args()
|
||||
mode = parser.add_mutually_exclusive_group(required=True)
|
||||
mode.add_argument('--verify-only', action='store_true')
|
||||
mode.add_argument('--verify-series-only', action='store_true', help='Read-only Stage R composition; always leaves Stage A pending')
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
require(bool(os.environ.get('BUILD_OUTPUT_DIR')), 'BUILD_OUTPUT_DIR required')
|
||||
if args.verify_series_only:
|
||||
directory = Path(os.environ['BUILD_OUTPUT_DIR']).absolute()
|
||||
native = verify_native(directory / 'audit-support/native-first-current')
|
||||
result = combine_native_series(directory, native)
|
||||
result.update(task_id='P04-01', machine_verified=False,
|
||||
error='candidate binding stage A pending; uncollected original boundaries: ' +
|
||||
', '.join(result['missing_conditions']))
|
||||
print(json.dumps(result, ensure_ascii=False, sort_keys=True))
|
||||
return 1
|
||||
with isolated_objects():
|
||||
verify(Path(os.environ['BUILD_OUTPUT_DIR']).resolve())
|
||||
result = verify(Path(os.environ['BUILD_OUTPUT_DIR']).resolve())
|
||||
except (ValueError, OSError, KeyError, TypeError, IndexError, AttributeError, subprocess.SubprocessError) as exc:
|
||||
print(json.dumps({'task_id': 'P04-01', 'machine_verified': False, 'formal_acceptance': False, 'error': str(exc)}, ensure_ascii=False))
|
||||
return 1
|
||||
print(json.dumps({'task_id': 'P04-01', 'machine_verified': True, 'formal_acceptance': False}))
|
||||
print(json.dumps({'task_id': 'P04-01', 'machine_verified': True, 'formal_acceptance': False,
|
||||
'role': result['role'], 'actual_head': result['actual_head']}))
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user