Skip to content

feat(hiring): Phase B -- source/interview/offer/onboard pipeline#664

Merged
PipFoweraker merged 12 commits into
mainfrom
feat/hiring-phase-b-pipeline
Jul 17, 2026
Merged

feat(hiring): Phase B -- source/interview/offer/onboard pipeline#664
PipFoweraker merged 12 commits into
mainfrom
feat/hiring-phase-b-pipeline

Conversation

@PipFoweraker

Copy link
Copy Markdown
Owner

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)

Stage Action(s) Attention Duration Mechanic
Source: advertise advertise 3 multi-month campaign money ($8k) -> candidates trickle into the pool over ~3 month boundaries; + light rep/discovery bump
Source: connections use_connections 2 2 ticks a favor (6 rep) -> ONE fast pre-vetted lead (reveal=SKILL); success scales with relative-rep standing; favor spent win or lose
Interview interview_next 2 3 ticks triage -> reveal_more() on the Phase-A card
Offer hire_best (+ targeted hiring_offer) 1 2 ticks hidden self-worth floor; in-range accepts, below-range risks rejection or a resentful accept (loyalty debt)
Onboard onboard_next (+ targeted hiring_onboard_step) per item -- laptop/visa checklist gates productivity; skipping mentoring = lasting debuff + attrition risk

Two sourcing channels differ mechanically

  • Advertise spends money, yields a slow high-volume trickle of unvetted (reveal 0) candidates over months, nudges reputation up (discovery).
  • Connections spends reputation (a favor), yields one fast pre-vetted (reveal 1) lead, gated by a rep-scaled success roll (the gamble).

Negotiation self-worth function (the one genuinely-hidden thing)

floor = E * (1 - tolerance), where E = salary_expectation (revealed at interview L1 per Pip's ruling) and tolerance shrinks 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 mints Ledger.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 mints Ledger.resentment_debt(name) (governance). Both use JSON-round-trip-safe interest rates.

Onboarding checklist vs events

  • Checklist (hard, gates productivity): laptop, + visa for foreign/remote hires (situational). Un-onboarded hires run at 40% productivity until the checklist clears.
  • Soft: mentoring -- optional; skipping it stamps a lasting 0.85x debuff and arms a monthly early-attrition roll (slack-as-insurance, slow and tempting).

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_tick per resolution tick, on_month_boundary per month), consumed by the existing playable month loop.

Verification

  • Fast unit gate: 415 tests, 0 failures (incl. 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).
  • Slow simulation/determinism tier: 93 tests, 0 failures (replay-safe).
  • Headless launch + month-button smoke: green.

Flagged (not guessed)

  • Player UI is the top-level action bar + existing CANDIDATE POOL dialog (candidate cards already show reveal state). Targeted per-candidate interview/offer runs via GameManager delegates (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.
  • Sweep bots NOT extended. The tests/manual sweep driver charges an action's action_points cost 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

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>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

PipFoweraker and others added 5 commits July 16, 2026 21:38
…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>
PipFoweraker and others added 5 commits July 17, 2026 13:05
…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
PipFoweraker merged commit 556f74b into main Jul 17, 2026
@PipFoweraker
PipFoweraker deleted the feat/hiring-phase-b-pipeline branch July 17, 2026 06:03
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant