P16-01-DISMISS: 一般従業員の通常操作での解雇(退職金を含む。P16-01 の補助の作業。原単位への加算 0)

This commit is contained in:
Agent Control Runner
2026-10-24 12:49:31 +09:00
parent 0b59d7c23c
commit b06fec17c7
17 changed files with 2503 additions and 17 deletions
@@ -3991,6 +3991,39 @@
"path": "docs/plans/original-completion-2026-10-02/execution/p07-01/SPEC.md",
"scope": "new P07-01 F7 restart reader marker; native execution pending"
}
},
{
"test": "tests/staff/test_general_dismissal.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "b69a786539d3afd9f80a9857169b8a1687e176a172c4d63bca248dfe0ee316c1",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p16-01-dismiss/SPEC.md",
"scope": "new P16-01-DISMISS suite marker; native execution pending"
}
},
{
"test": "tests/staff/test_general_dismissal_restart_1_write.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "05514993cd2fd37c709555a584256ee8233ec2e4b0feca37a54e59f00970457e",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p16-01-dismiss/SPEC.md",
"scope": "new P16-01-DISMISS F8 restart writer marker; native execution pending"
}
},
{
"test": "tests/staff/test_general_dismissal_restart_2_read.gd",
"template": "PASS: <checks> checks",
"required": true,
"fixed_check_count_used": false,
"source_sha256_at_schema_audit": "f8ad3cb30c40538131acca962abd8ee1234b1f8b04c9d0edeb0884f979e98b6f",
"authority": {
"path": "docs/plans/original-completion-2026-10-02/execution/p16-01-dismiss/SPEC.md",
"scope": "new P16-01-DISMISS F8 restart reader marker; native execution pending"
}
}
],
"unknown_roster_suite_policy": "refuse, explicitly review its source/schema before accepting",
@@ -0,0 +1,18 @@
{
"task": "P16-01-DISMISS",
"spec": "docs/plans/original-completion-2026-10-02/execution/p16-01-dismiss/SPEC.md",
"rule": "SPEC F10: every changed expectation of an existing suite is listed with the value before and after and the reason.",
"changes": [
{
"suite": "tests/business/test_required_kitchen.gd",
"check": "whole typed loaded state unchanged except complete identity/saveID-bound IO1: legacy14-load (and the byte-identical-copy legacy migration control, which uses the same mapped expectation)",
"before": "_approved_runtime_migrations maps A06 (mana reserve fresh start), P14-03 (elevators v1 to v2), P05-02 A12 (the five empty fund accounts) and P07-01 A13 (natural weather) into the old checkpoint before the whole typed comparison",
"after": "the economy mapping also adds the empty STAFF_SEVERANCE_EXPENSE account (allow_negative false) when the old save lacks it and the loaded save has it; the fund accounts and the severance account are added in one step and the whole economy must then be byte-identical, so nothing else may differ",
"reason": "A14 / SPEC section 3.6: a save from before the severance gains its empty expense account on load (FinancePolicy._prepare_severance_account, the A12 form). The genuine legacy14 fixture is such a save, so its loaded economy differs from the old checkpoint exactly in this account."
}
],
"not_expectation_changes": [
"tests/economy/test_p05_funds_runtime.gd _test_old_save_migration keeps its expectation: its old save is copied from a current save, so it already holds the severance account and only the five fund accounts are removed.",
"No existing suite expected a save with unpaid rank tax or interest after a close to be refused; the restore fix of IMPLEMENTATION_NOTES.md section 5 changes no listed expectation."
]
}
@@ -0,0 +1,82 @@
# P16-01-DISMISS 実装の記録(実 Opus の実装担当、2026-10-24)
- 仕様: 同じフォルダの `SPEC.md`(登録版 r04。main `0b59d7c` に固定。Runner の `spec_sha256` は `14efffd9…e6b`)。出典は `PROVENANCE.json`。
- 原本への加算は 0。原単位 P16-01・P16-03、`AR.staff.dismissal_severance` の package P05 の原 work・原 AC は閉じない(SPEC §8)。
- 管理の記録は `artifacts/original-completion-2026-10-02/p16-01-dismiss-control-r01/` にある(git の外)。
- 変えた製品のファイル: `autoload/simulation_runtime.gd`・`scripts/economy/finance_policy.gd`・`scripts/ui/staff_management_panel.gd`。`scripts/staff/staff_service.gd` と `scenes/main/main.gd` は変えていない(§1・§4)。
## 1. 命令と判定の順(SPEC §4.1)
- `SimulationRuntime` に通常の command `dismiss_staff`(payload `staff_id`)を足した(`_execute_dismiss_staff`)。
- (a) 運営の終了(`BANKRUPT`)と (b) 同じ key の再送(`DUPLICATE_IDEMPOTENCY_KEY`)は、今の `submit_command` と `CommandQueue.enqueue` のまま受付の前・受付で止まる(R5・R6、5.b)。
- (c) executor は、状態を変える前に次の順で確かめる。どれかに当たれば、その理由で失敗の結果を返す(5.c)。
1. payload に `staff_id` が無い → `INVALID_PAYLOAD`
2. 職員が居ない → `NOT_FOUND`(R4)
3. `UNIQUE` → `UNIQUE`(R1。今の `remove_staff` と同じ code と文)
4. `FREE_HIRE` でない(`ADULT_CONTRACT`)→ `NOT_GENERAL_EMPLOYEE`(R7)
5. 外部が持つ作業(仕入れの物流)→ `EXTERNAL_TASK`(R2。今の `remove_staff` と同じ code と文)
6. 終わっていない襲撃の防衛者 → `DEFENSE_LOCKED`(R9。Root の一時の境界)
7. 救護の組が一緒に昇降機に乗っている → `RESCUE_PAIR_ABOARD`(§3 の決め)
8. 表金と裏金の合計が退職金に足りない → `INSUFFICIENT_FUNDS`(R8)
- (d) 全て通れば、次を1つの操作として行う。
1. 既存の削除の契約 `StaffService.remove_staff(staff_id, &"DISMISSED", true, 分)`。作業の予約を解けない時(R3)は、この契約が何も変えずに拒む(在庫の解放は自分で巻き戻す)。理由は `RESERVATION_RELEASE_FAILED`、文は今の契約のまま。
2. 退職金の記帳(§2)。
3. 救護の後始末(§3)、施設の人員の投影(`_sync_facility_staffing`)、出来事の欠けた職員の扱い(`event_service.audit_actor_availability`)。
- 1 の後で失敗すれば、命令の前の写し(staff・inventory・path・elevators・移動の残り・出来事・ID・帳簿・Gold の写し)に全て戻して、`TRANSACTION_APPLY_FAILED` を返す。試験は、退職金の記帳の key を先に使っておく fixture でこの経路を通し、5.c の比べ方で何も変わらないことを確かめる。
- 画面と試験のための読むだけの関数 `get_staff_dismissal_preview(staff_id)` を足した(解雇できるか・理由・退職金・未払い・開き直す作業・表金・資金の合計)。`get_staff_management_report` の各行に `dismissal` として載る。R3 は前もって分からないので、押した後に命令が拒み、理由を出す。
## 2. 退職金(SPEC §3.5・§3.6、A14)
- `FinancePolicy` に `SEVERANCE_EXPENSE`(口座 `STAFF_SEVERANCE_EXPENSE`)・`SEVERANCE_CATEGORY`(区分 `STAFF_SEVERANCE`)・`SEVERANCE_DAYS`(3)・`severance(日給)` を足した。額は `get_effective_daily_wage() × 3`。
- 記帳は命令の時刻に、1つの取引(idempotency key `staff.severance.<command の ID>`)で行う。
- 表金で足りない分: 裏金 → 表金の補填(区分 `BACK_FUND_TOP_UP`、P05-02 の返済と同じ形)。
- 退職金: 表金 → `STAFF_SEVERANCE_EXPENSE`(区分 `STAFF_SEVERANCE`、source は職員の ID)。
- 日次決算の照合では `cost_of_goods`(画面「運営費(仕入れ・契約金等)」)に入れる。写した表は `_build_operations_daily_financials` と `_operations_expected_category_totals` の2つ。施設ごとの表は、施設でない source を読まないので変えていない。補填は fund report v2 の `back_top_up`(その日の全ての補填)に入る。
- 旧セーブ: `FinancePolicy.prepare_accounts` の最後で、退職金の口座が無ければ空の口座として足す(A12 の5つの口座とは別に判定する。P05-02 の後・P16 の前のセーブにも足す)。口座があって負を許す設定なら拒む。
- 解雇の前に貯まった未払いの給与は、今の契約のまま `_departed_payroll` に保持され、次の 00:00 の締めで払われる(A09 の補填を含む)。解雇の日の日給は今の給与の契約のまま(締めの時に雇用中の職員だけ)。
## 3. 後始末(SPEC §6.2・§6.2.1)
- 勤務・予定・配置は `remove_staff`、経路の actor・昇降機の予約・移動の残り・`STAFF_REMOVED`(reason `DISMISSED`)は今の `_on_staff_removed` が行う。
- 施設の人員の投影は命令の中で `_sync_facility_staffing` をすぐ行う。サービスの途中の職員が抜けて人数が `staff_min` を欠けると、次の施設の更新から今の規則で職員を待ち(5 分)、代わりが来なければ失敗する(A4 の試験)。
- 救護(`_interrupt_dismissed_staff_rescue`): 次の時刻の救護の確認(`_advance_staff_rescue` の最初の段)と同じ規則を、命令の中で、解雇した職員に関わる作業にだけ当てる。
- 患者を解雇した時: その患者を目標にした終わっていない救護の作業を `RESCUE_INTERRUPTED` で取り消し、患者と運搬者の昇降機の予約を取り消す。運搬者が1人で昇降機に乗っている時は、今の規則(乗っている間は保つ)どおり予約を取り消さず、運搬者は本物の階に着く。作業は取り消すので、解雇した患者への参照は残らない。
- 運搬者を解雇した時: `remove_staff` が開き直した搬送(`rescue_transport`)は運搬者が居ないので取り消す(今の規則と同じ)。迎え(`rescue_pickup`)は開き直ったまま、別の医療職を待つ(今の規則でも有効な作業)。患者は次の時刻に今の規則で救護をやり直す。
- **運搬者と患者が一緒に昇降機に乗っている時(決め)**: どちらの解雇も `RESCUE_PAIR_ABOARD` で拒む(5.c)。着くまで保つ今の規則(`ElevatorService.cancel_actor` も乗っている組の取り消しを拒む)と、命令の直後の保存の照合の両方が成り立つのはこの形だけである。決めた範囲(Runtime と `StaffService`)の外の `ElevatorService` を変えずに、乗っている1人だけを消すと、昇降機に持ち主の居ない予約が残る。これは R2 と同じ「着くまで待つ」の一時の拒否で、許可の行の全面の拒否ではない。A6・A7 は他の全ての段階(歩いている・昇降機を待つ・1人で乗っている・組で待つ・降りた後・治療中)で解雇でき、着いた後は同じ職員を解雇できる(試験)。退却や戦闘のような新しい規則は作っていない。
- 出来事: 命令の中で公開の `event_service.audit_actor_availability` を呼び、進行中・選択待ち・中断の出来事の欠けた職員を今の `missing_actor_policy` で扱う(CANCEL と CONTINUE_WITHOUT を試験)。予約した費用は今の規則で1回だけ戻る。この audit は他の欠けた actor も今の規則で扱う(次の時刻と同じ処理が早く行われるだけ)。
- 命令の直後に保存し、同じ process(写しの復元と、ディスクへの保存と読み込み)と別の process(再開の2段)で読み込んでも、欠けた職員への生きた参照(終わっていない作業の患者と運搬者、昇降機の予約と相方、経路の actor、施設の人員、出来事の拘束と actor)が残らず、保存の照合が通る。
- 履歴として残るもの: 終わった作業の記録の `target_entity_id`、サービスの `staff_ids`(始めた時の記録)、給与の締めの行、雇った候補の記録(同じ候補は二度雇えない今の規則のまま)。
## 4. 画面(SPEC §4.3)
- `StaffManagementPanel` に「解雇する…」と確定の欄(「解雇を確定」「やめる」)を足した。判定は Runtime の preview に従う。
- 押す前に、退職金の額(日給の3日分)、裏金からの補填、未払いの給与、開き直す作業を出す。
- 拒否の行ではボタンを無効にし、理由を出す。`UNIQUE` は今の「ユニーク:雇用・維持費無料 / 解雇・売却不可」、それ以外は「解雇不可:…」。無効のボタンを無理に押しても命令を送らない(5.a)。
- 確定の時に名簿を読み直し、読み込みや変化の後の古い確認では送らない。
- 時間が止まっている時はすぐに、進んでいる時は次の時刻に命令が処理され、結果(払った退職金・補填・保持した未払い・開き直した作業、または拒否の理由)を出し、名簿と選択を更新する。解雇の受理を待つ間、その職員の配属の変更とドラッグは止める。
- `Main` の職員の管理の画面は、今の部品と送り手(`_audio_feedback.submit_command`)のままで使えるので、`scenes/main/main.gd` は変えていない。UI は今の部品のままで、完成の見た目ではない。
## 5. 見つけた既存の不具合と直し
- 締めの後に税・利息の未払いが残ったゲームの保存が読めなかった(P05-02 からの不具合)。
- 保存を読む時の Operations の照合(`_parse_operations_runtime_snapshot`)が、締めた未払いを「給与 + 維持費」だけで数え、実行中の照合(`_operations_closed_arrears_total`)が足している税・利息の未払い(`_finance_arrears_through`)を数えていなかった。
- 見つけた経路: 帳簿の日の流れの試験で、退職金の補填で裏金が減り、2日目の締めで世界樹ランク税 70 のうち 30 が未払いになった。その後の保存を復元できなかった(診断の記録は管理のフォルダ)。退職金の無い同じ流れでは税を払いきれるので起きない。
- 直し: 読む時の照合を実行中と同じ式(給与 + 維持費 + 税・利息の未払い)にそろえた。照合を緩めたのではなく、実行中の照合と同じにした。
- 試験: `test_general_dismissal.gd` の帳簿の日の流れが、税の未払い 30 を確かめてから、締めの後の保存を復元する。再開の2段も締めの後の保存を別の process で読む。
## 6. 範囲の外として残すもの(主張しない)
- 予約つきの作業を開き直した後、別の職員がその作業を取る時に、在庫の予約の操作 ID(`staff.task.reserve.<作業>.<品目>`)が前の予約と同じなので `IDEMPOTENCY_CONFLICT` で止まる(BLOCKED)。解雇でも今の `remove_staff` でも同じ結果になる(試験は、解雇の後の作業と在庫が今の `remove_staff` の結果と同じことを確かめる)。開き直しの全般の問題なので、別の単位として提案する。
- まだ始まっていない(待ち行列の)出来事の actor の ID は、拘束を持たず、始まる時に今の規則(`start_next_event` の欠けた actor の扱い)で扱われる。命令の中では変えない。
- 進行中の襲撃の防衛者の解雇(R9)は、原本の要件として未達のまま(SPEC §6.3・§8)。
- 解雇した職員が歩いて出る描写は作らない。UI は今の部品のまま。Windows 実機は後。
- 原単位 P16-01・P16-03 の残り(求人募集、奴隷市場・破産客からの獲得、人身売買、勤務・休息・気絶・救護の横断、解雇の経路の外の退職時の参照の除去)は未達のまま(SPEC §8)。
## 7. 試験
- 新しい suite:
- `tests/staff/test_general_dismissal.gd`(F1〜F9。§6 の全ての行。共通の部品は `tests/helpers/p16_dismissal_fixture.gd`)。最後の場面がゲームの Main を使うので、suite-policy で ordinary の音の後片付けの証明(`single`)を持つ。
- 再開の2段 `tests/staff/test_general_dismissal_restart_1_write.gd`(唯一の生成ファイルは `artifacts/p16-01-dismiss/restart-handoff.json`)と `..._2_read.gd`。
- validation `p16-dismiss`: `tools/run_p16_dismiss_tests.py`。`tools/run_p07_weather_tests.py` から、名前・名簿・再開の段・handoff・protocol 名だけを明示の置き換えで変えた(段の手順・厳密な照合・ordinary と old_bounded の分類は同じ)。実 12 段(新しい suite 1 + 既存 9 + 再開 2)と import・parse。
- suite-policy と marker contract に新しい3本の行を足し、今の source の SHA-256 で固定した。
- 既存の試験の期待値の変更は `EXPECTATION_CHANGES.json`(kitchen の旧セーブの写しに退職金の口座を足す1件)。