Skip to content

feat(lifecycle): build_world + relational tables [LTV-Pn.4a]#125

Merged
shaypal5 merged 2 commits into
mainfrom
feat/lifecycle-scheme-e2e
Jun 14, 2026
Merged

feat(lifecycle): build_world + relational tables [LTV-Pn.4a]#125
shaypal5 merged 2 commits into
mainfrom
feat/lifecycle-scheme-e2e

Conversation

@shaypal5

Copy link
Copy Markdown
Contributor

Summary

First sub-PR of the split LTV-Pn.4 (the write half follows in Pn.4b–c). LTV-Pn.4 was too large for one PR — split along the two GenerationScheme methods, then public-safety, then the carried orchestrator cleanup. This is Pn.4a: build_world.

build_world (layer: api)

  • Deterministic motif sampling from config.seed (a named RNG substream) — honours the "world structure varies via named motif families" hard constraint, so different seeds yield structurally different worlds. (Verified: all 5 retention families appear across seeds.)
  • Builds the customer population, runs the weekly simulation, and wraps the result in a new LifecycleArtifacts container on the bundle — the scheme-owned payload behind the opaque WorldBundle.artifacts introduced in Pn.2.
  • Consumes the Pn.3 config fields (n_customers, observation_date, early_tenure_weeks, forward_windows_days — engine simulates through max(windows)=730d so every pLTV target is fully covered), making the config authoritative as the Pn.3 doc note promised.
  • narrative is accepted for protocol parity but unused — the lifecycle population builder generates its own firmographics.

Lifecycle relational export (layer: render)

New schemes/lifecycle/render/relational.py to_dataframes: six tables (accounts + customers + subscriptions + subscription_events + health_signals + invoices), mirroring the lead-scoring table-source registry pattern with dtypes from each entity's DTYPE_MAP.

write_bundle / write_metadata remain stubbed (Pn.4b–c).

Tests (10 new; full suite 1848 passed / 51 skipped)

build_world returns a LifecycleArtifacts bundle; consumes config fields incl. full-window coverage; determinism; cross-seed motif variability (≥3 distinct, ⊆ the 5 families); relational table set/shape/typing; FK integrity across LIFECYCLE_CONSTRAINTS; relational determinism; tiny-world empties. The registry stub test is updated (build_world live; write path still raises).

Next: Pn.4b — instructor-mode write_bundle (relational + both-regime snapshots → 8 task dirs + card + manifest + write_metadata).

🤖 Generated with Claude Code

First sub-PR of the split LTV-Pn.4. Implements the lifecycle scheme's
build_world half (the write half is Pn.4b-c).

build_world (layer: api):
- Deterministically samples a retention motif family from config.seed (a
  named RNG substream), honouring the "world structure varies via named
  motif families" hard constraint — different seeds yield structurally
  different worlds (verified: all 5 families appear across seeds).
- Builds the customer population, runs the weekly simulation, and wraps the
  result in a new LifecycleArtifacts container on the WorldBundle (the
  scheme-owned payload behind the opaque WorldBundle.artifacts from Pn.2).
- Consumes the Pn.3 lifecycle config fields: n_customers, observation_date,
  early_tenure_weeks, and forward_windows_days (the engine simulates through
  max(windows) = 730d so every pLTV target is fully covered). This makes the
  config authoritative, as the Pn.3 doc note promised.
- narrative is accepted for protocol parity but unused — the lifecycle
  population builder generates its own firmographics.

Lifecycle relational export (layer: render):
- New schemes/lifecycle/render/relational.py to_dataframes: six tables
  (accounts + customers + subscriptions + subscription_events +
  health_signals + invoices), mirroring the lead-scoring table-source
  registry pattern with dtypes from each entity's DTYPE_MAP.

write_bundle / write_metadata remain stubbed (Pn.4b-c).

Tests (10 new): build_world returns a LifecycleArtifacts bundle; consumes
config fields (n_customers, early_tenure, full-window coverage); determinism;
cross-seed motif variability; relational table set/shape/typing; FK integrity
across LIFECYCLE_CONSTRAINTS; relational determinism; tiny-world empties.
Registry stub test updated (build_world live; write path still raises). Full
suite 1848 passed / 51 skipped; ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 14, 2026 08:30
@shaypal5 shaypal5 added this to the dataset: leadforge-ltv-v1 milestone Jun 14, 2026
@shaypal5 shaypal5 added type: feature New capability layer: render render/ bundle and artifact output layer: api api/ public Python surface dataset: leadforge-ltv-v1 Issue/PR scoped to the b2b_saas_ltv_v1 LTV dataset workstream labels Jun 14, 2026
@github-actions

This comment has been minimized.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the lifecycle scheme’s build_world path and introduces a relational export that flattens lifecycle population + simulation output into typed pandas DataFrames, along with tests and roadmap/plan documentation updates for the Pn.4a split.

Changes:

  • Implement LifecycleScheme.build_world to sample a motif family deterministically from the seed, build a customer population, run the simulation, and return a WorldBundle carrying LifecycleArtifacts.
  • Add lifecycle relational export to_dataframes producing six typed tables (accounts, customers, subscriptions, subscription_events, health_signals, invoices).
  • Add/adjust tests to cover determinism, motif variability across seeds, FK integrity, relational typing, and stubbing of write paths.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/schemes/test_registry.py Updates lifecycle registry test to reflect build_world implementation while keeping write paths stubbed.
tests/schemes/lifecycle/test_build_world.py Adds coverage for lifecycle build_world behavior and relational export correctness/determinism.
leadforge/schemes/lifecycle/render/relational.py New lifecycle relational export to typed DataFrames via a table-source registry.
leadforge/schemes/lifecycle/render/init.py Adds lifecycle render package init/docstring.
leadforge/schemes/lifecycle/artifacts.py Introduces LifecycleArtifacts container carried in WorldBundle.artifacts.
leadforge/schemes/lifecycle/init.py Implements LifecycleScheme.build_world and updates module docs/stub messaging.
docs/ltv/roadmap.md Documents the Pn.4 split and records Pn.4a progress.
.agent-plan.md Updates agent plan to reflect the Pn.4a–d split sequencing.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 47 to 52
def build_world(
self,
config: GenerationConfig,
narrative: NarrativeSpec,
**options: Any,
) -> WorldBundle:
Self-review of build_world: it silently dropped several config inputs. The
deferrals are legitimate, but silence is the footgun — surface them at the
call site and pin them to concrete PRs.

1. Difficulty (the real one). build_world does not consume config.difficulty /
   difficulty_params — so every difficulty tier currently yields the SAME
   lifecycle world, and when Pn.4b builds snapshots it would pass
   difficulty_params=None (no distortions ever fire). Documented in the
   build_world docstring; roadmap Pn.4b now explicitly owns resolving
   difficulty_params and threading it into build_customer_snapshot;
   simulation-level difficulty scaling is recorded as a flagged deferral near
   Po/Pp. New test_difficulty_not_yet_differentiating pins the gap as a tested
   fact (intro == advanced today) — it must be flipped when Pn.4b closes it.

2. Narrative ignored. The lifecycle population hardcodes firmographics and
   build_world ignores `narrative`, so the recipe's narrative.yaml will not
   drive them. Documented; roadmap Po now carries the decide-narrative-
   consumption task.

3. to_dataframes near-duplicates the lead-scoring conversion loop. Noted the
   deliberate parallel + earmarked unification behind a shared render helper
   for Pn.4d (the orchestrator PR, where the byte-identity surface is tested).

Docs/tests only — build_world behavior unchanged. Full suite 1849 passed /
51 skipped; ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

pr-agent-context report:

This run includes an unresolved review comment on PR #125 in repository https://github.com/leadforge-dev/leadforge

For each unresolved review comment, recommend one of: resolve as irrelevant, accept and implement
the recommended solution, open a separate issue and resolve as out-of-scope for this PR, accept and
implement a different solution, or resolve as already treated by the code.

After I reply with my decision per item, implement the accepted actions, resolve the corresponding
PR comments, and push all of these changes in a single commit.

# Copilot Comments

## COPILOT-1
Location: leadforge/schemes/lifecycle/__init__.py:52
URL: https://github.com/leadforge-dev/leadforge/pull/125#discussion_r3409257575
Status: outdated
Root author: copilot-pull-request-reviewer

Comment:
    `build_world` is documented and exercised as accepting `narrative=None`, and `WorldSpec.narrative` is `NarrativeSpec | None`, but the method signature currently requires a non-optional `NarrativeSpec`. Making the parameter optional improves API clarity and avoids downstream type friction when lifecycle is invoked without a narrative (as intended here).

Run metadata:

Tool ref: v4
Tool version: 4.0.21
Trigger: commit pushed
Workflow run: 27494284677 attempt 1
Comment timestamp: 2026-06-14T09:13:39.165401+00:00
PR head commit: 56d302b2f964b212ef92e3a39896f596ee1f0dc9

@shaypal5 shaypal5 merged commit fc75146 into main Jun 14, 2026
10 checks passed
@shaypal5 shaypal5 deleted the feat/lifecycle-scheme-e2e branch June 14, 2026 09:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dataset: leadforge-ltv-v1 Issue/PR scoped to the b2b_saas_ltv_v1 LTV dataset workstream layer: api api/ public Python surface layer: render render/ bundle and artifact output type: feature New capability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants