import%20marimo%0A%0A__generated_with%20%3D%20%220.17.6%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Validation%20and%20execution%20planning%0A%0A%20%20%20%20Loman%20can%20inspect%20a%20computation%20before%20running%20it%3A%0A%0A%20%20%20%20-%20%60validate()%60%20checks%20the%20**whole%20graph**%20for%20cycles%2C%20missing%20inputs%2C%20placeholders%2C%0A%20%20%20%20%20%20failed%20nodes%2C%20and%20unknown%20executors.%0A%20%20%20%20-%20%60plan(target)%60%20examines%20only%20the%20dependencies%20needed%20for%20a%20**specific%20result**.%0A%20%20%20%20%20%20It%20shows%20what%20would%20run%2C%20in%20dependency%20order%2C%20without%20executing%20or%20changing%20anything.%0A%20%20%20%20-%20%60plan()%60%20examines%20the%20work%20remaining%20across%20the%20**whole%20graph**.%0A%0A%20%20%20%20This%20notebook%20develops%20those%20ideas%20through%20a%20series%20of%20small%20use%20cases.%20It%20has%20no%0A%20%20%20%20controls%3A%20run%20it%20from%20top%20to%20bottom%2C%20then%20edit%20values%20or%20graph%20definitions%20to%20explore.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20from%20loman%20import%20Computation%0A%0A%20%20%20%20def%20names(nodes)%3A%0A%20%20%20%20%20%20%20%20return%20%5Bstr(node)%20for%20node%20in%20nodes%5D%0A%0A%20%20%20%20def%20validation_summary(report)%3A%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22is_valid%22%3A%20report.is_valid%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22is_ready%22%3A%20report.is_ready%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22cycles%22%3A%20%5Bnames(component)%20for%20component%20in%20report.cycles%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22placeholders%22%3A%20names(report.placeholders)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22uninitialized_inputs%22%3A%20names(report.uninitialized_inputs)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22error_nodes%22%3A%20names(report.error_nodes)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22missing_executors%22%3A%20%5B(str(node)%2C%20executor)%20for%20node%2C%20executor%20in%20report.missing_executors%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20def%20plan_summary(plan)%3A%0A%20%20%20%20%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22is_feasible%22%3A%20plan.is_feasible%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22targets%22%3A%20None%20if%20plan.targets%20is%20None%20else%20names(plan.targets)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22execution_order%22%3A%20names(plan.execution_order)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22current_nodes%22%3A%20names(plan.current_nodes)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22blocked_nodes%22%3A%20names(plan.blocked_nodes)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22executor_assignments%22%3A%20%5B(str(node)%2C%20executor)%20for%20node%2C%20executor%20in%20plan.executor_assignments%5D%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20return%20Computation%2C%20mo%2C%20plan_summary%2C%20validation_summary%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Preview%20work%20before%20execution%0A%0A%20%20%20%20Consider%20an%20order%20total%20with%20three%20supplied%20inputs%20and%20three%20calculations.%20The%0A%20%20%20%20%60discount%60%20and%20%60total%60%20nodes%20share%20a%20named%20executor%3B%20here%20it%20points%20to%20the%20computation's%0A%20%20%20%20normal%20executor%2C%20but%20in%20a%20real%20application%20it%20could%20identify%20a%20process%20or%20thread%20pool.%0A%0A%20%20%20%20Calling%20%60plan(%22total%22)%60%20should%20identify%20the%20supplied%20inputs%20as%20current%20and%20list%20the%0A%20%20%20%20calculations%20in%20the%20exact%20dependency%20order%20required%20to%20produce%20%60total%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20order%20%3D%20Computation()%0A%20%20%20%20order.executor_map%5B%22pricing%22%5D%20%3D%20order.default_executor%0A%20%20%20%20order.add_node(%22quantity%22%2C%20value%3D3)%0A%20%20%20%20order.add_node(%22unit_price%22%2C%20value%3D12.5)%0A%20%20%20%20order.add_node(%22discount_rate%22%2C%20value%3D0.1)%0A%20%20%20%20order.add_node(%22subtotal%22%2C%20lambda%20quantity%2C%20unit_price%3A%20quantity%20*%20unit_price)%0A%20%20%20%20order.add_node(%0A%20%20%20%20%20%20%20%20%22discount%22%2C%0A%20%20%20%20%20%20%20%20lambda%20subtotal%2C%20discount_rate%3A%20subtotal%20*%20discount_rate%2C%0A%20%20%20%20%20%20%20%20executor%3D%22pricing%22%2C%0A%20%20%20%20)%0A%20%20%20%20order.add_node(%22total%22%2C%20lambda%20subtotal%2C%20discount%3A%20subtotal%20-%20discount%2C%20executor%3D%22pricing%22)%0A%20%20%20%20order%0A%20%20%20%20return%20(order%2C)%0A%0A%0A%40app.cell%0Adef%20_(order%2C%20plan_summary%2C%20validation_summary)%3A%0A%20%20%20%20order_validation_before%20%3D%20order.validate()%0A%20%20%20%20order_plan_before%20%3D%20order.plan(%22total%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22validation%22%3A%20validation_summary(order_validation_before)%2C%0A%20%20%20%20%20%20%20%20%22plan%22%3A%20plan_summary(order_plan_before)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20(order_plan_before%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20graph%20is%20both%20structurally%20valid%20and%20ready.%20Planning%20did%20not%20calculate%20anything%3A%0A%20%20%20%20the%20three%20calculation%20nodes%20are%20still%20merely%20pending.%20Now%20execute%20the%20target%20and%20plan%0A%20%20%20%20it%20again.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(order%2C%20order_plan_before)%3A%0A%20%20%20%20%23%20Depending%20on%20the%20first%20plan%20makes%20the%20before%2Fafter%20sequence%20explicit%20to%20Marimo.%0A%20%20%20%20_%20%3D%20order_plan_before%0A%20%20%20%20order.compute(%22total%22)%0A%20%20%20%20order%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(order)%3A%0A%20%20%20%20order.to_df()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(order%2C%20plan_summary)%3A%0A%20%20%20%20order_plan_after%20%3D%20order.plan(%22total%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22values%22%3A%20order.to_dict()%2C%0A%20%20%20%20%20%20%20%20%22next_plan%22%3A%20plan_summary(order_plan_after)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20(order_plan_after%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20second%20plan%20has%20an%20empty%20%60execution_order%60%3A%20%60total%60%20is%20already%20current.%20This%20makes%0A%20%20%20%20%60plan()%60%20useful%20as%20a%20dry-run%20check%20before%20an%20expensive%20calculation%2C%20and%20as%20confirmation%0A%20%20%20%20that%20a%20requested%20result%20needs%20no%20work.%0A%0A%20%20%20%20%23%23%202.%20Understand%20selective%20recalculation%0A%0A%20%20%20%20Changing%20%60discount_rate%60%20should%20invalidate%20%60discount%60%20and%20%60total%60%2C%20but%20not%20%60subtotal%60.%0A%20%20%20%20The%20next%20plan%20makes%20that%20selective%20recalculation%20visible%20before%20it%20happens.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(order%2C%20order_plan_after)%3A%0A%20%20%20%20_%20%3D%20order_plan_after%0A%20%20%20%20order.insert(%22discount_rate%22%2C%200.2%2C%20force%3DTrue)%0A%20%20%20%20order%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(order%2C%20plan_summary)%3A%0A%20%20%20%20recalculation_plan%20%3D%20order.plan(%22total%22)%0A%20%20%20%20plan_summary(recalculation_plan)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%60subtotal%60%20now%20appears%20in%20%60current_nodes%60%2C%20while%20only%20%60discount%60%20and%20%60total%60%20appear%20in%0A%20%20%20%20%60execution_order%60.%20This%20is%20the%20same%20incremental%20behavior%20that%20%60compute(%22total%22)%60%20will%0A%20%20%20%20follow%2C%20exposed%20without%20doing%20the%20work.%0A%0A%20%20%20%20%23%23%203.%20Diagnose%20a%20missing%20input%0A%0A%20%20%20%20A%20graph%20can%20be%20structurally%20valid%20but%20not%20ready.%20Here%2C%20%60raw_data%60%20is%20a%20legitimate%20input%0A%20%20%20%20node%20whose%20value%20has%20not%20yet%20been%20supplied.%20Validation%20identifies%20that%20root%20cause%3B%0A%20%20%20%20planning%20shows%20every%20requested%20node%20that%20it%20blocks.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20import%20statistics%0A%0A%20%20%20%20pipeline%20%3D%20Computation()%0A%20%20%20%20pipeline.add_node(%22raw_data%22)%0A%20%20%20%20pipeline.add_node(%22clean_data%22%2C%20lambda%20raw_data%3A%20%5Bvalue%20for%20value%20in%20raw_data%20if%20value%20is%20not%20None%5D)%0A%20%20%20%20pipeline.add_node(%22mean%22%2C%20lambda%20clean_data%3A%20statistics.mean(clean_data))%0A%20%20%20%20pipeline%0A%20%20%20%20return%20(pipeline%2C)%0A%0A%0A%40app.cell%0Adef%20_(pipeline%2C%20plan_summary%2C%20validation_summary)%3A%0A%20%20%20%20pipeline_validation_missing%20%3D%20pipeline.validate()%0A%20%20%20%20pipeline_plan_blocked%20%3D%20pipeline.plan(%22mean%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22validation%22%3A%20validation_summary(pipeline_validation_missing)%2C%0A%20%20%20%20%20%20%20%20%22plan%22%3A%20plan_summary(pipeline_plan_blocked)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%20(pipeline_plan_blocked%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Notice%20the%20distinction%3A%0A%0A%20%20%20%20-%20%60is_valid%60%20is%20%60True%60%3A%20the%20graph%20has%20a%20coherent%20structure.%0A%20%20%20%20-%20%60is_ready%60%20is%20%60False%60%3A%20an%20external%20value%20is%20still%20required.%0A%20%20%20%20-%20%60blocked_nodes%60%20includes%20%60raw_data%60%2C%20%60clean_data%60%2C%20and%20%60mean%60%3A%20none%20of%20that%20branch%20can%0A%20%20%20%20%20%20complete%20yet.%0A%0A%20%20%20%20Supplying%20the%20input%20removes%20the%20blocker%20and%20produces%20a%20feasible%20plan.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(pipeline%2C%20pipeline_plan_blocked)%3A%0A%20%20%20%20_%20%3D%20pipeline_plan_blocked%0A%20%20%20%20pipeline.insert(%22raw_data%22%2C%20%5B10%2C%20None%2C%2020%2C%2030%5D)%0A%20%20%20%20pipeline%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(pipeline%2C%20plan_summary)%3A%0A%20%20%20%20pipeline_plan_ready%20%3D%20pipeline.plan(%22mean%22)%0A%20%20%20%20plan_summary(pipeline_plan_ready)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204.%20Plan%20useful%20work%20in%20a%20partially%20broken%20graph%0A%0A%20%20%20%20%60validate()%60%20intentionally%20checks%20the%20entire%20graph.%20A%20production%20computation%20may%20contain%0A%20%20%20%20an%20unfinished%20or%20broken%20branch%20that%20is%20unrelated%20to%20today's%20requested%20output.%20A%20targeted%0A%20%20%20%20plan%20answers%20the%20more%20practical%20question%3A%20**can%20this%20particular%20result%20be%20calculated%3F**%0A%0A%20%20%20%20The%20following%20graph%20has%20a%20healthy%20reporting%20branch%20and%20an%20unrelated%20export%20branch%20with%0A%20%20%20%20a%20placeholder%20dependency.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20mixed%20%3D%20Computation()%0A%20%20%20%20mixed.add_node(%22sales%22%2C%20value%3D%5B100%2C%20125%2C%20150%5D)%0A%20%20%20%20mixed.add_node(%22sales_total%22%2C%20lambda%20sales%3A%20sum(sales))%0A%20%20%20%20mixed.add_node(%22sales_report%22%2C%20lambda%20sales_total%3A%20f%22Sales%3A%20%7Bsales_total%7D%22)%0A%20%20%20%20mixed.add_node(%22export_result%22%2C%20lambda%20database_connection%2C%20sales_report%3A%20(database_connection%2C%20sales_report))%0A%20%20%20%20mixed%0A%20%20%20%20return%20(mixed%2C)%0A%0A%0A%40app.cell%0Adef%20_(mixed%2C%20plan_summary%2C%20validation_summary)%3A%0A%20%20%20%20mixed_validation%20%3D%20mixed.validate()%0A%20%20%20%20report_plan%20%3D%20mixed.plan(%22sales_report%22)%0A%20%20%20%20whole_graph_plan%20%3D%20mixed.plan()%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22whole_graph_validation%22%3A%20validation_summary(mixed_validation)%2C%0A%20%20%20%20%20%20%20%20%22sales_report_plan%22%3A%20plan_summary(report_plan)%2C%0A%20%20%20%20%20%20%20%20%22whole_graph_plan%22%3A%20plan_summary(whole_graph_plan)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20whole%20graph%20is%20invalid%20because%20%60database_connection%60%20is%20a%20placeholder%2C%20and%20the%20whole%0A%20%20%20%20graph%20plan%20marks%20the%20export%20branch%20as%20blocked.%20The%20targeted%20%60sales_report%60%20plan%20remains%0A%20%20%20%20feasible%20because%20that%20placeholder%20is%20not%20one%20of%20its%20dependencies.%0A%0A%20%20%20%20This%20distinction%20is%20useful%20for%20large%20computations%20containing%20optional%20outputs%2C%20staged%0A%20%20%20%20development%2C%20or%20environment-specific%20integrations.%0A%0A%20%20%20%20%23%23%205.%20Detect%20dependency%20cycles%0A%0A%20%20%20%20Cycles%20are%20structural%20errors%3A%20no%20member%20can%20be%20evaluated%20first.%20Validation%20reports%20each%0A%20%20%20%20cyclic%20strongly%20connected%20group%2C%20while%20planning%20marks%20the%20cycle%20and%20its%20required%0A%20%20%20%20descendants%20as%20blocked.%0A%0A%20%20%20%20Computing%20the%20target%20would%20raise%20an%20error.%20Validation%20makes%20the%20cycle%20visible%20before%20any%0A%20%20%20%20execution%20is%20attempted.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20cyclic%20%3D%20Computation()%0A%20%20%20%20cyclic.add_node(%22positions%22%2C%20lambda%20risk%3A%20risk%20%2B%201)%0A%20%20%20%20cyclic.add_node(%22value%22%2C%20lambda%20positions%3A%20positions%20*%202)%0A%20%20%20%20cyclic.add_node(%22risk%22%2C%20lambda%20value%3A%20value%20%2F%2010)%0A%20%20%20%20cyclic.add_node(%22report%22%2C%20lambda%20value%3A%20f%22Value%3A%20%7Bvalue%7D%22)%0A%20%20%20%20cyclic%0A%20%20%20%20return%20(cyclic%2C)%0A%0A%0A%40app.cell%0Adef%20_(cyclic%2C%20plan_summary%2C%20validation_summary)%3A%0A%20%20%20%20cycle_validation%20%3D%20cyclic.validate()%0A%20%20%20%20cycle_plan%20%3D%20cyclic.plan(%22report%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22validation%22%3A%20validation_summary(cycle_validation)%2C%0A%20%20%20%20%20%20%20%20%22plan%22%3A%20plan_summary(cycle_plan)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%206.%20Use%20a%20current%20value%20as%20a%20calculation%20boundary%0A%0A%20%20%20%20Sometimes%20an%20intermediate%20result%20is%20restored%20from%20a%20checkpoint%2C%20supplied%20from%20a%20cache%2C%0A%20%20%20%20or%20deliberately%20pinned%20for%20an%20experiment.%20Its%20original%20inputs%20may%20be%20unavailable%2C%20but%0A%20%20%20%20downstream%20work%20can%20still%20proceed%20from%20the%20current%20value.%0A%0A%20%20%20%20Here%20%60calibrated_model%60%20is%20pinned.%20Planning%20%60forecast%60%20stops%20there%20and%20does%20not%20require%0A%20%20%20%20the%20unavailable%20training%20data%20behind%20it.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20checkpointed%20%3D%20Computation()%0A%20%20%20%20checkpointed.add_node(%22training_data%22)%0A%20%20%20%20checkpointed.add_node(%22calibrated_model%22%2C%20lambda%20training_data%3A%20%7B%22mean%22%3A%20sum(training_data)%20%2F%20len(training_data)%7D)%0A%20%20%20%20checkpointed.insert(%22calibrated_model%22%2C%20%7B%22mean%22%3A%2042.0%7D)%0A%20%20%20%20checkpointed.pin(%22calibrated_model%22)%0A%20%20%20%20checkpointed.add_node(%22scenario%22%2C%20value%3D1.1)%0A%20%20%20%20checkpointed.add_node(%22forecast%22%2C%20lambda%20calibrated_model%2C%20scenario%3A%20calibrated_model%5B%22mean%22%5D%20*%20scenario)%0A%20%20%20%20checkpointed%0A%20%20%20%20return%20(checkpointed%2C)%0A%0A%0A%40app.cell%0Adef%20_(checkpointed%2C%20plan_summary%2C%20validation_summary)%3A%0A%20%20%20%20checkpoint_validation%20%3D%20checkpointed.validate()%0A%20%20%20%20forecast_plan%20%3D%20checkpointed.plan(%22forecast%22)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22whole_graph_validation%22%3A%20validation_summary(checkpoint_validation)%2C%0A%20%20%20%20%20%20%20%20%22forecast_plan%22%3A%20plan_summary(forecast_plan)%2C%0A%20%20%20%20%7D%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Whole-graph%20validation%20still%20tells%20us%20that%20%60training_data%60%20is%20unavailable.%20The%20targeted%0A%20%20%20%20plan%20is%20nevertheless%20feasible%3A%20%60calibrated_model%60%20and%20%60scenario%60%20are%20current%20boundaries%2C%0A%20%20%20%20so%20only%20%60forecast%60%20needs%20to%20run.%0A%0A%20%20%20%20%23%23%207.%20Diagnose%20a%20large%20computation%20with%20nested%20blocks%0A%0A%20%20%20%20Small%20examples%20make%20each%20failure%20obvious.%20The%20real%20value%20of%20planning%20appears%20when%20a%20graph%0A%20%20%20%20is%20large%20enough%20that%20manually%20following%20edges%20is%20tedious.%0A%0A%20%20%20%20The%20portfolio%20pipeline%20below%20uses%20reusable%20blocks%20under%20nested%20paths%3A%0A%0A%20%20%20%20-%20three%20feed%20blocks%20normalize%20positions%2C%20prices%2C%20and%20FX%20data%3B%0A%20%20%20%20-%20a%20valuation%20block%20combines%20those%20feeds%20and%20schedules%20stress%20calculation%20on%20a%20named%0A%20%20%20%20%20%20executor%3B%0A%20%20%20%20-%20a%20reporting%20branch%20consumes%20the%20valuation%20result.%0A%0A%20%20%20%20It%20also%20contains%20four%20realistic%20configuration%20mistakes%3A%0A%0A%20%20%20%201.%20The%20prices%20feed%20was%20added%20without%20linking%20its%20%60raw%60%20input.%0A%20%20%20%202.%20The%20FX%20feed%20links%20to%20%60inputs%2Ffx_spots%60%2C%20but%20the%20supplied%20node%20is%20%60inputs%2Ffx_spot%60.%0A%20%20%20%203.%20The%20valuation%20block%20requests%20a%20%60quant_pool%60%20executor%20that%20was%20never%20configured.%0A%20%20%20%204.%20%60report%2Fdraft%60%20and%20%60report%2Fsignoff%60%20depend%20on%20one%20another.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Computation)%3A%0A%20%20%20%20def%20make_feed_block()%3A%0A%20%20%20%20%20%20%20%20block%20%3D%20Computation()%0A%20%20%20%20%20%20%20%20block.add_node(%22raw%22)%0A%20%20%20%20%20%20%20%20block.add_node(%22records%22%2C%20lambda%20raw%3A%20raw)%0A%20%20%20%20%20%20%20%20block.add_node(%22count%22%2C%20lambda%20records%3A%20len(records))%0A%20%20%20%20%20%20%20%20return%20block%0A%0A%20%20%20%20def%20make_valuation_block()%3A%0A%20%20%20%20%20%20%20%20block%20%3D%20Computation()%0A%20%20%20%20%20%20%20%20block.add_node(%22positions%22)%0A%20%20%20%20%20%20%20%20block.add_node(%22prices%22)%0A%20%20%20%20%20%20%20%20block.add_node(%22fx%22)%0A%20%20%20%20%20%20%20%20block.add_node(%22market_value%22%2C%20lambda%20positions%2C%20prices%2C%20fx%3A%20(positions%2C%20prices%2C%20fx))%0A%20%20%20%20%20%20%20%20block.add_node(%22stress_loss%22%2C%20lambda%20market_value%3A%20market_value%2C%20executor%3D%22quant_pool%22)%0A%20%20%20%20%20%20%20%20return%20block%0A%0A%20%20%20%20portfolio%20%3D%20Computation()%0A%20%20%20%20portfolio.add_node(%22inputs%2Fpositions%22%2C%20value%3D%5B%7B%22symbol%22%3A%20%22ABC%22%2C%20%22quantity%22%3A%20100%7D%5D)%0A%20%20%20%20portfolio.add_node(%22inputs%2Fprices%22%2C%20value%3D%7B%22ABC%22%3A%2025.0%7D)%0A%20%20%20%20portfolio.add_node(%22inputs%2Ffx_spot%22%2C%20value%3D1.25)%0A%0A%20%20%20%20portfolio.add_block(%0A%20%20%20%20%20%20%20%20%22feeds%2Fpositions%22%2C%0A%20%20%20%20%20%20%20%20make_feed_block()%2C%0A%20%20%20%20%20%20%20%20keep_values%3DFalse%2C%0A%20%20%20%20%20%20%20%20links%3D%7B%22raw%22%3A%20%22inputs%2Fpositions%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20portfolio.add_block(%22feeds%2Fprices%22%2C%20make_feed_block()%2C%20keep_values%3DFalse)%0A%20%20%20%20portfolio.add_block(%0A%20%20%20%20%20%20%20%20%22feeds%2Ffx%22%2C%0A%20%20%20%20%20%20%20%20make_feed_block()%2C%0A%20%20%20%20%20%20%20%20keep_values%3DFalse%2C%0A%20%20%20%20%20%20%20%20links%3D%7B%22raw%22%3A%20%22inputs%2Ffx_spots%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20portfolio.add_block(%0A%20%20%20%20%20%20%20%20%22risk%2Fequity%22%2C%0A%20%20%20%20%20%20%20%20make_valuation_block()%2C%0A%20%20%20%20%20%20%20%20keep_values%3DFalse%2C%0A%20%20%20%20%20%20%20%20links%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22positions%22%3A%20%22feeds%2Fpositions%2Frecords%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22prices%22%3A%20%22feeds%2Fprices%2Frecords%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22fx%22%3A%20%22feeds%2Ffx%2Frecords%22%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20)%0A%0A%20%20%20%20portfolio.add_node(%0A%20%20%20%20%20%20%20%20%22report%2Fdraft%22%2C%0A%20%20%20%20%20%20%20%20lambda%20stress_loss%2C%20signoff%3A%20(stress_loss%2C%20signoff)%2C%0A%20%20%20%20%20%20%20%20kwds%3D%7B%22stress_loss%22%3A%20%22risk%2Fequity%2Fstress_loss%22%2C%20%22signoff%22%3A%20%22report%2Fsignoff%22%7D%2C%0A%20%20%20%20)%0A%20%20%20%20portfolio.add_node(%22report%2Fsignoff%22%2C%20lambda%20draft%3A%20draft%2C%20kwds%3D%7B%22draft%22%3A%20%22report%2Fdraft%22%7D)%0A%20%20%20%20portfolio.add_node(%22report%2Ffinal%22%2C%20lambda%20draft%3A%20draft%2C%20kwds%3D%7B%22draft%22%3A%20%22report%2Fdraft%22%7D)%0A%20%20%20%20portfolio%0A%20%20%20%20return%20(portfolio%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20complete%20issue%20list%20takes%20one%20line.%20Instead%20of%20searching%20through%20every%20block%2C%20it%0A%20%20%20%20identifies%20the%20precise%20paths%20containing%20the%20root%20problems.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(portfolio)%3A%0A%20%20%20%20portfolio.validate().to_df()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(portfolio)%3A%0A%20%20%20%20%23%20see%20the%20cycle%0A%20%20%20%20portfolio.draw(%22report%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Validation%20answers%20**what%20is%20wrong%20anywhere%20in%20the%20graph**.%20Planning%20the%20desired%20final%0A%20%20%20%20report%20answers%20**how%20those%20problems%20affect%20this%20output**.%20%60blocked_by%60%20carries%20each%20root%0A%20%20%20%20cause%20through%20the%20dependency%20graph%2C%20so%20the%20final%20row%20explains%20all%20reasons%20the%20report%0A%20%20%20%20cannot%20run%20without%20manually%20tracing%20an%20edge.%0A%0A%20%20%20%20The%20table%20keeps%20two%20related%20concepts%20separate%3A%0A%0A%20%20%20%20-%20%60state%60%20is%20Loman's%20existing%20node%20state%20(%60UPTODATE%60%2C%20%60COMPUTABLE%60%2C%20%60STALE%60%2C%20and%20so%20on).%0A%20%20%20%20-%20%60plan_status%60%20is%20contextual%20to%20this%20target%3A%20%60current%60%2C%20%60pending%60%2C%20or%20%60blocked%60.%0A%0A%20%20%20%20A%20node%20can%20therefore%20be%20%60STALE%60%20in%20the%20graph%20while%20being%20%60blocked%60%20in%20this%20plan%20because%0A%20%20%20%20an%20upstream%20input%20or%20executor%20is%20unavailable.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(portfolio)%3A%0A%20%20%20%20portfolio.plan(%22report%2Ffinal%22).to_df()%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20two%20tables%20separate%20diagnosis%20from%20impact%3A%0A%0A%20%20%20%20-%20%60feeds%2Fprices%2Fraw%60%20reveals%20the%20omitted%20block%20link.%0A%20%20%20%20-%20%60inputs%2Ffx_spots%60%20exposes%20the%20typo%20next%20to%20the%20existing%20%60inputs%2Ffx_spot%60%20node.%0A%20%20%20%20-%20%60risk%2Fequity%2Fstress_loss%60%20names%20the%20missing%20executor%20and%20every%20downstream%20node%20it%20blocks.%0A%20%20%20%20-%20the%20cycle%20is%20shown%20once%20as%20a%20connected%20group%20rather%20than%20as%20a%20confusing%20compute-time%0A%20%20%20%20%20%20traceback.%0A%20%20%20%20-%20current%20and%20independently%20runnable%20nodes%20remain%20visible%2C%20making%20clear%20how%20far%20the%20graph%0A%20%20%20%20%20%20can%20proceed%20after%20each%20fix.%0A%0A%20%20%20%20This%20is%20the%20intended%20investigation%20loop%20for%20a%20large%20DAG%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20comp.validate().to_df()%0A%20%20%20%20comp.plan(%22desired%2Foutput%22).to_df()%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%23%23%20Where%20to%20use%20each%20API%0A%0A%20%20%20%20%7C%20Question%20%7C%20API%20%7C%0A%20%20%20%20%7C%20---%20%7C%20---%20%7C%0A%20%20%20%20%7C%20Is%20the%20complete%20graph%20well%20formed%20and%20fully%20supplied%3F%20%7C%20%60comp.validate()%60%20%7C%0A%20%20%20%20%7C%20What%20would%20run%20to%20produce%20one%20result%3F%20%7C%20%60comp.plan(%22result%22)%60%20%7C%0A%20%20%20%20%7C%20Can%20several%20outputs%20be%20produced%20together%3F%20%7C%20%60comp.plan(%5B%22a%22%2C%20%22b%22%5D)%60%20%7C%0A%20%20%20%20%7C%20What%20work%20and%20blockers%20remain%20anywhere%20in%20the%20graph%3F%20%7C%20%60comp.plan()%60%20%7C%0A%20%20%20%20%7C%20Has%20a%20target%20become%20current%20after%20execution%3F%20%7C%20%60comp.plan(%22result%22).execution_order%20%3D%3D%20()%60%20%7C%0A%20%20%20%20%7C%20Which%20root%20issue%20blocks%20each%20downstream%20node%3F%20%7C%20%60comp.plan(%22result%22).to_df()%60%20%7C%0A%0A%20%20%20%20Try%20changing%20supplied%20values%2C%20inserting%20missing%20inputs%2C%20removing%20a%20cyclic%20dependency%2C%20or%0A%20%20%20%20unpinning%20the%20checkpointed%20model.%20Marimo%20will%20rerun%20the%20dependent%20cells%20and%20expose%20how%0A%20%20%20%20each%20graph%20change%20affects%20validation%20and%20planning.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
cb0adb097d76a8863ab5d1f57ad45f703e86acfa148f47800dc92fdec6b18820