feat(hiring): Phase B -- source/interview/offer/onboard pipeline#664
Merged
Conversation
The fishing-line: every stage is an Attention-gated, duration-bearing action (ADR-0009, nothing instant), built on the Phase-A hidden-ability layer. - Source (two channels): advertise (money + Attention -> multi-month candidate trickle + a light rep/discovery bump) vs use_connections (a reputation favor for one fast pre-vetted lead; success scales with relative-rep standing). - Interview: Attention-gated triage that calls reveal_more() after a delay (blind hires legal, just leave more hidden). - Offer + negotiate (no minigame): a hidden self-worth FLOOR the cash must clear; appetites are the negotiation currency -- a first-authorship/mentorship promise buys the floor down and mints a ledger obligation (ADR-0003); a lowball risks rejection or a resentful accept (loyalty-debt ledger entry). A recruiter on staff narrows the visible band (personified SA). - Onboard: a laptop/visa checklist gates productivity; skimping mentoring leaves a lasting debuff + an early-attrition roll (slack-as-insurance). Deterministic + replay-safe (RNG drawn only when a stage/campaign/job is live, so pre-existing streams are byte-unchanged); pipeline state round-trips through save/load. Wired as top-level actions (advertise/use_connections/interview_next/ hire_best/onboard_next) + GameManager delegates; ticked from MonthController. Fast gate 415 green; simulation/determinism tier 93 green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…rve (#664) end_month() set the implicit reserve to (attention_total - attention_spent) BEFORE execute_turn(), which drove MonthPlan.available() to 0. The five Phase-B pipeline actions self-charge Attention at EXECUTION via month_plan.spend_attention (which draws from available), so every stage failed "not enough Attention" despite a full 20 budget -- the pipeline was dead through the real End-Turn path. Reorder so the plan turn executes first, then the reserve banks whatever Attention this month's actions left. This is also the correct semantics: the reserve guards response windows during the day-tick playback that runs AFTER execution. The existing integration test called turn_manager.execute_turn() directly, bypassing end_month's reserve block, so it never caught this. Add a regression test that drives gm.end_month() and asserts advertise executes (campaign created, Attention charged once, reserve = post-execution remainder); it fails on the old ordering. Co-Authored-By: Claude <noreply@anthropic.com>
… starters, ad-response feed Three coupled Phase-B hiring changes (Pip ruling), all deterministic/replay-safe (ADR-0006): 1. Remove the free per-turn candidate-pool refill (turn_manager.gd). It was a pre-sourcing placeholder that kept the 6-slot pool full for free every turn, silently discarding paid advertised/connections candidates and undermining the pipeline. Candidates now come ONLY from the turn-0 founding-team seed + sourcing. Legacy instant-hire from the pool is intact. Drops the per-turn candidate_spawn/trait RNG draws from the turn stream. 2. Turn-0 starter pool is now EXACTLY 4 founders, each GUARANTEED a hidden rider -- a rare quirk OR a strong appetite (>= STARTER_STRONG_APPETITE), starting hidden (quirk_known false, reveal_level 0). Deterministic from the seeded rng (game_state.gd). Pip framing: "starter pokemon" -- guaranteed latent depth for the late-game deep-trust core. 3. Ad-campaign responses surface a player-facing FEED notification (month_controller.gd, EventTiers TIER_FEED -- the existing readable/no-decision channel) when a campaign trickles a candidate in at a month boundary; previously silent. Batched + pluralized per month. Tests: added starter-count/rider/determinism tests, no-free-refill tests, and ad-response feed (single + batched) tests. Updated test_hire_capability_researcher_execution to seed its own candidate (the deterministic 4-starter lane mix is seed-dependent; same #561 pattern as the interpretability/alignment sibling tests). Fast gate 423 pass, simulation tier 93 pass. Co-Authored-By: Claude <noreply@anthropic.com>
…rait system Replace the shallow, hardcoded positive/negative "trait" placeholders with a fresh, thematically-grounded QUIRK catalogue for the hidden-info hiring layer. - 14 quirks in godot/data/researchers/quirks.json (doomer/e-acc tension, compute-hunger, prestige/first-authorship, mentorship, burnout, secrecy/leaks, cats-as-doom-barometer), loaded via new QuirkCatalogue (sorted-index, seed- deterministic pick). Design doc: docs/game-design/RESEARCHER_QUIRKS.md. - Each quirk is hidden-but-TRUE with one small legible effect on a fixed channel set (self_productivity / burnout / doom / leak / team / skill-growth / loyalty) read through Researcher.quirk_effect(). Effect is live from creation; play reveals it (tenure fallback, leak incident, or exposure event). - Retire POSITIVE_TRAITS/NEGATIVE_TRAITS, the `traits` array, add_trait/ has_trait/get_trait_description, dead _assign_random_traits, and the media_savvy hooks. Kept-by-reframing: workaholic->runs_hot, team_player->lab_parent, leak_prone->loose_lips, fast_learner->sponge, safety_conscious->true_believer. - Repointed the trait effect hooks (researcher/turn_manager/actions) at the quirk channels; decoupled the placeholder staff-perks/employee UI from `traits`. - The 4 guaranteed-rider starters draw their quirk from the catalogue. Determinism (ADR-0006): all assignment from the seeded RNG; generate_random reuses the same two hidden-RNG draws so the stream is byte-unchanged downstream. quirk/ quirk_known serialize and round-trip. Fast gate 436/0 and simulation tier 93/0. Co-Authored-By: Claude <noreply@anthropic.com>
Replace the legacy instant-hire CANDIDATE POOL submenu with a pure-view pipeline panel over the existing GameManager.hiring_* delegates: - Candidate pool cards render Researcher.get_card_data() -- revealed fields show true values, hidden fields show the "??? (interview to reveal)" placeholder, with the reveal ladder level. - Per-candidate Interview (raises reveal on THAT candidate) and Make Offer (offer dialog shows the recruiter negotiation read/band, a cash field, and appetite-promise toggles that re-read the band live). - Two sourcing channels (Advertise / Use Connections) as buttons. - Onboarding section for hired-but-not-onboarded researchers: checklist (laptop/visa/mentoring), per-step actions, skip-mentoring, and the x0.4 un-onboarded / x0.85 skimped productivity debuff made legible. Pure VIEW: no game logic reimplemented -- every action routes through the existing delegates; the panel reads live Researcher objects so save/load and the action-bar flow are untouched. Removes the now-dead legacy _on_candidate_hired / _on_hiring_option_selected helpers. Co-Authored-By: Claude <noreply@anthropic.com>
Ship-blocker: after pregame setup the player reached MainUI but the first turn never became actionable -- the INIT button did nothing and every action button stayed greyed out. Root cause: the Phase-B hiring pipeline commit (4e34d8f) added var att := st.get_available_ap() in _show_hiring_submenu(). `st` is `game_manager.state`, declared with `=` (untyped -> Variant), so `:=` cannot infer a static type from the Variant method call. That is a hard PARSE error -- the ENTIRE main_ui.gd script fails to load, so the MainUI node never instantiates, _ready() never runs, auto-init never fires, and no actions_available/event signals are emitted. The 436 core unit tests passed because they exercise GameManager logic, which never loads main_ui.gd. Fix: give `att` an explicit type (`var att: int = ...`) so no inference is needed off the Variant base. Minimal, surgical -- no main_ui refactor. Also replace the non-ASCII triangle glyph in the "INITIALIZE LAB" button (pregame_setup.tscn + config_confirmation.tscn) with a clean ASCII ">>" that fits the retro terminal theme. Regression test: tests/unit/test_game_start_actionable.gd boots the REAL main.tscn (TabManager -> MainUI auto-init) and asserts a fresh game reaches ACTION_SELECTION with a non-empty action list (or an initial event dialog). It reproduced the failure (MainUI null / parse error) before the fix and passes after. Not present on origin/main -- the offending line is only on this branch. Co-Authored-By: Claude <noreply@anthropic.com>
PipFoweraker
added a commit
that referenced
this pull request
Jul 17, 2026
…og, playtest data) (#684) Co-authored-by: Claude <noreply@anthropic.com>
…omb (#685) Appetite-promises (first_authorship / compute_budget / mission_charter / mentorship) minted a REPUTATION-currency ledger entry (~2000 principal) while reputation starts at 50 and death fires at reputation <= 0 -- so a single promise's bill zeroed the whole reputation bar and instantly killed the player (a real playtest run died on turn 14 this way). The headline negotiation mechanic was a mis-scaled instant-death trap. Option B fix (blessed): re-home each promise's cost onto the thing it actually promises, as a survivable future obligation in its OWN domain (still a Ledger entry per ADR-0003): - first_authorship -> a paper / first-author slot OWED (currency "papers", 1) - compute_budget -> compute committed to the hire (currency "compute", 20) - mission_charter -> a standing governance/mission constraint (governance, 6) - mentorship -> a mentee commitment (governance, 4) Interest 0.0 (obligations, not compounding mortality levers); integer principals so save/load round-trips exactly (determinism, ADR-0006). The reputation-currency promise principal is removed entirely; the discount-to-self_worth_floor negotiation currency (ADR-0011) is unchanged -- only the COST side moved. Legibility: the offer flow now shows each ticked promise's future obligation BEFORE the player commits (data-driven cost line via Ledger.appetite_promise_cost_text). Adds ledger _bill arms for "papers"/"compute" (survivable, no doom/rep teeth). Magnitudes are data-driven in defaults.json (ledger.promise.*). Tests (test_promise_currency.gd): no promise mints reputation; a promise bill never zeroes reputation; a promise-heavy hire cannot lose the game within 6 turns (Pip guardrail) incl. a worst-case all-promises-bill-at-once stress; the domain obligations serialize round-trip; each promise exposes a cost line. Fast gate: 444 tests, 0 fail. Simulation/determinism: 93 tests, 0 fail. Co-authored-by: Claude <noreply@anthropic.com>
…dy turn/date, in-flight hiring progress VIEW-only (ADR-0006): no sim / RNG / turn-loop touched. 1. Manual PLAN<->WATCH toggle: a switch button in the mode banner (shows the target view) + a V keybind, so the player can flip screens at will to look things over. Reuses the existing scaffold's set_mode; the game's own COMMIT / month-review transitions still drive the mode as before. 2. Tidy turn/time: one element "Turn 14 - Fri 21 Jul 2017" (turn = the plan period, counted; calendar date = the human when). Folds the old month-badge + separate "Turn N" duplicate into TurnLabel and hides the redundant TurnCountLabel. 3. In-flight hiring tracker: a lightweight list under the committed-month queue in the shared instrument column, showing each cooking interview/offer/networking job and each onboarding checklist with a progress bar + "done/total" (visible in both PLAN and WATCH since jobs cook during day-tick playback). Fast gate: 437 tests, 0 failures, 45/45 files. test_screen_mode 6/6. Co-Authored-By: Claude <noreply@anthropic.com>
The defeat subtitle hardcoded "The AI Destroyed Humanity" even when the run died of reputation collapse (doom was only 50 in the turn-229 playtest). Derive the subtitle from the real cause -- rep collapse -> "You Lost All Credibility", doom-max -> the AI title, negative money -> bankruptcy -- mirroring GameState.check_win_lose() order so the headline never lies. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Quit-to-menu (rage-quit friction): during a run, a window-close (X / Alt+F4) now returns to the Main Menu instead of quitting to desktop. main_ui takes over NOTIFICATION_WM_CLOSE_REQUEST and restores default close handling in _exit_tree so the menu screens still close the app. Deliberate quit-to-desktop stays on the pause menu and main menu. Event-feed filter (cheap alpha): the arxiv/technical_research_breakthrough deck (200+ entries) floods the feed. EventService now demotes that stream to a low-severity "flavour" channel on the FEED tier (never demands a decision), the channel rides through to the feed line, and the WATCH screen gains a "Hide arxiv flood" toggle (default on) that collapses the spam while keeping real events legible. No event-system rearchitecture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
feat(ship): P0 batch -- quit-to-menu, honest defeat title, event-feed filter (+#686)
PipFoweraker
added a commit
that referenced
this pull request
Jul 21, 2026
…port-decode note (#766, closes #762) Icon regression: 8 top-level actions the main panel renders had no entry in data/icon_mapping.json, so IconLoader.get_action_icon() fell back to the magenta-checkerboard placeholder. The ids (operations, travel, financing and the hiring pipeline advertise/use_connections/interview_next/hire_best/ onboard_next) entered core.json in #627 (2026-07-13) and #664 (2026-07-17) without matching icon mappings; the new office backdrop (#760) made the placeholders newly conspicuous in playtest. Root fix: lookup is already id-keyed (correct); the gap was pure data. Added the 8 missing action-id mappings, reusing existing repo icons (interim -- the art-batch lane may promote dedicated icons later; entries appended, minimal). Added tests/unit/test_action_icon_resolution.gd: asserts every unlocked action id resolves to a real (non-placeholder) icon path -- this would have caught it. Log readability: the WATCH feed had no background stylebox, so its text floated over the office backdrop. TerminalTheme.style_feed now paints an off-black panel (#170A1C @ 0.88 alpha, matching the turn-counter box / backdrop scrim). Fast gate: 539 tests, 0 failures, 60/60 files. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Hiring Pipeline -- Phase B (the fishing-line)
Builds ON Phase A (the hidden-ability candidate model). Implements the four-stage
pipeline as Attention-gated, duration-bearing actions (ADR-0009 -- nothing
instant): cast effort out now, the reward lands later. Deterministic (WS-0/ADR-0006),
save/load round-trips, game stays fully playable.
DO NOT MERGE -- Pip PLAYTESTS this first (this is where the feel gets judged).
Stages (each spends Attention via
month_plan; money/rep through normal resources)advertiseuse_connectionsinterview_nextreveal_more()on the Phase-A cardhire_best(+ targetedhiring_offer)onboard_next(+ targetedhiring_onboard_step)Two sourcing channels differ mechanically
Negotiation self-worth function (the one genuinely-hidden thing)
floor = E * (1 - tolerance), whereE= salary_expectation (revealed at interview L1 per Pip's ruling) andtoleranceshrinks as the money appetite grows. Promises feed a matching appetite and buy the floor down:discount = 0.20 * appetite[k] * E. Offer >= floor accepts; below rolls reject-vs-resentment by how far under.Appetite promise -> ledger entry (ADR-0003)
A
first_authorship(etc.) promise attached to an accepted offer mintsLedger.appetite_promise(name, promise)-- a reputation obligation that bills later unless honoured (the discount you took up front is a debt). A resentful lowball-accept mintsLedger.resentment_debt(name)(governance). Both use JSON-round-trip-safe interest rates.Onboarding checklist vs events
Determinism / integration
RNG is drawn only when a stage is taken or a campaign/job/at-risk-hire is live -- so a run with no pipeline activity leaves the stream byte-unchanged (pre-existing replays unaffected). Ticked from
MonthController(on_tickper resolution tick,on_month_boundaryper month), consumed by the existing playable month loop.Verification
test_hiring_pipeline.gd-- sourcing/interview/offer-in-range-vs-below/promise-mints-ledger/onboard-gates-productivity/determinism/save-load, plus a real-month-loop integration test).Flagged (not guessed)
GameManagerdelegates (hiring_interview/hiring_offer/hiring_onboard_step) which are wired but not yet surfaced in a dedicated panel -- the menu drivers auto-pick (least-revealed / highest-skill). A dedicated recruit panel is the recommended next iteration.tests/manualsweep driver charges an action'saction_pointscost as its Attention cost (default 1); the pipeline actions self-charge Attention at execute time, so a naive sweep policy would double-count. Covered instead by the CI real-month-loop integration test. Reconciling the attention-cost model (execute-time self-charge vs cost-declaration) is a follow-up.🤖 Generated with Claude Code