# P16-01-DISMISS 一般従業員の通常操作での解雇(退職金を含む) 仕様(登録版 r04。P16-01 の補助の作業) - 状態: 登録の前の仕様(実 Opus の commander、2026-10-24)。§3.5 の退職金はユーザーの決定(A14、Root の記録 2026-10-23T18:07:36Z)で決まり、凍結できる。§6.3 R9 は Root が既存の範囲の権限で採った、この補助の作業の一時の境界(2026-10-23、`../p16-01-dismiss-registration-r03/ROOT_DECISION_R9_AND_COMMAND_BOUNDARY_R01.md`)で、人の承認ではない。Root が main に `docs/plans/original-completion-2026-10-02/execution/p16-01-dismiss/SPEC.md` として固定し、その commit(P07-01 の統合の後の main の上)を `base_commit` に通常の登録をする。製品は登録まで変えない。出典は同じフォルダの `PROVENANCE.json`。 - 履歴: r01・r02(誤りを含む)と r03(退職金の決定の前の版)は書き換えずに残す。r03 からの違いは `CHANGES_FROM_R03.md`。 - 原本への加算: 0。原単位 P16-01・P16-03、原子要件 `AR.staff.dismissal_severance` の package P05 の原 work・原 AC は、この作業では閉じない(§8)。 ## 1. 原本の条件(原文。変えない) - `docs/traceability/original/BASELINE.txt`: - 98 行 `* **一般従業員:** ギルド雇用。日給発生。ストレスが溜まりやすく解雇自由。` - 99 行 `* **奴隷:** 市場購入や借金客拘束で獲得。食費のみ。ストレス耐性が高く「人身売買」で売却可能。` - 113 行 `* **処分:** 解雇(退職金)、人身売買(ステータスにより価格変動。他の従業員の忠誠度低下リスクあり)。` - 116 行 `* **特性:** 代替不可能、雇用コスト・維持費無料、成長上限が高い、解雇・売却不可。固有スキルを所持。`(ユニークの節) - 原子要件: `AR.staff.general_dismissal`(P16)「一般従業員を通常操作で解雇できる。」、`AR.staff.dismissal_severance`(P05、D04 待ち)「解雇で退職金を支払う。」、`AR.unique.no_dismissal`(P17)「ユニーク人物を解雇できない。」 - 原単位の文: P16-01 の AC「求人から配置・精算・退職まで通常操作でき、ユニーク禁止操作を維持する。」。P16-03 の work「勤務、休息、給与、気絶、救護、退職時参照除去を横断検証する。」 - ユーザーの言葉(原文): 「まずゲームとしての機能実装完了を優先して」(2026-10-23、P05-02 の `PROVENANCE.json` と同じ出典)。「開発作業を進めて。またがわわは後で変えるので、、統合試験の動作速度のチューニングを、今やるよりも機能実装の完了を優先して」(2026-10-23、Root が共有した原文)。以下の要約は要約と記す。 - 要件 ID: `P16-01-DISMISS`、`AR.staff.general_dismissal`、`AR.staff.dismissal_severance`、`AR.unique.no_dismissal`。 ## 2. 解雇できる職員の区分(今の製品の区分) - 一般従業員 = `StaffRuntime.EmploymentKind.FREE_HIRE` の職員。根拠: ギルドの候補の雇用(`hire_staff_candidate`)は、`scripts/data/mvp_content_catalog.gd` 329 行で `heroine_anri` を `UNIQUE`、それ以外の全ての候補を `FREE_HIRE` にする。`FREE_HIRE` は日給を持つ(`StaffRuntime.get_effective_daily_wage`、`UNIQUE` だけ 0)。原本 98 行の「ギルド雇用。日給発生。」に当たる。 - 解雇の命令の対象にしない区分(この作業の外。他の処分の流れには触れない): - `UNIQUE`: 原本 116 行と `AR.unique.no_dismissal`。今の `StaffService.remove_staff` も拒む。 - `ADULT_CONTRACT`: `scripts/staff/staff_runtime.gd` 31 行に定義があるが、今のゲームにこの区分を作る経路は無く、原本 98 行の一般従業員と同じとは確かめられない。命令は拒む(理由 `NOT_GENERAL_EMPLOYEE`)。これは対象の区分の範囲で、新しいゲームの規則ではない。 - 奴隷(原本 99 行)と人身売買(113 行): 今のゲームに区分も流れも無く、この作業で作らない。 ## 3. 退職金(原本の要件と A14 の決定) - 3.1 原本 113 行「解雇(退職金)」と `AR.staff.dismissal_severance` により、通常の解雇は退職金の支払と切り離せない。 - 3.2 原本に額・式・払う元は無い(D04)。A09 の元の質問の対象(維持費・仕入れ・人件費・返済・利息・税)に退職金は入っていない。 - 3.3 退職金 0 を前提にしない(A14 で額が決まった)。 - 3.4 既存の規則(変えない): 解雇の前に貯まった未払いの給与は、退職金とは別に `_departed_payroll` に保持し、次の 00:00 の締めで払う(A09 の順)。 - 3.5 **A14(ユーザーの決定。回答の時刻は記録に無く、Root の記録は 2026-10-23T18:07:36Z)**: 問い(原文は `PROVENANCE.json`)の4項目に、ユーザーが「この初期仕様で進める」と答えた。 - 退職金は、その職員の日給の3日分。 - 表金から払い、不足分を裏金で補う。合計でも足りなければ解雇しない。 - 未払い給与は退職金と分けて保持し、次の午前0時に精算する。 - 退職金は帳簿で区別し、日次決算では運営費に計上する。 - 3.6 実装の詳細(問いが示した案 `../p16-01-dismiss-registration-r02/SEVERANCE_DECISION_PROPOSAL.md` §3、SHA-256 `02039fa2…3842` のとおり。案の詳細は実装担当の選択で、人の新しい承認として扱わない): - 額 = `StaffRuntime.get_effective_daily_wage` × 3(整数)。日給 0 の職員は 0。 - 支払は解雇の命令の時に行う。表金で足りない分を裏金から表金へ補填し(`BACK_FUND_TOP_UP`、P05-02 と同じ)、補填と退職金を1つの取引で記帳する。 - 表金と裏金の合計が足りなければ R8 で拒み、§5.c の比べ方で状態を変えない(滞納にしない)。 - 帳簿の新しい区分 `STAFF_SEVERANCE`: 表金から、給与(`WAGE`)の照合と混ざらない退職金の費用の口座へ記帳する。日次決算の照合では `cost_of_goods`(画面「運営費(仕入れ・契約金等)」。雇用の契約金 `STAFF_SIGNING_COST` と同じ)に入れる。 - 旧セーブに退職金の費用の口座が無ければ、空の口座として足す(残高・既存の口座・取引の履歴は変えない。P05-02 の A12 の口座の追加と同じ形)。 - 命令ごとに1回だけ記帳し、再送と保存再開で二度払わない。 ## 4. 挙動 - 4.1 命令: `SimulationRuntime` に通常の command `dismiss_staff`(payload `staff_id`)を足す。判定の順: (a) 運営の終了なら受付の前に拒む(今の `submit_command` の規則)。(b) 同じ idempotency key なら受付で拒む(今の `CommandQueue.enqueue` の規則)。(c) executor で、職員が存在するか、区分が `FREE_HIRE` か、§6 の拒否の行に当たらないか、§3.5 の退職金を払えるかを、状態を変える前に全て確かめる。(d) 全て通れば、退職金の支払(§3.5)、`StaffService.remove_staff(staff_id, &"DISMISSED", true, 分)`、§6.2 の後始末を1つの操作として行い、途中で失敗すれば前の状態に戻す。 - 4.2 成功の結果: `staff_id`、払った退職金(§3.5)、保持した未払いの給与、消した勤務の ID、開き直した作業の ID。`STAFF_REMOVED`(reason `DISMISSED`)の出来事。 - 4.3 通常の画面: 職員の管理の画面(`StaffManagementPanel`)で、選んだ職員が §6 の許可の行に当たる時だけ「解雇」の操作を出し、通常の入力(Main の画面の操作)で 4.1 の命令を送る。拒否の行に当たる時は操作を出さないか無効にし、理由(`UNIQUE` は今の「ユニーク:雇用・維持費無料 / 解雇・売却不可」、それ以外は §6 の理由)を出す。退職金の額(§3.5)と、作業を持つ職員なら開き直る作業を、押す前に出す。結果(成功、拒否の理由、払った退職金、保持した未払い)を出し、名簿と選択を更新する。UI は今の部品のままで、完成の見た目は求めない。 ## 5. 拒否の時に変わってよいもの(原子性の比べ方) - 5.a 画面で操作を出さない・無効の時、画面の中の判定で送らない時: 命令を送らないので、Runtime の snapshot(`create_snapshot`)の全体が bytes で同じ。 - 5.b 運営の終了の後(4.1 a)、同じ key の再送(4.1 b): 受付の前・受付で止まり command の ID を使わないので、snapshot の全体が bytes で同じ。同じ key の再送の結果は `DUPLICATE_IDEMPOTENCY_KEY` で、details に最初の結果がつく。 - 5.c executor の拒否(4.1 c): 命令は受け付けて処理されるので、次の4つの区画だけが、決まった形で変わってよい。`systems.runtime.next_command_id`(ちょうど 1 増える)、`systems.runtime.command_queue`(その command の処理済みの失敗の結果・受付の記録、key を付けた時はその key と command の ID の対応だけが増える)、`systems.runtime.id_generator`(出来事の番号が 1 進む)、`systems.runtime.domain_events`(その command の ID の `COMMAND_FAILED` の出来事がちょうど1つ増え、他の出来事は変わらない)。他の全ての区画(`staff`・`economy`・`facilities`・`path`・`elevators`・`guests`・`operations`・`defense`・`events` など)は bytes で同じ。 - 5.d 試験は、5.c の4つの区画から上の決まった増分だけを取り除いた写しの全体を bytes で比べ、増分そのもの(command の ID、失敗の結果の理由、出来事の種類)も確かめる。局所の確認で他の区画が変わると分かったら、それは直す不具合で、比べる範囲を後から広げない。 ## 6. 許可と拒否の表(実際の削除と所有の契約から) - 6.0 使う契約: 解雇は既存の削除の契約 `StaffService.remove_staff`(main `48d3b00` でも 888〜935 行。`staff_service.gd` は P05-02 で変わっていない)に従う。この契約は、`UNIQUE`(897 行)、外部が持つ作業(`is_externally_driven_task`、899 行)、作業の予約を解けない時(905 行)だけを拒み、取り消せる今の作業は予約を解いて開き直す(902〜909 行)。配置の変更の規則 `_staff_assignment_block_reason`(Runtime 6964〜6973 行)は配置の変更のための規則なので、解雇には使わない。作業の有無や状態だけで一律に解雇を止めない。 - 6.1 許可(`FREE_HIRE`、§3.5 の支払ができる時。どの行も §6.2 の後始末を確かめる): - A1 非番(作業なし)。 - A2 通勤中(昇降機を待つ・乗っている時を含む)。`_on_staff_removed` が昇降機の予約を取り消し、経路の actor を消す。 - A3 勤務中で手が空いている(施設に配置)。 - A4 施設でサービスの途中(`ServiceInstance.staff_ids` に入っている)。施設の人員の投影から外れ、人数が `staff_min` を欠けると、今の施設の規則でサービスが職員を待ち(`STAFF_REPLACEMENT_GRACE_MINUTES` 5 分)、代わりが来なければ失敗になる(`scripts/facility/facility_service.gd` 21 行と 875〜891 行)。 - A5 取り消せる作業を持つ(清掃など。予約つきを含む)。`remove_staff` が予約を解き、作業を開き直す(進み具合は作業の方針のまま)。清掃の派遣は生きた職員と作業から毎回導く判定(`scripts/environment/facility_cleaning_bridge.gd` 12〜15 行)で、保存された参照は残らない。 - A6 救護の運搬者(`rescue_pickup`・`rescue_transport`。外部が持つ作業ではない)。作業は開き直り、次の時刻の救護の確認(`_advance_staff_rescue`、Runtime 13475〜13494 行)が、運搬者の居ない運搬を `RESCUE_INTERRUPTED` で取り消し(13492 行。運搬者が居ないので 13490 行の「乗っている間は保つ」は当たらず、患者の昇降機の予約も取り消す、13493 行)、倒れた職員の救護を今の規則でやり直す。次の時刻の確認だけに頼らず、命令の境目でも整合を保つ(§6.2.1)。 - A7 倒れている(INCAPACITATED。救護の対象)。`remove_staff` は状態を見ない。次の時刻の救護の確認が、患者の居ない救護の作業を取り消し、運搬者の昇降機の予約も取り消す。運搬の完了は患者が居なければ何もしない(Runtime 12460〜12467 行)。命令の境目の整合は §6.2.1。救護の確認は、運搬者が昇降機に乗っている時だけ着くまで保つ(13490〜13491 行)。 - A8 出来事の拘束中(`event_service` の `_actor_locks`)。出来事の側の既存の契約 `_handle_missing_actors`(`scripts/events/event_service.gd` 1056 行〜。`missing_actor_policy` の続ける・役で置き換える・失敗の分岐が 1069〜1098 行)が、次の評価で欠けた職員を扱い、拘束を外す。欠けの判定は `_missing_actor_ids`(1226 行)と `_actor_is_available`(1234〜1235 行)。命令の境目の整合は §6.2.1。 - A9 同じ職員を対象にした別の命令が待ち行列にある時。その命令は解雇の後に `NOT_FOUND` などで拒まれ、何も変えない。 - A10 未払いの給与がある時(§3.4 で保持)。 - 6.2 後始末(許可の全ての行。決めた範囲のファイルの中で行う): 勤務・予定・配置(`remove_staff`)、経路の actor と昇降機の予約(`_on_staff_removed`)、施設の人員の投影(解雇の命令の中で `_sync_facility_staffing`(Runtime 9565 行)をすぐ行い、保存の時の施設の人員の照合(1241〜1255 行)と食い違わない)、画面の選択(`StaffManagementPanel`)、`audit_orphan_tasks` で孤立 0、`STAFF_REMOVED`(reason `DISMISSED`)が1つ。関係の系(P18)はユニークだけを参照し(Runtime 7051〜7067 行)、一般従業員の参照は無い。局所の確認で他の参照が残ると分かったら、それを §6.2 の後始末として決めた範囲の中で直し、拒否に変えない。 - 6.2.1 命令の境目の整合(A6・A7・A8。Root 2026-10-23、`ROOT_DECISION_R9_AND_COMMAND_BOUNDARY_R01.md`): 次の時刻の後始末(`_advance_staff_rescue`、出来事の評価)だけに頼らない。解雇の命令の中で(同じ時刻、次の進行の前に)、欠けた職員を患者か運搬者に持つ救護の作業を今の規則(`RESCUE_INTERRUPTED` の取り消しと昇降機の予約の取り消し)で片づけ、出来事は公開の `event_service.audit_actor_availability`(`scripts/events/event_service.gd` 556〜571 行。Runtime の進行も 8909 行で使う)で欠けた職員を今の `missing_actor_policy` で扱う。命令の直後に保存し、同じ process と別の process で読み込んでも、欠けた職員への参照(救護の作業の患者と運搬者、昇降機の予約、出来事の actor と拘束)が残らず、保存の照合が通る。運搬者と患者が一緒に昇降機に乗っている時は、着くまで保つ今の規則(13490〜13491 行)と命令の直後の保存の照合の両方が成り立つ形を、決めた範囲(Runtime と `StaffService`)の中で実装で決めて記録する。退却や戦闘のような新しい規則は作らず、許可の行を全面の拒否に置き換えない。 - 6.3 拒否(業務の状態を変えない。5.b か 5.c の比べ方): - 既存の規則による拒否: - R1 `UNIQUE`(アンリなど)→ 今の `remove_staff` の拒否の文(`Unique Staff cannot be dismissed, sold or permanently removed.`)。5.c。 - R2 外部が持つ作業(仕入れの物流の受け取り・配達・返送。`supply_logistics_bridge.owns_task`)→ 今の `remove_staff` の拒否の文(`Finish the employee external operation before removal.`)。取り消すと物流の要求と荷物が孤立するので、外部の操作の終わりを待つ。5.c。 - R3 作業の予約を解けない → 今の `remove_staff` の拒否の文(`Staff removal could not release Task Reservations.`)。5.c。 - R4 存在しない職員 → `NOT_FOUND`。5.c。 - R5 運営の終了の後 → 今の `submit_command` の拒否(reason `BANKRUPT`)。5.b。 - R6 同じ key の再送 → `DUPLICATE_IDEMPOTENCY_KEY`。5.b。 - 対象の区分の範囲による拒否: R7 `ADULT_CONTRACT` → `NOT_GENERAL_EMPLOYEE`(§2)。5.c。 - §3.5 の決定による拒否: R8 表金と裏金の合計が退職金に足りない時 → `INSUFFICIENT_FUNDS`(A14「合計でも足りなければ解雇しない」)。5.c。 - **Root が採った一時の境界(既存の解雇の契約に無い。根拠つき)**: R9 進行中(終わっていない)の襲撃の防衛者(`_defense_lock_owner(staff_id, &"DEFENDER")` が空でない)→ `DEFENSE_LOCKED`。5.c。 - 根拠: 終わっていない襲撃の防衛側の職員について、Runtime の防衛の照合は、職員が存在し、状態が `UNAVAILABLE` で、作業を持たないことを求める(`simulation_runtime.gd` 10919〜10924 行。準備中は体力・疲労の一致も 10925〜10929 行。終わった襲撃は履歴なので照合しない、10915〜10920 行)。襲撃の結果の当てはめも `staff_service.has_staff` を求める(10194〜10195 行)。そのため、進行中の襲撃の防衛者を消すと、保存の照合と襲撃の結果の当てはめが失敗する。 - 後始末で避けられない理由: 襲撃の名簿から防衛者を外すには `scripts/defense/**`(決めた範囲の外)を変える必要があり、防衛者が途中で抜けた時の戦闘の扱い(退却、戦力の減り方)は原本にも決定にも無い。 - Root が 2026-10-23 に、既存の範囲の権限で、この補助の作業の一時の境界として採った(`ROOT_DECISION_R9_AND_COMMAND_BOUNDARY_R01.md`)。人への追加の問いは要らない。進行中の襲撃の防衛者の解雇は原本の要件として未達のまま開示し(§8)、退却や戦闘の規則を作らず、自由な解雇の完了として扱わない。 ## 7. 受入条件(acceptance_baseline の本文と同じ) - P16-01-DISMISS-F1 通常の命令: §6.1 の許可の行の `FREE_HIRE` の職員を §4.1 の command で解雇でき、名簿から消え、`STAFF_REMOVED`(reason `DISMISSED`)が1つ記録される。 - P16-01-DISMISS-F2 区分の拒否: `UNIQUE` と `ADULT_CONTRACT` は命令でも画面でも解雇できず、§5 の比べ方で業務の状態が変わらない。 - P16-01-DISMISS-F3 退職金: §3.5 で決めた額と払う元で、解雇ごとに1回だけ払われ、日次決算と帳簿の照合が一致する。§3.5 で決めた支払ができない時は解雇されず、§5.c の比べ方で業務の状態が変わらない。 - P16-01-DISMISS-F4 未払いの給与: 解雇の前の未払いが退職金とは別に保持され、次の 00:00 の締めで1回だけ払われ(A09 の補填を含む)、解雇の後に日給が生じない。 - P16-01-DISMISS-F5 再送: 同じ key の再送が `DUPLICATE_IDEMPOTENCY_KEY` で、details に最初の結果がつき、§5.b の比べ方で snapshot の全体が同じ。退職金を二度払わない。 - P16-01-DISMISS-F6 拒否の境界: §6.3 の拒否の全ての行(R1〜R9)で拒み、§5 の該当の比べ方で業務の状態が変わらない。 - P16-01-DISMISS-F7 許可の境界と後始末: §6.1 の許可の全ての行(A1〜A10)で解雇でき、§6.2 の後始末が全て成り立つ。開き直した作業、サービスの待ちと失敗、救護のやり直し、出来事の欠けた職員の扱いは、それぞれ既存の規則どおりになる。A6・A7・A8 は §6.2.1 のとおり、命令の直後に保存して同じ process と別の process で読み込んでも参照が残らず、保存の照合が通る。 - P16-01-DISMISS-F8 保存と再開: 解雇の前後(命令の直後、次の進行の前を含む)・締めの前後の保存を同じ process と別の process で読み、同じ状態で、退職金と未払いの給与の支払が二重にならない。 - P16-01-DISMISS-F9 通常の画面: ゲームの本物の Runtime と Main の職員の管理の画面で、通常の入力で許可の行の一般従業員を解雇でき(退職金の額と開き直る作業を押す前に見られる)、拒否の行では操作が出ないか無効で理由が出る。fixture の直接の呼び出し(`staff_service.remove_staff`)だけで通常の入口の完了を主張しない。 - P16-01-DISMISS-F10 回帰: 関連の回帰(狭い roster と再開の2段)と最終の S の全 roster が PASS。既存の試験の期待値の変更は `EXPECTATION_CHANGES.json` に変更前後と根拠を一覧にする。 ## 8. 原単位との差(開示) - P16-01 の残り: 求人募集の通常の操作(`AR.staff.recruitment`)、奴隷市場・破産客からの獲得(`AR.staff.market_acquisition`・`debt_acquisition`、D04)、雇用の区分ごとの費用の整理。P16-03 の残り: 勤務・休息・気絶・救護の横断(D04)、解雇の経路の外の退職時の参照の除去。人身売買(`AR.staff.sale_*`、D04)。どれも未達のまま。`AR.staff.dismissal_severance` は §3.5 の決定とこの作業の受入で接続するが、package P05 の原単位の完了には数えない。§6.3 R9 により、進行中の襲撃の防衛者の解雇は原本の要件として未達のまま開示する(自由な解雇の完了ではない)。 ## 9. 検証 - validation `p16-dismiss`(`tools/run_p16_dismiss_tests.py --output {build_dir}/p16-dismiss-evidence`。P05-02 の `tools/run_p05_funds_tests.py` と同じ guarded の段の手順を、この作業の中で作る)。 - 狭い roster: 新しい suite `tests/staff/test_general_dismissal.gd`(F1〜F9。§6 の全ての行。退職金の額・補填・不足の拒否・決算の区分を含む)、既存の `tests/ui/test_staff_management.gd`・`tests/ui/test_selection_departure.gd`・`tests/s3/test_runtime_staff_assignment.gd`・`tests/s3/test_staff_service.gd`・`tests/ui/test_anri_recruitment.gd`・`tests/ui/test_approved_free_unique_arrears_ui.gd`・`tests/s6/test_runtime_save_lifecycle.gd`・`tests/s6/test_runtime_snapshot_serialization.gd`・`tests/mvp/test_runtime_operations_integration.gd`。再開の2段 `tests/staff/test_general_dismissal_restart_1_write.gd`(解雇の後・締めの前に保存。唯一の生成ファイルは handoff)と `..._2_read.gd`(別の process で読み、締めまで進めて1回だけの支払を確かめる)。実 12 段。 - その後: 正式の validation → 最終の S の全 roster を1回 → fresh な独立レビュー(この補助の作業の範囲。原単位の whole-unit の受入ではない)。速度の調整の作業と、微小な変更ごとの全回帰はしない。 ## 10. 開示 - UI は今の部品のまま(完成の見た目ではない)。Windows 実機は後。解雇の日の日給は今の給与の契約(締めの時に雇用中の職員に日給)のまま新しい規則を作らない。解雇した候補の再雇用は今の `hire_staff_candidate` の規則(同じ候補を二度雇えない)を変えない。解雇した職員が歩いて出る描写は作らない(非描写の状態として命令の時点で取り除く)。 - 行番号は main `48d3b00` のもの(P05-02 で `simulation_runtime.gd` の行が動いたので r03 から直した)。登録の時の main で読み直す。