alphacouncil-agent 0.9.4 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (481) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/.claude-plugin/plugin.json +2 -2
  3. package/.codex-plugin/plugin.json +4 -4
  4. package/AGENTS.md +25 -11
  5. package/CHANGELOG.md +146 -0
  6. package/CLAUDE.md +24 -7
  7. package/README.ja.md +15 -10
  8. package/README.md +21 -13
  9. package/README.zh-CN.md +17 -12
  10. package/data/authored/core-seats.mjs +808 -0
  11. package/data/authored/growth-seats.mjs +581 -0
  12. package/data/authored/index.mjs +114 -0
  13. package/data/authored/quant-seats.mjs +564 -0
  14. package/data/authored/value-seats.mjs +663 -0
  15. package/data/build-profile.v1.json +2 -1
  16. package/data/council-evaluation-protocol.v1.json +7 -4
  17. package/data/master-selector-method-locales.v1.mjs +10 -0
  18. package/data/persona-v3-ai-assisted-solo-profile.v1.json +2 -2
  19. package/data/persona-v3-build-specs.v1.mjs +60 -3
  20. package/docs/INSTALL.md +6 -10
  21. package/docs/personas.md +2 -1
  22. package/docs/plans/v1.0-roadmap.md +487 -0
  23. package/docs/plans/v1.1-basket-native-seats.md +267 -0
  24. package/docs/releases/v0.9.5.md +96 -0
  25. package/docs/releases/v1.0.0.md +164 -0
  26. package/docs/report-contract.md +30 -6
  27. package/knowledge/ai-assisted-solo/experiments/runs/a.json +13 -13
  28. package/knowledge/ai-assisted-solo/experiments/runs/b.json +2 -2
  29. package/knowledge/ai-assisted-solo/experiments/runs/c.json +2 -2
  30. package/knowledge/ai-assisted-solo/experiments/runs/d13.json +126 -126
  31. package/knowledge/ai-assisted-solo/experiments/runs/d26.json +274 -254
  32. package/knowledge/ai-assisted-solo/experiments/runs/e-d13.json +129 -129
  33. package/knowledge/ai-assisted-solo/experiments/runs/e-d26.json +277 -257
  34. package/knowledge/ai-assisted-solo/experiments/runs/h_ai_reference.json +278 -258
  35. package/knowledge/ai-assisted-solo/experiments/simulation-input.json +7278 -556
  36. package/knowledge/ai-assisted-solo/experiments/simulation-manifest.json +22 -22
  37. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{change_value_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  38. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_ackman/reviews/{power_map.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
  39. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{compute_power_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  40. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_aschenbrenner/reviews/{timeline_reverse_valuation.ai-review.json → price_implied_cash_yield.ai-review.json} +46 -46
  41. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{crowding_cost_stress.ai-review.json → nominal_earnings_yield_gap.ai-review.json} +49 -49
  42. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_asness/reviews/{factor_decomposer.ai-review.json → real_earnings_yield_gap.ai-review.json} +49 -49
  43. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/expected_return_over_long_bond.ai-review.json +277 -0
  44. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_bogle/reviews/fundamental_expected_return.ai-review.json +277 -0
  45. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{incremental_returns.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  46. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_buffett/reviews/{owner_earnings_rebuilder.ai-review.json → owner_earnings_yield.ai-review.json} +46 -46
  47. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{mispricing_carry.ai-review.json → net_current_asset_surplus_per_share.ai-review.json} +49 -49
  48. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_burry/reviews/{capital_structure_reader.ai-review.json → net_current_asset_value_per_share.ai-review.json} +46 -46
  49. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{cost_adoption_curve.ai-review.json → current_cash_yield.ai-review.json} +46 -46
  50. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_cathie_wood/reviews/{capture_valuation_bridge.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  51. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_dalio/reviews/{regime_classifier.ai-review.json → policy_real_rate.ai-review.json} +49 -49
  52. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/{master_munger/reviews/incentive_map.ai-review.json → master_dalio/reviews/real_curve_slope.ai-review.json} +51 -51
  53. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{reverse_valuation.ai-review.json → implied_equity_risk_premium.ai-review.json} +49 -49
  54. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_damodaran/reviews/{story_dcf.ai-review.json → premium_versus_long_run_average.ai-review.json} +49 -49
  55. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{liquidity_revision_map.ai-review.json → liquidity_curve_impulse.ai-review.json} +49 -49
  56. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_druckenmiller/reviews/{inflection_payoff.ai-review.json → policy_real_rate.ai-review.json} +49 -49
  57. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{user_value_evidence.ai-review.json → five_year_opportunity_cost.ai-review.json} +46 -46
  58. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_duan_yongping/reviews/{opportunity_cost.ai-review.json → margin_retention.ai-review.json} +46 -46
  59. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{research_productivity.ai-review.json → margin_retention.ai-review.json} +46 -46
  60. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_fisher/reviews/{scuttlebutt_graph.ai-review.json → reinvestment_spread.ai-review.json} +49 -49
  61. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/{master_dalio/reviews/debt_driver_stress.ai-review.json → master_forensic_short/reviews/cash_conversion_gap.ai-review.json} +51 -51
  62. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/{accounting_rebuilder.ai-review.json → cash_cover_of_leverage.ai-review.json} +46 -46
  63. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{asset_floor.ai-review.json → defensive_hurdle_base.ai-review.json} +46 -46
  64. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_graham/reviews/{normalized_earnings.ai-review.json → half_normalised_earnings_yield.ai-review.json} +46 -46
  65. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{penetration_liquidity.ai-review.json → cash_quality_gap.ai-review.json} +49 -49
  66. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_jhunjhunwala/reviews/{promoter_governance.ai-review.json → real_structural_growth.ai-review.json} +49 -49
  67. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{recovery_waterfall.ai-review.json → downside_asset_coverage.ai-review.json} +46 -46
  68. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_klarman/reviews/{cash_catalyst_compare.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  69. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{promise_integrity_ledger.ai-review.json → cash_backed_decade_return.ai-review.json} +46 -46
  70. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_li_lu/reviews/{ten_year_return_bridge.ai-review.json → decade_return_over_long_bond.ai-review.json} +49 -49
  71. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{story_numbers_check.ai-review.json → growth_justified_capitalisation.ai-review.json} +46 -46
  72. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_lynch/reviews/{category_classifier.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  73. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{cycle_temperature.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
  74. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_marks/reviews/{implied_expectations.ai-review.json → equity_over_corporate_debt.ai-review.json} +49 -49
  75. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/{failure_path_graph.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
  76. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_munger/reviews/reported_earnings_cash_gap.ai-review.json +277 -0
  77. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{surface_builder.ai-review.json → normalised_skew.ai-review.json} +46 -46
  78. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_natenberg/reviews/{greeks_payoff.ai-review.json → round_trip_cost.ai-review.json} +46 -46
  79. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{downside_floor.ai-review.json → downside_floor_coverage.ai-review.json} +46 -46
  80. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_pabrai/reviews/{discrete_payoff.ai-review.json → market_capitalisation.ai-review.json} +46 -46
  81. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{leakage_audit.ai-review.json → absolute_session_move.ai-review.json} +42 -42
  82. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_simons/reviews/{oos_cost_test.ai-review.json → session_move_over_implied.ai-review.json} +46 -46
  83. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{realized_vol_forecast.ai-review.json → round_trip_cost.ai-review.json} +46 -46
  84. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_sinclair/reviews/{net_edge_sizer.ai-review.json → volatility_per_unit_of_width.ai-review.json} +46 -46
  85. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reflexivity_graph.ai-review.json → credit_cycle_position.ai-review.json} +49 -49
  86. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_soros/reviews/{reversal_monitor.ai-review.json → financing_burden.ai-review.json} +46 -46
  87. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{tail_friction.ai-review.json → debt_service_cushion.ai-review.json} +49 -49
  88. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_taleb/reviews/{payoff_ruin.ai-review.json → normalised_skew.ai-review.json} +46 -46
  89. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{fractional_kelly.ai-review.json → edge_per_unit_of_volatility.ai-review.json} +46 -46
  90. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_thorp/reviews/{edge_recalculator.ai-review.json → net_edge.ai-review.json} +49 -49
  91. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/review-manifest.json +288 -270
  92. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-adjudications/index.json +2 -2
  93. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-extractions/index.json +2 -2
  94. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-semantic-skeptic-reviews/index.json +2 -2
  95. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-source-prereviews/index.json +6 -2
  96. package/knowledge/solo-test/masters/master_ackman/decision_policy.json +122 -11
  97. package/knowledge/solo-test/masters/master_ackman/doctrine.jsonl +2 -2
  98. package/knowledge/solo-test/masters/master_ackman/manifest.json +13 -6
  99. package/knowledge/solo-test/masters/master_ackman/provisional-index.json +3 -3
  100. package/knowledge/solo-test/masters/master_ackman/sources.jsonl +1 -1
  101. package/knowledge/solo-test/masters/master_ackman/tools.json +56 -30
  102. package/knowledge/solo-test/masters/master_aschenbrenner/decision_policy.json +101 -10
  103. package/knowledge/solo-test/masters/master_aschenbrenner/doctrine.jsonl +2 -2
  104. package/knowledge/solo-test/masters/master_aschenbrenner/manifest.json +12 -4
  105. package/knowledge/solo-test/masters/master_aschenbrenner/provisional-index.json +3 -3
  106. package/knowledge/solo-test/masters/master_aschenbrenner/sources.jsonl +1 -1
  107. package/knowledge/solo-test/masters/master_aschenbrenner/tools.json +56 -30
  108. package/knowledge/solo-test/masters/master_asness/decision_policy.json +89 -10
  109. package/knowledge/solo-test/masters/master_asness/doctrine.jsonl +2 -2
  110. package/knowledge/solo-test/masters/master_asness/manifest.json +11 -4
  111. package/knowledge/solo-test/masters/master_asness/provisional-index.json +3 -3
  112. package/knowledge/solo-test/masters/master_asness/sources.jsonl +1 -1
  113. package/knowledge/solo-test/masters/master_asness/tools.json +56 -30
  114. package/knowledge/solo-test/masters/master_bogle/counterfactuals.jsonl +0 -0
  115. package/knowledge/solo-test/masters/master_bogle/decision_cases.jsonl +0 -0
  116. package/knowledge/solo-test/masters/master_bogle/decision_policy.json +170 -0
  117. package/knowledge/solo-test/masters/master_bogle/doctrine.jsonl +14 -0
  118. package/knowledge/solo-test/masters/master_bogle/evaluation/calibration_cases.jsonl +0 -0
  119. package/knowledge/solo-test/masters/master_bogle/evaluation/experiments.json +5 -0
  120. package/knowledge/solo-test/masters/master_bogle/evaluation/golden_cases.jsonl +0 -0
  121. package/knowledge/solo-test/masters/master_bogle/evaluation/pairwise_cases.jsonl +0 -0
  122. package/knowledge/solo-test/masters/master_bogle/failures.jsonl +0 -0
  123. package/knowledge/solo-test/masters/master_bogle/manifest.json +141 -0
  124. package/knowledge/solo-test/masters/master_bogle/memory_policy.json +5 -0
  125. package/knowledge/solo-test/masters/master_bogle/provisional-index.json +41 -0
  126. package/knowledge/solo-test/masters/master_bogle/research_policy.json +11 -0
  127. package/knowledge/solo-test/masters/master_bogle/sources.jsonl +1 -0
  128. package/knowledge/solo-test/masters/master_bogle/tools.json +120 -0
  129. package/knowledge/solo-test/masters/master_bogle/voice.en.md +7 -0
  130. package/knowledge/solo-test/masters/master_bogle/voice.zh.md +7 -0
  131. package/knowledge/solo-test/masters/master_buffett/decision_policy.json +102 -11
  132. package/knowledge/solo-test/masters/master_buffett/doctrine.jsonl +2 -2
  133. package/knowledge/solo-test/masters/master_buffett/manifest.json +10 -4
  134. package/knowledge/solo-test/masters/master_buffett/provisional-index.json +3 -3
  135. package/knowledge/solo-test/masters/master_buffett/sources.jsonl +1 -1
  136. package/knowledge/solo-test/masters/master_buffett/tools.json +55 -29
  137. package/knowledge/solo-test/masters/master_burry/decision_policy.json +102 -12
  138. package/knowledge/solo-test/masters/master_burry/doctrine.jsonl +2 -2
  139. package/knowledge/solo-test/masters/master_burry/manifest.json +12 -6
  140. package/knowledge/solo-test/masters/master_burry/provisional-index.json +3 -3
  141. package/knowledge/solo-test/masters/master_burry/sources.jsonl +1 -1
  142. package/knowledge/solo-test/masters/master_burry/tools.json +56 -30
  143. package/knowledge/solo-test/masters/master_cathie_wood/decision_policy.json +74 -12
  144. package/knowledge/solo-test/masters/master_cathie_wood/doctrine.jsonl +2 -2
  145. package/knowledge/solo-test/masters/master_cathie_wood/manifest.json +12 -6
  146. package/knowledge/solo-test/masters/master_cathie_wood/provisional-index.json +3 -3
  147. package/knowledge/solo-test/masters/master_cathie_wood/sources.jsonl +1 -1
  148. package/knowledge/solo-test/masters/master_cathie_wood/tools.json +56 -30
  149. package/knowledge/solo-test/masters/master_dalio/decision_policy.json +123 -16
  150. package/knowledge/solo-test/masters/master_dalio/doctrine.jsonl +2 -2
  151. package/knowledge/solo-test/masters/master_dalio/manifest.json +10 -4
  152. package/knowledge/solo-test/masters/master_dalio/provisional-index.json +3 -3
  153. package/knowledge/solo-test/masters/master_dalio/sources.jsonl +1 -1
  154. package/knowledge/solo-test/masters/master_dalio/tools.json +56 -30
  155. package/knowledge/solo-test/masters/master_damodaran/decision_policy.json +58 -10
  156. package/knowledge/solo-test/masters/master_damodaran/doctrine.jsonl +2 -2
  157. package/knowledge/solo-test/masters/master_damodaran/manifest.json +9 -4
  158. package/knowledge/solo-test/masters/master_damodaran/provisional-index.json +3 -3
  159. package/knowledge/solo-test/masters/master_damodaran/sources.jsonl +1 -1
  160. package/knowledge/solo-test/masters/master_damodaran/tools.json +54 -28
  161. package/knowledge/solo-test/masters/master_druckenmiller/decision_policy.json +89 -10
  162. package/knowledge/solo-test/masters/master_druckenmiller/doctrine.jsonl +2 -2
  163. package/knowledge/solo-test/masters/master_druckenmiller/manifest.json +11 -4
  164. package/knowledge/solo-test/masters/master_druckenmiller/provisional-index.json +3 -3
  165. package/knowledge/solo-test/masters/master_druckenmiller/sources.jsonl +1 -1
  166. package/knowledge/solo-test/masters/master_druckenmiller/tools.json +55 -29
  167. package/knowledge/solo-test/masters/master_duan_yongping/decision_policy.json +138 -13
  168. package/knowledge/solo-test/masters/master_duan_yongping/doctrine.jsonl +2 -2
  169. package/knowledge/solo-test/masters/master_duan_yongping/manifest.json +17 -7
  170. package/knowledge/solo-test/masters/master_duan_yongping/provisional-index.json +3 -3
  171. package/knowledge/solo-test/masters/master_duan_yongping/sources.jsonl +1 -1
  172. package/knowledge/solo-test/masters/master_duan_yongping/tools.json +82 -30
  173. package/knowledge/solo-test/masters/master_fisher/decision_policy.json +102 -11
  174. package/knowledge/solo-test/masters/master_fisher/doctrine.jsonl +2 -2
  175. package/knowledge/solo-test/masters/master_fisher/manifest.json +13 -6
  176. package/knowledge/solo-test/masters/master_fisher/provisional-index.json +3 -3
  177. package/knowledge/solo-test/masters/master_fisher/sources.jsonl +1 -1
  178. package/knowledge/solo-test/masters/master_fisher/tools.json +58 -32
  179. package/knowledge/solo-test/masters/master_forensic_short/decision_policy.json +93 -14
  180. package/knowledge/solo-test/masters/master_forensic_short/doctrine.jsonl +2 -2
  181. package/knowledge/solo-test/masters/master_forensic_short/manifest.json +6 -4
  182. package/knowledge/solo-test/masters/master_forensic_short/provisional-index.json +3 -3
  183. package/knowledge/solo-test/masters/master_forensic_short/sources.jsonl +1 -1
  184. package/knowledge/solo-test/masters/master_forensic_short/tools.json +53 -27
  185. package/knowledge/solo-test/masters/master_graham/decision_policy.json +103 -12
  186. package/knowledge/solo-test/masters/master_graham/doctrine.jsonl +2 -2
  187. package/knowledge/solo-test/masters/master_graham/manifest.json +12 -4
  188. package/knowledge/solo-test/masters/master_graham/provisional-index.json +3 -3
  189. package/knowledge/solo-test/masters/master_graham/sources.jsonl +1 -1
  190. package/knowledge/solo-test/masters/master_graham/tools.json +67 -28
  191. package/knowledge/solo-test/masters/master_jhunjhunwala/decision_policy.json +77 -12
  192. package/knowledge/solo-test/masters/master_jhunjhunwala/doctrine.jsonl +2 -2
  193. package/knowledge/solo-test/masters/master_jhunjhunwala/manifest.json +11 -6
  194. package/knowledge/solo-test/masters/master_jhunjhunwala/provisional-index.json +3 -3
  195. package/knowledge/solo-test/masters/master_jhunjhunwala/sources.jsonl +1 -1
  196. package/knowledge/solo-test/masters/master_jhunjhunwala/tools.json +54 -28
  197. package/knowledge/solo-test/masters/master_klarman/decision_policy.json +105 -14
  198. package/knowledge/solo-test/masters/master_klarman/doctrine.jsonl +2 -2
  199. package/knowledge/solo-test/masters/master_klarman/manifest.json +8 -2
  200. package/knowledge/solo-test/masters/master_klarman/provisional-index.json +3 -3
  201. package/knowledge/solo-test/masters/master_klarman/sources.jsonl +1 -1
  202. package/knowledge/solo-test/masters/master_klarman/tools.json +55 -29
  203. package/knowledge/solo-test/masters/master_li_lu/decision_policy.json +101 -10
  204. package/knowledge/solo-test/masters/master_li_lu/doctrine.jsonl +2 -2
  205. package/knowledge/solo-test/masters/master_li_lu/manifest.json +13 -6
  206. package/knowledge/solo-test/masters/master_li_lu/provisional-index.json +3 -3
  207. package/knowledge/solo-test/masters/master_li_lu/sources.jsonl +1 -1
  208. package/knowledge/solo-test/masters/master_li_lu/tools.json +58 -32
  209. package/knowledge/solo-test/masters/master_lynch/decision_policy.json +127 -16
  210. package/knowledge/solo-test/masters/master_lynch/doctrine.jsonl +2 -2
  211. package/knowledge/solo-test/masters/master_lynch/manifest.json +14 -6
  212. package/knowledge/solo-test/masters/master_lynch/provisional-index.json +3 -3
  213. package/knowledge/solo-test/masters/master_lynch/sources.jsonl +1 -1
  214. package/knowledge/solo-test/masters/master_lynch/tools.json +67 -28
  215. package/knowledge/solo-test/masters/master_marks/decision_policy.json +71 -9
  216. package/knowledge/solo-test/masters/master_marks/doctrine.jsonl +2 -2
  217. package/knowledge/solo-test/masters/master_marks/manifest.json +11 -6
  218. package/knowledge/solo-test/masters/master_marks/provisional-index.json +3 -3
  219. package/knowledge/solo-test/masters/master_marks/sources.jsonl +1 -1
  220. package/knowledge/solo-test/masters/master_marks/tools.json +54 -28
  221. package/knowledge/solo-test/masters/master_munger/decision_policy.json +141 -16
  222. package/knowledge/solo-test/masters/master_munger/doctrine.jsonl +2 -2
  223. package/knowledge/solo-test/masters/master_munger/manifest.json +10 -6
  224. package/knowledge/solo-test/masters/master_munger/provisional-index.json +3 -3
  225. package/knowledge/solo-test/masters/master_munger/sources.jsonl +1 -1
  226. package/knowledge/solo-test/masters/master_munger/tools.json +52 -26
  227. package/knowledge/solo-test/masters/master_natenberg/decision_policy.json +59 -11
  228. package/knowledge/solo-test/masters/master_natenberg/doctrine.jsonl +2 -2
  229. package/knowledge/solo-test/masters/master_natenberg/manifest.json +9 -6
  230. package/knowledge/solo-test/masters/master_natenberg/provisional-index.json +3 -3
  231. package/knowledge/solo-test/masters/master_natenberg/sources.jsonl +1 -1
  232. package/knowledge/solo-test/masters/master_natenberg/tools.json +50 -24
  233. package/knowledge/solo-test/masters/master_pabrai/decision_policy.json +125 -14
  234. package/knowledge/solo-test/masters/master_pabrai/doctrine.jsonl +2 -2
  235. package/knowledge/solo-test/masters/master_pabrai/manifest.json +8 -2
  236. package/knowledge/solo-test/masters/master_pabrai/provisional-index.json +3 -3
  237. package/knowledge/solo-test/masters/master_pabrai/sources.jsonl +1 -1
  238. package/knowledge/solo-test/masters/master_pabrai/tools.json +55 -29
  239. package/knowledge/solo-test/masters/master_simons/decision_policy.json +59 -11
  240. package/knowledge/solo-test/masters/master_simons/doctrine.jsonl +2 -2
  241. package/knowledge/solo-test/masters/master_simons/manifest.json +8 -4
  242. package/knowledge/solo-test/masters/master_simons/provisional-index.json +3 -3
  243. package/knowledge/solo-test/masters/master_simons/sources.jsonl +1 -1
  244. package/knowledge/solo-test/masters/master_simons/tools.json +43 -30
  245. package/knowledge/solo-test/masters/master_sinclair/decision_policy.json +59 -11
  246. package/knowledge/solo-test/masters/master_sinclair/doctrine.jsonl +2 -2
  247. package/knowledge/solo-test/masters/master_sinclair/manifest.json +8 -6
  248. package/knowledge/solo-test/masters/master_sinclair/provisional-index.json +3 -3
  249. package/knowledge/solo-test/masters/master_sinclair/sources.jsonl +1 -1
  250. package/knowledge/solo-test/masters/master_sinclair/tools.json +54 -28
  251. package/knowledge/solo-test/masters/master_soros/decision_policy.json +98 -10
  252. package/knowledge/solo-test/masters/master_soros/doctrine.jsonl +2 -2
  253. package/knowledge/solo-test/masters/master_soros/manifest.json +10 -7
  254. package/knowledge/solo-test/masters/master_soros/provisional-index.json +3 -3
  255. package/knowledge/solo-test/masters/master_soros/sources.jsonl +1 -1
  256. package/knowledge/solo-test/masters/master_soros/tools.json +54 -28
  257. package/knowledge/solo-test/masters/master_taleb/decision_policy.json +124 -13
  258. package/knowledge/solo-test/masters/master_taleb/doctrine.jsonl +2 -2
  259. package/knowledge/solo-test/masters/master_taleb/manifest.json +9 -5
  260. package/knowledge/solo-test/masters/master_taleb/provisional-index.json +3 -3
  261. package/knowledge/solo-test/masters/master_taleb/sources.jsonl +1 -1
  262. package/knowledge/solo-test/masters/master_taleb/tools.json +50 -24
  263. package/knowledge/solo-test/masters/master_thorp/decision_policy.json +61 -13
  264. package/knowledge/solo-test/masters/master_thorp/doctrine.jsonl +2 -2
  265. package/knowledge/solo-test/masters/master_thorp/manifest.json +11 -6
  266. package/knowledge/solo-test/masters/master_thorp/provisional-index.json +3 -3
  267. package/knowledge/solo-test/masters/master_thorp/sources.jsonl +1 -1
  268. package/knowledge/solo-test/masters/master_thorp/tools.json +56 -30
  269. package/knowledge/solo-test/persona-v3-solo-test-formulas/compilation-manifest.json +290 -264
  270. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/components/tools.json +58 -32
  271. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  272. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
  273. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/components/tools.json +58 -32
  274. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  275. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/price_implied_cash_yield.derived-proxy-evidence.json +76 -0
  276. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/components/tools.json +58 -32
  277. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/nominal_earnings_yield_gap.derived-proxy-evidence.json +76 -0
  278. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/real_earnings_yield_gap.derived-proxy-evidence.json +76 -0
  279. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/components/tools.json +120 -0
  280. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/expected_return_over_long_bond.derived-proxy-evidence.json +77 -0
  281. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_bogle/provisional-derivations/fundamental_expected_return.derived-proxy-evidence.json +76 -0
  282. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/components/tools.json +57 -31
  283. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  284. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_yield.derived-proxy-evidence.json +76 -0
  285. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/components/tools.json +58 -32
  286. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_surplus_per_share.derived-proxy-evidence.json +77 -0
  287. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/net_current_asset_value_per_share.derived-proxy-evidence.json +76 -0
  288. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/components/tools.json +58 -32
  289. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/current_cash_yield.derived-proxy-evidence.json +76 -0
  290. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  291. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/components/tools.json +58 -32
  292. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
  293. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/real_curve_slope.derived-proxy-evidence.json +76 -0
  294. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/components/tools.json +56 -30
  295. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/implied_equity_risk_premium.derived-proxy-evidence.json +76 -0
  296. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/{reverse_valuation.derived-proxy-evidence.json → premium_versus_long_run_average.derived-proxy-evidence.json} +29 -16
  297. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/components/tools.json +57 -31
  298. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_curve_impulse.derived-proxy-evidence.json +76 -0
  299. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/policy_real_rate.derived-proxy-evidence.json +76 -0
  300. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/components/tools.json +84 -32
  301. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/five_year_opportunity_cost.derived-proxy-evidence.json +102 -0
  302. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
  303. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/components/tools.json +60 -34
  304. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/margin_retention.derived-proxy-evidence.json +76 -0
  305. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/reinvestment_spread.derived-proxy-evidence.json +76 -0
  306. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/components/tools.json +55 -29
  307. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/{accounting_rebuilder.derived-proxy-evidence.json → cash_conversion_gap.derived-proxy-evidence.json} +28 -15
  308. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/cash_cover_of_leverage.derived-proxy-evidence.json +76 -0
  309. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/components/tools.json +69 -30
  310. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/defensive_hurdle_base.derived-proxy-evidence.json +89 -0
  311. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/half_normalised_earnings_yield.derived-proxy-evidence.json +76 -0
  312. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/components/tools.json +56 -30
  313. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/cash_quality_gap.derived-proxy-evidence.json +76 -0
  314. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/real_structural_growth.derived-proxy-evidence.json +76 -0
  315. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/components/tools.json +57 -31
  316. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/downside_asset_coverage.derived-proxy-evidence.json +76 -0
  317. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  318. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/components/tools.json +60 -34
  319. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/cash_backed_decade_return.derived-proxy-evidence.json +76 -0
  320. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/decade_return_over_long_bond.derived-proxy-evidence.json +76 -0
  321. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/components/tools.json +69 -30
  322. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/growth_justified_capitalisation.derived-proxy-evidence.json +89 -0
  323. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  324. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/components/tools.json +56 -30
  325. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
  326. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/equity_over_corporate_debt.derived-proxy-evidence.json +76 -0
  327. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/components/tools.json +54 -28
  328. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
  329. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/reported_earnings_cash_gap.derived-proxy-evidence.json +76 -0
  330. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/components/tools.json +52 -26
  331. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/{surface_builder.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
  332. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_natenberg/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
  333. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/components/tools.json +57 -31
  334. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor_coverage.derived-proxy-evidence.json +76 -0
  335. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/market_capitalisation.derived-proxy-evidence.json +76 -0
  336. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/components/tools.json +45 -32
  337. package/knowledge/solo-test/persona-v3-solo-test-formulas/{master_natenberg/provisional-derivations/greeks_payoff.derived-proxy-evidence.json → master_simons/provisional-derivations/absolute_session_move.derived-proxy-evidence.json} +17 -17
  338. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/session_move_over_implied.derived-proxy-evidence.json +77 -0
  339. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/components/tools.json +56 -30
  340. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/round_trip_cost.derived-proxy-evidence.json +76 -0
  341. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/volatility_per_unit_of_width.derived-proxy-evidence.json +76 -0
  342. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/components/tools.json +56 -30
  343. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/credit_cycle_position.derived-proxy-evidence.json +76 -0
  344. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/financing_burden.derived-proxy-evidence.json +76 -0
  345. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/components/tools.json +52 -26
  346. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/debt_service_cushion.derived-proxy-evidence.json +76 -0
  347. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/{tail_friction.derived-proxy-evidence.json → normalised_skew.derived-proxy-evidence.json} +27 -14
  348. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/components/tools.json +58 -32
  349. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_per_unit_of_volatility.derived-proxy-evidence.json +77 -0
  350. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/net_edge.derived-proxy-evidence.json +76 -0
  351. package/mcp/lib/breadth.mjs +126 -0
  352. package/mcp/lib/constants.mjs +15 -0
  353. package/mcp/lib/damodaran.mjs +119 -0
  354. package/mcp/lib/fred.mjs +246 -0
  355. package/mcp/lib/fund-holdings-parsers.mjs +297 -0
  356. package/mcp/lib/fund-observations.mjs +152 -0
  357. package/mcp/lib/fundamentals.mjs +806 -0
  358. package/mcp/lib/funds.mjs +785 -0
  359. package/mcp/lib/gates.mjs +71 -4
  360. package/mcp/lib/grounding.mjs +209 -36
  361. package/mcp/lib/index-aggregate.mjs +733 -0
  362. package/mcp/lib/insider-ownership.mjs +149 -0
  363. package/mcp/lib/instrument-facts.mjs +855 -0
  364. package/mcp/lib/instruments.mjs +216 -0
  365. package/mcp/lib/markdown.mjs +216 -43
  366. package/mcp/lib/master-catalog.mjs +4 -0
  367. package/mcp/lib/orchestrator.mjs +173 -29
  368. package/mcp/lib/packets.mjs +59 -6
  369. package/mcp/lib/personas/engine.mjs +23 -10
  370. package/mcp/lib/personas-v3/deterministic-executor.mjs +13 -2
  371. package/mcp/lib/personas-v3/ga-external-evidence.mjs +3 -2
  372. package/mcp/lib/personas-v3/ga-gate.mjs +2 -1
  373. package/mcp/lib/personas-v3/ga-package-evidence.mjs +5 -4
  374. package/mcp/lib/personas-v3/grounding-adapter.mjs +309 -2
  375. package/mcp/lib/personas-v3/production-root.mjs +5 -4
  376. package/mcp/lib/personas-v3/release-formula-evidence.mjs +14 -7
  377. package/mcp/lib/personas-v3/release-source-evidence.mjs +2 -1
  378. package/mcp/lib/personas-v3/releases.mjs +5 -4
  379. package/mcp/lib/personas-v3/runtime-release-integrity.mjs +6 -5
  380. package/mcp/lib/personas-v3/semantic-source-adjudication.mjs +2 -1
  381. package/mcp/lib/personas-v3/semantic-source-extraction.mjs +2 -1
  382. package/mcp/lib/personas-v3/semantic-source-skeptic-review.mjs +2 -1
  383. package/mcp/lib/personas-v3/source-review-operations.mjs +3 -2
  384. package/mcp/lib/personas-v3/staging.mjs +22 -3
  385. package/mcp/lib/prompts.mjs +87 -3
  386. package/mcp/lib/quotes.mjs +5 -0
  387. package/mcp/lib/rpc.mjs +27 -10
  388. package/mcp/lib/run-store.mjs +5 -3
  389. package/mcp/lib/sec.mjs +32 -0
  390. package/mcp/lib/tables.mjs +11 -0
  391. package/mcp/lib/voice.mjs +122 -0
  392. package/mcp/lib/xbrl-aggregates.mjs +153 -0
  393. package/mcp/lib/xbrl-series.mjs +341 -0
  394. package/mcp/lib/xlsx.mjs +155 -0
  395. package/package.json +5 -5
  396. package/personas/masters/masters-value/bogle.md +145 -0
  397. package/schemas/council-evaluation-protocol-v1.schema.json +2 -2
  398. package/schemas/persona-v3-ai-assisted-solo-profile-v1.schema.json +2 -2
  399. package/schemas/persona-v3-ai-machine-simulation-v1.schema.json +1 -1
  400. package/schemas/persona-v3-build-spec-v1.schema.json +3 -3
  401. package/schemas/persona-v3-release-formula-review-evidence-v1.schema.json +3 -3
  402. package/schemas/persona-v3-release-manifest-v1.schema.json +6 -6
  403. package/schemas/persona-v3-release-source-review-evidence-v1.schema.json +3 -3
  404. package/schemas/persona-v3-tool-graph-v1.schema.json +1 -1
  405. package/schemas/source-review-batch-v1.schema.json +9 -9
  406. package/scripts/assemble-persona-v3-release.mjs +3 -2
  407. package/scripts/build-persona-v3-solo-test-packs.mjs +6 -8
  408. package/scripts/check-persona-v3-ga.mjs +1 -1
  409. package/scripts/check-persona-v3-production-candidates.mjs +1 -1
  410. package/scripts/compile-persona-v3-formulas.mjs +19 -18
  411. package/scripts/doctor.mjs +11 -9
  412. package/scripts/export-persona-source-review-batch.mjs +1 -1
  413. package/scripts/generate-persona-v3-drafts.mjs +2 -2
  414. package/scripts/lib/build-profile.mjs +24 -0
  415. package/scripts/lib/council-evaluation-protocol.mjs +7 -6
  416. package/scripts/lib/host-capabilities.mjs +2 -1
  417. package/scripts/lib/packaged-host-parity.mjs +9 -3
  418. package/scripts/lib/persona-v3-ai-assisted-solo-status.mjs +6 -4
  419. package/scripts/lib/persona-v3-ai-formula-review.mjs +6 -5
  420. package/scripts/lib/persona-v3-ai-machine-simulations.mjs +32 -4
  421. package/scripts/lib/persona-v3-build-specs.mjs +3 -2
  422. package/scripts/lib/persona-v3-draft-packs.mjs +2 -2
  423. package/scripts/lib/persona-v3-formula-pipeline.mjs +11 -10
  424. package/scripts/lib/persona-v3-production-candidates.mjs +10 -9
  425. package/scripts/lib/persona-v3-solo-formula-pipeline.mjs +188 -16
  426. package/scripts/lib/persona-v3-solo-test-packs.mjs +112 -11
  427. package/scripts/package-smoke.mjs +2 -1
  428. package/scripts/report-persona-corpus-gaps.mjs +4 -1
  429. package/scripts/review-persona-v3-solo-formulas.mjs +12 -8
  430. package/scripts/scaffold-persona-v3-staging.mjs +1 -1
  431. package/skills/agent-skills-governance/SKILL.md +8 -2
  432. package/skills/alphacouncil-agent/SKILL.md +12 -4
  433. package/skills/public-equity-investing/SKILL.md +17 -0
  434. package/knowledge/ai-assisted-solo/reviews/persona-v3-ai-formula-reviews/master_forensic_short/reviews/borrow_catalyst.ai-review.json +0 -277
  435. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/change_value_bridge.derived-proxy-evidence.json +0 -64
  436. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_ackman/provisional-derivations/power_map.derived-proxy-evidence.json +0 -64
  437. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/compute_power_bridge.derived-proxy-evidence.json +0 -64
  438. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_aschenbrenner/provisional-derivations/timeline_reverse_valuation.derived-proxy-evidence.json +0 -64
  439. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/crowding_cost_stress.derived-proxy-evidence.json +0 -64
  440. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_asness/provisional-derivations/factor_decomposer.derived-proxy-evidence.json +0 -64
  441. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/incremental_returns.derived-proxy-evidence.json +0 -64
  442. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_buffett/provisional-derivations/owner_earnings_rebuilder.derived-proxy-evidence.json +0 -64
  443. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/capital_structure_reader.derived-proxy-evidence.json +0 -64
  444. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_burry/provisional-derivations/mispricing_carry.derived-proxy-evidence.json +0 -64
  445. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/capture_valuation_bridge.derived-proxy-evidence.json +0 -64
  446. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_cathie_wood/provisional-derivations/cost_adoption_curve.derived-proxy-evidence.json +0 -64
  447. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/debt_driver_stress.derived-proxy-evidence.json +0 -64
  448. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_dalio/provisional-derivations/regime_classifier.derived-proxy-evidence.json +0 -64
  449. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_damodaran/provisional-derivations/story_dcf.derived-proxy-evidence.json +0 -64
  450. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/inflection_payoff.derived-proxy-evidence.json +0 -64
  451. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_druckenmiller/provisional-derivations/liquidity_revision_map.derived-proxy-evidence.json +0 -64
  452. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/opportunity_cost.derived-proxy-evidence.json +0 -64
  453. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_duan_yongping/provisional-derivations/user_value_evidence.derived-proxy-evidence.json +0 -64
  454. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/research_productivity.derived-proxy-evidence.json +0 -64
  455. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_fisher/provisional-derivations/scuttlebutt_graph.derived-proxy-evidence.json +0 -64
  456. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_forensic_short/provisional-derivations/borrow_catalyst.derived-proxy-evidence.json +0 -64
  457. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/asset_floor.derived-proxy-evidence.json +0 -64
  458. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_graham/provisional-derivations/normalized_earnings.derived-proxy-evidence.json +0 -64
  459. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/penetration_liquidity.derived-proxy-evidence.json +0 -64
  460. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_jhunjhunwala/provisional-derivations/promoter_governance.derived-proxy-evidence.json +0 -64
  461. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/cash_catalyst_compare.derived-proxy-evidence.json +0 -64
  462. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_klarman/provisional-derivations/recovery_waterfall.derived-proxy-evidence.json +0 -64
  463. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/promise_integrity_ledger.derived-proxy-evidence.json +0 -64
  464. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_li_lu/provisional-derivations/ten_year_return_bridge.derived-proxy-evidence.json +0 -64
  465. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/category_classifier.derived-proxy-evidence.json +0 -64
  466. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_lynch/provisional-derivations/story_numbers_check.derived-proxy-evidence.json +0 -64
  467. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/cycle_temperature.derived-proxy-evidence.json +0 -64
  468. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_marks/provisional-derivations/implied_expectations.derived-proxy-evidence.json +0 -64
  469. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/failure_path_graph.derived-proxy-evidence.json +0 -64
  470. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_munger/provisional-derivations/incentive_map.derived-proxy-evidence.json +0 -64
  471. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/discrete_payoff.derived-proxy-evidence.json +0 -64
  472. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_pabrai/provisional-derivations/downside_floor.derived-proxy-evidence.json +0 -64
  473. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/leakage_audit.derived-proxy-evidence.json +0 -64
  474. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_simons/provisional-derivations/oos_cost_test.derived-proxy-evidence.json +0 -64
  475. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/net_edge_sizer.derived-proxy-evidence.json +0 -64
  476. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_sinclair/provisional-derivations/realized_vol_forecast.derived-proxy-evidence.json +0 -64
  477. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reflexivity_graph.derived-proxy-evidence.json +0 -64
  478. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_soros/provisional-derivations/reversal_monitor.derived-proxy-evidence.json +0 -64
  479. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_taleb/provisional-derivations/payoff_ruin.derived-proxy-evidence.json +0 -64
  480. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/edge_recalculator.derived-proxy-evidence.json +0 -64
  481. package/knowledge/solo-test/persona-v3-solo-test-formulas/master_thorp/provisional-derivations/fractional_kelly.derived-proxy-evidence.json +0 -64
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Fiscal-year aggregates assembled from an aligned XBRL series.
3
+ *
4
+ * These answer "what does this filer report for year N", not "is that good". Debt is summed
5
+ * from non-overlapping buckets and names the ones a filer omits; tangible book deducts what
6
+ * is tagged and records the reading when a concept is absent from the whole document; the
7
+ * trailing-four-quarters window checks its own seams. Each returns null rather than zero when
8
+ * the filings do not support the figure -- a company with unknown debt is not a debt-free one.
9
+ */
10
+
11
+ import {
12
+ DEBT_BUCKETS,
13
+ FUNDAMENTAL_CONCEPTS,
14
+ alignYear,
15
+ daysBetween,
16
+ decimal,
17
+ fiscalYearOf,
18
+ gap,
19
+ } from "./xbrl-series.mjs";
20
+
21
+ const sum = (values) => values.reduce((total, value) => total + value, 0);
22
+ const finite = (value) => typeof value === "number" && Number.isFinite(value);
23
+
24
+ /**
25
+ * Both endpoints on one profit basis. Pairing a NOPAT endpoint with a net-income endpoint
26
+ * would difference two different quantities and call the result a return.
27
+ */
28
+ /** Four contiguous quarters ending at `index`, or null when a quarter is missing or overlaps. */
29
+ /**
30
+ * Profit for the incremental-return window: NOPAT when the tax inputs exist, otherwise net
31
+ * income -- and the basis is named in the output rather than left for the reader to guess.
32
+ */
33
+ /**
34
+ * Tangible book value: equity less goodwill and intangibles.
35
+ *
36
+ * A filer with goodwill on its balance sheet must tag it, so a concept absent from every
37
+ * period of the document is evidence of nil rather than a data gap -- and that reading is
38
+ * still written into `assumptions`. A concept reported in other years but not this one is
39
+ * treated as a genuine misalignment and refuses to compute.
40
+ */
41
+ /**
42
+ * Total debt for one fiscal year.
43
+ *
44
+ * A single combined tag wins outright. Otherwise the three non-overlapping buckets are summed
45
+ * and the ones a filer does not report are named in `missing_buckets` rather than assumed
46
+ * away silently. No bucket at all is null, never zero: a company with unknown debt is not a
47
+ * debt-free company.
48
+ */
49
+ export function totalDebtForYear(series, fiscalYear) {
50
+ const combined = series.debt_combined.find((entry) => entry.fiscal_year === fiscalYear);
51
+ if (combined) {
52
+ return { value: combined.value, entries: [combined], buckets: ["debt_combined"], missing_buckets: [] };
53
+ }
54
+ const present = DEBT_BUCKETS
55
+ .map((bucket) => ({ bucket, entry: series[bucket].find((item) => item.fiscal_year === fiscalYear) }))
56
+ .filter((item) => item.entry);
57
+ if (!present.length) return null;
58
+ return {
59
+ value: sum(present.map((item) => item.entry.value)),
60
+ entries: present.map((item) => item.entry),
61
+ buckets: present.map((item) => item.bucket),
62
+ missing_buckets: DEBT_BUCKETS.filter((bucket) => !present.some((item) => item.bucket === bucket)),
63
+ };
64
+ }
65
+
66
+ export function tangibleBookForYear(series, fiscalYear) {
67
+ const equity = series.equity.find((entry) => entry.fiscal_year === fiscalYear);
68
+ if (!equity) return null;
69
+ const deductions = [];
70
+ const assumptions = [];
71
+ const entries = [equity];
72
+ for (const concept of ["goodwill", "intangibles"]) {
73
+ const entry = series[concept].find((item) => item.fiscal_year === fiscalYear);
74
+ if (entry) {
75
+ deductions.push({ concept, tag: entry.tag, value: entry.value });
76
+ entries.push(entry);
77
+ continue;
78
+ }
79
+ if (series[concept].length) return { misaligned: concept };
80
+ assumptions.push(`${concept} is not tagged in any period of this filer's Company Facts and is treated as 0`);
81
+ }
82
+ return {
83
+ value: equity.value - sum(deductions.map((item) => item.value)),
84
+ equity: equity.value,
85
+ deductions,
86
+ assumptions,
87
+ entries,
88
+ };
89
+ }
90
+
91
+ export function profitForYear(series, fiscalYear) {
92
+ const nopat = alignYear(series, ["operating_income", "tax_expense", "pretax_income"], fiscalYear);
93
+ if (nopat.ok && nopat.entries.pretax_income.value > 0) {
94
+ const rate = nopat.entries.tax_expense.value / nopat.entries.pretax_income.value;
95
+ if (rate >= 0 && rate < 1) {
96
+ return {
97
+ basis: "nopat",
98
+ value: nopat.entries.operating_income.value * (1 - rate),
99
+ effective_tax_rate: decimal(rate),
100
+ entries: nopat.used,
101
+ };
102
+ }
103
+ }
104
+ const netIncome = series.net_income.find((entry) => entry.fiscal_year === fiscalYear);
105
+ if (!netIncome) return null;
106
+ return { basis: "net_income", value: netIncome.value, effective_tax_rate: null, entries: [netIncome] };
107
+ }
108
+
109
+ export function fourQuarters(quarters, index) {
110
+ if (index < 3) return null;
111
+ const window = quarters.slice(index - 3, index + 1);
112
+ const span = daysBetween(window[0].start, window.at(-1).end);
113
+ if (span < 330 || span > 400) return null;
114
+ for (let position = 1; position < window.length; position += 1) {
115
+ const seam = daysBetween(window[position - 1].end, window[position].start);
116
+ if (seam < -5 || seam > 15) return null;
117
+ }
118
+ return { total: sum(window.map((entry) => entry.val)), window };
119
+ }
120
+
121
+ export function matchedProfits(series, startYear, endYear) {
122
+ const profits = [startYear, endYear].map((year) => profitForYear(series, year));
123
+ if (profits.some((profit) => !profit)) return null;
124
+ if (profits.every((profit) => profit.basis === "nopat")) return { basis: "nopat", profits };
125
+ const fallback = [startYear, endYear]
126
+ .map((year) => series.net_income.find((entry) => entry.fiscal_year === year));
127
+ if (fallback.some((entry) => !entry)) return null;
128
+ return {
129
+ basis: "net_income",
130
+ profits: fallback.map((entry) => ({ basis: "net_income", value: entry.value, entries: [entry] })),
131
+ };
132
+ }
133
+
134
+ export function investedCapital(series, fiscalYear) {
135
+ const alignment = alignYear(series, ["equity", "cash"], fiscalYear);
136
+ if (!alignment.ok) return null;
137
+ const debt = totalDebtForYear(series, fiscalYear);
138
+ if (!debt) return null;
139
+ return {
140
+ value: alignment.entries.equity.value + debt.value - alignment.entries.cash.value,
141
+ period_end: alignment.period_end,
142
+ entries: [...alignment.used, ...debt.entries],
143
+ };
144
+ }
145
+
146
+ export function ebitdaForYear(series, fiscalYear) {
147
+ const alignment = alignYear(series, ["operating_income", "depreciation_amortisation"], fiscalYear);
148
+ if (!alignment.ok) return null;
149
+ return {
150
+ value: alignment.entries.operating_income.value + alignment.entries.depreciation_amortisation.value,
151
+ entries: alignment.used,
152
+ };
153
+ }
@@ -0,0 +1,341 @@
1
+ /**
2
+ * XBRL series plumbing shared by every derived fundamental.
3
+ *
4
+ * Nothing here knows what a metric means. It answers four mechanical questions about SEC
5
+ * Company Facts documents -- which periods exist for a concept, which of them align, when the
6
+ * newest input became public, and how a computed number is packaged with its lineage -- and
7
+ * `fundamentals.mjs` builds the operating figures on top. Separating them keeps the alignment
8
+ * rules in one place: a metric that quietly aligned differently from its neighbours would be
9
+ * the kind of defect no test asks about.
10
+ *
11
+ * All network access goes through sec.mjs, which owns the throttle and the User-Agent.
12
+ */
13
+
14
+ import { CONCEPTS, annualSeries } from "./sec.mjs";
15
+ import { sha256 } from "./personas-v3/canonical.mjs";
16
+
17
+ export const TOOL_ID = "sec_fundamentals";
18
+ export const TOOL_VERSION = "1.0.0";
19
+ const HASH_DOMAIN = "alphacouncil.fundamentals.sec-companyfacts.v1";
20
+
21
+ const finite = (value) => typeof value === "number" && Number.isFinite(value);
22
+ export const decimal = (value) => Number(value.toFixed(6));
23
+ const DAY_MS = 86_400_000;
24
+ export const daysBetween = (from, to) => Math.round((Date.parse(to) - Date.parse(from)) / DAY_MS);
25
+
26
+ /** Two facts claiming the same period may not disagree by more than this. */
27
+ export const MAX_ALIGNMENT_DRIFT_DAYS = 45;
28
+
29
+ /**
30
+ * Concept aliases, ordered by preference, on top of the ones sec.mjs already publishes.
31
+ *
32
+ * Aliases inside one entry are the same economic quantity under different taxonomies. They
33
+ * are never a fallback to a different concept: total liabilities is not reconstructed from
34
+ * an equity subtraction, and a missing depreciation tag stays missing.
35
+ */
36
+ export const FUNDAMENTAL_CONCEPTS = Object.freeze({
37
+ revenue: { tags: CONCEPTS.revenue, unit: "USD" },
38
+ net_income: { tags: CONCEPTS.netIncome, unit: "USD" },
39
+ operating_income: { tags: CONCEPTS.operatingIncome, unit: "USD" },
40
+ capex: { tags: CONCEPTS.capex, unit: "USD" },
41
+ equity: { tags: CONCEPTS.equity, unit: "USD" },
42
+ depreciation_amortisation: {
43
+ tags: [
44
+ "DepreciationDepletionAndAmortization",
45
+ "DepreciationAmortizationAndAccretionNet",
46
+ "DepreciationAndAmortization",
47
+ "Depreciation",
48
+ ],
49
+ unit: "USD",
50
+ },
51
+ current_assets: { tags: ["AssetsCurrent"], unit: "USD" },
52
+ total_liabilities: { tags: ["Liabilities"], unit: "USD" },
53
+ goodwill: { tags: ["Goodwill"], unit: "USD" },
54
+ intangibles: { tags: ["IntangibleAssetsNetExcludingGoodwill", "FiniteLivedIntangibleAssetsNet"], unit: "USD" },
55
+ cash: {
56
+ tags: [
57
+ "CashAndCashEquivalentsAtCarryingValue",
58
+ "CashCashEquivalentsRestrictedCashAndRestrictedCashEquivalents",
59
+ ],
60
+ unit: "USD",
61
+ },
62
+ // Debt buckets are non-overlapping by construction so they can be summed. `debt_combined`
63
+ // is the whole of it in one tag and wins outright when a filer publishes it.
64
+ debt_combined: { tags: ["DebtLongtermAndShorttermCombinedAmount"], unit: "USD" },
65
+ debt_long_term_noncurrent: { tags: ["LongTermDebtNoncurrent", "LongTermDebtAndCapitalLeaseObligations"], unit: "USD" },
66
+ debt_long_term_current: { tags: ["LongTermDebtCurrent", "LongTermDebtAndCapitalLeaseObligationsCurrent"], unit: "USD" },
67
+ debt_short_term: { tags: ["ShortTermBorrowings", "OtherShortTermBorrowings", "CommercialPaper"], unit: "USD" },
68
+ tax_expense: { tags: ["IncomeTaxExpenseBenefit"], unit: "USD" },
69
+ pretax_income: {
70
+ tags: [
71
+ "IncomeLossFromContinuingOperationsBeforeIncomeTaxesExtraordinaryItemsNoncontrollingInterest",
72
+ "IncomeLossFromContinuingOperationsBeforeIncomeTaxesMinorityInterestAndIncomeLossFromEquityMethodInvestments",
73
+ ],
74
+ unit: "USD",
75
+ },
76
+ diluted_shares: {
77
+ tags: ["WeightedAverageNumberOfDilutedSharesOutstanding", "WeightedAverageNumberOfDilutedSharesOutstandingBasicAndDiluted"],
78
+ // Share concepts are filed under `shares`, not USD. Asking for USD returns nothing, which
79
+ // is how the screen's dilution rule once reported "skipped" for every company alive.
80
+ unit: "shares",
81
+ },
82
+ });
83
+
84
+ export const DEBT_BUCKETS = Object.freeze(["debt_long_term_noncurrent", "debt_long_term_current", "debt_short_term"]);
85
+
86
+ /** A date-only SEC field becomes an explicit UTC instant; anything unparseable becomes null. */
87
+ export function toInstant(value) {
88
+ if (typeof value !== "string" || value.length < 10) return null;
89
+ const time = Date.parse(value.length === 10 ? `${value}T00:00:00.000Z` : value);
90
+ return Number.isFinite(time) ? new Date(time).toISOString() : null;
91
+ }
92
+
93
+ /** The fiscal year a period belongs to, taken from its end date -- the sec.mjs convention. */
94
+ export const fiscalYearOf = (end) => Number(String(end).slice(0, 4));
95
+
96
+ /* -------------------------------------------------------------- series and alignment -- */
97
+
98
+ /** One concept's annual history, flattened the way screen.mjs flattens it. */
99
+ export function conceptSeries(companyFacts, concept, asOf) {
100
+ const spec = FUNDAMENTAL_CONCEPTS[concept];
101
+ const found = annualSeries(companyFacts, spec.tags, { asOf, unit: spec.unit });
102
+ if (!found) return [];
103
+ return found.series.map((entry) => Object.freeze({
104
+ concept,
105
+ tag: entry.tag || found.tag,
106
+ unit: spec.unit,
107
+ start: entry.start || null,
108
+ end: entry.end,
109
+ filed: entry.filed || null,
110
+ accession: entry.accn || null,
111
+ value: entry.val,
112
+ fiscal_year: fiscalYearOf(entry.end),
113
+ }));
114
+ }
115
+
116
+ export function loadSeries(companyFacts, asOf) {
117
+ return Object.freeze(Object.fromEntries(Object.keys(FUNDAMENTAL_CONCEPTS)
118
+ .map((concept) => [concept, conceptSeries(companyFacts, concept, asOf)])));
119
+ }
120
+
121
+ /**
122
+ * Quarterly history for a concept. sec.mjs only merges annual periods, so this mirrors its
123
+ * rules for ~quarter-length durations: alias preference decides a period, a later filing of
124
+ * the same alias supersedes an earlier one, and nothing filed after `asOf` is visible.
125
+ */
126
+ export function quarterlySeries(companyFacts, tags, { asOf = null, unit = "USD" } = {}) {
127
+ const cutoff = asOf ? new Date(asOf).getTime() : null;
128
+ const byEnd = new Map();
129
+ for (const tag of tags) {
130
+ const entries = companyFacts?.facts?.["us-gaap"]?.[tag]?.units?.[unit];
131
+ if (!Array.isArray(entries)) continue;
132
+ for (const entry of entries) {
133
+ if (entry.form !== "10-Q" && entry.form !== "10-K") continue;
134
+ if (!entry.start || !entry.end || !Number.isFinite(entry.val)) continue;
135
+ const days = daysBetween(entry.start, entry.end);
136
+ if (days < 80 || days > 100) continue;
137
+ if (cutoff && new Date(entry.filed).getTime() > cutoff) continue;
138
+ const prior = byEnd.get(entry.end);
139
+ if (!prior) {
140
+ byEnd.set(entry.end, { ...entry, tag });
141
+ } else if (prior.tag === tag && new Date(entry.filed) > new Date(prior.filed)) {
142
+ byEnd.set(entry.end, { ...entry, tag });
143
+ }
144
+ }
145
+ }
146
+ if (!byEnd.size) return null;
147
+ return [...byEnd.values()].sort((left, right) => Date.parse(left.end) - Date.parse(right.end));
148
+ }
149
+
150
+ /** The newest filing instant across a set of entries -- the moment the metric became public. */
151
+ export function publicInstant(entries) {
152
+ const instants = entries.map((entry) => toInstant(entry.filed)).filter(Boolean);
153
+ if (instants.length !== entries.length) return null;
154
+ return instants.sort().at(-1);
155
+ }
156
+
157
+ /**
158
+ * The most recent fiscal year in which every named concept has a value.
159
+ *
160
+ * Aligning matters more than it looks: pairing this year's net income with last year's
161
+ * balance sheet because one tag stopped being filed is exactly the stale-period carry-forward
162
+ * this module exists to refuse.
163
+ */
164
+ export function alignLatest(series, concepts) {
165
+ const missing = concepts.filter((concept) => !series[concept]?.length);
166
+ if (missing.length) return { ok: false, reason: "missing_tag", missing };
167
+
168
+ const byYear = concepts.map((concept) => new Map(series[concept].map((entry) => [entry.fiscal_year, entry])));
169
+ const shared = [...byYear[0].keys()].filter((year) => byYear.every((map) => map.has(year)));
170
+ if (!shared.length) return { ok: false, reason: "no_common_period", missing: [] };
171
+
172
+ const fiscalYear = Math.max(...shared);
173
+ const entries = concepts.map((concept, index) => byYear[index].get(fiscalYear));
174
+ return finishAlignment(entries, concepts, fiscalYear);
175
+ }
176
+
177
+ /** The same alignment, pinned to a stated fiscal year rather than to the newest one. */
178
+ export function alignYear(series, concepts, fiscalYear) {
179
+ const picked = concepts.map((concept) => series[concept]?.find((entry) => entry.fiscal_year === fiscalYear));
180
+ const missing = concepts.filter((concept, index) => !picked[index]);
181
+ if (missing.length) return { ok: false, reason: "no_common_period", missing };
182
+ return finishAlignment(picked, concepts, fiscalYear);
183
+ }
184
+
185
+ export function finishAlignment(entries, concepts, fiscalYear) {
186
+ const ends = entries.map((entry) => Date.parse(entry.end));
187
+ if (Math.max(...ends) - Math.min(...ends) > MAX_ALIGNMENT_DRIFT_DAYS * DAY_MS) {
188
+ return { ok: false, reason: "period_misaligned", missing: [] };
189
+ }
190
+ const publicAt = publicInstant(entries);
191
+ if (!publicAt) return { ok: false, reason: "missing_filing_date", missing: [] };
192
+
193
+ const starts = entries.map((entry) => entry.start).filter(Boolean).sort();
194
+ return {
195
+ ok: true,
196
+ fiscal_year: fiscalYear,
197
+ period_start: starts[0] || null,
198
+ period_end: entries.map((entry) => entry.end).sort().at(-1),
199
+ public_at: publicAt,
200
+ entries: Object.fromEntries(concepts.map((concept, index) => [concept, entries[index]])),
201
+ used: entries,
202
+ };
203
+ }
204
+
205
+ /** Merge alignments that must describe the same balance sheet or the same fiscal year. */
206
+ export function combineAlignments(alignments) {
207
+ const ends = alignments.map((alignment) => Date.parse(alignment.period_end));
208
+ if (Math.max(...ends) - Math.min(...ends) > MAX_ALIGNMENT_DRIFT_DAYS * DAY_MS) {
209
+ return { ok: false, reason: "period_misaligned", missing: [] };
210
+ }
211
+ const used = alignments.flatMap((alignment) => alignment.used);
212
+ const starts = used.map((entry) => entry.start).filter(Boolean).sort();
213
+ return {
214
+ ok: true,
215
+ fiscal_year: Math.max(...alignments.map((alignment) => alignment.fiscal_year)),
216
+ period_start: starts[0] || null,
217
+ period_end: alignments.map((alignment) => alignment.period_end).sort().at(-1),
218
+ public_at: alignments.map((alignment) => alignment.public_at).sort().at(-1),
219
+ entries: Object.assign({}, ...alignments.map((alignment) => alignment.entries)),
220
+ used,
221
+ };
222
+ }
223
+
224
+ /* ------------------------------------------------------------------------------- gaps -- */
225
+
226
+ export const tagsFor = (concepts) => concepts.flatMap((concept) => FUNDAMENTAL_CONCEPTS[concept]?.tags || []);
227
+
228
+ export function gap({ factId, component = null, code, detail, missingTags = [] }) {
229
+ return Object.freeze({
230
+ metric: factId,
231
+ component,
232
+ code,
233
+ detail,
234
+ missing_tags: Object.freeze([...missingTags]),
235
+ });
236
+ }
237
+
238
+ /** Turn a failed alignment into a gap that names the concept and every tag that was tried. */
239
+ export function alignmentGap(factId, alignment, concepts, component = null) {
240
+ if (alignment.reason === "missing_tag" || (alignment.reason === "no_common_period" && alignment.missing.length)) {
241
+ const missingTags = tagsFor(alignment.missing);
242
+ return gap({
243
+ factId,
244
+ component,
245
+ code: alignment.reason,
246
+ detail: alignment.reason === "missing_tag"
247
+ ? `no XBRL tag for ${alignment.missing.join(", ")} in Company Facts (tried ${missingTags.join(", ")})`
248
+ : `${alignment.missing.join(", ")} is not reported for the aligned fiscal year`,
249
+ missingTags,
250
+ });
251
+ }
252
+ const detail = {
253
+ no_common_period: `no fiscal year in which all of ${concepts.join(", ")} are reported together`,
254
+ period_misaligned: `${concepts.join(", ")} disagree on the period end by more than ${MAX_ALIGNMENT_DRIFT_DAYS} days`,
255
+ missing_filing_date: `an input carries no filing date, so public_at cannot be taken from the filing`,
256
+ }[alignment.reason] || `inputs unavailable for ${concepts.join(", ")}`;
257
+ return gap({ factId, component, code: alignment.reason, detail, missingTags: tagsFor(concepts) });
258
+ }
259
+
260
+ /* --------------------------------------------------------------------- metric factory -- */
261
+
262
+ const CONFIDENCE = Object.freeze({ reported: 0.95, rederived: 0.9, estimated: 0.6 });
263
+
264
+ export function sourceRecords(entries) {
265
+ const seen = new Set();
266
+ const records = [];
267
+ for (const entry of entries) {
268
+ const key = `${entry.tag}:${entry.accession || entry.filed}:${entry.end}`;
269
+ if (seen.has(key)) continue;
270
+ seen.add(key);
271
+ records.push(Object.freeze({
272
+ concept: entry.concept,
273
+ tag: entry.tag,
274
+ accession: entry.accession,
275
+ filed: entry.filed,
276
+ period_end: entry.end,
277
+ unit: entry.unit,
278
+ value: entry.value,
279
+ }));
280
+ }
281
+ return Object.freeze(records);
282
+ }
283
+
284
+ /**
285
+ * `sec:companyfacts:<cik>:<tag>:<accession|filed>:<period_end>` -- the identity the typed-fact
286
+ * adapter already knows how to parse back into a filing locator.
287
+ */
288
+ export function sourceIds(cik, entries) {
289
+ return Object.freeze([...new Set(sourceRecords(entries)
290
+ .map((record) => `sec:companyfacts:${cik}:${record.tag}:${record.accession || record.filed}:${record.period_end}`))]);
291
+ }
292
+
293
+ export function buildMetric({
294
+ factId,
295
+ valueKind,
296
+ value,
297
+ unit,
298
+ currency = null,
299
+ scale = null,
300
+ ratioDenominator = null,
301
+ alignment,
302
+ cik,
303
+ derivation,
304
+ inputs,
305
+ assumptions,
306
+ components = null,
307
+ }) {
308
+ const derived = derivation !== "reported";
309
+ const parameters = { hash_domain: HASH_DOMAIN, fact_id: factId, tool_id: TOOL_ID, tool_version: TOOL_VERSION };
310
+ return Object.freeze({
311
+ fact_id: factId,
312
+ value_kind: valueKind,
313
+ value,
314
+ unit,
315
+ currency,
316
+ scale,
317
+ ratio_denominator: ratioDenominator,
318
+ // A metric may declare itself a point-in-time quantity, in which case the span its inputs
319
+ // were aligned over is not the period it covers and stating one would be wrong.
320
+ period_start: alignment.suppressInterval ? null : alignment.period_start,
321
+ period_end: alignment.suppressInterval ? null : alignment.period_end,
322
+ fiscal_year: Number.isInteger(alignment.fiscal_year) ? alignment.fiscal_year : null,
323
+ // Never the clock: the newest filing among the inputs is the moment this became knowable.
324
+ public_at: alignment.public_at,
325
+ derivation,
326
+ confidence: CONFIDENCE[derivation],
327
+ source_ids: sourceIds(cik, alignment.used),
328
+ source_records: sourceRecords(alignment.used),
329
+ inputs: Object.freeze(inputs),
330
+ components: components ? Object.freeze(components) : null,
331
+ assumptions: Object.freeze([...assumptions]),
332
+ tool_id: derived ? TOOL_ID : null,
333
+ tool_version: derived ? TOOL_VERSION : null,
334
+ // A reported figure is not a calculation, so it carries no calculation hash -- which is
335
+ // exactly what the typed-fact contract requires of `derivation: "reported"`.
336
+ calculation_hash: derived ? sha256({ ...parameters, inputs, assumptions, value }) : null,
337
+ });
338
+ }
339
+
340
+ /* -------------------------------------------------------------- shared derived pieces -- */
341
+
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Read an .xlsx with Node built-ins and nothing else.
3
+ *
4
+ * An xlsx is a ZIP of XML parts, and `zlib.inflateRawSync` is enough to open one. Extracted
5
+ * from the fund adapter because it has no fund-domain knowledge at all: any publisher that
6
+ * ships a workbook -- issuer holdings, an academic dataset -- reads through the same three
7
+ * steps. Adding a spreadsheet dependency to a zero-dependency package is not worth it for
8
+ * what fits in a hundred lines.
9
+ *
10
+ * Everything unusual fails closed. This reads the ordinary single-workbook shape; it is not a
11
+ * general ZIP implementation and must not quietly behave like one.
12
+ */
13
+
14
+ import { inflateRawSync } from "node:zlib";
15
+
16
+ /**
17
+ * Minimal ZIP central-directory reader.
18
+ *
19
+ * An xlsx is a ZIP of XML parts, and `zlib.inflateRawSync` is enough to read one, so a workbook is
20
+ * readable without adding a dependency. Entries are located through the CENTRAL directory
21
+ * rather than the local headers on purpose: when the archive uses a data descriptor, the
22
+ * local header's sizes are zeroed and only the central directory is truthful.
23
+ *
24
+ * Everything unusual fails closed. This reads the one archive shape SSGA publishes; it is not
25
+ * a general ZIP implementation and must not silently behave like one.
26
+ */
27
+ export function unzip(buffer) {
28
+ const buf = Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer);
29
+ if (buf.length < 22 || buf.readUInt32LE(0) !== 0x04034b50) {
30
+ throw new Error("response is not a ZIP/xlsx container");
31
+ }
32
+ let eocd = -1;
33
+ for (let i = buf.length - 22; i >= 0; i -= 1) {
34
+ if (buf.readUInt32LE(i) === 0x06054b50) { eocd = i; break; }
35
+ }
36
+ if (eocd < 0) throw new Error("xlsx container has no end-of-central-directory record");
37
+ if (buf.readUInt16LE(eocd + 10) === 0xffff) throw new Error("ZIP64 xlsx containers are not supported");
38
+
39
+ const count = buf.readUInt16LE(eocd + 10);
40
+ let offset = buf.readUInt32LE(eocd + 16);
41
+ const entries = new Map();
42
+ for (let i = 0; i < count; i += 1) {
43
+ if (offset + 46 > buf.length || buf.readUInt32LE(offset) !== 0x02014b50) {
44
+ throw new Error("xlsx central directory is malformed");
45
+ }
46
+ const flags = buf.readUInt16LE(offset + 8);
47
+ const method = buf.readUInt16LE(offset + 10);
48
+ const compressedSize = buf.readUInt32LE(offset + 20);
49
+ const nameLen = buf.readUInt16LE(offset + 28);
50
+ const extraLen = buf.readUInt16LE(offset + 30);
51
+ const commentLen = buf.readUInt16LE(offset + 32);
52
+ const localOffset = buf.readUInt32LE(offset + 42);
53
+ const name = buf.toString("utf8", offset + 46, offset + 46 + nameLen);
54
+ if (flags & 0x0001) throw new Error(`xlsx entry ${name} is encrypted`);
55
+ if (method !== 0 && method !== 8) throw new Error(`xlsx entry ${name} uses unsupported compression method ${method}`);
56
+ const localNameLen = buf.readUInt16LE(localOffset + 26);
57
+ const localExtraLen = buf.readUInt16LE(localOffset + 28);
58
+ const start = localOffset + 30 + localNameLen + localExtraLen;
59
+ const raw = buf.subarray(start, start + compressedSize);
60
+ entries.set(name, () => (method === 0 ? raw : inflateRawSync(raw)).toString("utf8"));
61
+ offset += 46 + nameLen + extraLen + commentLen;
62
+ }
63
+ return entries;
64
+ }
65
+
66
+ const XML_ENTITIES = Object.freeze({ "&lt;": "<", "&gt;": ">", "&quot;": '"', "&apos;": "'", "&amp;": "&" });
67
+ const unescapeXml = (text) => String(text).replace(/&(?:lt|gt|quot|apos|amp);/gu, (m) => XML_ENTITIES[m]);
68
+
69
+ /** Concatenate every <t> inside an element: a shared string may be split into rich-text runs. */
70
+ export const xmlText = (fragment) => {
71
+ let out = "";
72
+ for (const match of String(fragment).matchAll(/<t[^>]*>([\s\S]*?)<\/t>/gu)) out += match[1];
73
+ return unescapeXml(out);
74
+ };
75
+
76
+ /** "AB12" -> 27. Cells are addressed, not ordered: empty cells are simply absent. */
77
+ export const columnIndex = (ref) => {
78
+ const letters = /^([A-Z]+)/u.exec(String(ref || "").toUpperCase());
79
+ if (!letters) return -1;
80
+ return [...letters[1]].reduce((acc, ch) => acc * 26 + (ch.charCodeAt(0) - 64), 0) - 1;
81
+ };
82
+
83
+ export function xlsxRows(sheetXml, sharedStrings) {
84
+ const rows = [];
85
+ for (const rowMatch of sheetXml.matchAll(/<row[^>]*>([\s\S]*?)<\/row>/gu)) {
86
+ const cells = [];
87
+ for (const cellMatch of rowMatch[1].matchAll(/<c\s+r="([A-Z]+\d+)"([^>]*)>([\s\S]*?)<\/c>/gu)) {
88
+ const index = columnIndex(cellMatch[1]);
89
+ if (index < 0) continue;
90
+ const type = /t="([^"]+)"/u.exec(cellMatch[2])?.[1] || "n";
91
+ const body = cellMatch[3];
92
+ const value = /<v>([\s\S]*?)<\/v>/u.exec(body)?.[1];
93
+ if (type === "s") cells[index] = sharedStrings[Number(value)] ?? null;
94
+ else if (type === "inlineStr") cells[index] = xmlText(/<is>([\s\S]*?)<\/is>/u.exec(body)?.[1] || "");
95
+ else cells[index] = value === undefined ? null : unescapeXml(value);
96
+ }
97
+ rows.push(cells);
98
+ }
99
+ return rows;
100
+ }
101
+
102
+ /**
103
+ * Open a workbook: every sheet's rows, plus the date system its serials are counted in.
104
+ *
105
+ * The date system is not optional detail. A workbook written by Mac Excel sets
106
+ * `date1904="1"` and its serials sit 1,462 days from the default epoch -- read with the wrong
107
+ * one, every date is silently four years and a day early, which looks entirely plausible and
108
+ * is how a current dataset gets mistaken for a stale one.
109
+ *
110
+ * Callers locate their header row by content rather than by offset: publishers move the table
111
+ * when the surrounding label and disclosure rows change length.
112
+ */
113
+ export function readWorkbook(buffer) {
114
+ const entries = unzip(buffer);
115
+ const workbookPart = entries.get("xl/workbook.xml");
116
+ const date1904 = workbookPart ? /date1904\s*=\s*"(?:1|true)"/iu.test(workbookPart()) : false;
117
+ const sharedPart = entries.get("xl/sharedStrings.xml");
118
+ const sharedStrings = sharedPart
119
+ ? [...sharedPart().matchAll(/<si>([\s\S]*?)<\/si>/gu)].map((match) => xmlText(match[1]))
120
+ : [];
121
+ const sheets = [...entries.keys()]
122
+ .filter((name) => /^xl\/worksheets\/[^/]+\.xml$/u.test(name))
123
+ .sort()
124
+ .map((name) => ({ name, rows: xlsxRows(entries.get(name)(), sharedStrings) }));
125
+ if (!sheets.length) throw new Error("workbook contains no worksheet part");
126
+ return { date1904, sheets };
127
+ }
128
+
129
+ /**
130
+ * Excel serial date to ISO, honouring the workbook's date system.
131
+ *
132
+ * In the default 1900 system day 1 is 1900-01-01 and the format wrongly treats 1900 as a leap
133
+ * year, so serials at or above 60 carry an extra day that has to come back out. The 1904
134
+ * system has neither quirk.
135
+ */
136
+ export function excelSerialToIso(serial, { date1904 = false } = {}) {
137
+ const value = Number(serial);
138
+ if (!Number.isFinite(value) || value < 0) return null;
139
+ const epoch = date1904 ? Date.UTC(1904, 0, 1) : Date.UTC(1899, 11, 31);
140
+ const days = date1904 ? value : (value >= 60 ? value - 1 : value);
141
+ return new Date(epoch + (days * 86400000)).toISOString().slice(0, 10);
142
+ }
143
+
144
+ export function readWorkbookRows(buffer, { sheetPath = null } = {}) {
145
+ const entries = unzip(buffer);
146
+ const sharedPart = entries.get("xl/sharedStrings.xml");
147
+ const sharedStrings = sharedPart
148
+ ? [...sharedPart().matchAll(/<si>([\s\S]*?)<\/si>/gu)].map((match) => xmlText(match[1]))
149
+ : [];
150
+ const target = sheetPath
151
+ || [...entries.keys()].find((name) => /^xl\/worksheets\/sheet1\.xml$/u.test(name))
152
+ || [...entries.keys()].find((name) => /^xl\/worksheets\/.+\.xml$/u.test(name));
153
+ if (!target) throw new Error("workbook contains no worksheet part");
154
+ return xlsxRows(entries.get(target)(), sharedStrings);
155
+ }