P04-01: 同時発生の更新規約
This commit is contained in:
@@ -1,4 +1,12 @@
|
||||
# Current checker protocol — repair29
|
||||
# Checker protocol — historical repair29 description
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -1,31 +1,29 @@
|
||||
# P04-01 current status — repair29
|
||||
# P04-01 current status — repair31
|
||||
|
||||
Worker checks: SOURCE-READY / NOTRUN. No probes or tests were run by this worker.
|
||||
**BLOCKED: partial implementation; do not start the once-only native/full run.**
|
||||
Formal completed status is **16/88**, with **zero P04 credit**.
|
||||
|
||||
This canonical table overrides creation-time NOT RUN / pending-execution entries in REPAIR22–28, their status/validation records, and the runbooks (including S4_ROOT_RUN.md). Those files are historical and remain untouched. It does not relabel an old run as execution of repair29.
|
||||
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.
|
||||
|
||||
`B` = `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
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.
|
||||
|
||||
| Item | Canonical status | Exact-version evidence |
|
||||
| --- | --- | --- |
|
||||
| Current native product / main | Root-measured `5f6bc11d` / `9b1a18c1` | Product1338 at B/audit-support/p04-r17-full-inputs-r01/product; current SHA256 inventory in REPAIR29_STATIC_SOURCE.json. Old deb11300 is not current. |
|
||||
| S4 r28 | Root-measured 4/4 PASS; import/parse/Writer/Reader PIDs49940,50044,50078,50105 all ended; guards0; cleanup0 | B/p04-s4-native-r28; B/audit-support/p04-root-fb8b-s4-r01/native.result.json. Full controls in that same support directory. |
|
||||
| Local8 | Root-measured PASS | B/audit-support/p04-root-fb8b-local-r01: bulk, negative, applicability, native, series, encoder, join, roles result/stdio records. |
|
||||
| Collection | Root-measured r22 collected, no native re-run claimed | B/audit-support/p04-raw-collect-r22/MANIFEST.json and s1/s2a/s2b/s3/s4.json. |
|
||||
| Index | Root-measured schema2, missing_conditions=[], all15 conditions + EV mechanically verified | B/p04-evidence/index.json and generation-invocation.json; local-r01 index/checker output. This is not D05 approval. |
|
||||
| Registered validation on fb8b7919 | Root-measured PASS,28.095s | B/audit-support/p04-root-fb8b-local-r01/registered-validation.result.json and stdout log. Does not establish final repair29 validation or acceptance. |
|
||||
| Full178 | Root-measured178 suites /21615 checks PASS used checker f5, **not a61** | B/audit-support/p18-regression-support-final-r01/native-full178/runner-ended.json and source-start.json; exact receipt remains preserved. Checker f5 SHA256 `f5b44827958f2ae352fe5c4bc3a1ffd4211ec7a41fc4c0759c8b954891662324`. |
|
||||
| Full1339 against a61 | **No such full run exists** | a61 SHA256 `a61c5b496e656f60c66898e42500bc9a2d45f947690ce8f5f19a03984ebbc850`. Local8 and registered validation are separate evidence. |
|
||||
| Worker repair29 | SOURCE-READY / NOTRUN | Static source/line/registration/SHA256 inspection only. No tests, probes, Git, engine, Runner or agents. |
|
||||
| Owner D05 | Pending a–d, candidate only | D05_REMAINING_ORDER_CANDIDATE.md/json; A01, dependency hold and18:00 remain approved. |
|
||||
| Original unit | **Incomplete;13/88 unchanged** | Mechanical PASS does not approve unknown mandatory priorities, replace independent review, or integrate the unit. |
|
||||
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.
|
||||
|
||||
Proof hashes for the exact collection/index/full receipt/validation and current source are in [REPAIR29_STATIC_SOURCE.json](REPAIR29_STATIC_SOURCE.json). Applicable source/config/input/environment joins stay in the immutable schema2 index and raw collection; no raw is normalized, spliced, regenerated or relabelled here.
|
||||
|
||||
Product reuse requires exact applicability: the178 suites do not execute the Python checker and the316-file GDScript parse roster excludes it. Static comparison with the preserved full178 selected_source_map found one test-source difference, tests/business/test_required_kitchen.gd (full178 3a9c92f8…, current product5dcc86d6…), and no game-source difference. Preserve the Runner applicability binding for this test; do not claim all1338 selected product hashes match that full178 receipt or byte-identical full1339 validation against a61. Preserve the f5 full178 receipt and all historical partial/full outputs separately.
|
||||
|
||||
Remaining Runner steps: independent review of these documents, then Root obtains owner decisions (or explicit non-mandatory rulings) for D05 a–d. Unknown mandatory rules stay pending. Any destruction authority pin/registry or other approved implementation change follows a normal repair. The support53cb normal registered rebind, final full run on the final source/checker, final source/config/input/environment binding, formal independent review and integration remain Runner work. Integration order is **P04 first, then support on main9b (`9b1a18c1`)**. NativeWindows obligations remain individually pending if required. No criterion is waived and no extra original unit is counted.
|
||||
|
||||
Known authority/scope gaps: destruction_stop binds BASELINE:183 (mana depletion, not destruction); explicit D05 destruction rule required before normal pin repair. S4 NO_POWER was induced by direct disconnect_nodes; global depletion appears only in B1 combined diagnostic. Preserve these distinctions even though the mechanical index passes.
|
||||
|
||||
Changed files in repair29 (all under this directory): ORDER_AUDIT.md; order-audit.json; D05_REMAINING_ORDER_CANDIDATE.md; D05_REMAINING_ORDER_CANDIDATE.json; CHECKER_PROTOCOL.md; EVIDENCE_COLLECTION.md; P04_ORDER_EVIDENCE_PROTOCOL.json; CURRENT_STATUS.md; REPAIR29_STATIC_SOURCE.json. Product/checker/registry, native footprint, staged inputs, raw and historical runbooks are unchanged.
|
||||
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
|
||||
}
|
||||
|
||||
+4
-3
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"task_id": "P04-01",
|
||||
"revision": "repair29",
|
||||
"status": "approval candidate only; SOURCE-READY / NOTRUN",
|
||||
"approval": null,
|
||||
"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": {
|
||||
@@ -126,5 +126,6 @@
|
||||
},
|
||||
"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
|
||||
"original_unit_complete": false,
|
||||
"superseded_by": "D05_REMAINING_APPROVAL.json"
|
||||
}
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
> 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.
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
# Evidence collection status — repair29
|
||||
# Evidence collection — historical repair29 description
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -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,61 +1,28 @@
|
||||
# P04-01 order audit — repair29
|
||||
# P04-01 order audit — repair31
|
||||
|
||||
Worker **SOURCE-READY / NOTRUN**: static Read and SHA256 only. No original completion. [CURRENT_STATUS.md](CURRENT_STATUS.md) is canonical for execution status. [order-audit.json](order-audit.json) is the matching structured audit.
|
||||
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.
|
||||
|
||||
B = `/Users/korizatou114/Projects/TheTree-managed-worktrees/p04-01/.runtime/P04-01/build`.
|
||||
P = `B/audit-support/p04-r17-full-inputs-r01/product`. Source references below are relative to P, not old deb11300. All1338 product file SHA256 values, function lines and16 handler registrations are recomputed in [REPAIR29_STATIC_SOURCE.json](REPAIR29_STATIC_SOURCE.json).
|
||||
Kernel `scripts/core/simulation_kernel.gd` SHA256: `341c0c878c4894adbac628d0db6362ff69fc3347bdd9e61e50936e15dc2d1edb`.
|
||||
Runtime `autoload/simulation_runtime.gd` SHA256: `7f01b2dcdf28bc965c7588173c2dc70054633faf7204b1ac38ba0f4369331b47`.
|
||||
Root-measured native product `5f6bc11d`; main `9b1a18c1`.
|
||||
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.
|
||||
|
||||
## Actual eleven phases — observed-current
|
||||
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.
|
||||
|
||||
Kernel advance_one_minute:122 defines the order; _run_phase:236 string-sorts handler IDs. Registrations below are Runtime lines366–435 plus operations:1766. Every literal ID begins `simulation_runtime.`. Observations do not approve D05.
|
||||
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.
|
||||
|
||||
1. **APPLY_COMMANDS** — simulation_runtime.commands → _process_tick_commands (registration:369). Previous minute: commands → _process_tick_commands:5743. Operations gate requests current tick abort and returns before Clock changes; otherwise commands apply for minute+1.
|
||||
| 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 |
|
||||
|
||||
2. **TIME_BOUNDARIES** — simulation_runtime.0_weather → _advance_weather (registration:366); simulation_runtime.a_upkeep → _advance_facility_upkeep (registration:368); simulation_runtime.operations → _advance_operations_time (registration:1766). After APPLY: absolute_minute += 1, _sync_calendar_fields, _handle_time_boundaries:254 emits settlement_requested(completed_day) then day_started at00:00, before TIME_BOUNDARIES handlers. Handler order: weather, upkeep, operations. Runtime upkeep:1855; operations:1898 → coordinator _process_minute_boundaries:882: 00:00 upkeep, daily close, 06:00 morning, demand, arrivals (conditional boundaries and early returns).
|
||||
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.
|
||||
|
||||
3. **MANA** — simulation_runtime.mana → _advance_mana (registration:379). Runtime _advance_mana:10694: proxy preflight → mana_network advance → mana_power_projection.apply → MANA_STATE_CHANGED events.
|
||||
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.
|
||||
|
||||
4. **CONSTRUCTION** — simulation_runtime.construction → _advance_construction (registration:384). Construction:8064 follows power and precedes logistics.
|
||||
|
||||
5. **LOGISTICS** — simulation_runtime.logistics → _advance_inventory_and_logistics (registration:389). Logistics:8472 precedes completion.
|
||||
|
||||
6. **FACILITY_COMPLETION** — simulation_runtime.facilities → _advance_facilities (registration:394). Runtime _advance_facilities:8482: facility_service.advance_one_minute then kitchen. FacilityService _prepare_service_for_tick:853 checks BROKEN, related_facility_operating (RELATED_FACILITY_INACTIVE), power_available (NO_POWER), staff grace. Only ACTIVE services advance (:569). _complete_service:895 → Runtime _complete_service_transaction:11769 → _apply_service_transaction:11785 posts SERVICE_SALE / SERVICE_CASH_SWEEP at the post-increment minute (:11826–11838).
|
||||
|
||||
7. **VITALS_AND_ENVIRONMENT** — simulation_runtime.guest_vitals → _advance_guest_vitals (registration:399). Guest vitals:8488 follows completion.
|
||||
|
||||
8. **INCIDENTS_AND_EVENTS** — simulation_runtime.authority_incidents → _advance_authority_incidents (registration:409); simulation_runtime.defense → _advance_defense (registration:414); simulation_runtime.relationship_events → _advance_relationship_events (registration:419). String-sorted authority_incidents:8551 → defense:8577 → relationship_events:8615; after completion/charging.
|
||||
|
||||
9. **FACILITY_QUEUES** — simulation_runtime.guest_queues → _advance_guest_queues (registration:404). _advance_guest_queues:8502 releases closed queues first (_release_closed_business_queues:12624); closed queues spend no patience (:8516–8517).
|
||||
|
||||
10. **AI_DECISIONS** — simulation_runtime.staff_ai → _advance_staff_ai (registration:424). _advance_staff_ai:8658: prune elevator waits, elevators, rescue, staff_service, prune waits, staff_commute (shift handover), staffing sync, cleaning, fatigue, rescue and staffing sync again, then _advance_guest_ai_decisions:8738. DECIDING PLANNED_EXIT → LEAVING; despawn occurs only on later minute (:8760–8762).
|
||||
|
||||
11. **PUBLISH_SNAPSHOT** — simulation_runtime.snapshot → _publish_snapshot (registration:374); simulation_runtime.zz_save_checkpoints → _process_save_checkpoints (registration:431). snapshot:12476 then zz_save_checkpoints:4514; persistence follows publication.
|
||||
|
||||
## Seven domains
|
||||
|
||||
Proofs below are **Root-measured**. R = B/audit-support/p04-raw-collect-r22; I = B/p04-evidence/index.json (schema2). I native_bundle/raw_references/versions/source_map/environment preserve exact C and series bindings. R/MANIFEST.json pins series bytes/SHA256 and relocation; each series retains raw provenance. REPAIR29_STATIC_SOURCE.json also pins R files and I. Source function paths/lines/hashes are in that static manifest.
|
||||
|
||||
| Domain | Status / authority | Phase and source function+line | Relative priority | Exact-version proof | Pending |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 閉店 | approved; D-034, D05-P04-DEPENDENCY | APPLY_COMMANDS / FACILITY_COMPLETION / FACILITY_QUEUES: Runtime _process_tick_commands:5743; FacilityService _prepare_service_for_tick:853; Runtime _release_closed_business_queues:12624 | Command closure before completion; dependency hold during preparation; closed queue release after completion. | I native_bundle C; R/s2a.json; R/s3.json B1 | D05(a) simultaneous reason selection |
|
||||
| 停電 | observed-current; BASELINE:183, P04-01 safety acceptance | MANA → FACILITY_COMPLETION: Runtime _advance_mana:10694; FacilityService _prepare_service_for_tick:853 | Projection before completion; RELATED chosen before NO_POWER. | R/s4.json; B/p04-s4-native-r28; R/s3.json B1 combined diagnostic | D05(a) |
|
||||
| 破壊 | pending D05; P04-01 safety acceptance (original); explicit destruction rule pending D05 | APPLY_COMMANDS vs INCIDENTS_AND_EVENTS: Runtime _process_tick_commands:5743; _advance_defense:8577; _apply_defense_outcome_boundary:9804; FacilityService _prepare_service_for_tick:853 | Command destruction before completion; incident destruction after completion: B4a charges25G then destroys in same tick; B4b next-tick failure/release. | R/s3.json B4a/B4b | D05(b); BASELINE:183 is mana depletion, not destruction authority; destruction_stop registry correction only after approval |
|
||||
| 退出 | pending D05; D-034, P04-01 safety acceptance | APPLY_COMMANDS vs AI_DECISIONS: Runtime _execute_leave_facility_queue:7553; _advance_guest_ai_decisions:8738 | Command exit before completion/charging; AI PLANNED_EXIT after them; despawn later. | R/s3.json B2a/B2b/B3 and B1 departure history | D05(c) |
|
||||
| 課金 | pending D05; P04-01 safety acceptance, D05-P04-DEPENDENCY, D05-P04-SHIFT-1800 | TIME_BOUNDARIES → FACILITY_COMPLETION: Runtime _advance_facility_upkeep:1855; _complete_service_transaction:11769; _apply_service_transaction:11785; coordinator _process_daily_close:1125 | Upkeep/daily close before sale; post-increment sale timestamp, exclusive close period_end;00:00 sale belongs to new day. | I native_bundle C; R/s2a.json; R/s4.json | D05(d), beyond approved A01 and18:00; timing/attribution mandatory P04, numeric math D04/P05 separate |
|
||||
| シフト交代 | approved; BASELINE:64, BASELINE:67, D05-P04-SHIFT-1800 | FACILITY_COMPLETION → AI_DECISIONS: Runtime _advance_facilities:8482; _advance_staff_ai:8658; StaffService advance_one_minute:700 | 18:00 completion/payment before shift handover; day staff [17:59,18:00), night [18:00,18:01). | I C first1080 sale joins; R/s2b.json no-night1081/overtime | Already approved; do not re-ask |
|
||||
| 00:00決算 | approved; BASELINE:71, BASELINE:72, A01, D05-P04-DEPENDENCY | pre-TIME_BOUNDARIES signals → TIME_BOUNDARIES → FACILITY_COMPLETION: Kernel _handle_time_boundaries:254; Runtime _advance_operations_time:1898; coordinator _process_minute_boundaries:882; _process_daily_close:1125 | Midnight signals, then upkeep/close, then completion. | I C paused/formal; R/s2a.json nonzero payroll/upkeep, replay and typed restore | A01 approved ownership-based midnight upkeep not reopened; extra attribution D05(d) |
|
||||
|
||||
S4 proves isolated NO_POWER caused by the probe calling disconnect_nodes directly, not global mana depletion. Global depletion appears only in B1 combined diagnostic; B1 does not prove isolated power or approve priority. BASELINE:183 is mana depletion, not destruction authority. Current destruction_stop binding is therefore a known authority gap. Checker a61 and registry stay unchanged until approval and normal repair.
|
||||
|
||||
## Cross-cutting safety
|
||||
|
||||
- **No charge while paused NO_POWER or RELATED** — original; FACILITY_COMPLETION; FacilityService _prepare_service_for_tick:853, ACTIVE-only gate:569, _complete_service:895. Preparation before advance/completion/payment. Root-measured proof: R/s4.json isolated power; I C and R/s2a.json dependency hold.
|
||||
- **Double-settlement idempotency** — original; TIME_BOUNDARIES; coordinator _process_daily_close:1125; _day_summaries replay:1127–1130; daily-close:N:1137. Before facility completion. Root-measured proof: I C; R/s2a.json same-boundary replay and separate-PID restore, unchanged accounting.
|
||||
- **Orphan reservation release** — original; APPLY_COMMANDS / FACILITY_QUEUES; Runtime _execute_leave_facility_queue:7553 (release keys:7565–7569); _leave_guest_queue_boundary:9178; _release_closed_business_queues:12624; _release_closed_business_request:12642. Command release before completion; closed-queue release afterward; closed patience not spent. Root-measured proof: R/s3.json B2a/B2b/B3/B4b; I C; R/s2a.json reservation/service identity and restore.
|
||||
|
||||
A01 midnight ownership upkeep, dependency hold and18:00 are already approved; do not re-ask. Authority pins are in I/authority; D05_OWNER_APPROVAL.json approves only the two named D05 choices. Remaining candidate choices and their side effects are in [D05_REMAINING_ORDER_CANDIDATE.md](D05_REMAINING_ORDER_CANDIDATE.md). P04 timing/attribution remains mandatory and separate from D04/P05 numeric cost math. Unknown mandatory rules remain pending.
|
||||
|
||||
Original credit **13/88 unchanged**,15 conditions + EV, seven domains, three safety outcomes and30/88 denominator unchanged. P04 remains incomplete. Root execution history and remaining Runner steps are in CURRENT_STATUS.md.
|
||||
See [CURRENT_STATUS.md](CURRENT_STATUS.md) and [REPAIR31_STATUS.md](REPAIR31_STATUS.md) before any Root execution.
|
||||
|
||||
+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.
|
||||
@@ -35,8 +35,8 @@ def payload(build, gate=None):
|
||||
native = build / 'audit-support/native-first-current'
|
||||
c = gate.verify_native(native)
|
||||
raw = gate.combine_native_series(build, c)
|
||||
source = gate.product_manifest(gate.tree_entries(gate.TESTED_PRODUCT))
|
||||
gate.require(len(source) == 1338, 'complete native source required')
|
||||
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.
|
||||
@@ -80,13 +80,13 @@ def payload(build, gate=None):
|
||||
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.DOC_SCOPE + 'D05_OWNER_APPROVAL.json' if ref.startswith('D05-P04-') else
|
||||
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.require(gate.sha(path) == gate.OWNER_HASH, 'owner approval hash')
|
||||
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',
|
||||
@@ -103,16 +103,16 @@ def payload(build, gate=None):
|
||||
for name, rows in gate.REACH_REFS.items():
|
||||
refs = []
|
||||
for path, function, digest, lines in rows:
|
||||
gate.resolve_anchor(gate.git_bytes('show', gate.TESTED_PRODUCT + ':' + path), function, digest, lines)
|
||||
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, gate.TESTED_PRODUCT)
|
||||
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': gate.TESTED_PRODUCT},
|
||||
'product_binding': {'tested_product': gate.TESTED_PRODUCT, 'runner_inputs': runners},
|
||||
'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
|
||||
@@ -123,10 +123,10 @@ def payload(build, gate=None):
|
||||
'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': '13/88',
|
||||
'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, gate.TESTED_PRODUCT)
|
||||
gate.applicability(index, gate.load(gate.SPEC), source_map, tested)
|
||||
return index
|
||||
|
||||
|
||||
@@ -140,13 +140,14 @@ def main():
|
||||
args = parser.parse_args()
|
||||
gate = checker()
|
||||
os.environ['BUILD_OUTPUT_DIR'] = str(args.build.resolve(strict=True))
|
||||
gate.require(args.registered_ref == gate.REGISTERED_COMMIT and args.tested_ref == gate.TESTED_PRODUCT,
|
||||
'source refs differ from immutable evidence roles')
|
||||
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.
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Root-only disposable repository controls; never original acceptance evidence."""
|
||||
"""Isolated disposable repository controls; never original acceptance evidence."""
|
||||
import importlib.util
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -24,8 +24,9 @@ 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-') as temporary:
|
||||
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
|
||||
|
||||
+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))
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Isolated candidate qualification negative; no whole-role/native credit."""
|
||||
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 run():
|
||||
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)
|
||||
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
|
||||
try:
|
||||
gate.qualify_normal_candidate(synthetic, tree, main, submission, tree)
|
||||
except ValueError as exc:
|
||||
assert 'qualification context gap' in str(exc), str(exc)
|
||||
else:
|
||||
raise AssertionError('synthetic C accepted')
|
||||
return {'scope': 'isolated candidate qualification only; full role fixtures remain Root-run',
|
||||
'synthetic_equal_parents_and_tree': 'rejected', 'formal_acceptance': False,
|
||||
'normal_C_positive': 'unexecuted: authoritative receipt contract unavailable'}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(json.dumps(run(), indent=2))
|
||||
+4
-1
@@ -48,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',
|
||||
|
||||
+20
-4
@@ -23,8 +23,11 @@ def run(build, evidence):
|
||||
for name in ('GIT_DIR', 'GIT_WORK_TREE', 'GIT_INDEX_FILE', 'GIT_OBJECT_DIRECTORY',
|
||||
'GIT_ALTERNATE_OBJECT_DIRECTORIES'):
|
||||
env.pop(name, None)
|
||||
with tempfile.TemporaryDirectory(prefix='p04-role-fixtures-', dir=build) as temp:
|
||||
# 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)
|
||||
@@ -33,7 +36,7 @@ def run(build, evidence):
|
||||
return subprocess.check_output(['git', '-c', 'core.hooksPath=/dev/null', *args], cwd=checkout, env=env).decode().strip()
|
||||
|
||||
actual = git('rev-parse', 'HEAD')
|
||||
anchor = '9b1a18c1eb25ca1300dcd59da78423c0dda2f498'
|
||||
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)
|
||||
@@ -101,8 +104,21 @@ def run(build, evidence):
|
||||
git('checkout', '--detach', anchor)
|
||||
git('merge', '--no-ff', '--no-edit', submission)
|
||||
merged = git('rev-parse', 'HEAD')
|
||||
check('real_integration_positive', positive=True)
|
||||
check('real_final_positive', positive=True)
|
||||
# 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.
|
||||
# Equal parents/tree are insufficient, even for git merge --no-ff.
|
||||
check('unqualified_merge_rejected')
|
||||
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_rejected')
|
||||
git('checkout', '--detach', merged)
|
||||
game.unlink()
|
||||
git('add', '-u')
|
||||
git('commit', '--amend', '--no-edit')
|
||||
|
||||
@@ -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())
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"revision": "repair29",
|
||||
"status": "SOURCE-READY / NOTRUN; Root measurements separately recorded",
|
||||
"revision": "repair30",
|
||||
"status": "BLOCKED: partial implementation; no native admission",
|
||||
"task_id": "P04-01",
|
||||
"current_table": true,
|
||||
"scope": {
|
||||
@@ -14,166 +14,296 @@
|
||||
],
|
||||
"packages": 30,
|
||||
"deliveries": 88,
|
||||
"original_credit": "13/88 unchanged; P04 receives zero credit",
|
||||
"original_credit": "16/88 formal; P04 receives zero credit",
|
||||
"CurrentAlphaScope": "unchanged",
|
||||
"main_9b": "frozen; nothing integrated"
|
||||
"frozen_main": "c0ea009b28a7649aca6a433a1a24dc0be5e7ab81"
|
||||
},
|
||||
"domains": [
|
||||
{
|
||||
"domain": "閉店",
|
||||
"normative_rule": "D-034: direct closure releases waiting requests and continues started service. Approved dependency closure holds progress/payment and preserves reservation/service ID until one completion.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Runtime _process_tick_commands:5743",
|
||||
"FacilityService _prepare_service_for_tick:853",
|
||||
"Runtime _release_closed_business_queues:12624"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_process_tick_commands",
|
||||
"line": 5765,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "9de66a4bb84b12304300d49085f44546412319bd4aee91e67b61becf97e50294"
|
||||
},
|
||||
{
|
||||
"path": "scripts/facility/facility_service.gd",
|
||||
"function": "_prepare_service_for_tick",
|
||||
"line": 853,
|
||||
"sha256": "ceb4a3e9224587423f273cdb2b56e6843ab176ac1fc4fe79dd498b5ed5c33b77",
|
||||
"function_sha256": "b35c2c0fb23a04258b71995dd7c3b6dfacbbcdf36106e2e3348acef0e18d21cc"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_release_closed_business_queues",
|
||||
"line": 12672,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "3a05fbe94877eeb750fb0d692501954555ab6a8005448ab3b6023dab9bad816b"
|
||||
}
|
||||
],
|
||||
"evidence": "I native_bundle C; R/s2a.json; R/s3.json B1",
|
||||
"approved_authority": [
|
||||
"D-034",
|
||||
"D05-P04-DEPENDENCY"
|
||||
],
|
||||
"pending_D05": [
|
||||
"D05(a) simultaneous reason selection"
|
||||
"D05-P04-DEPENDENCY",
|
||||
"D05-P04-PAUSE-REASON"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "APPLY_COMMANDS / FACILITY_COMPLETION / FACILITY_QUEUES",
|
||||
"relative_priority": "Command closure before completion; dependency hold during preparation; closed queue release after completion.",
|
||||
"status": "approved",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "I native_bundle C; R/s2a.json; R/s3.json B1",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "停電",
|
||||
"normative_rule": "BASELINE:183 mana depletion stops facilities. A legitimately power-stopped service must not progress/charge; retain reservation identity and complete once after recovery. This does not approve a simultaneous-cause priority.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Runtime _advance_mana:10694",
|
||||
"FacilityService _prepare_service_for_tick:853"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_mana",
|
||||
"line": 10716,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "05c54626ce6e355acd92994ab80aaf2177c5cf194885e66934552139e087599e"
|
||||
},
|
||||
{
|
||||
"path": "scripts/facility/facility_service.gd",
|
||||
"function": "_prepare_service_for_tick",
|
||||
"line": 853,
|
||||
"sha256": "ceb4a3e9224587423f273cdb2b56e6843ab176ac1fc4fe79dd498b5ed5c33b77",
|
||||
"function_sha256": "b35c2c0fb23a04258b71995dd7c3b6dfacbbcdf36106e2e3348acef0e18d21cc"
|
||||
}
|
||||
],
|
||||
"evidence": "R/s4.json; B/p04-s4-native-r28; R/s3.json B1 combined diagnostic",
|
||||
"approved_authority": [
|
||||
"BASELINE:183",
|
||||
"P04-01 safety acceptance"
|
||||
],
|
||||
"pending_D05": [
|
||||
"D05(a)"
|
||||
"P04-01 safety acceptance",
|
||||
"D05-P04-PAUSE-REASON"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "MANA → FACILITY_COMPLETION",
|
||||
"relative_priority": "Projection before completion; RELATED chosen before NO_POWER.",
|
||||
"status": "observed-current",
|
||||
"observation_status": "observed-current",
|
||||
"evidence_scope": "S4 probe calls disconnect_nodes directly: isolated NO_POWER, not global depletion. Global depletion appears only in S3 B1 combined diagnostic. S1 covers separate staff/EV safety."
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"evidence_scope": "S4 probe calls disconnect_nodes directly: isolated NO_POWER, not global depletion. Global depletion appears only in S3 B1 combined diagnostic. S1 covers separate staff/EV safety.",
|
||||
"historical_evidence": "R/s4.json; B/p04-s4-native-r28; R/s3.json B1 combined diagnostic",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "破壊",
|
||||
"normative_rule": "No charge while legitimately failed/stopped and no orphan reservation. Distinguish command destruction before facility processing from late INCIDENTS event destruction after completion; observation is not normative priority approval.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Runtime _process_tick_commands:5743",
|
||||
"_advance_defense:8577",
|
||||
"_apply_defense_outcome_boundary:9804",
|
||||
"FacilityService _prepare_service_for_tick:853"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_process_tick_commands",
|
||||
"line": 5765,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "9de66a4bb84b12304300d49085f44546412319bd4aee91e67b61becf97e50294"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_defense",
|
||||
"line": 8599,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "db38b51531276d2620999d57eed7a6219b73f8b8d9ff3706cf569f4797918527"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_apply_defense_outcome_boundary",
|
||||
"line": 9826,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "182c48282d96c6ccd1e5f636751db9de8cef2770ad433b291db89111c5fb5043"
|
||||
},
|
||||
{
|
||||
"path": "scripts/facility/facility_service.gd",
|
||||
"function": "_prepare_service_for_tick",
|
||||
"line": 853,
|
||||
"sha256": "ceb4a3e9224587423f273cdb2b56e6843ab176ac1fc4fe79dd498b5ed5c33b77",
|
||||
"function_sha256": "b35c2c0fb23a04258b71995dd7c3b6dfacbbcdf36106e2e3348acef0e18d21cc"
|
||||
}
|
||||
],
|
||||
"evidence": "R/s3.json B4a/B4b",
|
||||
"approved_authority": [
|
||||
"P04-01 safety acceptance (original); explicit destruction rule pending D05"
|
||||
],
|
||||
"pending_D05": [
|
||||
"D05(b); BASELINE:183 is mana depletion, not destruction authority; destruction_stop registry correction only after approval"
|
||||
"D05-P04-DESTRUCTION"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "APPLY_COMMANDS vs INCIDENTS_AND_EVENTS",
|
||||
"relative_priority": "Command destruction before completion; incident destruction after completion: B4a charges25G then destroys in same tick; B4b next-tick failure/release.",
|
||||
"status": "pending D05",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "R/s3.json B4a/B4b",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "退出",
|
||||
"normative_rule": "Leave/abandon releases the owned reservation once; no orphan or duplicate charge. Preserve D-034. Completion, queue release and later normal despawn are distinct observations.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Runtime _execute_leave_facility_queue:7553",
|
||||
"_advance_guest_ai_decisions:8738"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_execute_leave_facility_queue",
|
||||
"line": 7575,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "04733da2c6f9627e6d831f8bb6b620018d63305fe43bb927abf21a969288d90d"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_guest_ai_decisions",
|
||||
"line": 8760,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "e33b41294e88dba840affd5cc0a5c58fe5d46f5d98a80a8d5f4e907a3b9a7b93"
|
||||
}
|
||||
],
|
||||
"evidence": "R/s3.json B2a/B2b/B3 and B1 departure history",
|
||||
"approved_authority": [
|
||||
"D-034",
|
||||
"P04-01 safety acceptance"
|
||||
],
|
||||
"pending_D05": [
|
||||
"D05(c)"
|
||||
"P04-01 safety acceptance",
|
||||
"D05-P04-EXIT"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "APPLY_COMMANDS vs AI_DECISIONS",
|
||||
"relative_priority": "Command exit before completion/charging; AI PLANNED_EXIT after them; despawn later.",
|
||||
"status": "pending D05",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "R/s3.json B2a/B2b/B3 and B1 departure history",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "課金",
|
||||
"normative_rule": "No stopped charge; one sale per completed service and idempotent resend. Reservation consumption, actual sale and payment key must join by service ID and original priceSnapshot.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Runtime _advance_facility_upkeep:1855",
|
||||
"_complete_service_transaction:11769",
|
||||
"_apply_service_transaction:11785",
|
||||
"coordinator _process_daily_close:1125"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_facility_upkeep",
|
||||
"line": 1876,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "33a969a14d1a15296059cecadc4bf7e5a35c9eb61d82cb9e2d6d9bbcf5c32c68"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_complete_service_transaction",
|
||||
"line": 11817,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "f2b6e96a8eeef3dd3f8419b4d9d5ef4e1d13241600ce970bdd78a924428cac6c"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_apply_service_transaction",
|
||||
"line": 11833,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "950474ff3ca8a867ee025c2b7635586d79f2385f79586fc66107191ceb329a9a"
|
||||
},
|
||||
{
|
||||
"path": "scripts/operations/operations_coordinator.gd",
|
||||
"function": "_process_daily_close",
|
||||
"line": 1125,
|
||||
"sha256": "871a248ee101b6df543fd5cf0440b5d4b0f02c21fa145a99f0687888c1bc6f90",
|
||||
"function_sha256": "665ac0ac4d4c220e68e98810da063fd2ad7ae7dc9e1997944a1e7bea7a3c30e6"
|
||||
}
|
||||
],
|
||||
"evidence": "I native_bundle C; R/s2a.json; R/s4.json",
|
||||
"approved_authority": [
|
||||
"P04-01 safety acceptance",
|
||||
"D05-P04-DEPENDENCY",
|
||||
"D05-P04-SHIFT-1800"
|
||||
],
|
||||
"pending_D05": [
|
||||
"D05(d), beyond approved A01 and18:00; timing/attribution mandatory P04, numeric math D04/P05 separate"
|
||||
"D05-P04-SHIFT-1800",
|
||||
"D05-P04-MIDNIGHT-DAY"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "TIME_BOUNDARIES → FACILITY_COMPLETION",
|
||||
"relative_priority": "Upkeep/daily close before sale; post-increment sale timestamp, exclusive close period_end;00:00 sale belongs to new day.",
|
||||
"status": "pending D05",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "I native_bundle C; R/s2a.json; R/s4.json",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "シフト交代",
|
||||
"normative_rule": "Approved [17:59,18:00) work belongs to day staff: complete/pay once at18:00 then switch; night work starts [18:00,18:01).",
|
||||
"normative_rule": "Approved 18:00 order remains: completion and payment before handover. No renewal of prior approval.",
|
||||
"current_source": [
|
||||
"Runtime _advance_facilities:8482",
|
||||
"_advance_staff_ai:8658",
|
||||
"StaffService advance_one_minute:700"
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_facilities",
|
||||
"line": 8504,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "f1efd0c23bdd60d1ef19a21879640b53b8b4d88844c0c881fe7cd724387ec274"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_staff_ai",
|
||||
"line": 8680,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "0e222987f7c5e25583dbaba8fe63784aa563715067396e8d31d10bd191e7179b"
|
||||
}
|
||||
],
|
||||
"evidence": "I C first1080 sale joins; R/s2b.json no-night1081/overtime",
|
||||
"approved_authority": [
|
||||
"BASELINE:64",
|
||||
"BASELINE:67",
|
||||
"D05-P04-SHIFT-1800"
|
||||
],
|
||||
"pending_D05": [
|
||||
"Already approved; do not re-ask"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "FACILITY_COMPLETION → AI_DECISIONS",
|
||||
"relative_priority": "18:00 completion/payment before shift handover; day staff [17:59,18:00), night [18:00,18:01).",
|
||||
"status": "approved",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "I C first1080 sale joins; R/s2b.json no-night1081/overtime",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
},
|
||||
{
|
||||
"domain": "00:00決算",
|
||||
"normative_rule": "A01 approved ownership-prorated upkeep at midnight is retained. One daily close/payroll/upkeep, no duplicate replay/reload; distinguish prior [0,1440) accounting and sale at1440.",
|
||||
"normative_rule": "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.",
|
||||
"current_source": [
|
||||
"Kernel _handle_time_boundaries:254",
|
||||
"Runtime _advance_operations_time:1898",
|
||||
"coordinator _process_minute_boundaries:882",
|
||||
"_process_daily_close:1125"
|
||||
{
|
||||
"path": "scripts/core/simulation_kernel.gd",
|
||||
"function": "_handle_time_boundaries",
|
||||
"line": 254,
|
||||
"sha256": "341c0c878c4894adbac628d0db6362ff69fc3347bdd9e61e50936e15dc2d1edb",
|
||||
"function_sha256": "2ced47a2911b36def9d89de82a43afc69148ead44d19db6a2a6c7bce5a910ae9"
|
||||
},
|
||||
{
|
||||
"path": "autoload/simulation_runtime.gd",
|
||||
"function": "_advance_operations_time",
|
||||
"line": 1919,
|
||||
"sha256": "45514aea9367ff678f4dc05166bb9b58121f240d4b5565edeb7ab258e3ded8e0",
|
||||
"function_sha256": "511580d1fac3d2785072e94c2981065fa47ba18b1c97122922645dac5c0d94ab"
|
||||
},
|
||||
{
|
||||
"path": "scripts/operations/operations_coordinator.gd",
|
||||
"function": "_process_minute_boundaries",
|
||||
"line": 882,
|
||||
"sha256": "871a248ee101b6df543fd5cf0440b5d4b0f02c21fa145a99f0687888c1bc6f90",
|
||||
"function_sha256": "1312166dc17aee0ef470069a73ff3fe6b86d11d04ca7de76f3d04f1d2d14674e"
|
||||
},
|
||||
{
|
||||
"path": "scripts/operations/operations_coordinator.gd",
|
||||
"function": "_process_daily_close",
|
||||
"line": 1125,
|
||||
"sha256": "871a248ee101b6df543fd5cf0440b5d4b0f02c21fa145a99f0687888c1bc6f90",
|
||||
"function_sha256": "665ac0ac4d4c220e68e98810da063fd2ad7ae7dc9e1997944a1e7bea7a3c30e6"
|
||||
}
|
||||
],
|
||||
"evidence": "I C paused/formal; R/s2a.json nonzero payroll/upkeep, replay and typed restore",
|
||||
"approved_authority": [
|
||||
"BASELINE:71",
|
||||
"BASELINE:72",
|
||||
"A01",
|
||||
"D05-P04-DEPENDENCY"
|
||||
],
|
||||
"pending_D05": [
|
||||
"A01 approved ownership-based midnight upkeep not reopened; extra attribution D05(d)"
|
||||
"D05-P04-DEPENDENCY",
|
||||
"D05-P04-MIDNIGHT-DAY"
|
||||
],
|
||||
"pending_D05": [],
|
||||
"phase": "pre-TIME_BOUNDARIES signals → TIME_BOUNDARIES → FACILITY_COMPLETION",
|
||||
"relative_priority": "Midnight signals, then upkeep/close, then completion.",
|
||||
"status": "approved",
|
||||
"observation_status": "observed-current"
|
||||
"status": "approved policy; fresh Q evidence pending",
|
||||
"observation_status": "historical observations; current M source inspected",
|
||||
"historical_evidence": "I C paused/formal; R/s2a.json nonzero payroll/upkeep, replay and typed restore",
|
||||
"evidence": "Fresh exact-Q native proof pending; no old raw reused as Q",
|
||||
"nativeWindows": "pending"
|
||||
}
|
||||
],
|
||||
"source_facts": "REPAIR29_STATIC_SOURCE.json",
|
||||
"authority": "D05_OWNER_APPROVAL.json approves dependency and shift only; original BASELINE/D-034/A01 retain their scope",
|
||||
"source_facts": "REPAIR30_SOURCE_FACTS.json",
|
||||
"authority": "D05_OWNER_APPROVAL.json preserves dependency/18:00; D05_REMAINING_APPROVAL.json binds all four actual 2026-10-18 answers; BASELINE/D-034/A01 unchanged.",
|
||||
"historical_superseded": [
|
||||
"REPAIR17_STATUS.md",
|
||||
"REPAIR18_STATUS.md",
|
||||
@@ -184,18 +314,16 @@
|
||||
"REPAIR21_STATUS.md",
|
||||
"REPAIR22–28 creation-time status and runbooks"
|
||||
],
|
||||
"history_policy": "Creation-time NOT RUN in REPAIR22–28 and runbooks is historical; CURRENT_STATUS.md canonical table overrides execution status. Historical files unchanged.",
|
||||
"evidence_policy": "Root-measured S4r28 4/4 PASS, controls, local8, r22 collection, schema2 index, registered validation PASS28s; worker SOURCE-READY / NOTRUN.",
|
||||
"history_policy": "Historical source/proof pins retain their original revisions; CURRENT_STATUS.md is canonical. New approvals are not new native evidence.",
|
||||
"evidence_policy": "All old S1-S4/r22/native/full178 records are historical. None is relabelled as Q. Fresh native S1-S5 and full184/329 on final Q remain NOT RUN.",
|
||||
"pending": [
|
||||
"Owner D05 a–d approval or explicit non-mandatory ruling; unknown mandatory rules stay pending",
|
||||
"Post-approval normal checker/registry repair (a61 unchanged here)",
|
||||
"support53cb normal registered rebind",
|
||||
"final full1339 against final checker; none exists against a61",
|
||||
"independent review and integration: P04 first, support on main9b afterward",
|
||||
"nativeWindows obligations individually if required"
|
||||
"Complete current-Q collector/wrapper/index binding and S5 drivers; see REPAIR30_STATUS.md",
|
||||
"Fresh native S1-S5 on final Q, then full184/329 on the same Q including checker",
|
||||
"Truthful index, formal registered validation on S --verify-only, whole-unit independent review, normal C integration",
|
||||
"nativeWindows individually pending for every condition"
|
||||
],
|
||||
"priority_candidates_if_required": "D05_REMAINING_ORDER_CANDIDATE.json",
|
||||
"collection_plan": "s4/S4_ROOT_RUN.md",
|
||||
"priority_candidates_if_required": "D05_REMAINING_ORDER_CANDIDATE.json (superseded history)",
|
||||
"collection_plan": "REPAIR30_STATUS.md",
|
||||
"formal_acceptance": false,
|
||||
"original_unit_complete": false,
|
||||
"phase_order": [
|
||||
@@ -436,5 +564,24 @@
|
||||
],
|
||||
"observed-current": "All phase and source behavior observations; not approval"
|
||||
},
|
||||
"full178_applicability_context": "REPAIR29_STATIC_SOURCE.json full_source_static_comparison: full178 selected1339 includes f5; one test-source mismatch (test_required_kitchen.gd), no game-source mismatch; preserve Runner applicability binding, no blanket1338 identity or a61 full proof."
|
||||
"full178_applicability_context": "REPAIR29_STATIC_SOURCE.json full_source_static_comparison: full178 selected1339 includes f5; one test-source mismatch (test_required_kitchen.gd), no game-source mismatch; preserve Runner applicability binding, no blanket1338 identity or a61 full proof.",
|
||||
"current_source_commit": "c0ea009b28a7649aca6a433a1a24dc0be5e7ab81",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,22 @@
|
||||
# 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 and historical
|
||||
template bindings. It is not fresh-Q plumbing. Do not run it against Q or start
|
||||
the once-only native/full batch until the separately pending identity work is
|
||||
finished. No old S1–S4 record is renamed or reused as fresh proof.
|
||||
|
||||
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. Root alone runs engines and scratch fixtures.
|
||||
@@ -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,405 @@
|
||||
#!/usr/bin/env python3
|
||||
"""S5-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_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 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-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")
|
||||
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("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)
|
||||
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 / "S5_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-s5-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("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) == 316, "complete 316-script parse roster")
|
||||
r.write(out / "parse-paths.json", paths)
|
||||
markers = module("s5_markers", root / "tools/strict_test_markers.py")
|
||||
contracts = markers.load_contracts(CONTRACTS)
|
||||
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,
|
||||
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 = 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 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()
|
||||
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,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,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()
|
||||
@@ -20,13 +20,31 @@ 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 = '9b1a18c1eb25ca1300dcd59da78423c0dda2f498'
|
||||
TESTED_PRODUCT = '5f6bc11d6bb6e92d22d787989b219fef3a435bc1'
|
||||
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'
|
||||
# Required evidence content, not an invented Runner API. Root must supply the
|
||||
# independently authoritative receipt path/hash contract before C can qualify.
|
||||
# Neither an index nor a commit with matching parents/tree can supply authority.
|
||||
NORMAL_C_RECEIPT_CONTRACT = None
|
||||
NORMAL_C_RECEIPT_FIELDS = 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.
|
||||
@@ -43,19 +61,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}),
|
||||
@@ -244,6 +263,16 @@ def product_manifest(entries):
|
||||
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()
|
||||
|
||||
@@ -266,91 +295,6 @@ def supplied_file(directory, descriptor):
|
||||
return path
|
||||
|
||||
|
||||
def verify_product_target(index, directory, checker_commit):
|
||||
"""Four actual roles, complete source selection, and candidate equality.
|
||||
|
||||
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')
|
||||
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)]
|
||||
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')
|
||||
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)
|
||||
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(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()}
|
||||
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')
|
||||
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}
|
||||
|
||||
|
||||
def ancestor(older, newer):
|
||||
# merge-base's nonzero status is data only for the ordinary non-ancestor case.
|
||||
try:
|
||||
@@ -407,7 +351,7 @@ def verify_roles(index, directory):
|
||||
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(ancestor(MAIN_ANCHOR, main), 'merge main ancestry')
|
||||
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',
|
||||
@@ -425,6 +369,7 @@ def verify_roles(index, directory):
|
||||
'unknown source hidden as metadata: ' + path)
|
||||
if role == 'integration/final':
|
||||
require(actual_tree == composed and actual == target, 'actual merge differs from composition')
|
||||
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:
|
||||
@@ -432,16 +377,22 @@ def verify_roles(index, directory):
|
||||
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')
|
||||
require(binding['tested_product'] == TESTED_PRODUCT, 'wrong tested revision')
|
||||
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')
|
||||
product = product_manifest(target)
|
||||
tested_entries = tree_entries(TESTED_PRODUCT)
|
||||
tested_entries = tree_entries(tested)
|
||||
expected = product_manifest(tested_entries)
|
||||
require(len(product) == 1338 and product == expected and
|
||||
require(product == expected and
|
||||
product_manifest(main_entries) == expected and product_manifest(anchor_entries) == expected,
|
||||
'complete 1338-file native product mismatch')
|
||||
'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')
|
||||
require(index['source_map'] == {p: v['sha256'] for p, v in product.items()}, 'stale pin: source map')
|
||||
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 binding['runner_inputs']:
|
||||
path = supplied_file(directory, descriptor)
|
||||
@@ -449,14 +400,14 @@ def verify_roles(index, directory):
|
||||
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()),
|
||||
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(len(overrides) == 1 and overrides[0].startswith('tests/') and overrides[0] in product,
|
||||
'QA override membership')
|
||||
selected = {p: v['sha256'] for p, v in product.items()}
|
||||
selected = {p: v['sha256'] for p, v in selected_product.items()}
|
||||
selected[overrides[0]] = native['QA']['sha256']
|
||||
require(native['selected_source_map'] == selected, 'selected QA source map')
|
||||
needed = native.get('fixed_input_pins', []) + [native['QA']]
|
||||
@@ -467,11 +418,26 @@ def verify_roles(index, directory):
|
||||
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_PRODUCT}, 'evidence version roles')
|
||||
'tested': tested}, 'evidence version roles')
|
||||
return composed, {'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}
|
||||
'tested_product': tested, 'product_file_count': len(product), 'selected_file_count': len(selected_product)}
|
||||
|
||||
|
||||
def qualify_normal_candidate(commit, tree, main, submitted, composed):
|
||||
"""Fail closed until the actual Root/Runner authority contract is supplied.
|
||||
|
||||
The field roster above records the required joins. It intentionally is not
|
||||
a self-issued attestation format that a synthetic commit can satisfy.
|
||||
"""
|
||||
require(tree == composed and main == MAIN_ANCHOR and commit != submitted,
|
||||
'normal candidate identity mismatch')
|
||||
require(NORMAL_C_RECEIPT_CONTRACT is not None,
|
||||
'normal C qualification context gap: authoritative Root/Runner receipt '
|
||||
'path, SHA256 and provenance verification contract are not supplied')
|
||||
# No authority implementation is inferred from a caller-supplied object.
|
||||
raise ValueError('normal C receipt authority verifier is not implemented')
|
||||
|
||||
|
||||
def function_bytes(data):
|
||||
@@ -572,6 +538,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}:
|
||||
@@ -580,11 +559,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
|
||||
@@ -983,14 +958,13 @@ 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)
|
||||
require(native_applicability(base) == TESTED_PRODUCT, 'shift tested revision')
|
||||
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 = product_manifest(tree_entries(TESTED_PRODUCT))
|
||||
require(len(inputs['source_before']) == 1338 and
|
||||
{p: {'bytes': v['bytes'], 'sha256': v['sha256']} for p, v in inputs['source_before'].items()}
|
||||
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'))
|
||||
|
||||
Reference in New Issue
Block a user